-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/baseline conflicts #52
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,17 +6,10 @@ import { ERC20 } from "@solady/tokens/ERC20.sol"; | |
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; | ||
import { ISemver } from "src/universal/interfaces/ISemver.sol"; | ||
import { Predeploys } from "src/libraries/Predeploys.sol"; | ||
import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; | ||
import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; | ||
import { ERC165 } from "@openzeppelin/contracts-v5/utils/introspection/ERC165.sol"; | ||
|
||
/// @notice Thrown when attempting to relay a message and the function caller (msg.sender) is not | ||
/// L2ToL2CrossDomainMessenger. | ||
error CallerNotL2ToL2CrossDomainMessenger(); | ||
|
||
/// @notice Thrown when attempting to relay a message and the cross domain message sender is not this | ||
/// OptimismSuperchainERC20. | ||
error InvalidCrossDomainSender(); | ||
|
||
/// @notice Thrown when attempting to mint or burn tokens and the function caller is not the StandardBridge. | ||
error OnlyBridge(); | ||
|
||
|
@@ -31,10 +24,13 @@ error ZeroAddress(); | |
/// token, turning it fungible and interoperable across the superchain. Likewise, it also enables the inverse | ||
/// conversion path. | ||
/// Moreover, it builds on top of the L2ToL2CrossDomainMessenger for both replay protection and domain binding. | ||
contract OptimismSuperchainERC20 is IOptimismSuperchainERC20Extension, ERC20, ISemver, Initializable, ERC165 { | ||
/// @notice Address of the L2ToL2CrossDomainMessenger Predeploy. | ||
address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; | ||
|
||
contract OptimismSuperchainERC20 is | ||
IOptimismSuperchainERC20Extension, | ||
SuperchainERC20, | ||
ISemver, | ||
Initializable, | ||
ERC165 | ||
{ | ||
/// @notice Address of the StandardBridge Predeploy. | ||
address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE; | ||
|
||
|
@@ -48,16 +44,10 @@ contract OptimismSuperchainERC20 is IOptimismSuperchainERC20Extension, ERC20, IS | |
struct OptimismSuperchainERC20Metadata { | ||
/// @notice Address of the corresponding version of this token on the remote chain. | ||
address remoteToken; | ||
Comment on lines
44
to
46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we just have a storage slot for the address instead of defining a struct for that contains only 1 field? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, that can be done at well, i actually like it better, good suggestion |
||
/// @notice Name of the token | ||
string name; | ||
/// @notice Symbol of the token | ||
string symbol; | ||
/// @notice Decimals of the token | ||
uint8 decimals; | ||
} | ||
|
||
/// @notice Returns the storage for the OptimismSuperchainERC20Metadata. | ||
function _getMetadataStorage() private pure returns (OptimismSuperchainERC20Metadata storage _storage) { | ||
function _getStorage() private pure returns (OptimismSuperchainERC20Metadata storage _storage) { | ||
assembly { | ||
_storage.slot := OPTIMISM_SUPERCHAIN_ERC20_METADATA_SLOT | ||
} | ||
|
@@ -92,11 +82,10 @@ contract OptimismSuperchainERC20 is IOptimismSuperchainERC20Extension, ERC20, IS | |
external | ||
initializer | ||
{ | ||
OptimismSuperchainERC20Metadata storage _storage = _getMetadataStorage(); | ||
_setMetadataStorage(_name, _symbol, _decimals); | ||
|
||
OptimismSuperchainERC20Metadata storage _storage = _getStorage(); | ||
_storage.remoteToken = _remoteToken; | ||
_storage.name = _name; | ||
_storage.symbol = _symbol; | ||
_storage.decimals = _decimals; | ||
} | ||
|
||
/// @notice Allows the L2StandardBridge to mint tokens. | ||
|
@@ -121,64 +110,9 @@ contract OptimismSuperchainERC20 is IOptimismSuperchainERC20Extension, ERC20, IS | |
emit Burn(_from, _amount); | ||
} | ||
|
||
/// @notice Sends tokens to some target address on another chain. | ||
/// @param _to Address to send tokens to. | ||
/// @param _amount Amount of tokens to send. | ||
/// @param _chainId Chain ID of the destination chain. | ||
function sendERC20(address _to, uint256 _amount, uint256 _chainId) external { | ||
if (_to == address(0)) revert ZeroAddress(); | ||
|
||
_burn(msg.sender, _amount); | ||
|
||
bytes memory _message = abi.encodeCall(this.relayERC20, (msg.sender, _to, _amount)); | ||
IL2ToL2CrossDomainMessenger(MESSENGER).sendMessage(_chainId, address(this), _message); | ||
|
||
emit SendERC20(msg.sender, _to, _amount, _chainId); | ||
} | ||
|
||
/// @notice Relays tokens received from another chain. | ||
/// @param _from Address of the msg.sender of sendERC20 on the source chain. | ||
/// @param _to Address to relay tokens to. | ||
/// @param _amount Amount of tokens to relay. | ||
function relayERC20(address _from, address _to, uint256 _amount) external { | ||
if (_to == address(0)) revert ZeroAddress(); | ||
|
||
if (msg.sender != MESSENGER) revert CallerNotL2ToL2CrossDomainMessenger(); | ||
|
||
if (IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageSender() != address(this)) { | ||
revert InvalidCrossDomainSender(); | ||
} | ||
|
||
uint256 source = IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageSource(); | ||
|
||
_mint(_to, _amount); | ||
|
||
emit RelayERC20(_from, _to, _amount, source); | ||
} | ||
|
||
/// @notice Returns the address of the corresponding version of this token on the remote chain. | ||
function remoteToken() public view override returns (address) { | ||
return _getMetadataStorage().remoteToken; | ||
} | ||
|
||
/// @notice Returns the name of the token. | ||
function name() public view virtual override returns (string memory) { | ||
return _getMetadataStorage().name; | ||
} | ||
|
||
/// @notice Returns the symbol of the token. | ||
function symbol() public view virtual override returns (string memory) { | ||
return _getMetadataStorage().symbol; | ||
} | ||
|
||
/// @notice Returns the number of decimals used to get its user representation. | ||
/// For example, if `decimals` equals `2`, a balance of `505` tokens should | ||
/// be displayed to a user as `5.05` (`505 / 10 ** 2`). | ||
/// NOTE: This information is only used for _display_ purposes: it in | ||
/// no way affects any of the arithmetic of the contract, including | ||
/// {IERC20-balanceOf} and {IERC20-transfer}. | ||
function decimals() public view override returns (uint8) { | ||
return _getMetadataStorage().decimals; | ||
return _getStorage().remoteToken; | ||
} | ||
|
||
/// @notice ERC165 interface check function. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import { ISuperchainERC20Extensions } from "src/L2/interfaces/ISuperchainERC20.sol"; | ||
import { ERC20 } from "@solady/tokens/ERC20.sol"; | ||
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; | ||
import { Predeploys } from "src/libraries/Predeploys.sol"; | ||
|
||
/// @notice Thrown when attempting to relay a message and the function caller (msg.sender) is not | ||
/// L2ToL2CrossDomainMessenger. | ||
error CallerNotL2ToL2CrossDomainMessenger(); | ||
|
||
/// @notice Thrown when attempting to relay a message and the cross domain message sender is not this SuperchainERC20. | ||
error InvalidCrossDomainSender(); | ||
|
||
/// @notice Thrown when attempting to mint or burn tokens and the account is the zero address. | ||
error ZeroAddress(); | ||
|
||
/// @title SuperchainERC20 | ||
/// @notice SuperchainERC20 is a standard extension of the base ERC20 token contract that unifies ERC20 token | ||
/// bridging to make it fungible across the Superchain. It builds on top of the L2ToL2CrossDomainMessenger for | ||
/// both replay protection and domain binding. | ||
abstract contract SuperchainERC20 is ISuperchainERC20Extensions, ERC20 { | ||
/// @notice Address of the L2ToL2CrossDomainMessenger Predeploy. | ||
address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; | ||
|
||
/// @notice Storage slot that the SuperchainERC20Metadata struct is stored at. | ||
/// keccak256(abi.encode(uint256(keccak256("superchainERC20.metadata")) - 1)) & ~bytes32(uint256(0xff)); | ||
bytes32 internal constant SUPERCHAIN_ERC20_METADATA_SLOT = | ||
0xd17d6ca6a839692cc315581e57453e7dbbeba09485cfb8c48daa1d1181778600; | ||
|
||
/// @notice Storage struct for the SuperchainERC20 metadata. | ||
/// @custom:storage-location erc7201:superchainERC20.metadata | ||
struct SuperchainERC20Metadata { | ||
/// @notice Name of the token | ||
string name; | ||
/// @notice Symbol of the token | ||
string symbol; | ||
/// @notice Decimals of the token | ||
uint8 decimals; | ||
} | ||
|
||
/// @notice Returns the storage for the SuperchainERC20Metadata. | ||
function _getMetadataStorage() private pure returns (SuperchainERC20Metadata storage _storage) { | ||
assembly { | ||
_storage.slot := SUPERCHAIN_ERC20_METADATA_SLOT | ||
} | ||
} | ||
|
||
/// @notice Sets the storage for the SuperchainERC20Metadata. | ||
/// @param _name Name of the token. | ||
/// @param _symbol Symbol of the token. | ||
/// @param _decimals Decimals of the token. | ||
function _setMetadataStorage(string memory _name, string memory _symbol, uint8 _decimals) internal { | ||
SuperchainERC20Metadata storage _storage = _getMetadataStorage(); | ||
_storage.name = _name; | ||
_storage.symbol = _symbol; | ||
_storage.decimals = _decimals; | ||
} | ||
|
||
/// @notice Sends tokens to some target address on another chain. | ||
/// @param _to Address to send tokens to. | ||
/// @param _amount Amount of tokens to send. | ||
/// @param _chainId Chain ID of the destination chain. | ||
function sendERC20(address _to, uint256 _amount, uint256 _chainId) external virtual { | ||
if (_to == address(0)) revert ZeroAddress(); | ||
|
||
_burn(msg.sender, _amount); | ||
|
||
bytes memory _message = abi.encodeCall(this.relayERC20, (msg.sender, _to, _amount)); | ||
IL2ToL2CrossDomainMessenger(MESSENGER).sendMessage(_chainId, address(this), _message); | ||
|
||
emit SendERC20(msg.sender, _to, _amount, _chainId); | ||
} | ||
|
||
/// @notice Relays tokens received from another chain. | ||
/// @param _from Address of the msg.sender of sendERC20 on the source chain. | ||
/// @param _to Address to relay tokens to. | ||
/// @param _amount Amount of tokens to relay. | ||
function relayERC20(address _from, address _to, uint256 _amount) external virtual { | ||
if (_to == address(0)) revert ZeroAddress(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't this check redundant? the |
||
|
||
if (msg.sender != MESSENGER) revert CallerNotL2ToL2CrossDomainMessenger(); | ||
|
||
if (IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageSender() != address(this)) { | ||
revert InvalidCrossDomainSender(); | ||
} | ||
|
||
uint256 source = IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageSource(); | ||
|
||
_mint(_to, _amount); | ||
|
||
emit RelayERC20(_from, _to, _amount, source); | ||
} | ||
|
||
/// @notice Returns the name of the token. | ||
function name() public view virtual override returns (string memory) { | ||
return _getMetadataStorage().name; | ||
} | ||
|
||
/// @notice Returns the symbol of the token. | ||
function symbol() public view virtual override returns (string memory) { | ||
return _getMetadataStorage().symbol; | ||
} | ||
|
||
/// @notice Returns the number of decimals used to get its user representation. | ||
/// For example, if `decimals` equals `2`, a balance of `505` tokens should | ||
/// be displayed to a user as `5.05` (`505 / 10 ** 2`). | ||
/// NOTE: This information is only used for _display_ purposes: it in | ||
/// no way affects any of the arithmetic of the contract, including | ||
/// {IERC20-balanceOf} and {IERC20-transfer}. | ||
function decimals() public view virtual override returns (uint8) { | ||
return _getMetadataStorage().decimals; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,13 +14,17 @@ import { IERC165 } from "@openzeppelin/contracts-v5/utils/introspection/IERC165. | |
|
||
// Target contract | ||
import { | ||
OptimismSuperchainERC20, | ||
IOptimismSuperchainERC20Extension, | ||
OptimismSuperchainERC20, IOptimismSuperchainERC20Extension, OnlyBridge | ||
} from "src/L2/OptimismSuperchainERC20.sol"; | ||
|
||
import { | ||
CallerNotL2ToL2CrossDomainMessenger, | ||
InvalidCrossDomainSender, | ||
OnlyBridge, | ||
SuperchainERC20, | ||
ISuperchainERC20Extensions, | ||
ZeroAddress | ||
} from "src/L2/OptimismSuperchainERC20.sol"; | ||
} from "src/L2/SuperchainERC20.sol"; | ||
|
||
import { ISuperchainERC20Extensions } from "src/L2/interfaces/ISuperchainERC20.sol"; | ||
|
||
/// @title OptimismSuperchainERC20Test | ||
|
@@ -127,11 +131,11 @@ contract OptimismSuperchainERC20Test is Test { | |
uint256 _toBalanceBefore = superchainERC20.balanceOf(_to); | ||
|
||
// Look for the emit of the `Transfer` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's the same as if they were there, it's shorthand way of writing it |
||
emit IERC20.Transfer(ZERO_ADDRESS, _to, _amount); | ||
|
||
// Look for the emit of the `Mint` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit IOptimismSuperchainERC20Extension.Mint(_to, _amount); | ||
|
||
// Call the `mint` function with the bridge caller | ||
|
@@ -180,11 +184,11 @@ contract OptimismSuperchainERC20Test is Test { | |
uint256 _fromBalanceBefore = superchainERC20.balanceOf(_from); | ||
|
||
// Look for the emit of the `Transfer` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit IERC20.Transfer(_from, ZERO_ADDRESS, _amount); | ||
|
||
// Look for the emit of the `Burn` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit IOptimismSuperchainERC20Extension.Burn(_from, _amount); | ||
|
||
// Call the `burn` function with the bridge caller | ||
|
@@ -222,11 +226,11 @@ contract OptimismSuperchainERC20Test is Test { | |
uint256 _senderBalanceBefore = superchainERC20.balanceOf(_sender); | ||
|
||
// Look for the emit of the `Transfer` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit IERC20.Transfer(_sender, ZERO_ADDRESS, _amount); | ||
|
||
// Look for the emit of the `SendERC20` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit ISuperchainERC20Extensions.SendERC20(_sender, _to, _amount, _chainId); | ||
|
||
// Mock the call over the `sendMessage` function and expect it to be called properly | ||
|
@@ -330,11 +334,11 @@ contract OptimismSuperchainERC20Test is Test { | |
uint256 _toBalanceBefore = superchainERC20.balanceOf(_to); | ||
|
||
// Look for the emit of the `Transfer` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit IERC20.Transfer(ZERO_ADDRESS, _to, _amount); | ||
|
||
// Look for the emit of the `RelayERC20` event | ||
vm.expectEmit(true, true, true, true, address(superchainERC20)); | ||
vm.expectEmit(address(superchainERC20)); | ||
emit ISuperchainERC20Extensions.RelayERC20(_from, _to, _amount, _source); | ||
|
||
// Call the `relayERC20` function with the messenger caller | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lines are repeated below, were them autogenerated? Are we sure we need them or could be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is autogenerated, but if this is repeated then we may have the error in both the SuperchainERC20 and the OptimismSuperchainERC20, will check and remove accordingly