Skip to content

feat: mint CFG from legacy variants#796

Open
wischli wants to merge 7 commits intomainfrom
feat_v2-cleanings-cfg-mint
Open

feat: mint CFG from legacy variants#796
wischli wants to merge 7 commits intomainfrom
feat_v2-cleanings-cfg-mint

Conversation

@wischli
Copy link
Contributor

@wischli wischli commented Feb 26, 2026

Product requirements

  • Mint CFG tokens on Ethereum to account for legacy wCFG supply and CFG remaining on Centrifuge Chain
  • The minted amount equals wCFG.totalSupply() - wCFG.balanceOf(IOU_CFG) + CENTRIFUGE_CHAIN_CFG_AMOUNT and is sent to the CNF treasury wallet
  • wCFG.balanceOf(IOU_CFG) is subtracted because those wCFG were already redeemed 1:1 for CFG via the IOU contract, but the wCFG total supply was never reduced
  • CENTRIFUGE_CHAIN_CFG_AMOUNT (~34.8M CFG) accounts for remaining CFG on Centrifuge Chain after subtracting already-migrated and chainbridge balances
  • CFG_MINTER (0x50a168Cd...) is relied on CFG via ROOT_V2 during ward updates
  • Operation is Ethereum-only since wCFG only exists on that chain

Design notes

  • Added _mintCFGToTreasury() to V2CleaningsSpell, called after _updateCFGWards() which establishes ROOT_V3 as a ward on CFG
  • The spell temporarily grants itself ward access on CFG via ROOT_V3.relyContract, mints, then cleans up with ROOT_V3.denyContract
  • CENTRIFUGE_CHAIN_CFG_AMOUNT is derived from Centrifuge Chain total issuance (578.4M) minus migrated account (350.9M) minus chainbridge account (192.7M) = ~34.8M
  • CNF_TREASURY_WALLET (0xD052A46b...) receives the minted CFG, distinct from the existing TREASURY address used for USDC
  • Fork test extended with pre/post assertions verifying total supply increase, treasury balance, ward cleanup, and a hard cap on post-mint CFG total supply (~692.05M)

@wischli wischli self-assigned this Feb 26, 2026
@wischli wischli force-pushed the feat_v2-cleanings-cfg-mint branch from 11f409c to dcac508 Compare February 26, 2026 09:08
IERC20 constant USDC_BASE = IERC20(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913);
IERC20 constant USDC_ARBITRUM = IERC20(0xaf88d065e77c8cC2239327C5EDb3A432268e5831);

address constant CNF_TREASURY_WALLET = 0xD052A46b8e0C89fAcB393805E1917AfD20f293Cb;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified ✅

lemunozm
lemunozm previously approved these changes Feb 26, 2026
address constant GUARDIAN_V2_ETHEREUM_OR_ARBITRUM = 0x09ab10a9c3E6Eac1d18270a2322B6113F4C7f5E8;
address constant GUARDIAN_V2_BASE = 0x427A1ce127b1775e4Cbd4F58ad468B9F832eA7e9;

uint256 constant MAX_POST_MINT_CFG_SUPPLY = 692_049_712_426_095_885_688_933_007;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wischli wischli requested a review from hieronx March 5, 2026 17:16
@github-actions
Copy link

github-actions bot commented Mar 5, 2026

Coverage after merging feat_v2-cleanings-cfg-mint into main will be

96.83%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/adapters
   AxelarAdapter.sol97.67%90%100%100%118
   ChainlinkAdapter.sol98.11%91.67%100%100%95
   LayerZeroAdapter.sol98.41%90%100%100%124
   RecoveryAdapter.sol100%100%100%100%
   WormholeAdapter.sol97.67%90%100%100%108
src/admin
   OpsGuardian.sol100%100%100%100%
   ProtocolGuardian.sol100%100%100%100%
   Root.sol100%100%100%100%
   TokenRecoverer.sol100%100%100%100%
src/core/hub
   Accounting.sol94.79%88%100%96.61%132, 134, 137, 141, 144
   Holdings.sol97.71%88.89%100%100%118, 243, 82
   Hub.sol93.98%81.13%93.02%97.54%269, 287, 305, 339, 343, 374–375, 413, 498–499, 544, 549, 586, 601, 87
   HubHandler.sol98%90.91%100%100%71
   HubRegistry.sol92.39%76.67%100%100%118, 124, 130, 35, 46, 79, 99
   ShareClassManager.sol98.81%95.45%100%100%42
src/core/libraries
   PricingLib.sol100%100%100%100%
src/core/messaging
   GasService.sol96.55%100%87.50%96.20%106, 135, 145
   Gateway.sol100%100%100%100%
   MessageDispatcher.sol99.60%98.55%100%100%733
   MessageProcessor.sol82.42%60.26%100%99.01%101, 103, 109, 112, 114, 125, 130, 139, 142, 145, 157, 160, 163, 168, 178, 181, 190, 193, 196, 209, 220, 225, 228, 232, 83–84, 87–88, 91–92, 97, 99
   MultiAdapter.sol100%100%100%100%
src/core/messaging/libraries
   MessageLib.sol100%100%100%100%
src/core/spoke
   BalanceSheet.sol97.47%96.97%92.59%98.55%291, 348, 57
   PoolEscrow.sol100%100%100%100%
   ShareToken.sol92.41%60%94.44%98.04%100, 112, 144, 146, 32
   Spoke.sol97.46%89.71%100%100%132, 132–133, 133, 135, 92–93
   VaultRegistry.sol93.88%85.71%100%98.18%54, 60–62, 98–99
