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.
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 Policy Manager & Merkle Trees). 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.
msg.sender on the onchain tx must equal that strategist_address — the Manager reverts otherwise. Strategists are typically Safes or other multisigs, not EOAs, so submission usually means proposing the transaction through the Safe and executing after threshold.
The workflow:
Discover what actions are available for a vault —
GET /actionsInspect constraints for actions with correlated parameters —
GET /constraintsBuild a single action —
POST /execute— or several atomically —POST /execute-multipleSubmit the returned
encoded_tx_datato the Manager from the strategist address
Every action endpoint is scoped by three path parameters:
chain
Lowercase chain slug (e.g. ethereum, base, arbitrum). See Chain Support 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.
1. List available actions
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
Response structure
A JSON object keyed by route (e.g. aavev3/supply). Each entry describes one action:
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:
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)
Example response (abbreviated)
2. Inspect parameter constraints
When has_constraints is true, not every combination of choices is valid. The constraints endpoint returns the valid combinations.
Example request
Example response
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
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
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
Response structure
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 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:
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
4. Build multiple actions (atomic batch)
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
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:
Response structure
Same shape as /execute, with two differences:
descriptionis always"Multiple actions execution"action_api_responseis an array — one entry per action, in the same order asactions
Example response
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. 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.
Last updated