Skip to content

Marcrus813/bridge-contracts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Core Contracts Documentation

This directory contains the core contract implementations for the cross-chain bridge and staking system.

📁 Directory Structure

src/core/
├── PoolManager.sol          # Main contract for fund pool and staking management
├── PoolManagerStorage.sol   # Storage layer for fund pool
├── MessageManager.sol       # Cross-chain message management contract
└── MessageManagerStorage.sol # Message management storage layer

🏗️ Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                   User Interaction Layer                 │
├─────────────────────────────────────────────────────────┤
│  Staking Features     │  Bridge Features   │  Withdrawal │
│  - DepositStaking     │  - BridgeInitiate  │  - Withdraw │
│  - ClaimReward        │  - BridgeFinalize  │  - ClaimAll │
└─────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────┐
│                    PoolManager.sol                       │
│  - Fund pool management                                  │
│  - Staking logic                                         │
│  - Bridge interface                                      │
│  - Reward distribution                                   │
└─────────────────────────────────────────────────────────┘
                           ↓
┌────────────────────┬───────────────────────────────────┐
│  MessageManager    │    PoolManagerStorage             │
│  - Cross-chain msg │    - State storage                │
│  - Message tracking│    - Pool data                    │
│  - Replay attack   │    - User data                    │
│    prevention      │                                   │
└────────────────────┴───────────────────────────────────┘

📄 Detailed Contract Descriptions

1. PoolManager.sol

Main Contract - Fund Pool and Staking Management

Core Function Modules

🔸 Liquidity Management

Provides base liquidity for the cross-chain bridge.

// Deposit ETH to provide liquidity
function depositEthToBridge() public payable returns (bool)

// Deposit ERC20 to provide liquidity
function depositErc20ToBridge(address tokenAddress, uint256 amount) public returns (bool)

// Admin withdraws liquidity (only withdrawManager)
function withdrawEthFromBridge(address payable withdrawAddress, uint256 amount) public returns (bool)
function withdrawErc20FromBridge(address tokenAddress, address withdrawAddress, uint256 amount) public returns (bool)
🔸 Cross-Chain Bridge Features

Transfer assets between different chains.

// Source chain: Initiate cross-chain transfer
function BridageInitiateETH(uint256 sourceChainId, uint256 destChainId, address destTokenAddress, address to) external payable

function BridgeInitiateERC20(
    uint256 sourceChainId,
    uint256 destChainId,
    address sourceTokenAddress,
    address destTokenAddress,
    address to,
    uint256 value
) external

// Destination chain: Complete cross-chain transfer (only Relayer)
function BridgeFinalizeETH(...) external payable onlyRelayer
function BridgeFinalizeERC20(...) external onlyRelayer

Cross-Chain Flow Example:

User on Ethereum          Relayer monitors             User receives on BSC
     │                        │                            │
     ├─ BridageInitiateETH    │                            │
     │  (1 ETH)              │                            │
     │                        │                            │
     │  ─────────────────────>│                            │
     │  Send 1 ETH to contract│                            │
     │  Deduct fee 0.001 ETH  │                            │
     │                        │                            │
     │                        ├─ Listen MessageSent event  │
     │                        │                            │
     │                        ├─ Verify transaction        │
     │                        │                            │
     │                        ├─ BridgeFinalizeETH ────────>│
     │                        │  Execute on BSC            │
     │                        │                            │
     │                        │                            ├─ Receive 0.999 ETH
🔸 Staking System

Users stake assets to earn cross-chain transaction fee rewards.

// Stake
function DepositAndStakingETH() external payable
function DepositAndStakingERC20(address _token, uint256 _amount) external

// Withdraw (principal + rewards)
function WithdrawAll() external                    // Withdraw all tokens
function WithdrawByID(uint256 i) external          // Withdraw specific stake record

// Claim rewards only (principal continues staking)
function ClaimAllReward() external                 // Claim all token rewards
function ClaimbyID(uint256 i) external             // Claim specific record rewards

Staking Reward Mechanism:

Each staking period (21 days):
├─ Cross-chain fees accumulate to FeePoolValue
├─ Relayer calls CompletePoolAndNew to rotate pools
│  └─ Distributes FeePoolValue to completed pool
└─ Users claim rewards

User reward calculation formula:
Reward = (User stake amount / Pool total stake) × Pool total fees

Core Advantages of Pool Rotation Mechanism:

1. Continuous Staking + Flexible Withdrawal

  // Users can stake to "not started" pools anytime
  if (Pools[address(ETH_ADDRESS)][PoolIndex].startTimeStamp > block.timestamp) {
      Users[msg.sender].push(...);  // Staking successful
  }

  - ✅ Users don't need locking, can withdraw principal+rewards anytime
  - ✅ Continuing stakers automatically enter next period (TotalAmount inherited)
  - ✅ New users can join before pool starts, fair participation

2. Fair Distribution of Transaction Fees

  // Calculate user rewards in each pool
  uint256 _Reward = (Amount * Pools[_token][j].TotalFee) / Pools[_token][j].TotalAmount;

  Distribution logic:
  - Each pool's TotalFee is determined when period ends
  - User reward = (Individual stake / Pool total stake) × Pool total fees
  - Users staking across multiple periods accumulate rewards from multiple pools

  Example scenario:
  User A stakes 100 ETH in Pool 1
  - Pool 1 (21 days) ends: 10 ETH fees, A gets their share
  - Pool 2 (21 days): A's 100 ETH continues staking, gets new fee share
  - Pool 3: And so on...

  User A withdraws on day 60: receives accumulated rewards from 3 pools

3. Incentivizes Long-term Stakers

  // Traverse all participating pools during withdrawal
  for (uint256 j = startPoolId; j < EndPoolId; j++) {
      uint256 _Reward = (Amount * Pools[_token][j].TotalFee) / Pools[_token][j].TotalAmount;
      Reward += _Reward;
  }

  - Longer staking time = more pool periods participated
  - More accumulated rewards (compound effect)
  - Encourages users to hold long-term, increases liquidity stability

4. Gas Efficiency Optimization

  Compare to traditional approach:
  - ❌ Real-time reward calculation: Every bridge transaction updates all stakers' rewards (O(n) operation)
  - ✅ Pool rotation: Rewards calculated only on withdrawal, bridging only accumulates FeePoolValue (O(1) operation)

  // Bridging only needs to accumulate fees (very low gas)
  FeePoolValue[ETH_ADDRESS] += fee;

  // Only traverse pools to calculate during withdrawal (user bears gas)
  for (uint256 j = startPoolId; j < EndPoolId; j++) {
      // Calculate reward...
  }
🔸 Query Functions
// Query user staked principal
function getPrincipal() external view returns (KeyValuePair[] memory)

// Query user unclaimed rewards
function getReward() external view returns (KeyValuePair[] memory)

// Query funding pool balance
function fetchFundingPoolBalance(address token) external view returns (uint256)

// Query pool information
function getPoolLength(address _token) external view returns (uint256)
function getPool(address _token, uint256 _index) external view returns (Pool memory)

// Query user information
function getUserLength(address _user) external view returns (uint256)
function getUser(address _user) external view returns (User[] memory)
🔸 Admin Functions
// Relayer permissions
function CompletePoolAndNew(Pool[] memory CompletePools) external onlyRelayer
function setMinTransferAmount(uint256 _MinTransferAmount) external onlyReLayer
function setValidChainId(uint256 chainId, bool isValid) external onlyReLayer
function setSupportToken(address _token, bool _isSupport, uint32 startTimes) external onlyReLayer
function setPerFee(uint256 _PerFee) external onlyReLayer
function setMinStakeAmount(address _token, uint256 _amount) external onlyReLayer

// Owner permissions
function pause() external onlyOwner
function unpause() external onlyOwner

// WithdrawManager permissions
function QuickSendAssertToUser(address _token, address to, uint256 _amount) external onlyWithdrawManager

Key Data Structures

// Staking pool
struct Pool {
    uint32 startTimestamp;     // Pool start time
    uint32 endTimestamp;       // Pool end time
    address token;             // Token address
    uint256 TotalAmount;       // Total staked amount
    uint256 TotalFee;          // Total fees
    uint256 TotalFeeClaimed;   // Total fees claimed
    bool IsCompleted;          // Whether completed
}

// User stake record
struct User {
    bool isWithdrawed;         // Whether withdrawn
    uint256 StartPoolId;       // Start staking pool ID
    uint256 EndPoolId;         // End pool ID (currently unused)
    address token;             // Staked token
    uint256 Amount;            // Stake amount
}

// Key-value pair (for query returns)
struct KeyValuePair {
    address key;               // Token address
    uint256 value;             // Value (principal or reward)
}

Access Control

Role Permissions Description
Owner Pause/unpause contract Emergency contract state control
Relayer Execute bridge, rotate pools, set Core operational role
parameters
WithdrawManager Withdraw liquidity, emergency Fund management role
transfers
Regular User Stake, withdraw, bridge Daily operations

2. PoolManagerStorage.sol

Storage Layer - State Variable Definitions

State Variables

// Constants
address public constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

// Configuration parameters
uint32 public periodTime;              // Staking period (default 21 days)
uint256 public MinTransferAmount;      // Minimum bridge amount
uint256 public PerFee;                 // Fee rate (default 10000 = 0.1%)
uint256 public stakingMessageNumber;   // Staking message number

// Role addresses
address public relayerAddress;         // Relayer address
address public withdrawManager;        // Withdrawal manager address
IMessageManager public messageManager; // Message manager contract

// Supported tokens
address[] public SupportTokens;        // Supported token list

// Mappings
mapping(uint256 => bool) public IsSupportedChainId;        // Supported chain IDs
mapping(address => bool) public IsSupportToken;            // Supported tokens
mapping(address => uint256) public FundingPoolBalance;     // Funding pool balance
mapping(address => uint256) public FeePoolValue;           // Fee pool balance
mapping(address => uint256) public MinStakeAmount;         // Minimum stake amount
mapping(address => Pool[]) public Pools;                   // Staking pool list
mapping(address => User[]) public Users;                   // User staking records

3. MessageManager.sol

Message Manager - Cross-Chain Message Verification

Core Functions

Send Message (Source Chain)
function sendMessage(
    uint256 sourceChainId,
    uint256 destChainId,
    address sourceTokenAddress,
    address destTokenAddress,
    address _from,
    address _to,
    uint256 _value,
    uint256 _fee
) external onlyTokenBridge

Functionality:

  1. Generate unique message hash
  2. Increment message number (nonce)
  3. Mark message as sent
  4. Emit MessageSent event for Relayer to listen
Claim Message (Destination Chain)
function claimMessage(
    uint256 sourceChainId,
    uint256 destChainId,
    address sourceTokenAddress,
    address destTokenAddress,
    address _from,
    address _to,
    uint256 _value,
    uint256 _fee,
    uint256 _nonce
) external onlyTokenBridge nonReentrant

Functionality:

  1. Verify message hash
  2. Check if message already claimed (prevent replay)
  3. Mark message as claimed
  4. Emit MessageClaimed event

Security Mechanisms

Replay Attack Prevention:
├─ Source chain: sentMessageStatus[messageHash] = true
└─ Destination chain: claimMessageStatus[messageHash] = true

Message Uniqueness:
messageHash = keccak256(
    sourceChainId,
    destChainId,
    sourceToken,
    destToken,
    from,
    to,
    value,
    fee,
    nonce  ← Unique per message
)

4. MessageManagerStorage.sol

Message Storage Layer

abstract contract MessageManagerStorage is IMessageManager {
    uint256 public nextMessageNumber;                  // Next message number
    address public poolManagerAddress;                 // PoolManager contract address

    mapping(bytes32 => bool) public sentMessageStatus;   // Sent messages
    mapping(bytes32 => bool) public claimMessageStatus;  // Claimed messages
}

🔄 Complete Cross-Chain Flow

Step 1: User Initiates Bridge on Source Chain

// User calls on Ethereum
poolManager.BridageInitiateETH{value: 1 ether}(
    1,      // sourceChainId: Ethereum
    56,     // destChainId: BSC
    WBNB,   // destTokenAddress
    user    // to
);

Contract Execution:

  1. Verify chain ID and amount
  2. Receive user's 1 ETH
  3. Calculate fee: 0.001 ETH
  4. Actual bridge amount: 0.999 ETH
  5. Call messageManager.sendMessage()
  6. Emit InitiateETH and MessageSent events

Step 2: Relayer Monitors and Relays

// Relayer listens to source chain events
poolManager.on("MessageSent", async (event) => {
  const { sourceChainId, destChainId, messageHash, ...params } = event;

  // Verify transaction confirmations
  await waitForConfirmations(event.transactionHash, 12);

  // Execute Finalize on destination chain
  await destPoolManager.BridgeFinalizeETH(params);
});

Step 3: Complete Bridge on Destination Chain

// Relayer calls on BSC
poolManager.BridgeFinalizeETH{value: 0.999 ether}(
    1,      // sourceChainId: Ethereum
    56,     // destChainId: BSC
    ETH_ADDRESS,
    user,   // from
    user,   // to
    0.999 ether,
    0.001 ether,
    nonce
);

Contract Execution:

  1. Verify chain ID and Relayer permissions
  2. Send 0.999 ETH to user
  3. Call messageManager.claimMessage() to prevent replay
  4. Emit FinalizeETH and MessageClaimed events

💰 Staking and Reward Flow

Scenario: User Stakes ETH to Earn Transaction Fee Rewards

1. Initialize Token Support (Owner/Relayer)

// Add ETH staking support
poolManager.setSupportToken(
    ETH_ADDRESS,
    true,
    block.timestamp + 1 days  // Start accepting stakes in 1 day
);

// Result: Creates two pools
// Pool 0: Genesis pool (placeholder)
// Pool 1: First real staking pool (21-day period)

2. User Stakes

// User stakes 10 ETH
poolManager.DepositAndStakingETH{value: 10 ether}();

// Record created:
// Users[user].push({
//     isWithdrawed: false,
//     StartPoolId: 1,
//     token: ETH_ADDRESS,
//     Amount: 10 ether
// });

3. Fee Accumulation

Bridge transactions over 21 days:
├─ Transaction 1: 1 ETH × 0.1% = 0.001 ETH
├─ Transaction 2: 5 ETH × 0.1% = 0.005 ETH
├─ Transaction 3: 2 ETH × 0.1% = 0.002 ETH
└─ ...

Accumulated in FeePoolValue[ETH_ADDRESS] = 0.5 ETH

4. Pool Rotation (Relayer)

// After 21 days, Relayer rotates pool
Pool[] memory pools = new Pool[](1);
pools[0].token = ETH_ADDRESS;
poolManager.CompletePoolAndNew(pools);

// Execution:
// 1. Mark Pool 1 as completed
// 2. Distribute 0.5 ETH fees to Pool 1
// 3. Create new Pool 2, inherit all stakes

5. User Claims Rewards

// Query rewards
KeyValuePair[] memory rewards = poolManager.getReward();
// Returns: { key: ETH_ADDRESS, value: 0.05 ether }
// Calculation: (10 ETH / 100 ETH total stake) × 0.5 ETH fees = 0.05 ETH

// Claim rewards (principal continues staking)
poolManager.ClaimAllReward();

// Or withdraw principal + rewards
poolManager.WithdrawAll();

⚠️ Security Considerations

Implemented Security Measures

  1. Reentrancy Attack Prevention

    • Uses nonReentrant modifier
    • CEI pattern (some functions)
  2. Replay Attack Prevention

    • MessageManager message hash verification
    • Nonce mechanism
  3. Access Control

    • onlyOwner, onlyRelayer, onlyWithdrawManager
    • Multi-role permission separation
  4. Pause Mechanism

    • Can pause contract in emergency situations
  5. Safe Math Operations

    • Uses SafeERC20 for token transfers
    • Prevents integer overflow (Solidity 0.8+)

📊 Contract Interaction Diagram

┌─────────────┐
│    User     │
└──────┬──────┘
       │
       │ 1. Stake/Bridge/Withdraw
       ↓
┌─────────────────────────────┐
│      PoolManager.sol        │
│  ┌─────────────────────────┐│
│  │  Liquidity Management   ││
│  │  - depositEthToBridge   ││
│  │  - withdrawEthFromBridge││
│  └─────────────────────────┘│
│  ┌─────────────────────────┐│
│  │  Bridge Features        ││
│  │  - BridageInitiateETH   ││
│  │  - BridgeFinalizeETH    ││
│  └─────────────────────────┘│
│  ┌─────────────────────────┐│
│  │  Staking System         ││
│  │  - DepositAndStaking    ││
│  │  - WithdrawAll          ││
│  │  - ClaimReward          ││
│  └─────────────────────────┘│
└───────┬──────────────────┬──┘
        │                  │
        │ 2. Send/Claim    │
        ↓     messages     │
┌────────────────────┐     │
│ MessageManager.sol │     │
│  - sendMessage     │     │
│  - claimMessage    │     │
│  - Replay attack   │     │
│    prevention      │     │
└────────────────────┘     │
                           │
                           │ 3. Read/Write state
                           ↓
        ┌──────────────────────────────┐
        │  Storage Layer               │
        │  ┌───────────────────────────┤
        │  │ PoolManagerStorage        │
        │  │  - Pools                  │
        │  │  - Users                  │
        │  │  - FundingPoolBalance     │
        │  └───────────────────────────┤
        │  │ MessageManagerStorage     │
        │  │  - sentMessageStatus      │
        │  │  - claimMessageStatus     │
        │  └───────────────────────────┘
        └──────────────────────────────┘

🔧 Configuration Parameters

Parameter Default Description Modification Function
periodTime 21 days Staking period Set during init
MinTransferAmount 0.1 ether Minimum bridge amount setMinTransferAmount
PerFee 10000 (0.1%) Fee rate setPerFee
MinStakeAmount Per token Minimum staking amount setMinStakeAmount

📝 Event List

PoolManager Events

event DepositToken(address indexed token, address indexed from, uint256 amount);
event WithdrawToken(address indexed token, address indexed operator, address indexed to, uint256 amount);
event InitiateETH(uint256 indexed sourceChainId, uint256 indexed destChainId, address destTokenAddress, address indexed from, address to, uint256 amount);
event InitiateERC20(uint256 indexed sourceChainId, uint256 indexed destChainId, address sourceTokenAddress, address destTokenAddress, address indexed from, address to, uint256 amount);
event FinalizeETH(uint256 indexed sourceChainId, uint256 indexed destChainId, address sourceTokenAddress, address indexed from, address to, uint256 amount);
event FinalizeERC20(uint256 indexed sourceChainId, uint256 indexed destChainId, address sourceTokenAddress, address destTokenAddress, address indexed from, address to, uint256 amount);
event StakingETHEvent(address indexed user, uint256 indexed chainId, uint256 amount);
event StakingERC20Event(address indexed user, address indexed token, uint256 indexed chainId, uint256 amount);
event Withdraw(address indexed user, uint256 startPoolId, uint256 endPoolId, uint256 indexed chainId, address indexed token, uint256 amount, uint256 reward);
event ClaimReward(address indexed user, uint256 startPoolId, uint256 endPoolId, uint256 indexed chainId, address indexed token, uint256 reward);
event CompletePoolEvent(address indexed token, uint256 indexed poolId, uint256 indexed chainId);
event SetSupportTokenEvent(address indexed token, bool isSupport, uint256 indexed chainId);
event SetMinStakeAmountEvent(address indexed token, uint256 amount, uint256 indexed chainId);
event SetMinTransferAmount(uint256 amount);
event SetValidChainId(uint256 indexed chainId, bool isValid);
event SetPerFee(uint256 fee);

