Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Exchange API

sdk.exchange exposes named, typed methods for signed POST /exchange actions. Every method signs and sends in one step.

import { TxFlow } from 'txflow-typescript-sdk'
 
const sdk = new TxFlow('testnet')
sdk.exchange.setPrivateKey(agentPrivateKey)

Configure the correct owner wallet or agent key before calling a method.

Place orders

Use a high-level helper for common single orders:

const result = await sdk.exchange.placeLimitOrder({
  asset: 1,
  side: 'buy',
  px: '60000',
  sz: '0.001',
  tif: 'gtc',
  reduceOnly: false,
})

Supported limit-order tif values are:

ValueBehavior
gtcGood-til-canceled
iocImmediate-or-cancel
post_onlyAdd-liquidity-only

For a market order, px is the slippage reference price:

const result = await sdk.exchange.placeMarketOrder({
  asset: 1,
  side: 'sell',
  px: '59900',
  sz: '0.001',
  reduceOnly: true,
})

Use placeOrder for batches, trigger orders, client order ids, per-order margin mode, or expiration. The class signature is sdk.exchange.placeOrder(orderOrOrders, grouping?, builder?). Its standalone equivalent is placeOrder(context, { orders, grouping, builder }).

All three order methods accept an optional action-level builder object to charge a Builder Code fee — see Builder Codes.

The underlying compact action is documented under order.

Check every response

Transport failures throw an exception. A valid HTTP response can still contain an exchange rejection, so always inspect status:

const result = await sdk.exchange.placeLimitOrder({
  asset: 1,
  side: 'buy',
  px: '60000',
  sz: '0.001',
})
 
if (result.status !== 'ok') {
  const message =
    typeof result.response === 'string'
      ? result.response
      : 'Exchange action failed'
 
  throw new Error(message)
}
 
if (typeof result.response === 'object') {
  console.log(result.response.data?.statuses)
}

A successful order response contains one status per submitted order at response.data.statuses. It does not contain the new order id; resolve it with sdk.info.openorders or sdk.info.frontendOpenOrders.

Cancel orders

Cancel one order or a batch by exchange-assigned id:

const result = await sdk.exchange.cancelOrder({
  asset: 1,
  oid: '1770779909233',
})

oid accepts number, string, or bigint. Prefer a string or bigint when an id may exceed Number.MAX_SAFE_INTEGER.

await sdk.exchange.cancelOrder([
  { asset: 1, oid: 1770779909233n },
  { asset: 2, oid: '1770779909234' },
])

Use cancelOrderByCloid to cancel by your own client order id. See cancel and cancelByCloid for the wire actions.

Leverage and margin

await sdk.exchange.updatePositionMode({
  isOneway: true,
})
 
await sdk.exchange.updateMarginMode({
  asset: 1,
  isCross: true,
})
 
await sdk.exchange.updateLeverage({
  asset: 1,
  leverage: 5,
  marginMode: 'cross',
})

These are agent-signed actions. In hedge mode, updateLeverage can use a negative leverage value for the short side.

Withdraw

withdraw3 is an owner-signed bridge withdrawal. It currently supports USDC only.

Do not pass a token contract address. The SDK fills tokenAddress from the active network preset (networkConfig.usdcTokenAddress / TXFLOW_USDC_TOKEN_ADDRESS) when building the EIP-712 message and the /exchange action. Callers only supply destination, amount, and an optional bridge target chain:

const result = await sdk.exchange.withdraw3({
  destination: '0x922dB1A931327CA2680343eD2d5E4541669701e9',
  amount: '100',
  // Optional. Defaults to the network preset's bridge chain
  // (Arbitrum Sepolia on testnet, Arbitrum One on mainnet).
  targetChainId: 421614,
})

Configure an owner wallet before calling. The wallet's connected chain is used for the EIP-712 domain; targetChainId selects the bridge destination and may differ.

Method groups

GroupMethods
OrdersplaceMarketOrder, placeLimitOrder, placeOrder
Cancel and modifycancelOrder, cancelOrderByCloid, modifyOrder2, modifyTpslOrders
TWAPplaceTwapOrder, cancelTwapOrder
Position configurationupdateLeverage, updateMarginMode, updatePositionMode, updateIsolatedMargin
Owner account actionsapproveAgent, tokenSend, withdraw3
Builder CodesapproveBuilderFee, revokeBuilderFee, claimBuilderRewards
Vault, referral, and rewardsvaultTransfer, setReferrer, registerReferrer, claimRewards
Outcome and nettingoutcomeOperate, netPositions

Owner account actions require an owner signer. Trading, TWAP, leverage, and margin actions normally use an authorized agent. approveBuilderFee and revokeBuilderFee are owner-signed, while claimBuilderRewards uses an agent of the builder account — see Builder Codes.

Outcome markets

Outcome orders use the same placeLimitOrder / placeMarketOrder / cancelOrder methods as perps, with the Outcome instrument id as asset.

One difference matters. The gateway may park an Outcome order in a delay queue, so a resolved promise means accepted, not sent. A queued order that fails is reported on the outcomeOrderUpdate channel of sdk.apiSubscriptions, matched by traceId (successes are not pushed), while the matching-engine result arrives on outcomeOrderUpdates of sdk.subscriptions. See Subscriptions.

outcomeOperate splits and merges complete share sets:

// Lock collateral and open symmetric YES and NO positions.
await sdk.exchange.outcomeOperate({
  split: [{ asset: 19127212, sz: '10' }],
})
 
// Close a symmetric pair and release the collateral.
await sdk.exchange.outcomeOperate({
  merge: [{ asset: 19127212, sz: '10' }],
})

Exactly one of split or merge is present per action. Each leg is an instrument id plus a share count as a decimal string.

Moving USDC into the Outcome sub-account is a tokenSend between account types, which is owner-signed:

await sdk.exchange.tokenSend({
  token: 0,
  amount: '100',
  fromAccountType: 'perp',
  toAccountType: 'outcome',
})

Important behavior

  • asset is the numeric index returned by sdk.info.perpMeta(). Outcome markets use the instrument id from outcomeMeta.
  • Prices, quantities, token amounts, and margins are strings.
  • Each SDK instance owns a monotonic millisecond nonce generator.
  • setVaultAddress(address) changes default vault routing for vault-capable trading actions.
  • The SDK does not expose raw exchange requests or sign-only/build-only methods.
  • TWAP response internals are beta and may become more specific as live response coverage expands.

Protocol reference

The SDK hides serialization and signing mechanics, but the protocol remains useful when debugging:

  • Exchange API — action schemas and response envelope
  • Signing — EIP-712 domains, MessagePack, nonces, and test vectors
  • Info API — confirm balances, positions, and order state

Next steps