forked from scroll-tech/scroll
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathL1ETHGateway.sol
150 lines (121 loc) · 5.3 KB
/
L1ETHGateway.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// SPDX-License-Identifier: MIT
pragma solidity =0.8.24;
import {IL2ETHGateway} from "../../L2/gateways/IL2ETHGateway.sol";
import {IL1ScrollMessenger} from "../IL1ScrollMessenger.sol";
import {IL1ETHGateway} from "./IL1ETHGateway.sol";
import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol";
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
// solhint-disable avoid-low-level-calls
/// @title L1ETHGateway
/// @notice The `L1ETHGateway` is used to deposit ETH on layer 1 and
/// finalize withdraw ETH from layer 2.
/// @dev The deposited ETH tokens are held in this gateway. On finalizing withdraw, the corresponding
/// ETH will be transfer to the recipient directly.
contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback {
/***************
* Constructor *
***************/
/// @notice Constructor for `L1ETHGateway` implementation contract.
///
/// @param _counterpart The address of `L2ETHGateway` contract in L2.
/// @param _router The address of `L1GatewayRouter` contract in L1.
/// @param _messenger The address of `L1ScrollMessenger` contract in L1.
constructor(
address _counterpart,
address _router,
address _messenger
) ScrollGatewayBase(_counterpart, _router, _messenger) {
if (_router == address(0)) revert ErrorZeroAddress();
_disableInitializers();
}
/// @notice Initialize the storage of L1ETHGateway.
///
/// @dev The parameters `_counterpart`, `_router` and `_messenger` are no longer used.
///
/// @param _counterpart The address of L2ETHGateway in L2.
/// @param _router The address of L1GatewayRouter in L1.
/// @param _messenger The address of L1ScrollMessenger in L1.
function initialize(
address _counterpart,
address _router,
address _messenger
) external initializer {
ScrollGatewayBase._initialize(_counterpart, _router, _messenger);
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IL1ETHGateway
function depositETH(uint256 _amount, uint256 _gasLimit) external payable override {
_deposit(_msgSender(), _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ETHGateway
function depositETH(
address _to,
uint256 _amount,
uint256 _gasLimit
) external payable override {
_deposit(_to, _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ETHGateway
function depositETHAndCall(
address _to,
uint256 _amount,
bytes calldata _data,
uint256 _gasLimit
) external payable override {
_deposit(_to, _amount, _data, _gasLimit);
}
/// @inheritdoc IL1ETHGateway
function finalizeWithdrawETH(
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable override onlyCallByCounterpart nonReentrant {
require(msg.value == _amount, "msg.value mismatch");
// @note can possible trigger reentrant call to messenger,
// but it seems not a big problem.
(bool _success, ) = _to.call{value: _amount}("");
require(_success, "ETH transfer failed");
_doCallback(_to, _data);
emit FinalizeWithdrawETH(_from, _to, _amount, _data);
}
/// @inheritdoc IMessageDropCallback
function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant {
// _message should start with 0x232e8748 => finalizeDepositETH(address,address,uint256,bytes)
require(bytes4(_message[0:4]) == IL2ETHGateway.finalizeDepositETH.selector, "invalid selector");
// decode (receiver, amount)
(address _receiver, , uint256 _amount, ) = abi.decode(_message[4:], (address, address, uint256, bytes));
require(_amount == msg.value, "msg.value mismatch");
(bool _success, ) = _receiver.call{value: _amount}("");
require(_success, "ETH transfer failed");
emit RefundETH(_receiver, _amount);
}
/**********************
* Internal Functions *
**********************/
/// @dev The internal ETH deposit implementation.
/// @param _to The address of recipient's account on L2.
/// @param _amount The amount of ETH to be deposited.
/// @param _data Optional data to forward to recipient's account.
/// @param _gasLimit Gas limit required to complete the deposit on L2.
function _deposit(
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual nonReentrant {
require(_amount > 0, "deposit zero eth");
// 1. Extract real sender if this call is from L1GatewayRouter.
address _from = _msgSender();
if (router == _from) {
(_from, _data) = abi.decode(_data, (address, bytes));
}
// @note no rate limit here, since ETH is limited in messenger
// 2. Generate message passed to L1ScrollMessenger.
bytes memory _message = abi.encodeCall(IL2ETHGateway.finalizeDepositETH, (_from, _to, _amount, _data));
IL1ScrollMessenger(messenger).sendMessage{value: msg.value}(counterpart, _amount, _message, _gasLimit, _from);
emit DepositETH(_from, _to, _amount, _data);
}
}