Skip to content

Commit

Permalink
test: add superchain erc20 factory tests (#25)
Browse files Browse the repository at this point in the history
* test: add superchain erc20 factory tests

* test: add erc20 asserts
  • Loading branch information
agusduha authored Aug 22, 2024
1 parent 05a54f3 commit 2534555
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 19 deletions.
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369356)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967496)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564483)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076526)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564489)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076532)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 466947)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512629)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72624)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92970)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68433)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68903)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155618)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155615)
4 changes: 4 additions & 0 deletions packages/contracts-bedrock/scripts/Artifacts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ abstract contract Artifacts {
return payable(Predeploys.SCHEMA_REGISTRY);
} else if (digest == keccak256(bytes("EAS"))) {
return payable(Predeploys.EAS);
} else if (digest == keccak256(bytes("OptimismSuperchainERC20Factory"))) {
return payable(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY);
} else if (digest == keccak256(bytes("OptimismSuperchainERC20Beacon"))) {
return payable(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON);
}
return payable(address(0));
}
Expand Down
14 changes: 14 additions & 0 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ contract L2Genesis is Deployer {
setL2ToL2CrossDomainMessenger(); // 23
setSuperchainWETH(); // 24
setETHLiquidity(); // 25
setOptimismSuperchainERC20Factory(); // 26
setOptimismSuperchainERC20Beacon(); // 27
}
}

Expand Down Expand Up @@ -505,6 +507,18 @@ contract L2Genesis is Deployer {
_setImplementationCode(Predeploys.SUPERCHAIN_WETH);
}

/// @notice This predeploy is following the safety invariant #1.
/// This contract has no initializer.
function setOptimismSuperchainERC20Factory() internal {
_setImplementationCode(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY);
}

/// @notice This predeploy is following the safety invariant #1.
/// This contract has no initializer.
function setOptimismSuperchainERC20Beacon() internal {
_setImplementationCode(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON);
}

/// @notice Sets all the preinstalls.
/// Warning: the creator-accounts of the preinstall contracts have 0 nonce values.
/// When performing a regular user-initiated contract-creation of a preinstall,
Expand Down
15 changes: 6 additions & 9 deletions packages/contracts-bedrock/test/L2/L2StandardBridgeInterop.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/I
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { IOptimismMintableERC20 } from "src/universal/IOptimismMintableERC20.sol";

// TODO: Replace Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY with optimismSuperchainERC20Factory
import { Predeploys } from "src/libraries/Predeploys.sol";

contract L2StandardBridgeInterop_Test is Bridge_Initializer {
/// @notice Emitted when a conversion is made.
event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount);
Expand Down Expand Up @@ -145,7 +142,7 @@ contract L2StandardBridgeInterop_LegacyToSuper_Test is L2StandardBridgeInterop_T
_mockDeployments(address(l2OptimismMintableERC20Factory), _from, _remoteToken);

// Mock the superchain factory to return address(0)
_mockDeployments(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY, _to, address(0));
_mockDeployments(address(l2OptimismSuperchainERC20Factory), _to, address(0));

// Expect the revert with `InvalidSuperchainERC20Address` selector
vm.expectRevert(InvalidSuperchainERC20Address.selector);
Expand Down Expand Up @@ -176,7 +173,7 @@ contract L2StandardBridgeInterop_LegacyToSuper_Test is L2StandardBridgeInterop_T
_mockDeployments(address(l2OptimismMintableERC20Factory), _from, _fromRemoteToken);

// Mock the superchain factory to return `_toRemoteToken`
_mockDeployments(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY, _to, _toRemoteToken);
_mockDeployments(address(l2OptimismSuperchainERC20Factory), _to, _toRemoteToken);

// Expect the revert with `InvalidTokenPair` selector
vm.expectRevert(InvalidTokenPair.selector);
Expand All @@ -203,7 +200,7 @@ contract L2StandardBridgeInterop_LegacyToSuper_Test is L2StandardBridgeInterop_T

