Thornode.get_quote(): get data via remote RPC interface

This commit is contained in:
The MMGen Project 2026-10-07 16:40:39 +00:00
commit 8e75f9f23d
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
6 changed files with 36 additions and 38 deletions

View file

@ -11,6 +11,7 @@ proto.rune.rpc.remote: THORChain base protocol remote RPC client for the MMGen P
"""
import json
from decimal import Decimal
from ....http import RemoteJSONClient
from ....rpc.remote import RemoteRPCClient
@ -38,6 +39,10 @@ class RESTClient(RemoteJSONClient): # same endpoint as RPCClient for test suite
params = 'rpc_remote_rest_params'
timeout = 15
class SwapClient(RemoteJSONClient): # same endpoint as RESTClient for mainnet
params = 'rpc_remote_swap_params'
timeout = 15
class MidgardClient(RemoteJSONClient):
params = 'rpc_remote_midgard_params'
timeout = 30
@ -46,6 +51,7 @@ class THORChainRemoteRPCClient(RemoteRPCClient):
"retrieve data from a remote THORChain JSON-RPC endpoint"
server_proto = 'THORChain'
swap_proto = 'thorchain'
mods = {
'get': (
'ping',
@ -66,6 +72,7 @@ class THORChainRemoteRPCClient(RemoteRPCClient):
self.caps = ('lbl_id',)
self.rest_api = RESTClient(cfg, proto)
self.rpc_api = RPCClient(cfg, proto)
self.swap_api = SwapClient(cfg, proto)
self.midgard_api = MidgardClient(cfg, proto)
@staticmethod
@ -131,6 +138,18 @@ class THORChainRemoteRPCClient(RemoteRPCClient):
assert len(rune_res) == 1, f'{rune_res}: result length is not one!'
return self.proto.coin_amt(int(rune_res[0]['amount']), from_unit='satoshi')
def get_quote(self, from_asset, to_asset, amt, *, amt_is_atomic=False):
from ....swap.util import get_swap_proto_mod
swap_cfg = get_swap_proto_mod(self.swap_proto).SwapCfg(self.cfg)
qs = make_query_string({
'from_asset': from_asset.full_name,
'to_asset': to_asset.full_name,
'amount': amt if amt_is_atomic else amt.to_unit('atomic'),
'streaming_interval': swap_cfg.stream_interval})
return process_response(
self.swap_api.get(path=f'/thorchain/quote/swap?{qs}'),
errmsg = 'swap quote request failed')
def tx_info(self, txid: str):
"get information for a transaction by TxID"
return process_response(

View file

@ -10,12 +10,12 @@
swap.proto.thorchain.thornode: THORChain swap protocol network query ops
"""
import time, json
import time
from collections import namedtuple
from ....protocol import init_proto
from ....amt import UniAmt
from ....http import RemoteJSONClient
from ....rpc import get_remote_rpc
_gd = namedtuple('gas_unit_data', ['code', 'disp'])
gas_unit_data = {
@ -23,54 +23,33 @@ gas_unit_data = {
'gwei': _gd('G', 'Gwei'),
}
class ThornodeSwapClient(RemoteJSONClient):
params = 'rpc_remote_swap_params'
timeout = 5
class Thornode:
def __init__(self, tx, amt):
self.tx = tx
self.in_amt = UniAmt(f'{amt:.8f}')
self.rpc = ThornodeSwapClient(tx.cfg, init_proto(tx.cfg, 'rune'))
self.rpc = get_remote_rpc(tx.cfg, init_proto(tx.cfg, 'rune'))
def get_quote(self, swap_cfg):
def get_data(send, recv, amt):
get_str = (
'/thorchain/quote/swap?'
f'from_asset={send}&'
f'to_asset={recv}&'
f'amount={amt}&'
f'streaming_interval={swap_cfg.stream_interval}')
data = json.loads(self.rpc.get(path=get_str))
if not 'expiry' in data:
from ....util import pp_fmt, die
die(2, pp_fmt(data))
return data
if (
(self.tx.proto.tokensym or self.tx.recv_asset.tokensym)
and self.tx.send_asset.chain != 'THOR'): # token swap
in_data = get_data(
self.tx.send_asset.full_name,
'THOR.RUNE',
self.in_amt.to_unit('satoshi'))
from ....swap.util import get_swap_asset
rune_asset = get_swap_asset(self.tx.cfg, 'RUNE')
in_data = self.rpc.get_quote(self.tx.send_asset, rune_asset, self.in_amt)
if self.tx.proto.network != 'regtest':
time.sleep(1.1) # ninerealms max request rate 1/sec
out_data = get_data(
'THOR.RUNE',
self.tx.recv_asset.full_name,
in_data['expected_amount_out'])
out_data = self.rpc.get_quote(
rune_asset,
self.tx.recv_asset,
in_data['expected_amount_out'],
amt_is_atomic = True)
self.data = in_data | {
'expected_amount_out': out_data['expected_amount_out'],
'fees': out_data['fees'],
'expiry': min(in_data['expiry'], out_data['expiry'])}
else:
self.data = get_data(
self.tx.send_asset.full_name,
self.tx.recv_asset.full_name,
self.in_amt.to_unit('satoshi'))
self.data = self.rpc.get_quote(self.tx.send_asset, self.tx.recv_asset, self.in_amt)
async def format_quote(self, trade_limit, *, deduct_est_fee=False):
from ....util import make_timestr, ymsg
@ -131,7 +110,7 @@ class Thornode:
fees_t = UniAmt(int(fees['total']), from_unit='satoshi')
fees_pct_disp = str(fees['total_bps'] / 100) + '%'
slip_pct_disp = str(fees['slippage_bps'] / 100) + '%'
hdr = f'SWAP QUOTE (source: {self.rpc.host})'
hdr = f'SWAP QUOTE (source: {self.rpc.swap_api.host})'
vault_info = '' if tx.send_asset.chain == 'THOR' else """
Vault address: {}""".format(cyan(self.inbound_address))

View file

@ -197,7 +197,7 @@ class NewSwap(New):
return UniAmt(int(c.data['expected_amount_out']), from_unit='satoshi') * x
while True:
self.cfg._util.qmsg(f'Retrieving data from {c.rpc.host}...')
self.cfg._util.qmsg(f'Retrieving data from {c.rpc.swap_api.host}...')
c.get_quote(self.swap_cfg)
self.cfg._util.qmsg('OK')
self.swap_quote_refresh_time = time.time()

View file

@ -117,7 +117,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
networks = ('eth',)
tmpdir_nums = [42]
thornode_servers = ('swap',)
thornode_servers = ('rpc', 'swap')
dfl_devnet_block_period = {'geth': 7, 'reth': 9}
fund_amt = 100000
token_fund_amt = 1000

View file

@ -121,7 +121,7 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest):
bdb_wallet = True
tmpdir_nums = [47]
networks = ('btc',)
thornode_servers = ('swap',)
thornode_servers = ('rpc', 'swap')
passthru_opts = ('coin', 'rpc_backend', 'eth_daemon_id')
cross_group = 'ethswap_eth'
cross_coin = 'eth'

View file

@ -19,7 +19,7 @@ class CmdTestRemote(CmdTestBase, CmdTestSwapMethods, CmdTestRuneMethods):
networks = ('btc',)
tmpdir_nums = [37]
thornode_servers = ('rpc', 'midgard')
thornode_servers = ('rpc', 'midgard', 'swap')
cmd_group = (
('ping', 'pinging remote REST server'),