> ## Documentation Index
> Fetch the complete documentation index at: https://docs.1tx.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Calldata API

> Build and simulate an atomic call bundle your own account signs

## Overview

The Calldata API is the execution surface of 1tx. It takes an instrument and an amount and
returns **the ordered calls to make**, already simulated against chain state at a pinned block.

It holds nothing and signs nothing. There is no 1tx router in the path, no on-chain registration
step for an instrument, and no custody: the calls target the protocol directly, and the account
named in the request is the effective caller, token owner and receiver. Your wallet — an EOA
sending them one at a time, or a smart account batching them — decides how they land.

Three endpoints return the same envelope:

| Endpoint                                  | Builds                                                       |
| ----------------------------------------- | ------------------------------------------------------------ |
| `GET /instruments/:instrumentId/calldata` | A deposit into, or a withdrawal from, one instrument         |
| `GET /bridge/calldata`                    | A CCTP V2 source burn, to move USDC between chains           |
| `GET /loops/:loopId/calldata`             | A levered loop — see [Loops](/api-reference/endpoints/loops) |

<Note>
  Every bundle is single-chain. A cross-chain deposit is a bridge bundle on the source chain and,
  once Circle attests the burn, an instrument bundle on the destination chain — two requests, not
  one call that spans chains.
</Note>

***

## Build an Instrument Bundle

<span style={{color: '#10B981', fontWeight: 'bold'}}>GET</span> `/instruments/:instrumentId/calldata`

Builds the calls for one deposit or withdrawal, simulates them atomically at a pinned quote block,
and returns what the simulation measured rather than what a model predicts.

### Path Parameters

<ParamField path="instrumentId" type="string" required>
  The 32-byte instrument id from [`GET /instruments`](/api-reference/endpoints/instruments). It
  embeds the chain, so the request carries no `chainId`.
</ParamField>

### Query Parameters

<ParamField query="action" type="string" required>
  `deposit` or `withdraw`.
</ParamField>

<ParamField query="account" type="string" required>
  The account that supplies the tokens and receives the output. It is the effective caller of every
  call in the bundle.
</ParamField>

<ParamField query="amount" type="string" required>
  Raw token units — never a decimal string. For `deposit` it is the amount of `tokenIn`; for
  `withdraw`, the amount of the underlying asset to take out. `max` empties the position and is
  valid only for `withdraw`.
</ParamField>

<ParamField query="tokenIn" type="string" default="chain USDC">
  What the account spends on a deposit. Anything other than the instrument's underlying asset adds
  a swap leg in front of the deposit.
</ParamField>

<ParamField query="tokenOut" type="string" default="chain USDC">
  What the account receives on a withdrawal. Anything other than the underlying asset adds a swap
  leg after the withdrawal.
</ParamField>

<ParamField query="slippageBps" type="number" default="50">
  Applies to the swap leg only. A bundle with no swap ignores it.
</ParamField>

### Request

```bash theme={null}
curl -H "x-api-key: your-api-key" \
  "https://api.1tx.fi/api/v1/instruments/0x0000a4b194d4938ed6aab5bdbac7ca4b622f3639b1bca1b8b9c3271403d3b1b5/calldata?action=deposit&account=0x59F84Af036712AFe2fD0Ec77f4D9a6F0a612f1fE&amount=2200000"
```

### Response

A USDC deposit into an ERC-4626 vault, calldata truncated:

```json theme={null}
{
  "instrumentId": "0x0000a4b194d4938ed6aab5bdbac7ca4b622f3639b1bca1b8b9c3271403d3b1b5",
  "chainId": 42161,
  "account": "0x59F84Af036712AFe2fD0Ec77f4D9a6F0a612f1fE",
  "action": "deposit",
  "tokenIn": { "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "symbol": "USDC", "decimals": 6 },
  "tokenOut": { "address": "0x4e65fE4DbA92790696d040ac24Aa414708F5c0AB", "symbol": "gtUSDCa", "decimals": 18 },
  "amountIn": "2200000",
  "depositAmount": "2200000",
  "expectedOut": "2189412783920145233",
  "minOut": null,
  "expiresAt": null,
  "quoteBlock": 298113402,
  "requires": [{ "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "amount": "2200000" }],
  "leftovers": [],
  "calls": [
    {
      "to": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
      "data": "0x095ea7b3...",
      "value": "0",
      "chainId": 42161,
      "type": "approve"
    },
    {
      "to": "0x4e65fE4DbA92790696d040ac24Aa414708F5c0AB",
      "data": "0x6e553f65...",
      "value": "0",
      "chainId": 42161,
      "type": "deposit"
    }
  ],
  "simulation": {
    "ok": true,
    "scope": "protocol_bundle",
    "engine": "wallet_neutral_atomic",
    "gasUsed": "241883",
    "quoteBlock": 298113402,
    "tokenOutDelta": "2189412783920145233",
    "assumedBalances": true
  }
}
```

