Skip to main content
Execute trades seamlessly by building transaction data and sending it with your wallet signer. The SDK handles transaction building, gas estimation, and execution automatically.
The trade method combines transaction building and execution into a single, streamlined operation. This approach simplifies the trading process while maintaining full control over transaction parameters.
Before executing trades, tokens typically require approval to allow the DZap contracts to spend them on your behalf. Learn more about gas-optimized approval mechanisms in the Approval Mechanisms section.
Rate Limits: Our API and SDK have rate limits in place to ensure fair usage. If you need increased rate limits for your application, please reach out to our team on Telegram.

Trade Execution

Execute a trade using the trade method:
import { DZapClient, TxnStatus } from "@dzapio/sdk";
import type { TradeBuildTxnRequest, TradeBuildTxnResponse } from "@dzapio/sdk";

const dZap = DZapClient.getInstance();

// Build trade request
const request: TradeBuildTxnRequest = {
  fromChain: 42161, // Arbitrum
  sender: userAccount,
  refundee: userAccount,
  gasless: false,
  data: [
    {
      amount: "1000000", // 1 USDC
      srcToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
      destToken: "0x4200000000000000000000000000000000000006", // WETH
      toChain: 8453, // Base
      protocol: "relayLink", // From quotes response
      recipient: userAccount,
      slippage: 1, // 1% slippage
    },
  ],
};

// Option 1: Build and execute in one call

const result = await dZap.trade({
  request,
  signer,
});

if (result.status === TxnStatus.success) {
  console.log("Trade executed successfully:", result.txnHash);
} else {
  console.error("Trade execution failed:", result.errorMsg);
}

// Option 2: Build transaction data separately and execute

let buildResponse: TradeBuildTxnResponse;
try {
  buildResponse = await dZap.buildTradeTxn(request);
  console.log("Build response:", buildResponse);
} catch (error) {
  console.error("Error building trade transaction:", error);
  return;
}

const txnResponse = await dZap.trade({
  request,
  signer,
  txnData: buildResponse,
});

if (txnResponse.status === TxnStatus.success) {
  console.log("Trade executed successfully:", txnResponse.txnHash);
} else {
  console.error("Trade failed:", txnResponse.errorMsg);
}

Parameters

The trade function expects an object with these parameters:

Required Parameters

ParameterTypeRequiredDescription
requestTradeBuildTxnRequestyesThe trade build request containing all trade details
signerSigner | WalletClientyesEthers signer or viem wallet client for signing transactions
txnDataTradeBuildTxnResponsenoPre-built transaction data (skips build step if provided)

TradeBuildTxnRequest Structure

ParameterTypeRequiredDescription
fromChainnumberyesSource chain ID
senderHexStringyesWallet address sending the transaction
refundeeHexStringyesAddress to receive refunds
gaslessbooleanyesSet to false for standard execution; true for gasless (if supported)
dataTradeBuildTxnRequestData[]yesArray of trade data
disableEstimationbooleannoDisable gas estimation (default: false)
disablePricingbooleannoDisable pricing calculations for faster builds
publicKeystringnoPublic key for some chains (e.g., Solana)

TradeBuildTxnRequestData Structure

ParameterTypeRequiredDescription
amountstringyesAmount to trade (in token units)
srcTokenstringyesSource token address
destTokenstringyesDestination token address
toChainnumberyesDestination chain ID
protocolstringyesProtocol identifier from quotes
recipientstringyesAddress to receive destination tokens
slippagenumberyesSlippage tolerance percentage
additionalInfoAdditionalInfonoAdditional route-specific data
permitDatastringnoPre-signed permit data

TradeBuildTxnResponse (build response)

buildTradeTxn(request) returns an object with:
  • status"success" when the build succeeds
  • txId — Unique build/transaction ID (hex string)
  • chainId — Chain where the transaction is to be executed
  • transaction — EVM payload: { data, to, from, value, gasLimit } (hex calldata, contract address, sender, value in wei, gas limit)
  • gaslessfalse for standard EVM; true for gasless/intent flows
  • quotes — Per-pair summary keyed by pair ID: { additionalInfo?: { requestId, bridgeProvider, srcSwapProvider }, provider, destAmount, minDestAmount }
Pass this object as txnData to trade({ request, signer, txnData }) to skip the build step. See Types reference for the full shape.

Complete Example with Quotes and Approvals

Here’s a full example that gets quotes, handles approvals, and executes a trade. The getAllowance response has data keyed by token address; each entry has approvalNeeded and signatureNeeded:
import {
  DZapClient,
  Services,
  ApprovalModes,
  PermitTypes,
  TxnStatus,
} from "@dzapio/sdk";
import type { TradeQuotesRequest, TradeBuildTxnRequest } from "@dzapio/sdk";

