Commit 234351c3 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #6766 from ncitron/ncitron/deterministic-token-addresses

feat: use deterministic deployment for token factories
parents 04044b24 55d41f21
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -410,7 +410,8 @@ OptimismMintableERC20_Test:test_mint_notBridge_reverts() (gas: 11165)
OptimismMintableERC20_Test:test_mint_succeeds() (gas: 63544)
OptimismMintableERC20_Test:test_remoteToken_succeeds() (gas: 7667)
OptimismMintableERC721Factory_Test:test_constructor_succeeds() (gas: 8351)
OptimismMintableERC721Factory_Test:test_createOptimismMintableERC721_succeeds() (gas: 2321820)
OptimismMintableERC721Factory_Test:test_createOptimismMintableERC721_sameTwice_reverts() (gas: 8937393460516801476)
OptimismMintableERC721Factory_Test:test_createOptimismMintableERC721_succeeds() (gas: 2363100)
OptimismMintableERC721Factory_Test:test_createOptimismMintableERC721_zeroRemoteToken_reverts() (gas: 9418)
OptimismMintableERC721_Test:test_burn_notBridge_reverts() (gas: 136966)
OptimismMintableERC721_Test:test_burn_succeeds() (gas: 118832)
......@@ -419,10 +420,10 @@ OptimismMintableERC721_Test:test_safeMint_notBridge_reverts() (gas: 11121)
OptimismMintableERC721_Test:test_safeMint_succeeds() (gas: 140547)
OptimismMintableERC721_Test:test_supportsInterfaces_succeeds() (gas: 9005)
OptimismMintableERC721_Test:test_tokenURI_succeeds() (gas: 163441)
OptimismMintableTokenFactory_Test:test_bridge_succeeds() (gas: 7580)
OptimismMintableTokenFactory_Test:test_createStandardL2Token_remoteIsZero_succeeds() (gas: 9390)
OptimismMintableTokenFactory_Test:test_createStandardL2Token_sameTwice_succeeds() (gas: 2523203)
OptimismMintableTokenFactory_Test:test_createStandardL2Token_succeeds() (gas: 1268564)
OptimismMintableTokenFactory_Test:test_bridge_succeeds() (gas: 7646)
OptimismMintableTokenFactory_Test:test_createStandardL2Token_remoteIsZero_reverts() (gas: 9401)
OptimismMintableTokenFactory_Test:test_createStandardL2Token_sameTwice_reverts() (gas: 8937393460516769032)
OptimismMintableTokenFactory_Test:test_createStandardL2Token_succeeds() (gas: 1293594)
OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 11178)
OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 16111)
OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 26781)
......
......@@ -24,8 +24,8 @@
"src/periphery/op-nft/Optimist.sol": "0x38407f766aa9d394403e9da388dd0265b48901789f3e8a28af50014f9f5251d9",
"src/periphery/op-nft/OptimistAllowlist.sol": "0x53e9a9dfecbae036fd468e8f34c80c7d9c35bd8908c8a6483db44dbc5128ad69",
"src/periphery/op-nft/OptimistInviter.sol": "0xfdd5b9d45205ef9372ba37f7a6394724695e676d27a47cb154ee6e4148490013",
"src/universal/OptimismMintableERC20.sol": "0xddb1f0047ee90db724405ad6a775fd2b26882cbc31d464c3def2c20c88aaf750",
"src/universal/OptimismMintableERC20Factory.sol": "0x1c77e31f624d0a7306dec8ffc3947a72f8e1e93a01cd911b4561c038732ab7fe",
"src/universal/OptimismMintableERC721.sol": "0x4eaa5ea0d923ce967f299c2ded84c2a53431159c47c0a344a9b8f534a25b7e1f",
"src/universal/OptimismMintableERC721Factory.sol": "0x6c19bc51bf9184aafad46f292bf9c23cc87c3406f31cccf57d4e3e096b887a8d"
"src/universal/OptimismMintableERC20.sol": "0xa0b4f168802d0f9eca9ddc54347ca66c34ad7aa0fd84b01e0d7e99a9f86f46d6",
"src/universal/OptimismMintableERC20Factory.sol": "0x8afb6b634f40e8ac8c60ec7e2ca3bcd610b020d09b3cae70a3d4995722cab3ce",
"src/universal/OptimismMintableERC721.sol": "0x49dc863caf3e002bf0c90b3af3873990fb282771f4c63735fd61a885b7873983",
"src/universal/OptimismMintableERC721Factory.sol": "0x502438b009bfaf0ab187914662468261f10446fd85d44a79b29cdaef7b4982ba"
}
\ No newline at end of file
......@@ -35,7 +35,7 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20,
_;
}
/// @custom:semver 1.0.3
/// @custom:semver 1.1.0
/// @param _bridge Address of the L2 standard bridge.
/// @param _remoteToken Address of the corresponding L1 token.
/// @param _name ERC20 name.
......@@ -47,7 +47,7 @@ contract OptimismMintableERC20 is IOptimismMintableERC20, ILegacyMintableERC20,
string memory _symbol
)
ERC20(_name, _symbol)
Semver(1, 0, 3)
Semver(1, 1, 0)
{
REMOTE_TOKEN = _remoteToken;
BRIDGE = _bridge;
......
......@@ -28,12 +28,12 @@ contract OptimismMintableERC20Factory is Semver {
/// @param deployer Address of the account that deployed the token.
event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer);
/// @custom:semver 1.1.2
/// @custom:semver 1.2.0
/// @notice The semver MUST be bumped any time that there is a change in
/// the OptimismMintableERC20 token contract since this contract
/// is responsible for deploying OptimismMintableERC20 contracts.
/// @param _bridge Address of the StandardBridge on this chain.
constructor(address _bridge) Semver(1, 1, 2) {
constructor(address _bridge) Semver(1, 2, 0) {
BRIDGE = _bridge;
}
......@@ -70,7 +70,8 @@ contract OptimismMintableERC20Factory is Semver {
{
require(_remoteToken != address(0), "OptimismMintableERC20Factory: must provide remote token address");
address localToken = address(new OptimismMintableERC20(BRIDGE, _remoteToken, _name, _symbol));
bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol));
address localToken = address(new OptimismMintableERC20{salt: salt}(BRIDGE, _remoteToken, _name, _symbol));
// Emit the old event too for legacy support.
emit StandardL2TokenCreated(_remoteToken, localToken);
......
......@@ -31,7 +31,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721, Se
_;
}
/// @custom:semver 1.1.2
/// @custom:semver 1.2.0
/// @param _bridge Address of the bridge on this network.
/// @param _remoteChainId Chain ID where the remote token is deployed.
/// @param _remoteToken Address of the corresponding token on the other network.
......@@ -45,7 +45,7 @@ contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721, Se
string memory _symbol
)
ERC721(_name, _symbol)
Semver(1, 1, 2)
Semver(1, 2, 0)
{
require(_bridge != address(0), "OptimismMintableERC721: bridge cannot be address(0)");
require(_remoteChainId != 0, "OptimismMintableERC721: remote chain id cannot be zero");
......
......@@ -22,13 +22,13 @@ contract OptimismMintableERC721Factory is Semver {
/// @param deployer Address of the initiator of the deployment
event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer);
/// @custom:semver 1.2.3
/// @custom:semver 1.3.0
/// @notice The semver MUST be bumped any time that there is a change in
/// the OptimismMintableERC721 token contract since this contract
/// is responsible for deploying OptimismMintableERC721 contracts.
/// @param _bridge Address of the ERC721 bridge on this network.
/// @param _remoteChainId Chain ID for the remote network.
constructor(address _bridge, uint256 _remoteChainId) Semver(1, 2, 3) {
constructor(address _bridge, uint256 _remoteChainId) Semver(1, 3, 0) {
BRIDGE = _bridge;
REMOTE_CHAIN_ID = _remoteChainId;
}
......@@ -47,7 +47,9 @@ contract OptimismMintableERC721Factory is Semver {
{
require(_remoteToken != address(0), "OptimismMintableERC721Factory: L1 token address cannot be address(0)");
address localToken = address(new OptimismMintableERC721(BRIDGE, REMOTE_CHAIN_ID, _remoteToken, _name, _symbol));
bytes32 salt = keccak256(abi.encode(_remoteToken, _name, _symbol));
address localToken =
address(new OptimismMintableERC721{salt: salt}(BRIDGE, REMOTE_CHAIN_ID, _remoteToken, _name, _symbol));
isOptimismMintableERC721[localToken] = true;
emit OptimismMintableERC721Created(localToken, _remoteToken, msg.sender);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { OptimismMintableERC20 } from "../src/universal/OptimismMintableERC20.sol";
import { Bridge_Initializer } from "./CommonTest.t.sol";
import { LibRLP } from "./RLP.t.sol";
contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken);
......@@ -18,7 +18,7 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
function test_createStandardL2Token_succeeds() external {
address remote = address(4);
address local = LibRLP.computeAddress(address(L2TokenFactory), 2);
address local = calculateTokenAddress(remote, "Beep", "BOOP");
vm.expectEmit(true, true, true, true);
emit StandardL2TokenCreated(remote, local);
......@@ -30,27 +30,37 @@ contract OptimismMintableTokenFactory_Test is Bridge_Initializer {
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
function test_createStandardL2Token_sameTwice_succeeds() external {
function test_createStandardL2Token_sameTwice_reverts() external {
address remote = address(4);
vm.prank(alice);
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
address local = LibRLP.computeAddress(address(L2TokenFactory), 3);
vm.expectEmit(true, true, true, true);
emit StandardL2TokenCreated(remote, local);
vm.expectEmit(true, true, true, true);
emit OptimismMintableERC20Created(local, remote, alice);
vm.expectRevert();
vm.prank(alice);
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
function test_createStandardL2Token_remoteIsZero_succeeds() external {
function test_createStandardL2Token_remoteIsZero_reverts() external {
address remote = address(0);
vm.expectRevert("OptimismMintableERC20Factory: must provide remote token address");
L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP");
}
function calculateTokenAddress(
address _remote,
string memory _name,
string memory _symbol
)
internal
view
returns (address)
{
bytes memory constructorArgs = abi.encode(address(L2Bridge), _remote, _name, _symbol);
bytes memory bytecode = abi.encodePacked(type(OptimismMintableERC20).creationCode, constructorArgs);
bytes32 salt = keccak256(abi.encode(_remote, _name, _symbol));
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(L2TokenFactory), salt, keccak256(bytecode)));
return address(uint160(uint256(hash)));
}
}
......@@ -3,7 +3,6 @@ pragma solidity 0.8.15;
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Bridge_Initializer } from "./CommonTest.t.sol";
import { LibRLP } from "./RLP.t.sol";
import { OptimismMintableERC721 } from "../src/universal/OptimismMintableERC721.sol";
import { OptimismMintableERC721Factory } from "../src/universal/OptimismMintableERC721Factory.sol";
......@@ -28,20 +27,20 @@ contract OptimismMintableERC721Factory_Test is ERC721Bridge_Initializer {
}
function test_createOptimismMintableERC721_succeeds() external {
// Predict the address based on the factory address and nonce.
address predicted = LibRLP.computeAddress(address(factory), 1);
address remote = address(1234);
address local = calculateTokenAddress(address(1234), "L2Token", "L2T");
// Expect a token creation event.
vm.expectEmit(true, true, true, true);
emit OptimismMintableERC721Created(predicted, address(1234), alice);
emit OptimismMintableERC721Created(local, remote, alice);
// Create the token.
vm.prank(alice);
OptimismMintableERC721 created =
OptimismMintableERC721(factory.createOptimismMintableERC721(address(1234), "L2Token", "L2T"));
OptimismMintableERC721(factory.createOptimismMintableERC721(remote, "L2Token", "L2T"));
// Token address should be correct.
assertEq(address(created), predicted);
assertEq(address(created), local);
// Should be marked as created by the factory.
assertEq(factory.isOptimismMintableERC721(address(created)), true);
......@@ -49,14 +48,42 @@ contract OptimismMintableERC721Factory_Test is ERC721Bridge_Initializer {
// Token should've been constructed correctly.
assertEq(created.name(), "L2Token");
assertEq(created.symbol(), "L2T");
assertEq(created.REMOTE_TOKEN(), address(1234));
assertEq(created.REMOTE_TOKEN(), remote);
assertEq(created.BRIDGE(), address(L2Bridge));
assertEq(created.REMOTE_CHAIN_ID(), 1);
}
function test_createOptimismMintableERC721_sameTwice_reverts() external {
address remote = address(1234);
vm.prank(alice);
factory.createOptimismMintableERC721(remote, "L2Token", "L2T");
vm.expectRevert();
vm.prank(alice);
factory.createOptimismMintableERC721(remote, "L2Token", "L2T");
}
function test_createOptimismMintableERC721_zeroRemoteToken_reverts() external {
// Try to create a token with a zero remote token address.
vm.expectRevert("OptimismMintableERC721Factory: L1 token address cannot be address(0)");
factory.createOptimismMintableERC721(address(0), "L2Token", "L2T");
}
function calculateTokenAddress(
address _remote,
string memory _name,
string memory _symbol
)
internal
view
returns (address)
{
bytes memory constructorArgs = abi.encode(address(L2Bridge), 1, _remote, _name, _symbol);
bytes memory bytecode = abi.encodePacked(type(OptimismMintableERC721).creationCode, constructorArgs);
bytes32 salt = keccak256(abi.encode(_remote, _name, _symbol));
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(factory), salt, keccak256(bytecode)));
return address(uint160(uint256(hash)));
}
}
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