Erbie Bridge#

Contract Overview#

Erbie Bridge is a multi-token bridge contract that supports locking and emergency withdrawal of both XL and L tokens. The contract is implemented based on OpenZeppelin’s upgradeable contract framework with the following core features:

  • Upgradeability: Secure upgrades through UUPS upgrade pattern

  • Access Control: Owner permission management based on OwnableUpgradeable

  • Pause Mechanism: Support for global pause/unpause operations

  • Token Lock Records: Tracking user’s locked token information

Core Dependencies and Interfaces#

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; // UUPS upgrade
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // ERC20 interface
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; // Initialization
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; // Owner permissions
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; // Pause functionality
import "./interfaces/IErbieBridge.sol"; // Custom interface

Core Variables and Structs#

Token Addresses#

IERC20 public XL; // XL token interface
IERC20 public L;   // L token interface

Lock Counter and Version Number#

uint256 public lockCounter; // Lock record counter
uint256 public version;     // Contract version number

Lock Record Structure#

struct LockInfo {
    address user;         // User address
    address tokenAddress; // Locked token address
    uint256 amount;       // Lock amount
    bool withdrawn;       // Whether withdrawn
}
mapping(uint256 => LockInfo) public lockRecords; // Lock records mapping

Core Function Details#

1. Initialize Function#

function initialize(
    address _XL,
    address _L,
    address initialOwner
) external initializer {
    require(_XL != address(0) && _L != address(0), "Invalid token address");
    __Ownable_init(initialOwner);
    __Pausable_init();
    __UUPSUpgradeable_init();
    XL = IERC20(_XL);
    L = IERC20(_L);
    version = 1;
    lockCounter = 0;
}
  • Function: Contract initialization, setting token addresses, owner, and initial state

  • Key Points:
    • Validate token address legitimacy

    • Initialize parent contracts (permissions, pause, upgrade)

    • Initialize counter and version number

2. Token Address Management#

function setXLTokenAddress(address _XL) public onlyOwner {
    require(_XL != address(0), "Cannot set to the zero address");
    XL = IERC20(_XL);
}

function setLTokenAddress(address _L) public onlyOwner {
    require(_L != address(0), "Cannot set to the zero address");
    L = IERC20(_L);
}
  • Function: Allows owner to update token contract addresses

  • Risk: Malicious modification of token addresses could lead to fund loss

3. User Token Locking#

function lock(
    address tokenAddress,
    uint256 amount
) external payable override whenNotPaused {
    require(
        tokenAddress == address(XL) || tokenAddress == address(L),
        "Invalid token address"
    );
    require(amount > 0, "Amount must be greater than 0");
    IERC20 token = IERC20(tokenAddress);
    require(
        token.allowance(msg.sender, address(this)) >= amount,
        "Allowance too low"
    );
    token.transferFrom(msg.sender, address(this), amount);
    lockCounter++;
    uint256 currentLockId = lockCounter;
    lockRecords[currentLockId] = LockInfo(
        msg.sender,
        tokenAddress,
        amount,
        false
    );
    emit TokenLocked(
        msg.sender,
        tokenAddress,
        amount,
        currentLockId,
        block.timestamp
    );
}
  • Function: Users lock specified tokens into the contract

  • Key Logic:
    1. Verify if token is XL or L

    2. Check token amount validity

    3. Verify user allowance

    4. Transfer tokens and record lock information

  • Event: TokenLocked records lock details

4. Emergency Token Withdrawal#

function emergencyWithdraw(
    address tokenAddress,
    address to,
    uint256 amount
) public override onlyOwner {
    IERC20 token = IERC20(tokenAddress);
    token.transfer(to, amount);
}
  • Function: Owner emergency withdraws tokens to specified address

  • Risks:
    • Can withdraw any token (including unsupported tokens)

    • Need to ensure owner permission security

5. Upgrade and Permission Control#

function _authorizeUpgrade(
    address newImplementation
) internal override onlyOwner {
    require(
        newImplementation != address(0),
        "Cannot upgrade to the zero address"
    );
}
  • Function: Authorize upgrade to new implementation contract

  • Restriction: Only owner can execute, and new address must be valid

6. Pause and Unpause#

function pause() external onlyOwner {
    _pause();
}

function unpause() external onlyOwner {
    _unpause();
}
  • Function: Owner controls contract pause status

7. Version Management#

function setVersion(uint256 newVersion) external onlyOwner {
    require(newVersion > 0, "Version must be greater than 0");
    version = newVersion;
}
  • Function: Update contract version number