forked from scroll-tech/scroll
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathL2StandardERC20Gateway.sol
192 lines (155 loc) · 6.92 KB
/
L2StandardERC20Gateway.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
182
183
184
185
186
187
188
189
190
191
192
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import {IL2ERC20Gateway, L2ERC20Gateway} from "./L2ERC20Gateway.sol";
import {IL2ScrollMessenger} from "../IL2ScrollMessenger.sol";
import {IL1ERC20Gateway} from "../../L1/gateways/IL1ERC20Gateway.sol";
import {IScrollERC20Upgradeable} from "../../libraries/token/IScrollERC20Upgradeable.sol";
import {ScrollStandardERC20} from "../../libraries/token/ScrollStandardERC20.sol";
import {IScrollStandardERC20Factory} from "../../libraries/token/IScrollStandardERC20Factory.sol";
import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol";
/// @title L2StandardERC20Gateway
/// @notice The `L2StandardERC20Gateway` is used to withdraw standard ERC20 tokens on layer 2 and
/// finalize deposit the tokens from layer 1.
/// @dev The withdrawn ERC20 tokens will be burned directly. On finalizing deposit, the corresponding
/// token will be minted and transferred to the recipient. Any ERC20 that requires non-standard functionality
/// should use a separate gateway.
contract L2StandardERC20Gateway is L2ERC20Gateway {
using AddressUpgradeable for address;
/*************
* Constants *
*************/
/// @notice The address of ScrollStandardERC20Factory.
address public immutable tokenFactory;
/*************
* Variables *
*************/
/// @notice Mapping from l2 token address to l1 token address.
mapping(address => address) private tokenMapping;
/// @dev The storage slot used as ScrollStandardERC20Factory contract, which is deprecated now.
address private __tokenFactory;
/***************
* Constructor *
***************/
/// @notice Constructor for `L2StandardERC20Gateway` implementation contract.
///
/// @param _counterpart The address of `L1StandardERC20Gateway` contract in L1.
/// @param _router The address of `L2GatewayRouter` contract in L2.
/// @param _messenger The address of `L2ScrollMessenger` contract in L2.
/// @param _tokenFactory The address of `ScrollStandardERC20Factory` contract in L2.
constructor(
address _counterpart,
address _router,
address _messenger,
address _tokenFactory
) ScrollGatewayBase(_counterpart, _router, _messenger) {
if (_router == address(0) || _tokenFactory == address(0)) revert ErrorZeroAddress();
_disableInitializers();
tokenFactory = _tokenFactory;
}
/// @notice Initialize the storage of L2StandardERC20Gateway.
///
/// @dev The parameters `_counterpart`, `_router`, `_messenger` and `_tokenFactory` are no longer used.
///
/// @param _counterpart The address of `L1StandardERC20Gateway` contract in L1.
/// @param _router The address of `L2GatewayRouter` contract in L2.
/// @param _messenger The address of `L2ScrollMessenger` contract in L2.
function initialize(
address _counterpart,
address _router,
address _messenger,
address /*_tokenFactory*/
) external initializer {
ScrollGatewayBase._initialize(_counterpart, _router, _messenger);
}
/*************************
* Public View Functions *
*************************/
/// @inheritdoc IL2ERC20Gateway
function getL1ERC20Address(address _l2Token) external view override returns (address) {
return tokenMapping[_l2Token];
}
/// @inheritdoc IL2ERC20Gateway
function getL2ERC20Address(address _l1Token) public view override returns (address) {
return IScrollStandardERC20Factory(tokenFactory).computeL2TokenAddress(address(this), _l1Token);
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IL2ERC20Gateway
function finalizeDepositERC20(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes memory _data
) external payable override onlyCallByCounterpart nonReentrant {
require(msg.value == 0, "nonzero msg.value");
require(_l1Token != address(0), "token address cannot be 0");
{
// avoid stack too deep
address _expectedL2Token = IScrollStandardERC20Factory(tokenFactory).computeL2TokenAddress(
address(this),
_l1Token
);
require(_l2Token == _expectedL2Token, "l2 token mismatch");
}
bool _hasMetadata;
(_hasMetadata, _data) = abi.decode(_data, (bool, bytes));
bytes memory _deployData;
bytes memory _callData;
if (_hasMetadata) {
(_callData, _deployData) = abi.decode(_data, (bytes, bytes));
} else {
require(tokenMapping[_l2Token] == _l1Token, "token mapping mismatch");
_callData = _data;
}
if (!_l2Token.isContract()) {
// first deposit, update mapping
tokenMapping[_l2Token] = _l1Token;
_deployL2Token(_deployData, _l1Token);
}
IScrollERC20Upgradeable(_l2Token).mint(_to, _amount);
_doCallback(_to, _callData);
emit FinalizeDepositERC20(_l1Token, _l2Token, _from, _to, _amount, _callData);
}
/**********************
* Internal Functions *
**********************/
/// @inheritdoc L2ERC20Gateway
function _withdraw(
address _token,
address _to,
uint256 _amount,
bytes memory _data,
uint256 _gasLimit
) internal virtual override nonReentrant {
require(_amount > 0, "withdraw zero amount");
// 1. Extract real sender if this call is from L2GatewayRouter.
address _from = _msgSender();
if (router == _from) {
(_from, _data) = abi.decode(_data, (address, bytes));
}
address _l1Token = tokenMapping[_token];
require(_l1Token != address(0), "no corresponding l1 token");
// 2. Burn token.
IScrollERC20Upgradeable(_token).burn(_from, _amount);
// 3. Generate message passed to L1StandardERC20Gateway.
bytes memory _message = abi.encodeCall(
IL1ERC20Gateway.finalizeWithdrawERC20,
(_l1Token, _token, _from, _to, _amount, _data)
);
// 4. send message to L2ScrollMessenger
IL2ScrollMessenger(messenger).sendMessage{value: msg.value}(counterpart, 0, _message, _gasLimit);
emit WithdrawERC20(_l1Token, _token, _from, _to, _amount, _data);
}
function _deployL2Token(bytes memory _deployData, address _l1Token) internal {
address _l2Token = IScrollStandardERC20Factory(tokenFactory).deployL2Token(address(this), _l1Token);
(string memory _symbol, string memory _name, uint8 _decimals) = abi.decode(
_deployData,
(string, string, uint8)
);
ScrollStandardERC20(_l2Token).initialize(_name, _symbol, _decimals, address(this), _l1Token);
}
}