From a30969dbfce5e88a01f38cda6ddee63de3e3e70d Mon Sep 17 00:00:00 2001 From: TuDo1403 Date: Fri, 3 Jan 2025 18:08:35 +0700 Subject: [PATCH] script: update script and post check --- foundry.toml | 2 +- remappings.txt | 6 +- .../01_Deploy_PauseEnforcer_And_Gateway.s.sol | 64 +- .../02_Propose_Upgrades.s.sol | 87 +- .../Migrate_Assets_Base.s.sol | 72 +- .../Migrate_Assets_FullFlow.s.sol | 84 +- .../config/config.testnet.json | 37 + .../AssetMigration_PostChecker.s.sol | 100 +++ ...teway_DepositAndWithdraw_AfterRestrict.sol | 848 ++++++++++++++++++ script/Migration.s.sol | 8 +- script/PostChecker.sol | 10 +- .../contracts/RoninBridgeManagerDeploy.s.sol | 2 +- script/post-check/BasePostCheck.s.sol | 37 +- soldeer.lock | 8 +- 14 files changed, 1182 insertions(+), 183 deletions(-) create mode 100644 script/20241217-migrate-assets/config/config.testnet.json create mode 100644 script/20241217-migrate-assets/postcheck/AssetMigration_PostChecker.s.sol create mode 100644 script/20241217-migrate-assets/postcheck/PostCheck_Gateway_DepositAndWithdraw_AfterRestrict.sol diff --git a/foundry.toml b/foundry.toml index 6b248fba..eb2ade83 100644 --- a/foundry.toml +++ b/foundry.toml @@ -69,7 +69,7 @@ ronin-mainnet = "https://api-archived.roninchain.com/rpc" ronin-testnet = "https://saigon-archive.roninchain.com/rpc" [dependencies] -"@fdk" = { version = "0.3.1-beta", url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.1-beta.zip" } +"@fdk" = { version = "0.3.5-rc", url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.5-rc.zip" } "@openzeppelin" = { version = "4.7.3", url = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v4.7.3.zip" } "@prb-math" = "4.0.3" "@prb-test" = "0.6.5" diff --git a/remappings.txt b/remappings.txt index 702ba929..4da2bc81 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,9 +1,9 @@ -forge-std/=dependencies/@fdk-0.3.1-beta/dependencies/@forge-std-1.9.1/src/ +forge-std/=dependencies/@fdk-0.3.5-rc/dependencies/forge-std-1.9.5/src/ @openzeppelin/=dependencies/@openzeppelin-4.7.3/ hardhat/=node_modules/hardhat/ @ronin/contracts/=src/ @ronin/test/=test/ -solady/=dependencies/@fdk-0.3.1-beta/dependencies/@solady-0.0.228/src/ -@fdk=dependencies/@fdk-0.3.1-beta/script/ +solady/=dependencies/@fdk-0.3.5-rc/dependencies/solady-0.0.228/src/ +@fdk=dependencies/@fdk-0.3.5-rc/script/ @prb/math/=lib/prb-math/ @prb/test/=dependencies/@prb-test-0.6.5/src/ \ No newline at end of file diff --git a/script/20241217-migrate-assets/01_Deploy_PauseEnforcer_And_Gateway.s.sol b/script/20241217-migrate-assets/01_Deploy_PauseEnforcer_And_Gateway.s.sol index 735ed230..61c26ddd 100644 --- a/script/20241217-migrate-assets/01_Deploy_PauseEnforcer_And_Gateway.s.sol +++ b/script/20241217-migrate-assets/01_Deploy_PauseEnforcer_And_Gateway.s.sol @@ -17,18 +17,28 @@ import { DefaultNetwork } from "@fdk/utils/DefaultNetwork.sol"; contract Migration_01_Deploy_PauseEnforcer_And_Gateway is Migrate_Assets_Base { using LibCompanionNetwork for *; - address private constant _PRV_RON_PAUSE_ENFORCER = 0x2367cD5468c2b3cD18aA74AdB7e14E43426aF837; - address private constant _PRV_ETH_PAUSE_ENFORCER = 0xe514d9DEB7966c8BE0ca922de8a064264eA6bcd4; - address private _ronPauseEnforcer; address private _ronGatewayV3Logic; address private _ethPauseEnforcer; address private _ethGatewayV3Logic; - function run() public virtual { + function run() public virtual override { + super.run(); + + MigrateConfig memory ronCfg = ronConfig(); + MigrateConfig memory ethCfg = ethConfig(); + + require(ronCfg.prevPauseEnforcer != address(0), "[Ronin] prevPauseEnforcer is required"); + require(ronCfg.newPauseEnforcer == address(0), "[Ronin] newPauseEnforcer must be empty"); + require(ronCfg.newGatewayLogic == address(0), "[Ronin] newGatewayLogic must be empty"); + + require(ethCfg.prevPauseEnforcer != address(0), "[Ethereum] prevPauseEnforcer is required"); + require(ethCfg.newPauseEnforcer == address(0), "[Ethereum] newPauseEnforcer must be empty"); + require(ethCfg.newGatewayLogic == address(0), "[Ethereum] newGatewayLogic must be empty"); + address ronGw = loadContract(Contract.RoninGatewayV3.key()); - (address[] memory admins, address[] memory sentries) = _getAllSentriesFromPreviousPauseEnforcer(_PRV_RON_PAUSE_ENFORCER); + (address[] memory admins, address[] memory sentries) = _getAllSentriesFromPreviousPauseEnforcer(ronCfg.prevPauseEnforcer); _ronPauseEnforcer = _deployImmutable(Contract.RoninPauseEnforcer.key(), abi.encode(ronGw, admins, sentries)); _ronGatewayV3Logic = _deployLogic(Contract.RoninGatewayV3.key()); @@ -38,12 +48,30 @@ contract Migration_01_Deploy_PauseEnforcer_And_Gateway is Migrate_Assets_Base { (TNetwork prvNetwork, uint256 prvForkId) = switchTo(companionNetwork); address ethGw = loadContract(Contract.MainchainGatewayV3.key()); - (admins, sentries) = _getAllSentriesFromPreviousPauseEnforcer(_PRV_ETH_PAUSE_ENFORCER); + (admins, sentries) = _getAllSentriesFromPreviousPauseEnforcer(ethCfg.prevPauseEnforcer); _ethPauseEnforcer = _deployImmutable(Contract.MainchainPauseEnforcer.key(), abi.encode(ethGw, admins, sentries)); _ethGatewayV3Logic = _deployLogic(Contract.MainchainGatewayV3.key()); switchBack(prvNetwork, prvForkId); + + _saveToConfig(); + } + + function _saveToConfig() internal { + string memory path = configPath(); + + require(_ronPauseEnforcer != address(0), "Ronin Pause Enforcer is not deployed"); + require(_ronGatewayV3Logic != address(0), "Ronin Gateway Logic is not deployed"); + + require(_ethPauseEnforcer != address(0), "Ethereum Pause Enforcer is not deployed"); + require(_ethGatewayV3Logic != address(0), "Ethereum Gateway Logic is not deployed"); + + vm.writeJson(vm.toString(_ronPauseEnforcer), path, ".ronin.newPauseEnforcer"); + vm.writeJson(vm.toString(_ronGatewayV3Logic), path, ".ronin.newGatewayLogic"); + + vm.writeJson(vm.toString(_ethPauseEnforcer), path, ".ethereum.newPauseEnforcer"); + vm.writeJson(vm.toString(_ethGatewayV3Logic), path, ".ethereum.newGatewayLogic"); } function _getAllSentriesFromPreviousPauseEnforcer( @@ -60,28 +88,4 @@ contract Migration_01_Deploy_PauseEnforcer_And_Gateway is Migrate_Assets_Base { sentries[i] = AccessControlEnumerable(pauseEnforcer).getRoleMember(keccak256("SENTRY_ROLE"), i); } } - - function _getRoninMigratorAddress() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getEthereumMigratorAddress() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getRoninGatewayV3Logic() internal view virtual override returns (address) { - return _ronGatewayV3Logic; - } - - function _getMainchainGatewayV3Logic() internal view virtual override returns (address) { - return _ethGatewayV3Logic; - } - - function _getRoninPauseEnforcer() internal view virtual override returns (address) { - return _ronPauseEnforcer; - } - - function _getEthereumPauseEnforcer() internal view virtual override returns (address) { - return _ethPauseEnforcer; - } } diff --git a/script/20241217-migrate-assets/02_Propose_Upgrades.s.sol b/script/20241217-migrate-assets/02_Propose_Upgrades.s.sol index 1e54a0c1..6330933f 100644 --- a/script/20241217-migrate-assets/02_Propose_Upgrades.s.sol +++ b/script/20241217-migrate-assets/02_Propose_Upgrades.s.sol @@ -38,39 +38,9 @@ contract Migration_02_Propose_Upgrades is Migrate_Assets_Base { IRoninBridgeManager internal _ronBM; IMainchainBridgeManager internal _ethBM; - function _getProposalExecutor() internal view virtual override returns (address) { - return address(0); - } - - function _getProposalProposer() internal view virtual override returns (address) { - return _ronBM.getGovernors()[0]; - } - - function _getRoninMigratorAddress() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getEthereumMigratorAddress() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getRoninGatewayV3Logic() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getMainchainGatewayV3Logic() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getRoninPauseEnforcer() internal view virtual override returns (address) { - revert("Not implemented"); - } - - function _getEthereumPauseEnforcer() internal view virtual override returns (address) { - revert("Not implemented"); - } + function run() public virtual override { + super.run(); - function run() public virtual { (_companionChainId, _companionNetwork) = network().companionNetworkData(); _expiry = block.timestamp + _DEFAULT_EXPIRY_DURATION; @@ -87,19 +57,27 @@ contract Migration_02_Propose_Upgrades is Migrate_Assets_Base { uint256[] memory values = new uint256[](1); uint256[] memory gasAmounts = new uint256[](1); + MigrateConfig memory cfg = ethConfig(); + (address[] memory tokens, address[] memory recipients, uint64[] memory remoteChainSelectors) = toWhitelistData(cfg.whitelistInfos); + targets[0] = vme.getAddress(_companionNetwork, Contract.MainchainGatewayV3.key()); callDatas[0] = abi.encodeCall( ITransparentUpgradeableProxyV2.upgradeToAndCall, - (_getMainchainGatewayV3Logic(), abi.encodeWithSignature("initializeV5(address,address)", _getEthereumMigratorAddress(), _getEthereumPauseEnforcer())) + ( + cfg.newGatewayLogic, + abi.encodeWithSignature( + "initializeV5(address,address,address[],address[],uint64[])", cfg.migrator, cfg.newPauseEnforcer, tokens, recipients, remoteChainSelectors + ) + ) ); values[0] = 0; gasAmounts[0] = 1_000_000; LibProposal.verifyMainchainProposalGasAmount(_companionNetwork, address(_ethBM), targets, values, callDatas, gasAmounts); - vm.broadcast(_getProposalProposer()); + vm.broadcast(cfg.proposer); vm.recordLogs(); - _ronBM.propose(_companionChainId, _expiry, _getProposalExecutor(), targets, values, callDatas, gasAmounts); + _ronBM.propose(_companionChainId, _expiry, cfg.executor, targets, values, callDatas, gasAmounts); Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); for (uint256 i; i < recordedLogs.length; ++i) { if (recordedLogs[i].emitter == address(_ronBM) && recordedLogs[i].topics[0] == IRoninBridgeManager.ProposalCreated.selector) { @@ -117,13 +95,16 @@ contract Migration_02_Propose_Upgrades is Migrate_Assets_Base { uint256[] memory values = new uint256[](1); uint256[] memory gasAmounts = new uint256[](1); + MigrateConfig memory cfg = ronConfig(); + (address[] memory tokens, address[] memory recipients, uint64[] memory remoteChainSelectors) = toWhitelistData(cfg.whitelistInfos); + targets[0] = gw; callDatas[0] = abi.encodeCall( ITransparentUpgradeableProxyV2.upgradeToAndCall, ( - _getRoninGatewayV3Logic(), + cfg.newGatewayLogic, abi.encodeWithSignature( - "initializeV4(address,address,address)", loadContract(Contract.WRON.key()), _getRoninMigratorAddress(), _getRoninPauseEnforcer() + "initializeV4(address,address,address[],address[],uint64[])", cfg.migrator, cfg.newPauseEnforcer, tokens, recipients, remoteChainSelectors ) ) ); @@ -132,18 +113,25 @@ contract Migration_02_Propose_Upgrades is Migrate_Assets_Base { uint256 nonce = _ronBM.round(block.chainid) + 1; _ronProposal = LibProposal.createProposal(address(_ronBM), nonce, _expiry, targets, values, callDatas, gasAmounts); - _ronProposal.executor = _getProposalExecutor(); + _ronProposal.executor = cfg.executor; + + vm.broadcast(cfg.proposer); + _ronBM.propose(block.chainid, _expiry, cfg.executor, targets, values, callDatas, gasAmounts); + } - vm.broadcast(_getProposalProposer()); - _ronBM.propose(block.chainid, _expiry, _getProposalExecutor(), targets, values, callDatas, gasAmounts); + function postCheck() external { + _postCheck(); } function _postCheck() internal virtual override { // Simulate voting for the Ronin proposal LibProposal.voteFor(_ronBM, _ronProposal); + if (_ronProposal.executor != address(0)) { + vm.prank(_ronProposal.executor); + _ronBM.execute(_ronProposal); + } // Simulate voting for the Ethereum proposal - uint256 ronSnapshotId = vm.snapshot(); genMockBOs(address(_ronBM)); overrideMockBOs(address(_ronBM)); @@ -151,28 +139,25 @@ contract Migration_02_Propose_Upgrades is Migrate_Assets_Base { (TNetwork prvNetwork, uint256 prvForkId) = switchTo(_companionNetwork); - uint256 ethSnapshotId = vm.snapshot(); - overrideMockBOs(address(_ethBM)); + MigrateConfig memory cfg = ethConfig(); + // Cheat re-add executor as bridge operator since we assigned executor as bridge operator in the proposal address[] memory ops = new address[](1); ops[0] = makeAddr("cheat-re-added-sm-bo"); uint96[] memory vws = new uint96[](1); vws[0] = 1; address[] memory gvs = new address[](1); - gvs[0] = _getProposalExecutor(); - // SkyMavis Gnosis Safe - vm.prank(0x51F6696Ae42C6C40CA9F5955EcA2aaaB1Cefb26e); + gvs[0] = cfg.executor; + + address pa = LibProxy.getProxyAdmin(address(_ethBM)); + vm.prank(pa); ITransparentUpgradeableProxyV2(address(_ethBM)).functionDelegateCall(abi.encodeCall(IBridgeManager.addBridgeOperators, (vws, gvs, ops))); - vm.prank(_getProposalExecutor()); + vm.prank(cfg.executor); _ethBM.relayProposal(_ethProposal, new Ballot.VoteType[](sigs.length), sigs); - vm.revertTo(ethSnapshotId); - switchBack(prvNetwork, prvForkId); - - vm.revertTo(ronSnapshotId); } function genMockBOs( diff --git a/script/20241217-migrate-assets/Migrate_Assets_Base.s.sol b/script/20241217-migrate-assets/Migrate_Assets_Base.s.sol index d6ca0c1c..2f64743b 100644 --- a/script/20241217-migrate-assets/Migrate_Assets_Base.s.sol +++ b/script/20241217-migrate-assets/Migrate_Assets_Base.s.sol @@ -2,25 +2,77 @@ pragma solidity ^0.8.23; import { Migration } from "script/Migration.s.sol"; +import { Network } from "script/utils/Network.sol"; +import { DefaultNetwork } from "@fdk/utils/DefaultNetwork.sol"; abstract contract Migrate_Assets_Base is Migration { - function _getProposalExecutor() internal view virtual returns (address) { - return address(0); + struct WhitelistInfo { + address recipient; + uint64 remoteChainSelector; + address token; } - function _getProposalProposer() internal view virtual returns (address) { - return address(0); + struct MigrateConfig { + address executor; + address migrator; + address newGatewayLogic; + address newPauseEnforcer; + address prevPauseEnforcer; + address proposer; + WhitelistInfo[] whitelistInfos; } - function _getRoninMigratorAddress() internal view virtual returns (address); + string private constant CONFIG_PATH = "script/20241217-migrate-assets/config/config"; - function _getEthereumMigratorAddress() internal view virtual returns (address); + function run() public virtual { + vm.makePersistent(address(this)); + validateConfig(ronConfig()); + validateConfig(ethConfig()); + } + + function configPath() public view returns (string memory) { + if (network() == DefaultNetwork.RoninMainnet.key() || network() == Network.EthMainnet.key()) { + return string.concat(CONFIG_PATH, ".mainnet.json"); + } else if (network() == DefaultNetwork.RoninTestnet.key() || network() == Network.Sepolia.key()) { + return string.concat(CONFIG_PATH, ".testnet.json"); + } else { + revert("Unsupported network"); + } + } + + function validateConfig( + MigrateConfig memory cfg + ) public pure { + require(cfg.executor != address(0), "executor is required"); + require(cfg.migrator != address(0), "migrator is required"); + require(cfg.proposer != address(0), "proposer is required"); + require(cfg.whitelistInfos.length > 0, "whitelistInfos is required"); + } - function _getRoninGatewayV3Logic() internal view virtual returns (address); + function ronConfig() public view returns (MigrateConfig memory) { + return this.parseConfig(vm.readFile(configPath()), ".ronin"); + } - function _getMainchainGatewayV3Logic() internal view virtual returns (address); + function ethConfig() public view returns (MigrateConfig memory) { + return this.parseConfig(vm.readFile(configPath()), ".ethereum"); + } - function _getRoninPauseEnforcer() internal view virtual returns (address); + function parseConfig(string memory json, string memory key) external pure returns (MigrateConfig memory) { + MigrateConfig memory parsed = abi.decode(vm.parseJson(json, key), (MigrateConfig)); + return parsed; + } - function _getEthereumPauseEnforcer() internal view virtual returns (address); + function toWhitelistData( + WhitelistInfo[] memory info + ) public pure returns (address[] memory tokens, address[] memory recipients, uint64[] memory remoteChainSelectors) { + tokens = new address[](info.length); + recipients = new address[](info.length); + remoteChainSelectors = new uint64[](info.length); + + for (uint256 i; i < info.length; ++i) { + tokens[i] = info[i].token; + recipients[i] = info[i].recipient; + remoteChainSelectors[i] = info[i].remoteChainSelector; + } + } } diff --git a/script/20241217-migrate-assets/Migrate_Assets_FullFlow.s.sol b/script/20241217-migrate-assets/Migrate_Assets_FullFlow.s.sol index ede330c3..8ede3b52 100644 --- a/script/20241217-migrate-assets/Migrate_Assets_FullFlow.s.sol +++ b/script/20241217-migrate-assets/Migrate_Assets_FullFlow.s.sol @@ -3,85 +3,27 @@ pragma solidity ^0.8.23; import { Migration } from "script/Migration.s.sol"; +import { AssetMigration_PostChecker } from "script/20241217-migrate-assets/postcheck/AssetMigration_PostChecker.s.sol"; import { Migrate_Assets_Base } from "script/20241217-migrate-assets/Migrate_Assets_Base.s.sol"; import { Migration_01_Deploy_PauseEnforcer_And_Gateway } from "script/20241217-migrate-assets/01_Deploy_PauseEnforcer_And_Gateway.s.sol"; import { Migration_02_Propose_Upgrades } from "script/20241217-migrate-assets/02_Propose_Upgrades.s.sol"; -contract Migrate_Assets_FullFlow is Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades { - function run() public virtual override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) { - Migration_01_Deploy_PauseEnforcer_And_Gateway.run(); - Migration_02_Propose_Upgrades.run(); - } - - function _getRoninMigratorAddress() - internal - view - virtual - override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) - returns (address) - { - return address(0x1); - } +contract Migrate_Assets_FullFlow is Migration { + Migration_01_Deploy_PauseEnforcer_And_Gateway public migration_01; + Migration_02_Propose_Upgrades public migration_02; - function _getEthereumMigratorAddress() - internal - view - virtual - override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) - returns (address) - { - return address(0x2); - } + function run() public virtual { + migration_01 = new Migration_01_Deploy_PauseEnforcer_And_Gateway(); + migration_01.run(); - function _getProposalProposer() internal view virtual override(Migrate_Assets_Base, Migration_02_Propose_Upgrades) returns (address) { - return Migration_02_Propose_Upgrades._getProposalProposer(); + migration_02 = new Migration_02_Propose_Upgrades(); + migration_02.run(); } - function _getProposalExecutor() internal view virtual override(Migrate_Assets_Base, Migration_02_Propose_Upgrades) returns (address) { - return address(0x4); - } - - function _getRoninPauseEnforcer() - internal - view - virtual - override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) - returns (address) - { - return Migration_01_Deploy_PauseEnforcer_And_Gateway._getRoninPauseEnforcer(); - } - - function _getEthereumPauseEnforcer() - internal - view - virtual - override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) - returns (address) - { - return Migration_01_Deploy_PauseEnforcer_And_Gateway._getEthereumPauseEnforcer(); - } - - function _getRoninGatewayV3Logic() - internal - view - virtual - override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) - returns (address) - { - return Migration_01_Deploy_PauseEnforcer_And_Gateway._getRoninGatewayV3Logic(); - } - - function _getMainchainGatewayV3Logic() - internal - view - virtual - override(Migration_01_Deploy_PauseEnforcer_And_Gateway, Migration_02_Propose_Upgrades) - returns (address) - { - return Migration_01_Deploy_PauseEnforcer_And_Gateway._getMainchainGatewayV3Logic(); - } + function _postCheck() internal virtual override { + AssetMigration_PostChecker assetMigration_PostChecker = new AssetMigration_PostChecker(); - function _postCheck() internal virtual override(Migration, Migration_02_Propose_Upgrades) { - Migration_02_Propose_Upgrades._postCheck(); + migration_02.postCheck(); + assetMigration_PostChecker.run(); } } diff --git a/script/20241217-migrate-assets/config/config.testnet.json b/script/20241217-migrate-assets/config/config.testnet.json new file mode 100644 index 00000000..4eefde7e --- /dev/null +++ b/script/20241217-migrate-assets/config/config.testnet.json @@ -0,0 +1,37 @@ +{ + "ethereum": { + "executor": "0xd24D87DDc1917165435b306aAC68D99e0F49A3Fa", + "migrator": "0x0597db786f6aB39634aAe2B33cE0AA556C1D54e5", + "newGatewayLogic": "0x9dd1B885ECef3Aa1F0b820aAC94faF32b338e4c9", + "newPauseEnforcer": "0xe91Ab627c8465F6F3C2278Af01e950Df761f8173", + "prevPauseEnforcer": "0x61eC0ebf966AE84C414BDA715E17CeF657e039DF", + "proposer": "0xd24D87DDc1917165435b306aAC68D99e0F49A3Fa", + "whitelistInfos": [ + { + "recipient": "0x0000000000000000000000000000000000000000", + "remoteChainSelector": 13116810400804392105, + "token": "0x1Aa1BC6BaEFCF09D6Fd0139B828b5E764D050F08" + }, + { + "recipient": "0x0000000000000000000000000000000000000000", + "remoteChainSelector": 13116810400804392105, + "token": "0xc65DEC9c627e636E32E0c84BC08C30395f2dc4AD" + } + ] + }, + "ronin": { + "executor": "0xd24D87DDc1917165435b306aAC68D99e0F49A3Fa", + "migrator": "0x0597db786f6aB39634aAe2B33cE0AA556C1D54e5", + "newGatewayLogic": "0x2387b3383E89c164781d173B7Aa14d9c46eD2642", + "newPauseEnforcer": "0x9B137463d4E7986D7f535f9B79e28b4EF1938E9b", + "prevPauseEnforcer": "0x1aD54D61F47acBcBA99fb6540A1694EB2F47AB95", + "proposer": "0xd24D87DDc1917165435b306aAC68D99e0F49A3Fa", + "whitelistInfos": [ + { + "recipient": "0x0000000000000000000000000000000000000000", + "remoteChainSelector": 16015286601757825753, + "token": "0x82f5483623d636bc3deba8ae67e1751b6cf2bad2" + } + ] + } +} \ No newline at end of file diff --git a/script/20241217-migrate-assets/postcheck/AssetMigration_PostChecker.s.sol b/script/20241217-migrate-assets/postcheck/AssetMigration_PostChecker.s.sol new file mode 100644 index 00000000..4988885b --- /dev/null +++ b/script/20241217-migrate-assets/postcheck/AssetMigration_PostChecker.s.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import { PostCheck_Gateway_Quorum } from "script/post-check/gateway/quorum/PostCheck_Gateway_Quorum.s.sol"; +import { PostCheck_BridgeManager } from "script/post-check/manager/PostCheck_BridgeManager.s.sol"; +import { PostCheck_Gateway_DepositAndWithdraw_AfterRestrict } from + "script/20241217-migrate-assets/postcheck/PostCheck_Gateway_DepositAndWithdraw_AfterRestrict.sol"; + +import { console } from "forge-std/console.sol"; + +import { Migration } from "script/Migration.s.sol"; +import { BaseMigration } from "@fdk/BaseMigration.s.sol"; +import { TContract } from "@fdk/types/TContract.sol"; +import { DefaultContract } from "@fdk/utils/DefaultContract.sol"; +import { DefaultNetwork } from "@fdk/utils/DefaultNetwork.sol"; +import { TNetwork } from "@fdk/types/TNetwork.sol"; +import { Network } from "script/utils/Network.sol"; +import { Contract } from "script/utils/Contract.sol"; +import { ProxyInterface } from "@fdk/libraries/LibDeploy.sol"; +import { LibProxy } from "@fdk/libraries/LibProxy.sol"; +import { ScriptExtended } from "@fdk/extensions/ScriptExtended.s.sol"; +import { IRuntimeConfig } from "@fdk/interfaces/configs/IRuntimeConfig.sol"; +import { LibCompanionNetwork } from "script/shared/libraries/LibCompanionNetwork.sol"; + +contract AssetMigration_PostChecker is Migration, PostCheck_BridgeManager, PostCheck_Gateway_Quorum, PostCheck_Gateway_DepositAndWithdraw_AfterRestrict { + using LibCompanionNetwork for *; + + function run() external { + vm.makePersistent(address(this)); + IRuntimeConfig.Option memory opt; + opt = CONFIG.getRuntimeConfig(); + _originForkBlockNumber = opt.forkBlockNumber; + + _loadSysContract(); + + _validate_Gateway_DepositAndWithdraw(); + } + + function _deployLogic( + TContract contractType + ) internal virtual override(BaseMigration, Migration) returns (address payable logic) { + return super._deployLogic(contractType); + } + + function _upgradeCallback( + address proxy, + address logic, + uint256 callValue, + bytes memory callData, + ProxyInterface proxyInterface + ) internal virtual override(BaseMigration, Migration) { + super._upgradeCallback(proxy, logic, callValue, callData, proxyInterface); + } + + function _postCheck() internal virtual override(ScriptExtended, Migration) { + super._postCheck(); + } + + function _getProxyAdmin() internal virtual override(BaseMigration, Migration) returns (address payable) { + return super._getProxyAdmin(); + } + + function _deployProxy(TContract contractType, bytes memory args) internal virtual override(BaseMigration, Migration) returns (address payable) { + return super._deployProxy(contractType, args); + } + + function _loadSysContract() private { + TNetwork currentNetwork = network(); + if ( + currentNetwork == DefaultNetwork.RoninMainnet.key() || currentNetwork == DefaultNetwork.RoninTestnet.key() || currentNetwork == Network.RoninDevnet.key() + || currentNetwork == DefaultNetwork.LocalHost.key() + ) { + _loadRoninContracts(); + + (, TNetwork companionNetwork) = currentNetwork.companionNetworkData(); + ethGW = CONFIG.getAddress(companionNetwork, Contract.MainchainGatewayV3.key()); + ethBM = CONFIG.getAddress(companionNetwork, Contract.MainchainBridgeManager.key()); + } else { + ethGW = loadContract(Contract.MainchainGatewayV3.key()); + ethBM = loadContract(Contract.MainchainBridgeManager.key()); + + console.log("Mainchain Bridge Manager Logic:", LibProxy.getProxyImplementation(ethBM)); + (, TNetwork companionNetwork) = currentNetwork.companionNetworkData(); + + uint256 originForkBlockNumber = config.getRuntimeConfig().forkBlockNumber; + uint256 originForkId = config.getForkId(companionNetwork, originForkBlockNumber); + config.switchTo(originForkId); + + _loadRoninContracts(); + } + } + + function _loadRoninContracts() private { + brSl = loadContract(Contract.BridgeSlash.key()); + brRw = loadContract(Contract.BridgeReward.key()); + ronGW = loadContract(Contract.RoninGatewayV3.key()); + brTk = loadContract(Contract.BridgeTracking.key()); + ronBM = loadContract(Contract.RoninBridgeManager.key()); + } +} diff --git a/script/20241217-migrate-assets/postcheck/PostCheck_Gateway_DepositAndWithdraw_AfterRestrict.sol b/script/20241217-migrate-assets/postcheck/PostCheck_Gateway_DepositAndWithdraw_AfterRestrict.sol new file mode 100644 index 00000000..80446856 --- /dev/null +++ b/script/20241217-migrate-assets/postcheck/PostCheck_Gateway_DepositAndWithdraw_AfterRestrict.sol @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { BasePostCheck } from "script/post-check/BasePostCheck.s.sol"; +import { MockERC20 } from "src/mocks/token/MockERC20.sol"; +import { MockERC721 } from "src/mocks/token/MockERC721.sol"; +import { Contract } from "script/utils/Contract.sol"; +import { TokenStandard } from "src/libraries/LibTokenInfo.sol"; +import { Transfer as LibTransfer } from "src/libraries/Transfer.sol"; +import { TNetwork } from "@fdk/types/TNetwork.sol"; +import { DefaultNetwork } from "@fdk/utils/DefaultNetwork.sol"; +import { LibProxy } from "@fdk/libraries/LibProxy.sol"; +import { LibCompanionNetwork } from "script/shared/libraries/LibCompanionNetwork.sol"; +import { IBridgeManager } from "src/interfaces/bridge/IBridgeManager.sol"; +import { LibArray } from "script/shared/libraries/LibArray.sol"; +import { IRoninGatewayV3 } from "src/interfaces/IRoninGatewayV3.sol"; +import { IRoninBridgeManager } from "script/interfaces/IRoninBridgeManager.sol"; +import { IMainchainBridgeManager } from "script/interfaces/IMainchainBridgeManager.sol"; +import { IWETH } from "src/interfaces/IWETH.sol"; +import { IMainchainGatewayV3 } from "src/interfaces/IMainchainGatewayV3.sol"; +import { IQuorum } from "src/interfaces/IQuorum.sol"; +import { ITransparentUpgradeableProxyV2 } from "script/interfaces/ITransparentUpgradeableProxyV2.sol"; +import { IHasContracts } from "src/interfaces/collections/IHasContracts.sol"; +import { ContractType } from "src/utils/ContractType.sol"; +import { FunctionRestrictable } from "src/extensions/FunctionRestrictable.sol"; + +abstract contract PostCheck_Gateway_DepositAndWithdraw_AfterRestrict is BasePostCheck { + using LibProxy for *; + using LibArray for *; + using LibCompanionNetwork for *; + using LibTransfer for LibTransfer.Request; + using LibTransfer for LibTransfer.Receipt; + + address private user = makeAddr("user"); + uint256 private quantity; + + LibTransfer.Request depositReq; + LibTransfer.Request withdrawReq; + + MockERC20 private ronERC20; + MockERC721 private ronERC721; + MockERC20 private ethERC20; + MockERC721 private ethERC721; + + address[] private ronTokens = new address[](2); + address[] private ethTokens = new address[](2); + TokenStandard[] private standards = [TokenStandard.ERC20, TokenStandard.ERC721]; + + uint256 private ronChainId; + uint256 private ethChainId; + + IWETH private ethWETH; + MockERC20 private ronWETH; + + TNetwork private currNetwork; + TNetwork private companionNetwork; + + function _setUp() private onlyOnRoninNetworkOrLocal { + console.log("RoninBridgeManager", ronBM); + console.log("MainchainBridgeManager", ethBM); + + console.log("RoninGateway", ronGW); + console.log("MainchainGateway", ethGW); + + _setUpOnRonin(); + _setUpOnMainchain(); + _mapTokenRonin(); + _mapTokenMainchain(); + } + + function _mapTokenRonin() private { + uint256[] memory chainIds = new uint256[](2); + chainIds[0] = network().companionChainId(); + chainIds[1] = chainIds[0]; + + address admin = ronGW.getProxyAdmin(); + console.log("Admin for ronin gateway", admin); + + vm.prank(admin); + ITransparentUpgradeableProxyV2(ronGW).functionDelegateCall(abi.encodeCall(IRoninGatewayV3.mapTokens, (ronTokens, ethTokens, chainIds, standards))); + } + + function _mapTokenMainchain() private { + (, companionNetwork) = network().companionNetworkData(); + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + uint256[][4] memory thresholds; + thresholds[0] = new uint256[](2); + thresholds[0][0] = 200_000_000 ether; + thresholds[1] = new uint256[](2); + thresholds[1][0] = 800_000_000 ether; + thresholds[2] = new uint256[](2); + thresholds[2][0] = 10; + thresholds[3] = new uint256[](2); + thresholds[3][0] = 500_000_000 ether; + + console.log("Mainchain Gateway", ethGW); + address admin = ethGW.getProxyAdmin(); + console.log("Admin", admin); + + vm.prank(admin); + ITransparentUpgradeableProxyV2(ethGW).functionDelegateCall( + abi.encodeCall(IMainchainGatewayV3.mapTokensAndThresholds, (ethTokens, ronTokens, standards, thresholds)) + ); + + switchBack(prevNetwork, prevForkId); + } + + function _setUpOnRonin() private { + ronERC20 = new MockERC20("RoninERC20", "RERC20"); + ronERC721 = new MockERC721("RoninERC721", "RERC721"); + + ronTokens[0] = address(ronERC20); + ronTokens[1] = address(ronERC721); + + ronChainId = block.chainid; + currNetwork = network(); + + vm.deal(user, 10 ether); + deal(address(ronERC20), user, 1000 ether); + + ronWETH = MockERC20(loadContract(Contract.WETH.key())); + } + + function _setUpOnMainchain() private { + (, companionNetwork) = network().companionNetworkData(); + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + + ethChainId = block.chainid; + gwDomainHash = IMainchainGatewayV3(ethGW).DOMAIN_SEPARATOR(); + + ethERC20 = new MockERC20("MainchainERC20", "MERC20"); + ethERC721 = new MockERC721("MainchainERC721", "MERC721"); + + ethTokens[0] = address(ethERC20); + ethTokens[1] = address(ethERC721); + + ethWETH = IWETH(loadContract(Contract.WETH.key())); + + vm.deal(user, 10 ether); + deal(address(ethERC20), user, 1000 ether); + + switchBack(prevNetwork, prevForkId); + } + + function _validate_Gateway_DepositAndWithdraw() internal { + _setUp(); + + validate_Can_RequestWithdraw_ERC721_RoninGateway_And_CanWithdraw_In_MainchainGateway(); + validate_Can_RequestDeposit_ERC721_MainchainGateway_And_OperatorCanDeposit_In_RoninGateway(); + validate_RevertIf_Deposit_WETH_MainchainGateway_ButOperatorCanDeposit_In_RoninGateway(); + // validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20(); + validate_RevertIf_RequestWithdraw_ETH_RoninGateway_But_CanWithdraw_In_MainchainGateway(); + validate_Gateway_WETHAddressUnchanged(); + // validate_Gateway_Deposit_ETH(); + // validate_Gateway_RevertIf_InsufficientSentValue_Deposit_ETH(); + + // validate_Gateway_RevertIf_DuplicatedSigs_Withdraw_ERC20(); + // validate_Gateway_RevertIf_UnsortedSigs_Withdraw_ERC20(); + validate_HasBridgeManager(); + // validate_Gateway_Deposit_ERC20(); + // validate_Gateway_RevertIf_InsufficientThreshold_Deposit_ERC20(); + // validate_Gateway_Withdraw_ERC20(); + // validate_Gateway_RevertIf_InvalidSignature_Withdraw_ERC20(); + // validate_Gateway_RevertIf_InsufficientThreshold_Withdraw_ERC20(); + } + + function validate_Can_RequestDeposit_ERC721_MainchainGateway_And_OperatorCanDeposit_In_RoninGateway() + private + onPostCheck("validate_Can_RequestDeposit_ERC721_MainchainGateway_And_OperatorCanDeposit_In_RoninGateway") + onlyOnRoninNetworkOrLocal + { + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(ethERC721); + depositReq.info.erc = TokenStandard.ERC721; + depositReq.info.id = 1; + + ethERC721.mint(user, depositReq.info.id); + vm.prank(user); + ethERC721.approve(ethGW, depositReq.info.id); + + vm.prank(user); + vm.recordLogs(); + IMainchainGatewayV3(ethGW).requestDepositFor(depositReq); + + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); + + vm.revertTo(snapshotId); + + switchBack(prevNetwork, prevForkId); + + ronERC721.mint(address(ronGW), depositReq.info.id); + + overrideMockBOs(ronBM); + + uint256 minSigRequired = _calcMinSigOrVoteRequired(ronBM, ronGW); + assertTrue(minSigRequired > 1, "Invalid test setup"); + + for (uint256 i; i < minSigRequired; ++i) { + vm.prank(mockOps[i]); + IRoninGatewayV3(ronGW).depositFor(receipt); + } + } + + function validate_Can_RequestWithdraw_ERC721_RoninGateway_And_CanWithdraw_In_MainchainGateway() + private + onPostCheck("validate_Can_RequestWithdraw_ERC721_RoninGateway_And_CanWithdraw_In_MainchainGateway") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronERC721); + withdrawReq.info.erc = TokenStandard.ERC721; + withdrawReq.info.id = 1; + + ronERC721.mint(user, withdrawReq.info.id); + vm.prank(user); + ronERC721.approve(ronGW, withdrawReq.info.id); + vm.prank(user); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + overrideMockBOs(ethBM); + + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM, ethGW); + assertTrue(minSigRequired > 1, "Invalid test setup"); + + Signature[] memory sigs = _bulkSignReceipt(mockOps.slice(0, minSigRequired), receiptDigest); + ethERC721.mint(address(ethGW), withdrawReq.info.id); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_RevertIf_RequestWithdraw_ETH_RoninGateway_But_CanWithdraw_In_MainchainGateway() + private + onPostCheck("validate_RevertIf_RequestWithdraw_ETH_RoninGateway_But_CanWithdraw_In_MainchainGateway") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronWETH); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + if (network() == DefaultNetwork.RoninTestnet.key()) { + withdrawReq.info.quantity = 0.00009 ether; // Low tier 0.0001 + } + + deal(address(ronWETH), user, withdrawReq.info.quantity); + vm.prank(user); + ronWETH.approve(ronGW, withdrawReq.info.quantity); + vm.prank(user); + vm.expectRevert(abi.encodeWithSelector(FunctionRestrictable.ErrRestricted.selector, IRoninGatewayV3.requestWithdrawalFor.selector, TokenStandard.ERC20)); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + // Un-restrict + address admin = ronGW.getProxyAdmin(); + uint256 snapshotId = vm.snapshot(); + vm.prank(admin); + ITransparentUpgradeableProxyV2(ronGW).functionDelegateCall( + abi.encodeCall(FunctionRestrictable.restrict, (IRoninGatewayV3.requestWithdrawalFor.selector, 0)) + ); + + vm.prank(user); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 ethSnapshotId = vm.snapshot(); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + overrideMockBOs(ethBM); + + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM, ethGW); + assertTrue(minSigRequired > 1, "Invalid test setup"); + + Signature[] memory sigs = _bulkSignReceipt(mockOps.slice(0, minSigRequired), receiptDigest); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + vm.revertTo(ethSnapshotId); + + switchBack(prevNetwork, prevForkId); + vm.revertTo(snapshotId); + } + + function validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20() + private + onPostCheck("validate_Gateway_RevertIf_OperatorsRenounced_InsufficientThreshold_Withdraw_ERC20") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronERC20); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + vm.prank(user); + ronERC20.approve(ronGW, withdrawReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + overrideMockBOs(ethBM); + + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM, ethGW); + uint256 unmetSigCount = minSigRequired - 1; + console.log("Unmet sig count", unmetSigCount); + assertTrue(unmetSigCount >= 1, "Invalid test setup"); + + // Sign first to get renounced operator signatures + Signature[] memory sigs = _bulkSignReceipt(mockOps, receiptDigest); + + // Cache to be renounced operators + address[] memory gvsToRemove = mockOps.slice(unmetSigCount, mockOps.length); + + uint256 reAddOpCount = mockOps.length - unmetSigCount; + + mockOps = mockOps.slice(0, unmetSigCount); + mockGvs = mockGvs.slice(0, unmetSigCount); + + address[] memory newOps = new address[](reAddOpCount); + address[] memory newGvs = new address[](reAddOpCount); + uint96[] memory newVWs = new uint96[](reAddOpCount); + + for (uint256 i; i < reAddOpCount; ++i) { + uint256 opPK; + uint256 gvPK; + + (newOps[i], opPK) = makeAddrAndKey(string.concat("new-op", vm.toString(i))); + (newGvs[i], gvPK) = makeAddrAndKey(string.concat("new-gv", vm.toString(i))); + newVWs[i] = 100; + + mockOps.push(newOps[i]); + mockGvs.push(newGvs[i]); + + vm.rememberKey(opPK); + vm.rememberKey(gvPK); + } + + // Re-add new operators + vm.prank(address(ethBM)); + ITransparentUpgradeableProxyV2(ethBM).functionDelegateCall(abi.encodeCall(IBridgeManager.addBridgeOperators, (newVWs, newGvs, newOps))); + + // Renounce operators + vm.prank(address(ethBM)); + ITransparentUpgradeableProxyV2(ethBM).functionDelegateCall(abi.encodeCall(IBridgeManager.removeBridgeOperators, (gvsToRemove))); + + assertEq(IMainchainBridgeManager(ethBM).totalBridgeOperator(), 4, "Invalid total BOs"); + + vm.expectRevert(); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_RevertIf_InsufficientSentValue_Deposit_ETH() + private + onPostCheck("validate_Gateway_RevertIf_InsufficientSentValue_Deposit_ETH") + onlyOnRoninNetworkOrLocal + { + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(0x0); + depositReq.info.erc = TokenStandard.ERC20; + depositReq.info.id = 0; + depositReq.info.quantity = 1 ether; + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + vm.deal(user, depositReq.info.quantity); + vm.prank(user); + vm.expectRevert(); + IMainchainGatewayV3(ethGW).requestDepositFor{ value: depositReq.info.quantity - 1 }(depositReq); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_RevertIf_Deposit_WETH_MainchainGateway_ButOperatorCanDeposit_In_RoninGateway() + private + onPostCheck("validate_RevertIf_Deposit_WETH_MainchainGateway_ButOperatorCanDeposit_In_RoninGateway") + onlyOnRoninNetworkOrLocal + { + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(ethWETH); + depositReq.info.erc = TokenStandard.ERC20; + depositReq.info.id = 0; + depositReq.info.quantity = 1 ether; + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + deal(address(ethWETH), user, depositReq.info.quantity); + + vm.prank(user); + ethWETH.approve(ethGW, depositReq.info.quantity); + + uint256 balBefore = ethGW.balance; + + vm.prank(user); + vm.recordLogs(); + vm.expectRevert(abi.encodeWithSelector(FunctionRestrictable.ErrRestricted.selector, IMainchainGatewayV3.requestDepositFor.selector, TokenStandard.ERC20)); + IMainchainGatewayV3(ethGW).requestDepositFor(depositReq); + + // Un-restrict + address admin = ethGW.getProxyAdmin(); + vm.prank(admin); + ITransparentUpgradeableProxyV2(ethGW).functionDelegateCall( + abi.encodeCall(FunctionRestrictable.restrict, (IMainchainGatewayV3.requestDepositFor.selector, 0)) + ); + + vm.prank(user); + vm.recordLogs(); + IMainchainGatewayV3(ethGW).requestDepositFor(depositReq); + + uint256 balAfter = address(ethGW).balance; + assertEq(balAfter - balBefore, depositReq.info.quantity, "ETH should be deposited"); + + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + + overrideMockBOs(ronBM); + + uint256 minVoteRequired = _calcMinSigOrVoteRequired(ronBM, ronGW); + assertTrue(minVoteRequired > 1, "Invalid test setup"); + + for (uint256 i; i < minVoteRequired; ++i) { + vm.prank(mockOps[i]); + IRoninGatewayV3(ronGW).depositFor(receipt); + } + } + + function validate_Gateway_Deposit_ETH() private onPostCheck("validate_Gateway_Deposit_ETH") onlyOnRoninNetworkOrLocal { + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(0x0); + depositReq.info.erc = TokenStandard.ERC20; + depositReq.info.id = 0; + depositReq.info.quantity = 1 ether; + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + vm.deal(user, depositReq.info.quantity); + vm.prank(user); + + uint256 balBefore = address(ethGW).balance; + + vm.recordLogs(); + IMainchainGatewayV3(ethGW).requestDepositFor{ value: depositReq.info.quantity }(depositReq); + + uint256 balAfter = address(ethGW).balance; + assertEq(balAfter - balBefore, depositReq.info.quantity, "ETH should be deposited"); + + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + + overrideMockBOs(ronBM); + + uint256 minVoteRequired = _calcMinSigOrVoteRequired(ronBM, ronGW); + assertTrue(minVoteRequired > 1, "Invalid test setup"); + + for (uint256 i; i < minVoteRequired; ++i) { + vm.prank(mockOps[i]); + IRoninGatewayV3(ronGW).depositFor(receipt); + } + } + + function validate_Gateway_WETHAddressUnchanged() private onPostCheck("validate_Gateway_WETHAddressUnchanged") onlyOnRoninNetworkOrLocal { + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + assertEq(address(ethWETH), address(IMainchainGatewayV3(ethGW).wrappedNativeToken()), "WETH address should not change"); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_RevertIf_DuplicatedSigs_Withdraw_ERC20() + private + onPostCheck("validate_Gateway_RevertIf_DuplicatedSigs_Withdraw_ERC20") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronERC20); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + vm.prank(user); + ronERC20.approve(ronGW, withdrawReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + overrideMockBOs(ethBM); + + address[] memory bos = new address[](2); + + bos[0] = mockOps[0]; + bos[1] = mockOps[1]; + + console.log("GW min vote weight:", IQuorum(ethGW).minimumVoteWeight()); + assertEq(IMainchainBridgeManager(ethBM).totalBridgeOperator(), 4, "Invalid total BOs"); + + Signature[] memory sigs = _bulkSignReceipt(bos.extend(mockOps), receiptDigest); + + vm.expectRevert(); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_RevertIf_UnsortedSigs_Withdraw_ERC20() + private + onPostCheck("validate_Gateway_RevertIf_UnsortedSigs_Withdraw_ERC20") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronERC20); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + vm.prank(user); + ronERC20.approve(ronGW, withdrawReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + overrideMockBOs(ethBM); + + Signature[] memory sigs = new Signature[](mockOps.length); + + for (uint256 i; i < mockOps.length; ++i) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(mockOps[i], receiptDigest); + sigs[i] = Signature(v, r, s); + } + + if (uint160(mockOps[0]) < uint160(mockOps[1])) { + // Swap if sorted + Signature memory temp = sigs[0]; + sigs[0] = sigs[1]; + sigs[1] = temp; + + // Swap the mock ops + address tempOp = mockOps[0]; + mockOps[0] = mockOps[1]; + mockOps[1] = tempOp; + + // Swap the mock gvs + address tempGv = mockGvs[0]; + mockGvs[0] = mockGvs[1]; + mockGvs[1] = tempGv; + } + + console.log("GW min vote weight:", IQuorum(ethGW).minimumVoteWeight()); + assertEq(IMainchainBridgeManager(ethBM).totalBridgeOperator(), 4, "Invalid total BOs"); + + vm.expectRevert(); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_HasBridgeManager() private onPostCheck("validate_HasBridgeManager") onlyOnRoninNetworkOrLocal { + assertEq(ronBM.getProxyAdmin(), ronBM, "Invalid ProxyAdmin in RoninBridgeManager, expected self"); + assertEq(IHasContracts(ronGW).getContract(ContractType.BRIDGE_MANAGER), ronBM, "Invalid RoninBridgeManager in ronGW"); + assertEq(IHasContracts(brTk).getContract(ContractType.BRIDGE_MANAGER), ronBM, "Invalid RoninBridgeManager in bridgeTracking"); + assertEq(IHasContracts(brRw).getContract(ContractType.BRIDGE_MANAGER), ronBM, "Invalid RoninBridgeManager in bridgeReward"); + assertEq(IHasContracts(brSl).getContract(ContractType.BRIDGE_MANAGER), ronBM, "Invalid RoninBridgeManager in bridgeSlash"); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + assertEq(ethBM.getProxyAdmin(), ethBM, "Invalid ProxyAdmin in MainchainBridgeManager, expected self"); + assertEq(IHasContracts(ethGW).getContract(ContractType.BRIDGE_MANAGER), ethBM, "Invalid MainchainBridgeManager in ethGW"); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_Deposit_ERC20() private onPostCheck("validate_Gateway_Deposit_ERC20") onlyOnRoninNetworkOrLocal { + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(ethERC20); + depositReq.info.erc = TokenStandard.ERC20; + depositReq.info.id = 0; + depositReq.info.quantity = 1 ether; + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + vm.prank(user); + ethERC20.approve(ethGW, depositReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IMainchainGatewayV3(ethGW).requestDepositFor(depositReq); + + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + + overrideMockBOs(ronBM); + + uint256 minVoteRequired = _calcMinSigOrVoteRequired(ronBM, ronGW); + assertTrue(minVoteRequired > 1, "Invalid test setup"); + + for (uint256 i; i < minVoteRequired; ++i) { + vm.prank(mockOps[i]); + IRoninGatewayV3(ronGW).depositFor(receipt); + } + } + + function validate_Gateway_RevertIf_InsufficientThreshold_Deposit_ERC20() + private + onPostCheck("validate_Gateway_RevertIf_InsufficientThreshold_Deposit_ERC20") + onlyOnRoninNetworkOrLocal + { + depositReq.recipientAddr = makeAddr("ronin-recipient"); + depositReq.tokenAddr = address(ethERC20); + depositReq.info.erc = TokenStandard.ERC20; + depositReq.info.id = 0; + depositReq.info.quantity = 1 ether; + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + vm.prank(user); + ethERC20.approve(ethGW, depositReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IMainchainGatewayV3(ethGW).requestDepositFor(depositReq); + + (LibTransfer.Receipt memory receipt,) = _getReceiptHash(ethGW, IMainchainGatewayV3.DepositRequested.selector); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + + overrideMockBOs(ronBM); + + uint256 minVW = IQuorum(ronGW).minimumVoteWeight(); + uint256 defaultVW = IBridgeManager(ronBM).getTotalWeight() / IBridgeManager(ronBM).totalBridgeOperator(); + uint256 minVoteRequired = minVW / defaultVW + 1; + uint256 unmetVoteCount = minVoteRequired - 1; + assertTrue(unmetVoteCount > 1, "Invalid test setup"); + + for (uint256 i; i < unmetVoteCount; ++i) { + vm.prank(mockOps[i]); + IRoninGatewayV3(ronGW).depositFor(receipt); + } + } + + function validate_Gateway_RevertIf_InvalidSignature_Withdraw_ERC20() + private + onPostCheck("validate_Gateway_RevertIf_InvalidSignature_Withdraw_ERC20") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("malicious-recipient"); + withdrawReq.tokenAddr = address(ronERC20); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + vm.prank(user); + ronERC20.approve(ronGW, withdrawReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + (address invalidSigner, uint256 invalidPK) = makeAddrAndKey(string.concat("invalid-signer-", vm.toString(vm.unixTime()))); + vm.rememberKey(invalidPK); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(invalidSigner, receiptDigest); + Signature[] memory sigs = new Signature[](1); + sigs[0] = Signature(v, r, s); + + vm.expectRevert(); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_RevertIf_InsufficientThreshold_Withdraw_ERC20() + private + onPostCheck("validate_Gateway_RevertIf_InsufficientThreshold_Withdraw_ERC20") + onlyOnRoninNetworkOrLocal + { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronERC20); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + vm.prank(user); + ronERC20.approve(ronGW, withdrawReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + overrideMockBOs(ethBM); + + uint256 minSigRequired = _calcMinSigOrVoteRequired(ethBM, ethGW); + uint256 unmetSigCount = minSigRequired - 1; + assertTrue(unmetSigCount >= 1, "Invalid test setup"); + + Signature[] memory sigs = _bulkSignReceipt(mockOps, receiptDigest); + + assembly { + mstore(sigs, unmetSigCount) + } + + vm.expectRevert(); + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function validate_Gateway_Withdraw_ERC20() private onPostCheck("validate_Gateway_Withdraw_ERC20") onlyOnRoninNetworkOrLocal { + withdrawReq.recipientAddr = makeAddr("mainchain-recipient"); + withdrawReq.tokenAddr = address(ronERC20); + withdrawReq.info.erc = TokenStandard.ERC20; + withdrawReq.info.id = 0; + withdrawReq.info.quantity = 1 ether; + + vm.prank(user); + ronERC20.approve(ronGW, withdrawReq.info.quantity); + + vm.prank(user); + vm.recordLogs(); + IRoninGatewayV3(ronGW).requestWithdrawalFor(withdrawReq, ethChainId); + + (LibTransfer.Receipt memory receipt, bytes32 receiptHash) = _getReceiptHash(ronGW, IRoninGatewayV3.WithdrawalRequested.selector); + + bytes32 receiptDigest = LibTransfer.receiptDigest(gwDomainHash, receiptHash); + + (TNetwork prevNetwork, uint256 prevForkId) = switchTo(companionNetwork); + uint256 snapshotId = vm.snapshot(); + + overrideMockBOs(ethBM); + Signature[] memory sigs = _bulkSignReceipt(mockOps, receiptDigest); + + IMainchainGatewayV3(ethGW).submitWithdrawal(receipt, sigs); + + vm.revertTo(snapshotId); + switchBack(prevNetwork, prevForkId); + } + + function _calcMinSigOrVoteRequired(address bm, address gw) private view returns (uint256 minVoteOrSig) { + uint256 minVW = IQuorum(gw).minimumVoteWeight(); + uint256 defaultVW = IBridgeManager(bm).getTotalWeight() / IBridgeManager(bm).totalBridgeOperator(); + minVoteOrSig = (minVW / defaultVW) + 1; + } + + function _bulkSignReceipt(address[] memory signers, bytes32 receiptDigest) private pure returns (Signature[] memory sigs) { + LibArray.inplaceAscSort(signers); + + sigs = new Signature[](signers.length); + + for (uint256 i; i < signers.length; ++i) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signers[i], receiptDigest); + sigs[i] = Signature(v, r, s); + } + } + + function _concat(Signature[] memory a, Signature[] memory b) private pure returns (Signature[] memory c) { + c = new Signature[](a.length + b.length); + + for (uint256 i; i < a.length; ++i) { + c[i] = a[i]; + } + + for (uint256 i; i < b.length; ++i) { + c[a.length + i] = b[i]; + } + } + + function _getReceiptHash(address emitter, bytes32 eventTopic) private returns (LibTransfer.Receipt memory receipt, bytes32 receiptHash) { + Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); + + for (uint256 i; i < recordedLogs.length; ++i) { + if (recordedLogs[i].emitter == emitter && recordedLogs[i].topics[0] == eventTopic) { + (receiptHash, receipt) = abi.decode(recordedLogs[i].data, (bytes32, LibTransfer.Receipt)); + } + } + } +} diff --git a/script/Migration.s.sol b/script/Migration.s.sol index 183855f6..0409e977 100644 --- a/script/Migration.s.sol +++ b/script/Migration.s.sol @@ -254,13 +254,13 @@ contract Migration is BaseMigration, Utils, SignatureConsumer { } // @dev Called by `Migration._upgradeProxy()` - function upgradeCallback( + function _upgradeCallback( address proxy, address logic, uint256, /* callValue */ bytes memory args, ProxyInterface /* proxyInterface */ - ) public virtual override { + ) internal virtual override { if (!config.isPostChecking() && logic.codehash == proxy.getProxyImplementation({ nullCheck: true }).codehash) { console.log("BaseMigration: Logic is already upgraded!".yellow()); return; @@ -332,7 +332,7 @@ contract Migration is BaseMigration, Utils, SignatureConsumer { address proxyAdmin = _getProxyAdmin(); assertTrue(proxyAdmin != address(0x0), "BaseMigration: Null ProxyAdmin"); - deployed = LibDeploy.deployTransparentProxyV2({ + deployed = LibDeploy.deployTransparentProxy({ implInfo: DeployInfo({ callValue: 0, by: nominatedSender, @@ -366,7 +366,7 @@ contract Migration is BaseMigration, Utils, SignatureConsumer { address proxyAdmin = _getProxyAdmin(); assertTrue(proxyAdmin != address(0x0), "BaseMigration: Null ProxyAdmin"); - deployed = LibDeploy.deployTransparentProxyV2({ + deployed = LibDeploy.deployTransparentProxy({ implInfo: DeployInfo({ callValue: 0, by: sender(), diff --git a/script/PostChecker.sol b/script/PostChecker.sol index 3231bd2b..e9e93fdd 100644 --- a/script/PostChecker.sol +++ b/script/PostChecker.sol @@ -28,18 +28,20 @@ contract PostChecker is Migration, PostCheck_BridgeManager, PostCheck_Gateway { _validate_BridgeManager(); } - function _deployLogic(TContract contractType) internal virtual override(BaseMigration, Migration) returns (address payable logic) { + function _deployLogic( + TContract contractType + ) internal virtual override(BaseMigration, Migration) returns (address payable logic) { return super._deployLogic(contractType); } - function upgradeCallback( + function _upgradeCallback( address proxy, address logic, uint256 callValue, bytes memory callData, ProxyInterface proxyInterface - ) public virtual override(BaseMigration, Migration) { - super.upgradeCallback(proxy, logic, callValue, callData, proxyInterface); + ) internal virtual override(BaseMigration, Migration) { + super._upgradeCallback(proxy, logic, callValue, callData, proxyInterface); } function _postCheck() internal virtual override(ScriptExtended, Migration) { diff --git a/script/contracts/RoninBridgeManagerDeploy.s.sol b/script/contracts/RoninBridgeManagerDeploy.s.sol index a5986181..63f5e0ef 100644 --- a/script/contracts/RoninBridgeManagerDeploy.s.sol +++ b/script/contracts/RoninBridgeManagerDeploy.s.sol @@ -54,7 +54,7 @@ contract RoninBridgeManagerDeploy is Migration { callData: EMPTY_ARGS, proxyInterface: ProxyInterface.Transparent, shouldPrompt: false, - upgradeCallback: this.upgradeCallback, + upgradeCallback: _upgradeCallback, shouldUseCallback: true }).upgrade(); diff --git a/script/post-check/BasePostCheck.s.sol b/script/post-check/BasePostCheck.s.sol index 03e9ce52..fa8ce1b2 100644 --- a/script/post-check/BasePostCheck.s.sol +++ b/script/post-check/BasePostCheck.s.sol @@ -38,7 +38,9 @@ abstract contract BasePostCheck is BaseMigration, SignatureConsumer { bytes32 internal gwDomainHash; - modifier onPostCheck(string memory postCheckLabel) { + modifier onPostCheck( + string memory postCheckLabel + ) { uint256 snapshotId = _beforePostCheck(postCheckLabel); _; _afterPostCheck(postCheckLabel, snapshotId); @@ -53,7 +55,9 @@ abstract contract BasePostCheck is BaseMigration, SignatureConsumer { _; } - function cheatAddOverWeightedGovernor(address bm) internal { + function cheatAddOverWeightedGovernor( + address bm + ) internal { uint256 totalWeight; try IBridgeManager(bm).getTotalWeight() returns (uint256 res) { totalWeight = res; @@ -88,7 +92,9 @@ abstract contract BasePostCheck is BaseMigration, SignatureConsumer { } } - function overrideMockBOs(address bm) internal { + function overrideMockBOs( + address bm + ) internal { uint256 boCount = IBridgeManager(bm).totalBridgeOperator(); address[] memory bos = IBridgeManager(bm).getBridgeOperators(); uint96[] memory vws = new uint96[](boCount); @@ -133,6 +139,27 @@ abstract contract BasePostCheck is BaseMigration, SignatureConsumer { deal(token, to, give, false); } + function dealERC721(address token, address to, uint256 id) internal virtual { + // check if token id is already minted and the actual owner. + (bool successMinted, bytes memory ownerData) = token.staticcall(abi.encodeWithSelector(0x6352211e, id)); + require(successMinted, "StdCheats deal(address,address,uint,bool): id not minted."); + + // get owner current balance + (, bytes memory fromBalData) = token.staticcall(abi.encodeWithSelector(0x70a08231, abi.decode(ownerData, (address)))); + uint256 fromPrevBal = abi.decode(fromBalData, (uint256)); + + // get new user current balance + (, bytes memory toBalData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to)); + uint256 toPrevBal = abi.decode(toBalData, (uint256)); + + // update balances + stdstore.target(token).sig(0x70a08231).with_key(abi.decode(ownerData, (address))).checked_write(--fromPrevBal); + stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(++toPrevBal); + + // update owner + stdstore.target(token).sig(0x6352211e).with_key(id).checked_write(to); + } + function deal(address token, address to, uint256 give, bool adjust) internal virtual { // get current balance (, bytes memory balData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to)); @@ -154,7 +181,9 @@ abstract contract BasePostCheck is BaseMigration, SignatureConsumer { } } - function _beforePostCheck(string memory postCheckLabel) private returns (uint256 snapshotId) { + function _beforePostCheck( + string memory postCheckLabel + ) private returns (uint256 snapshotId) { snapshotId = vm.snapshot(); console.log("\n> ".cyan(), postCheckLabel.blue().italic(), "..."); } diff --git a/soldeer.lock b/soldeer.lock index 5b800a46..897bf359 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,9 +1,9 @@ [[dependencies]] name = "@fdk" -version = "0.3.1-beta" -url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.1-beta.zip" -checksum = "53cb5bf15abdc909d177c64e78070387af24ef39b2a4b408651836ac9de059c4" -integrity = "112ec6a82b06c190e027635779b052ca4fb4ee534c42407ea73a5009fd160d53" +version = "0.3.5-rc" +url = "https://github.com/axieinfinity/foundry-deployment-kit/archive/refs/tags/v0.3.5-rc.zip" +checksum = "1b2a5c4cb7836463d821d79483d45594fcab2f96f0200641da52d14ccdd6ccbc" +integrity = "a9a42316943b53d68a2dd135906632a264175585a0d4f9f50927228061a35b82" [[dependencies]] name = "@openzeppelin"