Skip to content

Sort earners before migrator construction#135

Merged
PierrickGT merged 2 commits into
proto-969-fix-remaining-chainsecurity-issuesfrom
pr-134_sort_deploy
Jun 22, 2026
Merged

Sort earners before migrator construction#135
PierrickGT merged 2 commits into
proto-969-fix-remaining-chainsecurity-issuesfrom
pr-134_sort_deploy

Conversation

@denalimarsh

Copy link
Copy Markdown

Summary

Issue 016 in #134 added a strictly-ascending check to the ListOfEarnersToMigrate constructor, but the sorting it now requires was only applied to the generator script and the test fixtures, not to the production deploy path or the committed EarnersAddresses.sol. As written, the mainnet upgrade would revert at deploy time. This PR closes that gap.

Problem

DeployUpgradeMainnet passes EarnersAddresses.getEthereumEarners() straight into the migrator constructor. That committed list predates the sorting change and is not strictly ascending, so new ListOfEarnersToMigrate(...) reverts with EarnersNotSortedOrUnique and the deployment aborts. CI didn't surface it because the integration tests sort their fixtures at runtime and the upgrade test is mainnet-fork-gated.

Changes

  • Sort earners in the production deploy path (DeployBase._deployMigrator), so a deploy is robust to input ordering. The constructor's uniqueness and non-zero checks still apply.
  • Route both test paths (TestBase, Upgrade.t.sol) through DeployBase and drop the two duplicated _sortAddresses helpers, so the tests now exercise the real deploy path.
  • Re-sort the committed EarnersAddresses.sol in place. Membership is unchanged: the address set is identical before/after (verified); only the Ethereum array's order moves.
  • Add a non-fork regression test that builds ListOfEarnersToMigrate directly from each committed array, so an unsorted/duplicate/zero-address artifact now fails in default CI.

@denalimarsh denalimarsh requested a review from PierrickGT June 22, 2026 10:07
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Changes to gas cost

Generated at commit: f77c314f46951b7974f19096dc3a072c98798543, compared to commit: 1642598aa98bf8d849a47edfaa489bb8f91ac1b6

🧾 Summary (20% most significant diffs)

Contract Method Avg (+/-) %
MockSwapFacility swapOutM +7,544 ❌ +6.55%

Full diff report 👇
Contract Deployment Cost (+/-) Method Min (+/-) % Avg (+/-) % Median (+/-) % Max (+/-) % # Calls (+/-)
WrappedMTokenHarness 5,689,329 (0) accruedYieldOf
approve
claimExcess
claimFor
currentIndex
excess
projectedEarningSupply
setAccountOf(address,uint256)
setAccountOf(address,uint256,uint256,bool)
setEnableMIndex
setTotalEarningPrincipal
setTotalEarningSupply
setTotalNonEarningSupply
startEarningFor(address)
stopEarningFor(address)
totalAccruedYield
transfer
transferFrom
unwrap
wrap
2,722 (0)
2,754 (0)
2,456 (0)
2,561 (0)
2,645 (0)
14,973 (0)
7,252 (0)
5,190 (0)
8,179 (0)
5,339 (0)
2,563 (0)
2,551 (0)
2,595 (0)
2,564 (0)
2,698 (0)
7,298 (0)
5,045 (0)
6,014 (0)
503 (0)
525 (0)
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
7,290 (+275)
28,286 (+751)
28,629 (+1,110)
35,267 (+53)
5,922 (+318)
16,669 (+196)
10,240 (+380)
24,619 (-33)
43,834 (-47)
6,644 (-111)
21,224 (-73)
20,720 (+131)
21,707 (-76)
93,194 (-1,762)
71,037 (+783)
10,940 (+302)
30,018 (-391)
63,255 (+5,567)
32,260 (+1,245)
44,327 (+902)
+3.92%
+2.73%
+4.03%
+0.15%
+5.67%
+1.19%
+3.85%
-0.13%
-0.11%
-1.64%
-0.34%
+0.64%
-0.35%
-1.86%
+1.11%
+2.84%
-1.29%
+9.65%
+4.01%
+2.08%
7,659 (0)
29,139 (0)
20,198 (0)
15,076 (0)
8,260 (0)
17,961 (+127)
12,740 (+5,488)
25,090 (0)
45,179 (0)
5,339 (0)
22,463 (0)
22,451 (0)
22,495 (0)
100,112 (0)
75,285 (+4,849)
12,786 (+13)
22,576 (0)
50,759 (0)
35,498 (0)
41,769 (0)
0.00%
0.00%
0.00%
0.00%
0.00%
+0.71%
+75.68%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
+6.88%
+0.10%
0.00%
0.00%
0.00%
0.00%
13,164 (0)
29,139 (0)
50,960 (0)
97,256 (0)
8,260 (0)
17,961 (0)
12,740 (0)
25,090 (0)
45,179 (0)
22,439 (0)
22,463 (0)
22,451 (0)
22,495 (0)
100,112 (0)
75,412 (0)
12,913 (0)
100,918 (0)
89,470 (0)
35,498 (0)
109,538 (+33,569)
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
0.00%
+44.19%
343 (0)
106 (0)
102 (0)
107 (0)
2,160 (+2)
107 (0)
101 (0)
616 (-2)
366 (+2)
537 (+42)
552 (+2)
354 (+2)
698 (-2)
108 (0)
107 (0)
127 (0)
215 (0)
104 (0)
100 (+6)
106 (0)
ListOfEarnersToMigrate 254,186 (0) getEarners 2,617 (0) 0.00% 59,928 (-4,527) -7.02% 81,552 (0) 0.00% 81,552 (0) 0.00% 27 (+3)
MockSwapFacility 441,322 (0) swapInM
swapOutM
62,750 (+14,193)
35,887 (0)
+29.23%
0.00%
84,590 (+300)
122,697 (+7,544)
+0.36%
+6.55%
82,825 (0)
115,601 (+86)
0.00%
+0.07%
151,225 (0)
142,759 (0)
0.00%
0.00%
309 (0)
108 (0)
MockM 522,934 (0) balanceOf
setBalanceOf
setCurrentIndex
setPrincipalBalanceOf
593 (0)
24,181 (0)
26,568 (0)
24,239 (0)
0.00%
0.00%
0.00%
0.00%
2,348 (-20)
43,503 (+87)
43,373 (-1)
43,617 (-201)
-0.84%
+0.20%
-0.00%
-0.46%
2,593 (0)
44,117 (-24)
43,692 (0)
44,187 (0)
0.00%
-0.05%
0.00%
0.00%
2,593 (0)
44,261 (0)
43,692 (0)
44,307 (0)
0.00%
0.00%
0.00%
0.00%
834 (+16)
427 (0)
866 (0)
202 (0)
WrappedMToken 5,296,761 (0) transfer
transferFrom
33,891 (0)
39,403 (0)
0.00%
0.00%
60,064 (-139)
49,563 (-49)
-0.23%
-0.10%
67,374 (0)
55,602 (0)
0.00%
0.00%
83,913 (0)
72,886 (0)
0.00%
0.00%
327 (0)
227 (0)
MockRegistrar 180,239 (0) get 405 (0) 0.00% 2,011 (+4) +0.20% 2,405 (0) 0.00% 2,405 (0) 0.00% 483 (0)
Proxy 104,329 (0) fallback 5,029 (0) 0.00% 27,380 (+35) +0.13% 13,047 (0) 0.00% 200,947 (0) 0.00% 12,779 (+74)

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

