For the complete documentation index, see llms.txt. This page is also available as Markdown.

Builder Codes in Trading

The term "builder" here does not refer to ValueChain block builders. Builder codes let application builders receive a fee on fills they send on behalf of a user. They are set per-order. The user must approve a maximum builder fee for each builder, and can revoke that approval at any time.

To use builder codes, the user first approves a max fee for the builder account ID. Sign that action with the user's master wallet, not an API key. Granting or updating an approval with a non-zero maxFeeRate requires the builder to hold at least 100 vUSDC in Spot and 100 vUSDC in Perps.

Builder codes currently only apply to fees collected in the quote or collateral asset. They do not apply to the buying side of spot trades; they apply to both sides of perp trades. Builder fees charged can be at most 0.2% on perps and 2% on spot.

After approval, orders may include an optional builder parameter: {"id": number, "fee": number}. id is the builder account ID. fee is the requested builder fee rate, expressed in tenths of a basis point. A value of 10 requests 1 basis point of the fill notional. The actual rate is subject to the user's approval and the engine cap.

Each user can have a maximum of 10 active builder approvals at a time.

Become a builder

Any activated trading account can act as a builder once it holds at least 100 vUSDC in each engine. There is no separate builder registration. The builder ID is that account's numeric account ID.

Read it from Query Subaccounts (data.primaryAccountID) or from Spot or Perps account state (data.aid).

The standalone approval endpoint checks that the builder account exists in both engines and meets the 100 vUSDC requirement in each when granting or updating an approval with a non-zero maxFeeRate. Falling below this balance after approval does not stop builder fee credits. Clearing approval with maxFeeRate: 0 skips these builder eligibility checks and frees an approval slot.

Fee units and limits

Both fee and maxFeeRate are integers measured in tenths of a basis point:

API value
Basis points
Percentage of order notional

10

1 bp

0.01%

50

5 bp

0.05%

200

20 bp

0.2%

2000

200 bp

2%

Engine
Maximum builder fee
API value

Spot

2%

2000

Perps

0.2%

200

The approval endpoint accepts maxFeeRate from 0 through 2000, inclusive. Each order requests a rate in builder.fee. The actual rate charged is limited to the user's approved maximum and the engine cap in the table above. Without approval for that builder, the actual builder fee is zero. The actual builder fee rate for Perps cannot exceed 200 (0.2%), even when maxFeeRate is 2000. Missing approval or a requested rate above either limit does not by itself reject an otherwise valid order; it results in a zero or reduced builder fee.

Integration flow

  1. Resolve the builder account ID as above, and confirm 100 vUSDC in each engine.

  2. Query subaccounts and use data.primaryAccountID as the approval's accountID.

  3. Set the builder's numeric account ID and the requested fee rate.

  4. Register a trading API key, then approve the builder with the user's master wallet. Sign these requests separately, or use the combined registration action below.

  5. Query GET /api/v1/user/{userAddress}/builders. Check builderID and feeRate in data.spot or data.perps for each engine the application uses.

  6. Include the approved builder in the signed order and track execution.

An otherwise valid order carrying an unapproved builder can still execute, but no builder fee is charged. Check the actual fee on the fill rather than assuming the requested fee was charged. For orders without a builder, omit the builder field before signing.

Approval succeeds when both engines accept the request. Queries may lag behind submission. After a timeout, error, or incomplete query result, re-query both engines before retrying with a fresh nonce.

Approval example

Approve a builder with the user's master wallet. Client.from_env() uses Mainnet unless SODEX_NETWORK=testnet. Set SODEX_PRIVATE_KEY to the master wallet private key, SODEX_BUILDER_ID to the builder account ID, and SODEX_BUILDER_FEE_RATE to the maximum fee rate.

EIP-712 signing

Sign account-level actions with the user's master wallet and the following domain:

Domain field
Value

name

universal

version

1

chainId

The exact uint64 value sent in X-API-Chain

verifyingContract

0x0000000000000000000000000000000000000000

For builder actions, the message field chainID is different from domain chainId: use 286623 for SoDEX Mainnet or 138565 for Testnet, regardless of the wallet's selected network. The SDK example uses the SoDEX network ID for both values. Ordinary AddAPIKey registration has no message-level chainID; it uses the network ID in the domain.

Action types

These type strings are case-sensitive, and field order is part of the signature:

Combined API key registration and approval

To register a key and approve a builder in one action, include builder in POST /api/v1/user/{userAddress}/api-keys and sign the AddAPIKeyWithBuilder structure.

HTTP request field
EIP-712 message field

type

keyType (1 for EVM)

builder.id

builderID

builder.fee

maxFeeRate

The remaining message fields follow the type string above. builder and permissions are mutually exclusive within a registration request.

See Authentication & Signing for the complete typed-data objects, including the permissioned-key variant.

Signature bytes and headers

For the account-level actions above, X-API-Sign is the hex encoding of 0x02 || r[32] || s[32] || v[1], where v is 0 or 1. Wallet libraries that return 27 or 28 must normalize the recovery byte before adding the prefix. Send the same nonce in the message and X-API-Nonce, and the domain chain ID in X-API-Chain.

