Skip to content

Deposit Bridge

The deposit bridge proves that an ordered range of Ethereum deposits produces the expected Zeko deposit actions.

Deposits on Ethereum

Users call deposit for ERC20 tokens or depositETH for native ETH on EthereumZekoBridge.sol.

For each deposit, the contract:

  1. Checks that the token is allowed and the amount is non-zero.
  2. Locks the funds and rejects fee-on-transfer ERC20 tokens.
  3. Normalizes the Ethereum amount to the configured Zeko decimals.
  4. Increments depositNonce.
  5. Computes a deposit leaf.
  6. Appends the leaf to currentDepositState.
  7. Stores the checkpoint in depositStateByNonce.

Packed Zeko addresses contain a Pasta Fp x-coordinate in the lower 255 bits and the public-key parity flag in the highest bit.

Proof input

BridgeTransitionInput contains:

FieldMeaning
ethereum.chain_idChain ID included in every deposit leaf.
ethereum.bridge_addressBridge address included in leaves and used as holderAccountL1.
ethereum.deposit_nonceNonce immediately before the batch.
ethereum.deposit_stateDeposit accumulator immediately before the batch.
zeko.action_stateZeko action state immediately before the batch.
deposits[]Ordered deposits to replay.

Each deposit contains token, amount, zeko_amount, zeko_recipient, and timeout. The guest uses zeko_amount; the original Ethereum amount is informational and is not included in the proof calculation.

Deposit accumulator

For each deposit, the guest increments the nonce and computes values equivalent to Solidity keccak256(abi.encode(...)):

text
deposit_leaf = keccak256(
  keccak256("ZEKO_BRIDGE_DEPOSIT_LEAF_V1"),
  chain_id,
  bridge_address,
  token,
  zeko_recipient,
  zeko_amount,
  timeout,
  nonce
)

deposit_state_after = keccak256(
  keccak256("ZEKO_BRIDGE_DEPOSIT_STATE_V1"),
  deposit_state_before,
  deposit_leaf
)

Zeko deposit action

The guest unpacks zeko_recipient into (x, isOdd) and computes:

text
action = Poseidon.hashWithPrefix("Deposit_params - qFB3jXP*)", [
  Field(0),
  holderAccountL1,
  zekoAmount,
  recipient.x,
  recipient.isOdd,
  timeout
])

Each deposit is wrapped in its own Mina action list and appended with the same domain-separated Poseidon operations used by o1js.

Public values

FieldMeaning
ethereum_state_beforeDeposit accumulator before the batch.
ethereum_state_afterDeposit accumulator after the batch.
ethereum_nonce_beforeDeposit nonce before the batch.
ethereum_nonce_afterDeposit nonce after the batch.
zeko_action_state_beforeSupplied Zeko action state before the batch.
zeko_action_state_afterComputed Zeko action state after the batch.
deposit_countNumber of replayed deposits.

Contract checks

submitBridgeTransition verifies:

text
depositStateByNonce[ethereum_nonce_before] == ethereum_state_before
ethereum_nonce_after                       == depositNonce
ethereum_state_after                       == currentDepositState
ethereum_nonce_after                       == ethereum_nonce_before + deposit_count
zeko_action_state_after                    has not already been processed

This binds the proven batch to the current Ethereum deposit history.

Settlement binding

The deposit transition currently does not require its before or after Zeko action state to be a checkpoint recorded by ZekoSettlement. The accepted event alone does not prove that Zeko consumed the deposit actions.

Proof-powered settlement and bridging between Zeko and Ethereum.