mirror of
https://git.wownero.com/lza_menace/neroswap.git
synced 2024-08-15 01:03:24 +00:00
242 lines
9.6 KiB
Python
242 lines
9.6 KiB
Python
import click
|
|
import arrow
|
|
from os.path import isfile
|
|
from time import sleep
|
|
from flask import Flask, url_for
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
from flask_wtf.csrf import CSRFProtect
|
|
from redis import Redis
|
|
from datetime import datetime, timezone, timedelta
|
|
from app import config
|
|
from app.library.mattermost import post_webhook
|
|
|
|
|
|
db = SQLAlchemy()
|
|
|
|
def _setup_db(app: Flask):
|
|
uri = 'postgresql+psycopg2://{user}:{pw}@{host}:{port}/{db}'.format(
|
|
user=config.DB_USER,
|
|
pw=config.DB_PASS,
|
|
host=config.DB_HOST,
|
|
port=config.DB_PORT,
|
|
db=config.DB_NAME
|
|
)
|
|
app.config['SQLALCHEMY_DATABASE_URI'] = uri
|
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
|
db = SQLAlchemy(app)
|
|
|
|
def create_app():
|
|
app = Flask(__name__)
|
|
app.config.from_envvar('FLASK_SECRETS')
|
|
|
|
# Setup backends
|
|
_setup_db(app)
|
|
|
|
with app.app_context():
|
|
|
|
# todo - find a way to move filters into a new file
|
|
|
|
# Template filters
|
|
@app.template_filter('pst')
|
|
def pst(s):
|
|
d = arrow.get(s).to('US/Pacific').format('YYYY-MM-DD HH:mm:ss')
|
|
return d
|
|
|
|
@app.template_filter('from_atomic_wow')
|
|
def from_atomic(v):
|
|
from app.library.crypto import wownero
|
|
return wownero.as_real(wownero.from_atomic(v))
|
|
|
|
@app.template_filter('from_atomic_xmr')
|
|
def from_atomic(v):
|
|
from app.library.crypto import monero
|
|
return monero.as_real(monero.from_atomic(v))
|
|
|
|
@app.template_filter('from_atomic_usd')
|
|
def to_ausd(v):
|
|
from app.models import Swap
|
|
return Swap().from_ausd(v)
|
|
|
|
@app.template_filter('ts')
|
|
def from_ts(v):
|
|
from datetime import datetime
|
|
return datetime.fromtimestamp(v)
|
|
|
|
@app.template_filter('wow_block_explorer')
|
|
def wow_block_explorer(v):
|
|
return f'https://wownero.club/transaction/{v}'
|
|
|
|
@app.template_filter('xmr_block_explorer')
|
|
def xmr_block_explorer(v):
|
|
return f'https://www.exploremonero.com/transaction/{v}'
|
|
|
|
# todo - find a way to move into a new file
|
|
# CLI
|
|
@app.cli.command('init')
|
|
def init():
|
|
import app.models
|
|
db.create_all()
|
|
|
|
@app.cli.command('store')
|
|
def store():
|
|
from app.library.crypto import wow_wallet, xmr_wallet
|
|
a, b = wow_wallet.make_wallet_rpc('store'), xmr_wallet.make_wallet_rpc('store')
|
|
return (a, b)
|
|
|
|
@app.cli.command('show')
|
|
@click.argument('swap_id')
|
|
def show_swap(swap_id):
|
|
from pprint import pprint
|
|
from app.models import Swap
|
|
s = Swap.query.get(swap_id)
|
|
if s:
|
|
pprint(s.show_details())
|
|
else:
|
|
print('Swap ID does not exist')
|
|
|
|
@app.cli.command('delete')
|
|
@click.argument('swap_id')
|
|
def show_swap(swap_id):
|
|
from app.models import Swap
|
|
s = Swap.query.get(swap_id)
|
|
if s:
|
|
print(s.show_details())
|
|
db.session.delete(s)
|
|
db.session.commit()
|
|
print(f'Swap {s.id} was deleted')
|
|
post_webhook(f'Deleted swap {s.id} from console')
|
|
else:
|
|
print('Swap ID does not exist')
|
|
|
|
@app.cli.command('list')
|
|
def list_swaps():
|
|
from app.models import Swap
|
|
s = Swap.query.all()
|
|
for i in s:
|
|
print(i.id)
|
|
|
|
@app.cli.command('wow_transfer')
|
|
@click.argument('address')
|
|
@click.argument('amount')
|
|
def wow_transfer(address, amount):
|
|
from app.library.crypto import wow_wallet, wownero
|
|
tx = wow_wallet.transfer(address, wownero.to_atomic(wownero.as_real(amount)))
|
|
post_webhook(f'Sent {amount} WOW to `{address}` from console. Tx ID: `{tx["tx_hash"]}`')
|
|
print(tx)
|
|
|
|
@app.cli.command('xmr_transfer')
|
|
@click.argument('address')
|
|
@click.argument('amount')
|
|
def xmr_transfer(address, amount):
|
|
from app.library.crypto import xmr_wallet, monero
|
|
tx = xmr_wallet.transfer(address, monero.to_atomic(monero.as_real(amount)))
|
|
post_webhook(f'Sent {amount} XMR to `{address}` from console. Tx ID: `{tx["tx_hash"]}`')
|
|
print(tx)
|
|
|
|
@app.cli.command('process_expired')
|
|
def process_expired():
|
|
from app.models import Swap
|
|
_hours = config.SWAP_EXPIRATION_HOURS
|
|
for swap in Swap.query.filter(Swap.funds_received == False):
|
|
elapsed = swap.hours_elapsed()
|
|
details = swap.show_details()
|
|
if elapsed >= _hours:
|
|
db.session.delete(swap)
|
|
db.session.commit()
|
|
print(f'Swap {swap.id} has not received funds in over {_hours} hours ({elapsed} hours) and was deleted: {details}')
|
|
post_webhook(f'Deleted swap {swap.id}; {round(elapsed)} hours old with no incoming transfers.')
|
|
sleep(5)
|
|
|
|
@app.cli.command('process_incoming')
|
|
def process_incoming():
|
|
from app.library.crypto import wow_wallet, xmr_wallet
|
|
from app.models import Swap
|
|
for swap in Swap.query.filter(Swap.funds_received == False):
|
|
swap_tx_amounts = list()
|
|
if swap.wow_to_xmr:
|
|
incoming_wallet = wow_wallet
|
|
else:
|
|
incoming_wallet = xmr_wallet
|
|
txes = incoming_wallet.incoming_transfers(swap.swap_address_index)
|
|
if 'transfers' in txes:
|
|
for tx in txes['transfers']:
|
|
if tx['unlocked']:
|
|
swap_tx_amounts.append(tx['amount'])
|
|
amount_expected = swap.receive_amount_atomic
|
|
amount_received = sum(swap_tx_amounts)
|
|
if amount_received >= amount_expected:
|
|
url = url_for('swap.get_swap', swap_id=swap.id, _external=True)
|
|
msg = f'Funds received for swap [{swap.id}]({url}) ({swap.show_details()["receive"]})'
|
|
swap.funds_received = True
|
|
db.session.add(swap)
|
|
db.session.commit()
|
|
print(msg)
|
|
post_webhook(msg)
|
|
|
|
@app.cli.command('process_outgoing')
|
|
def process_outgoing():
|
|
from datetime import datetime
|
|
from app.library.crypto import wow_wallet, xmr_wallet, wownero, monero
|
|
from app.models import Swap
|
|
from time import sleep
|
|
|
|
for swap in Swap.query.filter(Swap.funds_received == True, Swap.completed == False):
|
|
print(f'Planning to send {swap.show_details()["send"]} for swap {swap.id}')
|
|
sleep(10)
|
|
if swap.wow_to_xmr:
|
|
outgoing_wallet = xmr_wallet
|
|
dest_address = swap.return_xmr_address
|
|
crypto = monero
|
|
else:
|
|
outgoing_wallet = wow_wallet
|
|
dest_address = swap.return_wow_address
|
|
crypto = wownero
|
|
available_funds = outgoing_wallet.balances()[0]
|
|
if available_funds > swap.send_amount_atomic:
|
|
res = outgoing_wallet.transfer(dest_address, swap.send_amount_atomic)
|
|
if not 'message' in res:
|
|
url = url_for('swap.get_swap', swap_id=swap.id, _external=True)
|
|
res['address'] = dest_address
|
|
msg = 'Sent {amount} {coin} to return address for swap [{id}]({url}): `{tx_id}` - `{res}`'.format(
|
|
amount=crypto.from_atomic(swap.send_amount_atomic),
|
|
coin=swap.send_coin().upper(),
|
|
id=swap.id,
|
|
tx_id=res['tx_hash'],
|
|
url=url,
|
|
res=res
|
|
)
|
|
print(msg)
|
|
post_webhook(msg)
|
|
swap.completed = True
|
|
swap.completed_date = datetime.utcnow()
|
|
swap.send_tx_id = res['tx_hash']
|
|
swap.funds_sent = True
|
|
db.session.add(swap)
|
|
db.session.commit()
|
|
else:
|
|
print('There was an error sending funds for swap {id}: {msg}'.format(
|
|
id=swap.id,
|
|
msg=res['message']
|
|
))
|
|
if not isfile(f'/tmp/{swap.id}'):
|
|
post_webhook(f'Unable to send funds for swap {swap.id}: {res["message"]}')
|
|
with open(f'/tmp/{swap.id}', 'w') as f:
|
|
f.write(swap.id)
|
|
else:
|
|
print('Not enough funds to transfer for swap {id}. Available: {avail}, Required: {req}'.format(
|
|
id=swap.id,
|
|
avail=crypto.from_atomic(available_funds),
|
|
req=crypto.from_atomic(swap.send_amount_atomic)
|
|
))
|
|
if not isfile(f'/tmp/{swap.id}'):
|
|
post_webhook(f'Unable to send funds for swap {swap.id}: Not enough')
|
|
with open(f'/tmp/{swap.id}', 'w') as f:
|
|
f.write(swap.id)
|
|
|
|
# Routes/blueprints
|
|
from app.routes import meta, swap, api
|
|
app.register_blueprint(meta.bp)
|
|
app.register_blueprint(swap.bp)
|
|
app.register_blueprint(api.bp)
|
|
|
|
return app
|