// Mock the legacy and superchain factory to return `_remoteToken`
_mockDeployments(address(l2OptimismMintableERC20Factory), _from, _remoteToken);
_mockDeployments(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY, _to, _remoteToken);
_mockDeployments(address(l2OptimismSuperchainERC20Factory), _to, _remoteToken);

// Expect the `Converted` event to be emitted
vm.expectEmit(true, true, true, true, address(l2StandardBridge));
Expand Down Expand Up @@ -294,7 +291,7 @@ contract L2StandardBridgeInterop_SuperToLegacy_Test is L2StandardBridgeInterop_T
_mockDeployments(address(l2OptimismMintableERC20Factory), _to, _remoteToken);

// Mock the superchain factory to return address(0)
_mockDeployments(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY, _from, address(0));
_mockDeployments(address(l2OptimismSuperchainERC20Factory), _from, address(0));

// Expect the revert with `InvalidSuperchainERC20Address` selector
vm.expectRevert(InvalidSuperchainERC20Address.selector);
Expand Down Expand Up @@ -325,7 +322,7 @@ contract L2StandardBridgeInterop_SuperToLegacy_Test is L2StandardBridgeInterop_T
_mockDeployments(address(l2OptimismMintableERC20Factory), _to, _fromRemoteToken);

// Mock the superchain factory to return `_toRemoteToken`
_mockDeployments(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY, _from, _toRemoteToken);
_mockDeployments(address(l2OptimismSuperchainERC20Factory), _from, _toRemoteToken);

// Expect the revert with `InvalidTokenPair` selector
vm.expectRevert(InvalidTokenPair.selector);
Expand All @@ -352,7 +349,7 @@ contract L2StandardBridgeInterop_SuperToLegacy_Test is L2StandardBridgeInterop_T

// Mock the legacy and superchain factory to return `_remoteToken`
_mockDeployments(address(l2OptimismMintableERC20Factory), _to, _remoteToken);
_mockDeployments(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY, _from, _remoteToken);
_mockDeployments(address(l2OptimismSuperchainERC20Factory), _from, _remoteToken);

// Expect the `Converted` event to be emitted
vm.expectEmit(true, true, true, true, address(l2StandardBridge));
Expand Down
32 changes: 28 additions & 4 deletions packages/contracts-bedrock/test/L2/OptimismSuperchainERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ pragma solidity 0.8.25;

// Testing utilities
import { Test } from "forge-std/Test.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";

// Libraries
import { Predeploys } from "src/libraries/Predeploys.sol";
import { IERC20 } from "@openzeppelin/contracts-v5/token/ERC20/IERC20.sol";
import { IL2ToL2CrossDomainMessenger } from "src/L2/IL2ToL2CrossDomainMessenger.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol";
import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol";
import { IERC165 } from "@openzeppelin/contracts-v5/utils/introspection/IERC165.sol";
import { IBeacon } from "@openzeppelin/contracts-v5/proxy/beacon/IBeacon.sol";
import { BeaconProxy } from "@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol";