src/core/spoke/factories
   PoolEscrowFactory.sol100%100%100%100%
   TokenFactory.sol100%100%100%100%
src/core/utils
   BatchedMulticall.sol100%100%100%100%
   ContractUpdater.sol100%100%100%100%
src/deployment
   ActionBatchers.sol84.80%62.50%80%86.46%438, 440–441, 443, 443–444, 446, 448, 448–449, 453, 453–454, 456, 459, 459–460, 462, 465, 465–466, 469, 472, 472, 474, 476, 508–512, 517–518, 520–521, 523–524
src/hooks
   BaseTransferHook.sol100%100%100%100%
   FreelyTransferable.sol92.31%80%100%100%37
   FreezeOnly.sol100%100%100%100%
   FullRestrictions.sol95.24%88.89%100%100%47
   RedemptionRestrictions.sol85.71%50%100%100%37
src/hooks/libraries
   UpdateRestrictionMessageLib.sol90%50%100%100%40, 61, 82
src/managers/hub
   NAVManager.sol100%100%100%100%
   SimplePriceManager.sol100%100%100%100%
src/managers/spoke
   MerkleProofManager.sol97.01%87.50%100%100%103, 110
   OnOfframpManager.sol100%100%100%100%
   QueueManager.sol100%100%100%100%
src/managers/spoke/decoders
   BaseDecoder.sol100%100%100%100%
   CircleDecoder.sol83.33%100%100%75%22
   VaultDecoder.sol100%100%100%100%
src/misc
   Auth.sol100%100%100%100%
   ERC20.sol100%100%100%100%
   Escrow.sol56.25%33.33%100%66.67%17, 19, 23–24, 24, 24, 26
   Multicall.sol91.67%66.67%100%100%19
   Recoverable.sol100%100%100%100%
   ReentrancyProtection.sol90%75%100%100%24
src/misc/libraries
   ArrayLib.sol100%100%100%100%
   BitmapLib.sol100%100%100%100%
   BytesLib.sol90.27%56%100%100%109, 120, 131, 14, 142, 153, 16, 164, 175, 186, 87
   CastLib.sol95.24%66.67%100%100%10, 34
   EIP712Lib.sol100%100%100%100%
   ExcessivelySafeCallLib.sol100%100%100%100%
   MathLib.sol93.46%76.19%100%97.33%34–35, 44, 46, 48, 50, 52
   MerkleProofLib.sol100%100%100%100%
   SafeTransferLib.sol96.97%92.86%100%100%75
   SignatureLib.sol95.24%80%100%100%17
   StringLib.sol100%100%100%100%
   TransientArrayLib.sol100%100%100%100%
   TransientBytesLib.sol100%100%100%100%
   TransientStorageLib.sol100%100%100%100%
src/spell
   V2CleaningsSpell.sol0%0%0%0%100–101, 105–108, 110–111, 114–117, 117–120, 125–126, 126–128, 128, 128–130, 130, 130–132, 132, 132–133, 135, 138, 140–141, 143, 147–151, 154–155, 155, 158–161, 59–60, 60, 60–61, 63–67, 69, 69–70, 72, 75, 77, 77, 79, 79–80, 80–83, 85, 89, 89–90, 95, 95–96, 96–98
src/utils
   RefundEscrow.sol100%100%100%100%
   RefundEscrowFactory.sol100%100%100%100%
   SubsidyManager.sol100%100%100%100%
src/valuations
   IdentityValuation.sol100%100%100%100%
   OracleValuation.sol100%100%100%100%
src/vaults
   AsyncRequestManager.sol96.85%86.36%100%99.64%195, 198, 201, 204, 215, 227, 295, 328, 455, 460, 505, 573, 580
   AsyncVault.sol96.25%83.33%95%98.15%146, 47
   BaseVaults.sol92.50%80.77%95.24%93.94%124, 137, 239, 309–310, 396–397, 85–86, 86, 86–88
   BatchRequestManager.sol100%100%100%100%
   SyncDepositVault.sol100%100%100%100%
   SyncManager.sol96.45%88.89%100%97.89%106–107, 109, 67, 72
   VaultRouter.sol87.37%58.82%100%91.53%107, 107, 109–110, 110, 112, 149, 70, 73–74, 86–87
src/vaults/factories
   AsyncVaultFactory.sol93.75%50%100%100%32
   SyncDepositVaultFactory.sol95%50%100%100%40
src/vaults/libraries
   RequestCallbackMessageLib.sol89.58%50%100%100%104, 139, 38, 57, 77
   RequestMessageLib.sol89.74%50%100%100%37, 55, 72, 89

Comment on lines +155 to +162
if (block.chainid == ETHEREUM_CHAIN_ID) {
// Subtract wCFG balance held by the IOU_CFG contract, since those were already
// redeemed 1:1 for CFG and the wCFG total supply was not reduced upon redemption.
uint256 amount = IERC20(WCFG).totalSupply() - IERC20(WCFG).balanceOf(IOU_CFG) + CENTRIFUGE_CHAIN_CFG_AMOUNT;
rootV3.relyContract(CFG, address(this));
CFGTokenLike(CFG).mint(CNF_TREASURY_WALLET, amount);
rootV3.denyContract(CFG, address(this));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the WCFG contract already denyed to mint new CFG tokens?

Copy link
Contributor

@lemunozm lemunozm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, this is correct. LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants