Solidity smart contracts for the EVM side of the Canton Network bridge.
These contracts enable bidirectional token transfers between EVM chains (Ethereum, Base, etc.) and Canton Network:
- Deposits (EVM → Canton): Users lock ERC-20 tokens in the bridge contract. The middleware detects the deposit event and mints CIP-56 tokens on Canton.
- Withdrawals (Canton → EVM): Canton burns CIP-56 tokens. The middleware signs a withdrawal proof, and the relayer releases ERC-20 tokens on EVM.
| Contract | Description |
|---|---|
CantonBridge.sol |
Main bridge contract with deposit/withdraw functionality |
TokenRegistry.sol |
Registry for managing bridgeable ERC-20 tokens |
| Interface | Description |
|---|---|
ICantonBridge.sol |
Bridge interface with events and errors |
IBridgeEvents.sol |
Administrative and operational events |
| Contract | Description |
|---|---|
RateLimiter.sol |
Rate limiting per token per time period |
| Contract | Description |
|---|---|
MockERC20.sol |
Mock tokens for testing (USDC, WBTC, PROMPT) |
- Role-Based Access Control: Admin, Relayer, and Pauser roles
- Rate Limiting: Configurable per-token rate limits
- Reentrancy Protection: All state-changing functions protected
- Pausable: Emergency pause functionality
- Large Withdrawal Time Lock: Configurable delay for large withdrawals
- Signature Verification: Withdrawals require valid relayer signature
# Install dependencies
forge install
# Build contracts
forge build
# Run tests
forge test -vv# Set environment variables
export PRIVATE_KEY=<deployer-private-key>
export ADMIN_ADDRESS=<admin-address>
export RELAYER_ADDRESS=<relayer-address>
# Deploy to network
forge script script/Deploy.s.sol --rpc-url <rpc-url> --broadcast// 1. Approve tokens
IERC20(token).approve(bridgeAddress, amount);
// 2. Deposit with Canton fingerprint
bridge.depositToCanton(token, amount, cantonRecipient);Called by the relayer after Canton burn is confirmed:
// Relayer signs the withdrawal message
bridge.withdrawFromCanton(token, amount, recipient, withdrawalId, proof);- Only registered tokens can be bridged
- Withdrawals require valid signature from authorized relayer
- Rate limits prevent drain attacks
- Large withdrawals are time-locked
- Emergency pause available for incidents
DepositToCanton(token, sender, amount, cantonRecipient, nonce)
WithdrawalFromCanton(token, recipient, amount, cantonSender, withdrawalId)WithdrawalProcessed(withdrawalId, success)
TokenRegistered(token, symbol, cantonTokenId, isNative)BridgePaused(by)/BridgeUnpaused(by)RateLimitSet(token, amount, period)
Apache 2.0