Skip to main content
SODAX swaps are intents, not bridges. Your user signs one transaction that locks the input and declares what they want on the destination network. Solvers fill it, and if nothing fills, the intent can be cancelled and funds recovered. On every other network, that transaction goes to a spoke contract and the SODAX relayer carries it to the hub. On Sonic, the user’s transaction calls the SODAX intents contract on the hub directly. From your side it is the same SDK call as on any network. From your user’s side it is one wallet prompt, plus an approval the first time they swap an ERC-20.

What this looks like on Sonic

  • Source: the intent transaction is an EVM transaction on Sonic that creates the intent on the hub intents contract. Native S is sent as the transaction value; ERC-20 inputs are pulled by the intents contract. ChainKeys.SONIC_MAINNET ('sonic') identifies the network everywhere in the SDK.
  • No relay hop: the source transaction is the hub transaction. swap() skips the relay step, and the same hash is what the solver and getStatus key on.
  • Reach: any solver-compatible asset on any SODAX network is a valid destination, 166 assets today. The on-Sonic asset list only bounds what users hold on Sonic itself, not what they can swap into.
  • Tokens: the Sonic list mixes Sonic-native tokens (S, wS, USDC, and others) with SODAX hub tokens, such as the soda* vault tokens and hub representations of assets from other networks. Read addresses and decimals from the SDK config; never hard-code them.
  • Settlement: output lands on the destination network at dstAddress. A swap into Sonic pays out in the Sonic token at dstAddress on Sonic.

Approvals

swap() does not approve the input token for you. Native S needs no approval. Every ERC-20 on Sonic does, and the spender is different from a spoke network: on a spoke the SDK approves that network’s asset manager, while on Sonic it approves the SODAX intents contract, because the user creates the intent there directly. isAllowanceValid and approve select the spender from srcChainKey, so the code is the same as on any EVM network:
params is the same intent params object you pass to swap(). An allowance for swaps does not carry over to the money market. Supplying or repaying on Sonic approves a different spender, the user’s hub wallet; see Money Market on Sonic.

Quoting

The solver API quotes both directions:
getQuote deducts any configured partner fee before quoting, so quoted_amount is the net output the user actually receives.

Fees

Using SODAX is free to integrate. Two fees can apply to a trade:
  • Base fee: a fixed 0.1% of the input, taken by the protocol. Not configurable; compute it ahead of time with getSolverFee(inputAmount).
  • Your fee: optional platform fee on top, set by you and paid to you. It is the only fee you control. Configure it at SDK setup and check it with getPartnerFee(inputAmount). See Monetize SDK.
Your fee receiver is a Sonic address, whichever network your users swap from. Fees accrue there as hub tokens, and you claim or convert them on Sonic through sodax.partners.feeClaim, covered in Monetize SDK. The user also pays Sonic’s network fee for the intent transaction, in S.

Executing

Prefer swap(). On Sonic it creates the intent on the hub, confirms it, and notifies the solver; the relay step it runs for spoke sources is skipped. The Quickstart has the complete call. Track progress with getStatus({ intent_tx_hash }), passing intentDeliveryInfo.dstTxHash. Limit orders (intents without deadlines) work from Sonic too, via createLimitOrder. Cancelling is shorter on Sonic as well. cancelIntent sends one transaction on the hub and returns without waiting for a relay packet, so srcChainTxHash and dstChainTxHash are the same hash. Orchestrating manually with createIntent: a Sonic intent needs no relay extra data and no relay submission. Pass the Sonic transaction hash straight to postExecution as the hub hash. Migrate a manual swap to the backend submit-tx flow shows the hub-source branch next to the spoke one.

Error handling

Operations return Result<T> instead of throwing. The core methods (swap, createIntent, postExecution) return a typed SodaxError union you can switch on by error.code.
Full reference, including raw mode, limit orders, cancellation, and the complete error-code table: Swaps.