Repository navigation
fix(trade): screen addresses for sanctions before quoting and executing swaps - #664
Conversation
…ng swaps `trade quote` and `trade execute` talked to the trading backend directly and never ran the pre-trade compliance screen that `bridge` and `perp` already perform client-side, leaving DEX swaps (same-chain and cross-chain) as the one trading surface with no such check. Both handlers now call the same fail-closed `screenOrThrow()` helper: - `quote` screens the signing wallet plus any distinct destination wallet (`--to-wallet`, or the auto-derived cross-chain destination) before the quote request goes out, so a flagged wallet never receives a signable quote. - `execute` screens once, before the per-quote signing loop, covering the resolved signer, the wallet the quote was built for, and any distinct recipient — so a refusal is final rather than "try the next quote". A flagged address aborts with code `SANCTIONED`; a screening call that cannot complete aborts with `SCREENING_UNAVAILABLE`. Nothing is signed or broadcast in either case, and the quote is left unconsumed. A local wallet file with no key for the quote's chain now fails with the same actionable message the signing branch gives instead of reaching the screen at all. Adds trading-sanctions-screening.test.js covering both commands across local, WalletConnect and provider-signed paths; existing trade tests pass an API double whose screen reports clean. All network calls are mocked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
pr-reviewer Summary for #f409934✅ No issues found The code review completed successfully with no findings. Review effort: 3/5 (Moderate) SummaryThis PR correctly extends the existing fail-closed compliance screening ( All three signer paths (local, WalletConnect, Privy) are handled correctly: local and WalletConnect resolve Test coverage is thorough: the new 417-line Token usage: 3,074 input, 3,295 output, 319,594 cache read, 46,272 cache write | Usage Guide New pushes are reviewed automatically with a 10-minute cooldown between reviews. To request a review at any time, comment |
|
Merge position: 6 of 9. Merge before #667. Both this and #667 change |
Summary
nansen trade quoteandnansen trade executenow run the same pre-trade compliance screening thatnansen bridgeandnansen perpalready perform, so DEX swaps are no longer the one trading surface in the CLI without it — for same-chain and cross-chain (--to-chain) swaps alike.Both commands reuse the existing fail-closed
screenOrThrow()helper (POST /api/v1/sanctions/screen), exactly asbridge executedoes:trade quotescreens the signing wallet plus any distinct destination wallet (--to-wallet, or the destination auto-derived for a cross-chain swap) before the quote request goes out, so a flagged wallet never receives a signable quote.trade executescreens once, immediately before the per-quote signing loop — covering the resolved signing wallet, the wallet the quote was built for, and any distinct recipient. A quote stays valid for up to an hour and the signed transaction is broadcast with no further check, so this is the last gate before funds move. Screening outside the loop means a refusal is final rather than "try the next quote".A flagged address aborts with code
SANCTIONED; a screening call that cannot complete (or a response that doesn't cover every requested address) aborts withSCREENING_UNAVAILABLE. In both cases nothing is signed or broadcast and the quote is left unconsumed, so a fresh attempt is still possible once the situation is resolved.Two smaller behaviour notes:
SCREENING_UNAVAILABLEand a prompt to re-quote, rather than silently skipping the check.Screening requires API access — the same requirement
bridgeandperpalready have — so an unauthenticated call fails closed rather than trading unscreened.No new commands or flags, so
src/schema.jsonis unchanged.Changes
src/trading.js— importscreenOrThrow; new exportedtradeScreeningAddresses()helper (drops blanks, de-duplicates EVM addresses case-insensitively while keeping base58 addresses distinct by exact spelling); screen in thequotehandler before the quote request; resolve the signer ahead of the signing loop and screen in theexecutehandler before it.src/__tests__/trading-sanctions-screening.test.js— new: the gate for both commands across local, WalletConnect and provider-signed paths, plus the address-list helper.src/__tests__/trading.test.js,src/__tests__/trading-quote-reuse.test.js,src/__tests__/trade-validation.test.js,src/__tests__/blank-option-values.test.js— existing trade tests pass an API double whose screen reports clean (they previously passednull, since these handlers made no API-instance calls).skills/nansen-trading/SKILL.md— document the screen and its two error codes under Notes; note that the API key is needed for it..changeset/trade-sanctions-screening.md— patch.Validation
Clean, no output.
All network calls in the new tests are mocked (API instance double + stubbed
fetchfor the trading backend and RPC); no real trade is quoted or executed. The tests assert that the screen call precedes the/quoteand/executecalls, that a flagged signer or destination aborts with no transaction sent and the quote left unconsumed, and that a screening failure or an incomplete screening response aborts the same way.Checklist
npm test)src/schema.jsonupdated if new commands or flags were added — n/a, no new commands or flagsREADME.mdupdated if new top-level commands or categories were added — n/a, no new commands.changeset/) — only required for user-facing changes (new commands, flags, bug fixes, breaking changes)🤖 Generated with Claude Code