mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 04:08:01 -05:00
190 lines
6.0 KiB
Solidity
190 lines
6.0 KiB
Solidity
// SPDX-License-Identifier: AGPL-3.0
|
|
pragma solidity ^0.8.30;
|
|
|
|
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
|
|
import { IL1ETHBridge } from "./interfaces/IL1ETHBridge.sol";
|
|
import { IL2ETHBridge } from "./interfaces/IL2ETHBridge.sol";
|
|
import { MessageServiceBase } from "../../messaging/MessageServiceBase.sol";
|
|
import { IMessageService } from "../../messaging/interfaces/IMessageService.sol";
|
|
import { IETHYieldManager } from "./interfaces/IETHYieldManager.sol";
|
|
|
|
contract L1ETHBridge is IL1ETHBridge, Initializable, UUPSUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, MessageServiceBase {
|
|
/**
|
|
* @notice The message status enum.
|
|
*/
|
|
enum MessageStatus {
|
|
Unknown,
|
|
Requested,
|
|
Finalized
|
|
}
|
|
|
|
/**
|
|
* @notice The completed message struct.
|
|
*/
|
|
struct Message {
|
|
uint256 withdrawalRequestId;
|
|
bytes32 messageHash;
|
|
MessageStatus status;
|
|
}
|
|
|
|
/**
|
|
* @notice The yield manager address.
|
|
*/
|
|
address public yieldManager;
|
|
|
|
/**
|
|
* @notice The next completed message id.
|
|
*/
|
|
uint256 public nextCompletedMessageId;
|
|
|
|
/**
|
|
* @notice The completed messages.
|
|
*/
|
|
mapping(uint256 => Message) public messages;
|
|
|
|
/**
|
|
* @dev Ensures the address is not address(0).
|
|
* @param _addr Address to check.
|
|
*/
|
|
modifier nonZeroAddress(address _addr) {
|
|
if (_addr == address(0)) revert L1ETHBridge__ZeroAddressNotAllowed();
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @dev Ensures the amount is not 0.
|
|
* @param _amount amount to check.
|
|
*/
|
|
modifier nonZeroAmount(uint256 _amount) {
|
|
if (_amount == 0) revert L1ETHBridge__ZeroValueNotAllowed();
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice Disables initializers to prevent reinitialization.
|
|
*/
|
|
constructor() {
|
|
_disableInitializers();
|
|
}
|
|
|
|
/**
|
|
* @notice Initializes the contract.
|
|
* @param _initialOwner The initial owner of the contract.
|
|
* @param _messageService The message service address (previously rollup).
|
|
* @param _yieldManager The yield manager address.
|
|
* @param _remoteSender The remote sender address (previously l2ETHBridge).
|
|
*/
|
|
function initialize(
|
|
address _initialOwner,
|
|
address _messageService,
|
|
address _remoteSender,
|
|
address _yieldManager
|
|
) external initializer nonZeroAddress(_yieldManager) {
|
|
_transferOwnership(_initialOwner);
|
|
__MessageServiceBase_init(_messageService);
|
|
_setRemoteSender(_remoteSender);
|
|
|
|
yieldManager = _yieldManager;
|
|
}
|
|
|
|
/**
|
|
* @notice Sets the message service address.
|
|
* @param _messageService The new message service address.
|
|
*/
|
|
function setMessageService(address _messageService) external onlyOwner nonZeroAddress(_messageService) {
|
|
emit MessageServiceUpdated(_messageService, address(messageService), msg.sender);
|
|
messageService = IMessageService(_messageService);
|
|
}
|
|
|
|
/**
|
|
* @notice Sets the remote sender address.
|
|
* @param _remoteSender The new remote sender address.
|
|
*/
|
|
function setRemoteSender(address _remoteSender) external onlyOwner {
|
|
_setRemoteSender(_remoteSender);
|
|
}
|
|
|
|
/**
|
|
* @notice Sets the yield manager address.
|
|
* @param _yieldManager The new yield manager address.
|
|
*/
|
|
function setYieldManager(address _yieldManager) external onlyOwner nonZeroAddress(_yieldManager) {
|
|
emit YieldManagerUpdated(_yieldManager, yieldManager, msg.sender);
|
|
yieldManager = _yieldManager;
|
|
}
|
|
|
|
/**
|
|
* @notice Completes the bridge. Callable only by the L1MessageService.
|
|
* @param _to The recipient address.
|
|
* @param _value The amount of ETH to transfer.
|
|
* @param _calldata The calldata to pass to the recipient.
|
|
*/
|
|
function completeBridging(
|
|
address _to,
|
|
uint256 _value,
|
|
bytes memory _calldata
|
|
) external nonReentrant onlyMessagingService onlyAuthorizedRemoteSender {
|
|
uint256 requestId = IETHYieldManager(yieldManager).requestWithdrawal(_value);
|
|
bytes32 hash = keccak256(abi.encode(_to, _value, _calldata));
|
|
messages[nextCompletedMessageId] = Message(requestId, hash, MessageStatus.Requested);
|
|
|
|
emit BridgingCompleted(nextCompletedMessageId);
|
|
|
|
nextCompletedMessageId++;
|
|
}
|
|
|
|
/**
|
|
* @notice Bridges ETH to the L2ETHBridge.
|
|
* @param _to The recipient address on the L2.
|
|
* @param _calldata The calldata to be sent to the L2ETHBridge.
|
|
*/
|
|
function bridgeETH(
|
|
address _to,
|
|
bytes memory _calldata
|
|
) external payable nonZeroAmount(msg.value) nonZeroAddress(_to) {
|
|
(bool success, ) = yieldManager.call{ value: msg.value }("");
|
|
if (!success) {
|
|
revert L1ETHBridge__YieldManagerDepositFailed();
|
|
}
|
|
|
|
bytes memory data = abi.encodeWithSelector(IL2ETHBridge.completeBridging.selector, _to, msg.value, _calldata);
|
|
messageService.sendMessage(remoteSender, 0, data);
|
|
}
|
|
|
|
function finalizeWithdrawal(uint256 _messageId, uint256 _hintId, address _to, uint256 _value, bytes memory _calldata) external {
|
|
Message memory message = messages[_messageId];
|
|
bytes32 hash = keccak256(abi.encode(_to, _value, _calldata));
|
|
|
|
if (message.status != MessageStatus.Requested) {
|
|
revert L1ETHBridge__MessageAlreadyFinalized();
|
|
}
|
|
|
|
if (message.messageHash != hash) {
|
|
revert L1ETHBridge__InvalidMessageParameters();
|
|
}
|
|
|
|
message.status = MessageStatus.Finalized;
|
|
|
|
uint256 balanceBefore = address(this).balance;
|
|
IETHYieldManager(yieldManager).claimWithdrawal(message.withdrawalRequestId, _hintId);
|
|
if (address(this).balance != balanceBefore + _value) {
|
|
revert L1ETHBridge__WithdrawalClaimFailed();
|
|
}
|
|
|
|
|
|
(bool success, ) = _to.call{ value: _value }(_calldata);
|
|
if (!success) {
|
|
revert L1ETHBridge__ETHTransferFailed();
|
|
}
|
|
|
|
emit MessageFinalized(_messageId);
|
|
}
|
|
|
|
function _authorizeUpgrade(address) internal view override {
|
|
_checkOwner();
|
|
}
|
|
}
|