const dZap = DZapClient.getInstance();
const tokenAddress = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" as const;

// Step 1: Get quotes
const quotesRequest: TradeQuotesRequest = {
  fromChain: 42161, // Arbitrum
  data: [
    {
      amount: "1000000", // 1 USDC
      srcToken: tokenAddress,
      destToken: "0x4200000000000000000000000000000000000006",
      toChain: 8453, // Base
      slippage: 1,
    },
  ],
  account: userAccount,
};

const quotes = await dZap.getTradeQuotes(quotesRequest);
const pairKey = Object.keys(quotes)[0];
const pairData = quotes[pairKey];
const recommendedSource =
  pairData.recommendedSource ?? pairData.bestReturnSource;

// Step 2: Check allowances (data is keyed by token address)
const tokens = [{ address: tokenAddress as `0x${string}`, amount: "1000000" }];

const allowanceCheck = await dZap.getAllowance({
  chainId: 42161,
  sender: userAccount,
  tokens,
  service: Services.trade,
  mode: ApprovalModes.AutoPermit,
});

const allowanceData = allowanceCheck.data ?? {};
const isApprovalNeeded = tokens.some(
  (t) => allowanceData[t.address]?.approvalNeeded
);
const isSignatureNeeded = tokens.some(
  (t) => allowanceData[t.address]?.signatureNeeded
);

if (isApprovalNeeded) {
  await dZap.approve({
    chainId: 42161,
    signer,
    sender: userAccount,
    tokens,
    service: Services.trade,
    mode: ApprovalModes.AutoPermit,
    approvalTxnCallback: async ({ txnDetails, address }) => {
      console.log(`Approval for ${address}:`, txnDetails);
    },
  });
}

let permitData: string | undefined;
if (isSignatureNeeded) {
  const signatures = await dZap.sign({
    chainId: 42161,
    sender: userAccount,
    tokens: tokens.map((t) => ({ address: t.address, amount: t.amount })),
    signer,
    service: Services.trade,
    permitType: PermitTypes.AutoPermit,
    signatureCallback: async (res) => {
      permitData = res.permitData;
    },
  });
  if (signatures.status === TxnStatus.success && "tokens" in signatures) {
    permitData = signatures.tokens?.[0]?.permitData;
  }
}

// Step 3: Build trade request from selected quote
const request: TradeBuildTxnRequest = {
  fromChain: 42161,
  sender: userAccount,
  refundee: userAccount,
  gasless: false,
  data: [
    {
      amount: "1000000",
      srcToken: tokenAddress,
      destToken: "0x4200000000000000000000000000000000000006",
      toChain: 8453,
      protocol: recommendedSource,
      recipient: userAccount,
      slippage: 1,
      permitData,
    },
  ],
};

// Step 4: Execute the trade (trade() accepts only request and signer)
const tradeResult = await dZap.trade({ request, signer });

if (tradeResult.status === TxnStatus.success) {
  console.log("Transaction hash:", tradeResult.txnHash);
  const status = await dZap.getTradeTxnStatus({
    txHash: tradeResult.txnHash!,
    chainId: "42161",
  });
  console.log("Trade status:", status);
} else {
  console.error("Trade failed:", tradeResult.errorMsg);
}
Viem/wagmi: If using wagmi v2 or viem v2, you may need to cast the wallet client: signer: walletClient as any when passing to approve() or trade().

Error handling and retry (TRY_ANOTHER_ROUTE)

When a trade fails, the response may include action: "TRY_ANOTHER_ROUTE". In that case, retry the trade using the fallback route (bestReturnSource from the quote) instead of recommendedSource:
let tradeResult = await dZap.trade({ request, signer });

if (
  tradeResult.status !== TxnStatus.success &&
  tradeResult.action === "TRY_ANOTHER_ROUTE"
) {
  // Rebuild request with bestReturnSource for each pair
  const fallbackData = request.data.map((d, i) => {
    const pairKey = Object.keys(quotes)[i];
    const fallbackSource = quotes[pairKey]?.bestReturnSource;
    return fallbackSource ? { ...d, protocol: fallbackSource } : d;
  });
  tradeResult = await dZap.trade({
    request: { ...request, data: fallbackData },
    signer,
  });
}

if (tradeResult.status !== TxnStatus.success) {
  throw new Error(tradeResult.errorMsg ?? "Trade failed");
}

Best Practices

  1. Always validate balances before execution
  2. Check allowances and execute approvals if needed
  3. Implement proper error handling with retry logic (including TRY_ANOTHER_ROUTE)
  4. Use appropriate slippage settings based on market conditions
  5. Monitor transaction status for cross-chain trades

Next Steps

After executing trades:
  1. Track the trade status to monitor progress
Always ensure users have sufficient balance for both the trade amount and gas fees before execution.