Skip to main content
When chaining multiple actions in a single intent, you often don’t know the exact amounts or addresses until the transaction executes onchain. The SDK provides two placeholders that resolve at execution time.

dynamic()

dynamic() represents whatever balance the intent wallet holds at execution time. Pass it as the amount for any action that should consume the output of the previous step.
import { swap, lend, dynamic } from "0xtrails";

[
  swap({
    tokenIn: "ETH",
    tokenOut: "USDC",
    amountIn: "1",        // spend exactly 1 ETH
  }),
  lend({
    marketId: "aave-v3-usdc-polygon",
    token: "USDC",
    amount: dynamic(),    // lend whatever USDC the swap produced
  }),
]
This is the standard pattern for multi-step sequences. Bridge fees, slippage, and price moves mean the exact output of one step can’t be known in advance. dynamic() eliminates the need to predict it. dynamic() is accepted by swap, lend, deposit, and custom. It is not accepted by assertCondition, which evaluates concrete conditions.

self()

self() resolves to the intent wallet address at execution time. The intent wallet is created per-transaction and its address is unknown before execution.
import { deposit, self } from "0xtrails";

deposit({
  marketId: "morpho-usdc-vault",
  token: "USDC",
  amount: "100",
  recipient: self(), // deposit on behalf of the intent wallet itself
})
In most cases you don’t need self() explicitly. Swap defaults to self() for intermediate actions and to the user’s wallet for the final action. It is available for unusual compositions where you need to be explicit about the recipient.

Using both together

import { swap, deposit, dynamic, self } from "0xtrails";

[
  swap({
    tokenIn: "USDT",
    tokenOut: "USDC",
    amountIn: dynamic(),  // consume incoming USDT
  }),
  deposit({
    marketId: "yearn-usdc-vault",
    token: "USDC",
    amount: dynamic(),    // deposit the USDC from the swap
    recipient: self(),    // vault shares go to the intent wallet
  }),
]