Skip to content

Require swap claims to pay recipient the full amount#72

Open
1440000bytes wants to merge 4 commits into
LayerTwo-Labs:mainfrom
1440000bytes:fix/swap-claim-amount
Open

Require swap claims to pay recipient the full amount#72
1440000bytes wants to merge 4 commits into
LayerTwo-Labs:mainfrom
1440000bytes:fix/swap-claim-amount

Conversation

@1440000bytes

@1440000bytes 1440000bytes commented Jun 9, 2026

Copy link
Copy Markdown

Built on top of #71

validate_swap_claim only checked that some output paid the swap recipient, with no amount requirement. Because the address-binding check on SwapPending inputs is intentionally skipped for claims, anyone can author a claim that spends a ReadyToClaim swap's locked output, pay the recipient a token amount, and keep the rest. This requires no mining, the claim passes mempool validation and relays normally. This PR requires the recipient to receive at least the swapped amount.

For an L2 - L1 swap, the SwapPending output holds l2_amount and the recipient is owed l2_amount in exchange for the L1 payment they already made. The claim check was:

let recipient_receives = transaction.outputs.iter()
    .any(|output| output.address == expected_recipient);

So a claimer (not necessarily the recipient claims skip the input address check) can spend the locked output, send 1 sat to the recipient, and direct l2_amount - 1 to themselves. Value conservation still holds, so nothing else rejects it. The recipient, who already paid the L1 side, is left with dust.

Fix

Require the recipient to receive at least swap.l2_amount. A shared helper sums the value of all outputs paying the recipient and compares against the swapped amount:

  • validate_swap_claim (mempool) enforces it for every claim.
  • validate_swap_claim_consensus (block path) enforces it for pre-specified swaps, where both recipient and amount are fixed at creation and therefore deterministic. Open swaps derive their recipient from node-local L1 monitoring, so that binding stays in the mempool check.

Tests

New unit tests in lib/state/swap.rs:

  • block_validation_rejects_underpaying_claim — a pre-specified claim paying the recipient 1 sat and keeping the rest is rejected
  • mempool_claim_rejects_underpayment — the mempool validator rejects an underpaying claim
  • mempool_claim_accepts_full_payment — a full-amount claim still passes

@rsantacroce

Copy link
Copy Markdown
Collaborator

Thanks for the contribution! There are a few CI failures to address — mostly formatting and similar minor issues. Once those are fixed, I'll go ahead and merge.

@1440000bytes 1440000bytes force-pushed the fix/swap-claim-amount branch from a3c4ecf to c2bbebf Compare June 13, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants