Skip to main content
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
Requires @trustware/sdk 1.1.10 or later, through the headless core or the REST API. The widget and Trustware.runTopUp() do not accept hooks.

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 or Trustware.setDestinationAddress(). A posthook is only for running a contract call.

Requirements and support

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. 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.
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.

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.
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.

2. Build the route with a destination posthook

Pass the encoded call as hooks.postHook. Everything outside hooks is a normal route request.
hooks is optional and additive. Omit it and buildRoute behaves exactly as it did before 1.1.10.

Posthook fields

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.
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

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.

5. Submit the receipt and poll status

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.
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.
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.
Reading your own contract, as above, is the strongest check and works regardless of the flag.
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.

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

Destination call options

ERC-20 destination calls, dynamic landed-balance mode, fallback, deposit-address routes, and error handling.

Headless core

The full Trustware namespace API, including buildRoute, sendRouteTransaction, and pollStatus.

POST /route

The REST request and response schema, including hooks.postHook and route.execution.approvals.

TypeScript types

PostHookRequest, RouteApproval, RoutePlan, and RouteEstimate.

Error handling

Posthook validation errors, approval failures, and the imperative try/catch pattern.