Read deposit into vault destinations first. Every
example here assumes that six-step flow and changes only the posthook. The full
hooks.postHook field reference is there too.- your function pulls an ERC-20 instead of taking native value: ERC-20 destination calls
- the amount is unknown when you encode the call: amount patching
- funds need somewhere to land if the call fails: fallback behavior
- source funds arrive by plain payment: deposit-address routes
- the destination is not a vault: call other destination contracts
Source token approvals
Source token approvals and destination call funding are two different mechanisms. Do not conflate them. Source token approval is a wallet action on the source chain. For an ERC-20 source asset, the route response can includeroute.execution.approvals[], each entry carrying the tokenAddress, spender, and amount to approve before the route transaction can succeed. The spender is a provider contract, never a Trustware one.
sendRouteTransaction handles these on EVM routes: it reads the current allowance, approves the exact amount when one is missing, waits for confirmation, then requests the route signature. It never grants an unlimited allowance. If the allowance read fails, it approves anyway rather than sending a transaction it knows would revert.
Two consequences for your UI:
- The user can see more than one wallet prompt. Tell them an approval may come first, then the deposit itself.
- Each approval must confirm on chain before the route signature is requested, so the call can stay pending for a minute or more.
route.execution.approvals[], grant each allowance, wait for confirmation, then send route.execution.transaction. Every field is optional, so skip any entry missing a spender, tokenAddress, or non-zero amount.
Destination call funding is toApprovalAddress, covered next. It is not a wallet prompt.
ERC-20 destination calls
If your destination function pulls tokens withtransferFrom instead of taking native value, set toApprovalAddress to the address allowed to pull fundToken. That is normally the contract performing the transferFrom, usually the vault itself.
depositFor is illustrative. Substitute your own pull-based deposit function and ABI.
Note there is no value field. The call is funded with an ERC-20, so nothing native is attached.
How the allowance is granted depends on the provider that resolves the route. Some prepend an ERC-20 approve() ahead of your call, patching the approval amount alongside your call’s amount when fullAmount is set. Others hand it to their own execution engine. You set the same field either way.
Amount patching
In this mode, the amount argument in your calldata is patched for you.How it is patched is provider dependent. Some providers patch it at
execution time with the amount received. For others, Trustware patches it
before building the route, with a quoted
toAmountMin, or toAmount when
no minimum is quoted.fullAmount: true and use amountInputPos to say which ABI argument holds the amount.
amountInputPos is the zero-based index of the ABI argument to patch. In depositFor(address recipient, address token, uint256 amount) the amount is the third argument, so it is 2. Index 0 is valid, so do not treat a falsy index as unset. The argument must be a static type such as uint256. Patching a dynamic argument such as bytes corrupts the encoding.
Do not send fundAmount in this mode. buildRoute rejects the request before it is sent if fullAmount is true and amountInputPos is missing.
Fallback behavior
toFallbackAddress names an address that receives the funds if the destination call fails.
Deposit-address routes
Trustware.buildDepositAddress() accepts the same hooks.postHook shape as buildRoute. Use it when the source funds arrive by plain payment to an address rather than from a wallet you can prompt.
Deposit-address routing is provider dependent, and so are deposit-address
routes with a posthook. This path needs a provider that supports both, so it
is not available on every route.
buildDepositAddress() takes the same request body as buildRoute() and returns a deposit address instead of a signable transaction.
Call other destination contracts
Nothing abouthooks.postHook is vault specific. It executes an arbitrary encoded call, so the same pattern covers staking contracts, margin accounts, lending pools, and anything else you control. Only the ABI and the function you encode change.
Two questions decide which sections apply to you:
- Does your function accept native value or pull an ERC-20? Native value uses
value. An ERC-20 pull usestoApprovalAddressand novalue. - Do you know the amount in advance? If yes, use a predetermined
fundAmount. If not, use amount patching.
Errors and failure handling
buildRoute and buildDepositAddress reject a structurally incomplete posthook before sending. These four are thrown as a plain Error with no code, so instanceof TrustwareError and error.code branching do not apply.
To run the same checks before building a route, for example in form validation, call
assertValidPostHook({ postHook }). It throws the same errors and requires @trustware/sdk 1.1.11 or later.
fullAmount and amountInputPos are provider dependent, as covered in amount patching.
These checks only confirm completeness. The API is authoritative for what the client cannot check: calldata correctness, destination compatibility, and provider eligibility. A rejected posthook returns 400 with a message describing the problem.
Two more plain Error cases come from the approval step inside sendRouteTransaction: Approval transaction reverted and Timed out waiting for approval confirmation. Treat both as recoverable and let the user retry.
Related reference
Deposit into vault destinations
The six-step walkthrough, the
hooks.postHook field reference, and the complete example.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.Error handling
Posthook validation errors, approval failures, and the imperative try/catch pattern.
