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

# Deposit into vault destinations

> Deposit any supported asset into a vault or another destination contract in one flow, using a destination posthook on Trustware.buildRoute.

A destination posthook calls a contract on the destination chain once routed funds arrive, instead of paying the destination token to a recipient address. A user holding USDC on Arbitrum ends up deposited in a vault on Base, without visiting the vault.

The flow is the same six steps every time:

1. Encode the destination contract call with viem or ethers
2. `Trustware.buildRoute()` with `hooks.postHook` to get a signable route
3. Show the user the final route estimate
4. `Trustware.sendRouteTransaction()` to sign and send on the source chain
5. `Trustware.submitReceipt()` and `Trustware.pollStatus()` to track settlement
6. Read the destination contract to confirm the deposit landed

<Note>
  Requires `@trustware/sdk` 1.1.10 or later, through the
  [headless core](/integration/headless-core) or the
  [REST API](/api-reference/route). The widget and `Trustware.runTopUp()` do not
  accept `hooks`.
</Note>

## When to use this pattern

Choose this pattern when:

* your product holds user funds in a vault, staking contract, or margin account and you want deposits to arrive already credited
* your users hold assets on chains your contract is not deployed on
* you want to remove the step where a user has to approve and deposit on the destination chain themselves

If funds only need to arrive at an address, use a normal route and set the recipient through [route configuration](/configuration/routes) or [`Trustware.setDestinationAddress()`](/guides/runtime-destination). A posthook is only for running a contract call.

## Requirements and support

| Requirement      | Detail                                                                                                                              |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| SDK version      | `@trustware/sdk` 1.1.10 or later                                                                                                    |
| Integration path | Headless core or REST API. The widget and `runTopUp()` do not accept `hooks`.                                                       |
| Destination      | An EVM chain, with your contract already deployed and its ABI known to you                                                          |
| Amount           | A predetermined destination-token amount, or dynamic landed-balance mode where supported                                            |
| Signatures       | One route signature on the source chain, plus a source token approval if the source asset is an ERC-20 without sufficient allowance |

`fundAmount` is the destination-token amount for the contract call. It is unrelated to `fixedFromAmount`, which locks the widget's source USD amount; see [fixed deposit amounts](/guides/fixed-amount).

Destination contract calls are an EVM-only capability. `postHook.target` must be a valid EVM address, and the destination chain must be an EVM chain.

Some capabilities depend on which liquidity provider resolves the route.

