Skip to main content
A programmable destination is an action encoded into the payment intent that executes automatically when funds arrive at their destination. The customer confirms once; the funding and the product action happen together. This removes a second transaction step that would otherwise require the customer to separately initiate a deposit, stake, or other product action after funds have already arrived.

How it works

When you include destination calldata, funds are delivered to the destination contract and the encoded action is called immediately using the exact amount that arrived. You do not need to know the final amount in advance; the router substitutes it at execution time.
  1. You encode the desired action (for example, a vault deposit) with a placeholder for the token amount.
  2. The API quotes a route that ends at the destination contract.
  3. Funds route and arrive at the contract.
  4. The encoded action executes with the actual arrived amount substituted in.

The amount placeholder

When encoding calldata, call dynamic() in the position where the token amount should appear. Trails replaces this placeholder with the exact amount of tokens that arrive at execution time. This matters because the final amount may differ slightly from the quoted amount due to slippage or fee changes between quote and execution. Using the placeholder ensures the action always matches what actually arrived at execution time. For other placeholders like self() (the intent wallet address), see Dynamic values.
import { Fund, dynamic } from "0xtrails";
import { encodeFunctionData } from "viem";

const calldata = encodeFunctionData({
  abi: VAULT_ABI,
  functionName: "deposit",
  args: [dynamic(), "0xCUSTOMER_ADDRESS"],
});

export function VaultFundButton() {
  return (
    <Fund
      apiKey="YOUR_API_KEY"
      to={{
        recipient: "0xYOUR_VAULT_CONTRACT",
        token: "USDC",
        chain: "polygon",
        calldata,
      }}
      onFundingSuccess={(result) => console.log("Deposited:", result)}
    />
  );
}

Direct API

For server-side or headless integrations, pass destinationCalldata in the quote request:
import { TrailsApi, dynamic } from "@0xtrails/api";
import { encodeFunctionData } from "viem";

const destinationCalldata = encodeFunctionData({
  abi: VAULT_ABI,
  functionName: "deposit",
  args: [dynamic(), "0xCUSTOMER_ADDRESS"],
});

const trails = new TrailsApi({
  baseUrl: "https://trails-api.sequence.app/rpc/Trails/",
  accessKey: "YOUR_ACCESS_KEY",
});

const { intent, feeOptions } = await trails.quoteIntent({
  ownerAddress: "0xCUSTOMER_ADDRESS",
  originChainId: 1,
  originTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  destinationChainId: 137,
  destinationTokenAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
  destinationToAddress: "0xYOUR_VAULT_CONTRACT",
  tradeType: "EXACT_OUTPUT",
  destinationTokenAmount: "100000000", // 100 USDC
  destinationCalldata,
});

Common destination actions

ActionDescription
Yield vault depositDeposit into an ERC-4626 or compatible vault; user receives yield-bearing shares
StakingStake tokens in a protocol; user earns staking rewards
Lending depositSupply to a lending protocol; user earns interest
Custom contract callAny ABI-encoded function call on any contract at the destination
Programmable destinations combine with any funding source. A customer can fund from a debit card and deposit into a yield vault in one action, with no intermediate steps.
For a full walkthrough of vault deposit integration, see the Morpho vault deposit guide.