mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-05 05:15:12 +00:00
swaps: move fee logic to swap_manager, fix command line
This commit is contained in:
parent
3874f7ec77
commit
5fa09970b6
3 changed files with 119 additions and 91 deletions
|
@ -79,6 +79,8 @@ def satoshis(amount):
|
|||
# satoshi conversion must not be performed by the parser
|
||||
return int(COIN*Decimal(amount)) if amount not in ['!', None] else amount
|
||||
|
||||
def format_satoshis(x):
|
||||
return str(Decimal(x)/COIN) if x is not None else None
|
||||
|
||||
def json_normalize(x):
|
||||
# note: The return value of commands, when going through the JSON-RPC interface,
|
||||
|
@ -1102,12 +1104,56 @@ class Commands:
|
|||
return await self.network.local_watchtower.sweepstore.get_ctn(channel_point, None)
|
||||
|
||||
@command('wnp')
|
||||
async def submarine_swap(self, amount, password=None, wallet: Abstract_Wallet = None):
|
||||
return await wallet.lnworker.swap_manager.normal_swap(satoshis(amount), password)
|
||||
async def normal_swap(self, onchain_amount, lightning_amount, password=None, wallet: Abstract_Wallet = None):
|
||||
"""
|
||||
Normal submarine swap: send on-chain BTC, receive on Lightning
|
||||
Note that your funds will be locked for 24h if you do not have enough incoming capacity.
|
||||
"""
|
||||
sm = wallet.lnworker.swap_manager
|
||||
if lightning_amount == 'dryrun':
|
||||
await sm.get_pairs()
|
||||
onchain_amount_sat = satoshis(onchain_amount)
|
||||
lightning_amount_sat = sm.get_recv_amount(onchain_amount_sat, is_reverse=False)
|
||||
txid = None
|
||||
elif onchain_amount == 'dryrun':
|
||||
await sm.get_pairs()
|
||||
lightning_amount_sat = satoshis(lightning_amount)
|
||||
onchain_amount_sat = sm.get_send_amount(lightning_amount_sat, is_reverse=False)
|
||||
txid = None
|
||||
else:
|
||||
lightning_amount_sat = satoshis(lightning_amount)
|
||||
onchain_amount_sat = satoshis(onchain_amount)
|
||||
txid = await wallet.lnworker.swap_manager.normal_swap(lightning_amount_sat, onchain_amount_sat, password)
|
||||
return {
|
||||
'txid': txid,
|
||||
'lightning_amount': format_satoshis(lightning_amount_sat),
|
||||
'onchain_amount': format_satoshis(onchain_amount_sat),
|
||||
}
|
||||
|
||||
@command('wn')
|
||||
async def reverse_swap(self, amount, wallet: Abstract_Wallet = None):
|
||||
return await wallet.lnworker.swap_manager.reverse_swap(satoshis(amount))
|
||||
async def reverse_swap(self, lightning_amount, onchain_amount, wallet: Abstract_Wallet = None):
|
||||
"""Reverse submarine swap: send on Lightning, receive on-chain
|
||||
"""
|
||||
sm = wallet.lnworker.swap_manager
|
||||
if onchain_amount == 'dryrun':
|
||||
await sm.get_pairs()
|
||||
lightning_amount_sat = satoshis(lightning_amount)
|
||||
onchain_amount_sat = sm.get_recv_amount(lightning_amount_sat, is_reverse=True)
|
||||
success = None
|
||||
elif lightning_amount == 'dryrun':
|
||||
await sm.get_pairs()
|
||||
onchain_amount_sat = satoshis(onchain_amount)
|
||||
lightning_amount_sat = sm.get_send_amount(onchain_amount_sat, is_reverse=True)
|
||||
success = None
|
||||
else:
|
||||
lightning_amount_sat = satoshis(lightning_amount)
|
||||
onchain_amount_sat = satoshis(onchain_amount)
|
||||
success = await wallet.lnworker.swap_manager.reverse_swap(lightning_amount_sat, onchain_amount_sat)
|
||||
return {
|
||||
'success': success,
|
||||
'lightning_amount': format_satoshis(lightning_amount_sat),
|
||||
'onchain_amount': format_satoshis(onchain_amount_sat),
|
||||
}
|
||||
|
||||
|
||||
def eval_bool(x: str) -> bool:
|
||||
|
@ -1135,6 +1181,8 @@ param_descriptions = {
|
|||
'requested_amount': 'Requested amount (in BTC).',
|
||||
'outputs': 'list of ["address", amount]',
|
||||
'redeem_script': 'redeem script (hexadecimal)',
|
||||
'lightning_amount': "Amount sent or received in a submarine swap. Set it to 'dryrun' to receive a value",
|
||||
'onchain_amount': "Amount sent or received in a submarine swap. Set it to 'dryrun' to receive a value",
|
||||
}
|
||||
|
||||
command_options = {
|
||||
|
|
|
@ -29,12 +29,6 @@ class SwapDialog(WindowModalDialog):
|
|||
self.config = window.config
|
||||
self.swap_manager = self.window.wallet.lnworker.swap_manager
|
||||
self.network = window.network
|
||||
self.normal_fee = 0
|
||||
self.lockup_fee = 0
|
||||
self.claim_fee = self.swap_manager.get_tx_fee()
|
||||
self.percentage = 0
|
||||
self.min_amount = 0
|
||||
self.max_amount = 0
|
||||
vbox = QVBoxLayout(self)
|
||||
vbox.addWidget(WWLabel('Swap lightning funds for on-chain funds if you need to increase your receiving capacity. This service is powered by the Boltz backend.'))
|
||||
self.send_amount_e = BTCAmountEdit(self.window.get_decimal_point)
|
||||
|
@ -82,8 +76,6 @@ class SwapDialog(WindowModalDialog):
|
|||
self.config.set_key('fee_level', pos, False)
|
||||
else:
|
||||
self.config.set_key('fee_per_kb', fee_rate, False)
|
||||
# read claim_fee from config
|
||||
self.claim_fee = self.swap_manager.get_tx_fee()
|
||||
if self.send_follows:
|
||||
self.on_recv_edited()
|
||||
else:
|
||||
|
@ -102,7 +94,7 @@ class SwapDialog(WindowModalDialog):
|
|||
self.send_amount_e.setStyleSheet(ColorScheme.DEFAULT.as_stylesheet())
|
||||
amount = self.send_amount_e.get_amount()
|
||||
self.recv_amount_e.follows = True
|
||||
self.recv_amount_e.setAmount(self.get_recv_amount(amount))
|
||||
self.recv_amount_e.setAmount(self.swap_manager.get_recv_amount(amount, self.is_reverse))
|
||||
self.recv_amount_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
|
||||
self.recv_amount_e.follows = False
|
||||
self.send_follows = False
|
||||
|
@ -113,72 +105,26 @@ class SwapDialog(WindowModalDialog):
|
|||
self.recv_amount_e.setStyleSheet(ColorScheme.DEFAULT.as_stylesheet())
|
||||
amount = self.recv_amount_e.get_amount()
|
||||
self.send_amount_e.follows = True
|
||||
self.send_amount_e.setAmount(self.get_send_amount(amount))
|
||||
self.send_amount_e.setAmount(self.swap_manager.get_send_amount(amount, self.is_reverse))
|
||||
self.send_amount_e.setStyleSheet(ColorScheme.BLUE.as_stylesheet())
|
||||
self.send_amount_e.follows = False
|
||||
self.send_follows = True
|
||||
|
||||
def on_pairs(self, pairs):
|
||||
fees = pairs['pairs']['BTC/BTC']['fees']
|
||||
self.percentage = fees['percentage']
|
||||
self.normal_fee = fees['minerFees']['baseAsset']['normal']
|
||||
self.lockup_fee = fees['minerFees']['baseAsset']['reverse']['lockup']
|
||||
#self.claim_fee = fees['minerFees']['baseAsset']['reverse']['claim']
|
||||
limits = pairs['pairs']['BTC/BTC']['limits']
|
||||
self.min_amount = limits['minimal']
|
||||
self.max_amount = limits['maximal']
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
sm = self.swap_manager
|
||||
self.send_button.setIcon(read_QIcon("lightning.png" if self.is_reverse else "bitcoin.png"))
|
||||
self.recv_button.setIcon(read_QIcon("lightning.png" if not self.is_reverse else "bitcoin.png"))
|
||||
fee = self.lockup_fee + self.claim_fee if self.is_reverse else self.normal_fee
|
||||
fee = sm.lockup_fee + sm.get_claim_fee() if self.is_reverse else sm.normal_fee
|
||||
self.fee_label.setText(self.window.format_amount(fee) + ' ' + self.window.base_unit())
|
||||
self.percentage_label.setText('%.2f'%self.percentage + '%')
|
||||
|
||||
def set_minimum(self):
|
||||
self.send_amount_e.setAmount(self.min_amount)
|
||||
|
||||
def set_maximum(self):
|
||||
self.send_amount_e.setAmount(self.max_amount)
|
||||
|
||||
def get_recv_amount(self, send_amount):
|
||||
if send_amount is None:
|
||||
return
|
||||
if send_amount < self.min_amount or send_amount > self.max_amount:
|
||||
return
|
||||
x = send_amount
|
||||
if self.is_reverse:
|
||||
x = int(x * (100 - self.percentage) / 100)
|
||||
x -= self.lockup_fee
|
||||
x -= self.claim_fee
|
||||
else:
|
||||
x -= self.normal_fee
|
||||
x = int(x * (100 - self.percentage) / 100)
|
||||
if x < 0:
|
||||
return
|
||||
return x
|
||||
|
||||
def get_send_amount(self, recv_amount):
|
||||
if not recv_amount:
|
||||
return
|
||||
x = recv_amount
|
||||
if self.is_reverse:
|
||||
x += self.lockup_fee
|
||||
x += self.claim_fee
|
||||
x = int(x * 100 / (100 - self.percentage)) + 1
|
||||
else:
|
||||
x = int(x * 100 / (100 - self.percentage)) + 1
|
||||
x += self.normal_fee
|
||||
return x
|
||||
self.percentage_label.setText('%.2f'%sm.percentage + '%')
|
||||
|
||||
def run(self):
|
||||
self.window.run_coroutine_from_thread(self.swap_manager.get_pairs(), self.on_pairs)
|
||||
self.window.run_coroutine_from_thread(self.swap_manager.get_pairs(), lambda x: self.update())
|
||||
if not self.exec_():
|
||||
return
|
||||
if self.is_reverse:
|
||||
lightning_amount = self.send_amount_e.get_amount()
|
||||
onchain_amount = self.recv_amount_e.get_amount() + self.claim_fee
|
||||
onchain_amount = self.recv_amount_e.get_amount() + self.swap_manager.get_claim_fee()
|
||||
coro = self.swap_manager.reverse_swap(lightning_amount, onchain_amount)
|
||||
self.window.run_coroutine_from_thread(coro)
|
||||
else:
|
||||
|
|
|
@ -96,6 +96,23 @@ def create_claim_tx(txin, witness_script, preimage, privkey:bytes, address, amou
|
|||
|
||||
class SwapManager(Logger):
|
||||
|
||||
def __init__(self, wallet: 'Abstract_Wallet', network:'Network'):
|
||||
Logger.__init__(self)
|
||||
self.normal_fee = 0
|
||||
self.lockup_fee = 0
|
||||
self.percentage = 0
|
||||
self.min_amount = 0
|
||||
self.max_amount = 0
|
||||
self.network = network
|
||||
self.wallet = wallet
|
||||
self.lnworker = wallet.lnworker
|
||||
self.lnwatcher = self.wallet.lnworker.lnwatcher
|
||||
self.swaps = self.wallet.db.get_dict('submarine_swaps')
|
||||
for swap in self.swaps.values():
|
||||
if swap.is_redeemed:
|
||||
continue
|
||||
self.add_lnwatcher_callback(swap)
|
||||
|
||||
@log_exceptions
|
||||
async def _claim_swap(self, swap):
|
||||
if not self.lnwatcher.is_up_to_date():
|
||||
|
@ -117,7 +134,7 @@ class SwapManager(Logger):
|
|||
self.lnwatcher.remove_callback(swap.lockup_address)
|
||||
swap.is_redeemed = True
|
||||
continue
|
||||
amount_sat = txin._trusted_value_sats - self.get_tx_fee()
|
||||
amount_sat = txin._trusted_value_sats - self.get_claim_fee()
|
||||
if amount_sat < dust_threshold():
|
||||
self.logger.info('utxo value below dust threshold')
|
||||
continue
|
||||
|
@ -128,21 +145,9 @@ class SwapManager(Logger):
|
|||
# save txid
|
||||
swap.spending_txid = tx.txid()
|
||||
|
||||
def get_tx_fee(self):
|
||||
def get_claim_fee(self):
|
||||
return self.lnwatcher.config.estimate_fee(136, allow_fallback_to_static_rates=True)
|
||||
|
||||
def __init__(self, wallet: 'Abstract_Wallet', network:'Network'):
|
||||
Logger.__init__(self)
|
||||
self.network = network
|
||||
self.wallet = wallet
|
||||
self.lnworker = wallet.lnworker
|
||||
self.lnwatcher = self.wallet.lnworker.lnwatcher
|
||||
self.swaps = self.wallet.db.get_dict('submarine_swaps')
|
||||
for swap in self.swaps.values():
|
||||
if swap.is_redeemed:
|
||||
continue
|
||||
self.add_lnwatcher_callback(swap)
|
||||
|
||||
def get_swap(self, payment_hash):
|
||||
return self.swaps.get(payment_hash.hex())
|
||||
|
||||
|
@ -211,12 +216,7 @@ class SwapManager(Logger):
|
|||
self.swaps[payment_hash.hex()] = swap
|
||||
self.add_lnwatcher_callback(swap)
|
||||
await self.network.broadcast_transaction(tx)
|
||||
#
|
||||
attempt = await self.lnworker.await_payment(payment_hash)
|
||||
return {
|
||||
'id':response_id,
|
||||
'success':attempt.success,
|
||||
}
|
||||
return tx.txid()
|
||||
|
||||
@log_exceptions
|
||||
async def reverse_swap(self, amount_sat, expected_amount):
|
||||
|
@ -278,10 +278,7 @@ class SwapManager(Logger):
|
|||
self.add_lnwatcher_callback(swap)
|
||||
# initiate payment.
|
||||
success, log = await self.lnworker._pay(invoice, attempts=10)
|
||||
return {
|
||||
'id':response_id,
|
||||
'success':success,
|
||||
}
|
||||
return success
|
||||
|
||||
@log_exceptions
|
||||
async def get_pairs(self):
|
||||
|
@ -289,5 +286,42 @@ class SwapManager(Logger):
|
|||
'get',
|
||||
API_URL + '/getpairs',
|
||||
timeout=30)
|
||||
data = json.loads(response)
|
||||
return data
|
||||
pairs = json.loads(response)
|
||||
fees = pairs['pairs']['BTC/BTC']['fees']
|
||||
self.percentage = fees['percentage']
|
||||
self.normal_fee = fees['minerFees']['baseAsset']['normal']
|
||||
self.lockup_fee = fees['minerFees']['baseAsset']['reverse']['lockup']
|
||||
limits = pairs['pairs']['BTC/BTC']['limits']
|
||||
self.min_amount = limits['minimal']
|
||||
self.max_amount = limits['maximal']
|
||||
|
||||
def get_recv_amount(self, send_amount, is_reverse):
|
||||
if send_amount is None:
|
||||
return
|
||||
if send_amount < self.min_amount or send_amount > self.max_amount:
|
||||
return
|
||||
x = send_amount
|
||||
if is_reverse:
|
||||
x = int(x * (100 - self.percentage) / 100)
|
||||
x -= self.lockup_fee
|
||||
x -= self.get_claim_fee()
|
||||
else:
|
||||
x -= self.normal_fee
|
||||
x = int(x * (100 - self.percentage) / 100)
|
||||
if x < 0:
|
||||
return
|
||||
return x
|
||||
|
||||
def get_send_amount(self, recv_amount, is_reverse):
|
||||
if not recv_amount:
|
||||
return
|
||||
x = recv_amount
|
||||
if is_reverse:
|
||||
x += self.lockup_fee
|
||||
x += self.get_claim_fee()
|
||||
x = int(x * 100 / (100 - self.percentage)) + 1
|
||||
else:
|
||||
x = int(x * 100 / (100 - self.percentage)) + 1
|
||||
x += self.normal_fee
|
||||
return x
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue