// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title L1BridgeContract
* @notice 以太坊主网跨链桥合约
* @dev 处理存款和提款
*/
contract L1BridgeContract is ReentrancyGuard, AccessControl {
using SafeERC20 for IERC20;
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
// ==================== 数据结构 ====================
struct DepositInfo {
address sender; // 存款人 (L1)
address recipient; // 接收人 (L2)
address token; // 代币地址 (L1)
uint256 amount; // 金额
uint256 depositId; // 存款 ID
uint256 timestamp; // 时间戳
}
struct WithdrawalInfo {
address recipient; // 接收人 (L1)
address token; // 代币地址 (L1)
uint256 amount; // 金额
bytes32 withdrawalHash; // 提款哈希
bool claimed; // 是否已领取
}
// ==================== 状态变量 ====================
// 下一个存款 ID
uint256 public nextDepositId;
// 存款记录: depositId => DepositInfo
mapping(uint256 => DepositInfo) public deposits;
// 提款记录: withdrawalHash => WithdrawalInfo
mapping(bytes32 => WithdrawalInfo) public withdrawals;
// L2 已验证的提款 Merkle Root
bytes32 public l2WithdrawalRoot;
// 支持的代币列表
mapping(address => bool) public supportedTokens;
// 最小/最大存款金额
mapping(address => uint256) public minDepositAmount;
mapping(address => uint256) public maxDepositAmount;
// 暂停状态
bool public isPaused;
// ==================== 事件 ====================
event Deposit(
uint256 indexed depositId,
address indexed sender,
address indexed recipient,
address token,
uint256 amount
);
event WithdrawalClaimed(
bytes32 indexed withdrawalHash,
address indexed recipient,
address token,
uint256 amount
);
event L2WithdrawalRootUpdated(
bytes32 indexed oldRoot,
bytes32 indexed newRoot,
uint256 timestamp
);
// ==================== 构造函数 ====================
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(GUARDIAN_ROLE, msg.sender);
// 支持 ETH (零地址)
supportedTokens[address(0)] = true;
}
// ==================== 存款功能 ====================
/**
* @notice 存款 ETH 到 L2
* @param recipient L2 接收地址
*/
function depositETH(address recipient)
external
payable
nonReentrant
whenNotPaused
{
require(msg.value > 0, "Zero amount");
require(recipient != address(0), "Invalid recipient");
require(
msg.value >= minDepositAmount[address(0)] &&
msg.value <= maxDepositAmount[address(0)],
"Amount out of range"
);
uint256 depositId = nextDepositId++;
deposits[depositId] = DepositInfo({
sender: msg.sender,
recipient: recipient,
token: address(0), // ETH
amount: msg.value,
depositId: depositId,
timestamp: block.timestamp
});
emit Deposit(depositId, msg.sender, recipient, address(0), msg.value);
}
/**
* @notice 存款 ERC20 到 L2
* @param token ERC20 代币地址
* @param amount 金额
* @param recipient L2 接收地址
*/
function depositERC20(
address token,
uint256 amount,
address recipient
) external nonReentrant whenNotPaused {
require(supportedTokens[token], "Token not supported");
require(amount > 0, "Zero amount");
require(recipient != address(0), "Invalid recipient");
require(
amount >= minDepositAmount[token] &&
amount <= maxDepositAmount[token],
"Amount out of range"
);
// 转移代币到合约
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
uint256 depositId = nextDepositId++;
deposits[depositId] = DepositInfo({
sender: msg.sender,
recipient: recipient,
token: token,
amount: amount,
depositId: depositId,
timestamp: block.timestamp
});
emit Deposit(depositId, msg.sender, recipient, token, amount);
}
// ==================== 提款功能 ====================
/**
* @notice 领取提款 (需要 Merkle Proof)
* @param recipient 接收地址
* @param token 代币地址
* @param amount 金额
* @param merkleProof Merkle 证明
*/
function claimWithdrawal(
address recipient,
address token,
uint256 amount,
bytes32[] calldata merkleProof
) external nonReentrant {
// 计算提款哈希
bytes32 withdrawalHash = keccak256(
abi.encodePacked(recipient, token, amount)
);
// 检查是否已领取
require(!withdrawals[withdrawalHash].claimed, "Already claimed");
// 验证 Merkle Proof
require(
_verifyMerkleProof(withdrawalHash, merkleProof, l2WithdrawalRoot),
"Invalid merkle proof"
);
// 标记为已领取
withdrawals[withdrawalHash] = WithdrawalInfo({
recipient: recipient,
token: token,
amount: amount,
withdrawalHash: withdrawalHash,
claimed: true
});
// 转账资产
if (token == address(0)) {
// ETH
(bool success, ) = recipient.call{value: amount}("");
require(success, "ETH transfer failed");
} else {
// ERC20
IERC20(token).safeTransfer(recipient, amount);
}
emit WithdrawalClaimed(withdrawalHash, recipient, token, amount);
}
/**
* @notice 更新 L2 提款 Merkle Root (由 Relayer 调用)
* @param newRoot 新的 Merkle Root
*/
function updateWithdrawalRoot(bytes32 newRoot)
external
onlyRole(RELAYER_ROLE)
{
bytes32 oldRoot = l2WithdrawalRoot;
l2WithdrawalRoot = newRoot;
emit L2WithdrawalRootUpdated(oldRoot, newRoot, block.timestamp);
}
// ==================== 管理功能 ====================
/**
* @notice 添加支持的代币
*/
function addSupportedToken(
address token,
uint256 minAmount,
uint256 maxAmount
) external onlyRole(DEFAULT_ADMIN_ROLE) {
supportedTokens[token] = true;
minDepositAmount[token] = minAmount;
maxDepositAmount[token] = maxAmount;
}
/**
* @notice 紧急暂停
*/
function pause() external onlyRole(GUARDIAN_ROLE) {
isPaused = true;
}
/**
* @notice 恢复服务
*/
function unpause() external onlyRole(GUARDIAN_ROLE) {
isPaused = false;
}
/**
* @notice 紧急提取资产 (仅限 Guardian)
* @dev 用于极端情况: 合约升级或安全事件
*/
function emergencyWithdraw(address token, uint256 amount)
external
onlyRole(GUARDIAN_ROLE)
{
if (token == address(0)) {
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "ETH transfer failed");
} else {
IERC20(token).safeTransfer(msg.sender, amount);
}
}
// ==================== 内部函数 ====================
/**
* @notice 验证 Merkle Proof
*/
function _verifyMerkleProof(
bytes32 leaf,
bytes32[] memory proof,
bytes32 root
) private pure returns (bool) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
computedHash = keccak256(
abi.encodePacked(computedHash, proofElement)
);
} else {
computedHash = keccak256(
abi.encodePacked(proofElement, computedHash)
);
}
}
return computedHash == root;
}
// ==================== Modifiers ====================
modifier whenNotPaused() {
require(!isPaused, "Contract is paused");
_;
}
// ==================== 查询函数 ====================
/**
* @notice 获取存款信息
*/
function getDeposit(uint256 depositId)
external
view
returns (DepositInfo memory)
{
return deposits[depositId];
}
/**
* @notice 检查提款是否已领取
*/
function isWithdrawalClaimed(
address recipient,
address token,
uint256 amount
) external view returns (bool) {
bytes32 withdrawalHash = keccak256(
abi.encodePacked(recipient, token, amount)
);
return withdrawals[withdrawalHash].claimed;
}
}