LCOV of commit ebc7ac3 during Forge Coverage #653

Summary coverage rate:
  lines......: 99.4% (327 of 329 lines)
  functions..: 100.0% (68 of 68 functions)
  branches...: 87.5% (7 of 8 branches)

Files changed coverage rate: n/a

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.
@PierrickGT PierrickGT merged commit b873e34 into proto-969-fix-remaining-chainsecurity-issues Jun 22, 2026
5 checks passed
@PierrickGT PierrickGT deleted the pr-134_sort_deploy branch June 22, 2026 17:22
PierrickGT added a commit that referenced this pull request Jun 24, 2026
* fix(WrappedM): prevent frozen claim recipient from blocking stopEarningFor

An earner could point their claim recipient at a frozen address so that the
permissionless stopEarningFor() reverts inside _claim() -> _transfer() ->
_revertIfFrozen(recipient_), keeping the account earning indefinitely and
defeating the deauthorization mechanism.

Both stopEarningFor() overloads now fall back to skipTransfer when the resolved
claim recipient is frozen, mirroring the _beforeFreeze path: the yield is
crystallized onto the account's own balance instead of being routed to the
frozen recipient, so deauthorization always succeeds.

* fix(WrappedM): enforce sorted unique earners in migrator list

The WrappedMTokenMigratorV1 migration corrupts accounting if an address
appears twice in the earners array, since the first pass overwrites the
lastIndex slot with the computed principal. ListOfEarnersToMigrate now
requires strictly ascending addresses, enforcing sorted order, uniqueness
and non-zero in a single O(n) pass without the gas cost of an O(n^2) check.

Integration test fixtures sort the in-memory earners copy before deploying
the migrator, mirroring the production generation script.

* chore(script): harden earners list generation

Make the earners list generation safe to rely on for the v1->v2 migration:

- Paginate holder fetches so earners are never silently truncated by the
  `first` cap, with a max-pages guard against a broken `skip`.
- Build the whole file in memory and write once after every network
  succeeds; abort without writing on any error, and exit non-zero, so a
  partial run never mixes fresh and stale lists.
- Throw on duplicate or zero earner addresses instead of relying solely on
  the on-chain check.
- Fix the GraphQL response typing (drop the unsound cast), add a request
  timeout, centralize the chain enum, and drop the unused balance field.

* Sort earners before migrator construction (#135)

* fix(deploy): sort earners before migrator construction

* 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.

---------

Co-authored-by: Pierrick Turelier <pierrick@turelier.com>

---------

Co-authored-by: Denali Marsh <denalimarsh@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants