Skip to content

Contracts

A secure, multisig-based cross-chain bridge enabling token transfers between Ethereum and Tezos blockchains. The bridge implements a wrap/unwrap mechanism with robust multisig governance for secure token transfers.


Table of Contents


Ethereum

The TezosBridge.sol contract is designed to facilitate secure and efficient token transfers between Ethereum and Tezos. It allows users to wrap ERC20 tokens on Ethereum, which can then be minted on the Tezos blockchain, and vice versa for unwrapping. The contract employs a multisig governance model to ensure that token unwrapping operations are secure and require multiple approvals from designated owners.

The TezosBridge.sol contract manages cross-chain transfers with the following components:

  • Token Wrapping — Locks ERC20 tokens on Ethereum and emits metadata-enriched wrap events.
  • Token Unwrapping — Releases locked tokens to users after multisig-approved Tezos burn proofs.
  • Multisig Verification — Requires a threshold of owner signatures to authorize unwraps.
  • Administrative Controls — Administrator can manage owners, fees, and thresholds.
  • Fee System — A configurable percentage fee (in basis points) is applied to bridge transfers.

Key Features

  • Multisig Verification:
    Unwrap operations require multiple valid owner signatures.

  • Configurable Fees:
    Supports a percentage-based fee (fee / 10000), automatically transferred to a fee receiver.

  • Administrator Controls:
    Administrator can add/remove owners, set fee/threshold, and update configuration addresses.

  • Metadata Enriched Events:
    Wrap events include token name, symbol, and decimals for seamless Tezos-side minting.

  • Nonce & Tx Tracking:
    Each wrap generates a unique ID (keccak256(user, nonce)) timestamped for verification.

  • Comprehensive Testing:
    Unit, integration, and gas analysis test suites.


Getting Started

This section provides a quick guide to set up, test, and deploy the TezosBridge contract.

1. Environment Setup

First, copy the environment template and configure your parameters:

cp .env.example .env

Edit .env with your specific values:

# Example environment variables for TezosBridge deployment
 
# Private key of the deployer (with 0x prefix)
PRIVATE_KEY=""
 
# Administrator address for the bridge
BRIDGE_ADMIN=""
 
# Individual multisig owner addresses (required by script)
MULTISIG_OWNER_1=""
MULTISIG_OWNER_2=""
MULTISIG_OWNER_3=""
 
# Multisig threshold (minimum number of signatures required for unwrap operations)
MULTISIG_THRESHOLD=2
 
# Comma-separated list of multisig owner addresses
MULTISIG_OWNERS=""
 
# RPC URL for the network you're deploying to
RPC_URL=http://localhost:8545
 
# Optional: Etherscan API key for contract verification
ETHERSCAN_API_KEY=your_etherscan_api_key_here

2. Install Dependencies

# Install Foundry if not already installed
curl -L https://foundry.paradigm.xyz | bash
foundryup
 
# Install project dependencies
forge install

3. Test Run

# Run all tests
forge test
 
# Run specific test suites
forge test --match-contract TestUnitTezosBridge    # Unit tests
forge test --match-contract TestIntegratedBridge   # Integration tests
forge test --match-contract TestGasAnalysis        # Gas analysis

4. Deploy

# Local testing
anvil
forge script script/TezosBridge.s.sol:TezosBridgeScript --rpc-url http://localhost:8545 --private-key $PRIVATE_KEY --broadcast
 
# Testnet deployment
forge script script/TezosBridge.s.sol:TezosBridgeScript --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --verify

Production Deployment

For production deployment, ensure you follow these steps:

1. Security Checklist

  • Private keys stored securely (hardware wallet/secure key management)
  • Multisig owners verified and trusted
  • Threshold set appropriately (recommend 2/3 or 3/5)
  • Admin address is a multisig wallet

2. Configure Production Environment

# Production configuration
PRIVATE_KEY="0x..." # Deployer key (preferably from hardware wallet)
BRIDGE_ADMIN="0x..." # Production admin multisig address
MULTISIG_OWNER_1="0x..." # Production owner #1
MULTISIG_OWNER_2="0x..." # Production owner #2
MULTISIG_OWNER_3="0x..." # Production owner #3
MULTISIG_THRESHOLD=2 # Or higher for production
RPC_URL="https://${NETWORK}.infura.io/v3/YOUR_INFURA_KEY"
ETHERSCAN_API_KEY="your_etherscan_api_key"

