diff --git a/contracts/gas-snapshots/ccip.gas-snapshot b/contracts/gas-snapshots/ccip.gas-snapshot index a361de5134..3a717c5235 100644 --- a/contracts/gas-snapshots/ccip.gas-snapshot +++ b/contracts/gas-snapshots/ccip.gas-snapshot @@ -343,7 +343,7 @@ FactoryBurnMintERC20burnFromAlias:testBurnFromSuccess() (gas: 58187) FactoryBurnMintERC20burnFromAlias:testExceedsBalanceReverts() (gas: 36094) FactoryBurnMintERC20burnFromAlias:testInsufficientAllowanceReverts() (gas: 22009) FactoryBurnMintERC20burnFromAlias:testSenderNotBurnerReverts() (gas: 13446) -FactoryBurnMintERC20constructor:testConstructorSuccess() (gas: 1495659) +FactoryBurnMintERC20constructor:testConstructorSuccess() (gas: 1495459) FactoryBurnMintERC20decreaseApproval:testDecreaseApprovalSuccess() (gas: 31323) FactoryBurnMintERC20grantMintAndBurnRoles:testGrantMintAndBurnRolesSuccess() (gas: 121439) FactoryBurnMintERC20grantRole:testGrantBurnAccessSuccess() (gas: 53612) @@ -408,20 +408,20 @@ FeeQuoter_getValidatedFee:test_NotAFeeToken_Revert() (gas: 21172) FeeQuoter_getValidatedFee:test_SingleTokenMessage_Success() (gas: 113309) FeeQuoter_getValidatedFee:test_TooManyTokens_Revert() (gas: 22691) FeeQuoter_getValidatedFee:test_ZeroDataAvailabilityMultiplier_Success() (gas: 62714) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Above18Decimals_Success() (gas: 1973907) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Below18Decimals_Success() (gas: 1973865) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt0Decimals_Success() (gas: 1953984) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt18Decimals_Success() (gas: 1973639) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFlippedDecimals_Success() (gas: 1973843) -FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedMaxInt224Value_Success() (gas: 1973655) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Above18Decimals_Success() (gas: 1973707) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedErc20Below18Decimals_Success() (gas: 1973665) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt0Decimals_Success() (gas: 1953784) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFeedAt18Decimals_Success() (gas: 1973439) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedFlippedDecimals_Success() (gas: 1973643) +FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedMaxInt224Value_Success() (gas: 1973455) FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeedOverStalenessPeriod_Success() (gas: 64610) FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPriceFromFeed_Success() (gas: 64490) FeeQuoter_getValidatedTokenPrice:test_GetValidatedTokenPrice_Success() (gas: 58894) -FeeQuoter_getValidatedTokenPrice:test_OverflowFeedPrice_Revert() (gas: 1973352) +FeeQuoter_getValidatedTokenPrice:test_OverflowFeedPrice_Revert() (gas: 1973152) FeeQuoter_getValidatedTokenPrice:test_StaleFeeToken_Success() (gas: 61764) FeeQuoter_getValidatedTokenPrice:test_TokenNotSupportedFeed_Revert() (gas: 116495) FeeQuoter_getValidatedTokenPrice:test_TokenNotSupported_Revert() (gas: 14037) -FeeQuoter_getValidatedTokenPrice:test_UnderflowFeedPrice_Revert() (gas: 1972029) +FeeQuoter_getValidatedTokenPrice:test_UnderflowFeedPrice_Revert() (gas: 1971829) FeeQuoter_onReport:test_OnReport_StaleUpdate_Revert() (gas: 43631) FeeQuoter_onReport:test_onReport_InvalidForwarder_Reverts() (gas: 23492) FeeQuoter_onReport:test_onReport_Success() (gas: 80094) @@ -1023,12 +1023,12 @@ TokenPoolAndProxy:test_lockOrBurn_lockRelease_Success() (gas: 5793246) TokenPoolAndProxy:test_setPreviousPool_Success() (gas: 3070731) TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_2() (gas: 6434801) TokenPoolAndProxyMigration:test_tokenPoolMigration_Success_1_4() (gas: 6634934) -TokenPoolFactoryTests:test_TokenPoolFactory_Constructor_Revert() (gas: 1188046) -TokenPoolFactoryTests:test_createTokenPoolLockRelease_NoExistingToken_predict_Success() (gas: 12420072) -TokenPoolFactoryTests:test_createTokenPool_ExistingRemoteToken_AndPredictPool_Success() (gas: 12434881) -TokenPoolFactoryTests:test_createTokenPool_WithNoExistingRemoteContracts_predict_Success() (gas: 12702418) -TokenPoolFactoryTests:test_createTokenPool_WithNoExistingTokenOnRemoteChain_Success() (gas: 5740593) -TokenPoolFactoryTests:test_createTokenPool_WithRemoteTokenAndRemotePool_Success() (gas: 5880940) +TokenPoolFactoryTests:test_TokenPoolFactory_Constructor_Revert() (gas: 1206695) +TokenPoolFactoryTests:test_createTokenPoolLockRelease_NoExistingToken_predict_Success() (gas: 12624842) +TokenPoolFactoryTests:test_createTokenPool_ExistingRemoteToken_AndPredictPool_Success() (gas: 12546380) +TokenPoolFactoryTests:test_createTokenPool_WithNoExistingRemoteContracts_predict_Success() (gas: 12887535) +TokenPoolFactoryTests:test_createTokenPool_WithNoExistingTokenOnRemoteChain_Success() (gas: 5833663) +TokenPoolFactoryTests:test_createTokenPool_WithRemoteTokenAndRemotePool_Success() (gas: 5974010) TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 1979943) TokenPoolWithAllowList_applyAllowListUpdates:test_OnlyOwner_Revert() (gas: 12113) TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowListSkipsZero_Success() (gas: 23476) diff --git a/contracts/src/v0.8/ccip/test/tokenAdminRegistry/TokenPoolFactory.t.sol b/contracts/src/v0.8/ccip/test/tokenAdminRegistry/TokenPoolFactory.t.sol index 0a1f2a66f3..b7e9adf599 100644 --- a/contracts/src/v0.8/ccip/test/tokenAdminRegistry/TokenPoolFactory.t.sol +++ b/contracts/src/v0.8/ccip/test/tokenAdminRegistry/TokenPoolFactory.t.sol @@ -1,5 +1,7 @@ +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.24; +import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; import {IOwner} from "../../interfaces/IOwner.sol"; import {ITokenAdminRegistry} from "../../interfaces/ITokenAdminRegistry.sol"; @@ -154,7 +156,7 @@ contract TokenPoolFactoryTests is TokenPoolFactorySetup { // Since the remote chain information was provided, we should be able to get the information from the newly // deployed token pool using the available getter functions - (, address poolAddress) = s_tokenPoolFactory.deployTokenAndTokenPool( + (address tokenAddress, address poolAddress) = s_tokenPoolFactory.deployTokenAndTokenPool( remoteTokenPools, // No existing remote pools s_tokenInitCode, // Token Init Code s_poolInitCode, // Pool Init Code @@ -212,6 +214,39 @@ contract TokenPoolFactoryTests is TokenPoolFactorySetup { abi.encode(newTokenAddress), "New Token Address should have been deployed correctly" ); + + // Check that the token pool has the correct permissions + vm.startPrank(poolAddress); + IBurnMintERC20(tokenAddress).mint(poolAddress, 1e18); + + assertEq(1e18, IBurnMintERC20(tokenAddress).balanceOf(poolAddress), "Balance should be 1e18"); + + IBurnMintERC20(tokenAddress).burn(1e18); + assertEq(0, IBurnMintERC20(tokenAddress).balanceOf(poolAddress), "Balance should be 0"); + + vm.stopPrank(); + + assertEq(s_tokenAdminRegistry.getPool(tokenAddress), poolAddress, "Token Pool should be set"); + + // Check the token admin registry for config + TokenAdminRegistry.TokenConfig memory tokenConfig = s_tokenAdminRegistry.getTokenConfig(tokenAddress); + assertEq(tokenConfig.administrator, address(s_tokenPoolFactory), "Administrator should be set"); + assertEq(tokenConfig.pendingAdministrator, OWNER, "Pending Administrator should be 0"); + assertEq(tokenConfig.tokenPool, poolAddress, "Pool Address should be set"); + + // Accept Ownership of the token, pool, and adminRegistry + vm.startPrank(OWNER); + s_tokenAdminRegistry.acceptAdminRole(tokenAddress); + assertEq(s_tokenAdminRegistry.getTokenConfig(tokenAddress).administrator, OWNER, "Administrator should be set"); + assertEq( + s_tokenAdminRegistry.getTokenConfig(tokenAddress).pendingAdministrator, address(0), "Administrator should be set" + ); + + OwnerIsCreator(tokenAddress).acceptOwnership(); + OwnerIsCreator(poolAddress).acceptOwnership(); + + assertEq(IOwner(tokenAddress).owner(), OWNER, "Token should be controlled by the OWNER"); + assertEq(IOwner(poolAddress).owner(), OWNER, "Pool should be controlled by the OWNER"); } function test_createTokenPool_ExistingRemoteToken_AndPredictPool_Success() public { diff --git a/contracts/src/v0.8/ccip/tokenAdminRegistry/TokenPoolFactory/TokenPoolFactory.sol b/contracts/src/v0.8/ccip/tokenAdminRegistry/TokenPoolFactory/TokenPoolFactory.sol index f76a0f63eb..b545a601e2 100644 --- a/contracts/src/v0.8/ccip/tokenAdminRegistry/TokenPoolFactory/TokenPoolFactory.sol +++ b/contracts/src/v0.8/ccip/tokenAdminRegistry/TokenPoolFactory/TokenPoolFactory.sol @@ -1,7 +1,9 @@ +// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; import {IOwnable} from "../../../shared/interfaces/IOwnable.sol"; import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; +import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol"; import {ITokenAdminRegistry} from "../../interfaces/ITokenAdminRegistry.sol"; import {OwnerIsCreator} from "../../../shared/access/OwnerIsCreator.sol"; @@ -113,6 +115,9 @@ contract TokenPoolFactory is OwnerIsCreator, ITypeAndVersion { // Deploy the token pool address pool = _createTokenPool(token, remoteTokenPools, tokenPoolInitCode, salt, poolType); + // Grant the mint and burn roles to the pool for the token + IBurnMintERC20(token).grantMintAndBurnRoles(pool); + // Set the token pool for token in the token admin registry since this contract is the token and pool owner _setTokenPoolInTokenAdminRegistry(token, pool); @@ -126,6 +131,7 @@ contract TokenPoolFactory is OwnerIsCreator, ITypeAndVersion { /// @dev Since the token already exists, this contract is not the owner and therefore cannot configure the /// token pool in the token admin registry in the same transaction. The user must invoke the calls to the /// tokenAdminRegistry manually + /// @dev since the token already exists, the owner must grant the mint and burn roles to the pool manually /// @param token The address of the existing token to be used in the token pool /// @param remoteTokenPools An array of remote token pools info to be used in the pool's applyChainUpdates function /// @param tokenPoolInitCode The creation code for the token pool diff --git a/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol b/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol index b9b3b54bf7..0ee844d34f 100644 --- a/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol +++ b/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol @@ -26,4 +26,6 @@ interface IBurnMintERC20 is IERC20 { /// @param amount The number of tokens to be burned. /// @dev this function decreases the total supply. function burnFrom(address account, uint256 amount) external; + + function grantMintAndBurnRoles(address account) external; }