From 72a0c6f60214ee1531720e62da7fde3e4ed80600 Mon Sep 17 00:00:00 2001 From: Josh Date: Mon, 23 Sep 2024 14:23:05 -0400 Subject: [PATCH 1/7] add additional safety checks and add new token pool type --- .../BurnMintWithLockReleaseFlagTokenPool.sol | 35 ++++++++++ .../USDC/HybridLockReleaseUSDCTokenPool.sol | 16 ++--- .../ccip/pools/USDC/USDCBridgeMigrator.sol | 14 ++-- ...BurnMintWithLockReleaseFlagTokenPool.t.sol | 70 +++++++++++++++++++ .../HybridLockReleaseUSDCTokenPool.t.sol | 69 ++++++++++++++---- 5 files changed, 177 insertions(+), 27 deletions(-) create mode 100644 contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol create mode 100644 contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol diff --git a/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol new file mode 100644 index 0000000000..3e33fccc63 --- /dev/null +++ b/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.8.0; + +import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; + +import {Pool} from "../../libraries/Pool.sol"; +import {BurnMintTokenPool} from "../BurnMintTokenPool.sol"; + +contract BurnMintWithLockReleaseFlagTokenPool is BurnMintTokenPool { + /// bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) + bytes4 public constant LOCK_RELEASE_FLAG = 0xfa7c07de; + + constructor( + IBurnMintERC20 token, + address[] memory allowlist, + address rmnProxy, + address router + ) BurnMintTokenPool(token, allowlist, rmnProxy, router) {} + + /// @notice Burn the token in the pool + /// @dev The _validateLockOrBurn check is an essential security check + function lockOrBurn( + Pool.LockOrBurnInV1 calldata lockOrBurnIn + ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { + _validateLockOrBurn(lockOrBurnIn); + + _burn(lockOrBurnIn.amount); + + emit Burned(msg.sender, lockOrBurnIn.amount); + + return Pool.LockOrBurnOutV1({ + destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), + destPoolData: abi.encode(LOCK_RELEASE_FLAG) + }); + } +} diff --git a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol index 5fbd71bb2c..8eaa2463cd 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol @@ -34,8 +34,8 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { error LanePausedForCCTPMigration(uint64 remoteChainSelector); error TokenLockingNotAllowedAfterMigration(uint64 remoteChainSelector); - /// bytes4(keccak256("NO_CTTP_USE_LOCK_RELEASE")) - bytes4 public constant LOCK_RELEASE_FLAG = 0xd43c7897; + /// bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) + bytes4 public constant LOCK_RELEASE_FLAG = 0xfa7c07de; /// @notice The address of the liquidity provider for a specific chain. /// External liquidity is not required when there is one canonical token deployed to a chain, @@ -114,7 +114,6 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { s_lockedTokensByChainSelector[releaseOrMintIn.remoteChainSelector] -= releaseOrMintIn.amount; } - // Release to the offRamp, which forwards it to the recipient getToken().safeTransfer(releaseOrMintIn.receiver, releaseOrMintIn.amount); emit Released(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount); @@ -171,6 +170,10 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { function provideLiquidity(uint64 remoteChainSelector, uint256 amount) external { if (s_liquidityProvider[remoteChainSelector] != msg.sender) revert TokenPool.Unauthorized(msg.sender); + if (remoteChainSelector == s_proposedUSDCMigrationChain) { + revert LanePausedForCCTPMigration(remoteChainSelector); + } + s_lockedTokensByChainSelector[remoteChainSelector] += amount; i_token.safeTransferFrom(msg.sender, address(this), amount); @@ -211,13 +214,6 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { /// @param from The address of the old pool. /// @param remoteChainSelector The chain for which liquidity is being transferred. function transferLiquidity(address from, uint64 remoteChainSelector) external onlyOwner { - // Prevent Liquidity Transfers when a migration is pending. This prevents requiring the new pool to manage - // token exclusions for edge-case messages and ensures that the migration is completed before any new liquidity - // is added to the pool. - if (HybridLockReleaseUSDCTokenPool(from).getCurrentProposedCCTPChainMigration() == remoteChainSelector) { - revert LanePausedForCCTPMigration(remoteChainSelector); - } - OwnerIsCreator(from).acceptOwnership(); // Withdraw all available liquidity from the old pool. diff --git a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol index 98616cd79f..41077eb4ec 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol @@ -23,9 +23,8 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { error onlyCircle(); error ExistingMigrationProposal(); - error NoExistingMigrationProposal(); error NoMigrationProposalPending(); - error InvalidChainSelector(uint64 remoteChainSelector); + error InvalidChainSelector(); IBurnMintERC20 internal immutable i_USDC; Router internal immutable i_router; @@ -49,9 +48,9 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { /// @dev proposeCCTPMigration must be called first on an approved lane to execute properly. /// @dev This function signature should NEVER be overwritten, otherwise it will be unable to be called by /// circle to properly migrate USDC over to CCTP. - function burnLockedUSDC() public { + function burnLockedUSDC() external { if (msg.sender != s_circleUSDCMigrator) revert onlyCircle(); - if (s_proposedUSDCMigrationChain == 0) revert ExistingMigrationProposal(); + if (s_proposedUSDCMigrationChain == 0) revert NoMigrationProposalPending(); uint64 burnChainSelector = s_proposedUSDCMigrationChain; @@ -84,6 +83,9 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { // Prevent overwriting existing migration proposals until the current one is finished if (s_proposedUSDCMigrationChain != 0) revert ExistingMigrationProposal(); + // Ensure that the chain is currently using lock/release and not CCTP + if (!s_shouldUseLockRelease[remoteChainSelector]) revert InvalidChainSelector(); + s_proposedUSDCMigrationChain = remoteChainSelector; emit CCTPMigrationProposed(remoteChainSelector); @@ -92,7 +94,7 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { /// @notice Cancel an existing proposal to migrate a lane to CCTP. /// @notice This function will revert if no proposal is currently in progress. function cancelExistingCCTPMigrationProposal() external onlyOwner { - if (s_proposedUSDCMigrationChain == 0) revert NoExistingMigrationProposal(); + if (s_proposedUSDCMigrationChain == 0) revert NoMigrationProposalPending(); uint64 currentProposalChainSelector = s_proposedUSDCMigrationChain; delete s_proposedUSDCMigrationChain; @@ -136,6 +138,8 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { /// @dev This function should ONLY be called on the home chain, where tokens are locked, NOT on the remote chain /// and strict scrutiny should be applied to ensure that the amount of tokens excluded is accurate. function excludeTokensFromBurn(uint64 remoteChainSelector, uint256 amount) external onlyOwner { + if (s_proposedUSDCMigrationChain != remoteChainSelector) revert NoMigrationProposalPending(); + s_tokensExcludedFromBurn[remoteChainSelector] += amount; uint256 burnableAmountAfterExclusion = diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol new file mode 100644 index 0000000000..88f2075425 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; + +import {Pool} from "../../libraries/Pool.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; + +import {TokenPool} from "../../pools/TokenPool.sol"; +import {BurnMintWithLockReleaseFlagTokenPool} from "../../pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol"; +import {BurnMintSetup} from "./BurnMintSetup.t.sol"; + +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; + +contract BurnMintWithLockReleaseFlagTokenPoolSetup is BurnMintSetup { + BurnMintWithLockReleaseFlagTokenPool internal s_pool; + + function setUp() public virtual override { + BurnMintSetup.setUp(); + + s_pool = new BurnMintWithLockReleaseFlagTokenPool( + s_burnMintERC677, new address[](0), address(s_mockRMN), address(s_sourceRouter) + ); + s_burnMintERC677.grantMintAndBurnRoles(address(s_pool)); + + _applyChainUpdates(address(s_pool)); + } +} + +contract BurnMintWithLockReleaseFlagTokenPool_lockOrBurn is BurnMintWithLockReleaseFlagTokenPoolSetup { + function test_Setup_Success() public view { + assertEq(address(s_burnMintERC677), address(s_pool.getToken())); + assertEq(address(s_mockRMN), s_pool.getRmnProxy()); + assertEq(false, s_pool.getAllowListEnabled()); + assertEq("BurnMintTokenPool 1.5.0", s_pool.typeAndVersion()); + } + + function test_PoolBurn_CorrectReturnData_Success() public { + uint256 burnAmount = 20_000e18; + + deal(address(s_burnMintERC677), address(s_pool), burnAmount); + assertEq(s_burnMintERC677.balanceOf(address(s_pool)), burnAmount); + + vm.startPrank(s_burnMintOnRamp); + + vm.expectEmit(); + emit RateLimiter.TokensConsumed(burnAmount); + + vm.expectEmit(); + emit IERC20.Transfer(address(s_pool), address(0), burnAmount); + + vm.expectEmit(); + emit TokenPool.Burned(address(s_burnMintOnRamp), burnAmount); + + bytes4 expectedSignature = bytes4(keccak256("burn(uint256)")); + vm.expectCall(address(s_burnMintERC677), abi.encodeWithSelector(expectedSignature, burnAmount)); + + Pool.LockOrBurnOutV1 memory lockOrBurnOut = s_pool.lockOrBurn( + Pool.LockOrBurnInV1({ + originalSender: OWNER, + receiver: bytes(""), + amount: burnAmount, + remoteChainSelector: DEST_CHAIN_SELECTOR, + localToken: address(s_burnMintERC677) + }) + ); + + assertEq(s_burnMintERC677.balanceOf(address(s_pool)), 0); + + assertEq(bytes4(lockOrBurnOut.destPoolData), s_pool.LOCK_RELEASE_FLAG()); + } +} diff --git a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol index ddb8c29dc2..906ff2b113 100644 --- a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol @@ -373,6 +373,12 @@ contract HybridUSDCTokenPoolTests is USDCTokenPoolSetup { } function test_LockOrBurn_WhileMigrationPause_Revert() public { + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + // Create a fake migration proposal s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); @@ -380,12 +386,6 @@ contract HybridUSDCTokenPoolTests is USDCTokenPoolSetup { bytes32 receiver = bytes32(uint256(uint160(STRANGER))); - // Mark the destination chain as supporting CCTP, so use L/R instead. - uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; - - s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); - assertTrue( s_usdcTokenPool.shouldUseLockRelease(DEST_CHAIN_SELECTOR), "Lock Release mech not configured for outgoing message to DEST_CHAIN_SELECTOR" @@ -527,6 +527,12 @@ contract HybridUSDCTokenPoolTests is USDCTokenPoolSetup { // Set as the OWNER so we can provide liquidity vm.startPrank(OWNER); + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + s_usdcTokenPool.setLiquidityProvider(DEST_CHAIN_SELECTOR, OWNER); s_token.approve(address(s_usdcTokenPool), type(uint256).max); @@ -643,6 +649,12 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { function test_cancelExistingCCTPMigrationProposal() public { vm.startPrank(OWNER); + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + vm.expectEmit(); emit USDCBridgeMigrator.CCTPMigrationProposed(DEST_CHAIN_SELECTOR); @@ -665,7 +677,7 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { "migration proposal exists, but shouldn't after being cancelled" ); - vm.expectRevert(USDCBridgeMigrator.NoExistingMigrationProposal.selector); + vm.expectRevert(USDCBridgeMigrator.NoMigrationProposalPending.selector); s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); } @@ -684,7 +696,7 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { vm.startPrank(CIRCLE); - vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.ExistingMigrationProposal.selector)); + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.NoMigrationProposalPending.selector)); s_usdcTokenPool.burnLockedUSDC(); } @@ -700,7 +712,7 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { } function test_cannotCancelANonExistentMigrationProposal() public { - vm.expectRevert(USDCBridgeMigrator.NoExistingMigrationProposal.selector); + vm.expectRevert(USDCBridgeMigrator.NoMigrationProposalPending.selector); // Proposal to migrate doesn't exist, and so the chain selector is zero, and therefore should revert s_usdcTokenPool.cancelExistingCCTPMigrationProposal(); @@ -784,7 +796,7 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { // Mark the destination chain as supporting CCTP, so use L/R instead. uint64[] memory destChainAdds = new uint64[](1); - destChainAdds[0] = DEST_CHAIN_SELECTOR; + destChainAdds[0] = SOURCE_CHAIN_SELECTOR; s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); @@ -805,6 +817,8 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { // since there's no corresponding attestation to use for minting. vm.startPrank(OWNER); + s_usdcTokenPool.proposeCCTPMigration(SOURCE_CHAIN_SELECTOR); + // Exclude the tokens from being burned and check for the event vm.expectEmit(); emit USDCBridgeMigrator.TokensExcludedFromBurn(SOURCE_CHAIN_SELECTOR, amount, (amount * 3) - amount); @@ -825,8 +839,6 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { s_usdcTokenPool.setCircleMigratorAddress(CIRCLE); - s_usdcTokenPool.proposeCCTPMigration(SOURCE_CHAIN_SELECTOR); - vm.startPrank(CIRCLE); s_usdcTokenPool.burnLockedUSDC(); @@ -881,4 +893,37 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { // We also want to check that the system uses CCTP Burn/Mint for all other messages that don't have that flag test_MintOrRelease_incomingMessageWithPrimaryMechanism(); } + + function test_ProposeMigration_ChainNotUsingLockRelease_Revert() public { + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.InvalidChainSelector.selector)); + + vm.startPrank(OWNER); + + s_usdcTokenPool.proposeCCTPMigration(0x98765); + } + + function test_excludeTokensWhenNoMigrationProposalPending_Revert() public { + vm.expectRevert(abi.encodeWithSelector(USDCBridgeMigrator.NoMigrationProposalPending.selector)); + + vm.startPrank(OWNER); + + s_usdcTokenPool.excludeTokensFromBurn(SOURCE_CHAIN_SELECTOR, 1e6); + } + + function test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() public { + vm.startPrank(OWNER); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + + s_usdcTokenPool.proposeCCTPMigration(DEST_CHAIN_SELECTOR); + + vm.expectRevert( + abi.encodeWithSelector(HybridLockReleaseUSDCTokenPool.LanePausedForCCTPMigration.selector, DEST_CHAIN_SELECTOR) + ); + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); + } } From eb531ccd227f1fb321ae2d3cc5c44bca5e9e647d Mon Sep 17 00:00:00 2001 From: Josh Date: Fri, 27 Sep 2024 11:11:14 -0400 Subject: [PATCH 2/7] additional checks to prevent ownership abuse --- contracts/gas-snapshots/ccip.gas-snapshot | 51 +++++++++++-------- .../USDC/HybridLockReleaseUSDCTokenPool.sol | 15 ++++-- .../ccip/pools/USDC/USDCBridgeMigrator.sol | 4 ++ .../HybridLockReleaseUSDCTokenPool.t.sol | 32 ++++++++++++ 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 9861ed236c..5c9bf92f18 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -30,6 +30,8 @@ BurnMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 17851) BurnMintTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 28805) BurnMintTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56253) BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 112391) +BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_PoolBurn_CorrectReturnData_Success() (gas: 243221) +BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_Setup_Success() (gas: 17873) BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) @@ -431,33 +433,38 @@ FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressPrecompiles_Revert() ( FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddress_Revert() (gas: 10839) FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress_Success() (gas: 6731) FeeQuoter_validateDestFamilyAddress:test_ValidNonEVMAddress_Success() (gas: 6511) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209248) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135879) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 107090) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 144586) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 214817) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 423641) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209230) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135861) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109814) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147081) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217718) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 426564) HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268928) -HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 111484) +HybridUSDCTokenPoolMigrationTests:test_ProposeMigration_ChainNotUsingLockRelease_Revert() (gas: 15821) +HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 114166) HybridUSDCTokenPoolMigrationTests:test_burnLockedUSDC_invalidPermissions_Revert() (gas: 39362) -HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 33189) -HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12669) -HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13329) -HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 160900) -HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 255982) -HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 165921) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 154242) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 463740) +HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 56207) +HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12736) +HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13373) +HybridUSDCTokenPoolMigrationTests:test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() (gas: 67304) +HybridUSDCTokenPoolMigrationTests:test_cannotRevertChainMechanism_afterMigration_Revert() (gas: 313390) +HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 177033) +HybridUSDCTokenPoolMigrationTests:test_cnanotProvideLiquidity_AfterMigration_Revert() (gas: 313775) +HybridUSDCTokenPoolMigrationTests:test_excludeTokensWhenNoMigrationProposalPending_Revert() (gas: 13657) +HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 309952) +HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 167124) +HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 156736) +HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 516552) HybridUSDCTokenPoolTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209230) HybridUSDCTokenPoolTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135880) -HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 107135) -HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 144607) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 214795) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 423619) +HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109814) +HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147079) +HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217696) +HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 426520) HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268910) -HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 111528) -HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 160845) -HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 165904) +HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 114210) +HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 176989) +HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 167107) LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Revert() (gas: 10989) LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Success() (gas: 18028) LockReleaseTokenPoolPoolAndProxy_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 3051552) diff --git a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol index 8eaa2463cd..91db9c1b29 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol @@ -99,7 +99,7 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { _validateReleaseOrMint(releaseOrMintIn); // Circle requires a supply-lock to prevent incoming messages once the migration process begins. - // This prevents new outgoing messages once the migration has begun to ensure any the procedure runs as expected + // This prevents new incoming messages once the migration has begun to ensure any the procedure runs as expected if (s_proposedUSDCMigrationChain == releaseOrMintIn.remoteChainSelector) { revert LanePausedForCCTPMigration(s_proposedUSDCMigrationChain); } @@ -170,6 +170,10 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { function provideLiquidity(uint64 remoteChainSelector, uint256 amount) external { if (s_liquidityProvider[remoteChainSelector] != msg.sender) revert TokenPool.Unauthorized(msg.sender); + if (s_migratedChains.contains(remoteChainSelector)) { + revert TokenLockingNotAllowedAfterMigration(remoteChainSelector); + } + if (remoteChainSelector == s_proposedUSDCMigrationChain) { revert LanePausedForCCTPMigration(remoteChainSelector); } @@ -236,9 +240,10 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { return s_shouldUseLockRelease[remoteChainSelector]; } - /// @notice Updates Updates designations for chains on whether to use primary or alt mechanism on CCIP messages + /// @notice Updates designations for chains on whether to use primary or alt mechanism on CCIP messages /// @param removes A list of chain selectors to disable Lock-Release, and enforce BM - /// @param adds A list of chain selectors to enable LR instead of BM + /// @param adds A list of chain selectors to enable LR instead of BM. These chains must not have been migrated + /// to CCTP yet or the transaction will revert function updateChainSelectorMechanisms(uint64[] calldata removes, uint64[] calldata adds) external onlyOwner { for (uint256 i = 0; i < removes.length; ++i) { delete s_shouldUseLockRelease[removes[i]]; @@ -246,6 +251,10 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { } for (uint256 i = 0; i < adds.length; ++i) { + // Prevent enabling lock release on chains which have already been migrated + if (s_migratedChains.contains(adds[i])) { + revert TokenLockingNotAllowedAfterMigration(adds[i]); + } s_shouldUseLockRelease[adds[i]] = true; emit LockReleaseEnabled(adds[i]); } diff --git a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol index 41077eb4ec..12264c5d37 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol @@ -37,6 +37,8 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { mapping(uint64 chainSelector => bool shouldUseLockRelease) internal s_shouldUseLockRelease; + EnumerableSet.UintSet internal s_migratedChains; + constructor(address token, address router) { i_USDC = IBurnMintERC20(token); i_router = Router(router); @@ -70,6 +72,8 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { // Disable L/R automatically on burned chain and enable CCTP delete s_shouldUseLockRelease[burnChainSelector]; + s_migratedChains.add(burnChainSelector); + emit CCTPMigrationExecuted(burnChainSelector, tokensToBurn); } diff --git a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol index 906ff2b113..de92219435 100644 --- a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol @@ -926,4 +926,36 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { ); s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); } + + function test_cannotRevertChainMechanism_afterMigration_Revert() public { + test_lockOrBurn_then_BurnInCCTPMigration_Success(); + + vm.startPrank(OWNER); + + // Mark the destination chain as supporting CCTP, so use L/R instead. + uint64[] memory destChainAdds = new uint64[](1); + destChainAdds[0] = DEST_CHAIN_SELECTOR; + + vm.expectRevert( + abi.encodeWithSelector( + HybridLockReleaseUSDCTokenPool.TokenLockingNotAllowedAfterMigration.selector, DEST_CHAIN_SELECTOR + ) + ); + + s_usdcTokenPool.updateChainSelectorMechanisms(new uint64[](0), destChainAdds); + } + + function test_cnanotProvideLiquidity_AfterMigration_Revert() public { + test_lockOrBurn_then_BurnInCCTPMigration_Success(); + + vm.startPrank(OWNER); + + vm.expectRevert( + abi.encodeWithSelector( + HybridLockReleaseUSDCTokenPool.TokenLockingNotAllowedAfterMigration.selector, DEST_CHAIN_SELECTOR + ) + ); + + s_usdcTokenPool.provideLiquidity(DEST_CHAIN_SELECTOR, 1e6); + } } From b7067b5f7741d75af8d26cb0d1bcd8dcfd4b7675 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 3 Oct 2024 12:52:34 -0400 Subject: [PATCH 3/7] formatting, remove unnec. state vars, and better natspec --- .../USDC/BurnMintWithLockReleaseFlagTokenPool.sol | 10 ++++++++-- .../pools/USDC/HybridLockReleaseUSDCTokenPool.sol | 11 +++++++---- .../src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol | 9 +++------ .../pools/BurnMintWithLockReleaseFlagTokenPool.t.sol | 1 - 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol index 3e33fccc63..e260300bd7 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol @@ -1,10 +1,16 @@ -pragma solidity ^0.8.0; +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.24; import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; import {Pool} from "../../libraries/Pool.sol"; import {BurnMintTokenPool} from "../BurnMintTokenPool.sol"; +/// @notice A standard BurnMintTokenPool with modified destPoolData so that the remote pool knows to release tokens +/// instead of minting. This enables interoperability with HybridLockReleaseUSDCTokenPool which uses +// the destPoolData to determine whether to mint or release tokens. +/// @dev The only difference between this contract and BurnMintTokenPool is the destPoolData returns the +/// abi-encoded LOCK_RELEASE_FLAG instead of an empty string. contract BurnMintWithLockReleaseFlagTokenPool is BurnMintTokenPool { /// bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) bytes4 public constant LOCK_RELEASE_FLAG = 0xfa7c07de; @@ -20,7 +26,7 @@ contract BurnMintWithLockReleaseFlagTokenPool is BurnMintTokenPool { /// @dev The _validateLockOrBurn check is an essential security check function lockOrBurn( Pool.LockOrBurnInV1 calldata lockOrBurnIn - ) external virtual override returns (Pool.LockOrBurnOutV1 memory) { + ) external override returns (Pool.LockOrBurnOutV1 memory) { _validateLockOrBurn(lockOrBurnIn); _burn(lockOrBurnIn.amount); diff --git a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol index 91db9c1b29..c034fcad75 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol @@ -49,7 +49,7 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { address[] memory allowlist, address rmnProxy, address router - ) USDCTokenPool(tokenMessenger, token, allowlist, rmnProxy, router) USDCBridgeMigrator(address(token), router) {} + ) USDCTokenPool(tokenMessenger, token, allowlist, rmnProxy, router) USDCBridgeMigrator(address(token)) {} // ================================================================ // │ Incoming/Outgoing Mechanisms | @@ -170,10 +170,12 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { function provideLiquidity(uint64 remoteChainSelector, uint256 amount) external { if (s_liquidityProvider[remoteChainSelector] != msg.sender) revert TokenPool.Unauthorized(msg.sender); + // Prevent adding liquidity to a chain which has already been migrated if (s_migratedChains.contains(remoteChainSelector)) { revert TokenLockingNotAllowedAfterMigration(remoteChainSelector); } + // prevent adding liquidity to a chain which has been proposed for migration if (remoteChainSelector == s_proposedUSDCMigrationChain) { revert LanePausedForCCTPMigration(remoteChainSelector); } @@ -192,7 +194,7 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { /// withdrawn on this chain, otherwise a mismatch may occur between locked token balance and remote circulating supply /// which may block a potential future migration of the chain to CCTP. function withdrawLiquidity(uint64 remoteChainSelector, uint256 amount) external onlyOwner { - // Circle requires a supply-lock to prevent outgoing messages once the migration process begins. + // A supply-lock is required to prevent outgoing messages once the migration process begins. // This prevents new outgoing messages once the migration has begun to ensure any the procedure runs as expected if (remoteChainSelector == s_proposedUSDCMigrationChain) { revert LanePausedForCCTPMigration(remoteChainSelector); @@ -220,7 +222,8 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { function transferLiquidity(address from, uint64 remoteChainSelector) external onlyOwner { OwnerIsCreator(from).acceptOwnership(); - // Withdraw all available liquidity from the old pool. + // Withdraw all available liquidity from the old pool. No check is needed for pending migrations, as the old pool + // will revert if the migration has begun. uint256 withdrawAmount = HybridLockReleaseUSDCTokenPool(from).getLockedTokensForChain(remoteChainSelector); HybridLockReleaseUSDCTokenPool(from).withdrawLiquidity(remoteChainSelector, withdrawAmount); @@ -235,7 +238,7 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { /// @notice Return whether a lane should use the alternative L/R mechanism in the token pool. /// @param remoteChainSelector the remote chain the lane is interacting with - /// @return bool Return true if the alternative L/R mechanism should be used + /// @return bool Return true if the alternative L/R mechanism should be used, and is decided by the Owner function shouldUseLockRelease(uint64 remoteChainSelector) public view virtual returns (bool) { return s_shouldUseLockRelease[remoteChainSelector]; } diff --git a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol index 12264c5d37..82532e97f5 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.24; import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; @@ -5,8 +6,6 @@ import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; -import {Router} from "../../Router.sol"; - /// @notice Allows migration of a lane in a token pool from Lock/Release to CCTP supported Burn/Mint. Contract /// functionality is based on hard requirements defined by Circle to allow for future CCTP compatibility /// https://github.com/circlefin/stablecoin-evm/blob/master/doc/bridged_USDC_standard.md @@ -26,8 +25,7 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { error NoMigrationProposalPending(); error InvalidChainSelector(); - IBurnMintERC20 internal immutable i_USDC; - Router internal immutable i_router; + IBurnMintERC20 private immutable i_USDC; address internal s_circleUSDCMigrator; uint64 internal s_proposedUSDCMigrationChain; @@ -39,9 +37,8 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { EnumerableSet.UintSet internal s_migratedChains; - constructor(address token, address router) { + constructor(address token) { i_USDC = IBurnMintERC20(token); - i_router = Router(router); } /// @notice Burn USDC locked for a specific lane so that destination USDC can be converted from diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol index 88f2075425..b341624798 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.24; import {Pool} from "../../libraries/Pool.sol"; import {RateLimiter} from "../../libraries/RateLimiter.sol"; - import {TokenPool} from "../../pools/TokenPool.sol"; import {BurnMintWithLockReleaseFlagTokenPool} from "../../pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol"; import {BurnMintSetup} from "./BurnMintSetup.t.sol"; From 16a43d6dca2e86b12c3ad831453df6c5780a53b0 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 17 Oct 2024 10:16:48 -0400 Subject: [PATCH 4/7] lock pragma and update function visibility --- contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol index 82532e97f5..f323ee7d81 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/USDCBridgeMigrator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.24; +pragma solidity 0.8.24; import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; @@ -153,7 +153,7 @@ abstract contract USDCBridgeMigrator is OwnerIsCreator { /// @dev The sum of locked tokens and excluded tokens should equal the supply of the token on the remote chain /// @param remoteChainSelector The chain for which the excluded tokens are being queried /// @return uint256 amount of tokens excluded from being burned in a CCTP-migration - function getExcludedTokensByChain(uint64 remoteChainSelector) public view returns (uint256) { + function getExcludedTokensByChain(uint64 remoteChainSelector) external view returns (uint256) { return s_tokensExcludedFromBurn[remoteChainSelector]; } } From 2d89158c663679824c0c3ebeafeab9adc404d5e4 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 22 Oct 2024 11:30:45 -0400 Subject: [PATCH 5/7] remove unnecessary test --- contracts/gas-snapshots/ccip.gas-snapshot | 3 +-- .../test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index f643d81b43..371bd6fda2 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -30,8 +30,7 @@ BurnMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 17851) BurnMintTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 28805) BurnMintTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56253) BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 112391) -BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_PoolBurn_CorrectReturnData_Success() (gas: 243221) -BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_Setup_Success() (gas: 17873) +BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_PoolBurn_CorrectReturnData_Success() (gas: 242931) BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol index b341624798..c84fc2efc5 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol @@ -25,13 +25,6 @@ contract BurnMintWithLockReleaseFlagTokenPoolSetup is BurnMintSetup { } contract BurnMintWithLockReleaseFlagTokenPool_lockOrBurn is BurnMintWithLockReleaseFlagTokenPoolSetup { - function test_Setup_Success() public view { - assertEq(address(s_burnMintERC677), address(s_pool.getToken())); - assertEq(address(s_mockRMN), s_pool.getRmnProxy()); - assertEq(false, s_pool.getAllowListEnabled()); - assertEq("BurnMintTokenPool 1.5.0", s_pool.typeAndVersion()); - } - function test_PoolBurn_CorrectReturnData_Success() public { uint256 burnAmount = 20_000e18; From 9802bc0052b41269f273cb63a2b05d9aa469331e Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 22 Oct 2024 13:04:40 -0400 Subject: [PATCH 6/7] update BurnMintWithLockReleaseFlag to import the flag instead of redefining it for safety --- contracts/gas-snapshots/ccip.gas-snapshot | 64 +++++++++---------- .../BurnMintWithLockReleaseFlagTokenPool.sol | 7 +- .../USDC/HybridLockReleaseUSDCTokenPool.sol | 6 +- ...BurnMintWithLockReleaseFlagTokenPool.t.sol | 4 +- .../HybridLockReleaseUSDCTokenPool.t.sol | 9 +-- 5 files changed, 47 insertions(+), 43 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 371bd6fda2..3695e6d458 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -30,7 +30,7 @@ BurnMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 17851) BurnMintTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 28805) BurnMintTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56253) BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 112391) -BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_PoolBurn_CorrectReturnData_Success() (gas: 242931) +BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_PoolBurn_CorrectReturnData_Success() (gas: 242252) BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) @@ -425,38 +425,38 @@ FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddressPrecompiles_Revert() ( FeeQuoter_validateDestFamilyAddress:test_InvalidEVMAddress_Revert() (gas: 10839) FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress_Success() (gas: 6731) FeeQuoter_validateDestFamilyAddress:test_ValidNonEVMAddress_Success() (gas: 6511) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209230) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135861) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109814) -HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147081) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217718) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 426564) -HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268928) -HybridUSDCTokenPoolMigrationTests:test_ProposeMigration_ChainNotUsingLockRelease_Revert() (gas: 15821) -HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 114166) -HybridUSDCTokenPoolMigrationTests:test_burnLockedUSDC_invalidPermissions_Revert() (gas: 39362) -HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 56207) -HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12736) -HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13373) -HybridUSDCTokenPoolMigrationTests:test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() (gas: 67304) -HybridUSDCTokenPoolMigrationTests:test_cannotRevertChainMechanism_afterMigration_Revert() (gas: 313390) -HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 177033) -HybridUSDCTokenPoolMigrationTests:test_cnanotProvideLiquidity_AfterMigration_Revert() (gas: 313775) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209283) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135879) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109794) +HybridUSDCTokenPoolMigrationTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147082) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217024) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 425913) +HybridUSDCTokenPoolMigrationTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268945) +HybridUSDCTokenPoolMigrationTests:test_ProposeMigration_ChainNotUsingLockRelease_Revert() (gas: 15843) +HybridUSDCTokenPoolMigrationTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 113503) +HybridUSDCTokenPoolMigrationTests:test_burnLockedUSDC_invalidPermissions_Revert() (gas: 39300) +HybridUSDCTokenPoolMigrationTests:test_cancelExistingCCTPMigrationProposal() (gas: 56208) +HybridUSDCTokenPoolMigrationTests:test_cannotCancelANonExistentMigrationProposal() (gas: 12758) +HybridUSDCTokenPoolMigrationTests:test_cannotModifyLiquidityWithoutPermissions_Revert() (gas: 13395) +HybridUSDCTokenPoolMigrationTests:test_cannotProvideLiquidityWhenMigrationProposalPending_Revert() (gas: 67370) +HybridUSDCTokenPoolMigrationTests:test_cannotRevertChainMechanism_afterMigration_Revert() (gas: 313252) +HybridUSDCTokenPoolMigrationTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 177053) +HybridUSDCTokenPoolMigrationTests:test_cnanotProvideLiquidity_AfterMigration_Revert() (gas: 313637) HybridUSDCTokenPoolMigrationTests:test_excludeTokensWhenNoMigrationProposalPending_Revert() (gas: 13657) -HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 309952) -HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 167124) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 156736) -HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 516552) -HybridUSDCTokenPoolTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209230) -HybridUSDCTokenPoolTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135880) -HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109814) -HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147079) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217696) -HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 426520) -HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268910) -HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 114210) -HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 176989) -HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 167107) +HybridUSDCTokenPoolMigrationTests:test_lockOrBurn_then_BurnInCCTPMigration_Success() (gas: 309797) +HybridUSDCTokenPoolMigrationTests:test_transferLiquidity_Success() (gas: 167051) +HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_destChain_Success() (gas: 156096) +HybridUSDCTokenPoolMigrationTests:test_unstickManualTxAfterMigration_homeChain_Success() (gas: 515988) +HybridUSDCTokenPoolTests:test_LockOrBurn_LockReleaseMechanism_then_switchToPrimary_Success() (gas: 209283) +HybridUSDCTokenPoolTests:test_LockOrBurn_PrimaryMechanism_Success() (gas: 135897) +HybridUSDCTokenPoolTests:test_LockOrBurn_WhileMigrationPause_Revert() (gas: 109794) +HybridUSDCTokenPoolTests:test_LockOrBurn_onLockReleaseMechanism_Success() (gas: 147080) +HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_Success() (gas: 217002) +HybridUSDCTokenPoolTests:test_MintOrRelease_OnLockReleaseMechanism_then_switchToPrimary_Success() (gas: 425869) +HybridUSDCTokenPoolTests:test_MintOrRelease_incomingMessageWithPrimaryMechanism() (gas: 268928) +HybridUSDCTokenPoolTests:test_ReleaseOrMint_WhileMigrationPause_Revert() (gas: 113547) +HybridUSDCTokenPoolTests:test_cannotTransferLiquidityDuringPendingMigration_Revert() (gas: 177009) +HybridUSDCTokenPoolTests:test_transferLiquidity_Success() (gas: 167033) LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Revert() (gas: 10989) LockReleaseTokenPoolAndProxy_setRebalancer:test_SetRebalancer_Success() (gas: 18028) LockReleaseTokenPoolPoolAndProxy_canAcceptLiquidity:test_CanAcceptLiquidity_Success() (gas: 3051552) diff --git a/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol index e260300bd7..fd54387620 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol @@ -5,6 +5,7 @@ import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; import {Pool} from "../../libraries/Pool.sol"; import {BurnMintTokenPool} from "../BurnMintTokenPool.sol"; +import {LOCK_RELEASE_FLAG} from "./HybridLockReleaseUSDCTokenPool.sol"; /// @notice A standard BurnMintTokenPool with modified destPoolData so that the remote pool knows to release tokens /// instead of minting. This enables interoperability with HybridLockReleaseUSDCTokenPool which uses @@ -12,9 +13,6 @@ import {BurnMintTokenPool} from "../BurnMintTokenPool.sol"; /// @dev The only difference between this contract and BurnMintTokenPool is the destPoolData returns the /// abi-encoded LOCK_RELEASE_FLAG instead of an empty string. contract BurnMintWithLockReleaseFlagTokenPool is BurnMintTokenPool { - /// bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) - bytes4 public constant LOCK_RELEASE_FLAG = 0xfa7c07de; - constructor( IBurnMintERC20 token, address[] memory allowlist, @@ -24,6 +22,8 @@ contract BurnMintWithLockReleaseFlagTokenPool is BurnMintTokenPool { /// @notice Burn the token in the pool /// @dev The _validateLockOrBurn check is an essential security check + /// @dev Performs the exact same functionality as BurnMintTokenPool, but returns the LOCK_RELEASE_FLAG + /// as the destPoolData to signal to the remote pool to release tokens instead of minting them. function lockOrBurn( Pool.LockOrBurnInV1 calldata lockOrBurnIn ) external override returns (Pool.LockOrBurnOutV1 memory) { @@ -33,6 +33,7 @@ contract BurnMintWithLockReleaseFlagTokenPool is BurnMintTokenPool { emit Burned(msg.sender, lockOrBurnIn.amount); + // LOCK_RELEASE_FLAG = bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) return Pool.LockOrBurnOutV1({ destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: abi.encode(LOCK_RELEASE_FLAG) diff --git a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol index c034fcad75..e61f0cc7dd 100644 --- a/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol +++ b/contracts/src/v0.8/ccip/pools/USDC/HybridLockReleaseUSDCTokenPool.sol @@ -14,6 +14,9 @@ import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +// bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) +bytes4 constant LOCK_RELEASE_FLAG = 0xfa7c07de; + /// @notice A token pool for USDC which uses CCTP for supported chains and Lock/Release for all others /// @dev The functionality from LockReleaseTokenPool.sol has been duplicated due to lack of compiler support for shared /// constructors between parents @@ -34,9 +37,6 @@ contract HybridLockReleaseUSDCTokenPool is USDCTokenPool, USDCBridgeMigrator { error LanePausedForCCTPMigration(uint64 remoteChainSelector); error TokenLockingNotAllowedAfterMigration(uint64 remoteChainSelector); - /// bytes4(keccak256("NO_CCTP_USE_LOCK_RELEASE")) - bytes4 public constant LOCK_RELEASE_FLAG = 0xfa7c07de; - /// @notice The address of the liquidity provider for a specific chain. /// External liquidity is not required when there is one canonical token deployed to a chain, /// and CCIP is facilitating mint/burn on all the other chains, in which case the invariant diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol index c84fc2efc5..9fc7e160e9 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol @@ -5,6 +5,8 @@ import {Pool} from "../../libraries/Pool.sol"; import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {TokenPool} from "../../pools/TokenPool.sol"; import {BurnMintWithLockReleaseFlagTokenPool} from "../../pools/USDC/BurnMintWithLockReleaseFlagTokenPool.sol"; + +import {LOCK_RELEASE_FLAG} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; import {BurnMintSetup} from "./BurnMintSetup.t.sol"; import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; @@ -57,6 +59,6 @@ contract BurnMintWithLockReleaseFlagTokenPool_lockOrBurn is BurnMintWithLockRele assertEq(s_burnMintERC677.balanceOf(address(s_pool)), 0); - assertEq(bytes4(lockOrBurnOut.destPoolData), s_pool.LOCK_RELEASE_FLAG()); + assertEq(bytes4(lockOrBurnOut.destPoolData), LOCK_RELEASE_FLAG); } } diff --git a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol index de92219435..60f3c6720f 100644 --- a/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/HybridLockReleaseUSDCTokenPool.t.sol @@ -14,6 +14,7 @@ import {RateLimiter} from "../../libraries/RateLimiter.sol"; import {TokenPool} from "../../pools/TokenPool.sol"; import {HybridLockReleaseUSDCTokenPool} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {LOCK_RELEASE_FLAG} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; import {USDCBridgeMigrator} from "../../pools/USDC/USDCBridgeMigrator.sol"; import {USDCTokenPool} from "../../pools/USDC/USDCTokenPool.sol"; import {BaseTest} from "../BaseTest.t.sol"; @@ -224,7 +225,7 @@ contract HybridUSDCTokenPoolTests is USDCTokenPoolSetup { localToken: address(s_token), remoteChainSelector: SOURCE_CHAIN_SELECTOR, sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: abi.encode(s_usdcTokenPool.LOCK_RELEASE_FLAG()), + sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), offchainTokenData: "" }) ); @@ -442,7 +443,7 @@ contract HybridUSDCTokenPoolTests is USDCTokenPoolSetup { destGasAmount: USDC_DEST_TOKEN_GAS }); - bytes memory sourcePoolDataLockRelease = abi.encode(s_usdcTokenPool.LOCK_RELEASE_FLAG()); + bytes memory sourcePoolDataLockRelease = abi.encode(LOCK_RELEASE_FLAG); uint256 amount = 1e6; @@ -757,7 +758,7 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { localToken: address(s_token), remoteChainSelector: SOURCE_CHAIN_SELECTOR, sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: abi.encode(s_usdcTokenPool.LOCK_RELEASE_FLAG()), + sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), offchainTokenData: "" }) ); @@ -876,7 +877,7 @@ contract HybridUSDCTokenPoolMigrationTests is HybridUSDCTokenPoolTests { localToken: address(s_token), remoteChainSelector: SOURCE_CHAIN_SELECTOR, sourcePoolAddress: sourceTokenData.sourcePoolAddress, - sourcePoolData: abi.encode(s_usdcTokenPool.LOCK_RELEASE_FLAG()), + sourcePoolData: abi.encode(LOCK_RELEASE_FLAG), offchainTokenData: "" }) ); From 11bd6350d9f13f387caf52e387841c1478466113 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 22 Oct 2024 13:54:57 -0400 Subject: [PATCH 7/7] test rename --- contracts/gas-snapshots/ccip.gas-snapshot | 2 +- .../ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index 3695e6d458..ea2a893702 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -30,7 +30,7 @@ BurnMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 17851) BurnMintTokenPool_releaseOrMint:test_ChainNotAllowed_Revert() (gas: 28805) BurnMintTokenPool_releaseOrMint:test_PoolMintNotHealthy_Revert() (gas: 56253) BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 112391) -BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_PoolBurn_CorrectReturnData_Success() (gas: 242252) +BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_LockOrBurn_CorrectReturnData_Success() (gas: 242268) BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271) BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050) diff --git a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol index 9fc7e160e9..c9080a0e14 100644 --- a/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/BurnMintWithLockReleaseFlagTokenPool.t.sol @@ -27,7 +27,7 @@ contract BurnMintWithLockReleaseFlagTokenPoolSetup is BurnMintSetup { } contract BurnMintWithLockReleaseFlagTokenPool_lockOrBurn is BurnMintWithLockReleaseFlagTokenPoolSetup { - function test_PoolBurn_CorrectReturnData_Success() public { + function test_LockOrBurn_CorrectReturnData_Success() public { uint256 burnAmount = 20_000e18; deal(address(s_burnMintERC677), address(s_pool), burnAmount);