Skip to main content
What you’ll be able to do
  • Withdraw shielded funds to an address on another chain.
A bridge withdrawal spends shielded notes on Metal and sends a release instruction to the selected route’s vault. After the vault’s delay, anyone can settle the release into a holding address. A claimant key then authorizes a sweep to the final destination. Keep the withdrawal pending until that destination receives the tokens and the containing block is final on its chain.

Check notes, route and reserves

Start with recoverable, unspent bridged notes and the owner’s spending authorization. Obtain the route provider’s authenticated gateway, pool, route ID, destination vault and token, RPC endpoints, inbox and light-client configuration, public contract artifacts, and destination finality policy. The Metal signing account’s balance in the gas token pays its transaction fee. Fund the destination relayer’s signing account for that chain’s transaction fees. Use the bridge read interfaces to verify gateway pool(), pool bridgeGateway(), gateway routeOf(routeId), and foreignTokenOf(routeId, asset). Check the destination vault’s bridgeGateway(), metalInbox(), metalChainId() and supportedToken(token). The message route must match those values and accept Metal’s certificate epoch. The destination verifier accepts only epoch zero. Read assetRouteOf(routeId, token) and require the selected route’s reserve to cover the amount. This is the amount already minted through that route and available for withdrawal. Read pool bridgeLiabilityOf(asset) and the vault’s token balance, and verify both cover the withdrawal. Use withdrawalDelay() to display the destination-chain waiting period in the application. Obtain the route provider’s public note and key tools, matching BridgeWithdraw circuit artifact, and Barretenberg proving instructions. Prepare current note membership paths and a sanctions nonmembership witness, then check that the selected notes are unspent and their input lock IDs are unlocked. Bridge amounts retain the route’s raw units and must satisfy 0 < amount < 2^128.

Save the claimant’s recovery data

Choose a secp256k1 claimant key and a cryptographically random 32-byte salt. Store both securely before preparing the withdrawal, since both are needed to sweep the funds. The claimant key may differ from the keys used to spend notes or sign transactions. The proof’s destCommitment commits to this claimant and salt. The payout address is chosen when authorizing the sweep. Install viem with pnpm add viem and compute the commitment as follows:
Check the computation against known public fixtures or compare it with the destination vault’s claimCommitment(claimantAddress, salt) result. An RPC call reveals the salt to the endpoint, so use an endpoint you trust with that data.

Prove and submit the withdrawal

Use the public proving tools to construct the withdrawal request. It binds the pool asset, amount, route ID, claimant commitment, the note tree’s Merkle root, two input lock IDs, two nullifiers, change note commitment and sanctions root. The proof also binds Metal chain ID and pool contract address. Arrays contain exactly two entries, with zero padding for an unused input. Save the opening of any change note before submitting. The owner authorizes the complete statement with the spending key. Generate and locally verify the BridgeWithdraw proof against the pool’s current circuit. The sanctions root must match the pool’s current root at execution, so read it again before submission and regenerate the proof if it changed. Recheck the selected route’s reserve, then simulate and submit gateway.withdraw(request, withdrawProof) using the withdrawal ABI. Any Metal signing account may submit because the proof carries the owner’s authorization. Save signed transaction bytes and hash before broadcasting. Require a successful receipt with the pool’s BridgeWithdrawn and gateway’s WithdrawalRouted. Match asset, amount, route, claimant commitment and destination token. Save the actual withdrawal ID and outbox nonce. Verify the outbox’s message targets the configured vault with queueRelease(withdrawalId, token, amount, destCommitment) and authenticates the gateway as source sender. Confirm spent nullifiers, any appended change note, and the amount decrease in bridge liability and route reserve, accounting for concurrent activity when reading aggregate balances. After checking the receipt’s canonical block and Metal finality, mark inputs spent and change available in private wallet records. An insufficient reserve or invalid proof reverts the whole transaction, leaving the notes unspent.

Deliver the release and wait for the vault

Follow sending messages to construct a proof and deliver the saved outbox message through the destination’s MetalInbox. Check the inbox execution event and vault ReleaseQueued, then read releaseOf(withdrawalId). Verify token, amount and claimant commitment. The waiting period begins when the release is queued at the vault. releasableAt equals that destination block’s timestamp plus the vault’s withdrawalDelay. Compare the destination chain’s current block timestamp with releasableAt to decide when the release can be settled. At or after releasableAt, any destination signing account funded for transaction fees can call settle(withdrawalId). Use the release ABI, saving the signed attempt first. Require a successful receipt, the matching Settled event and releaseOf(withdrawalId).settled == true. Read claimAddress(withdrawalId) and verify the token transfer of the recorded amount from the vault to that holding address. Apply destination finality before recording settlement. The funds now rest in the holding address. Keep the withdrawal pending final delivery until the authorized sweep succeeds.

Authorize and submit the sweep

The claimant chooses the final destination and a Unix deadline in seconds on the destination chain. Validate the address against the application’s payment instruction and allow enough time for transaction inclusion. The signature binds destination chain ID, vault address, withdrawal ID, destination and deadline. With a viem local account or compatible signer for the claimant, sign the inner hash as a personal message:
claimant is the signer for the address derived from the saved claimant key. destinationChainId and vaultAddress come from the authenticated route, and deadline is a bigint within the uint64 range. Sign the inner hash shown above so signMessage adds the EIP-191 prefix once. The vault’s sweepDigest returns the already-prefixed digest. The claimant must use a secp256k1 key because the vault validates the signature by recovering the signing address. Simulate the call, then submit it from a signing account funded for destination-chain transaction fees: sweep(withdrawalId, claimantAddress, salt, destination, deadline, signature). Save its signed transaction and hash. The claimant address and salt become public in this transaction. If the authorization expires before execution, the claimant can sign a fresh deadline while the release remains unswept. Verify the successful receipt, vault Swept event, and releaseOf(withdrawalId).swept == true. Match the authorized destination and check the actual token transfer from the holding address to that destination. Reconcile the destination’s balance increase with the transferred amount, accounting for other transfers in the block. The sweep transfers the holding address’s entire token balance, so unsolicited funds can make it larger than the nominal release amount reported by Swept. Confirm the receipt’s canonical block and destination finality before marking the withdrawal delivered.

Recover each stage

Keep the withdrawal ID, message nonce, transaction attempts and finality evidence with the application record. Protect private notes, claimant keys and salt in the recovery store. A failed settle or sweep transaction rolls back its state change and remains retryable after the cause is resolved. Test the route with an approved withdrawal. Early settlement, wrong claimant or salt, altered destination, expired signature, and replay must fail without moving funds. Repeating the withdrawal proof must not spend the same notes again. Complete the sweep and verify that the final destination receives the funds.