Error handling conventions: Relay-layer failures emit one of two stable strings onThe intent relay service bridges spoke-chain transactions to the SODAX hub (Sonic). All cross-chain operations — swaps, bridges, money market deposits/withdrawals, staking — submit a spoke-chain transaction hash to the relay, then poll until the hub confirms execution.error.message:'SUBMIT_TX_FAILED'or'RELAY_TIMEOUT', also exported asRELAY_ERROR_CODESfrom@sodax/sdk. Modules other than swap propagate these errors raw. The swap module wraps them intoSodaxError<SwapErrorCode>withcontext.relayCode(see SWAPS.md Error Handling).
Mainnet
URL:https://xcall-relay.nw.iconblockchain.xyz
This is the default value of DEFAULT_RELAYER_API_ENDPOINT (exported from @sodax/sdk). It is set automatically in relayConfig.relayerApiEndpoint and picked up by ConfigService — no manual configuration is needed unless you are overriding the endpoint.
Testnet
There is no testnet relayer endpoint for integrators. SODAX is mainnet-only:ChainKeys contains
only *_MAINNET entries, and there are no testnet chain configs or RPC endpoints in the SDK. Build
and test against mainnet with small amounts — see Testing without a testnet.
SDK integration
IntentRelayApiService (packages/sdk/src/shared/services/intentRelay/IntentRelayApiService.ts) is an internal module. Callers never construct it directly. The relay is accessed through high-level service methods:
sodax.swaps.createIntent(...)— submits the spoke tx and waits for hub execution internallysodax.bridge.bridge(...)— similarly manages the full relay lifecyclesodax.moneyMarket.*,sodax.staking.*, and related methods do the same
Promise<Result<T>>. On relay failure the Result carries an error whose message is 'RELAY_TIMEOUT' or 'SUBMIT_TX_FAILED' (CODE form — see the error handling conventions note at the top of this page).
Chain IDs used by the relay
The relay API identifies chains byIntentRelayChainId — a bigint value that is distinct from the ChainKeys.* string keys used everywhere else in the SDK.
Use getIntentRelayChainId(chainKey) (exported from @sodax/sdk) to convert a SpokeChainKey to its relay chain ID. The SDK does this conversion internally; callers only need it when constructing raw relay requests directly (advanced usage).
Full mapping (RelayChainIdMap in @sodax/sdk):
Relay API actions
All requests are JSONPOST to the relay URL. The action field selects the operation.
submit — submit a transaction for relaying
RelayExtraData ({ address: Hex; payload: Hex }) carries the hub destination address and the full call payload. Solana and Bitcoin use split transactions: the on-chain tx stores only a verification hash; the full call data is submitted off-chain here.
SubmitTxResponse):
get_transaction_packets — poll for relay packets by tx hash
GetTransactionPacketsResponse):
status === 'executed'. Use dst_tx_hash as the hub-chain transaction hash for subsequent solver interactions.
get_packet — fetch a single packet by connection serial number
GetPacketResponse):
Low-level functions (advanced usage)
These are exported fromIntentRelayApiService for callers that need direct relay access (e.g. custom orchestration, bots):
All functions return
Promise<Result<T>> — no throws across service boundaries. Check result.ok before using result.value. On failure, result.error is an Error instance:
result.error.message === 'RELAY_TIMEOUT'— packet did not arrive within the timeout (default: 120 000 ms)result.error.message === 'SUBMIT_TX_FAILED'— the relay rejected the submission; checkresult.error.cause.messagefor the relay’s rejection reasonresult.error.message === 'HTTP_REQUEST_FAILED'— network-level failure; checkresult.error.causefor details
RelayAndWaitParams: