Skip to content

SOLO-Chain-Devs/Contracts-DePin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

94 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Multi-Chain Token Bridge with ERC6909 Collection Unification

A sophisticated cross-chain token bridge supporting multiple source chains bridging to SOLO testnet as a unified hub, using direct MetaLayer integration with ERC6909 collection unification and user-friendly original token IDs.

πŸ—οΈ Architecture

Multiple Source Chains                    SOLO Hub (8884571)
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
B3 (1993)           ──────────────►│                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   MetaLayer     β”‚   BridgedTokenReceiver  β”‚
β”‚   TokenBridge   β”‚   Mailbox       β”‚                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚ - Multi-bridge support β”‚
                                    β”‚ - Creates ERC6909       β”‚
Polygon (137)        ──────────────►│ - Original token IDs    β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚ - Collection unity      β”‚
β”‚   TokenBridge   β”‚                 β”‚ - Bridge-back routing   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚                         β”‚
                                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Arbitrum (42161)     ──────────────►                β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                 β”‚
β”‚   TokenBridge   β”‚                          MultiTokenFactory
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

More chains...       ──────────────►

✨ Key Features

  • Multi-Chain Hub Architecture: Multiple source chains β†’ SOLO as unified hub
  • User-Friendly Token IDs: Preserves original token IDs (#1, #2, #3) for better UX
  • Collision-Resistant Bridge-Back: Internal hash system ensures safe routing
  • Dynamic Bridge Management: Add/remove source chains without redeployment
  • ERC6909 Collection Unification: One ERC6909 contract per origin collection
  • Source Domain Tracking: Automatic routing back to correct origin chain
  • Perfect Bridge-Back Safety: Impossible to get wrong original token
  • Direct MetaLayer Integration: No intermediate wrapper contracts
  • Backward Compatible: Existing setRemoteBridge() function still works
  • No Bridge Fees: Only pay MetaLayer gas fees (with configurable uptick)

πŸ”„ How Token Identification Works (Hybrid System)

User-Friendly Display + Internal Collision Resistance

  • One ERC6909 contract per origin collection (not per token)
  • Original token IDs preserved for user experience (#1, #2, #3)
  • Internal bridge hashes for collision-resistant bridge-back routing
  • Source domain tracking for multi-chain routing

Example Flow:

Origin: Collection ABC Token #1 β†’ SOLO: ABC ERC6909 Contract, Token ID #1 (user sees #1)
Origin: Collection ABC Token #2 β†’ SOLO: ABC ERC6909 Contract, Token ID #2 (user sees #2)  
Origin: Collection DEF Token #1 β†’ SOLO: DEF ERC6909 Contract, Token ID #1 (user sees #1)

Internal Bridge Routing:

Token from B3 ABC #1       β†’ bridgeHash: hash(ABC,1) + sourceDomain: 1993
Token from Polygon ABC #1  β†’ bridgeHash: hash(ABC,1) + sourceDomain: 137
Token from Arbitrum ABC #1 β†’ bridgeHash: hash(ABC,1) + sourceDomain: 42161

Detailed Mapping Process

  1. Bridge Collection ABC Token #1 from B3:

    • Original: CoolNFT(0xABC...) Token ID #1
    • Creates: Bridged CoolNFT ERC6909(0xXYZ...) with:
      • User sees: Token ID #1 (original ID preserved)
      • Bridge stores: bridgeHash: hash(0xABC..., 1) + sourceDomain: 1993
  2. Bridge Collection ABC Token #2 (same collection):

    • Original: CoolNFT(0xABC...) Token ID #2
    • Uses existing: Bridged CoolNFT ERC6909(0xXYZ...) with:
      • User sees: Token ID #2 (original ID preserved)
      • Bridge stores: bridgeHash: hash(0xABC..., 2) + sourceDomain: 1993
  3. Bridge Collection DEF Token #1 (different collection):

    • Original: OtherNFT(0xDEF...) Token ID #1
    • Creates new: Bridged OtherNFT ERC6909(0xQRS...) with:
      • User sees: Token ID #1 (original ID preserved)
      • Bridge stores: bridgeHash: hash(0xDEF..., 1) + sourceDomain: 1993

Why This System is Superior

βœ… User-Friendly: Users see familiar token IDs (#1, #2, #3) instead of large hash numbers
βœ… Collection Integrity: All tokens from same origin collection stay in one ERC6909 contract
βœ… Multi-Chain Support: Automatic routing back to correct source chain
βœ… Collision-Resistant: Internal hash system prevents conflicts during bridge-back
βœ… Perfect Bridge-Back: Always knows exact original contract, token ID, and source chain
βœ… Trading Efficiency: Collection floor prices and liquidity maintained
βœ… Marketplace Compatibility: Collections appear unified with intuitive token numbering

🌐 Multi-Chain Bridge Management

Supported Source Chains

The SOLO hub can accept bridges from any number of source chains. Currently configured for:

  • B3 Sepolia (1993) - Primary testnet bridge
  • Polygon (137) - Production ready
  • Arbitrum (42161) - Production ready
  • Ethereum (1) - Production ready
  • Any EVM Chain - Easy to add new chains

Bridge Management Functions

// Add or update source chain bridge
setRemoteBridge(uint32 domain, address bridgeAddress)

// Remove source chain (set address to 0x0)
setRemoteBridge(uint32 domain, address(0))

// Enable/disable chain
setDomainStatus(uint32 domain, bool active)

// Query functions
getSupportedDomains() β†’ uint32[]
getBridgeForDomain(uint32 domain) β†’ address
isChainSupported(uint32 domain) β†’ bool

Adding New Chains

# Example: Add Polygon bridge
cast send $SOLO_BRIDGED_TOKEN_RECEIVER "setRemoteBridge(uint32,address)" 137 $POLYGON_BRIDGE_ADDRESS

# Example: Add Arbitrum bridge  
cast send $SOLO_BRIDGED_TOKEN_RECEIVER "setRemoteBridge(uint32,address)" 42161 $ARBITRUM_BRIDGE_ADDRESS

πŸ“‹ Supported Token Standards

  • βœ… ERC20: Fungible tokens β†’ ERC6909 fungible tokens
  • βœ… ERC721: Non-fungible tokens β†’ ERC6909 non-fungible tokens
  • βœ… ERC1155: Multi-tokens β†’ ERC6909 multi-tokens

πŸš€ Quick Start

1. Environment Setup

cp .env.example .env
# Edit .env with your values
source .env

2. Deploy Bridge

# Deploy contracts to both chains
make deploy-all

# Or step by step:
make deploy-b3       # Deploy TokenBridge on B3
make deploy-solo     # Deploy BridgedTokenReceiver on SOLO
make configure       # Link contracts together

3. Bridge Tokens

Bridge FROM B3 TO SOLO

# 1. Approve bridge to transfer your token
cast send <TOKEN_CONTRACT> "approve(address,uint256)" $B3_TOKEN_BRIDGE <TOKEN_ID> --rpc-url $B3_SEPOLIA_RPC_URL --private-key $PRIVATE_KEY

# 2. Bridge the token (creates ERC6909 with original token ID preserved)
cast send $B3_TOKEN_BRIDGE "bridgeERC721(address,uint256,uint256)" <TOKEN_CONTRACT> <TOKEN_ID> <GAS_LIMIT> --value $(cast call $B3_TOKEN_BRIDGE "getRequiredFee()" --rpc-url $B3_SEPOLIA_RPC_URL) --rpc-url $B3_SEPOLIA_RPC_URL --private-key $PRIVATE_KEY

Bridge FROM SOLO BACK TO Origin Chain

# 1. Find your bridged collection and token info (now requires source domain)
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getBridgedCollection(uint32,address)" <SOURCE_DOMAIN> <ORIGINAL_TOKEN_CONTRACT> --rpc-url $SOLO_TESTNET_RPC_URL
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getBridgedToken(address,uint256)" <ORIGINAL_TOKEN_CONTRACT> <ORIGINAL_TOKEN_ID> --rpc-url $SOLO_TESTNET_RPC_URL

# 2. Get the user-friendly token ID (same as original)
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getBridgedTokenIdFromOrigin(address,uint256)" <ORIGINAL_TOKEN_CONTRACT> <ORIGINAL_TOKEN_ID> --rpc-url $SOLO_TESTNET_RPC_URL

# 3. Bridge back using ERC6909 contract and original token ID
# (Bridge automatically routes to correct source chain using stored domain)
cast send $SOLO_BRIDGED_TOKEN_RECEIVER "requestBridgeBack(address,uint256,uint256,uint256)" <ERC6909_CONTRACT> <ORIGINAL_TOKEN_ID> <AMOUNT> <GAS_LIMIT> --value $(cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getRequiredFee()" --rpc-url $SOLO_TESTNET_RPC_URL) --rpc-url $SOLO_TESTNET_RPC_URL --private-key $PRIVATE_KEY

πŸ“ Contract Addresses

After deployment, update your .env:

# B3 Sepolia Contracts
B3_TOKEN_BRIDGE=<deployed_address>
B3_MULTI_TOKEN_FACTORY=<deployed_address>
B3_REGISTRY=<deployed_address>

# SOLO Testnet Contracts  
SOLO_BRIDGED_TOKEN_RECEIVER=<deployed_address>
SOLO_MULTI_TOKEN_FACTORY=<deployed_address>

πŸ”§ Configuration

Required Environment Variables

# Network RPCs
B3_SEPOLIA_RPC_URL=https://sepolia.b3.fun/
SOLO_TESTNET_RPC_URL=https://solo-testnet.rpc.caldera.xyz/http

# Chain Configuration
B3_SEPOLIA_CHAIN_ID=1993
SOLO_TESTNET_CHAIN_ID=8884571

# MetaLayer Mailbox (same on both chains)
METALAYER_CONTRACT=0x6F23B0211056035A22430a10fD27DED8547dc377

# No Bridge Fees - Only MetaLayer gas fees apply with configurable uptick

# Deployment
PRIVATE_KEY=your_private_key_here
ETHERSCAN_API_KEY=your_etherscan_key_here

πŸ§ͺ Testing

# Run contract tests
make test

# Verify configuration
make verify-config

# Check bridge functionality
make test-bridge

πŸ“š Contract Documentation

TokenBridge (B3 Sepolia)

  • Purpose: Locks original tokens, sends cross-chain messages
  • Functions: bridgeERC20(token, amount, gasLimit), bridgeERC721(token, tokenId, gasLimit), bridgeERC1155(token, tokenId, amount, gasLimit)
  • Key Features:
    • Open by default (gatekeeping disabled)
    • Configurable gas uptick percentage (10% default)
    • Caller-specified gas limits
  • Receives: Unlock messages from SOLO to release tokens

BridgedTokenReceiver (SOLO Hub)

  • Purpose: Multi-chain hub that creates ERC6909 collection contracts and handles bridge-back routing
  • Functions:
    • requestBridgeBack(erc6909Contract, tokenId, amount, gasLimit) - Routes back to correct source chain
    • addBridge(domain, bridgeAddress) - Add new source chain support
    • removeBridge(domain) - Remove source chain support
  • Creates: One ERC6909 contract per origin collection with original token IDs
  • Key Features:
    • generateBridgedTokenId() - Returns original token ID for user-friendly display
    • generateBridgeHash() - Creates collision-resistant hash for internal routing
    • Multi-chain source domain tracking and automatic routing
    • getBridgedCollection() - View collection information
    • getBridgedToken() - View specific token information
    • getSupportedDomains() - List all configured source chains
    • getRequiredFeeForDomain(domain) - Get fees for specific source chain

MultiTokenFactory

  • Purpose: Deploys ERC6909 token contracts for collections
  • Standards: Creates ERC6909 contracts optimized for collection unification

πŸ” Token Identification Functions

Generate Collision-Resistant Token ID

function generateBridgedTokenId(address originToken, uint256 originTokenId) 
    external pure returns (uint256)

Get Bridged Collection Info (Updated)

function getBridgedCollection(uint32 sourceDomain, address originToken) 
    external view returns (BridgedCollection memory)

// NEW: Collection discovery functions
function getSourceDomainsForToken(address originToken) 
    external view returns (uint32[] memory)
    
function hasCollection(uint32 sourceDomain, address originToken) 
    external view returns (bool)

Get Specific Token Info

function getBridgedToken(address originToken, uint256 originTokenId)
    external view returns (BridgedToken memory)

🚨 Emergency Unlock System

The TokenBridge includes an emergency unlock system for recovering assets when cross-chain messages fail or are delayed:

Features

  • Global Toggle: Admin can enable/disable emergency unlocks system-wide
  • User Requests: Any user can request unlock of any asset held by the bridge
  • Admin Approval: Only owner can approve unlock requests manually
  • Automatic Execution: Approved unlocks execute immediately upon approval
  • Double-Unlock Prevention: Prevents duplicate unlocks when delayed messages arrive later

Usage Flow

  1. Admin enables emergency unlocks: setEmergencyUnlockEnabled(true)
  2. User requests unlock: requestUnsafeUnlock(token, tokenId, amount, tokenType)
  3. Admin reviews and approves: approveEmergencyUnlock(requestId)
  4. Tokens transferred automatically to requester

Safety Mechanisms

  • βœ… Balance Verification: Ensures bridge actually owns requested tokens
  • βœ… Duplicate Prevention: Tracks executed unlocks to prevent double-spending
  • βœ… Event Logging: Full audit trail of all emergency unlock activities
  • βœ… Access Control: Only owner can approve unlock requests
  • βœ… Request Tracking: Unique request IDs for precise unlock management

Current Implementation (Option 1)

The emergency unlock system tracks duplicates using (user, token, tokenId, amount) hash. This provides:

  • βœ… Simple and efficient implementation
  • βœ… Prevents most double-unlock scenarios
  • ⚠️ Limitation: Cannot distinguish between different bridge transactions of same amount

Future Enhancement (Option 2)

A more precise tracking system could be implemented that links emergency unlocks to specific depositIds:

  • βœ… Perfect precision - tracks exact bridge transactions
  • βœ… Eliminates all edge cases with identical amounts
  • βœ… Better audit trail linking emergency unlocks to original deposits
  • ⚠️ More complex implementation requiring depositId tracking

Note: Option 2 can be implemented as a future enhancement if more precise tracking is needed. The current Option 1 implementation handles the vast majority of use cases safely and efficiently.

πŸ› οΈ Development

Build Contracts

forge build

Run Tests

forge test -vv

# Test specific functionality
forge test --match-test testCollectionUnification -vv
forge test --match-test testCollisionResistance -vv

Deploy to Custom Networks

Edit the domain constants in deployment scripts for different chains.

πŸ”— MetaLayer Integration

This bridge uses MetaLayer's message passing infrastructure:

  • Mailbox Contract: 0x6F23B0211056035A22430a10fD27DED8547dc377
  • B3 Domain: 1993
  • SOLO Domain: 8884571

Messages are automatically relayed by MetaLayer's decentralized relayer network.

πŸ’‘ Advanced Usage

Query Collection Information

# Get all info about a bridged collection (now requires source domain)
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getBridgedCollection(uint32,address)" <SOURCE_DOMAIN> <ORIGIN_CONTRACT>

# Discover which domains have bridged this token (NEW!)
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getSourceDomainsForToken(address)" <ORIGIN_CONTRACT>

# Check if a specific domain has bridged this token (NEW!)
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "hasCollection(uint32,address)" <SOURCE_DOMAIN> <ORIGIN_CONTRACT>

# Get info about a specific bridged token
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getBridgedToken(address,uint256)" <ORIGIN_CONTRACT> <ORIGIN_TOKEN_ID>

# Calculate what the bridged token ID will be (returns original ID)
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getBridgedTokenIdFromOrigin(address,uint256)" <ORIGIN_CONTRACT> <ORIGIN_TOKEN_ID>

# Query multi-bridge support
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "getSupportedDomains()"
cast call $SOLO_BRIDGED_TOKEN_RECEIVER "isChainSupported(uint32)" <DOMAIN_ID>

Bridge Multiple Tokens from Same Collection

All tokens from the same origin collection will be unified in the same ERC6909 contract with user-friendly IDs:

  1. Bridge CoolApes #1 β†’ Creates Bridged CoolApes ERC6909 with token ID #1 (user sees #1)
  2. Bridge CoolApes #5 β†’ Uses existing Bridged CoolApes ERC6909 with token ID #5 (user sees #5)
  3. Bridge CoolApes #10 β†’ Uses existing Bridged CoolApes ERC6909 with token ID #10 (user sees #10)

Result: One unified collection on SOLO with all CoolApes maintaining original token numbering and collection integrity.

Multi-Chain Collection Support

Now fully implemented with domain-aware collection management.

With multiple chains configured, the same collection from different chains creates separate ERC6909 contracts:

  1. Bridge CoolApes #1 from B3 β†’ SOLO ERC6909 Contract A, Token #1 (routes back to B3)
  2. Bridge CoolApes #1 from Polygon β†’ SOLO ERC6909 Contract B, Token #1 (routes back to Polygon)
  3. Bridge CoolApes #1 from Arbitrum β†’ SOLO ERC6909 Contract C, Token #1 (routes back to Arbitrum)

Each source chain gets its own dedicated ERC6909 collection contract, preventing collection metadata conflicts and ensuring clean separation between chains.

πŸ“– Additional Resources


This multi-chain bridge hub enables cross-chain token transfers from any number of source chains to SOLO, while preserving original token IDs, maintaining collection integrity, and ensuring bridge-back safety through internal collision-resistant routing and ERC6909 collection unification.

About

Cross-chain ERC721 & ERC1155 token bridge using MetaLayer integration, to SOLO as ERC6909

Topics

Resources

Stars

Watchers

Forks

Contributors