diff --git a/Makefile b/Makefile index 44aacd9..b508a25 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,14 @@ profile ?=default update:; forge update # Deployment helpers -deploy-local : - FOUNDRY_PROFILE=production forge script script/Deploy.s.sol --rpc-url localhost --broadcast -v +deploy: + FOUNDRY_PROFILE=production forge script script/Deploy.s.sol --skip src --skip test --rpc-url mainnet --slow --broadcast -vvv --verify -deploy-sepolia : - FOUNDRY_PROFILE=production forge script script/Deploy.s.sol --rpc-url sepolia --broadcast -vvv +deploy-sepolia: + FOUNDRY_PROFILE=production forge script script/Deploy.s.sol --skip src --skip test --rpc-url sepolia --slow --broadcast -vvv + +deploy-local: + FOUNDRY_PROFILE=production forge script script/Deploy.s.sol --skip src --skip test --rpc-url localhost --slow --broadcast -v # Run slither slither : diff --git a/script/DeployBase.sol b/script/DeployBase.sol new file mode 100644 index 0000000..857b601 --- /dev/null +++ b/script/DeployBase.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.23; + +import { ContractHelper } from "../lib/common/src/libs/ContractHelper.sol"; + +import { WrappedMToken } from "../src/WrappedMToken.sol"; +import { Proxy } from "../src/Proxy.sol"; + +contract DeployBase { + /** + * @dev Deploys Wrapped M Token. + * @param mToken_ The address the M Token contract. + * @param migrationAdmin_ The address the Migration Admin. + * @return implementation_ The address of the deployed Wrapped M Token implementation. + * @return proxy_ The address of the deployed Wrapped M Token proxy. + */ + function deploy( + address mToken_, + address migrationAdmin_ + ) public virtual returns (address implementation_, address proxy_) { + // Wrapped M token needs `mToken_` and `migrationAdmin_` addresses. + // Proxy needs `implementation_` addresses. + + implementation_ = address(new WrappedMToken(mToken_, migrationAdmin_)); + proxy_ = address(new Proxy(implementation_)); + } + + function _getExpectedWrappedMTokenImplementation( + address deployer_, + uint256 deployerNonce_ + ) internal pure returns (address) { + return ContractHelper.getContractFrom(deployer_, deployerNonce_); + } + + function getExpectedWrappedMTokenImplementation( + address deployer_, + uint256 deployerNonce_ + ) public pure virtual returns (address) { + return _getExpectedWrappedMTokenImplementation(deployer_, deployerNonce_); + } + + function _getExpectedWrappedMTokenProxy(address deployer_, uint256 deployerNonce_) internal pure returns (address) { + return ContractHelper.getContractFrom(deployer_, deployerNonce_ + 1); + } + + function getExpectedWrappedMTokenProxy( + address deployer_, + uint256 deployerNonce_ + ) public pure virtual returns (address) { + return _getExpectedWrappedMTokenProxy(deployer_, deployerNonce_); + } + + function getDeployerNonceAfterProtocolDeployment(uint256 deployerNonce_) public pure virtual returns (uint256) { + return deployerNonce_ + 2; + } +} diff --git a/script/DeployProduction.s.sol b/script/DeployProduction.s.sol new file mode 100644 index 0000000..f454350 --- /dev/null +++ b/script/DeployProduction.s.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.23; + +import { Script, console2 } from "../lib/forge-std/src/Script.sol"; + +import { DeployBase } from "./DeployBase.sol"; + +contract DeployProduction is Script, DeployBase { + error DeployerMismatch(address expected, address actual); + + error DeployerNonceTooHigh(); + + error UnexpectedDeployerNonce(); + + error CurrentNonceMismatch(uint64 expected, uint64 actual); + + error ExpectedProxyMismatch(address expected, address actual); + + error ResultingProxyMismatch(address expected, address actual); + + // NOTE: Ensure this is the correct M Token testnet/mainnet address. + address internal constant _M_TOKEN = 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b; + + // NOTE: Ensure this is the correct Migration Admin testnet/mainnet address. + address internal constant _MIGRATION_ADMIN = 0x431169728D75bd02f4053435b87D15c8d1FB2C72; + + // NOTE: Ensure this is the correct deployer testnet/mainnet to use. + address internal constant _EXPECTED_DEPLOYER = 0xF2f1ACbe0BA726fEE8d75f3E32900526874740BB; + + // NOTE: Ensure this is the correct nonce to use to deploy the Proxy on testnet/mainnet. + uint256 internal constant _DEPLOYER_PROXY_NONCE = 40; + + // NOTE: Ensure this is the correct expected testnet/mainnet address for the Proxy. + address internal constant _EXPECTED_PROXY = 0x437cc33344a0B27A429f795ff6B469C72698B291; + + function run() external { + address deployer_ = vm.rememberKey(vm.envUint("PRIVATE_KEY")); + + console2.log("Deployer:", deployer_); + + if (deployer_ != _EXPECTED_DEPLOYER) revert DeployerMismatch(_EXPECTED_DEPLOYER, deployer_); + + uint64 currentNonce_ = vm.getNonce(deployer_); + + if (currentNonce_ >= _DEPLOYER_PROXY_NONCE - 1) revert DeployerNonceTooHigh(); + + address expectedProxy_ = getExpectedWrappedMTokenProxy(deployer_, _DEPLOYER_PROXY_NONCE); + + if (expectedProxy_ != _EXPECTED_PROXY) revert ExpectedProxyMismatch(_EXPECTED_PROXY, expectedProxy_); + + vm.startBroadcast(deployer_); + + // Burn nonces until to 1 before `_DEPLOYER_PROXY_NONCE` since implementation is deployed before proxy. + while (currentNonce_ < _DEPLOYER_PROXY_NONCE - 1) { + payable(deployer_).transfer(0); + ++currentNonce_; + } + + if (currentNonce_ != vm.getNonce(deployer_)) revert CurrentNonceMismatch(currentNonce_, vm.getNonce(deployer_)); + + if (currentNonce_ != _DEPLOYER_PROXY_NONCE - 1) revert UnexpectedDeployerNonce(); + + (address implementation_, address proxy_) = deploy(_M_TOKEN, _MIGRATION_ADMIN); + + vm.stopBroadcast(); + + console2.log("Wrapped M Implementation address:", implementation_); + console2.log("Wrapped M Proxy address:", proxy_); + + if (proxy_ != _EXPECTED_PROXY) revert ResultingProxyMismatch(_EXPECTED_PROXY, proxy_); + } +} diff --git a/src/WrappedMToken.sol b/src/WrappedMToken.sol index bb1a3d5..8073ecc 100644 --- a/src/WrappedMToken.sol +++ b/src/WrappedMToken.sol @@ -17,7 +17,6 @@ import { Migratable } from "./Migratable.sol"; /* - ██╗ ██╗██████╗ █████╗ ██████╗ ██████╗ ███████╗██████╗ ███╗ ███╗ ████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗ ██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗ ████╗ ████║ ╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║ ██║ █╗ ██║██████╔╝███████║██████╔╝██████╔╝█████╗ ██║ ██║ ██╔████╔██║ ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║ diff --git a/test/integration/Deploy.t.sol b/test/integration/Deploy.t.sol new file mode 100644 index 0000000..79aebfc --- /dev/null +++ b/test/integration/Deploy.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.23; + +import { Test } from "../../lib/forge-std/src/Test.sol"; + +import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; +import { IMTokenLike } from "../../src/interfaces/IMTokenLike.sol"; +import { IRegistrarLike } from "../../src/interfaces/IRegistrarLike.sol"; + +import { DeployBase } from "../../script/DeployBase.sol"; + +contract Deploy is Test, DeployBase { + address internal constant _TTG_VAULT = 0xdeaDDeADDEaDdeaDdEAddEADDEAdDeadDEADDEaD; + + address internal constant _M_TOKEN = 0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b; + address internal constant _MIGRATION_ADMIN = 0x431169728D75bd02f4053435b87D15c8d1FB2C72; + address internal constant _DEPLOYER = 0xF2f1ACbe0BA726fEE8d75f3E32900526874740BB; + uint256 internal constant _DEPLOYER_PROXY_NONCE = 40; + address internal constant _EXPECTED_PROXY = 0x437cc33344a0B27A429f795ff6B469C72698B291; + + function test_deploy() external { + // Set nonce to 1 before `_DEPLOYER_PROXY_NONCE` since implementation is deployed before proxy. + vm.setNonce(_DEPLOYER, uint64(_DEPLOYER_PROXY_NONCE) - 1); + + vm.startPrank(_DEPLOYER); + (address implementation_, address proxy_) = deploy(_M_TOKEN, _MIGRATION_ADMIN); + vm.stopPrank(); + + // Wrapped M Token Implementation assertions + assertEq(implementation_, getExpectedWrappedMTokenImplementation(_DEPLOYER, 39)); + assertEq(IWrappedMToken(implementation_).migrationAdmin(), _MIGRATION_ADMIN); + assertEq(IWrappedMToken(implementation_).mToken(), _M_TOKEN); + assertEq(IWrappedMToken(implementation_).registrar(), IMTokenLike(_M_TOKEN).ttgRegistrar()); + assertEq(IWrappedMToken(implementation_).vault(), IRegistrarLike(IMTokenLike(_M_TOKEN).ttgRegistrar()).vault()); + + // // Wrapped M Token Proxy assertions + assertEq(proxy_, getExpectedWrappedMTokenProxy(_DEPLOYER, 39)); + assertEq(proxy_, _EXPECTED_PROXY); + assertEq(IWrappedMToken(proxy_).migrationAdmin(), _MIGRATION_ADMIN); + assertEq(IWrappedMToken(proxy_).mToken(), _M_TOKEN); + assertEq(IWrappedMToken(proxy_).registrar(), IMTokenLike(_M_TOKEN).ttgRegistrar()); + assertEq(IWrappedMToken(proxy_).vault(), IRegistrarLike(IMTokenLike(_M_TOKEN).ttgRegistrar()).vault()); + } +} diff --git a/test/integration/Protocol.t.sol b/test/integration/Protocol.t.sol index c84ac7c..823ab4a 100644 --- a/test/integration/Protocol.t.sol +++ b/test/integration/Protocol.t.sol @@ -560,7 +560,7 @@ contract ProtocolIntegrationTests is TestBase { } function testFuzz_full(uint256 seed_) external { - vm.skip(false); + vm.skip(true); for (uint256 index_; index_ < _accounts.length; ++index_) { _giveM(_accounts[index_], 100_000e6);