import pytest from embit import script from embit.bip32 import HDKey from embit.transaction import Transaction from app.wallet.plm_network import PLM_MAINNET from app.wallet.psbt_builder import InsufficientFundsError, Utxo, build_payout_transaction, estimate_vsize def _key(seed_byte: int) -> HDKey: root = HDKey.from_seed(bytes([seed_byte]) * 32, version=PLM_MAINNET["xprv"]) return root.derive("m/84h/746h/0h/0/0") def test_payout_deducts_fee_only_from_winner_share(): pool_key = _key(10) pool_script = script.p2wpkh(pool_key.to_public()) pool_address = pool_script.address(network=PLM_MAINNET) winner_address = script.p2wpkh(_key(11).to_public()).address(network=PLM_MAINNET) fee_address = script.p2wpkh(_key(12).to_public()).address(network=PLM_MAINNET) pool_amount = 10_000_000_000 # 100 PLM pot winner_share = pool_amount * 70 // 100 commission = pool_amount - winner_share utxos = [Utxo("aa" * 32, 0, pool_amount)] built = build_payout_transaction( signing_key=pool_key, from_script=pool_script, utxos=utxos, winner_address=winner_address, winner_share_sats=winner_share, fee_address=fee_address, commission_sats=commission, change_address=pool_address, fee_rate_sat_vb=1, ) fee = estimate_vsize(1, 3) assert built.fee_sats == fee assert built.winner_sats == winner_share - fee assert built.commission_sats == commission # untouched by the fee assert built.change_sats == pool_amount - (winner_share + commission) parsed = Transaction.parse(bytes.fromhex(built.raw_hex)) assert len(parsed.vout) == 2 # no change needed: winner_share + commission == pool_amount exactly amounts = sorted(o.value for o in parsed.vout) assert amounts == sorted([built.winner_sats, built.commission_sats]) def test_payout_adds_change_output_when_pool_utxos_exceed_target(): pool_key = _key(20) pool_script = script.p2wpkh(pool_key.to_public()) pool_address = pool_script.address(network=PLM_MAINNET) winner_address = script.p2wpkh(_key(21).to_public()).address(network=PLM_MAINNET) fee_address = script.p2wpkh(_key(22).to_public()).address(network=PLM_MAINNET) winner_share = 700_000_000 commission = 300_000_000 utxos = [Utxo("bb" * 32, 0, 2_000_000_000)] # more than winner_share+commission built = build_payout_transaction( signing_key=pool_key, from_script=pool_script, utxos=utxos, winner_address=winner_address, winner_share_sats=winner_share, fee_address=fee_address, commission_sats=commission, change_address=pool_address, fee_rate_sat_vb=1, ) assert built.change_sats == 2_000_000_000 - (winner_share + commission) parsed = Transaction.parse(bytes.fromhex(built.raw_hex)) assert len(parsed.vout) == 3 def test_payout_raises_when_winner_share_too_small(): pool_key = _key(30) pool_script = script.p2wpkh(pool_key.to_public()) pool_address = pool_script.address(network=PLM_MAINNET) winner_address = script.p2wpkh(_key(31).to_public()).address(network=PLM_MAINNET) fee_address = script.p2wpkh(_key(32).to_public()).address(network=PLM_MAINNET) winner_share = 100 # smaller than the ~172 sat fee at 1 sat/vB for 1-in-3-out commission = 50 assert winner_share < estimate_vsize(1, 3) utxos = [Utxo("cc" * 32, 0, winner_share + commission)] with pytest.raises(InsufficientFundsError): build_payout_transaction( signing_key=pool_key, from_script=pool_script, utxos=utxos, winner_address=winner_address, winner_share_sats=winner_share, fee_address=fee_address, commission_sats=commission, change_address=pool_address, fee_rate_sat_vb=1, )