BackendApiService provides a comprehensive HTTP client for the SODAX backend API, covering intent lookup, swap submission, solver orderbook, money market data, USD OHLC price candles, and runtime configuration. It implements IConfigApiV1 so that ConfigService and other services can fetch dynamic chain/token configuration without coupling to a concrete HTTP implementation.
The service is automatically instantiated when you create a Sodax instance and is available as sodax.backendApi.
All public methods return Promise<Result<T>> — they never throw. On network failure, timeout, or a non-2xx HTTP response the returned Result has ok: false with a descriptive Error in the error field.
Table of Contents
- Initialization
- Configuration
- Result<T> and Error Handling
- Intent Endpoints
- Swap Endpoints
- Solver Endpoints
- Money Market Endpoints
- Oracle Endpoints
- Config Endpoints
- Utility Methods
- Complete Example
- Notes
Initialization
BackendApiService is automatically created when you construct a Sodax instance. Access it via sodax.backendApi.
Basic Initialization
Custom Configuration
Initialization with sodax.config
If you need dynamic chain/token config fetched from the backend, call initialize() on ConfigService after construction. BackendApiService is the underlying HTTP transport that ConfigService uses.
Configuration
ApiConfig Type
ApiConfig is either a flat BackendApiConfig (shared by sodax.backendApi and the swaps client sodax.api.swaps) or a nested CustomApiConfig that points the swaps API at its own endpoint:
baseURL is the gateway root, never a service path. Every service resolves the same root and appends
its own path below it — /be here, /swaps for the swaps client, /bridge for the bridge client,
/leverage-yield for the leverage-yield client, /sponsorships/stellar for sponsoring. A baseURL ending in /be is trimmed with a warning: that was
the previous packaged default, and inheriting it nested the sibling services a level too deep
(/v1/be/swaps/submit-tx). See
CONFIGURE_SDK.md
for the full URL model.
basePath is read only by sodax.backendApi. Set it to '' when addressing a backend directly at its
origin instead of through the gateway ({ baseURL: 'http://localhost:4000', basePath: '' } →
http://localhost:4000/config/all).
Slices layer on top of the flat fields rather than replacing them: an api object that carries both a
top-level baseURL/timeout/headers and a slice keeps applying those flat values wherever the slice
does not define its own, so adding swapsApiConfig or sponsoringApiConfig to an existing flat config
never silently re-routes the base API back to the packaged default. The resolution order is
defaults → top-level flat fields → baseApiConfig → swapsApiConfig, so the most specific slice wins
per field.
The sponsoring slice resolves differently from the others on purpose: its baseURL never inherits
from baseApiConfig (it defaults to the same gateway root but reaches it independently, so pointing the
base API at a private proxy never drags sponsoring along), and neither do
its headers — a credential is scoped to the origin it was set for, so a base-API token is not
transmitted to the sponsoring host. timeout does inherit. For the same reason,
backendApi.setHeaders(...) reaches swaps but NOT sponsoring; set sponsoring headers on the
slice, or via sodax.api.sponsoring.setHeaders(...).
API key
There is one instance-wide backend key:new Sodax({ apiKey }), sent as x-api-key on this client,
sodax.api.swaps, sodax.api.bridge, and sodax.api.leverageYield. Override it for a single call with
apiKey on the trailing RequestOverrideConfig. The key follows a per-call baseURL override on these
gateway clients too —
including a plaintext local target — so point one only at a trusted SODAX-related deployment.
Sponsoring is the exception — its slice key wins there, and the instance-wide
key reaches it only when the call targets a SODAX gateway root. See
CONFIGURE_SDK.md § API key
for the full precedence order. The key belongs server-side; when this client runs in a browser, point
baseURL at your own proxy instead — see API key good practices.
RequestOverrideConfig Type
Every public method accepts an optional RequestOverrideConfig as its last argument. These per-call overrides take precedence over the ApiConfig the service was constructed with.
Default Configuration
Result<T> and Error Handling
All public methods returnPromise<Result<T>>, defined as:
try/catch around backendApi calls — errors are always captured in the Result.
Response validation. Data, token, and money-market responses are validated at runtime against valibot schemas (mirroring sodax.api.swaps): a 2xx body that fails the contract resolves to EXTERNAL_API_ERROR with context.reason: 'invalid_response_shape', not a mistyped value. The config/relay reads — getAllConfig, getSpokeChainConfig, getRelayChainIdMap — are not schema-validated (SodaxConfig is too large to mirror faithfully, and ConfigService already version-gates and falls back to packaged defaults).
Checking results
Error codes on error.message
Those sentinels are this client’s surface only. The typed sibling clients —
sodax.api.swaps and
sodax.api.bridge — take their error.message from the wire client they wrap, so discriminate on
error.context.code there instead (see Bridge Endpoints).
Intent Endpoints
Get Intent by Transaction Hash
Retrieves swap intent details using a hub-chain transaction hash.- Method: GET
- Endpoint:
/intent/tx/{txHash}
Get Intent by Intent Hash
Retrieves swap intent details using a canonical intent hash.- Method: GET
- Endpoint:
/intent/{intentHash}
IntentResponse above.
Get User Intents
Retrieves a paginated list of all swap intents created by a specific wallet address, with optional date-range filtering.startDate and endDate are Unix timestamps in milliseconds.
- Method: GET
- Endpoint:
/intent/user/{userAddress}?startDate=…&endDate=…&limit=…&offset=…
Swap Endpoints
Swap-tx submission and the rest of the typed Swaps API moved offBackendApiService onto the swaps
client — sodax.api.swaps (SwapsApiService). Submit a signed spoke-chain swap transaction with
sodax.api.swaps.submitTx(...) and poll it with
sodax.api.swaps.getSubmitTxStatus({ txHash, srcChainKey }) (both fields required). See
SWAPS_API.md for the full 21-endpoint reference (quote, create-intent, submit-tx,
status, fees, …).
Bridge Endpoints
The Bridge API is a sibling typed client —sodax.api.bridge (BridgeApiService), also reached via the
sodax.api alias and sharing the same backend host (/bridge/* sub-paths). It mirrors the swaps client
minus the solver/intent surface: allowance/approve/create-bridge-intent, submit-tx + status, tokens, and
the fee/bridgeable-amount/bridgeable discovery quotes. Submit a signed spoke-deposit with
sodax.api.bridge.submitTx(...) (passing the FULL relayData { address, payload } envelope, not just the
payload). See BRIDGE_API.md for the full reference.
BridgeApiService is a thin adapter over the standalone
@sodax/bridge-api client —
the same split as sodax.api.swaps over @sodax/swaps-api — so the wire behaviour below is shared by both:
- Errors. Every method returns
Result<T>and never throws. A failure carriesSodaxError<'EXTERNAL_API_ERROR'>withfeature: 'backend',context.api: 'bridge'andcontext.endpoint;error.causeis the underlyingBridgeApiError, whosecode(NETWORK_ERROR|TIMEOUT_ERROR|HTTP_ERROR|PARSE_ERROR|VALIDATION_ERROR) is mirrored onerror.context.code, withcontext.statusset for HTTP failures. Discriminate oncontext.code, not on the message.BridgeApiErrorandBridgeApiErrorCodeare re-exported from@sodax/sdk. - Retries. Idempotent calls — reads,
getSubmitTxStatuspolls, and pure-compute POSTs likegetFee— replay transient failures (network errors and 408/429/5xx), up to 3 attempts (the first try plus two retries) issued back to back with no delay. Mutations (approve,createBridgeIntent,submitTx) never retry, except on the API-key guard’s transient verification503: that one is rejected before the route handler runs, so replaying it cannot double-apply anything, and it is the one case that backs off. Atimeoutbounds the whole call, retries included, and is never replayed. - Budgets multiply. This wire budget is per call, so a caller that also retries multiplies against it.
The
@sodax/dapp-kitbackend hooks default to three React Query retries, which is four executions of a query — against three wire attempts each, a persistently failing idempotent endpoint can cost up to 12 requests before the error surfaces. A terminal API-key rejection (401/403) is never replayed at the wire level, and theswapsApi,leverageYieldApiandbridgeApihooks stop retrying it at the query level too (retryUnlessAuthFailure). Lower either withqueryOptions.retry(ormutationOptions.retry) when you want a tighter ceiling.
Leverage Yield Endpoints
The Leverage Yield API is a third sibling typed client —sodax.api.leverageYield
(LeverageYieldApiService), also reached via the sodax.api alias and sharing the same backend host
(/leverage-yield/* sub-paths). A leverage-yield deposit/withdraw is an intent-based swap (the vault’s
lsoda* share token is a solver-tradeable token), so its intent-relay / gas / fee / submit-tx endpoints
reuse the swaps wire shapes; what is its own is the vault registry (getVaults / getVault), the vault
reads (position, APRs, ERC-4626 previews, share balance, max-withdraw), and the split deposit/withdraw
quote + create-intent routes. sodax.api.leverageYield.submitTx(...) additionally carries a required
operation: 'deposit' | 'withdraw' discriminator — echoed back by getSubmitTxStatus as
data.operation: 'leverage_deposit' | 'leverage_withdraw' — and its terminal submit-tx status is
'solved' (the solver filled), not the bridge’s 'executed'. See
LEVERAGE_YIELD_API.md for the full reference.
Solver Endpoints
Get Orderbook
Retrieves a paginated snapshot of the solver orderbook — open swap intents waiting to be filled.- Method: GET
- Endpoint:
/solver/orderbook?offset={offset}&limit={limit}
Money Market Endpoints
Get User Position
Retrieves the current money market position for a wallet address — all reserves where the user holds aTokens (supplied collateral) or variable-debt tokens (outstanding borrows).- Method: GET
- Endpoint:
/moneymarket/position/{userAddress}
Get All Money Market Assets
Retrieves on-chain state snapshots for every active money market reserve asset.- Method: GET
- Endpoint:
/moneymarket/asset/all
Get Specific Money Market Asset
Retrieves the on-chain state snapshot for a single money market reserve asset.- Method: GET
- Endpoint:
/moneymarket/asset/{reserveAddress}
MoneyMarketAsset (same interface as above).
Get Asset Borrowers
Retrieves a paginated list of wallets with an outstanding borrow against a specific reserve.- Method: GET
- Endpoint:
/moneymarket/asset/{reserveAddress}/borrowers?offset={offset}&limit={limit}
Get Asset Suppliers
Retrieves a paginated list of wallets with an active supply (aToken balance) in a specific reserve.- Method: GET
- Endpoint:
/moneymarket/asset/{reserveAddress}/suppliers?offset={offset}&limit={limit}
Get All Money Market Borrowers
Retrieves a paginated list of all wallet addresses that hold an active borrow position across any reserve.- Method: GET
- Endpoint:
/moneymarket/borrowers?offset={offset}&limit={limit}
Oracle Endpoints
USD OHLC price candles for charting, served from the backend’s oracle candle store. Both reads are plainGETs with no authentication requirement beyond the usual API key handling.
Get Oracle Markets
Retrieves the candle store’s discovery payload: the quote currency, the selectable intervals, and the canonical symbols that have candle data. Use it to populate a symbol picker and interval switcher before requesting candles.- Method: GET
- Endpoint:
/oracle/markets
intervals[].key is typed string on purpose: discovery is what tells you which intervals exist, so this read stays tolerant of an interval a newer backend serves and this SDK version does not know. Membership-test it before passing it to getOracleCandles, which accepts only the four literals:
ORACLE_CANDLE_INTERVALS is also exported if you need the raw list (to render every option, say).
Get Oracle Candles
Retrieves USD OHLC candles for one symbol over the half-open time range[from, to).
- Method: GET
- Endpoint:
/oracle/candles?symbol={symbol}&interval={interval}&from={from}&to={to}
open/high/low/closeare USD prices as decimal strings — convert them yourself; never assume a JSON number.timestampis the bucket start in UNIX seconds. A1hcandle atTcovers[T, T + 3600).fromandtoare UNIX seconds (integers).tois exclusive and must be greater thanfrom; a zero-width or reversed range returns HTTP 400.- A request may cover at most 5000 buckets of the requested interval. A wider range (or an invalid parameter) returns HTTP 400, which surfaces as
ok: falsewitherror.message === 'HTTP_REQUEST_FAILED'anderror.context.status === 400(narrow withisSodaxError(result.error)first). - A valid range with no stored candles resolves
ok: truewithcandles: []. Render the gap rather than branching on an error. - An unknown symbol also currently resolves
ok: truewithcandles: []rather than a 404; usegetOracleMarkets()when you need to distinguish selectable symbols before requesting data. - Candles are ordered oldest first, and the last one may still be forming. Branch on the value, not the field’s presence:
final === falsemeans still forming, so re-poll while that holds; absent (the backend’s usual encoding for a closed candle) means closed. - There is no volume field — candles are sampled from the SODAX price feed, not trade flow.
intervalon the response echoes what you sent and is validated strictly — an unrecognized value resolvesok: falsewitherror.context.reason === 'invalid_response_shape'rather than a value outsideOracleCandleInterval.- Responses are cached server-side for roughly 10 seconds per distinct URL.
Config Endpoints
These methods implementIConfigApiV1 and are consumed internally by ConfigService. You generally do not call them directly — use sodax.config instead. They are documented here for completeness and for custom IConfigApiV1 implementations.
All methods accept an optional
RequestOverrideConfig as their last argument and return Promise<Result<T>>.
Utility Methods
Set Custom Headers
Merges additional headers into the service’s default header set. Useful for injecting authentication tokens or tracing headers at runtime without constructing a new service instance.Get Base URL
Returns the gateway root the service is currently pointing at, and the data API’s mount below it. Requests go togetBaseURL() + getBasePath() + <route>.
Complete Example
Notes
- All string amounts in responses are in wei format (18 decimals) unless the specific field description says otherwise.
- Pagination parameters (
offsetandlimit) are strings, not numbers. - All endpoints return JSON responses.
- Error messages follow the CODE form (
SCREAMING_SNAKE_CASE) for transport failures (HTTP_REQUEST_FAILED,REQUEST_TIMEOUT). Checkerror.causefor the underlying detail. XToken.chainKeyis the field used on token objects to identify the chain (notxChainId).- Chain constants are accessed via
ChainKeys.*(e.g.ChainKeys.ETHEREUM_MAINNET), not legacy*_CHAIN_IDconstants.