Skip to content

feat(swap): build Coinbase SwapV2 and request swap_version 2 - #1104

Merged
bmc08gt merged 9 commits into
mainfrom
feat/coinbase-swap-v2
Oct 22, 2026
Merged

bmc08gt merged 9 commits into
mainfrom
feat/coinbase-swap-v2

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Server-mediated stablecoin swaps now build CoinbaseStableSwapper SwapV2 and send swapVersion = 2. This matches code-payments/ocp-server#278, where the server builds whichever instruction the request names and the client rebuilds and signs the same transaction.

The ocp-client-protocol pin and Package.resolved are at 0.9.0 (cae3c52), which carries swap_version.

What changed

  • CoinbaseStableSwapperProgram.SwapV2 is new. It uses discriminator [43, 4, 237, 11, 26, 201, 30, 98] and the v1 swap args. Its 15 accounts are v1's 16 with the whitelist (index 12) removed.
  • SwapV2.version feeds both the request field and the local builder, so the two cannot disagree.
    • Stateful (USDF→USDC): stablecoinProto and buildUsdfToUsdcSwapInstructions.
    • Stateless (USDC sweep): the initiate params in SwapService+StatelessSwap and buildStatelessSwapInstructions.
  • The external-wallet deposit (WalletConnection → buildUsdcToUsdfSwapInstructions) builds SwapV2 too, without a version switch. The user signs it and the server never sees it, so there is no swap_version to match.
  • The v1 CoinbaseStableSwapperProgram.Swap and the whitelist derivation are removed. Every swap the app builds is SwapV2.

Tests

  • CoinbaseStableSwapperSwapV2Tests asserts the exact bytes and account list from the server's instructions_swap_v2_test.go. Android asserts the same vector. The tests also check that both requests carry swapVersion == 2 and that the stateless builder emits SwapV2.
  • SwapInstructionBuilderUsdfToUsdcTests and SwapInstructionBuilderUsdcToUsdfTests now expect 15 accounts and the v2 discriminator.
  • CoinbaseStableSwapperDepositTransactionTests pins the full encoded deposit transaction. Its SwapV2 bytes pass simulateTransaction on mainnet (Swapped 5000000 tokens, 34752 compute units), so mainnet CoinbaseStableSwapper accepts swap_v2 on the deposit path.

Before merging

  • Add support for swap v2 for the Coinbase Stableswapper ocp-server#278 is deployed. It had to be: an older server ignores swap_version and builds Swap, so the signed SwapV2 transaction wouldn't match.
  • On the stateful path, swapVersion is also inside the proto the proof signs over. That needs a confirmation from the server side that this is accepted.

Linked: Android code-payments/code-android-app#1817

Add the SwapV2 instruction (15 accounts, no whitelist) and use it for the
server-mediated USDF->USDC withdraw and stateless USDC->USDF sweep. Both
requests set swapVersion from SwapV2.version, the same constant the
instruction type owns, so the request and the locally built transaction
flip together. The external-wallet deposit path keeps the v1 Swap.
0.9.0 carries swap_version on both CoinbaseStableSwapper initiate params.
@bmc08gt
bmc08gt marked this pull request as ready for review October 22, 2026 17:26
The client-built USDC to USDF deposit swap now emits CoinbaseStableSwapper
swap_v2 (15 accounts, no whitelist) instead of the legacy swap, matching the
server-mediated paths. Test vectors updated; the deposit transaction bytes are
builder-derived.
SwapV2 has no whitelist account, so deriveSwapAccounts no longer derives or returns it. deriveWhitelistAddress() stays for the v1 Swap golden test and the no-whitelist assertion.
@bmc08gt
bmc08gt merged commit 0c87e58 into main Oct 22, 2026
2 of 3 checks passed
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