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