MessageManager Events

event MessageSent(
    uint256 indexed sourceChainId,
    uint256 indexed destChainId,
    address sourceTokenAddress,
    address destTokenAddress,
    address indexed from,
    address to,
    uint256 fee,
    uint256 value,
    uint256 messageNumber,
    bytes32 messageHash
);

event MessageClaimed(
    uint256 indexed sourceChainId,
    uint256 indexed destChainId,
    address sourceTokenAddress,
    address destTokenAddress,
    bytes32 indexed messageHash,
    uint256 nonce
);

🚀 Deployment Guide

1. Deployment Order

// 1. Deploy MessageManager
MessageManager messageManager = new MessageManager();
messageManager.initialize(owner, address(0)); // poolManager address set later

// 2. Deploy PoolManager
PoolManager poolManager = new PoolManager();
poolManager.initialize(
    owner,
    address(messageManager),
    relayerAddress,
    withdrawManagerAddress
);

// 3. Update MessageManager's poolManager address
messageManager.updatePoolManager(address(poolManager)); // Need to add this function

2. Initial Configuration

// Set supported chains
poolManager.setValidChainId(1, true);   // Ethereum
poolManager.setValidChainId(56, true);  // BSC
poolManager.setValidChainId(137, true); // Polygon

// Add token support and create staking pool
poolManager.setSupportToken(
    ETH_ADDRESS,
    true,
    block.timestamp + 1 days
);

// Set staking parameters
poolManager.setMinStakeAmount(ETH_ADDRESS, 0.1 ether);
poolManager.setPerFee(10000); // 0.1%

3. Provide Initial Liquidity

// Project provides initial liquidity
poolManager.depositEthToBridge{value: 100 ether}();

📚 Usage Examples

User Stakes ETH

const tx = await poolManager.DepositAndStakingETH({
  value: ethers.utils.parseEther("10"),
});
await tx.wait();
console.log("Staking successful!");

User Bridge Transfer

// Bridge 1 ETH from Ethereum to BSC
const tx = await poolManager.BridageInitiateETH(
  1, // Ethereum
  56, // BSC
  WBNB_ADDRESS,
  userAddress,
  { value: ethers.utils.parseEther("1") }
);
await tx.wait();
console.log("Bridge transaction initiated!");

Query and Claim Rewards

// Query rewards
const rewards = await poolManager.getReward();
console.log("ETH rewards:", ethers.utils.formatEther(rewards[0].value));

// Claim rewards
const tx = await poolManager.ClaimAllReward();
await tx.wait();
console.log("Rewards claimed!");

Command Examples

# Set valid chain ID on Sepolia
cast send --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY 0x015DD02C6D1CF3f1711dcf453ed4b4f34B778E65 "setValidChainId(uint256,bool)" 11155111 true

# Set valid chain ID on custom network
cast send --rpc-url $S_URP_URL --private-key $PRIVATE_KEY 0x9B3F87aa9ABbC18b78De9fF245cc945F794F7559 "setValidChainId(uint256,bool)" 90101 true

# Set supported ERC20 token on Sepolia
cast send --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY 0x9B3F87aa9ABbC18b78De9fF245cc945F794F7559 "setSupportERC20Token(address,bool)" 0x12E60438898FB3b4aac8439DEeD57194Dc9C87aa true

About

Contract repo for bridge

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published