-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: gateway for l2 native erc20 (#40)
- Loading branch information
Showing
10 changed files
with
1,153 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity =0.8.24; | ||
|
||
import {IL2ERC20Gateway} from "../../L2/gateways/IL2ERC20Gateway.sol"; | ||
import {IScrollERC20Upgradeable} from "../../libraries/token/IScrollERC20Upgradeable.sol"; | ||
import {IL1ScrollMessenger} from "../IL1ScrollMessenger.sol"; | ||
import {L1CustomERC20Gateway} from "./L1CustomERC20Gateway.sol"; | ||
import {L1ERC20Gateway} from "./L1ERC20Gateway.sol"; | ||
|
||
/// @title L1ReverseCustomERC20Gateway | ||
/// @notice The `L1ReverseCustomERC20Gateway` is used to deposit layer 2 native ERC20 tokens on layer 1 and | ||
/// finalize withdraw the tokens from layer 2. | ||
/// @dev The deposited tokens are transferred to this gateway and then burned. On finalizing withdraw, the corresponding | ||
/// tokens will be minted and transfer to the recipient. | ||
contract L1ReverseCustomERC20Gateway is L1CustomERC20Gateway { | ||
/********** | ||
* Errors * | ||
**********/ | ||
|
||
/// @dev Thrown when no l2 token exists. | ||
error ErrorNoCorrespondingL2Token(); | ||
|
||
/*************** | ||
* Constructor * | ||
***************/ | ||
|
||
/// @notice Constructor for `L1ReverseCustomERC20Gateway` implementation contract. | ||
/// | ||
/// @param _counterpart The address of `L2ReverseCustomERC20Gateway` contract in L2. | ||
/// @param _router The address of `L1GatewayRouter` contract in L1. | ||
/// @param _messenger The address of `L1ScrollMessenger` contract L1. | ||
constructor( | ||
address _counterpart, | ||
address _router, | ||
address _messenger | ||
) L1CustomERC20Gateway(_counterpart, _router, _messenger) { | ||
_disableInitializers(); | ||
} | ||
|
||
/********************** | ||
* Internal Functions * | ||
**********************/ | ||
|
||
/// @inheritdoc L1ERC20Gateway | ||
function _beforeFinalizeWithdrawERC20( | ||
address _l1Token, | ||
address _l2Token, | ||
address _from, | ||
address _to, | ||
uint256 _amount, | ||
bytes calldata _data | ||
) internal virtual override { | ||
super._beforeFinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data); | ||
|
||
IScrollERC20Upgradeable(_l1Token).mint(address(this), _amount); | ||
} | ||
|
||
/// @inheritdoc L1ERC20Gateway | ||
function _beforeDropMessage( | ||
address _token, | ||
address _receiver, | ||
uint256 _amount | ||
) internal virtual override { | ||
super._beforeDropMessage(_token, _receiver, _amount); | ||
|
||
IScrollERC20Upgradeable(_token).mint(address(this), _amount); | ||
} | ||
|
||
/// @inheritdoc L1ERC20Gateway | ||
function _deposit( | ||
address _token, | ||
address _to, | ||
uint256 _amount, | ||
bytes memory _data, | ||
uint256 _gasLimit | ||
) internal virtual override { | ||
super._deposit(_token, _to, _amount, _data, _gasLimit); | ||
|
||
IScrollERC20Upgradeable(_token).burn(address(this), _amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// 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 {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; | ||
|
||
import {IL1ERC20Gateway} from "../../L1/gateways/IL1ERC20Gateway.sol"; | ||
import {IL2ScrollMessenger} from "../IL2ScrollMessenger.sol"; | ||
import {IL2ERC20Gateway, L2ERC20Gateway} from "./L2ERC20Gateway.sol"; | ||
import {L2CustomERC20Gateway} from "./L2CustomERC20Gateway.sol"; | ||
|
||
/// @title L2ReverseCustomERC20Gateway | ||
/// @notice The `L2ReverseCustomERC20Gateway` is used to withdraw native ERC20 tokens on layer 2 and | ||
/// finalize deposit the tokens from layer 1. | ||
/// @dev The withdrawn ERC20 tokens are holed in this contract. On finalizing deposit, the corresponding | ||
/// token will be transferred to the recipient. | ||
contract L2ReverseCustomERC20Gateway is L2CustomERC20Gateway { | ||
using SafeERC20Upgradeable for IERC20Upgradeable; | ||
|
||
/********** | ||
* Errors * | ||
**********/ | ||
|
||
/// @dev Thrown when the message value is not zero. | ||
error ErrorNonzeroMsgValue(); | ||
|
||
/// @dev Thrown when the given l1 token address is zero. | ||
error ErrorL1TokenAddressIsZero(); | ||
|
||
/// @dev Thrown when the given l1 token address not match stored one. | ||
error ErrorL1TokenAddressMismatch(); | ||
|
||
/// @dev Thrown when no l1 token exists. | ||
error ErrorNoCorrespondingL1Token(); | ||
|
||
/// @dev Thrown when withdraw zero amount token. | ||
error ErrorWithdrawZeroAmount(); | ||
|
||
/*************** | ||
* Constructor * | ||
***************/ | ||
|
||
/// @notice Constructor for `L2ReverseCustomERC20Gateway` implementation contract. | ||
/// | ||
/// @param _counterpart The address of `L1ReverseCustomERC20Gateway` contract in L1. | ||
/// @param _router The address of `L2GatewayRouter` contract in L2. | ||
/// @param _messenger The address of `L2ScrollMessenger` contract in L2. | ||
constructor( | ||
address _counterpart, | ||
address _router, | ||
address _messenger | ||
) L2CustomERC20Gateway(_counterpart, _router, _messenger) { | ||
_disableInitializers(); | ||
} | ||
|
||
/***************************** | ||
* Public Mutating Functions * | ||
*****************************/ | ||
|
||
/// @inheritdoc IL2ERC20Gateway | ||
function finalizeDepositERC20( | ||
address _l1Token, | ||
address _l2Token, | ||
address _from, | ||
address _to, | ||
uint256 _amount, | ||
bytes calldata _data | ||
) external payable override onlyCallByCounterpart nonReentrant { | ||
if (msg.value != 0) revert ErrorNonzeroMsgValue(); | ||
if (_l1Token == address(0)) revert ErrorL1TokenAddressIsZero(); | ||
if (_l1Token != tokenMapping[_l2Token]) revert ErrorL1TokenAddressMismatch(); | ||
|
||
IERC20Upgradeable(_l2Token).safeTransfer(_to, _amount); | ||
|
||
_doCallback(_to, _data); | ||
|
||
emit FinalizeDepositERC20(_l1Token, _l2Token, _from, _to, _amount, _data); | ||
} | ||
|
||
/********************** | ||
* Internal Functions * | ||
**********************/ | ||
|
||
/// @inheritdoc L2ERC20Gateway | ||
function _withdraw( | ||
address _token, | ||
address _to, | ||
uint256 _amount, | ||
bytes memory _data, | ||
uint256 _gasLimit | ||
) internal virtual override nonReentrant { | ||
address _l1Token = tokenMapping[_token]; | ||
if (_l1Token == address(0)) revert ErrorNoCorrespondingL1Token(); | ||
if (_amount == 0) revert ErrorWithdrawZeroAmount(); | ||
|
||
// 1. Extract real sender if this call is from L2GatewayRouter. | ||
address _from = _msgSender(); | ||
if (router == _from) { | ||
(_from, _data) = abi.decode(_data, (address, bytes)); | ||
} | ||
|
||
// 2. transfer token to this contract | ||
uint256 balance = IERC20Upgradeable(_token).balanceOf(address(this)); | ||
IERC20Upgradeable(_token).safeTransferFrom(_from, address(this), _amount); | ||
_amount = IERC20Upgradeable(_token).balanceOf(address(this)) - balance; | ||
|
||
// 3. Generate message passed to L1ReverseCustomERC20Gateway. | ||
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); | ||
} | ||
} |
Oops, something went wrong.