From 834d1f1f1c780beacec2f656d4952b2deb16b364 Mon Sep 17 00:00:00 2001 From: denalimarsh Date: Mon, 22 Jun 2026 12:02:55 +0200 Subject: [PATCH 1/2] fix(deploy): sort earners before migrator construction --- script/DeployBase.sol | 18 ++++++++++ script/EarnersAddresses.sol | 34 +++++++++---------- test/integration/TestBase.sol | 46 ++++++++------------------ test/integration/Upgrade.t.sol | 19 ----------- test/unit/ListOfEarnersToMigrate.t.sol | 18 ++++++++++ 5 files changed, 67 insertions(+), 68 deletions(-) diff --git a/script/DeployBase.sol b/script/DeployBase.sol index 4ca252c..61494a2 100644 --- a/script/DeployBase.sol +++ b/script/DeployBase.sol @@ -82,6 +82,8 @@ contract DeployBase { address excessDestination_, UpgradeRoles memory roles_ ) internal returns (address migrator_) { + earners_ = _sortAddresses(earners_); + return address( new WrappedMTokenMigratorV1( @@ -97,6 +99,22 @@ contract DeployBase { ); } + function _sortAddresses(address[] memory addresses_) internal pure returns (address[] memory) { + for (uint256 i_ = 1; i_ < addresses_.length; ++i_) { + address key_ = addresses_[i_]; + uint256 j_ = i_; + + while (j_ > 0 && uint160(addresses_[j_ - 1]) > uint160(key_)) { + addresses_[j_] = addresses_[j_ - 1]; + --j_; + } + + addresses_[j_] = key_; + } + + return addresses_; + } + /** * @dev Mock deploys Wrapped M Token, returning the would-be addresses. * @param deployer_ The address of the deployer. diff --git a/script/EarnersAddresses.sol b/script/EarnersAddresses.sol index a138dcf..2d0cb9a 100644 --- a/script/EarnersAddresses.sol +++ b/script/EarnersAddresses.sol @@ -17,30 +17,30 @@ library EarnersAddresses { } function getEthereumEarners() internal pure returns (address[] memory) { address[24] memory earners = [ - 0x4Cbc25559DbBD1272EC5B64c7b5F48a2405e6470, - 0xfF95c5f35F4ffB9d5f596F898ac1ae38D62749c2, 0x0502d65f26f45d17503E4d34441F5e73Ea143033, - 0x970A7749EcAA4394C8B2Bf5F2471F41FD6b79288, + 0x13Ccb6E28F22E2f6783BaDedCe32cc74583A3647, + 0x48Afe17cB6363fD1aaeA50a8CB652C5978972c96, + 0x4Cbc25559DbBD1272EC5B64c7b5F48a2405e6470, + 0x569D7dccBF6923350521ecBC28A555A500c4f0Ec, + 0x7db685961F97c847A4C815D43E9cc0E5647328b9, 0x81ad394C0Fa87e99Ca46E1aca093BEe020f203f4, - 0xfE940BFE535013a52e8e2DF9644f95E3C94fa14B, - 0xE0663f2372cAa1459b7ade90812Dc737CE587FA6, - 0xa969cFCd9e583edb8c8B270Dc8CaFB33d6Cf662D, + 0x970A7749EcAA4394C8B2Bf5F2471F41FD6b79288, + 0x985DE23260743c2c2f09BFdeC50b048C7a18c461, 0x9c6e67fA86138Ab49359F595BfE4Fb163D0f16cc, - 0xdd82875f0840AAD58a455A70B88eEd9F59ceC7c7, + 0x9F6d1a62bf268Aa05a1218CFc89C69833D2d2a70, + 0xa969cFCd9e583edb8c8B270Dc8CaFB33d6Cf662D, + 0xB50A1f651A5ACb2679c8f679D782c728f3702E53, 0xB65a66621D7dE34afec9b9AC0755133051550dD7, + 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb, 0xcAD001c30E96765aC90307669d578219D4fb1DCe, + 0xCF3166181848eEC4Fd3b9046aE7CB582F34d2e6c, + 0xdd82875f0840AAD58a455A70B88eEd9F59ceC7c7, 0xDeD796De6a14E255487191963dEe436c45995813, - 0xea0C048c728578b1510EBDF9b692E8936D6Fbc90, - 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb, - 0x13Ccb6E28F22E2f6783BaDedCe32cc74583A3647, - 0x985DE23260743c2c2f09BFdeC50b048C7a18c461, - 0x7db685961F97c847A4C815D43E9cc0E5647328b9, - 0x48Afe17cB6363fD1aaeA50a8CB652C5978972c96, + 0xE0663f2372cAa1459b7ade90812Dc737CE587FA6, 0xE72Fe64840F4EF80E3Ec73a1c749491b5c938CB9, - 0xCF3166181848eEC4Fd3b9046aE7CB582F34d2e6c, - 0xB50A1f651A5ACb2679c8f679D782c728f3702E53, - 0x9F6d1a62bf268Aa05a1218CFc89C69833D2d2a70, - 0x569D7dccBF6923350521ecBC28A555A500c4f0Ec + 0xea0C048c728578b1510EBDF9b692E8936D6Fbc90, + 0xfE940BFE535013a52e8e2DF9644f95E3C94fa14B, + 0xfF95c5f35F4ffB9d5f596F898ac1ae38D62749c2 ]; address[] memory result = new address[](24); for (uint256 i = 0; i < 24; ++i) { diff --git a/test/integration/TestBase.sol b/test/integration/TestBase.sol index 286b318..55bb9e8 100644 --- a/test/integration/TestBase.sol +++ b/test/integration/TestBase.sol @@ -12,13 +12,14 @@ import { Proxy } from "../../lib/common/src/Proxy.sol"; import { IWrappedMToken } from "../../src/interfaces/IWrappedMToken.sol"; import { WrappedMToken } from "../../src/WrappedMToken.sol"; -import { WrappedMTokenMigratorV1 } from "../../src/WrappedMTokenMigratorV1.sol"; + +import { DeployBase } from "../../script/DeployBase.sol"; import { IMTokenLike, IRegistrarLike, ISwapFacilityLike } from "./vendor/protocol/Interfaces.sol"; import { MockSwapFacility } from "../utils/Mocks.sol"; -contract TestBase is Test { +contract TestBase is Test, DeployBase { uint256 public mainnetFork; IMTokenLike internal constant _mToken = IMTokenLike(0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b); @@ -192,22 +193,6 @@ contract TestBase is Test { _set(keccak256(abi.encode(_CLAIM_OVERRIDE_RECIPIENT_PREFIX, account_)), bytes32(uint256(uint160(recipient_)))); } - function _sortAddresses(address[] memory addresses_) internal pure returns (address[] memory) { - for (uint256 i_ = 1; i_ < addresses_.length; ++i_) { - address key_ = addresses_[i_]; - uint256 j_ = i_; - - while (j_ > 0 && uint160(addresses_[j_ - 1]) > uint160(key_)) { - addresses_[j_] = addresses_[j_ - 1]; - --j_; - } - - addresses_[j_] = key_; - } - - return addresses_; - } - function _deployV2Components() internal { _wrappedMTokenImplementationV2 = address( new WrappedMToken(address(_mToken), _registrar, _swapFacility, _migrationAdmin) @@ -219,20 +204,17 @@ contract TestBase is Test { earners_[index_] = _earners[index_]; } - // NOTE: `ListOfEarnersToMigrate` requires strictly ascending addresses, as the production script emits. - earners_ = _sortAddresses(earners_); - - _wrappedMTokenMigratorV1 = address( - new WrappedMTokenMigratorV1( - _wrappedMTokenImplementationV2, - earners_, - _admin, - _freezeManager, - _pauser, - _forcedTransferManager, - _excessManager, - _excessDestination - ) + _wrappedMTokenMigratorV1 = _deployMigrator( + _wrappedMTokenImplementationV2, + earners_, + _excessDestination, + UpgradeRoles({ + admin: _admin, + freezeManager: _freezeManager, + pauser: _pauser, + forcedTransferManager: _forcedTransferManager, + excessManager: _excessManager + }) ); } diff --git a/test/integration/Upgrade.t.sol b/test/integration/Upgrade.t.sol index b9b0443..8c43873 100644 --- a/test/integration/Upgrade.t.sol +++ b/test/integration/Upgrade.t.sol @@ -66,22 +66,6 @@ contract UpgradeTests is Test, DeployBase { vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 23_170_985); } - function _sortAddresses(address[] memory addresses_) internal pure returns (address[] memory) { - for (uint256 i_ = 1; i_ < addresses_.length; ++i_) { - address key_ = addresses_[i_]; - uint256 j_ = i_; - - while (j_ > 0 && uint160(addresses_[j_ - 1]) > uint160(key_)) { - addresses_[j_] = addresses_[j_ - 1]; - --j_; - } - - addresses_[j_] = key_; - } - - return addresses_; - } - function test_upgrade() external { vm.setNonce(_DEPLOYER, _DEPLOYER_NONCE); @@ -96,9 +80,6 @@ contract UpgradeTests is Test, DeployBase { earners_[index_] = _earners[index_]; } - // NOTE: `ListOfEarnersToMigrate` requires strictly ascending addresses, as the production script emits. - earners_ = _sortAddresses(earners_); - vm.startPrank(_DEPLOYER); (address wrappedMTokenImplementation_, address wrappedMTokenMigrator_) = deployUpgrade( _M_TOKEN, diff --git a/test/unit/ListOfEarnersToMigrate.t.sol b/test/unit/ListOfEarnersToMigrate.t.sol index 30de0df..9c474f2 100644 --- a/test/unit/ListOfEarnersToMigrate.t.sol +++ b/test/unit/ListOfEarnersToMigrate.t.sol @@ -6,6 +6,8 @@ import { Test } from "../../lib/forge-std/src/Test.sol"; import { ListOfEarnersToMigrate } from "../../src/ListOfEarnersToMigrate.sol"; +import { EarnersAddresses } from "../../script/EarnersAddresses.sol"; + contract ListOfEarnersToMigrateTests is Test { function test_constructor_empty() external { ListOfEarnersToMigrate list_ = new ListOfEarnersToMigrate(new address[](0)); @@ -69,4 +71,20 @@ contract ListOfEarnersToMigrateTests is Test { vm.expectRevert(ListOfEarnersToMigrate.EarnersNotSortedOrUnique.selector); new ListOfEarnersToMigrate(earners_); } + + function test_constructor_committedEarnersAreDeployable() external { + _assertDeployable(EarnersAddresses.getEthereumEarners()); + _assertDeployable(EarnersAddresses.getArbitrumEarners()); + _assertDeployable(EarnersAddresses.getPlumeEarners()); + } + + function _assertDeployable(address[] memory earners_) internal { + address[] memory stored_ = new ListOfEarnersToMigrate(earners_).getEarners(); + + assertEq(stored_.length, earners_.length); + + for (uint256 i_ = 1; i_ < stored_.length; ++i_) { + assertTrue(uint160(stored_[i_ - 1]) < uint160(stored_[i_])); + } + } } From ebc7ac3ae0644a645180ddf9eb64a932de4ff727 Mon Sep 17 00:00:00 2001 From: Pierrick Turelier Date: Mon, 22 Jun 2026 12:14:52 -0500 Subject: [PATCH 2/2] refactor(test): pass earners storage array directly to migrator deploy Drop the manual storage-to-memory copy loops in the integration test fixtures; assigning or passing the storage array to a memory parameter already performs the copy. --- test/integration/TestBase.sol | 8 +------- test/integration/Upgrade.t.sol | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/test/integration/TestBase.sol b/test/integration/TestBase.sol index 55bb9e8..733f558 100644 --- a/test/integration/TestBase.sol +++ b/test/integration/TestBase.sol @@ -198,15 +198,9 @@ contract TestBase is Test, DeployBase { new WrappedMToken(address(_mToken), _registrar, _swapFacility, _migrationAdmin) ); - address[] memory earners_ = new address[](_earners.length); - - for (uint256 index_; index_ < _earners.length; ++index_) { - earners_[index_] = _earners[index_]; - } - _wrappedMTokenMigratorV1 = _deployMigrator( _wrappedMTokenImplementationV2, - earners_, + _earners, _excessDestination, UpgradeRoles({ admin: _admin, diff --git a/test/integration/Upgrade.t.sol b/test/integration/Upgrade.t.sol index 8c43873..b8eadf0 100644 --- a/test/integration/Upgrade.t.sol +++ b/test/integration/Upgrade.t.sol @@ -74,12 +74,6 @@ contract UpgradeTests is Test, DeployBase { _DEPLOYER_NONCE ); - address[] memory earners_ = new address[](_earners.length); - - for (uint256 index_; index_ < _earners.length; ++index_) { - earners_[index_] = _earners[index_]; - } - vm.startPrank(_DEPLOYER); (address wrappedMTokenImplementation_, address wrappedMTokenMigrator_) = deployUpgrade( _M_TOKEN, @@ -87,7 +81,7 @@ contract UpgradeTests is Test, DeployBase { _EXCESS_DESTINATION, _SWAP_FACILITY, _WRAPPED_M_MIGRATION_ADMIN, - earners_, + _earners, UpgradeRoles({ admin: _ADMIN, freezeManager: _FREEZE_MANAGER,