Prerequisites
Before you begin, ensure you have:- A wallet provider implementation (e.g.,
IEvmWalletProviderfor EVM chains). You can use existing wallet provider implementations from the@sodax/wallet-sdk-corenpm package, or use the local package @wallet-sdk-core if working within the SODAX monorepo. - The
@sodax/sdkpackage installed - Sufficient token balance to cover the swap amount and fees
- RPC URLs for the chains you’re interacting with (we recommend having a dedicated node provider like Alchemy, Quicknode, etc.)
- Private key or wallet (browser) connection for signing transactions. For React applications, you can use the
@sodax/wallet-sdk-reactnpm package, or use the local package @wallet-sdk-react if working within the SODAX monorepo.
Step 1: Initialize Sodax Instance
First, create and initialize a Sodax instance. The Sodax constructor defaults to mainnet configuration, so no configuration is required for basic usage.- The
new Sodax()constructor defaults to mainnet configuration automatically. No configuration is required for basic usage. - Read effective configuration from
sodax.configto include constructor overrides. For initialization behavior and configuration options, see Configure SDK.
Optional: Custom Configuration
If you need to use custom solver configuration or hub provider settings, you can pass them when creating the Sodax instance:SodaxConfig shape and canonical override patterns.
Step 2: Obtain a Wallet Provider
The SDK does not require you to construct a spoke provider object. Instead, you supply a wallet provider directly in each service call. ThesrcChainKey field in the call params tells the SDK which chain to route to, and the walletProvider field is type-narrowed to the correct interface for that chain.
Note: For Node.js environments, we suggest you provide RPC URLs when creating wallet providers (default public ones might not work). For browser environments, wallet providers are typically injected by wallet extensions.
For EVM chains (Arbitrum, Polygon, BSC, etc.):
srcChainKey.
Getting Supported Tokens
Before creating a swap, you may want to check which tokens are supported for swaps on each chain:Step 3: Get a Quote (Optional but Recommended)
Before executing a swap, it is good practice to get a quote to show users the expected output amount. This helps set proper expectations and allows you to calculate slippage tolerance. Example: See how quotes are obtained in the example file:apps/node/src/swap.ts.
Step 4: Check Token Allowance
Before creating a swap intent, check whether the Asset Manager contract already has permission to spend your tokens. If not, you will need to approve it first. Example: See how allowance checking is implemented in the example file:apps/node/src/swap.ts.
CreateIntentParams uses srcChainKey and dstChainKey (not srcChain / dstChain). The on-chain Intent type has Intent.srcChain / Intent.dstChain as bigint relay chain IDs — these are internal identifiers and should not be confused with the user-facing chain key fields.
Step 5: Approve Tokens (If Needed)
If the allowance check returnedfalse, approve the Asset Manager contract to spend your tokens. The approval amount matches the inputAmount in your intent parameters (fees are automatically deducted from this amount).
Two signatures on some tokens. A few ERC-20s of the 2017 TetherToken lineage — Ethereum USDT is
the one in the SODAX token list today — reject an allowance change from one non-zero value to
another. When the wallet already holds a stale allowance on such a token, approve sends
approve(0) first, waits for it to be mined, then sends the real approval, so the user signs twice.
The returned value is still a single hash — the last transaction’s — so the code below is
unchanged. If you show an “Approving…” state, expect a second wallet prompt on those tokens.
Example: See how token approval is handled in the example file: apps/node/src/swap.ts.
approve also supports raw: true to return the unsigned transaction payload instead of broadcasting:
Step 6: Prepare Intent Parameters
Now that you have approval (if needed), prepare the complete intent parameters. Make sure to:- Use the quoted amount from Step 3 to set a reasonable
minOutputAmount - Set an appropriate
deadline(or use0nfor no deadline / limit-order behavior) - Ensure
srcAddressmatches your wallet address - Set
dstAddressto where you want to receive the output tokens
Step 7: Execute the Swap
Now you’re ready to execute the swap. Theswap method orchestrates the complete lifecycle automatically:
- Creates the intent deposit transaction on the source chain
- Verifies the spoke transaction landed on-chain
- Submits the transaction to the relayer and waits for the relay packet to land on the hub (Sonic). This step is skipped when
srcChainKeyis the hub itself. - Calls
postExecutionto notify the solver, triggering it to fill the intent
apps/node/src/swap.ts.
Step 8: Check Intent Status
After a successful swap submission, continuously monitor the intent status until it reaches a terminal state. Poll every 5 seconds until the swap is completed, failed, or not found. Example: See the complete status polling implementation in the example file:apps/node/src/swap.ts.
NOT_FOUND (-1): Intent not found in the solver system (may appear immediately after creation). After 3 consecutive NOT_FOUND responses, polling stops.NOT_STARTED_YET (1): Intent is queued and waiting to be processed (continues polling)STARTED_NOT_FINISHED (2): Intent is currently being processed (continues polling)SOLVED (3): Swap completed successfully (includesfill_tx_hashwhen available) — Terminal stateFAILED (4): Swap failed to complete — Terminal state
- Polls every 5 seconds (configurable via
intervalMsparameter) - Continues until a terminal state is reached (SOLVED, FAILED, or NOT_FOUND after 3 attempts)
- Maximum polling duration: 5 minutes by default (60 attempts × 5 seconds, configurable via
maxAttempts) - Handles temporary API errors gracefully by continuing to poll
fill_tx_hash field is only present when the status is SOLVED (3). This is the transaction hash of the fill transaction on the destination chain.
Step 9: Handle Errors
swap() returns Result<SwapResponse, SodaxError<SwapErrorCode>>. Lower-level methods such as getQuote() and getStatus() use their own error types; consult the per-method reference. Discriminate on result.error.code (a closed reason-only union), never on error.message (human-readable, may change). The original lower-level failure is preserved on error.cause; structured metadata is on error.context.
See SWAPS.md for the full per-method code unions and context schema.
isSwapError, isSwapCreateIntentError, isPostExecutionError from @sodax/sdk. Use them in catch blocks for cross-bundle type safety; see SWAPS.md.
Complete Example
Your first swap is the complete ESM example: follow its snippets in order for installation, configuration, quoting, execution, and settlement polling. It uses native gas tokens; for other inputs, also follow Check Token Allowance and Approve Tokens above. On EVM chains this grants an ERC-20 allowance, on Stellar it sets up a trustline, and on other chains the check returnstrue.
The Node smoke script
is an additional repository example. It executes on mainnet and reads its private key from the environment.
Next Steps
- Recovery: Follow the failed-swap recovery guidance before retrying a transaction that may have already been submitted
- Learn more about swap configuration and advanced features in SWAPS.md
- Explore other SDK features like Money Market, Bridge, and Staking
- Check the README.md for general SDK usage and configuration
- Read ARCHITECTURE_REFACTOR_SUMMARY.md for the full architecture reference