@@ -50,8 +50,6 @@ contract FuelERC20GatewayV4 is
50
50
// Constants //
51
51
///////////////
52
52
53
- bytes1 public constant DEPOSIT_TO_CONTRACT = bytes1 (keccak256 ("DEPOSIT_TO_CONTRACT " ));
54
-
55
53
/// @dev The admin related contract roles
56
54
bytes32 public constant PAUSER_ROLE = keccak256 ("PAUSER_ROLE " );
57
55
uint256 public constant FUEL_ASSET_DECIMALS = 9 ;
@@ -107,6 +105,7 @@ contract FuelERC20GatewayV4 is
107
105
}
108
106
109
107
/// @notice see `requireWhitelist`
108
+ /// @dev param `limit` must be down/up scaled according to _adjustDepositDecimals
110
109
function setGlobalDepositLimit (address token , uint256 limit ) external payable virtual onlyRole (DEFAULT_ADMIN_ROLE) {
111
110
_depositLimits[token] = limit;
112
111
}
@@ -143,18 +142,18 @@ contract FuelERC20GatewayV4 is
143
142
/// @dev Made payable to reduce gas costs
144
143
function deposit (bytes32 to , address tokenAddress , uint256 amount ) external payable virtual whenNotPaused {
145
144
uint8 decimals = _getTokenDecimals (tokenAddress);
145
+ uint256 l2MintedAmount = _adjustDepositDecimals (decimals, amount);
146
146
147
147
bytes memory messageData = abi.encodePacked (
148
- MessageType.DEPOSIT,
149
148
assetIssuerId,
149
+ MessageType.DEPOSIT,
150
150
bytes32 (uint256 (uint160 (tokenAddress))),
151
151
bytes32 (0 ),
152
152
bytes32 (uint256 (uint160 (msg .sender ))),
153
153
to,
154
- amount,
155
- decimals
154
+ l2MintedAmount
156
155
);
157
- _deposit (tokenAddress, amount, messageData);
156
+ _deposit (tokenAddress, amount, l2MintedAmount, messageData);
158
157
}
159
158
160
159
/// @notice Deposits the given tokens to a contract on Fuel with optional data
@@ -170,45 +169,56 @@ contract FuelERC20GatewayV4 is
170
169
bytes calldata data
171
170
) external payable virtual whenNotPaused {
172
171
uint8 decimals = _getTokenDecimals (tokenAddress);
172
+ uint256 l2MintedAmount = _adjustDepositDecimals (decimals, amount);
173
173
174
174
bytes memory messageData = abi.encodePacked (
175
- MessageType.DEPOSIT,
176
175
assetIssuerId,
176
+ MessageType.DEPOSIT,
177
177
bytes32 (uint256 (uint160 (tokenAddress))),
178
178
bytes32 (0 ),
179
179
bytes32 (uint256 (uint160 (msg .sender ))),
180
180
to,
181
- amount,
182
- decimals,
183
- DEPOSIT_TO_CONTRACT,
181
+ l2MintedAmount,
184
182
data
185
183
);
186
- _deposit (tokenAddress, amount, messageData);
184
+ _deposit (tokenAddress, amount, l2MintedAmount, messageData);
187
185
}
188
186
189
187
function sendMetadata (address tokenAddress ) external payable virtual whenNotPaused {
190
188
bytes memory messageData = abi.encodePacked (
189
+ assetIssuerId,
191
190
MessageType.METADATA,
192
- abi.encode (IERC20MetadataUpgradeable (tokenAddress).symbol (), IERC20MetadataUpgradeable (tokenAddress).name ())
191
+ abi.encode (
192
+ tokenAddress,
193
+ uint256 (0 ), // token_id = 0 for all erc20 deposits
194
+ IERC20MetadataUpgradeable (tokenAddress).symbol (),
195
+ IERC20MetadataUpgradeable (tokenAddress).name ()
196
+ )
193
197
);
194
198
sendMessage (CommonPredicates.CONTRACT_MESSAGE_PREDICATE, messageData);
195
199
}
196
200
197
201
/// @notice Deposits the given tokens to an account or contract on Fuel
198
202
/// @param tokenAddress Address of the token being transferred to Fuel
199
- /// @param amount Amount of tokens to deposit
203
+ /// @param amount tokens that have been deposited
204
+ /// @param l2MintedAmount tokens that will be minted on L2
200
205
/// @param messageData The data of the message to send for deposit
201
- function _deposit (address tokenAddress , uint256 amount , bytes memory messageData ) internal virtual {
206
+ function _deposit (
207
+ address tokenAddress ,
208
+ uint256 amount ,
209
+ uint256 l2MintedAmount ,
210
+ bytes memory messageData
211
+ ) internal virtual {
202
212
////////////
203
213
// Checks //
204
214
////////////
205
- if (amount == 0 ) revert CannotDepositZero ();
206
- if (amount > uint256 (type (uint64 ).max)) revert CannotDepositZero ();
215
+ if (l2MintedAmount == 0 ) revert CannotDepositZero ();
216
+ if (l2MintedAmount > uint256 (type (uint64 ).max)) revert CannotDepositZero ();
207
217
208
218
/////////////
209
219
// Effects //
210
220
/////////////
211
- uint256 updatedDeposits = _deposits[tokenAddress] + amount ;
221
+ uint256 updatedDeposits = _deposits[tokenAddress] + l2MintedAmount ;
212
222
if (updatedDeposits > type (uint64 ).max) revert BridgeFull ();
213
223
214
224
if (whitelistRequired && updatedDeposits > _depositLimits[tokenAddress]) {
@@ -274,7 +284,7 @@ contract FuelERC20GatewayV4 is
274
284
return uint8 (decimals);
275
285
}
276
286
277
- function _adjustDecimals (uint8 tokenDecimals , uint256 amount ) internal virtual returns (uint256 ) {
287
+ function _adjustDepositDecimals (uint8 tokenDecimals , uint256 amount ) internal pure virtual returns (uint256 ) {
278
288
// Most common case: less than 9 decimals (USDT, USDC, WBTC)
279
289
if (tokenDecimals < 9 ) {
280
290
return amount * (10 ** (9 - tokenDecimals));
@@ -295,6 +305,28 @@ contract FuelERC20GatewayV4 is
295
305
return amount;
296
306
}
297
307
308
+ function _adjustWithdrawalDecimals (uint8 tokenDecimals , uint256 amount ) internal pure virtual returns (uint256 ) {
309
+ unchecked {
310
+ if (tokenDecimals < 9 ) {
311
+ // Subject to precision losses (dust) in L2
312
+ // Economic losses due to this are estimated to be less
313
+ // than other evaluated alternatives, such as:
314
+ // - bouncing the deposit back to L2. E.g., in order to lose in the order of 0.01 USD
315
+ // BTC price should be sitting at 500k USDBTC
316
+ // - storing decimals in L2
317
+ return divByNonZero (amount, 10 ** (9 - tokenDecimals));
318
+ }
319
+ }
320
+
321
+ if (tokenDecimals > 9 ) {
322
+ uint256 precision = 10 ** (tokenDecimals - 9 );
323
+ return amount * precision;
324
+ }
325
+
326
+ return amount;
327
+ }
328
+
329
+ /// @dev gas efficient division. Must be used with care, `_div` must be non zero
298
330
function divByNonZero (uint256 _num , uint256 _div ) internal pure returns (uint256 result ) {
299
331
assembly {
300
332
result := div (_num, _div)
0 commit comments