3. Deploy to Mainnet

# Deploy with verification
forge script script/TezosBridge.s.sol:TezosBridgeScript --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --verify
 
# Alternative: Use production deployment function
forge script script/TezosBridge.s.sol:TezosBridgeScript --sig "runProduction()" --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --verify

Bridge Operations

The TezosBridge contract supports two main operations: wrapping tokens from Ethereum to Tezos and unwrapping tokens from Tezos back to Ethereum. The process involves user interactions, multisig approvals, and event emissions for cross-chain communication.

Wrapping Tokens (Ethereum → Tezos)

  1. Approve the bridge to spend tokens:
token.approve(bridgeAddress, amount);
  1. Wrap the tokens for Tezos:
bridge.wrapToken(tokenAddress, amount, tezosAddress);
  1. Event emitted:

Event is monitored by off-chain signers, tokens are minted on Tezos side.

Wrap(
  id, user, token, amountAfterFee,
  tezosAddress, name, symbol, decimals
)

Unwrapping Tokens (Tezos → Ethereum)

  1. User burns tokens on Tezos side, generating a unique burn tx ID.

  2. Bridge owners sign the unwrap payload off-chain:

Encoded data format:

(string id, address tokenOut, address to, uint256 amount)
  1. User calls unwrapToken:
bridge.unwrapToken(params, signatures);
  1. Bridge verification flow:
    • Validates that id wasn't processed before.
    • Checks multisig signatures using ecrecover.
    • Deducts the bridge fee and transfers tokens to the user.
  2. Event emitted:
Unwrap(user, token, amountAfterFee)

Administration

The following functions are available exclusively to the administrator for managing the bridge's configuration and multisig governance.

FunctionDescription
addOwner(address owner)Adds a new multisig owner
removeOwner(address prevOwner, address owner)Removes an existing owner
changeThreshold(uint256 threshold)Updates required signature count
changeFee(uint256 fee)Sets the bridge fee (in basis points)
changeFeeReceiver(address newReceiver)Updates the fee receiver address
changeAdmin(address newAdmin)Transfers administrative rights
Notes:
  • All administrative functions are protected by the authorized modifier.
  • _administrator is the only address allowed to perform these actions.

Views

The following view (read-only) functions allow users, relayers, or monitoring services to query the current state of the TezosBridge contract without sending transactions.

FunctionDescription
getFee() → uint256Returns the current bridge fee, expressed in basis points (1/100 of a percent).
getThreshold() → uint256Returns the number of required signatures for multisig operations.
getAdministrator() → addressReturns the current administrator address.
getOwners() → address[]Returns the list of all registered multisig owners.
isOwner(address owner) → boolChecks whether a given address is one of the registered multisig owners.
getNonce(address user) → uint256Returns the current nonce associated with a user (used to compute wrap IDs).
getIdsDesc(address user, uint256 fromNonce, uint256 count) → (bytes32[], uint256[])Retrieves a descending list of wrap transaction IDs and their timestamps.
getId(bytes32 id) → uint256Returns the timestamp of a specific wrap transaction by ID.
getProcessedId(string id) → (bool processed, uint256 timestamp)Checks whether a given unwrap ID from Tezos has already been processed.
Notes:
  • All these functions are read-only and do not require gas when called off-chain (via eth_call).
  • They can be used by indexers, off-chain signers, or front-end applications to track bridge activity and status.
  • Nonces and IDs are essential for cross-chain proof validation and preventing double-processing of unwrap operations.

Events

The following events are emitted by the bridge contract to record key operations and configuration changes on-chain.

EventDescription
Wrap(bytes32 id, address user, address token, uint256 amount, string tezosAddress, string name, string symbol, uint8 decimals)Emitted when tokens are wrapped for Tezos
Unwrap(address user, address token, uint256 amount)Emitted after successful unwrapping
AddedOwner(address owner)New multisig owner added
RemovedOwner(address owner)Owner removed
ChangedThreshold(uint256 threshold)Updated multisig threshold
ChangedFee(uint256 fee)Updated fee rate
ChangedFeeReceiver(address feeReceiver)Updated fee receiver address

Troubleshooting

If you encounter issues with the TezosBridge contract, here are some common troubleshooting lists and solutions.

ErrorCauseSolution
METHOD_CAN_ONLY_BE_CALLED_BY_ADMINISTRATORUnauthorized callerEnsure msg.sender is the admin address
INVALID_SIGNATURES_LENGTHIncorrect signature countCheck that signatures.length == threshold * 65
INVALID_OWNER_PROVIDEDOne or more invalid signer addressesVerify that all signers are registered owners
TRANSACTION_ALREADY_PROCESSEDDuplicate unwrap IDEnsure each unwrap id is unique
TOKEN_TRANSFER_FAILEDERC20 transfer errorEnsure token complies with ERC20 and has sufficient allowance

Tezos

The tezos_bridge contract is the Tezos-side implementation of the cross-chain bridge.
It manages the creation, minting, and burning of wrapped tokens corresponding to ERC20 assets locked on Ethereum.
This contract ensures that the state of wrapped tokens on Tezos always matches the locked balances on Ethereum.

The tezos_bridge contract works in conjunction with the Ethereum TezosBridge.sol contract to maintain cross-chain synchronization through event-driven communication and signature verification.

The bridge manages the following components:

  • Token Creation — Deploys new FA2-compatible token contracts for each supported Ethereum token.
  • Token Wrapping — Mints wrapped tokens on Tezos after validated wrap events on Ethereum.
  • Token Unwrapping — Burns Tezos tokens and emits events for Ethereum-side unwrapping.
  • Multisig Verification — Validates actions using signatures from multiple authorized owners.
  • Administrative Controls — Administrator can manage owners and update the required signature threshold.

Key Features

  • Cross-Chain Consistency:
    Maintains a one-to-one mapping between Ethereum and Tezos tokens through event-driven mint/burn mechanisms.

  • Multisig Validation:
    Ensures secure minting and token creation by requiring multiple valid signatures.

  • Dynamic Ownership:
    Administrator can add or remove owner keys and adjust the multisig threshold.

  • Event Transparency:
    Emits Wrap, Unwrap, and Create events for cross-chain coordination and auditing.

  • ID and Nonce Tracking:
    Maintains unique transaction identifiers (keccak(user, nonce)) and timestamps for traceability.


Getting Started

This section provides a quick overview for setting up, testing, and deploying the tezos_bridge contract.

1. Environment Setup

The project uses Completium CLI for compilation and deployment, and ts-mocha for testing.

Make sure you have both installed globally:

npm install -g completium-cli
npm install -g ts-mocha

You can modify the deployment parameters directly in this line of the package.json file to fit your setup for example, changing the admin address or the multisig threshold:

"deploy-bridge": "completium-cli deploy src/bridge.arl --parameters '{\"admin\" : \"tz1YourAdminAddressHere\", \"threshold\" : \"3\"}'"

2. Install Dependencies

# Install project dependencies
npm install

3. Test Run

All tests are written in TypeScript and executed using ts-mocha.

To run the full test suite:

npm run test-bridge

4. Deploy

You can deploy the Tezos bridge directly using the provided script:

npm run deploy-bridge

Production Deployment

For production deployment, ensure you follow these steps:

1. Security Checklist

  • Private keys stored securely (hardware wallet/secure key management)
  • Multisig owners verified and trusted
  • Threshold set appropriately (recommend 2/3 or 3/5)
  • Admin address is a multisig wallet

2. Configure Production Environment

Modify the deployment parameters directly in this line of the package.json file to fit your setup for example, changing the admin address or the multisig threshold:

"deploy-bridge": "completium-cli deploy src/bridge.arl --parameters '{\"admin\" : \"tz1YourAdminAddressHere\", \"threshold\" : \"3\"}'"

3. Deploy to Mainnet

npm run deploy-bridge

Bridge Operations

The Tezos bridge supports three core operations: create, wrap, and unwrap. These correspond to different cross-chain events between Ethereum and Tezos.

Creating a Wrapped Token (Ethereum → Tezos Initialization)

When a new ERC20 token is locked on Ethereum, the Tezos bridge must deploy a wrapped FA2 token contract.

entry create(params_c: params_create, signatures: bytes)
  1. Off-chain signers approve the creation by signing the payload.
  2. The bridge verifies signatures using check_signatures.
  3. A new permits contract and FA2 token contract are deployed.
  4. A Create event is emitted to confirm token creation.
Event emitted:
Create(
  eth_token_address,
  tez_token_address,
  contract_metadata,
  token_name,
  token_symbol,
  token_decimals
)

Wrapping Tokens (Ethereum → Tezos)

Triggered when the Ethereum bridge emits a Wrap event.

entry wrap(params_w: params_wrap, signatures: bytes)
  1. Verifies that the Ethereum transaction ID (eth_id) has not been processed.
  2. Checks the provided multisig signatures.
  3. Mints the wrapped token on Tezos to the user's address.
  4. Records the processed ID to prevent duplicates.
  5. Emits a Wrap event.
Event emitted:
Wrap(
  eth_id,
  user_address,
  ethereum_token,
  tez_token,
  token_amount
)

Unwrapping Tokens (Tezos → Ethereum)

When a user wants to send tokens back to Ethereum, they call:

entry unwrap(tz_addr: address, tz_at: nat, eth_addr: string)
Process:
  1. Confirms that the Tezos token exists in the mapping.
  2. Increments the caller's nonce and generates a unique transaction ID.
  3. Burns the tokens from the user's balance.
  4. Emits an Unwrap event containing the data needed by Ethereum signers.
Event emitted:
Unwrap(
  id,
  user,
  tez_token_address,
  token_amount,
  eth_token_address,
  eth_address
)

Administration

The Tezos bridge provides strong administrative control for managing ownership, thresholds, and governance configuration. Only the contract's administrator may execute these operations, ensuring security and consistency across the bridge.

FunctionDescription
change_admin(new_admin: address)Transfers administrator privileges to another address
change_threshold(new_threshold: nat)Updates the multisig signature threshold
add_owner(new_owner: key)Adds a new authorized signer key
remove_owner(owner_to_remove: key)Removes an existing authorized signer
Notes:
  • All administrative entries can only be called by the admin address.
  • Threshold must always be greater than zero.
  • Owner public keys are stored in the owners big_map.

Views

The contract provides several read-only views for querying state information.

ViewDescription
created(eth_tn_addr: string)Checks if an Ethereum token is already mapped
get_processed_id(eth_pr_id: string)Returns whether an Ethereum transaction ID was processed
get_ids_desc(address, from_nonce, count)Retrieves recent unwrap IDs with timestamps
get_id(bytes specific_id)Returns the timestamp of a specific unwrap ID
get_nonce(address user)Returns the current nonce for a user
get_owners()Returns the list of registered owner keys

Events

The following events are emitted by the Tezos bridge to synchronize operations with Ethereum and ensure transparency of cross-chain activity.

EventDescription
Create(string eth_token_address, address tez_token_address, bytes contract_metadata, bytes token_na, bytes token_sy, bytes token_de)Emitted when a new wrapped token contract is created
Wrap(string eth_id, address user, string ethereum_token, address token_address, nat token_amount)Emitted when tokens are minted (wrap)
Unwrap(bytes id, address user, address token_address, nat token_amount, string eth_token_address, string eth_address)Emitted when tokens are burned (unwrap)

Troubleshooting

If you encounter issues with the tezos_bridge contract, refer to the following common errors and their potential causes.

ErrorCauseSolution
TOKEN_NOT_SUPPORTEDThe given Tezos token is not registeredEnsure the token mapping exists before unwrapping
ALREADY_PROCESSED_IDThe Ethereum transaction ID was reusedUse a unique eth_id per wrap
NOT_ENOUGH_SIGNATURESInsufficient valid signatures providedEnsure signatures match registered owner keys
THRESHOLD_MUST_BE_GREATER_THAN_ZEROThreshold set to 0Use a threshold ≥ 1
OWNER_ALREADY_EXISTSAttempted to re-add an existing ownerVerify key list before adding
OWNER_NOT_FOUNDTried to remove an unregistered ownerEnsure the key exists before removal
SIGNATURE_SLICING_FAILEDSignature length or slicing incorrectCheck off-chain signature formatting
Copyright © 2025 RAID Square.