Commit 3ad1bb3f authored by AgusDuha's avatar AgusDuha Committed by GitHub

feat: liquidity migration (#11479)

* feat: add L2 standrad bridge interop contract

* test: add L2 standard bridge interop unit tests (#13)

* test: add L2 standard bridge interop unit tests

* fix: add tests natspec

* fix: unit tests fixes

* fix: super to legacy tests failing

* fix: mock and expect mint and burn

* fix: add generic factory interface (#14)

* test: add L2 standard bridge interop unit tests

* fix: add tests natspec

* fix: add generic factory interface

* feat: modify OptimismMintableERC20Factory for convert (#17)

* test: add L2 standard bridge interop unit tests

* fix: add tests natspec

* fix: add generic factory interface

* feat: modify OptimismMintableERC20Factory for convert

* fix: use only a public function for create3

* feat: rollback interop factory, modify legacy one

* fix: delete local token return variable

* fix: PR fixes

* test: fix address assuming

* test: fix view warning

* fix: snapshots

* test: small fixes
parent 27b0befb
......@@ -114,6 +114,8 @@ abstract contract Artifacts {
return payable(Predeploys.L2_TO_L1_MESSAGE_PASSER);
} else if (digest == keccak256(bytes("L2StandardBridge"))) {
return payable(Predeploys.L2_STANDARD_BRIDGE);
} else if (digest == keccak256(bytes("L2StandardBridgeInterop"))) {
return payable(Predeploys.L2_STANDARD_BRIDGE);
} else if (digest == keccak256(bytes("L2ERC721Bridge"))) {
return payable(Predeploys.L2_ERC721_BRIDGE);
} else if (digest == keccak256(bytes("SequencerFeeWallet"))) {
......
......@@ -286,7 +286,15 @@ contract L2Genesis is Deployer {
/// @notice This predeploy is following the safety invariant #1.
function setL2StandardBridge(address payable _l1StandardBridgeProxy) public {
address impl = _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE);
address impl;
if (cfg.useInterop()) {
string memory cname = "L2StandardBridgeInterop";
impl = Predeploys.predeployToCodeNamespace(Predeploys.L2_STANDARD_BRIDGE);
console.log("Setting %s implementation at: %s", cname, impl);
vm.etch(impl, vm.getDeployedCode(string.concat(cname, ".sol:", cname)));
} else {
impl = _setImplementationCode(Predeploys.L2_STANDARD_BRIDGE);
}
L2StandardBridge(payable(impl)).initialize({ _otherBridge: L1StandardBridge(payable(address(0))) });
......
......@@ -100,8 +100,12 @@
"sourceCodeHash": "0xb0806d362ba5cc39acfb09e0606059a2b10a7c1d5bc1f86cd4561fd24f7dcada"
},
"src/L2/L2StandardBridge.sol": {
"initCodeHash": "0xb6af582e9edaae531d48559b7cd0ca5813a112361ea19b8cb46292726ad88d40",
"sourceCodeHash": "0xb4a9f333f04008f610eb55fa6ff7e610bab340d53156cb50ec65a575c9576b0e"
"initCodeHash": "0xfbfc7bd101022024b94114c26128c6028c25dec07e8d40fdcfdb180c0ba6ddee",
"sourceCodeHash": "0xb1a5fb22b124e8fa8eb5bae4b8e0770abbdbebe32be00480317cf4aaada28ed3"
},
"src/L2/L2StandardBridgeInterop.sol": {
"initCodeHash": "0xd7f85eef12b60211104cddbd86d9f458cd31a0ba74f382404799bcf86ef003ba",
"sourceCodeHash": "0x00f415380689a5ee1762e93b032d5c3de2fcddb36b0a068cb5616f7e8001ddc0"
},
"src/L2/L2ToL1MessagePasser.sol": {
"initCodeHash": "0x08bbede75cd6dfd076903b8f04d24f82fa7881576c135825098778632e37eebc",
......@@ -200,8 +204,8 @@
"sourceCodeHash": "0x52737b23e99bf79dd2c23196b3298e80aa41f740efc6adc7916e696833eb546a"
},
"src/universal/OptimismMintableERC20Factory.sol": {
"initCodeHash": "0xf6f522681e7ae940cb778db68004f122b25194296a65bba7ad1d792bd593c4a6",
"sourceCodeHash": "0x9b8c73ea139f13028008eedef53a6b07576cd6b08979574e6dde3192656e9268"
"initCodeHash": "0x29a49fc387ad84f82199947e49a0d1960902f63492d974c26afd72372e748648",
"sourceCodeHash": "0xbc6cf74368c244bdea8ed64c501129d0b6d41db421dc91d1de051f7b505a4977"
},
"src/universal/OptimismMintableERC721.sol": {
"initCodeHash": "0xb400f430acf4d65bee9635e4935a6e1e3a0284fc50aea40ad8b7818dc826f31c",
......
......@@ -311,7 +311,7 @@
"type": "string"
}
],
"stateMutability": "view",
"stateMutability": "pure",
"type": "function"
},
{
......
......@@ -122,6 +122,25 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "deployments",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
......
This source diff could not be displayed because it is too large. You can view the blob instead.
[
{
"bytes": "1",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "30",
"label": "spacer_0_2_30",
"offset": 2,
"slot": "0",
"type": "bytes30"
},
{
"bytes": "20",
"label": "spacer_1_0_20",
"offset": 0,
"slot": "1",
"type": "address"
},
{
"bytes": "32",
"label": "deposits",
"offset": 0,
"slot": "2",
"type": "mapping(address => mapping(address => uint256))"
},
{
"bytes": "20",
"label": "messenger",
"offset": 0,
"slot": "3",
"type": "contract CrossDomainMessenger"
},
{
"bytes": "20",
"label": "otherBridge",
"offset": 0,
"slot": "4",
"type": "contract StandardBridge"
},
{
"bytes": "1440",
"label": "__gap",
"offset": 0,
"slot": "5",
"type": "uint256[45]"
}
]
\ No newline at end of file
......@@ -28,10 +28,17 @@
"type": "address"
},
{
"bytes": "1568",
"label": "__gap",
"bytes": "32",
"label": "deployments",
"offset": 0,
"slot": "2",
"type": "uint256[49]"
"type": "mapping(address => address)"
},
{
"bytes": "1536",
"label": "__gap",
"offset": 0,
"slot": "3",
"type": "uint256[48]"
}
]
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title IOptimismERC20Factory
/// @notice Generic interface for IOptimismMintableERC20Factory and ISuperchainERC20Factory. Used to
/// determine if a ERC20 contract is deployed by a factory.
interface IOptimismERC20Factory {
/// @notice Checks if a ERC20 token is deployed by the factory.
/// @param _localToken The address of the ERC20 token to check the deployment.
/// @return _remoteToken The address of the remote token if it is deployed or `address(0)` if not.
function deployments(address _localToken) external view returns (address _remoteToken);
}
......@@ -52,8 +52,11 @@ contract L2StandardBridge is StandardBridge, ISemver {
bytes extraData
);
/// @custom:semver 1.10.0
string public constant version = "1.10.0";
/// @notice Semantic version.
/// @custom:semver 1.11.0
function version() public pure virtual returns (string memory) {
return "1.11.0";
}
/// @notice Constructs the L2StandardBridge contract.
constructor() StandardBridge() {
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Predeploys } from "src/libraries/Predeploys.sol";
import { L2StandardBridge } from "src/L2/L2StandardBridge.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IOptimismERC20Factory } from "src/L2/IOptimismERC20Factory.sol";
/// @notice Thrown when the decimals of the tokens are not the same.
error InvalidDecimals();
/// @notice Thrown when the legacy address is not found in the OptimismMintableERC20Factory.
error InvalidLegacyERC20Address();
/// @notice Thrown when the SuperchainERC20 address is not found in the SuperchainERC20Factory.
error InvalidSuperchainERC20Address();
/// @notice Thrown when the remote addresses of the tokens are not the same.
error InvalidTokenPair();
/// TODO: Define a better naming convention for this interface.
/// @notice Interface for minting and burning tokens in the L2StandardBridge.
/// Used for StandardL2ERC20, OptimismMintableERC20 and OptimismSuperchainERC20.
interface MintableAndBurnable is IERC20 {
function mint(address, uint256) external;
function burn(address, uint256) external;
}
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000010
/// @title L2StandardBridgeInterop
/// @notice The L2StandardBridgeInterop is an extension of the L2StandardBridge that allows for
/// the conversion of tokens between legacy tokens (OptimismMintableERC20 or StandardL2ERC20)
/// and SuperchainERC20 tokens.
contract L2StandardBridgeInterop is L2StandardBridge {
/// @notice Emitted when a conversion is made.
/// @param from The token being converted from.
/// @param to The token being converted to.
/// @param caller The caller of the conversion.
/// @param amount The amount of tokens being converted.
event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount);
/// @notice Semantic version.
/// @custom:semver +interop
function version() public pure override returns (string memory) {
return string.concat(super.version(), "+interop");
}
/// @notice Converts `amount` of `from` token to `to` token.
/// @param _from The token being converted from.
/// @param _to The token being converted to.
/// @param _amount The amount of tokens being converted.
function convert(address _from, address _to, uint256 _amount) external {
_validatePair(_from, _to);
MintableAndBurnable(_from).burn(msg.sender, _amount);
MintableAndBurnable(_to).mint(msg.sender, _amount);
emit Converted(_from, _to, msg.sender, _amount);
}
/// @notice Validates the pair of tokens.
/// @param _from The token being converted from.
/// @param _to The token being converted to.
function _validatePair(address _from, address _to) internal view {
// 1. Decimals check
if (IERC20Metadata(_from).decimals() != IERC20Metadata(_to).decimals()) revert InvalidDecimals();
// Order tokens for factory validation
if (_isOptimismMintableERC20(_from)) {
_validateFactories(_from, _to);
} else {
_validateFactories(_to, _from);
}
}
/// @notice Validates that the tokens are deployed by the correct factory.
/// @param _legacyAddr The legacy token address (OptimismMintableERC20 or StandardL2ERC20).
/// @param _superAddr The SuperchainERC20 address.
function _validateFactories(address _legacyAddr, address _superAddr) internal view {
// 2. Valid legacy check
address _legacyRemoteToken =
IOptimismERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY).deployments(_legacyAddr);
if (_legacyRemoteToken == address(0)) revert InvalidLegacyERC20Address();
// 3. Valid SuperchainERC20 check
address _superRemoteToken =
IOptimismERC20Factory(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY).deployments(_superAddr);
if (_superRemoteToken == address(0)) revert InvalidSuperchainERC20Address();
// 4. Same remote address check
if (_legacyRemoteToken != _superRemoteToken) revert InvalidTokenPair();
}
}
......@@ -95,6 +95,10 @@ library Predeploys {
/// @notice Address of the ETHLiquidity predeploy.
address internal constant ETH_LIQUIDITY = 0x4200000000000000000000000000000000000025;
/// TODO: Add correct predeploy address for OptimismSuperchainERC20Factory
/// @notice Address of the OptimismSuperchainERC20Factory predeploy.
address internal constant OPTIMISM_SUPERCHAIN_ERC20_FACTORY = 0x4200000000000000000000000000000000000026;
/// @notice Returns the name of the predeploy at the given address.
function getName(address _addr) internal pure returns (string memory out_) {
require(isPredeployNamespace(_addr), "Predeploys: address must be a predeploy");
......@@ -123,6 +127,7 @@ library Predeploys {
if (_addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER) return "L2ToL2CrossDomainMessenger";
if (_addr == SUPERCHAIN_WETH) return "SuperchainWETH";
if (_addr == ETH_LIQUIDITY) return "ETHLiquidity";
if (_addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY) return "OptimismSuperchainERC20Factory";
revert("Predeploys: unnamed predeploy");
}
......@@ -140,7 +145,8 @@ library Predeploys {
|| _addr == OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == PROXY_ADMIN || _addr == BASE_FEE_VAULT
|| _addr == L1_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS || _addr == GOVERNANCE_TOKEN
|| (_useInterop && _addr == CROSS_L2_INBOX) || (_useInterop && _addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER)
|| (_useInterop && _addr == SUPERCHAIN_WETH) || (_useInterop && _addr == ETH_LIQUIDITY);
|| (_useInterop && _addr == SUPERCHAIN_WETH) || (_useInterop && _addr == ETH_LIQUIDITY)
|| (_useInterop && _addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY);
}
function isPredeployNamespace(address _addr) internal pure returns (bool) {
......
......@@ -4,6 +4,7 @@ pragma solidity 0.8.15;
import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol";
import { ISemver } from "src/universal/ISemver.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { IOptimismERC20Factory } from "src/L2/IOptimismERC20Factory.sol";
/// @custom:proxied
/// @custom:predeployed 0x4200000000000000000000000000000000000012
......@@ -12,7 +13,7 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable
/// contracts on the network it's deployed to. Simplifies the deployment process for users
/// who may be less familiar with deploying smart contracts. Designed to be backwards
/// compatible with the older StandardL2ERC20Factory contract.
contract OptimismMintableERC20Factory is ISemver, Initializable {
contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20Factory {
/// @custom:spacer OptimismMintableERC20Factory's initializer slot spacing
/// @notice Spacer to avoid packing into the initializer slot
bytes30 private spacer_0_2_30;
......@@ -21,10 +22,14 @@ contract OptimismMintableERC20Factory is ISemver, Initializable {
/// @custom:network-specific
address public bridge;
/// @notice Mapping of local token address to remote token address.
/// This is used to keep track of the token deployments.
mapping(address => address) public deployments;
/// @notice Reserve extra slots in the storage layout for future upgrades.
/// A gap size of 49 was chosen here, so that the first slot used in a child contract
/// would be 1 plus a multiple of 50.
uint256[49] private __gap;
/// A gap size of 48 was chosen here, so that the first slot used in a child contract
/// would be a multiple of 50.
uint256[48] private __gap;
/// @custom:legacy
/// @notice Emitted whenever a new OptimismMintableERC20 is created. Legacy version of the newer
......@@ -43,8 +48,8 @@ contract OptimismMintableERC20Factory is ISemver, Initializable {
/// the OptimismMintableERC20 token contract since this contract
/// is responsible for deploying OptimismMintableERC20 contracts.
/// @notice Semantic version.
/// @custom:semver 1.9.0
string public constant version = "1.9.0";
/// @custom:semver 1.10.0
string public constant version = "1.10.0";
/// @notice Constructs the OptimismMintableERC20Factory contract.
constructor() {
......@@ -117,9 +122,12 @@ contract OptimismMintableERC20Factory is ISemver, Initializable {
require(_remoteToken != address(0), "OptimismMintableERC20Factory: must provide remote token address");
bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol, _decimals));
address localToken =
address(new OptimismMintableERC20{ salt: salt }(bridge, _remoteToken, _name, _symbol, _decimals));
deployments[localToken] = _remoteToken;
// Emit the old event too for legacy support.
emit StandardL2TokenCreated(_remoteToken, localToken);
......
......@@ -150,8 +150,8 @@ contract L2GenesisTest is Test {
// 2 predeploys do not have proxies
assertEq(getCodeCount(_path, "Proxy.sol:Proxy"), Predeploys.PREDEPLOY_COUNT - 2);
// 21 proxies have the implementation set if useInterop is true and 17 if useInterop is false
assertEq(getPredeployCountWithSlotSet(_path, Constants.PROXY_IMPLEMENTATION_ADDRESS), _useInterop ? 21 : 17);
// 22 proxies have the implementation set if useInterop is true and 17 if useInterop is false
assertEq(getPredeployCountWithSlotSet(_path, Constants.PROXY_IMPLEMENTATION_ADDRESS), _useInterop ? 22 : 17);
// All proxies except 2 have the proxy 1967 admin slot set to the proxy admin
assertEq(
......
......@@ -28,7 +28,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99;
address internal constant mipsAddress = 0x444e09fe6D839273316a87002aB0EFBeA6fe7806;
address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567;
address internal constant optimismMintableERC20FactoryAddress = 0x7c06d3a0a2f45e39E1798afbd9C3330971332a4F;
address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131;
address internal constant optimismPortal2Address = 0xae5DadFc48928543f706a9E6Ce25c682aaD2b63b;
......@@ -580,7 +580,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000008";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000039aea2dd53f2d01c15877acc2791af6bdd7ad567";
value = hex"0000000000000000000000007c06d3a0a2f45e39e1798afbd9c3330971332a4f";
vm.store(optimismMintableERC20FactoryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......
......@@ -28,7 +28,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x39Af23E00F1e662025aA01b0cEdA19542B78DF99;
address internal constant mipsAddress = 0x444e09fe6D839273316a87002aB0EFBeA6fe7806;
address internal constant optimismMintableERC20FactoryAddress = 0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567;
address internal constant optimismMintableERC20FactoryAddress = 0x7c06d3a0a2f45e39E1798afbd9C3330971332a4F;
address internal constant optimismMintableERC20FactoryProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant optimismPortalAddress = 0xbdD90485FCbcac869D5b5752179815a3103d8131;
address internal constant optimismPortal2Address = 0xae5DadFc48928543f706a9E6Ce25c682aaD2b63b;
......@@ -583,7 +583,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000008";
vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000039aea2dd53f2d01c15877acc2791af6bdd7ad567";
value = hex"0000000000000000000000007c06d3a0a2f45e39e1798afbd9c3330971332a4f";
vm.store(optimismMintableERC20FactoryProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001";
......
......@@ -5,7 +5,7 @@ import { console2 as console } from "forge-std/console2.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol";
import { L2StandardBridge } from "src/L2/L2StandardBridge.sol";
import { L2StandardBridgeInterop } from "src/L2/L2StandardBridgeInterop.sol";
import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol";
import { L2ERC721Bridge } from "src/L2/L2ERC721Bridge.sol";
import { BaseFeeVault } from "src/L2/BaseFeeVault.sol";
......@@ -83,7 +83,7 @@ contract Setup {
L2CrossDomainMessenger l2CrossDomainMessenger =
L2CrossDomainMessenger(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER));
L2StandardBridge l2StandardBridge = L2StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE));
L2StandardBridgeInterop l2StandardBridge = L2StandardBridgeInterop(payable(Predeploys.L2_STANDARD_BRIDGE));
L2ToL1MessagePasser l2ToL1MessagePasser = L2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER));
OptimismMintableERC20Factory l2OptimismMintableERC20Factory =
OptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY);
......
......@@ -17,19 +17,20 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken);
event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer);
/// @dev Tests that the constructor is initialized correctly.
/// @notice Tests that the constructor is initialized correctly.
function test_constructor_succeeds() external {
OptimismMintableERC20Factory impl = new OptimismMintableERC20Factory();
assertEq(address(impl.BRIDGE()), address(0));
assertEq(address(impl.bridge()), address(0));
}
/// @dev Tests that the proxy is initialized correctly.
/// @notice Tests that the proxy is initialized correctly.
function test_initialize_succeeds() external view {
assertEq(address(l1OptimismMintableERC20Factory.BRIDGE()), address(l1StandardBridge));
assertEq(address(l1OptimismMintableERC20Factory.bridge()), address(l1StandardBridge));
}
/// @notice Tests that the upgrade is successful.
function test_upgrading_succeeds() external {
Proxy proxy = Proxy(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy"));
// Check an unused slot before upgrading.
......@@ -49,60 +50,156 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
assertEq(slot21Expected, slot21After);
}
function test_createStandardL2Token_succeeds() external {
address remote = address(4);
/// @notice Test that calling `createStandardL2Token` with valid parameters succeeds.
function test_createStandardL2Token_succeeds(
address _caller,
address _remoteToken,
string memory _name,
string memory _symbol
)
external
{
// Assume
vm.assume(_remoteToken != address(0));
// Arrange
// Defaults to 18 decimals
address local = calculateTokenAddress(remote, "Beep", "BOOP", 18);
address local = _calculateTokenAddress(_remoteToken, _name, _symbol, 18);
vm.expectEmit(true, true, true, true);
emit StandardL2TokenCreated(remote, local);
vm.expectEmit(address(l2OptimismMintableERC20Factory));
emit StandardL2TokenCreated(_remoteToken, local);
vm.expectEmit(true, true, true, true);
emit OptimismMintableERC20Created(local, remote, alice);
vm.expectEmit(address(l2OptimismMintableERC20Factory));
emit OptimismMintableERC20Created(local, _remoteToken, _caller);
vm.prank(alice);
address addr = l2OptimismMintableERC20Factory.createStandardL2Token(remote, "Beep", "BOOP");
// Act
vm.prank(_caller);
address addr = l2OptimismMintableERC20Factory.createStandardL2Token(_remoteToken, _name, _symbol);
// Assert
assertTrue(addr == local);
assertTrue(OptimismMintableERC20(local).decimals() == 18);
assertEq(l2OptimismMintableERC20Factory.deployments(local), _remoteToken);
}
function test_createStandardL2TokenWithDecimals_succeeds() external {
address remote = address(4);
address local = calculateTokenAddress(remote, "Beep", "BOOP", 6);
/// @notice Test that calling `createOptimismMintableERC20WithDecimals` with valid parameters succeeds.
function test_createStandardL2TokenWithDecimals_succeeds(
address _caller,
address _remoteToken,
string memory _name,
string memory _symbol,
uint8 _decimals
)
external
{
// Assume
vm.assume(_remoteToken != address(0));
// Arrange
address local = _calculateTokenAddress(_remoteToken, _name, _symbol, _decimals);
vm.expectEmit(true, true, true, true);
emit StandardL2TokenCreated(remote, local);
vm.expectEmit(address(l2OptimismMintableERC20Factory));
emit StandardL2TokenCreated(_remoteToken, local);
vm.expectEmit(true, true, true, true);
emit OptimismMintableERC20Created(local, remote, alice);
vm.expectEmit(address(l2OptimismMintableERC20Factory));
emit OptimismMintableERC20Created(local, _remoteToken, _caller);
vm.prank(alice);
address addr = l2OptimismMintableERC20Factory.createOptimismMintableERC20WithDecimals(remote, "Beep", "BOOP", 6);
// Act
vm.prank(_caller);
address addr = l2OptimismMintableERC20Factory.createOptimismMintableERC20WithDecimals(
_remoteToken, _name, _symbol, _decimals
);
// Assert
assertTrue(addr == local);
assertTrue(OptimismMintableERC20(local).decimals() == _decimals);
assertEq(l2OptimismMintableERC20Factory.deployments(local), _remoteToken);
}
/// @notice Test that calling `createStandardL2Token` with the same parameters twice reverts.
function test_createStandardL2Token_sameTwice_reverts(
address _caller,
address _remoteToken,
string memory _name,
string memory _symbol
)
external
{
// Assume
vm.assume(_remoteToken != address(0));
vm.prank(_caller);
l2OptimismMintableERC20Factory.createStandardL2Token(_remoteToken, _name, _symbol);
// Arrange
vm.expectRevert(bytes(""));
assertTrue(OptimismMintableERC20(local).decimals() == 6);
// Act
vm.prank(_caller);
l2OptimismMintableERC20Factory.createStandardL2Token(_remoteToken, _name, _symbol);
}
function test_createStandardL2Token_sameTwice_reverts() external {
address remote = address(4);
/// @notice Test that calling `createStandardL2TokenWithDecimals` with the same parameters twice reverts.
function test_createStandardL2TokenWithDecimals_sameTwice_reverts(
address _caller,
address _remoteToken,
string memory _name,
string memory _symbol,
uint8 _decimals
)
external
{
// Assume
vm.assume(_remoteToken != address(0));
vm.prank(alice);
l2OptimismMintableERC20Factory.createStandardL2Token(remote, "Beep", "BOOP");
vm.prank(_caller);
l2OptimismMintableERC20Factory.createOptimismMintableERC20WithDecimals(_remoteToken, _name, _symbol, _decimals);
// Arrange
vm.expectRevert(bytes(""));
vm.prank(alice);
l2OptimismMintableERC20Factory.createStandardL2Token(remote, "Beep", "BOOP");
// Act
vm.prank(_caller);
l2OptimismMintableERC20Factory.createOptimismMintableERC20WithDecimals(_remoteToken, _name, _symbol, _decimals);
}
function test_createStandardL2Token_remoteIsZero_reverts() external {
/// @notice Test that calling `createStandardL2Token` with a zero remote token address reverts.
function test_createStandardL2Token_remoteIsZero_reverts(
address _caller,
string memory _name,
string memory _symbol
)
external
{
// Arrange
address remote = address(0);
vm.expectRevert("OptimismMintableERC20Factory: must provide remote token address");
l2OptimismMintableERC20Factory.createStandardL2Token(remote, "Beep", "BOOP");
// Act
vm.prank(_caller);
l2OptimismMintableERC20Factory.createStandardL2Token(remote, _name, _symbol);
}
/// @notice Test that calling `createStandardL2TokenWithDecimals` with a zero remote token address reverts.
function test_createStandardL2TokenWithDecimals_remoteIsZero_reverts(
address _caller,
string memory _name,
string memory _symbol,
uint8 _decimals
)
external
{
// Arrange
address remote = address(0);
vm.expectRevert("OptimismMintableERC20Factory: must provide remote token address");
// Act
vm.prank(_caller);
l2OptimismMintableERC20Factory.createOptimismMintableERC20WithDecimals(remote, _name, _symbol, _decimals);
}
function calculateTokenAddress(
/// @notice Precalculates the address of the token contract.
function _calculateTokenAddress(
address _remote,
string memory _name,
string memory _symbol,
......
......@@ -285,6 +285,14 @@ contract Initializer_Test is Bridge_Initializer {
initializedSlotVal: deploy.loadInitializedSlot("L2StandardBridge")
})
);
// L2StandardBridgeInterop
contracts.push(
InitializeableContract({
target: address(l2StandardBridge),
initCalldata: abi.encodeCall(l2StandardBridge.initialize, (l1StandardBridge)),
initializedSlotVal: deploy.loadInitializedSlot("L2StandardBridgeInterop")
})
);
// L1ERC721BridgeImpl
contracts.push(
InitializeableContract({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment