Swap Engine — Build Unsigned Swap Transaction

POST /api/public/swap/build-tx

Constructs unsigned transaction calldata (to, data, value, gasLimit) for ERC-20 token approval and atomic trade execution via the TowerSwapExecutor contract on the Arc blockchain.


Request Details

  • Method: POST
  • Path: /api/public/swap/build-tx
  • Authentication: Required (Bearer <API_KEY> or x-api-key: <API_KEY>)
  • Required Scope: swaps
  • Compute Cost: 3 CU

Request Parameters (JSON Body)

NameTypeRequiredDescription
quoteobjectRequiredThe complete quote payload returned from POST /api/public/swap/quote.
userAddressstringRequiredThe EVM or Arc wallet address executing and signing the swap.

Request Body Example

json
{
  "quote": {
    "inputToken": "0x3600000000000000000000000000000000000000",
    "outputToken": "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
    "inputAmount": "100000000",
    "outputAmount": "92450000000000000000",
    "dexId": "tower-dex"
  },
  "userAddress": "0xa54FFd258815Ee711bA0d3Dbb7fA786AEA6095Fb"
}

Response Example (200 OK)

json
{
  "success": true,
  "data": {
    "approval": {
      "to": "0x3600000000000000000000000000000000000000", // Token contract being approved
      "data": "0x095ea7b30000000000000000000000002de8906a641d65d490bc60a4179d961d59742bcb0000000000000000000000000000000000000000000000000000000005f5e100", // ERC-20 approve(spender, amount) calldata
      "from": "0xa54FFd258815Ee711bA0d3Dbb7fA786AEA6095Fb", // User wallet address
      "gasLimit": "100000" // Estimated gas limit for approval
    },
    "swap": {
      "to": "0x2De8906a641d65d490bC60A4179d961d59742bCb", // TowerSwapExecutor contract on Arc
      "data": "0xcd6267d50000000000000000000000003600000000000000000000000000000000000000", // Swap calldata
      "value": "0", // Native value in wei (0 for token swaps)
      "from": "0xa54FFd258815Ee711bA0d3Dbb7fA786AEA6095Fb", // User wallet address
      "gasLimit": "500000", // Estimated gas limit for swap execution
      "chainId": 5042002 // Arc network chain ID
    }
  }
}

Conditional Approval: If data.approval is null, the user's wallet already holds an active token allowance for TowerSwapExecutor, meaning your application only needs to sign and broadcast data.swap.


Code Examples

cURL

bash
curl -X POST "https://tower-devapi.up.railway.app/api/public/swap/build-tx" \
  -H "Authorization: Bearer sk_live_********************" \
  -H "Content-Type: application/json" \
  -d '{
    "quote": {
      "inputToken": "0x3600000000000000000000000000000000000000",
      "outputToken": "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
      "inputAmount": "100000000",
      "outputAmount": "92450000000000000000",
      "dexId": "tower-dex"
    },
    "userAddress": "0xa54FFd258815Ee711bA0d3Dbb7fA786AEA6095Fb"
  }'

JavaScript (Fetch)

javascript
const response = await fetch("https://tower-devapi.up.railway.app/api/public/swap/build-tx", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_********************",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    quote: quoteObject,
    userAddress: "0xa54FFd258815Ee711bA0d3Dbb7fA786AEA6095Fb"
  })
});
const txPayload = await response.json();
console.log("Unsigned swap payload:", txPayload.data.swap);

Error Responses

Status CodeDescriptionExample Payload
400 Bad RequestMissing quote or userAddress in request payload.{"success": false, "error": "Missing userAddress"}
401 UnauthorizedMissing or invalid Developer API key.{"success": false, "error": "Invalid API key"}
403 ForbiddenAPI key lacks the swaps scope.{"success": false, "error": "Scope 'swaps' is required"}
429 Too Many RequestsRate limit quota exceeded.{"success": false, "error": "Rate limit exceeded"}