// Target contract
import {
Expand Down Expand Up @@ -40,9 +42,32 @@ contract OptimismSuperchainERC20Test is Test {
/// @notice Sets up the test suite.
function setUp() public {
superchainERC20Impl = new OptimismSuperchainERC20();

// Deploy the OptimismSuperchainERC20Beacon contract
_deployBeacon();

superchainERC20 = _deploySuperchainERC20Proxy(REMOTE_TOKEN, NAME, SYMBOL, DECIMALS);
}

/// @notice Deploy the OptimismSuperchainERC20Beacon predeploy contract
function _deployBeacon() internal {
// Deploy the OptimismSuperchainERC20Beacon implementation
address _addr = Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON;
address _impl = Predeploys.predeployToCodeNamespace(_addr);
vm.etch(_impl, vm.getDeployedCode("OptimismSuperchainERC20Beacon.sol:OptimismSuperchainERC20Beacon"));

// Deploy the ERC1967Proxy contract at the Predeploy
bytes memory code = vm.getDeployedCode("universal/Proxy.sol:Proxy");
vm.etch(_addr, code);
EIP1967Helper.setAdmin(_addr, Predeploys.PROXY_ADMIN);
EIP1967Helper.setImplementation(_addr, _impl);

// Mock implementation address
vm.mockCall(
_impl, abi.encodeWithSelector(IBeacon.implementation.selector), abi.encode(address(superchainERC20Impl))
);
}

/// @notice Helper function to deploy a proxy of the OptimismSuperchainERC20 contract.
function _deploySuperchainERC20Proxy(
address _remoteToken,
Expand All @@ -55,9 +80,8 @@ contract OptimismSuperchainERC20Test is Test {
{
return OptimismSuperchainERC20(
address(
// TODO: Use the SuperchainERC20 Beacon Proxy
new ERC1967Proxy(
address(superchainERC20Impl),
new BeaconProxy(
Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON,
abi.encodeCall(OptimismSuperchainERC20.initialize, (_remoteToken, _name, _symbol, _decimals))
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

// Testing utilities
import { Test } from "forge-std/Test.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";

// Libraries
import { Predeploys } from "src/libraries/Predeploys.sol";
import { CREATE3, Bytes32AddressLib } from "@rari-capital/solmate/src/utils/CREATE3.sol";
import { IBeacon } from "@openzeppelin/contracts-v5/proxy/beacon/IBeacon.sol";

// Target contract
import { OptimismSuperchainERC20Factory, OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20Factory.sol";

/// @title OptimismSuperchainERC20FactoryTest
/// @notice Contract for testing the OptimismSuperchainERC20Factory contract.
contract OptimismSuperchainERC20FactoryTest is Test {
using Bytes32AddressLib for bytes32;

OptimismSuperchainERC20 public superchainERC20Impl;
OptimismSuperchainERC20Factory public superchainERC20Factory;

/// @notice Sets up the test suite.
function setUp() public {
superchainERC20Impl = new OptimismSuperchainERC20();

// Deploy the OptimismSuperchainERC20Beacon contract
_deployBeacon();

superchainERC20Factory = new OptimismSuperchainERC20Factory();
}

/// @notice Deploy the OptimismSuperchainERC20Beacon predeploy contract
function _deployBeacon() internal {
// Deploy the OptimismSuperchainERC20Beacon implementation
address _addr = Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON;
address _impl = Predeploys.predeployToCodeNamespace(_addr);
vm.etch(_impl, vm.getDeployedCode("OptimismSuperchainERC20Beacon.sol:OptimismSuperchainERC20Beacon"));

// Deploy the ERC1967Proxy contract at the Predeploy
bytes memory code = vm.getDeployedCode("universal/Proxy.sol:Proxy");
vm.etch(_addr, code);
EIP1967Helper.setAdmin(_addr, Predeploys.PROXY_ADMIN);
EIP1967Helper.setImplementation(_addr, _impl);

// Mock implementation address
vm.mockCall(
_impl, abi.encodeWithSelector(IBeacon.implementation.selector), abi.encode(address(superchainERC20Impl))
);
}

/// @notice Test that calling `deploy` with valid parameters succeeds.
function test_deploy_succeeds(
address _caller,
address _remoteToken,
string memory _name,
string memory _symbol,
uint8 _decimals
)
public
{
// Arrange
bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol, _decimals));
address deployment = _calculateTokenAddress(salt, address(superchainERC20Factory));

vm.expectEmit(true, true, true, true);
emit OptimismSuperchainERC20Factory.OptimismSuperchainERC20Created(deployment, _remoteToken, _caller);

// Act
vm.prank(_caller);
address addr = superchainERC20Factory.deploy(_remoteToken, _name, _symbol, _decimals);

// Assert
assertTrue(addr == deployment);
assertTrue(OptimismSuperchainERC20(deployment).decimals() == _decimals);
assertTrue(OptimismSuperchainERC20(deployment).remoteToken() == _remoteToken);
assertEq(OptimismSuperchainERC20(deployment).name(), _name);
assertEq(OptimismSuperchainERC20(deployment).symbol(), _symbol);
assertEq(superchainERC20Factory.deployments(deployment), _remoteToken);
}

/// @notice Test that calling `deploy` with the same parameters twice reverts.
function test_deploy_sameTwice_reverts(
address _caller,
address _remoteToken,
string memory _name,
string memory _symbol,
uint8 _decimals
)
external
{
// Arrange
vm.prank(_caller);
superchainERC20Factory.deploy(_remoteToken, _name, _symbol, _decimals);

vm.expectRevert(bytes("DEPLOYMENT_FAILED"));

// Act
vm.prank(_caller);
superchainERC20Factory.deploy(_remoteToken, _name, _symbol, _decimals);
}

/// @notice Precalculates the address of the token contract using CREATE3.
function _calculateTokenAddress(bytes32 _salt, address _deployer) internal pure returns (address) {
address proxy =
keccak256(abi.encodePacked(bytes1(0xFF), _deployer, _salt, CREATE3.PROXY_BYTECODE_HASH)).fromLast20Bytes();

return keccak256(abi.encodePacked(hex"d694", proxy, hex"01")).fromLast20Bytes();
}
}
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/test/L2Genesis.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ contract L2GenesisTest is Test {
// 2 predeploys do not have proxies
assertEq(getCodeCount(_path, "Proxy.sol:Proxy"), Predeploys.PREDEPLOY_COUNT - 2);

// 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);
// 23 proxies have the implementation set if useInterop is true and 17 if useInterop is false
assertEq(getPredeployCountWithSlotSet(_path, Constants.PROXY_IMPLEMENTATION_ADDRESS), _useInterop ? 23 : 17);

// All proxies except 2 have the proxy 1967 admin slot set to the proxy admin
assertEq(
Expand Down
7 changes: 7 additions & 0 deletions packages/contracts-bedrock/test/setup/Setup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { DataAvailabilityChallenge } from "src/L1/DataAvailabilityChallenge.sol"
import { WETH } from "src/L2/WETH.sol";
import { SuperchainWETH } from "src/L2/SuperchainWETH.sol";
import { ETHLiquidity } from "src/L2/ETHLiquidity.sol";
import { IOptimismERC20Factory } from "src/L2/IOptimismERC20Factory.sol";

/// @title Setup
/// @dev This contact is responsible for setting up the contracts in state. It currently
Expand Down Expand Up @@ -99,6 +100,10 @@ contract Setup {
SuperchainWETH superchainWeth = SuperchainWETH(payable(Predeploys.SUPERCHAIN_WETH));
ETHLiquidity ethLiquidity = ETHLiquidity(Predeploys.ETH_LIQUIDITY);

// TODO: Replace with OptimismSuperchainERC20Factory when updating pragmas
IOptimismERC20Factory l2OptimismSuperchainERC20Factory =
IOptimismERC20Factory(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY);

/// @dev Deploys the Deploy contract without including its bytecode in the bytecode
/// of this contract by fetching the bytecode dynamically using `vm.getCode()`.
/// If the Deploy bytecode is included in this contract, then it will double
Expand Down Expand Up @@ -217,6 +222,8 @@ contract Setup {
labelPredeploy(Predeploys.WETH);
labelPredeploy(Predeploys.SUPERCHAIN_WETH);
labelPredeploy(Predeploys.ETH_LIQUIDITY);
labelPredeploy(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY);
labelPredeploy(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON);

// L2 Preinstalls
labelPreinstall(Preinstalls.MultiCall3);
Expand Down

0 comments on commit 2534555

Please sign in to comment.