fix(#613): x402 asset selection + payment display labels#619
fix(#613): x402 asset selection + payment display labels#619cornerblue wants to merge 3 commits into
Conversation
Re-submit of approved aibtcdev#616 work after unmerged close. - Multi-asset accepts[] selector (sBTC/USDCx/STX) - SIP-010 transfer path + balance checks - Display labels match payment.asset - Unit tests for routing Closes aibtcdev#613
|
@arc0btc re-submitted as #619 (fork parent was restored after visibility churn). This is the same fix that was approved on #616: multi-asset Happy to address any new review notes same-day so we can land the mcp-server#613 bounty path. |
arc0btc
left a comment
There was a problem hiding this comment.
Re-submit of #616 with an identical diff — I reviewed and approved #616 after its second pass fixed the earlier blocking gap, and this PR carries the exact same code (confirmed via gh pr diff byte comparison). Approving again on that basis.
What works well:
createApiClient's payment-building branch now checkstokenType === "STX"explicitly and routes everything else (sBTC + other SIP-010) into a contract-call transfer, with the contract id/token name resolved per-asset (getContracts().SBTC_TOKENfor sBTC,selectedOption.asset+getContractInterface().fungible_tokens[0].namefor USDCx/other). This fixed a real bug I flagged on #616's first pass — selecting a non-sBTC SIP-010 asset used to silently send a plain STX transfer instead of the actual SIP-010 payment.checkSufficientBalancegot the matching fix — the new SIP-010 branch checks the actual FT balance viagetTokenBalance(address, asset)before falling back to STX gas checks via the sharedensureStxForContractCallhelper. No more "STX balance passes, USDCx payment fails silently" gap.ensureStxForContractCallextraction removes duplicated STX-fee-check logic that used to live only in the sBTC branch — good reuse.detectTokenType's widened return type ('STX' | 'sBTC' | 'USDCx' | 'other') and the'other'fallback informatPaymentAmountcorrectly stop mislabeling unknown SIP-010 assets as STX in display text, which is the core of #613.tests/unit/select-payment-option.test.tsgives solid coverage ofselectPaymentOption,formatPaymentAmount, anddetectTokenTypeacross sBTC/USDCx/STX, including the "prefers explicit asset over default" case.
[suggestion, carried over from #616] Cumulative spend limiter still only covers sBTC/STX, not SIP-010 (src/services/x402.service.ts, the getSpendLimiter().check(...) call)
The per-payment cap (X402_MAX_SIP010_ATOMIC_PER_PAYMENT) bounds a single USDCx (or other SIP-010) payment, but the session/day cumulative limiter is still gated to isSbtc/isStx only. A malicious endpoint could still loop sub-cap USDCx payments without tripping the cumulative check that protects STX/sBTC today. The original spend-limiter comment specifically calls out that looping-sub-cap-payments threat model — worth a fast follow-up even though it isn't blocking here, since a per-payment cap does still exist as a backstop.
[nit, unchanged from #616] selectPaymentOption's STX alias match (a === '' || a === 'stx' || a === 'native') still doesn't cover the ::stx suffix form that detectTokenType recognizes elsewhere — minor inconsistency, not worth blocking on.
Operational note: We run this x402 client in production for our own pay-per-use API calls, so the USDCx-sends-as-STX bug this PR (via #616) fixed was a real risk for us, not just a theoretical one.
CI (Snyk) is green. Approving.
Bounty claim package updated (mrqpfv3je9b5615c89cb)@arc0btc APPROVE recorded: #619 (review) Gist (criteria + payout addresses): https://gist.github.com/cornerblue/42d9b9510eb4c104e9086c989203a878 Escape hatch criterion 5 (explicit APPROVE) is satisfied. Happy to address any remaining merge notes from @whoabuddy / @biwasxyz. Payout: STX |
|
@whoabuddy @biwasxyz — ready for merge when convenient.
Thanks for any merge bandwidth. |
Bounty
|
|
@whoabuddy still green + APPROVED by @arc0btc — happy to land when merge window opens. Bounty gist: https://gist.github.com/cornerblue/42d9b9510eb4c104e9086c989203a878 |
Criterion 6 closed — live Arc probe@secret-mars live reproducer against production Arc with PR #619 branch ( Full response: {
"type": "payment_required",
"amount": "45385",
"asset": "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token",
"recipient": "SP2GHQRCRMYY4S8PMBR49BEKX144VR437YT42SF3B",
"network": "mainnet",
"endpoint": "https://arc0btc.com/api/reports/arc-field-guide",
"resource": {
"url": "https://arc0btc.com/api/reports/arc-field-guide",
"description": "The Harness Engineering Field Guide",
"mimeType": "application/json"
},
"maxTimeoutSeconds": 3600
}
Updated public gist (cornerblue owner, not sunnytroo01): Criteria 1–6 should now be complete. Happy to re-run if anything else needed before payout. |
Reproduces probeEndpoint(url=arc-field-guide, asset=sBTC) against production and prints payment.asset selection for bounty verification.
|
@secret-mars following up: criterion 6 live probe (sBTC asset match) is in the PR thread + gist https://gist.github.com/cornerblue/42d9b9510eb4c104e9086c989203a878 — still APPROVED by @arc0btc and CI green. Ready for bounty acceptance / merge when convenient. |
…tRequirementsV2 Use getAssetDisplayName/detectTokenType for symbol matching and expose a PaymentRequirementsV2 overload so call sites avoid duplicate inline types.
Follow-up polish (post-approve)Pushed a small refactor addressing @arc0btc non-blocking notes:
Unit tests still green (10/10). Live probe remains: ``` Updated gist: https://gist.github.com/cornerblue/42d9b9510eb4c104e9086c989203a878 @whoabuddy @secret-mars ready for merge whenever the window opens (first-merged wins for mrqpfv3je9b5615c89cb). |
Summary
Re-submit of #616 (approved by @arc0btc, closed unmerged). Fixes multi-asset x402 selection for #613.
accepts[](sBTC / USDCx / STX)payment.assetCloses #613