Trading orders use the spot or futures domain and signature prefix 0x01.

Order payload and execution

After approval, include this fragment in the order parameters before computing the payload hash:

Replace 456 with your builder account ID. Spot batches specify builder after orders at the batch level. Perps supports a batch default and per-order overrides. Use a rate within both the approval and the engine cap.

SDK examples: Python

Canonical JSON

  1. Preserve the documented field order in {type, params}. builder is the last field in its containing order or batch structure, with id before fee.

  2. Omit absent optional fields marked omitempty, and retain required zero or false values.

  3. Encode uint64 values as unquoted JSON integers with full precision. Use the SDK's canonical serializer for signed orders.

The signature hashes the {type, params} wrapper; the HTTP body contains only params. See payload hashing and the order walkthrough for signing, submission, and order-status tracking.

Check the top-level response code and individual order results. Retain clOrdID and the returned order ID for status queries and reconciliation.

Clear approval

Submit maxFeeRate: 0 and verify removal in both approval arrays. Clearing approval skips the builder account eligibility and minimum balance checks. The user's account and signature checks still apply. Omit the builder from subsequent orders. Manage existing orders separately through the order-cancellation endpoints.

Builder statistics

Use the builder account owner's address to query combined Spot and Perps statistics. The optional accountID selects a builder account belonging to that owner; omitting it uses the primary account.

Query
Result

Cumulative builder fees, fill count and traded notional, plus the builder user count.

Latest daily records in ascending UTC date order. Defaults to 90 records, with a maximum of 300.

See BuilderFeeStats and BuilderFeeDailyStats for the response fields. These statistics summarize activity; account balances show the funds available in each engine.

Fee credits and withdrawals

Builder fees credit in real time to the builder account on the engine that produced the fill. There is no separate claim step. Query that account's balances to see income.

Pass the builder account ID as accountID. Spot total includes locked funds; Perps total is wallet balance.

To send credited funds off-chain, use ordinary Withdrawals: Perps → Spot → ValueChain EVM → external. Trading transferAsset only reaches EVM.

An approval sets a fee ceiling; it is not a payment record. Subscribe to the receiving builder account's Account Updates Stream for live balance changes, not the user's builder-approval list.

Engine
Stream
Updated balance

Spot

wss://mainnet-gw.sodex.dev/ws/spot

data.B[].t (includes locked funds); l is locked funds.

Perps

wss://mainnet-gw.sodex.dev/ws/perps

data.B[].wb (wallet balance).

Each balance includes coin ID i and name a. Values are decimal strings representing the updated balance, not an amount to add. Only updated assets appear in B; an omitted asset has not necessarily become zero.

Monitor builder balances

Use Node.js 22+, set BUILDER_USER_ADDRESS, BUILDER_ACCOUNT_ID, and ENGINE (spot or perps), save as builder-balances.mjs, and run node builder-balances.mjs. This read-only example prints balance updates without submitting trades and sends a ping every 40 seconds to keep idle connections alive. See the connection guide for heartbeat and reconnect requirements.

Reconciliation

  1. Query the builder account balances for the current balance view. The REST trade field builderFee and compact WebSocket field bf report the actual builder fee on a fill; use them instead of recomputing fees from the approval ceiling or requested rate. This fee is already included in REST fee and WebSocket f; do not add it again to calculate the total fee. An accepted order is not a fill, so use trade records to determine the final fee charged.

  2. Compare balance changes over the same interval with expected fee receipts, accounting separately for transfers, trades, and other balance changes.

  3. On disconnection, refresh Spot or Perps account balances and resubscribe. A new snapshot restores the current balance, not the missed transaction history.

accountUpdate does not contain a builder-fee reason, per-credit identifier, or trade ID. It shows the balance effect of credits but cannot by itself prove a one-to-one match between a trade fee and a credit. Keep unexplained differences pending; do not classify every positive balance change as builder income.

Troubleshooting

Symptom
Check

spot builder USDC balance is insufficient

When granting or updating an approval with a non-zero maxFeeRate, check the builder's Spot balance against the 100 vUSDC eligibility requirement.

perps builder USDC balance is insufficient

When granting or updating an approval with a non-zero maxFeeRate, check the builder's Perps balance against the 100 vUSDC eligibility requirement.

Builder account not found

For an approval with a non-zero maxFeeRate, confirm the builder ID is an existing trading account ID on this network.

Approval visible in only one engine

Re-query both approval arrays before retrying.

More than 10 builders

A user may hold at most 10 approvals; clear one with maxFeeRate: 0.

Signature recovery or API key lookup error

Signing wallet, primary type, field order, chain IDs, signature encoding, and registered API key name.

Builder fee is zero or lower than requested

Check approval for the relevant engine, the approved maximum, and the engine cap (Spot 2%, Perps 0.2%). Missing approval produces no builder fee; rates above either limit are reduced. These conditions do not by themselves reject the order. Inspect builderFee or bf on the fill.

Approve Builder Fee

Last updated