v0.3.1 — Real EVM smoke
Phase 3 patch. Closes Sub-task 4 — Coinbase CDP real-network smoke
on Base Sepolia, the last remaining deferred item from v0.3.0.
Added
scripts/smoke/real-evm/— 7-step real-network smoke suite
for Coinbase CDP settlement on Base Sepolia (eip155:84532). Each
04-settle/06-facilitator-settlerun broadcasts a real
transferWithAuthorizationto Base Sepolia via CDP and asserts
the on-chain receipt status (eth_getTransactionReceipt) is
0x1within ~60 seconds. Exercises both the internal/settle
admin surface AND the public/facilitator/settlesurface, so
the multi-chain facilitator surface is now real-tested on both
Cosmos and EVM.- Base Sepolia (
eip155:84532) network support:@suverse-pay/signer-evm— Circle's test USDC at
0x036CbD53842c5426634e7929541eC2318f3dCF7eadded to the
trusted domain table. Note: the test contract's on-chain
EIP-712 domainname()is"USDC"(not"USD Coin"like
Base mainnet) — verified viaeth_call.@suverse-pay/adapter-coinbase-cdp— Base Sepolia capability
registered alongside the mainnet EVM entries; CDP advertises
eip155:84532(exact / upto / batch-settlement) on
/supported.apps/api— Base Sepolia added to the production CDP
capability set.services/facilitator—eip155:84532:exactroutes to
coinbase-cdpin the facilitator routing config.
Fixed
- CDP wire-format envelope translation in the
coinbase-cdpadapter. CDP's hosted facilitator implements
x402V2PaymentRequirementswithamount(not the spec's
maxAmountRequired) AND requires anacceptedfield embedded
inside thepaymentPayloadcarrying the same requirements. The
rest of the codebase uses canonical spec field names; the
adapter'stoCdpRequestnow translates. Without this, the
adapter's/verify//settleagainst the real CDP endpoint
always returned HTTP 400 with
x402V2PaymentPayload requires 'accepted'. Caught by Sub-task 4
on the first end-to-end attempt; covered by a new unit test
(translates the spec wire format to CDP's internal x402V2 shape)
to prevent silent regression. scripts/smoke/mocked/— hard-pinnedAPI_PORT=3333in
00-setup.shand07-settle-fallback.sh. The previous
API_PORT="${API_PORT:-3333}"deferred to the caller's env; once
a long-running dev gateway started using.env'sAPI_PORT=3000,
the mocked smoke would collide withEADDRINUSE 0.0.0.0:3000.
Mocked smoke is a sandbox and must never use the prod port.scripts/smoke/facilitator-mocked/04-verify-evm.sh— added
a fourth accepted outcome (HTTP 502 whose
error.details.providerId == "coinbase-cdp"). Once CDP is wired
with credentials, the synthetic test payload now reaches CDP,
which rejects it at HTTP 400 (invalid signature: R is 0);
httpJsonthrows on 4xx and the gateway surfaces this as 502
with CDP attribution. This still proves the routing layer
reached CDP — what the test was meant to check.
Verified
- Real Base Sepolia on-chain USDC settle via Coinbase CDP.
Inaugural txs (txHashes recorded in the v0.3.1 GitHub release):- internal
/settlepath:
0x618913...c74abfd - public
/facilitator/settlepath:
0xac4ca1...39e21
- internal
- Idempotency on real CDP: replaying
/settlewith the same
Idempotency-KeyAND the same signed payload returns the same
paymentId+ sametxHash, with exactly one row in
payment_attempts— proven by an independent
GET /payments/:idlookup after the replay. - Both
/verifyand/settlepaths against real CDP on Base
Sepolia. Verify returns{isValid: true, payer}; settle returns
a 32-byte txHash that subsequently lands on-chain. - All 7 smoke suites green:
mocked(10),real(9),
mcp-mocked(7),mcp-real(4),facilitator-mocked(10),
mcp-solana(5),real-evm(7 — new). - CDP minimum settle amount on Base Sepolia is 1000 atomic
USDC (=$0.001). Below that CDP returns
{invalidReason: "amount_too_low"}; the smoke default is now
set there.
Deferred
- CDP 4xx-as-verify-result handling. CDP returns
{isValid: false, invalidReason, ...}as HTTP 400 (not 200), so
the adapter'sverifycurrently throws on every CDP rejection
instead of returning a normalized{valid: false, errorCode}
response. The CDP-attribution case is handled gracefully end to
end, but the adapter should parse CDP 4xx bodies. Not in v0.3.1
because the fix touches the commonhttpJsonretry/throw path
and warrants its own test surface.