### Response Fields

| Field                  | Type           | Description                                                                                                                         |
| ---------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `tokenIn` / `tokenOut` | object         | Address, symbol and decimals of what is spent and what is received                                                                  |
| `amountIn`             | string         | The `amount` as requested, in raw units — `max` for a full withdrawal                                                               |
| `depositAmount`        | string \| null | Underlying actually deposited after the swap leg; `null` on a withdrawal                                                            |
| `expectedOut`          | string         | The output the simulation measured, in raw units of `tokenOut`                                                                      |
| `minOut`               | string \| null | Floor written into the calldata. Set when a leg is slippage-bounded; `null` when the bundle carries no swap and the output is exact |
| `expiresAt`            | number \| null | Unix seconds after which the swap quote is stale. `null` means no swap, so nothing to expire                                        |
| `quoteBlock`           | number         | Block the bundle was built and simulated against                                                                                    |
| `requires`             | array          | Balances the account must already hold for the bundle to run                                                                        |
| `leftovers`            | array          | The most a leg can leave behind in the wallet — dust, not an error                                                                  |
| `calls`                | array          | The calls to send, **in order**                                                                                                     |
| `calls[].type`         | string         | `approve`, `swap`, `deposit`, `withdraw`, `bridge_burn`, `fee`, `borrow`, `repay`, `set_account_config`                             |
| `calls[].value`        | string         | Native value in wei, as a decimal string — `"0"` for every token path                                                               |
| `simulation`           | object         | The atomic simulation's own result                                                                                                  |

### Reading the bundle

<AccordionGroup>
  <Accordion title="simulation — measured, not modelled">
    Every bundle is simulated with `eth_simulateV1` at `quoteBlock` before it is returned, running all
    the calls in one block, in order, as the account. `tokenOutDelta` is the account's `tokenOut`
    balance after minus before — that is where `expectedOut` comes from. A bundle whose simulation
    reverts, or that produces no output tokens, is never returned; you get a `400` instead.

    `engine: "wallet_neutral_atomic"` means the simulation makes no assumption about how you execute:
    it does not model a router, a smart-account batch or a paymaster, only the protocol calls. It is a
    statement about the calls, not about your wallet's ability to send them.

    `assumedBalances: true` means the simulation overrode the account's token balances to satisfy
    `requires`. It proves the calls work; it does not prove the account is funded. Check `requires`
    against real balances yourself.
  </Accordion>

  <Accordion title="requires and approve calls">
    `requires` lists exactly what the account must hold before the first call. The bundle always
    includes its own `approve` calls where a protocol needs one, sized to the bundle, so an existing
    allowance is not assumed. Compound V3 is the exception every integrator hits: Comet uses
    `allow(manager, true)` rather than an ERC-20 `approve` on the withdrawal path, and the bundle
    emits that call for you.
  </Accordion>

  <Accordion title="Swap legs, expiresAt and leftovers">
    When `tokenIn`/`tokenOut` is not the instrument's underlying asset, the bundle gains a swap leg —
    Uniswap V3 or V4, on a route pinned per underlying — placed before the deposit or after the
    withdrawal. Only then are `expiresAt`, `minOut` and `leftovers` populated.

    A quote that prices further than 100 bps below par for a stable pair, or that carries more than
    100 bps of impact against a reference quote, is refused rather than returned at a bad price. Past
    `expiresAt`, request fresh calldata; the old bundle will revert on its own `minOut` rather than
    fill badly.

    A `max` withdrawal cannot be swapped: the exit amount is only known after the withdrawal runs, and
    an exact-output swap needs it up front. Ask for an exact underlying amount instead.
  </Accordion>

  <Accordion title="Errors">
    * `400` — inactive instrument, unsupported chain, `amount=max` on a deposit, a `max` withdrawal
      combined with a swap, a swap withdrawal on a non-exact amount, an expired quote, a reverted
      simulation, or a simulation that produced no output.
    * `404` — no such instrument.
    * `501` — the instrument has no recipe, or its recipe is not implemented. `erc4626`, `aave-v3` and
      `comet` are served today.
    * `503` — RPC, the swap quote or the simulator was unavailable.
  </Accordion>
