forked from scroll-tech/scroll
-
Notifications
You must be signed in to change notification settings - Fork 0
/
L1ERC20Gateway.sol
181 lines (155 loc) · 6.58 KB
/
L1ERC20Gateway.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {IL1ERC20Gateway} from "./IL1ERC20Gateway.sol";
import {IL1GatewayRouter} from "./IL1GatewayRouter.sol";
import {IL2ERC20Gateway} from "../../L2/gateways/IL2ERC20Gateway.sol";
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol";
/// @title L1ERC20Gateway
/// @notice The `L1ERC20Gateway` as a base contract for ERC20 gateways in L1.
/// It has implementation of common used functions for ERC20 gateways.
abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, ScrollGatewayBase {
using SafeERC20Upgradeable for IERC20Upgradeable;
/*************
* Variables *
*************/
/// @dev The storage slots for future usage.
uint256[50] private __gap;
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IL1ERC20Gateway
function depositERC20(
address _token,
uint256 _amount,
uint256 _gasLimit
) external payable override {
_deposit(_token, _msgSender(), _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ERC20Gateway
function depositERC20(
address _token,
address _to,
uint256 _amount,
uint256 _gasLimit
) external payable override {
_deposit(_token, _to, _amount, new bytes(0), _gasLimit);
}
/// @inheritdoc IL1ERC20Gateway
function depositERC20AndCall(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) external payable override {
_deposit(_token, _to, _amount, _data, _gasLimit);
}
/// @inheritdoc IL1ERC20Gateway
function finalizeWithdrawERC20(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) external payable virtual override onlyCallByCounterpart nonReentrant {
_beforeFinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
// @note can possible trigger reentrant call to this contract or messenger,
// but it seems not a big problem.
IERC20Upgradeable(_l1Token).safeTransfer(_to, _amount);
_doCallback(_to, _data);
emit FinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data);
}
/// @inheritdoc IMessageDropCallback
function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant {
// _message should start with 0x8431f5c1 => finalizeDepositERC20(address,address,address,address,uint256,bytes)
require(bytes4(_message[0:4]) == IL2ERC20Gateway.finalizeDepositERC20.selector, "invalid selector");
// decode (token, receiver, amount)
(address _token, , address _receiver, , uint256 _amount, ) = abi.decode(
_message[4:],
(address, address, address, address, uint256, bytes)
);
// do dome check for each custom gateway
_beforeDropMessage(_token, _receiver, _amount);
IERC20Upgradeable(_token).safeTransfer(_receiver, _amount);
emit RefundERC20(_token, _receiver, _amount);
}
/**********************
* Internal Functions *
**********************/
/// @dev Internal function hook to perform checks and actions before finalizing the withdrawal.
/// @param _l1Token The address of corresponding L1 token in L1.
/// @param _l2Token The address of corresponding L2 token in L2.
/// @param _from The address of account who withdraw the token in L2.
/// @param _to The address of recipient in L1 to receive the token.
/// @param _amount The amount of the token to withdraw.
/// @param _data Optional data to forward to recipient's account.
function _beforeFinalizeWithdrawERC20(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _data
) internal virtual;
/// @dev Internal function hook to perform checks and actions before dropping the message.
/// @param _token The L1 token address.
/// @param _receiver The recipient address on L1.
/// @param _amount The amount of token to refund.
function _beforeDropMessage(
address _token,
address _receiver,
uint256 _amount
) internal virtual;
/// @dev Internal function to transfer ERC20 token to this contract.
/// @param _token The address of token to transfer.
/// @param _amount The amount of token to transfer.
/// @param _data The data passed by caller.
function _transferERC20In(
address _token,
uint256 _amount,
bytes memory _data
)
internal
returns (
address,
uint256,
bytes memory
)
{
address _sender = _msgSender();
address _from = _sender;
if (router == _sender) {
// Extract real sender if this call is from L1GatewayRouter.
(_from, _data) = abi.decode(_data, (address, bytes));
_amount = IL1GatewayRouter(_sender).requestERC20(_from, _token, _amount);
} else {
// common practice to handle fee on transfer token.
uint256 _before = IERC20Upgradeable(_token).balanceOf(address(this));
IERC20Upgradeable(_token).safeTransferFrom(_from, address(this), _amount);
uint256 _after = IERC20Upgradeable(_token).balanceOf(address(this));
// no unchecked here, since some weird token may return arbitrary balance.
_amount = _after - _before;
}
// ignore weird fee on transfer token
require(_amount > 0, "deposit zero amount");
return (_from, _amount, _data);
}
/// @dev Internal function to do all the deposit operations.
///
/// @param _token The token to deposit.
/// @param _to The recipient address to receive the token in L2.
/// @param _amount The amount of token to deposit.
/// @param _data Optional data to forward to recipient's account.
/// @param _gasLimit Gas limit required to complete the deposit on L2.
function _deposit(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual;
}