ERC-7802: Cross-Chain Token Standard
ERC-7802 is a cross-chain token interface proposed by OP Labs that standardizes how tokens are minted and burned across chains. Instead of locking tokens on a source chain and minting wrapped versions on the destination (the traditional bridge model), ERC-7802 defines a crosschainMint and crosschainBurn interface that authorized bridge contracts call directly on the token. Tenzro implements ERC-7802 for wTNZO and any ERC-20 token created through the Token Factory, enabling seamless cross-chain movement without liquidity fragmentation.
How It Works
The ERC-7802 flow replaces lock-and-mint with burn-and-mint. When a user wants to move tokens from Chain A to Chain B:
- The user calls the bridge contract on Chain A, which calls
crosschainBurn()on the token contract, destroying the tokens on Chain A. - The bridge relays a message to Chain B (via LayerZero, CCIP, or any supported messaging protocol).
- On Chain B, the bridge contract calls
crosschainMint()on the same token contract, creating fresh tokens for the recipient. - Both events are emitted with matching metadata for cross-chain audit.
Key difference from wrapped tokens: With ERC-7802, there is only one canonical token contract per chain. No wrapped or synthetic tokens are created, and the total supply across all chains is always consistent (burned on source = minted on destination). This eliminates the bridge-risk problem where compromised bridge contracts can drain locked funds.
Interface Definition
// IERC7802 — Cross-chain mint/burn interface
interface IERC7802 is IERC165 {
/// @notice Mints tokens on the destination chain.
/// @param to Recipient address.
/// @param amount Amount to mint.
/// @param sender Original sender on the source chain.
/// @dev Only callable by authorized bridge contracts.
function crosschainMint(
address to,
uint256 amount,
address sender
) external;
/// @notice Burns tokens on the source chain before cross-chain transfer.
/// @param from Address whose tokens are burned.
/// @param amount Amount to burn.
/// @param sender Address initiating the cross-chain transfer.
/// @dev Only callable by authorized bridge contracts.
function crosschainBurn(
address from,
uint256 amount,
address sender
) external;
/// @notice Emitted when tokens are minted via cross-chain transfer.
event CrosschainMint(
address indexed to,
uint256 amount,
address indexed sender
);
/// @notice Emitted when tokens are burned via cross-chain transfer.
event CrosschainBurn(
address indexed from,
uint256 amount,
address indexed sender
);
}Bridge Authorization ACL
Not every contract can call crosschainMint or crosschainBurn. The CrosschainTokenManager maintains an access control list (ACL) of authorized bridge contracts. Only addresses registered in the ACL can invoke these functions. The ACL is managed by the token owner (for custom tokens) or by Tenzro governance (for wTNZO).
use tenzro_vm::crosschain::{CrosschainTokenManager, BridgeAuthorization};
let manager = CrosschainTokenManager::new(storage.clone())?;
// Authorize a bridge contract to call crosschainMint/crosschainBurn
manager.authorize_bridge(BridgeAuthorization {
bridge_address: layerzero_endpoint_address,
token_address: wtnzo_address,
authorized_by: governance_multisig,
chain_id: 1, // Ethereum
label: "LayerZero V2 Endpoint".to_string(),
per_tx_limit: 1_000_000 * 10u128.pow(18), // 1M TNZO per tx
daily_limit: 10_000_000 * 10u128.pow(18), // 10M TNZO per day
enabled: true,
})?;
// Revoke authorization
manager.revoke_bridge(layerzero_endpoint_address, wtnzo_address)?;
// Query all authorized bridges for a token
let bridges = manager.authorized_bridges(wtnzo_address)?;
// Check if a specific bridge is authorized
let is_authorized = manager.is_authorized(
layerzero_endpoint_address,
wtnzo_address,
)?;Rate Limits and Safety
Each bridge authorization carries two independent limits to prevent catastrophic loss in the event of a bridge compromise:
| Limit Type | Scope | Behavior on Breach |
|---|---|---|
per_tx_limit | Single crosschainMint or crosschainBurn call | Transaction reverts with ExceedsPerTxLimit |
daily_limit | Cumulative volume within a rolling 24-hour window | Transaction reverts with ExceedsDailyLimit. Resets after 24 hours. |
// Limit configuration example
let auth = BridgeAuthorization {
bridge_address: ccip_router_address,
token_address: wtnzo_address,
authorized_by: governance_multisig,
chain_id: 1,
label: "Chainlink CCIP Router".to_string(),
// Per-transaction limit: 500,000 TNZO
per_tx_limit: 500_000 * 10u128.pow(18),
// Daily rolling limit: 5,000,000 TNZO
daily_limit: 5_000_000 * 10u128.pow(18),
enabled: true,
};
// The manager tracks daily volume internally
// When a crosschainMint is attempted:
match manager.check_limits(ccip_router_address, wtnzo_address, amount) {
Ok(()) => { /* proceed with mint */ }
Err(CrosschainError::ExceedsPerTxLimit { limit, attempted }) => {
// Single transaction too large
}
Err(CrosschainError::ExceedsDailyLimit { limit, used, attempted }) => {
// Daily cap reached — wait for window to roll
}
Err(CrosschainError::BridgeNotAuthorized { bridge, token }) => {
// Bridge not in ACL
}
Err(CrosschainError::BridgeDisabled { bridge, token }) => {
// Bridge temporarily paused via enabled=false
}
Err(e) => return Err(e),
}Events and Audit Trail
Every cross-chain mint and burn emits structured events that are indexed by the Tenzro Ledger. These events carry the sender address from the source chain, enabling full traceability of cross-chain flows.
// Events emitted by the ERC-7802 implementation
// On source chain (burn side):
CrosschainBurn {
from: "0xuser...abc", // Token holder
amount: 1000000000000000000000, // 1000 TNZO (18 decimals)
sender: "0xbridge...def", // Bridge contract that initiated
}
// On destination chain (mint side):
CrosschainMint {
to: "0xuser...abc", // Recipient (same user, different chain)
amount: 1000000000000000000000, // 1000 TNZO
sender: "0xbridge...ghi", // Bridge contract on destination
}
// Additional Tenzro-specific audit event:
CrosschainTransferCompleted {
token: "0xwtnzo...123",
amount: 1000000000000000000000,
source_chain: 1, // Ethereum
dest_chain: 42161, // Arbitrum
source_tx: "0xtx_burn...abc",
dest_tx: "0xtx_mint...def",
bridge: "layerzero",
timestamp: 1712793600,
}Integration with Token Factory
Any ERC-20 token created through Tenzro's Token Factory precompile (0x1002) can opt into ERC-7802 support at creation time. When enabled, the token contract implements the IERC7802 interface and the creator can authorize bridges through the CrosschainTokenManager.
// Create a token with ERC-7802 support enabled
let token = token_factory.create_token(CreateTokenParams {
name: "My Protocol Token".to_string(),
symbol: "MPT".to_string(),
decimals: 18,
initial_supply: 100_000_000 * 10u128.pow(18),
creator: deployer_address,
crosschain_enabled: true, // Enables ERC-7802 interface
max_supply: Some(1_000_000_000 * 10u128.pow(18)),
})?;
// The token now supports crosschainMint/crosschainBurn
// Authorize bridges as needed
manager.authorize_bridge(BridgeAuthorization {
bridge_address: layerzero_endpoint_address,
token_address: token.address,
authorized_by: deployer_address,
chain_id: 1,
label: "LayerZero V2".to_string(),
per_tx_limit: 100_000 * 10u128.pow(18),
daily_limit: 1_000_000 * 10u128.pow(18),
enabled: true,
})?;CLI Usage
# Authorize a bridge for a token
tenzro token authorize-bridge --token <token-address> \
--bridge <bridge-contract> --chain-id 1 \
--per-tx-limit 1000000 --daily-limit 10000000 \
--label "LayerZero V2 Endpoint"
# Revoke bridge authorization
tenzro token revoke-bridge --token <token-address> \
--bridge <bridge-contract>
# List authorized bridges for a token
tenzro token bridges --token <token-address>
# Check current daily volume for a bridge
tenzro token bridge-volume --token <token-address> \
--bridge <bridge-contract>
# Update limits for an authorized bridge
tenzro token update-bridge-limits --token <token-address> \
--bridge <bridge-contract> \
--per-tx-limit 2000000 --daily-limit 20000000
# Temporarily disable a bridge (emergency pause)
tenzro token disable-bridge --token <token-address> \
--bridge <bridge-contract>
# Re-enable a paused bridge
tenzro token enable-bridge --token <token-address> \
--bridge <bridge-contract>Comparison with Traditional Wrapped Tokens
| Aspect | ERC-7802 (Burn/Mint) | Traditional (Lock/Mint) |
|---|---|---|
| Token type on destination | Canonical (same contract) | Wrapped (separate contract) |
| Supply consistency | Always consistent (burn = mint) | Risk of over-minting if bridge exploited |
| Liquidity fragmentation | None — one canonical token per chain | Split across wrapped representations |
| DeFi composability | Full — same token address accepted everywhere | Limited — each wrapper needs separate integrations |
| Bridge exploit impact | Capped by per-tx and daily limits | All locked tokens at risk |
| Gas cost | One burn tx + one mint tx | One lock tx + one mint tx (similar) |
| Token contract deployment | Same contract on every chain | Different wrapper contracts per bridge per chain |
Governance Controls
For the native wTNZO token, bridge authorizations are managed through the on-chain governance system. Adding or removing an authorized bridge requires a governance proposal with a majority vote. This ensures that no single entity can unilaterally authorize a new bridge to mint TNZO.
# Submit a governance proposal to authorize a new bridge
tenzro governance propose \
--title "Authorize deBridge DLN for wTNZO" \
--description "Add deBridge DLN as authorized ERC-7802 bridge for wTNZO with 500K per-tx and 5M daily limit" \
--action authorize-bridge \
--params '{"bridge": "0xdebridge...", "token": "wTNZO", "per_tx_limit": 500000, "daily_limit": 5000000}'
# Vote on the proposal
tenzro governance vote --proposal <proposal-id> --vote yes