Skip to content

Commit

Permalink
feat: introduce OptimismSuperchainERC20Factory (ethereum-optimism#11617)
Browse files Browse the repository at this point in the history
* 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

* feat: add superchain erc20 factory implementation (#23)

* feat: add superchain erc20 factory implementation

* fix: remove createX comments

* test: add superchain erc20 factory tests (#25)

* test: add superchain erc20 factory tests

* test: add erc20 asserts

* test: fix expect emit

* fix: remove comments

* feat: add constructor to superchain ERC20 beacon (#34)

* test: remove factory predeploy etch

----------

Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: gotzenx <[email protected]>

* fix: set an arbitrary address for superchain erc20 impl

* fix: deploy a proxy for the beacon on genesis (#45)


---------

Co-authored-by: 0xng <[email protected]>

* fix: conflicts and imports

* fix: interfaces

* chore: add .testdata

* fix: adding back .testdata to gitignore

* fix: new conflicts from ci improvements

---------

Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: gotzenx <[email protected]>
Co-authored-by: Disco <[email protected]>
  • Loading branch information
5 people authored Sep 12, 2024
1 parent 97aa08a commit 232c54d
Show file tree
Hide file tree
Showing 17 changed files with 483 additions and 58 deletions.
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ GasBenchMark_L1Block_SetValuesEcotone:test_setL1BlockValuesEcotone_benchmark() (
GasBenchMark_L1Block_SetValuesEcotone_Warm:test_setL1BlockValuesEcotone_benchmark() (gas: 7597)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 369242)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2967382)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564356)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076571)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 564362)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4076577)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 467019)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512701)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72618)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92970)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68312)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68943)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155610)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155607)
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 @@ -154,6 +154,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
26 changes: 26 additions & 0 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { L2ERC721Bridge } from "src/L2/L2ERC721Bridge.sol";
import { SequencerFeeVault } from "src/L2/SequencerFeeVault.sol";
import { BaseFeeVault } from "src/L2/BaseFeeVault.sol";
import { L1FeeVault } from "src/L2/L1FeeVault.sol";
import { OptimismSuperchainERC20Beacon } from "src/L2/OptimismSuperchainERC20Beacon.sol";
import { OptimismMintableERC721Factory } from "src/universal/OptimismMintableERC721Factory.sol";
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
import { StandardBridge } from "src/universal/StandardBridge.sol";
Expand Down Expand Up @@ -262,6 +263,8 @@ contract L2Genesis is Deployer {
setL2ToL2CrossDomainMessenger(); // 23
setSuperchainWETH(); // 24
setETHLiquidity(); // 25
setOptimismSuperchainERC20Factory(); // 26
setOptimismSuperchainERC20Beacon(); // 27
}
}

