BridgeDepositVault to a
Metal BridgeGateway and MIP410 pool. Its inboxes authenticate messages in both
directions. The route provider supplies the contract addresses, supported
assets, circuit artifacts and proving tools.
Route and asset reads
The gateway exposes these read methods. Struct fields are listed in ABI order.
An unknown route has a zero inbox. An unknown asset route or claim has a zero
asset. Each asset route maps a source token to a pool asset, with a reverse map
for withdrawals. Amounts retain their raw integer units across the route, so
confirm the asset denomination before displaying balances or constructing a
note.
pending counts claimed deposits awaiting minting. reserve counts minted
deposits minus withdrawals through that route. New claims require
reserve + pending + amount <= cap, while withdrawals require
reserve >= amount for the selected route. The gateway authority registers each
route once, and applications use that registered configuration.
The source vault exposes:
Deposit and claim
UseparseAbi from the public viem package to encode these calls and decode
these events. vaultAbi describes the source vault, and gatewayAbi describes
the Metal gateway.
deposit. Amounts must satisfy 0 < amount < 2^128.
The note commitment must be nonzero and below the
BN254 field modulus. The
token must return true from transfers and deliver the exact amount. A
fee-on-transfer deposit reverts DepositDeltaMismatch and rolls back escrow.
The vault derives the deposit ID from the pre-increment deposit nonce:
depositOf(depositId) and queues
claimDeposit(bytes32 depositId,address foreignToken,uint256 amount,bytes32 noteCommitment)
on the Metal gateway. Decode the actual ID and outboxNonce from
DepositReceived. A transaction simulation’s predicted ID can change if
another deposit executes first.
The gateway accepts claimDeposit only from a registered route’s inbox while
foreignSender() equals that route’s vault. An accepted claim increases
pending. Duplicate claims revert ClaimAlreadyRecorded. An unknown asset,
out-of-range amount, or insufficient cap reverts without consuming the inbox
nonce, so delivery remains retryable.
Screening and bridge mint proof
WhenscreeningAuthority() is nonzero, only that authority can call
screen(bytes32 depositId,bool passed). It sets an irreversible verdict, and
mint requires Passed. Unset reverts NotScreened, while Failed
reverts ScreeningFailed. A zero authority disables screening and refunds, and
minting proceeds with the proof after the claim is recorded.
A failed verdict releases the claim’s pending amount and queues
refundDeposit(bytes32 depositId) on the source vault. The gateway’s
DepositRefunded event identifies this message. Funds return only when that
message executes through the vault’s metalInbox() with the gateway as
metalSender(). The vault returns the recorded amount to the original
depositor, marks refunded, and emits its own DepositRefunded.
Call gateway mint(depositId, sanctionsRoot, mintProof) after the claim and
required verdict. Any signing account can submit, and its balance in the gas
token pays the transaction fee. The gateway calls the pool with the recorded
asset, amount and note commitment. The owner prepares a BridgeMint UltraHonk
proof whose ordered public inputs are:
The proof establishes the note opening, owner authorization, and sanctions
checks. It must use the pool’s current
BridgeMint circuit verification key.
For both minting and withdrawal, the sanctions root must equal the pool’s
current root at execution.
The pool appends the note, increases bridgeLiabilityOf(asset), sets
bridgeDepositMinted(depositId) to true, and emits
BridgeMinted(address indexed asset,bytes32 indexed depositId,uint256 amount,bytes32 indexed noteCommitment).
The gateway moves the amount from pending to reserve in the same transaction.
A duplicate mint reverts BridgeDepositAlreadyMinted.
The mapped pool asset must have local shielding disabled and no local token
backing. Deposits create shielded notes, and bridged value exits through the
bridge withdrawal path.
Withdrawal proof and routing
The gateway’swithdraw accepts this request and the owner’s proof:
BridgeWithdrawn comes from the pool. WithdrawalRouted comes from the
gateway. The route ID is a nonzero canonical BN254 field element assigned by the
route provider. destCommitment contains a commitment to a claimant address and
salt. The claimant authorizes the destination address when signing the sweep.
BridgeWithdraw proof uses these 13 public inputs in order: Metal chain ID,
pool address, asset address, amount, route ID, claimant commitment, commitment
root, nullifier 0, nullifier 1, input lock ID 0, input lock ID 1, change note
commitment, and current sanctions root. Addresses are field elements. Nullifier
and lock arrays each contain exactly two entries, with zero padding for an
unused input. The change commitment is zero when there is no change.
The pool verifies the proof against its current circuit and checks the
historical commitment root, current sanctions root, unlocked inputs and unspent
nullifiers. It spends the notes, appends any change note, and decreases bridge
liability. The gateway decreases the selected route’s reserve and queues this
destination-vault call:
Queue, settle, and sweep
Only the destination vault’s inbox executing a message from its configured Metal gateway can callqueueRelease. It records
releasableAt = block.timestamp + withdrawalDelay using the destination chain’s
queue block. The release begins with settled = false and swept = false. A
duplicate release ID reverts ReleaseAlreadyQueued.
settle at or after releasableAt. It transfers the recorded
amount to claimAddress(withdrawalId) and sets settled. That address is the
CREATE2 address derived from the vault, withdrawal ID as salt, and
claimInitCodeHash. Early settlement reverts ReleaseNotMature. The claimant
then authorizes the final transfer through sweep.
The claimant authorizes sweep with a secp256k1 signature. The signed message
is the 32-byte inner hash below with the EIP-191 personal-message prefix:
inner using
signMessage({ message: { raw: inner } }) so the EIP-191 prefix is applied
once. The vault’s sweepDigest returns the already-prefixed digest. The
contract accepts the signature while block.timestamp <= deadline and requires
the recovered address to equal the claimant address used in the commitment.
Any signing account can submit the signature and pay the destination-chain
transaction fee. sweep checks the commitment and signature, deploys the claim
contract at the holding address, and transfers that address’s entire token
balance to the signed destination. It marks swept and emits Swept with the
release’s recorded amount. Unsolicited tokens sent to the holding address can
make the actual transfer larger than that event amount. Verify the token
transfer and destination balance separately. The claimant and salt become public
in the sweep calldata.
The sweep runs once. Missing settlement, an expired authorization, an incorrect
commitment, an invalid signature, or a repeated sweep reverts. Failed token
transfers roll back the corresponding settlement or sweep. Retain the claimant
key and salt until final destination delivery is verified.