| Capability                                           | Support                                                                                                                                         |
| ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Predetermined `fundAmount`                           | Portable. Works with every provider. Include `estimatedGas` to keep the widest provider eligibility.                                            |
| Native-value destination calls                       | Supported. Use `value` and omit `toApprovalAddress`.                                                                                            |
| ERC-20 destination calls through `toApprovalAddress` | Supported, though the mechanism differs by provider. See [ERC-20 destination calls](/guides/destination-call-options#erc-20-destination-calls). |
| Dynamic landed-balance mode (`fullAmount`)           | **Provider dependent.** Only providers that implement amount patching are selected for these requests.                                          |
| Destination-call fallback (`toFallbackAddress`)      | **Provider dependent.** Not implemented by every provider, so never rely on it as a guarantee.                                                  |
| Deposit-address routes with a posthook               | **Provider dependent.** Needs a provider that supports both deposit-address routing and destination calls.                                      |

<Warning>
  Trustware validates only that your posthook is structurally complete. It does
  not check that your calldata is correct, that `target` is a contract, that the
  funding amount is sufficient, or that the destination contract accepts the call.
  A posthook encoding the wrong function or amount still routes, then fails on the
  destination chain. Test every destination call on the route you intend to use.
</Warning>

## How the flow works

The user signs one route transaction on the source chain, plus a source token approval if the source asset is an ERC-20 without sufficient allowance. The provider then executes your call on the destination chain, funded with the destination asset. There is no second signature.

The destination call is executed by the provider, not by the user's wallet and not by a Trustware contract. Trustware builds and tracks the route; it never holds the funds.

The destination call is a separate on-chain execution, so a route can move value and still have the call fail. There is no destination-call status field, so confirm the route reached a terminal `success` status and then read the destination contract, as in steps 5 and 6.

## 1. Encode the destination call

Encode the function you want to run on the destination chain. This example uses viem.

```ts theme={null}
import { encodeFunctionData, parseAbi } from "viem";

const vaultAbi = parseAbi([
  "function depositNativeFor(address recipient) payable",
  "function nativeBalanceOf(address account) view returns (uint256)",
]);

const callData = encodeFunctionData({
  abi: vaultAbi,
  functionName: "depositNativeFor",
  args: ["0xYourRecipientAddress"],
});
```

<Note>
  `depositNativeFor(address)` and `nativeBalanceOf(address)` are illustrative.
  Substitute your own deposit function, balance accessor, and ABI. If your deposit
  function takes an amount argument, encode the same value you pass as
  `fundAmount`, or the call and the funds disagree.
</Note>

## 2. Build the route with a destination posthook

Pass the encoded call as `hooks.postHook`. Everything outside `hooks` is a normal route request.

```ts theme={null}
import { Trustware } from "@trustware/sdk";

const fundAmount = "2000000000000000"; // 0.002 ETH in wei, destination base units

const route = await Trustware.buildRoute({
  fromChain: "42161",
  toChain: "8453",
  fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
  toToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", // native asset on Base
  fromAmount: "25000000", // 25 USDC, source base units
  fromAddress: await Trustware.getAddress(),
  toAddress: "0xYourRecipientAddress",
  hooks: {
    postHook: {
      target: "0xYourVaultAddress",
      callData,
      value: fundAmount,
      fundToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
      fundAmount,
      estimatedGas: "150000",
    },
  },
});
```

`hooks` is optional and additive. Omit it and `buildRoute` behaves exactly as it did before 1.1.10.

### Posthook fields

| Field               | Required                       | Description                                                                                                                                                                                                                                               |
| ------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `target`            | Yes                            | The contract address to call on the destination chain. Must be a valid EVM address.                                                                                                                                                                       |
| `callData`          | Yes                            | ABI-encoded calldata for the call.                                                                                                                                                                                                                        |
| `value`             | Native-value calls only        | Native value to send with the call, in destination base units.                                                                                                                                                                                            |
| `fundToken`         | No                             | The token the call acts on. Defaults to the route's `toToken`.                                                                                                                                                                                            |
| `fundAmount`        | Yes unless `fullAmount` is set | The predetermined destination-token amount the call is funded with, in destination base units.                                                                                                                                                            |
| `estimatedGas`      | Recommended                    | Gas limit hint for the destination call. Some providers require it, so omitting it narrows which providers can serve the route.                                                                                                                           |
| `toApprovalAddress` | ERC-20 destination calls       | The address allowed to pull `fundToken` when your contract uses `transferFrom`. Must not be set for native-value calls. See [ERC-20 destination calls](/guides/destination-call-options#erc-20-destination-calls).                                        |
| `fullAmount`        | No                             | **Provider dependent.** Patch the encoded call with the amount that actually arrives instead of a predetermined `fundAmount`. Requires `amountInputPos`. See [dynamic landed-balance mode](/guides/destination-call-options#dynamic-landed-balance-mode). |
| `amountInputPos`    | With `fullAmount`              | **Provider dependent**, and required whenever `fullAmount` is set. The zero-based index of the ABI argument to patch. Index `0` is valid.                                                                                                                 |
| `toFallbackAddress` | No                             | **Provider dependent.** Where funds go if the destination call fails, when the resolving provider implements a fallback. See [fallback behavior](/guides/destination-call-options#fallback-behavior).                                                     |
| `description`       | No                             | Optional free-text label for the call. Not forwarded by every provider, so do not depend on it appearing downstream.                                                                                                                                      |

## 3. Review the final route estimate

Build the route with `hooks` attached before the user confirms, and show that route's estimate. A quote taken without `hooks` is not execution truth: a destination call changes gas and can change the selected provider.

```ts theme={null}
console.log(route.finalExchangeRate.fromAmountUSD);
console.log(route.finalExchangeRate.toAmountMinUSD);
console.log(route.route?.estimate?.toAmountMin);
```

Check that the amount the route guarantees to deliver covers the `fundAmount` you encoded. If it does not, lower `fundAmount`, raise `fromAmount`, or rebuild the route.

## 4. Sign and send the source transaction

```ts theme={null}
const txHash = await Trustware.sendRouteTransaction(route, 42161);
```

`sendRouteTransaction` switches the wallet to the source chain if needed, grants any source token allowance the route requires, then sends the route transaction. An ERC-20 source asset can mean an approval prompt before the deposit prompt, so plan for more than one signature. See [source token approvals](/guides/destination-call-options#source-token-approvals).

## 5. Submit the receipt and poll status

```ts theme={null}
await Trustware.submitReceipt(route.intentId, txHash);

const finalStatus = await Trustware.pollStatus(route.intentId);

if (finalStatus.status !== "success") {
  throw new Error(`Route did not complete: ${finalStatus.status}`);
}
```

<Warning>
  `pollStatus` resolves on `success`, on `failed`, and on a polling timeout, so a
  resolved promise is not proof of success. Check
  `finalStatus.status === "success"` before showing a completed state.
</Warning>

The route status reflects the provider's aggregate result for the whole route. There is no separate field reporting the destination call on its own, which is why step 6 exists.

## 6. Verify the deposit landed

Read your own contract before you build the route and again after settlement, so you are asserting a change rather than an absolute value.

```ts theme={null}
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";

const client = createPublicClient({ chain: base, transport: http() });

// Read this before step 2, so you have a baseline.
const balanceBefore = await client.readContract({
  address: "0xYourVaultAddress",
  abi: vaultAbi,
  functionName: "nativeBalanceOf",
  args: ["0xYourRecipientAddress"],
});

// Read it again after the route reaches a terminal success status.
const balanceAfter = await client.readContract({
  address: "0xYourVaultAddress",
  abi: vaultAbi,
  functionName: "nativeBalanceOf",
  args: ["0xYourRecipientAddress"],
});

if (balanceAfter - balanceBefore < BigInt(fundAmount)) {
  // The route settled but the deposit did not credit as expected.
  // Investigate with finalStatus.destTxHash before showing a success state.
}
```

`finalStatus.destTxHash` and `finalStatus.toChainTxUrl` give you the destination-chain execution to inspect or to attach to a support request alongside `route.intentId`.

`finalStatus.toAmountWei` reports the destination amount, but treat it as an estimate unless `finalStatus.landed_amount_verified` is `true`. That flag means Trustware read the amount from the destination chain rather than carrying forward a pre-trade quote. Check it before crediting a user or triggering anything automatic.

```ts theme={null}
if (finalStatus.landed_amount_verified) {
  // finalStatus.toAmountWei is the confirmed destination amount.
} else {
  // Still an estimate. Prefer your own contract read.
}
```

Reading your own contract, as above, is the strongest check and works regardless of the flag.

<Note>
  That is the primary recipe: a predetermined destination-token amount funding a
  native-value call. For ERC-20 funding, provider-patched landed amounts, fallback
  addresses, deposit-address routes, source token approvals, and the posthook and
  approval errors to handle, see
  [destination call options](/guides/destination-call-options).
</Note>

## Security considerations

* **You own the calldata.** Trustware encodes nothing on your behalf. Build `callData` from an ABI you control, on your own backend or from a constant in your app, never from unvalidated user input.
* **Pin `target`.** Treat the destination contract address as configuration, not something a client can choose. A posthook calls whatever address you give it.
* **Keep the encoded amount and the funding amount in agreement.** If your function takes an amount argument and you are not using dynamic landed-balance mode, encode the same value you pass as `fundAmount`.
* **Do not treat a settled route as a credited deposit.** Read your own contract, as in step 6.
* **Non-custodial throughout.** Trustware never holds the funds and never signs on the user's behalf. The route transaction is signed by the user's wallet and the destination call is executed by the provider.

## Complete example

```ts theme={null}
import { Trustware } from "@trustware/sdk";
import { createPublicClient, encodeFunctionData, http, parseAbi } from "viem";
import { base } from "viem/chains";

const VAULT = "0xYourVaultAddress";
const RECIPIENT = "0xYourRecipientAddress";
const NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";

const vaultAbi = parseAbi([
  "function depositNativeFor(address recipient) payable",
  "function nativeBalanceOf(address account) view returns (uint256)",
]);

const client = createPublicClient({ chain: base, transport: http() });

export async function depositIntoVault(
  fromChain: string,
  fromToken: string,
  fromAmount: string,
) {
  const fundAmount = "2000000000000000"; // 0.002 ETH in wei

  const balanceBefore = await client.readContract({
    address: VAULT,
    abi: vaultAbi,
    functionName: "nativeBalanceOf",
    args: [RECIPIENT],
  });

  const callData = encodeFunctionData({
    abi: vaultAbi,
    functionName: "depositNativeFor",
    args: [RECIPIENT],
  });

  const route = await Trustware.buildRoute({
    fromChain,
    toChain: "8453",
    fromToken,
    toToken: NATIVE,
    fromAmount,
    fromAddress: await Trustware.getAddress(),
    toAddress: RECIPIENT,
    hooks: {
      postHook: {
        target: VAULT,
        callData,
        value: fundAmount,
        fundToken: NATIVE,
        fundAmount,
        estimatedGas: "150000",
      },
    },
  });

  const txHash = await Trustware.sendRouteTransaction(route, Number(fromChain));
  await Trustware.submitReceipt(route.intentId, txHash);

  const finalStatus = await Trustware.pollStatus(route.intentId);
  if (finalStatus.status !== "success") {
    throw new Error(`Route did not complete: ${finalStatus.status}`);
  }

  const balanceAfter = await client.readContract({
    address: VAULT,
    abi: vaultAbi,
    functionName: "nativeBalanceOf",
    args: [RECIPIENT],
  });

  return {
    intentId: route.intentId,
    sourceTxHash: txHash,
    destTxHash: finalStatus.destTxHash,
    credited: balanceAfter - balanceBefore,
  };
}
```

## Related reference

<CardGroup cols={2}>
  <Card title="Destination call options" icon="sliders" href="/guides/destination-call-options">
    ERC-20 destination calls, dynamic landed-balance mode, fallback, deposit-address routes, and error handling.
  </Card>

  <Card title="Headless core" icon="terminal" href="/integration/headless-core">
    The full `Trustware` namespace API, including `buildRoute`, `sendRouteTransaction`, and `pollStatus`.
  </Card>

  <Card title="POST /route" icon="code" href="/api-reference/route">
    The REST request and response schema, including `hooks.postHook` and `route.execution.approvals`.
  </Card>

  <Card title="TypeScript types" icon="brackets-curly" href="/api-reference/types">
    `PostHookRequest`, `RouteApproval`, `RoutePlan`, and `RouteEstimate`.
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/events-errors/error-handling">
    Posthook validation errors, approval failures, and the imperative try/catch pattern.
  </Card>
</CardGroup>
