> For the complete documentation index, see [llms.txt](https://docs.veda.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.veda.tech/api/transaction-builder.md).

# Transaction Builder

The Transaction Builder API turns a high-level intent — "supply 1,000 USDC to Aave V3" or "bridge USDC from Ethereum to Base via CCTP" — into ready-to-submit calldata for a vault's Manager contract. It's the same engine that powers the rebalance feature in the Veda Console, exposed for programmatic use.

**Base URL:** `https://api.veda.tech`

**OpenAPI / Swagger:** [api.veda.tech/docs](https://api.veda.tech/docs)

{% hint style="danger" %}
**Always verify calldata independently before signing.** The Transaction Builder is a convenience — it is not a substitute for review. Decode the returned `encoded_tx_data` against the Manager ABI and run it through [Tenderly](https://dashboard.tenderly.co/) or another simulator before broadcasting. Confirm the target address and every encoded argument match what you intended. **Never rely solely on the API response for correctness.** Independent simulation is the last line of defense.
{% endhint %}

## Concepts

Veda vaults route depositor funds to trusted DeFi protocols according to the curator's predefined yield strategies. A **rebalance** — moving funds between positions, swapping, supplying, withdrawing — is one or more of those onchain actions, executed through the vault's Manager contract.

The Manager only accepts actions that are present as leaves in the vault's active merkle tree; every call must carry a merkle proof against the root pinned on the Manager (see [Access Control & Merkle Trees](/api/access-control-and-merkle-trees.md)). The Transaction Builder embeds the right proof for the action you ask it to build, so the calldata it returns is one the Manager will accept.

The API is **build-only**: it returns encoded calldata and a target address; you sign and submit the transaction from your strategist address.

The workflow:

1. **Discover** what actions are available for a vault — `GET /actions`
2. **Inspect constraints** for actions with correlated parameters — `GET /constraints`
3. **Build** a single action — `POST /execute` — or several atomically — `POST /execute-multiple`
4. **Submit** the returned `encoded_tx_data` to the Manager from the strategist address

Every action endpoint is scoped by three path parameters:

| Param                | Meaning                                                                                                                                               |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chain`              | Lowercase chain slug (e.g. `ethereum`, `base`, `arbitrum`). See [Chain Support](/resources/chain-support.md) for the full list.                       |
| `vault_address`      | The BoringVault contract address                                                                                                                      |
| `strategist_address` | The strategist address whose merkle tree / permissions are being used. A single vault can have multiple strategists with different permitted actions. |

## Authentication

All Transaction Builder endpoints require an API key created in the [Veda Console](https://console.veda.tech). Pass it as a Bearer token:

```
Authorization: Bearer veda_user_live_...
```

See [Quick Start](/api/quick-start.md) for how to create one. All examples below assume the key is in `$VEDA_API_KEY`.

***

## 1. List available actions

```
GET /v1/tx-builder/api/actions/{chain}/{vault_address}/{strategist_address}
```

Returns every action the vault's merkle tree authorizes for the given strategist, along with the parameter metadata you need to build a transaction. No request body, no query parameters.

### Example request

```bash
curl https://api.veda.tech/v1/tx-builder/api/actions/ethereum/0xA20f97813014129E7609171d2D3AA3da5206259e/0x2322ba43eFF1542b6A7bAeD35e66099Ea0d12Bd1 \
  -H "Authorization: Bearer $VEDA_API_KEY"
```

### Response structure

A JSON object keyed by **route** (e.g. `aavev3/supply`). Each entry describes one action:

| Field              | Type    | Meaning                                                                               |
| ------------------ | ------- | ------------------------------------------------------------------------------------- |
| `name`             | string  | Human-readable action name                                                            |
| `protocol`         | string  | Protocol identifier (e.g. `AaveV3`, `1Inch`)                                          |
| `protocol_app_url` | string  | Link to the protocol's app, when applicable                                           |
| `description`      | string  | What the action does                                                                  |
| `has_constraints`  | boolean | If `true`, params are correlated — call `/constraints/{route}` for valid combinations |
| `params`           | array   | Parameter definitions (see below)                                                     |

Each `params[i]` is:

| Field             | Type            | Meaning                                                                                                                      |
| ----------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `name`            | string          | Parameter name to pass in the execute request                                                                                |
| `type`            | string          | Python type name (`str`, `int`, `float`, `Decimal`)                                                                          |
| `description`     | string          | Human-readable description                                                                                                   |
| `required`        | boolean         | Whether the parameter must be provided                                                                                       |
| `default_value`   | any \| null     | Default if not provided (only meaningful for non-required params)                                                            |
| `choices`         | array \| `null` | Allowed values. `[]` means freeform; `null` means optional and may be omitted; a populated list means you must pick from it. |
| `min_value`       | number \| null  | Minimum valid value, if applicable                                                                                           |
| `max_value`       | number \| null  | Maximum valid value, if applicable                                                                                           |
| `omitToUseAll`    | boolean         | If `true`, omitting this parameter uses the vault's full balance of the token                                                |
| `acceptRawString` | boolean         | If `true`, the value is forwarded as-is (no numeric conversion)                                                              |

{% hint style="info" %}
**Grouped choices.** Most params have `choices` as a flat array of strings (`["USDC", "USDT"]`). Some params use **nested arrays** (`[["USDC", "0xa0b8..."], ["USDT", "0xdac1..."]]`) where each inner array is `[display_label, value]`. When building a UI, check whether the first element of `choices` is itself an array to decide which format you're dealing with.
{% endhint %}

### Example response (abbreviated)

```json
{
  "aavev3/supply": {
    "name": "Supply",
    "protocol": "AaveV3",
    "protocol_app_url": "https://app.aave.com",
    "description": "Supply tokens to AaveV3",
    "has_constraints": false,
    "params": [
      { "name": "token",  "type": "str",   "required": true, "choices": ["USDC", "USDT"] },
      { "name": "amount", "type": "float", "required": true, "choices": [] },
      { "name": "market", "type": "str",   "required": true, "choices": ["core"] }
    ]
  },
  "one_inch/swap": {
    "name": "Swap",
    "protocol": "1Inch",
    "protocol_app_url": "https://app.1inch.io",
    "description": "Swap tokens using 1Inch",
    "has_constraints": true,
    "params": [
      { "name": "token_in",  "type": "str",   "required": true, "choices": ["MORPHO", "USDC", "USDT"] },
      { "name": "token_out", "type": "str",   "required": true, "choices": ["USDC", "USDT"] },
      { "name": "amount",    "type": "float", "required": true, "choices": [] },
      {
        "name": "slippage_tolerance",
        "type": "float",
        "required": true,
        "default_value": 0.0001,
        "choices": [],
        "min_value": 0,
        "max_value": 0.1
      }
    ]
  }
}
```

{% hint style="info" %}
When `has_constraints` is `true`, the values in each param's `choices` are the **projection** of the valid combinations onto that one param. You still need to call the constraints endpoint to know which combinations are actually valid (e.g. `USDC → USDT` may be allowed but `USDC → USDC` is not).
{% endhint %}

***

## 2. Inspect parameter constraints

When `has_constraints` is `true`, not every combination of `choices` is valid. The constraints endpoint returns the valid combinations.

```
GET /v1/tx-builder/api/actions/{chain}/{vault_address}/{strategist_address}/constraints/{route}
```

### Example request

```bash
curl https://api.veda.tech/v1/tx-builder/api/actions/ethereum/0xA20f97813014129E7609171d2D3AA3da5206259e/0x2322ba43eFF1542b6A7bAeD35e66099Ea0d12Bd1/constraints/one_inch/swap \
  -H "Authorization: Bearer $VEDA_API_KEY"
```

### Example response

```json
{
  "route": "one_inch/swap",
  "constraints": [
    { "token_in": "MORPHO", "token_out": "USDC" },
    { "token_in": "MORPHO", "token_out": "USDT" },
    { "token_in": "USDC",   "token_out": "USDT" },
    { "token_in": "USDT",   "token_out": "USDC" }
  ]
}
```

Each entry in `constraints` is one valid combination. Use this to drive dependent dropdowns in a UI, or to filter to a single combination in a script. Routes where `has_constraints` is `false` have fully independent parameters — any combination of values from `choices` is valid.

A `404` from this endpoint means the route has no parameter constraints (i.e. you didn't need to call it).

***

## 3. Build a single action

```
POST /v1/tx-builder/api/execute/{chain}/{vault_address}/{tail:path}
```

The `tail` matcher captures everything after the vault. It accepts either of:

* `{strategist_address}/{route}` — recommended; explicit
* `{route}` — the API resolves the strategist for the vault

When `tail` begins with a `0x…`-prefixed 42-character string, that prefix is treated as the strategist address and the rest is the route. The route itself contains a slash (e.g. `aavev3/supply`), which is why `tail:path` is used.

### Request body

| Field      | Type                  | Default | Description                                                                                                                                                                       |
| ---------- | --------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `params`   | object (string → any) | `{}`    | Key-value map of action parameters, matching the `/actions` metadata for this route                                                                                               |
| `verbose`  | boolean               | `false` | Return detailed simulation output — token transfers, gas cost in ETH, and protocol-level metadata in `action_api_response` (including warnings). Recommended for any review step. |
| `skip_sim` | boolean               | `false` | Skip the Tenderly simulation. Set `true` for faster responses or when you've already simulated.                                                                                   |

### Example request

```bash
curl -X POST \
  -H "Authorization: Bearer $VEDA_API_KEY" \
  -H "Content-Type: application/json" \
  https://api.veda.tech/v1/tx-builder/api/execute/ethereum/0xA20f97813014129E7609171d2D3AA3da5206259e/0x2322ba43eFF1542b6A7bAeD35e66099Ea0d12Bd1/aavev3/supply \
  -d '{
    "params": { "token": "USDC", "amount": 1000, "market": "core" },
    "skip_sim": false
  }'
```

### Response structure

| Field                     | Type              | Always present? | Meaning                                                                                          |
| ------------------------- | ----------------- | --------------- | ------------------------------------------------------------------------------------------------ |
| `encoded_tx_data`         | string            | Yes             | Calldata (hex, `0x`-prefixed) to submit to the vault's Manager contract                          |
| `target`                  | string            | Yes             | Manager contract address — the `to` of your transaction                                          |
| `description`             | string            | Yes             | Human-readable description of the action being built                                             |
| `action_api_response`     | object \| null    | Yes             | Protocol-specific metadata. See [Simulation warnings](#simulation-warnings) for the warning keys |
| `simulation_status`       | boolean \| string | When simulated  | `true` if the Tenderly simulation succeeded; may also be an error string on failure              |
| `tenderly_simulation_url` | string \| null    | When simulated  | Link to the Tenderly simulation                                                                  |
| `simulation_logs`         | array             | When simulated  | Logs emitted during the simulation (when available)                                              |
| `simulation_gas_used`     | number \| null    | When simulated  | Gas used during the simulation (when available)                                                  |
| `simulation_gas_cost_eth` | string \| null    | When verbose    | Estimated gas cost denominated in ETH                                                            |
| `token_transfers`         | array             | When verbose    | Structured list of tokens moved during the simulation (see below)                                |
| `simulation_skipped`      | boolean           | When skipped    | Present and `true` when `skip_sim: true` was passed                                              |

#### Token transfers

When `verbose: true`, the response includes a `token_transfers` array with one entry per token movement observed during the simulation:

| Field           | Type                     | Meaning                                               |
| --------------- | ------------------------ | ----------------------------------------------------- |
| `token`         | string                   | Token symbol (e.g. `USDC`)                            |
| `token_address` | string                   | Token contract address                                |
| `amount`        | string                   | Human-readable amount in display units                |
| `raw_amount`    | string                   | Amount in the token's smallest unit (wei / base unit) |
| `direction`     | `"in"` \| `"out"`        | Whether the vault receives or sends the token         |
| `dollar_value`  | string \| number \| null | Estimated USD value, when a price is available        |

### Example response

```json
{
  "encoded_tx_data": "0x244b0f6a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001...",
  "target": "0x20dCC05A7fE66d6442bE7d50C9EC41c4Bfda7f68",
  "description": "Supply tokens to AaveV3",
  "action_api_response": {
    "Input Amount": "1,000.00 USDC",
    "Output Amount": "999.87 aUSDC"
  },
  "simulation_status": true,
  "tenderly_simulation_url": "https://www.tdly.co/shared/simulation/abc123",
  "simulation_gas_used": 312418,
  "simulation_gas_cost_eth": "0.004218",
  "token_transfers": [
    {
      "token": "USDC",
      "token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "amount": "1,000.00",
      "raw_amount": "1000000000",
      "direction": "out",
      "dollar_value": "1000.00"
    },
    {
      "token": "aUSDC",
      "token_address": "0xBcca60bB61934080951369a648Fb03DF4F96263C",
      "amount": "999.87",
      "raw_amount": "999870000",
      "direction": "in",
      "dollar_value": "999.87"
    }
  ]
}
```

{% hint style="info" %}
The `token_transfers` and `simulation_gas_cost_eth` fields are only present when the request includes `verbose: true`. Without it, you still get `simulation_status`, `simulation_gas_used`, and `tenderly_simulation_url`.
{% endhint %}

***

## 4. Build multiple actions (atomic batch)

```
POST /v1/tx-builder/api/execute-multiple/{chain}/{vault_address}/{strategist_address}
```

Build a single atomic transaction that runs several actions in order. The response is one piece of calldata that calls the Manager with all actions packed together — recommended for any rebalance that involves more than one step (e.g. swap then supply, withdraw from one position and deposit into another).

### Request body

| Field      | Type             | Default | Description                                                                                    |
| ---------- | ---------------- | ------- | ---------------------------------------------------------------------------------------------- |
| `actions`  | array of objects | —       | One entry per action. Each is `{ "route": string, "params": object }`.                         |
| `verbose`  | boolean          | `false` | Return detailed simulation output — token transfers, gas cost in ETH, and per-action metadata. |
| `skip_sim` | boolean          | `false` | Skip the Tenderly simulation                                                                   |

An empty `actions` array is rejected with `400 No actions provided`.

### Example request

Swap 1,000 USDC into USDT via 1Inch, then supply 500 USDC into Aave V3 — in a single onchain transaction:

```bash
curl -X POST \
  -H "Authorization: Bearer $VEDA_API_KEY" \
  -H "Content-Type: application/json" \
  https://api.veda.tech/v1/tx-builder/api/execute-multiple/ethereum/0xA20f97813014129E7609171d2D3AA3da5206259e/0x2322ba43eFF1542b6A7bAeD35e66099Ea0d12Bd1 \
  -d '{
    "actions": [
      {
        "route": "one_inch/swap",
        "params": {
          "token_in": "USDC",
          "token_out": "USDT",
          "amount": 1000,
          "slippage_tolerance": 0.0001
        }
      },
      {
        "route": "aavev3/supply",
        "params": { "token": "USDC", "amount": 500, "market": "core" }
      }
    ],
    "skip_sim": false
  }'
```

### Response structure

Same shape as `/execute`, with two differences:

* `description` is always `"Multiple actions execution"`
* `action_api_response` is an **array** — one entry per action, in the same order as `actions`

### Example response

```json
{
  "encoded_tx_data": "0x244b0f6a...",
  "target": "0x20dCC05A7fE66d6442bE7d50C9EC41c4Bfda7f68",
  "description": "Multiple actions execution",
  "action_api_response": [
    { "expected_out": "999.42 USDT", "tx_payload": { "...": "..." } },
    null
  ],
  "simulation_status": true,
  "tenderly_simulation_url": "https://www.tdly.co/shared/simulation/def456"
}
```

***

## Error handling

The Transaction Builder endpoints predate the standardized error envelope used by the rest of the v1 API. Non-2xx responses come back on a `detail` field, which can be a string (`{ "detail": "Vault not found" }`), an object (`{ "detail": { "error": "...", "details": "..." } }`), or — for `422` validation errors — a Pydantic-style array (`{ "detail": [{ "loc": [...], "msg": "...", "type": "..." }] }`). For authentication failures from the gateway, see [API Reference → Authentication errors](/api/api-reference.md#authentication-errors). And remember that a `200` can still carry `error` / `details` alongside `encoded_tx_data` when calldata was built but simulation failed — always check `simulation_status`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.veda.tech/api/transaction-builder.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
