Skip to content

Fix(underwriter): bind the source-deposit hash to the caller's target amount - #557

Closed
heifner wants to merge 1 commit into
masterfrom
fix/underwriter-source-deposit-target-amount
Closed

Fix(underwriter): bind the source-deposit hash to the caller's target amount#557
heifner wants to merge 1 commit into
masterfrom
fix/underwriter-source-deposit-target-amount

Conversation

@heifner

@heifner heifner commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

The underwriter reproduces the source outpost's deposit hash before it will commit to a swap. Both verifiers packed the UWREQ's dst_amount — which #550 repurposed to hold the depot's own AMM quote, a number minted long after the outpost hashed the deposit and one it never saw. The outpost hashed SwapRequest.target_amount, so the recompute now misses for every swap whose quote differs from the target: every swap that pays a WIRE-leg fee.

The failure is silent. verify_source_deposit returns false, the underwriter logs SwapDeposit hash mismatch and skips the request, and retries on the next scan cycle — forever. Nothing aborts, so the swap simply never gets a commitment.

underwriter: source-deposit verify failed for uwreq 11 — SwapDeposit hash mismatch (id=1):
  on-chain=fd8f16aa...  recomputed=758ed0f4...
underwriter: skipping uwreq 11 — source-deposit verify failed
  (repeats every 5s until the flow's capture step times out at 840s)

The fix

Pack target_amount. The field already exists on the row — #550 added it for exactly this reason ("the destination amount the caller ASKED for") — and the plugin simply never read it.

The packing moves into routing_detail.hpp as pack_swap_deposit_preimage, shared by both verifiers. The EVM and SVM bodies were near-identical, differing only in depositor width (20-byte address vs 32-byte Ed25519 pubkey), and they had already drifted once — this is the second time a change had to be made in two places and landed in neither. swap_deposit_terms deliberately has no dst_amount field, so the quote cannot be packed here by mistake; the single uw_request adapter makes that choice once for both chains.

No compatibility fallback on the decode: the chain is unlaunched, so there are no pre-split rows to read.

Tests

underwriter_source_deposit_tests pins the byte layout for both chains (depositor bytes, seven big-endian u64s in outpost order, then the tolerance), that the EVM and SVM preimages share identical trailing bytes so the two verifiers cannot drift, and that the preimage tracks the caller's target rather than anything the depot re-prices.

The verifiers themselves need live RPC, so the end-to-end proof is a swap confirming on a cluster — that needs this merged and a gate run, and I have not claimed it here.

Scope

This affects any outpost-originated underwritten swap on master, not one flow. flow-swap-with-underwriting should be failing the same way right now. If a recent gate run on master is green, my model is incomplete and I would like to know why before this merges.

Found while re-running the e2e gate for #549 after merging master: that branch's flow-underwriter-slashing timed out waiting for a commitment, with the log above. #549 stays gate-blocked until this lands.

… amount

The source outpost hashes the terms the CALLER submitted -- ReserveManager.requestSwap's abi.encodePacked on EVM, request_swap.rs::correlation_hash on SVM -- and the underwriter reproduces that preimage before it will commit. Both verifiers packed the UWREQ's dst_amount, which the depot now overwrites with its own AMM quote (WNS-02): a number minted after the deposit was hashed and one the outpost never saw. Every swap whose quote differs from the target -- every swap that pays a WIRE-leg fee -- therefore failed to verify, and the underwriter declined to commit with a bare "SwapDeposit hash mismatch", stalling the swap instead of failing loudly.

Pack target_amount instead. The field already exists on the row and carries exactly what the outpost hashed.

The packing moves into routing_detail.hpp as pack_swap_deposit_preimage, shared by both verifiers -- the EVM and SVM bodies were near-identical, differing only in depositor width, and had already drifted once. swap_deposit_terms carries no dst_amount field at all, so the quote cannot be packed here by mistake; the single uw_request adapter makes that choice once for both chains.
@heifner
heifner requested a review from a team August 10, 2026 17:11
@heifner

heifner commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

See #556

@heifner heifner closed this Aug 10, 2026
@heifner
heifner deleted the fix/underwriter-source-deposit-target-amount branch August 10, 2026 17:44
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.

1 participant