</AccordionGroup>

***

## Build a Bridge Bundle

<span style={{color: '#10B981', fontWeight: 'bold'}}>GET</span> `/bridge/calldata`

Builds the CCTP V2 **source burn** — approve plus `depositForBurn` — that moves USDC to another
chain. The account is both the burner and the recipient on the destination side.

### Query Parameters

<ParamField query="fromChainId" type="number" required>
  Source chain. Must be configured for CCTP V2.
</ParamField>

<ParamField query="toChainId" type="number" required>
  Destination chain. Must differ from the source.
</ParamField>

<ParamField query="amount" type="string" required>
  Raw USDC units to burn.
</ParamField>

<ParamField query="account" type="string" required>
  Burns the USDC on the source chain and receives it on the destination chain.
</ParamField>

<ParamField query="fast" type="boolean" required>
  `true` uses Fast Transfer (finality threshold `1000`), `false` Standard (`2000`). Fast settles in
  seconds against Circle's allowance and charges the fee quoted in `maxFee`; Standard waits for
  hard finality. A source chain without fast support refuses `true`.
</ParamField>

### Request

```bash theme={null}
curl -H "x-api-key: your-api-key" \
  "https://api.1tx.fi/api/v1/bridge/calldata?fromChainId=8453&toChainId=42161&amount=2200000&account=0x59F84Af036712AFe2fD0Ec77f4D9a6F0a612f1fE&fast=true"
```

### Response

```json theme={null}
{
  "fromChainId": 8453,
  "toChainId": 42161,
  "sourceDomain": 6,
  "destinationDomain": 3,
  "account": "0x59F84Af036712AFe2fD0Ec77f4D9a6F0a612f1fE",
  "amount": "2200000",
  "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "fast": true,
  "maxFee": "220",
  "minFinalityThreshold": 1000,
  "quoteBlock": 24918233,
  "requires": [{ "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "2200000" }],
  "calls": [
    { "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "data": "0x095ea7b3...", "value": "0", "chainId": 8453, "type": "approve" },
    { "to": "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d", "data": "0xf856ddb6...", "value": "0", "chainId": 8453, "type": "bridge_burn" }
  ],
  "simulation": { "ok": true, "scope": "protocol_bundle", "engine": "wallet_neutral_atomic", "gasUsed": "162044", "quoteBlock": 24918233, "tokenOutDelta": "0", "assumedBalances": true }
}
```

`maxFee` is quoted from Circle for this route, amount and finality threshold, and is written into
the burn. `sourceDomain` and `destinationDomain` are CCTP domain ids, not chain ids — you need them
to track the message.

### After the burn

The bundle ends at the burn. To complete a cross-chain deposit:

1. Send the bridge bundle on `fromChainId`.
2. Poll bridge status with the source transaction hash until Circle's attestation lands and the
   USDC is minted to `account` on `toChainId`.
3. Request `GET /instruments/:instrumentId/calldata` on the destination chain and send that bundle.

<Note>
  Step 3 is a fresh request, built at a fresh quote block. Do not build it before the mint: its
  simulation would be pinned to a block where the funds have not arrived.
</Note>

### Errors

* `400` — a chain not configured for CCTP V2, identical source and destination, `fast=true` where
  fast transfer is unavailable, or a reverted simulation.
* `502` — Circle's fee quote was unavailable.
* `503` — RPC or the simulator was unavailable.

***

## Executing a bundle

The calls are plain transactions. Send them in the order returned, on `chainId`:

* **EOA** — one transaction per call, each confirmed before the next. Atomicity is the simulation's
  claim about the calls, not a guarantee about your sending; a bundle stopped halfway leaves the
  account holding whatever the last completed call produced.
* **Smart account (ERC-4337 / EIP-5792)** — send the calls as one batch and the bundle lands or
  reverts as a whole, which is what the simulation actually modelled. Gas and sponsorship are your
  wallet's concern; 1tx neither pays nor requires a paymaster.

Rebuild rather than reuse. A bundle is pinned to `quoteBlock`, and a bundle carrying a swap is
dead after `expiresAt`.
