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

# Withdraw bridged assets

> Spend bridged notes, deliver a delayed release, and sweep funds back to an authorized destination

> [!NOTE]
>
> **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](https://docs.metalntwx.com/reference/interop/bridge.md#route-and-asset-reads) 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:

```typescript theme={null}
import { encodeAbiParameters, keccak256, parseAbiParameters, toHex } from 'viem'

const destCommitment = toHex(BigInt(keccak256(encodeAbiParameters(
  parseAbiParameters('address,bytes32'),
  [claimantAddress, salt],
))) >> 8n, { size: 32 })
```

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](https://docs.metalntwx.com/reference/interop/bridge.md#withdrawal-proof-and-routing).
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](https://docs.metalntwx.com/reference/interop/bridge.md#withdrawal-proof-and-routing). 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](https://docs.metalntwx.com/reference/network-fundamentals/settlement.md), 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](https://docs.metalntwx.com/build/interop/send-messages-from-metal.md) 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](https://docs.metalntwx.com/reference/interop/bridge.md#queue-settle-and-sweep), 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:

```typescript theme={null}
const inner = keccak256(encodeAbiParameters(
  parseAbiParameters('uint256,address,bytes32,address,uint64'),
  [destinationChainId, vaultAddress, withdrawalId, destination, deadline],
))
const signature = await claimant.signMessage({ message: { raw: inner } })
```

`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

| Observed state | Next action |
| - | - |
| Metal attempt has an uncertain result | Recover its saved hash and spent state before retrying the same attempt |
| Metal withdrawal transaction is finalized, release is absent | Resume delivery of the saved outbox message |
| Release exists but is immature | Wait for destination chain time to reach `releasableAt` |
| Release is mature and unsettled | Submit or recover `settle` |
| Release is settled and unswept | Recover or refresh the claimant's authorization and submit `sweep` |
| Release is swept | Recover the existing token transfer and finality without withdrawing again |

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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.