Expand Down Expand Up @@ -513,6 +516,29 @@ 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 #2.
function setOptimismSuperchainERC20Beacon() internal {
address superchainERC20Impl = Predeploys.OPTIMISM_SUPERCHAIN_ERC20;
console.log("Setting %s implementation at: %s", "OptimismSuperchainERC20", superchainERC20Impl);
vm.etch(superchainERC20Impl, vm.getDeployedCode("OptimismSuperchainERC20.sol:OptimismSuperchainERC20"));

OptimismSuperchainERC20Beacon beacon = new OptimismSuperchainERC20Beacon(superchainERC20Impl);
address beaconImpl = Predeploys.predeployToCodeNamespace(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON);

console.log("Setting %s implementation at: %s", "OptimismSuperchainERC20Beacon", beaconImpl);
vm.etch(beaconImpl, address(beacon).code);

/// Reset so its not included state dump
vm.etch(address(beacon), "");
vm.resetNonce(address(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
8 changes: 8 additions & 0 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,14 @@
"initCodeHash": "0xdd16dbc0ccbbac53ec2d4273f06334960a907a9f20b7c40300833227ee31d0de",
"sourceCodeHash": "0x910d43a17800df64dbc104f69ef1f900ca761cec4949c01d1c1126fde5268349"
},
"src/L2/OptimismSuperchainERC20Beacon.sol": {
"initCodeHash": "0x99ce8095b23c124850d866cbc144fee6cee05dbc6bb5d83acadfe00b90cf42c7",
"sourceCodeHash": "0x5e58b7c867fafa49fe39d68d83875425e9cf94f05f2835bdcdaa08fc8bc6b68e"
},
"src/L2/OptimismSuperchainERC20Factory.sol": {
"initCodeHash": "0x98011045722178751e4a1112892f7d9a11bc1f5e42ac18205b6d30a1f1476d24",
"sourceCodeHash": "0x9e72b2a77d82fcf3963734232ba9faff9d63962594a032041c2561f0a9f1b0b5"
},
"src/L2/SequencerFeeVault.sol": {
"initCodeHash": "0x2e6551705e493bacba8cffe22e564d5c401ae5bb02577a5424e0d32784e13e74",
"sourceCodeHash": "0xd56922cb04597dea469c65e5a49d4b3c50c171e603601e6f41da9517cae0b11a"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_implementation",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "implementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_remoteToken",
"type": "address"
},
{
"internalType": "string",
"name": "_name",
"type": "string"
},
{
"internalType": "string",
"name": "_symbol",
"type": "string"
},
{
"internalType": "uint8",
"name": "_decimals",
"type": "uint8"
}
],
"name": "deploy",
"outputs": [
{
"internalType": "address",
"name": "_superchainERC20",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "superchainToken",
"type": "address"
}
],
"name": "deployments",
"outputs": [
{
"internalType": "address",
"name": "remoteToken",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "superchainToken",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "remoteToken",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "deployer",
"type": "address"
}
],
"name": "OptimismSuperchainERC20Created",
"type": "event"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"bytes": "32",
"label": "deployments",
"offset": 0,
"slot": "0",
"type": "mapping(address => address)"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { IBeacon } from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";

/// @custom:proxied
/// @custom:predeployed 0x4200000000000000000000000000000000000027
/// @title OptimismSuperchainERC20Beacon
/// @notice OptimismSuperchainERC20Beacon is the beacon proxy for the OptimismSuperchainERC20 implementation.
contract OptimismSuperchainERC20Beacon is IBeacon, ISemver {
/// @notice Address of the OptimismSuperchainERC20 implementation.
address internal immutable IMPLEMENTATION;

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.1
string public constant version = "1.0.0-beta.1";

constructor(address _implementation) {
IMPLEMENTATION = _implementation;
}

/// @inheritdoc IBeacon
function implementation() external view override returns (address) {
return IMPLEMENTATION;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import { IOptimismERC20Factory } from "src/L2/interfaces/IOptimismERC20Factory.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { BeaconProxy } from "@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol";
import { CREATE3 } from "@rari-capital/solmate/src/utils/CREATE3.sol";

/// @custom:proxied
/// @custom:predeployed 0x4200000000000000000000000000000000000026
/// @title OptimismSuperchainERC20Factory
/// @notice OptimismSuperchainERC20Factory is a factory contract that deploys OptimismSuperchainERC20 Beacon Proxies
/// using CREATE3.
contract OptimismSuperchainERC20Factory is IOptimismERC20Factory, ISemver {
/// @notice Mapping of the deployed OptimismSuperchainERC20 to the remote token address.
/// This is used to keep track of the token deployments.
mapping(address superchainToken => address remoteToken) public deployments;

/// @notice Emitted when an OptimismSuperchainERC20 is deployed.
/// @param superchainToken Address of the SuperchainERC20 deployment.
/// @param remoteToken Address of the corresponding token on the remote chain.
/// @param deployer Address of the account that deployed the token.
event OptimismSuperchainERC20Created(
address indexed superchainToken, address indexed remoteToken, address deployer
);

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.1
string public constant version = "1.0.0-beta.1";

/// @notice Deploys a OptimismSuperchainERC20 Beacon Proxy using CREATE3.
/// @param _remoteToken Address of the remote token.
/// @param _name Name of the OptimismSuperchainERC20.
/// @param _symbol Symbol of the OptimismSuperchainERC20.
/// @param _decimals Decimals of the OptimismSuperchainERC20.
/// @return _superchainERC20 Address of the OptimismSuperchainERC20 deployment.
function deploy(
address _remoteToken,
string memory _name,
string memory _symbol,
uint8 _decimals
)
external
returns (address _superchainERC20)
{
bytes memory initCallData =
abi.encodeCall(OptimismSuperchainERC20.initialize, (_remoteToken, _name, _symbol, _decimals));

bytes memory creationCode = bytes.concat(
type(BeaconProxy).creationCode, abi.encode(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON, initCallData)
);

bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol, _decimals));
_superchainERC20 = CREATE3.deploy({ salt: salt, creationCode: creationCode, value: 0 });

deployments[_superchainERC20] = _remoteToken;

emit OptimismSuperchainERC20Created(_superchainERC20, _remoteToken, msg.sender);
}
}
12 changes: 10 additions & 2 deletions packages/contracts-bedrock/src/libraries/Predeploys.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,16 @@ 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 Address of the OptimismSuperchainERC20Beacon predeploy.
address internal constant OPTIMISM_SUPERCHAIN_ERC20_BEACON = 0x4200000000000000000000000000000000000027;

// TODO: Precalculate the address of the implementation contract
/// @notice Arbitrary address of the OptimismSuperchainERC20 implementation contract.
address internal constant OPTIMISM_SUPERCHAIN_ERC20 = 0xB9415c6cA93bdC545D4c5177512FCC22EFa38F28;

/// @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");
Expand Down Expand Up @@ -128,6 +134,7 @@ library Predeploys {
if (_addr == SUPERCHAIN_WETH) return "SuperchainWETH";
if (_addr == ETH_LIQUIDITY) return "ETHLiquidity";
if (_addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY) return "OptimismSuperchainERC20Factory";
if (_addr == OPTIMISM_SUPERCHAIN_ERC20_BEACON) return "OptimismSuperchainERC20Beacon";
revert("Predeploys: unnamed predeploy");
}

Expand All @@ -146,7 +153,8 @@ library Predeploys {
|| _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 == OPTIMISM_SUPERCHAIN_ERC20_FACTORY);
|| (_useInterop && _addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY)
|| (_useInterop && _addr == OPTIMISM_SUPERCHAIN_ERC20_BEACON);
}

function isPredeployNamespace(address _addr) internal pure returns (bool) {
Expand Down
Loading

0 comments on commit 232c54d

Please sign in to comment.