diff --git a/README.md b/README.md index 55f4a82..3b07cb1 100644 --- a/README.md +++ b/README.md @@ -119,11 +119,13 @@ Deploy the implementation contracts. 1. Add the following [env](.env) variables - `CREATE2_FACTORY_CONTRACT_ADDRESS` + - `CREATE2_FACTORY_OWNER_KEY` + - `TOKEN_MINTER_V2_OWNER_ADDRESS` + - `TOKEN_MINTER_V2_OWNER_KEY` - `TOKEN_CONTROLLER_ADDRESS` - `DOMAIN` - `MESSAGE_BODY_VERSION` - `VERSION` - - `IMPLEMENTATION_DEPLOYER_PRIVATE_KEY` 2. Run `make simulate-deploy-implementations-v2 RPC_URL= SENDER=` to perform a dry run. @@ -170,8 +172,8 @@ The proxies are deployed via `CREATE2` through Create2Factory. The scripts assum - `BURN_LIMIT_PER_MESSAGE` - `CREATE2_FACTORY_OWNER_KEY` - - `TOKEN_MINTER_V2_DEPLOYER_KEY` - `TOKEN_CONTROLLER_KEY` + - `TOKEN_MINTER_V2_OWNER_KEY` 2. Run `make simulate-deploy-proxies-v2 RPC_URL= SENDER=` to perform a dry run. diff --git a/scripts/v2/DeployAddressUtilsExternal.s.sol b/scripts/v2/DeployAddressUtilsExternal.s.sol index 06b5bd9..6258006 100644 --- a/scripts/v2/DeployAddressUtilsExternal.s.sol +++ b/scripts/v2/DeployAddressUtilsExternal.s.sol @@ -20,6 +20,7 @@ pragma solidity 0.7.6; import {Script} from "forge-std/Script.sol"; import {AddressUtilsExternal} from "../../src/messages/v2/AddressUtilsExternal.sol"; import {Create2Factory} from "../../src/v2/Create2Factory.sol"; +import {SALT_ADDRESS_UTILS_EXTERNAL} from "./Salts.sol"; contract DeployAddressUtilsExternalScript is Script { Create2Factory private create2Factory; @@ -33,7 +34,7 @@ contract DeployAddressUtilsExternalScript is Script { _addressUtilsExternal = AddressUtilsExternal( create2Factory.deploy( 0, - bytes32(0), + SALT_ADDRESS_UTILS_EXTERNAL, type(AddressUtilsExternal).creationCode ) ); diff --git a/scripts/v2/DeployImplementationsV2.s.sol b/scripts/v2/DeployImplementationsV2.s.sol index 4e9cb98..a1f3cc1 100644 --- a/scripts/v2/DeployImplementationsV2.s.sol +++ b/scripts/v2/DeployImplementationsV2.s.sol @@ -16,12 +16,16 @@ * limitations under the License. */ pragma solidity 0.7.6; +pragma abicoder v2; import {Script} from "forge-std/Script.sol"; import {AdminUpgradableProxy} from "../../src/proxy/AdminUpgradableProxy.sol"; import {TokenMessengerV2} from "../../src/v2/TokenMessengerV2.sol"; import {TokenMinterV2} from "../../src/v2/TokenMinterV2.sol"; import {MessageTransmitterV2} from "../../src/v2/MessageTransmitterV2.sol"; +import {Create2Factory} from "../../src/v2/Create2Factory.sol"; +import {Ownable2Step} from "../../src/roles/Ownable2Step.sol"; +import {SALT_MESSAGE_TRANSMITTER, SALT_TOKEN_MESSENGER, SALT_TOKEN_MINTER} from "./Salts.sol"; contract DeployImplementationsV2Script is Script { // Expose for tests @@ -31,18 +35,21 @@ contract DeployImplementationsV2Script is Script { address public expectedMessageTransmitterV2ProxyAddress; address private factoryAddress; + address private tokenMinterOwnerAddress; + uint256 private tokenMinterOwnerKey; address private tokenControllerAddress; uint32 private messageBodyVersion; uint32 private version; uint32 private domain; - uint256 private implementationDeployerPrivateKey; + uint256 private create2FactoryOwnerPrivateKey; - function deployImplementationsV2( - uint256 privateKey - ) private returns (MessageTransmitterV2, TokenMinterV2, TokenMessengerV2) { + function deployImplementationsV2() + private + returns (MessageTransmitterV2, TokenMinterV2, TokenMessengerV2) + { // Calculate MessageTransmitterV2 proxy address expectedMessageTransmitterV2ProxyAddress = vm.computeCreate2Address( - keccak256(type(MessageTransmitterV2).creationCode), + SALT_MESSAGE_TRANSMITTER, keccak256( abi.encodePacked( type(AdminUpgradableProxy).creationCode, @@ -52,33 +59,69 @@ contract DeployImplementationsV2Script is Script { factoryAddress ); + Create2Factory factory = Create2Factory(factoryAddress); + // Start recording transactions - vm.startBroadcast(privateKey); + vm.startBroadcast(create2FactoryOwnerPrivateKey); // Deploy MessageTransmitterV2 implementation - MessageTransmitterV2 messageTransmitterV2Implementation = new MessageTransmitterV2( - domain, - version - ); + messageTransmitterV2 = MessageTransmitterV2( + factory.deploy( + 0, + SALT_MESSAGE_TRANSMITTER, + abi.encodePacked( + type(MessageTransmitterV2).creationCode, + abi.encode(domain, version) + ) + ) + ); - // Deploy TokenMinter - TokenMinterV2 tokenMinterV2Implementation = new TokenMinterV2( - tokenControllerAddress + // Deploy TokenMessengerV2 implementation + tokenMessengerV2 = TokenMessengerV2( + factory.deploy( + 0, + SALT_TOKEN_MESSENGER, + abi.encodePacked( + type(TokenMessengerV2).creationCode, + abi.encode( + expectedMessageTransmitterV2ProxyAddress, + messageBodyVersion + ) + ) + ) ); - // Deploy TokenMessengerV2 - TokenMessengerV2 tokenMessengerV2Implementation = new TokenMessengerV2( - expectedMessageTransmitterV2ProxyAddress, - messageBodyVersion + // Since the TokenMinter sets the msg.sender of the deployment to be + // the Owner, we'll need to rotate it from the Create2Factory atomically. + bytes memory tokenMinterOwnershipRotation = abi.encodeWithSelector( + Ownable2Step.transferOwnership.selector, + tokenMinterOwnerAddress + ); + bytes[] memory tokenMinterMultiCallData = new bytes[](1); + tokenMinterMultiCallData[0] = tokenMinterOwnershipRotation; + + // Deploy TokenMinter + tokenMinterV2 = TokenMinterV2( + factory.deployAndMultiCall( + 0, + SALT_TOKEN_MINTER, + abi.encodePacked( + type(TokenMinterV2).creationCode, + abi.encode(tokenControllerAddress) + ), + tokenMinterMultiCallData + ) ); // Stop recording transactions vm.stopBroadcast(); - return ( - messageTransmitterV2Implementation, - tokenMinterV2Implementation, - tokenMessengerV2Implementation - ); + + // Accept the TokenMinter 2-step ownership + vm.startBroadcast(tokenMinterOwnerKey); + tokenMinterV2.acceptOwnership(); + vm.stopBroadcast(); + + return (messageTransmitterV2, tokenMinterV2, tokenMessengerV2); } /** @@ -86,13 +129,15 @@ contract DeployImplementationsV2Script is Script { */ function setUp() public { factoryAddress = vm.envAddress("CREATE2_FACTORY_CONTRACT_ADDRESS"); + create2FactoryOwnerPrivateKey = vm.envUint("CREATE2_FACTORY_OWNER_KEY"); + tokenMinterOwnerAddress = vm.envAddress( + "TOKEN_MINTER_V2_OWNER_ADDRESS" + ); + tokenMinterOwnerKey = vm.envUint("TOKEN_MINTER_V2_OWNER_KEY"); tokenControllerAddress = vm.envAddress("TOKEN_CONTROLLER_ADDRESS"); domain = uint32(vm.envUint("DOMAIN")); messageBodyVersion = uint32(vm.envUint("MESSAGE_BODY_VERSION")); version = uint32(vm.envUint("VERSION")); - implementationDeployerPrivateKey = vm.envUint( - "IMPLEMENTATION_DEPLOYER_PRIVATE_KEY" - ); } /** @@ -103,6 +148,6 @@ contract DeployImplementationsV2Script is Script { messageTransmitterV2, tokenMinterV2, tokenMessengerV2 - ) = deployImplementationsV2(implementationDeployerPrivateKey); + ) = deployImplementationsV2(); } } diff --git a/scripts/v2/DeployProxiesV2.s.sol b/scripts/v2/DeployProxiesV2.s.sol index c32e08a..d4245d8 100644 --- a/scripts/v2/DeployProxiesV2.s.sol +++ b/scripts/v2/DeployProxiesV2.s.sol @@ -25,6 +25,7 @@ import {TokenMessengerV2} from "../../src/v2/TokenMessengerV2.sol"; import {TokenMinterV2} from "../../src/v2/TokenMinterV2.sol"; import {MessageTransmitterV2} from "../../src/v2/MessageTransmitterV2.sol"; import {AddressUtils} from "../../src/messages/v2/AddressUtils.sol"; +import {SALT_TOKEN_MESSENGER, SALT_MESSAGE_TRANSMITTER} from "./Salts.sol"; contract DeployProxiesV2Script is Script { // Expose for tests @@ -64,7 +65,7 @@ contract DeployProxiesV2Script is Script { uint256 private burnLimitPerMessage; uint256 private create2FactoryOwnerPrivateKey; - uint256 private tokenMinterV2DeployerPrivateKey; + uint256 private tokenMinterV2OwnerPrivateKey; uint256 private tokenControllerPrivateKey; function getProxyCreationCode( @@ -129,7 +130,7 @@ contract DeployProxiesV2Script is Script { address messageTransmitterV2ProxyAddress = Create2Factory(factory) .deployAndMultiCall( 0, - keccak256(type(MessageTransmitterV2).creationCode), // TODO: Verify salt + SALT_MESSAGE_TRANSMITTER, proxyCreateCode, multiCallData ); @@ -153,14 +154,13 @@ contract DeployProxiesV2Script is Script { // Calculate TokenMessengerV2 proxy address address expectedTokenMessengerV2ProxyAddress = vm.computeCreate2Address( - keccak256(type(TokenMessengerV2).creationCode), - keccak256( - proxyCreateCode - ), + SALT_TOKEN_MESSENGER, + keccak256(proxyCreateCode), factory ); - bool remoteTokenMessengerV2FromEnv = remoteTokenMessengerV2Addresses.length > 0; + bool remoteTokenMessengerV2FromEnv = remoteTokenMessengerV2Addresses + .length > 0; // Construct initializer bytes32[] memory remoteTokenMessengerAddresses = new bytes32[]( @@ -169,9 +169,13 @@ contract DeployProxiesV2Script is Script { uint256 remoteDomainsLength = remoteDomains.length; for (uint256 i = 0; i < remoteDomainsLength; ++i) { if (remoteTokenMessengerV2FromEnv) { - remoteTokenMessengerAddresses[i] = remoteTokenMessengerV2Addresses[i]; + remoteTokenMessengerAddresses[ + i + ] = remoteTokenMessengerV2Addresses[i]; } else { - remoteTokenMessengerAddresses[i] = AddressUtils.toBytes32(expectedTokenMessengerV2ProxyAddress); + remoteTokenMessengerAddresses[i] = AddressUtils.toBytes32( + expectedTokenMessengerV2ProxyAddress + ); } } bytes memory initializer = abi.encodeWithSelector( @@ -209,7 +213,7 @@ contract DeployProxiesV2Script is Script { address tokenMessengerV2ProxyAddress = Create2Factory(factory) .deployAndMultiCall( 0, - keccak256(type(TokenMessengerV2).creationCode), // TODO: Verify salt + SALT_TOKEN_MESSENGER, proxyCreateCode, multiCallData ); @@ -341,12 +345,12 @@ contract DeployProxiesV2Script is Script { burnLimitPerMessage = vm.envUint("BURN_LIMIT_PER_MESSAGE"); create2FactoryOwnerPrivateKey = vm.envUint("CREATE2_FACTORY_OWNER_KEY"); - tokenMinterV2DeployerPrivateKey = vm.envUint( - "TOKEN_MINTER_V2_DEPLOYER_KEY" - ); + tokenMinterV2OwnerPrivateKey = vm.envUint("TOKEN_MINTER_V2_OWNER_KEY"); tokenControllerPrivateKey = vm.envUint("TOKEN_CONTROLLER_KEY"); - bytes32[] memory emptyRemoteTokenMessengerV2Addresses = new bytes32[](0); + bytes32[] memory emptyRemoteTokenMessengerV2Addresses = new bytes32[]( + 0 + ); remoteTokenMessengerV2Addresses = vm.envOr( "REMOTE_TOKEN_MESSENGER_V2_ADDRESSES", ",", @@ -370,7 +374,7 @@ contract DeployProxiesV2Script is Script { ); addMessengerPauserRescuerToTokenMinterV2( - tokenMinterV2DeployerPrivateKey, + tokenMinterV2OwnerPrivateKey, tokenControllerPrivateKey, tokenMinterV2, address(tokenMessengerV2) diff --git a/scripts/v2/Salts.sol b/scripts/v2/Salts.sol new file mode 100644 index 0000000..536efee --- /dev/null +++ b/scripts/v2/Salts.sol @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Circle Internet Group, Inc. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +pragma solidity 0.7.6; + +// Salts used for CREATE2 deployments + +bytes32 constant SALT_TOKEN_MINTER = keccak256("cctp.v2.tokenminter"); +bytes32 constant SALT_TOKEN_MESSENGER = keccak256("cctp.v2.tokenmessenger"); +bytes32 constant SALT_MESSAGE_TRANSMITTER = keccak256( + "cctp.v2.messagetransmitter" +); +bytes32 constant SALT_ADDRESS_UTILS_EXTERNAL = keccak256( + "cctp.v2.addressutilsexternal" +); diff --git a/test/scripts/v2/DeployImplementationsV2.t.sol b/test/scripts/v2/DeployImplementationsV2.t.sol index 29a79d6..90f69b0 100644 --- a/test/scripts/v2/DeployImplementationsV2.t.sol +++ b/test/scripts/v2/DeployImplementationsV2.t.sol @@ -37,6 +37,7 @@ contract DeployImplementationsV2Test is ScriptV2TestUtils { // TokenMinterV2 assertEq(tokenMinterV2.tokenController(), deployer); + assertEq(tokenMinterV2.owner(), deployer); // TokenMessengerV2 assertEq( diff --git a/test/scripts/v2/DeployProxiesV2.t.sol b/test/scripts/v2/DeployProxiesV2.t.sol index 883f470..4f621bc 100644 --- a/test/scripts/v2/DeployProxiesV2.t.sol +++ b/test/scripts/v2/DeployProxiesV2.t.sol @@ -22,6 +22,7 @@ import {DeployImplementationsV2Script} from "../../../scripts/v2/DeployImplement import {DeployProxiesV2Script} from "../../../scripts/v2/DeployProxiesV2.s.sol"; import {MessageTransmitterV2} from "../../../src/v2/MessageTransmitterV2.sol"; import {TokenMessengerV2} from "../../../src/v2/TokenMessengerV2.sol"; +import {SALT_MESSAGE_TRANSMITTER, SALT_TOKEN_MESSENGER} from "../../../scripts/v2/Salts.sol"; contract DeployProxiesV2Test is ScriptV2TestUtils { DeployProxiesV2Script deployProxiesV2Script; @@ -36,7 +37,7 @@ contract DeployProxiesV2Test is ScriptV2TestUtils { function testDeployMessageTransmitterV2() public { // create2 address address predicted = create2Factory.computeAddress( - keccak256(type(MessageTransmitterV2).creationCode), + SALT_MESSAGE_TRANSMITTER, keccak256( deployProxiesV2Script.getProxyCreationCode( address(create2Factory), @@ -74,7 +75,7 @@ contract DeployProxiesV2Test is ScriptV2TestUtils { function testDeployTokenMessengerV2() public { // create2 address address predicted = create2Factory.computeAddress( - keccak256(type(TokenMessengerV2).creationCode), + SALT_TOKEN_MESSENGER, keccak256( deployProxiesV2Script.getProxyCreationCode( address(create2Factory), @@ -105,9 +106,13 @@ contract DeployProxiesV2Test is ScriptV2TestUtils { // remote token messengers for (uint256 i = 0; i < remoteDomains.length; i++) { uint32 remoteDomain = remoteDomains[i]; - bytes32 remoteTokenMessengerAddress = bytes32(uint256(uint160(address(tokenMessengerV2)))); + bytes32 remoteTokenMessengerAddress = bytes32( + uint256(uint160(address(tokenMessengerV2))) + ); if (remoteTokenMessengerV2FromEnv) { - remoteTokenMessengerAddress = bytes32(uint256(uint160(address(remoteTokenMessengerV2s[i])))); + remoteTokenMessengerAddress = bytes32( + uint256(uint160(address(remoteTokenMessengerV2s[i]))) + ); } assertEq( tokenMessengerV2.remoteTokenMessengers(remoteDomain), diff --git a/test/scripts/v2/ScriptV2TestUtils.sol b/test/scripts/v2/ScriptV2TestUtils.sol index 47ffc66..5f79885 100644 --- a/test/scripts/v2/ScriptV2TestUtils.sol +++ b/test/scripts/v2/ScriptV2TestUtils.sol @@ -32,7 +32,6 @@ contract ScriptV2TestUtils is TestUtils { uint32 _messageBodyVersion = 1; uint32 _version = 1; address token; - uint256 implDeployerPK; uint256 deployerPK; address deployer; address attester1; @@ -72,12 +71,13 @@ contract ScriptV2TestUtils is TestUtils { } function _deployImplementations() internal { - implDeployerPK = uint256(keccak256("DEPLOYTEST_IMPL_DEPLOYER_PK")); - vm.setEnv( "CREATE2_FACTORY_CONTRACT_ADDRESS", vm.toString(address(create2Factory)) ); + vm.setEnv("CREATE2_FACTORY_OWNER_KEY", vm.toString(deployerPK)); + vm.setEnv("TOKEN_MINTER_V2_OWNER_ADDRESS", vm.toString(deployer)); + vm.setEnv("TOKEN_MINTER_V2_OWNER_KEY", vm.toString(deployerPK)); vm.setEnv("TOKEN_CONTROLLER_ADDRESS", vm.toString(deployer)); vm.setEnv("DOMAIN", vm.toString(uint256(sourceDomain))); vm.setEnv( @@ -85,10 +85,6 @@ contract ScriptV2TestUtils is TestUtils { vm.toString(uint256(_messageBodyVersion)) ); vm.setEnv("VERSION", vm.toString(uint256(_version))); - vm.setEnv( - "IMPLEMENTATION_DEPLOYER_PRIVATE_KEY", - vm.toString(implDeployerPK) - ); DeployImplementationsV2Script deployImplScript = new DeployImplementationsV2Script(); deployImplScript.setUp(); @@ -167,7 +163,8 @@ contract ScriptV2TestUtils is TestUtils { ) ) ); - if (remoteTokenMessengerV2FromEnv) { // TODO: Figure out if there is a way to dynamically set this before setUp() + if (remoteTokenMessengerV2FromEnv) { + // TODO: Figure out if there is a way to dynamically set this before setUp() vm.setEnv( "REMOTE_TOKEN_MESSENGER_V2_ADDRESSES", string( @@ -251,7 +248,7 @@ contract ScriptV2TestUtils is TestUtils { ); vm.setEnv("CREATE2_FACTORY_OWNER_KEY", vm.toString(deployerPK)); - vm.setEnv("TOKEN_MINTER_V2_DEPLOYER_KEY", vm.toString(implDeployerPK)); + vm.setEnv("TOKEN_MINTER_V2_OWNER_KEY", vm.toString(deployerPK)); vm.setEnv("TOKEN_CONTROLLER_KEY", vm.toString(deployerPK)); DeployProxiesV2Script deployProxiesV2Script = new DeployProxiesV2Script(); @@ -264,9 +261,6 @@ contract ScriptV2TestUtils is TestUtils { function _setupRemoteResources() internal { vm.setEnv("TOKEN_MESSENGER_V2_OWNER_KEY", vm.toString(deployerPK)); - - // Use same TOKEN_CONTROLLER_DEPLOYER_KEY as TOKEN_CONTROLLER_KEY - vm.setEnv("TOKEN_CONTROLLER_KEY", vm.toString(deployerPK)); vm.setEnv( "TOKEN_MESSENGER_V2_CONTRACT_ADDRESS", vm.toString(address(tokenMessengerV2)) @@ -296,8 +290,7 @@ contract ScriptV2TestUtils is TestUtils { // [SKIP] Use same TOKEN_MESSENGER_CONTRACT_ADDRESS // [SKIP] Use same TOKEN_MINTER_CONTRACT_ADDRESS vm.setEnv("MESSAGE_TRANSMITTER_V2_OWNER_KEY", vm.toString(deployerPK)); - vm.setEnv("TOKEN_MESSENGER_V2_OWNER_KEY", vm.toString(deployerPK)); - vm.setEnv("TOKEN_MINTER_V2_OWNER_KEY", vm.toString(implDeployerPK)); + vm.setEnv("TOKEN_MINTER_V2_OWNER_KEY", vm.toString(deployerPK)); newOwnerPK = uint256(keccak256("ROTATEKEYSTEST_NEW_OWNER")); newOwner = vm.addr(newOwnerPK);