feat: implement L1ETHBridge.finalizeWithdrawal

This commit is contained in:
Andrea Franz
2025-06-27 16:16:50 +02:00
parent 6200e94927
commit 962c731aa9
5 changed files with 67 additions and 8 deletions

View File

@@ -12,12 +12,22 @@ 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 CompletedMessage {
struct Message {
uint256 withdrawalRequestId;
bytes32 messageHash;
MessageStatus status;
}
/**
@@ -33,7 +43,7 @@ contract L1ETHBridge is IL1ETHBridge, Initializable, UUPSUpgradeable, OwnableUpg
/**
* @notice The completed messages.
*/
mapping(uint256 => CompletedMessage) public completedMessages;
mapping(uint256 => Message) public messages;
/**
* @dev Ensures the address is not address(0).
@@ -117,10 +127,12 @@ contract L1ETHBridge is IL1ETHBridge, Initializable, UUPSUpgradeable, OwnableUpg
uint256 _value,
bytes memory _calldata
) external nonReentrant onlyMessagingService onlyAuthorizedRemoteSender {
emit MessageCompleted(nextCompletedMessageId);
bytes32 hash = keccak256(abi.encode(_to, _value, _calldata));
uint256 requestId = IETHYieldManager(yieldManager).requestWithdrawal(_value);
completedMessages[nextCompletedMessageId] = CompletedMessage(requestId, hash);
bytes32 hash = keccak256(abi.encode(_to, _value, _calldata));
messages[nextCompletedMessageId] = Message(requestId, hash, MessageStatus.Requested);
emit BridgingCompleted(nextCompletedMessageId);
nextCompletedMessageId++;
}
@@ -142,6 +154,35 @@ contract L1ETHBridge is IL1ETHBridge, Initializable, UUPSUpgradeable, OwnableUpg
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();
}

View File

@@ -3,4 +3,6 @@ pragma solidity ^0.8.26;
interface IETHYieldManager {
function requestWithdrawal(uint256 _amount) external returns (uint256 requestId);
function claimWithdrawal(uint256 _requestId, uint256 _hintId) external returns (bool success);
}

View File

@@ -14,7 +14,19 @@ interface IL1ETHBridge {
address indexed setBy
);
event MessageCompleted(
/**
* @notice Emitted when a bridging is completed.
* @param messageId The indexed message id.
*/
event BridgingCompleted(
uint256 indexed messageId
);
/**
* @notice Emitted when a message is finalized.
* @param messageId The indexed message id.
*/
event MessageFinalized(
uint256 indexed messageId
);
@@ -34,6 +46,9 @@ interface IL1ETHBridge {
error L1ETHBridge__ZeroAddressNotAllowed();
error L1ETHBridge__ETHTransferFailed();
error L1ETHBridge__YieldManagerDepositFailed();
error L1ETHBridge__MessageAlreadyFinalized();
error L1ETHBridge__InvalidMessageParameters();
error L1ETHBridge__WithdrawalClaimFailed();
function setRemoteSender(address _remoteSender) external;

View File

@@ -149,8 +149,9 @@ contract L1ETHBridgeTest is Test {
bridge.completeBridging(user1, 100, "test-data");
assertEq(bridge.nextCompletedMessageId(), 1);
(uint256 withdrawalRequestId, bytes32 hash) = bridge.completedMessages(0);
(uint256 withdrawalRequestId, bytes32 hash, L1ETHBridge.MessageStatus status) = bridge.messages(0);
assertEq(withdrawalRequestId, 0);
assertEq(hash, keccak256(abi.encode(user1, 100, "test-data")));
assertEq(uint256(status), uint256(L1ETHBridge.MessageStatus.Requested));
}
}

View File

@@ -24,7 +24,7 @@ contract ETHYieldManagerMock {
return deposits[deposits.length - 1];
}
function requestWithdrawal(uint256 _amount) external returns (uint256 requestId) {
function requestWithdrawal(uint256) external returns (uint256 requestId) {
return nextRequestId++;
}
}