test suite: ThornodeServer -> ThornodeSwapServer
This commit is contained in:
parent
ad6bd3cff7
commit
800b3cef36
5 changed files with 186 additions and 168 deletions
|
|
@ -22,14 +22,14 @@ from ..include.common import imsg, omsg_r
|
|||
|
||||
from .include.common import cleanup_env, dfl_words_file, dfl_sid
|
||||
from .include.runner import CmdTestRunner
|
||||
from .httpd.thornode import ThornodeServer
|
||||
from .httpd.thornode_swap import ThornodeSwapServer
|
||||
|
||||
from .ethdev import CmdTestEthdev, CmdTestEthdevMethods
|
||||
from .regtest import CmdTestRegtest
|
||||
from .swap import CmdTestSwapMethods
|
||||
from .ethswap import CmdTestEthSwapMethods
|
||||
|
||||
thornode_server = ThornodeServer()
|
||||
swap_server = ThornodeSwapServer()
|
||||
burn_addr = 'beefcafe22' * 4
|
||||
method_template = """
|
||||
def {name}(self):
|
||||
|
|
@ -292,7 +292,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
|
|||
ethbump_ltc = CmdTestRunner(cfg, t.repo_root, t.data_dir, t.trash_dir, t.trash_dir2)
|
||||
ethbump_ltc.init_group('ethbump_ltc')
|
||||
|
||||
thornode_server.start()
|
||||
swap_server.start()
|
||||
|
||||
def txcreate1(self):
|
||||
return self._txcreate(args=[f'{burn_addr},987'], acct='1')
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ from ..include.common import imsg, chk_equal
|
|||
|
||||
from .include.runner import CmdTestRunner
|
||||
from .include.common import dfl_sid, eth_inbound_addr, thorchain_router_addr_file
|
||||
from .httpd.thornode import ThornodeServer
|
||||
from .httpd.thornode_swap import ThornodeSwapServer
|
||||
|
||||
from .regtest import CmdTestRegtest
|
||||
from .swap import CmdTestSwapMethods
|
||||
from .ethdev import CmdTestEthdev
|
||||
|
||||
thornode_server = ThornodeServer()
|
||||
swap_server = ThornodeSwapServer()
|
||||
|
||||
method_template = """
|
||||
def {name}(self):
|
||||
|
|
@ -150,7 +150,7 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest):
|
|||
('subgroup.eth_token_swap', ['fund', 'token_init']),
|
||||
('stop', 'stopping regtest daemon'),
|
||||
('eth_stop', 'stopping Ethereum daemon'),
|
||||
('thornode_server_stop', 'stopping the Thornode server'),
|
||||
('swap_server_stop', 'stopping the Thornode server'),
|
||||
)
|
||||
cmd_subgroups = {
|
||||
'init': (
|
||||
|
|
@ -281,7 +281,7 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest):
|
|||
ethswap_eth = CmdTestRunner(cfg, t.repo_root, t.data_dir, t.trash_dir, t.trash_dir2)
|
||||
ethswap_eth.init_group(self.eth_group)
|
||||
|
||||
thornode_server.start()
|
||||
swap_server.start()
|
||||
|
||||
def swaptxcreate1(self):
|
||||
t = self._swaptxcreate(['BTC', '8.765', 'ETH'])
|
||||
|
|
@ -326,13 +326,13 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest):
|
|||
t.expect('OK? (Y/n): ', 'y')
|
||||
return self._swaptxcreate_ui_common(t)
|
||||
|
||||
def thornode_server_stop(self):
|
||||
def swap_server_stop(self):
|
||||
self.spawn(msg_only=True)
|
||||
if self.cfg.no_daemon_stop:
|
||||
msg_r('(leaving thornode server running by user request)')
|
||||
imsg('')
|
||||
else:
|
||||
thornode_server.stop()
|
||||
swap_server.stop()
|
||||
return 'ok'
|
||||
|
||||
class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev):
|
||||
|
|
|
|||
|
|
@ -12,162 +12,9 @@
|
|||
test.cmdtest_d.httpd.thornode: Thornode WSGI http server
|
||||
"""
|
||||
|
||||
import time, re, json
|
||||
|
||||
from mmgen.cfg import Config
|
||||
from mmgen.amt import UniAmt
|
||||
from mmgen.protocol import init_proto
|
||||
|
||||
from ..include.common import eth_inbound_addr, thorchain_router_addr_file
|
||||
|
||||
from . import HTTPD
|
||||
|
||||
cfg = Config()
|
||||
|
||||
# https://thornode.ninerealms.com/thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000
|
||||
sample_request = 'GET /thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000000'
|
||||
request_pat = r'/thorchain/quote/swap\?from_asset=(\S+)\.(\S+)&to_asset=(\S+)\.(\S+)&amount=(\d+)'
|
||||
prices = {'BTC': 97000, 'LTC': 115, 'BCH': 330, 'ETH': 2304, 'MM1': 0.998, 'RUNE': 1.4}
|
||||
gas_rate_units = {'ETH': 'gwei', 'BTC': 'satsperbyte'}
|
||||
recommended_gas_rate = {'ETH': '1', 'BTC': '6'}
|
||||
|
||||
data_template_from_rune = {
|
||||
'outbound_delay_blocks': 0,
|
||||
'outbound_delay_seconds': 0,
|
||||
'fees': {
|
||||
'asset': 'BTC.BTC',
|
||||
'affiliate': '0',
|
||||
'outbound': '1182',
|
||||
'liquidity': '110',
|
||||
'total': '1292',
|
||||
'slippage_bps': 7,
|
||||
'total_bps': 92
|
||||
},
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'Broadcast a MsgDeposit to the THORChain network with the appropriate memo. Do not use multi-in, multi-out transactions.',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0
|
||||
}
|
||||
|
||||
data_template_to_rune = {
|
||||
'inbound_confirmation_blocks': 2,
|
||||
'inbound_confirmation_seconds': 24,
|
||||
'outbound_delay_blocks': 0,
|
||||
'outbound_delay_seconds': 0,
|
||||
'fees': {
|
||||
'asset': 'THOR.RUNE',
|
||||
'affiliate': '0',
|
||||
'outbound': '2000000',
|
||||
'liquidity': '684966',
|
||||
'total': '2684966',
|
||||
'slippage_bps': 8,
|
||||
'total_bps': 31
|
||||
},
|
||||
'router': '0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146',
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'Base Asset: Send the inbound_address the asset with the memo encoded in hex in the data field. Tokens: First approve router to spend tokens from user: asset.approve(router, amount). Then call router.depositWithExpiry(inbound_address, asset, amount, memo, expiry). Asset is the token contract address. Amount should be in native asset decimals (eg 1e18 for most tokens). Do not swap to smart contract addresses.',
|
||||
'dust_threshold': '1',
|
||||
'recommended_gas_rate': '1',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0,
|
||||
'total_swap_seconds': 24
|
||||
}
|
||||
|
||||
data_template_btc = {
|
||||
'inbound_confirmation_blocks': 4,
|
||||
'inbound_confirmation_seconds': 2400,
|
||||
'outbound_delay_blocks': 5,
|
||||
'outbound_delay_seconds': 30,
|
||||
'fees': {
|
||||
'asset': 'LTC.LTC',
|
||||
'affiliate': '0',
|
||||
'outbound': '878656',
|
||||
'liquidity': '8945012',
|
||||
'total': '9823668',
|
||||
'slippage_bps': 31,
|
||||
'total_bps': 34
|
||||
},
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'First output should be to inbound_address, second output should be change back to self, third output should be OP_RETURN, limited to 80 bytes. Do not send below the dust threshold. Do not use exotic spend scripts, locks or address formats.',
|
||||
'dust_threshold': '10000',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0,
|
||||
'total_swap_seconds': 2430
|
||||
}
|
||||
|
||||
data_template_eth = {
|
||||
'inbound_confirmation_blocks': 2,
|
||||
'inbound_confirmation_seconds': 24,
|
||||
'outbound_delay_blocks': 0,
|
||||
'outbound_delay_seconds': 0,
|
||||
'fees': {
|
||||
'asset': 'BTC.BTC',
|
||||
'affiliate': '0',
|
||||
'outbound': '1097',
|
||||
'liquidity': '77',
|
||||
'total': '1174',
|
||||
'slippage_bps': 15,
|
||||
'total_bps': 237
|
||||
},
|
||||
'router': '0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146',
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'Base Asset: Send the inbound_address the asset with the memo encoded in hex in the data field. Tokens: First approve router to spend tokens from user: asset.approve(router, amount). Then call router.depositWithExpiry(inbound_address, asset, amount, memo, expiry). Asset is the token contract address. Amount should be in native asset decimals (eg 1e18 for most tokens). Do not swap to smart contract addresses.',
|
||||
'recommended_gas_rate': '1',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0,
|
||||
'total_swap_seconds': 24
|
||||
}
|
||||
|
||||
def make_inbound_addr(proto, mmtype):
|
||||
if proto.is_evm:
|
||||
return '0x' + eth_inbound_addr # non-checksummed as per ninerealms thornode
|
||||
else:
|
||||
from mmgen.tool.coin import tool_cmd
|
||||
n = int(time.time()) // (60 * 60 * 24) # increments once every 24 hrs
|
||||
return tool_cmd(
|
||||
cfg = cfg,
|
||||
cmdname = 'pubhash2addr',
|
||||
proto = proto,
|
||||
mmtype = mmtype).pubhash2addr(f'{n:040x}')
|
||||
|
||||
class ThornodeServer(HTTPD):
|
||||
name = 'thornode server'
|
||||
port = 18800
|
||||
content_type = 'application/json'
|
||||
|
||||
def make_response_body(self, method, environ):
|
||||
from wsgiref.util import request_uri
|
||||
|
||||
m = re.search(request_pat, request_uri(environ))
|
||||
send_chain, send_asset, recv_chain, recv_asset, amt_atomic = m.groups()
|
||||
|
||||
in_amt = UniAmt(int(amt_atomic), from_unit='satoshi')
|
||||
out_amt = in_amt * (prices[send_asset] / prices[recv_asset])
|
||||
|
||||
data_template = (
|
||||
data_template_from_rune if send_asset == 'RUNE' else
|
||||
data_template_to_rune if recv_asset == 'RUNE' else
|
||||
data_template_eth if send_asset == 'ETH' else
|
||||
data_template_btc)
|
||||
|
||||
data = data_template | {
|
||||
'recommended_min_amount_in': str(int(70 * 10**8 / prices[send_asset])), # $70
|
||||
'expected_amount_out': str(out_amt.to_unit('satoshi')),
|
||||
'expiry': int(time.time()) + (10 * 60),
|
||||
}
|
||||
|
||||
if send_asset != 'RUNE':
|
||||
send_proto = init_proto(cfg, send_chain, network='regtest', need_amt=True)
|
||||
data.update({
|
||||
'inbound_address': make_inbound_addr(send_proto, send_proto.preferred_mmtypes[0]),
|
||||
'gas_rate_units': gas_rate_units[send_proto.base_proto_coin],
|
||||
'recommended_gas_rate': recommended_gas_rate[send_proto.base_proto_coin]
|
||||
})
|
||||
|
||||
if send_asset == 'MM1':
|
||||
eth_proto = init_proto(cfg, 'eth', network='regtest')
|
||||
with open(thorchain_router_addr_file) as fh:
|
||||
raw_addr = fh.read().strip()
|
||||
data['router'] = '0x' + eth_proto.checksummed_addr(raw_addr)
|
||||
|
||||
return json.dumps(data).encode()
|
||||
|
|
|
|||
171
test/cmdtest_d/httpd/thornode_swap.py
Executable file
171
test/cmdtest_d/httpd/thornode_swap.py
Executable file
|
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# MMGen Wallet, a terminal-based cryptocurrency wallet
|
||||
# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
|
||||
# Licensed under the GNU General Public License, Version 3:
|
||||
# https://www.gnu.org/licenses
|
||||
# Public project repositories:
|
||||
# https://github.com/mmgen/mmgen-wallet
|
||||
# https://gitlab.com/mmgen/mmgen-wallet
|
||||
|
||||
"""
|
||||
test.cmdtest_d.httpd.thornode_swap: Thornode swap WSGI http server
|
||||
"""
|
||||
|
||||
import time, re, json
|
||||
from wsgiref.util import request_uri
|
||||
|
||||
from mmgen.cfg import Config
|
||||
from mmgen.amt import UniAmt
|
||||
from mmgen.protocol import init_proto
|
||||
|
||||
from ..include.common import eth_inbound_addr, thorchain_router_addr_file
|
||||
|
||||
from . import HTTPD
|
||||
from .thornode import ThornodeServer
|
||||
|
||||
cfg = Config()
|
||||
|
||||
# https://thornode.ninerealms.com/thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000
|
||||
prices = {'BTC': 97000, 'LTC': 115, 'BCH': 330, 'ETH': 2304, 'MM1': 0.998, 'RUNE': 1.4}
|
||||
gas_rate_units = {'ETH': 'gwei', 'BTC': 'satsperbyte'}
|
||||
recommended_gas_rate = {'ETH': '1', 'BTC': '6'}
|
||||
|
||||
data_template_from_rune = {
|
||||
'outbound_delay_blocks': 0,
|
||||
'outbound_delay_seconds': 0,
|
||||
'fees': {
|
||||
'asset': 'BTC.BTC',
|
||||
'affiliate': '0',
|
||||
'outbound': '1182',
|
||||
'liquidity': '110',
|
||||
'total': '1292',
|
||||
'slippage_bps': 7,
|
||||
'total_bps': 92
|
||||
},
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'Broadcast a MsgDeposit to the THORChain network with the appropriate memo. Do not use multi-in, multi-out transactions.',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0
|
||||
}
|
||||
|
||||
data_template_to_rune = {
|
||||
'inbound_confirmation_blocks': 2,
|
||||
'inbound_confirmation_seconds': 24,
|
||||
'outbound_delay_blocks': 0,
|
||||
'outbound_delay_seconds': 0,
|
||||
'fees': {
|
||||
'asset': 'THOR.RUNE',
|
||||
'affiliate': '0',
|
||||
'outbound': '2000000',
|
||||
'liquidity': '684966',
|
||||
'total': '2684966',
|
||||
'slippage_bps': 8,
|
||||
'total_bps': 31
|
||||
},
|
||||
'router': '0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146',
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'Base Asset: Send the inbound_address the asset with the memo encoded in hex in the data field. Tokens: First approve router to spend tokens from user: asset.approve(router, amount). Then call router.depositWithExpiry(inbound_address, asset, amount, memo, expiry). Asset is the token contract address. Amount should be in native asset decimals (eg 1e18 for most tokens). Do not swap to smart contract addresses.',
|
||||
'dust_threshold': '1',
|
||||
'recommended_gas_rate': '1',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0,
|
||||
'total_swap_seconds': 24
|
||||
}
|
||||
|
||||
data_template_btc = {
|
||||
'inbound_confirmation_blocks': 4,
|
||||
'inbound_confirmation_seconds': 2400,
|
||||
'outbound_delay_blocks': 5,
|
||||
'outbound_delay_seconds': 30,
|
||||
'fees': {
|
||||
'asset': 'LTC.LTC',
|
||||
'affiliate': '0',
|
||||
'outbound': '878656',
|
||||
'liquidity': '8945012',
|
||||
'total': '9823668',
|
||||
'slippage_bps': 31,
|
||||
'total_bps': 34
|
||||
},
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'First output should be to inbound_address, second output should be change back to self, third output should be OP_RETURN, limited to 80 bytes. Do not send below the dust threshold. Do not use exotic spend scripts, locks or address formats.',
|
||||
'dust_threshold': '10000',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0,
|
||||
'total_swap_seconds': 2430
|
||||
}
|
||||
|
||||
data_template_eth = {
|
||||
'inbound_confirmation_blocks': 2,
|
||||
'inbound_confirmation_seconds': 24,
|
||||
'outbound_delay_blocks': 0,
|
||||
'outbound_delay_seconds': 0,
|
||||
'fees': {
|
||||
'asset': 'BTC.BTC',
|
||||
'affiliate': '0',
|
||||
'outbound': '1097',
|
||||
'liquidity': '77',
|
||||
'total': '1174',
|
||||
'slippage_bps': 15,
|
||||
'total_bps': 237
|
||||
},
|
||||
'router': '0xD37BbE5744D730a1d98d8DC97c42F0Ca46aD7146',
|
||||
'warning': 'Do not cache this response. Do not send funds after the expiry.',
|
||||
'notes': 'Base Asset: Send the inbound_address the asset with the memo encoded in hex in the data field. Tokens: First approve router to spend tokens from user: asset.approve(router, amount). Then call router.depositWithExpiry(inbound_address, asset, amount, memo, expiry). Asset is the token contract address. Amount should be in native asset decimals (eg 1e18 for most tokens). Do not swap to smart contract addresses.',
|
||||
'recommended_gas_rate': '1',
|
||||
'max_streaming_quantity': 0,
|
||||
'streaming_swap_blocks': 0,
|
||||
'total_swap_seconds': 24
|
||||
}
|
||||
|
||||
def make_inbound_addr(proto, mmtype):
|
||||
if proto.is_evm:
|
||||
return '0x' + eth_inbound_addr # non-checksummed as per ninerealms thornode
|
||||
else:
|
||||
from mmgen.tool.coin import tool_cmd
|
||||
n = int(time.time()) // (60 * 60 * 24) # increments once every 24 hrs
|
||||
return tool_cmd(
|
||||
cfg = cfg,
|
||||
cmdname = 'pubhash2addr',
|
||||
proto = proto,
|
||||
mmtype = mmtype).pubhash2addr(f'{n:040x}')
|
||||
|
||||
class ThornodeSwapServer(ThornodeServer):
|
||||
name = 'thornode swap server'
|
||||
request_pat = r'/thorchain/quote/swap\?from_asset=(\S+)\.(\S+)&to_asset=(\S+)\.(\S+)&amount=(\d+)'
|
||||
|
||||
def make_response_body(self, method, environ):
|
||||
|
||||
m = re.search(self.request_pat, request_uri(environ))
|
||||
send_chain, send_asset, recv_chain, recv_asset, amt_atomic = m.groups()
|
||||
|
||||
in_amt = UniAmt(int(amt_atomic), from_unit='satoshi')
|
||||
out_amt = in_amt * (prices[send_asset] / prices[recv_asset])
|
||||
|
||||
data_template = (
|
||||
data_template_from_rune if send_asset == 'RUNE' else
|
||||
data_template_to_rune if recv_asset == 'RUNE' else
|
||||
data_template_eth if send_asset == 'ETH' else
|
||||
data_template_btc)
|
||||
|
||||
data = data_template | {
|
||||
'recommended_min_amount_in': str(int(70 * 10**8 / prices[send_asset])), # $70
|
||||
'expected_amount_out': str(out_amt.to_unit('satoshi')),
|
||||
'expiry': int(time.time()) + (10 * 60),
|
||||
}
|
||||
|
||||
if send_asset != 'RUNE':
|
||||
send_proto = init_proto(cfg, send_chain, network='regtest', need_amt=True)
|
||||
data.update({
|
||||
'inbound_address': make_inbound_addr(send_proto, send_proto.preferred_mmtypes[0]),
|
||||
'gas_rate_units': gas_rate_units[send_proto.base_proto_coin],
|
||||
'recommended_gas_rate': recommended_gas_rate[send_proto.base_proto_coin]
|
||||
})
|
||||
|
||||
if send_asset == 'MM1':
|
||||
eth_proto = init_proto(cfg, 'eth', network='regtest')
|
||||
with open(thorchain_router_addr_file) as fh:
|
||||
raw_addr = fh.read().strip()
|
||||
data['router'] = '0x' + eth_proto.checksummed_addr(raw_addr)
|
||||
|
||||
return json.dumps(data).encode()
|
||||
|
|
@ -19,12 +19,12 @@ from mmgen.wallet.mmgen import wallet as MMGenWallet
|
|||
|
||||
from ..include.common import make_burn_addr, gr_uc
|
||||
from .include.common import dfl_bip39_file, dfl_words_file
|
||||
from .httpd.thornode import ThornodeServer
|
||||
from .httpd.thornode_swap import ThornodeSwapServer
|
||||
|
||||
from .autosign import CmdTestAutosign, CmdTestAutosignThreaded
|
||||
from .regtest import CmdTestRegtest, rt_data, dfl_wcls, rt_pw, strip_ansi_escapes
|
||||
|
||||
thornode_server = ThornodeServer()
|
||||
swap_server = ThornodeSwapServer()
|
||||
|
||||
sample1 = gr_uc[:24]
|
||||
sample2 = '00010203040506'
|
||||
|
|
@ -280,7 +280,7 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded):
|
|||
('subgroup.signsend', ['init_swap']),
|
||||
('subgroup.signsend_bad', ['init_swap']),
|
||||
('subgroup.autosign', ['init_data', 'signsend']),
|
||||
('thornode_server_stop', 'stopping the Thornode server'),
|
||||
('swap_server_stop', 'stopping the Thornode server'),
|
||||
('stop', 'stopping regtest daemons'),
|
||||
)
|
||||
cmd_subgroups = {
|
||||
|
|
@ -408,7 +408,7 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded):
|
|||
|
||||
self.protos = [init_proto(cfg, k, network='regtest', need_amt=True) for k in ('btc', 'ltc', 'bch')]
|
||||
|
||||
thornode_server.start()
|
||||
swap_server.start()
|
||||
|
||||
self.opts.append('--bob')
|
||||
|
||||
|
|
@ -776,7 +776,7 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded):
|
|||
def mempool2(self):
|
||||
return self._mempool(2)
|
||||
|
||||
def thornode_server_stop(self):
|
||||
def swap_server_stop(self):
|
||||
self.spawn(msg_only=True)
|
||||
thornode_server.stop()
|
||||
swap_server.stop()
|
||||
return 'ok'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue