Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add token pool factory #1553

Merged
merged 2 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,35 @@ EtherSenderReceiverTest_validatedMessage:test_validatedMessage_emptyDataOverwrit
EtherSenderReceiverTest_validatedMessage:test_validatedMessage_invalidTokenAmounts() (gas: 17925)
EtherSenderReceiverTest_validatedMessage:test_validatedMessage_tokenOverwrittenToWeth() (gas: 25329)
EtherSenderReceiverTest_validatedMessage:test_validatedMessage_validMessage_extraArgs() (gas: 26370)
FactoryBurnMintERC20_approve:test_Approve_Success() (gas: 55766)
FactoryBurnMintERC20_approve:test_InvalidAddress_Reverts() (gas: 10709)
FactoryBurnMintERC20_burn:test_BasicBurn_Success() (gas: 172367)
FactoryBurnMintERC20_burn:test_BurnFromZeroAddress_Reverts() (gas: 47272)
FactoryBurnMintERC20_burn:test_ExceedsBalance_Reverts() (gas: 21939)
FactoryBurnMintERC20_burn:test_SenderNotBurner_Reverts() (gas: 13493)
FactoryBurnMintERC20_burnFrom:test_BurnFrom_Success() (gas: 58231)
FactoryBurnMintERC20_burnFrom:test_ExceedsBalance_Reverts() (gas: 36138)
FactoryBurnMintERC20_burnFrom:test_InsufficientAllowance_Reverts() (gas: 22031)
FactoryBurnMintERC20_burnFrom:test_SenderNotBurner_Reverts() (gas: 13460)
FactoryBurnMintERC20_burnFromAlias:test_BurnFrom_Success() (gas: 58205)
FactoryBurnMintERC20_burnFromAlias:test_ExceedsBalance_Reverts() (gas: 36102)
FactoryBurnMintERC20_burnFromAlias:test_InsufficientAllowance_Reverts() (gas: 21986)
FactoryBurnMintERC20_burnFromAlias:test_SenderNotBurner_Reverts() (gas: 13415)
FactoryBurnMintERC20_constructor:test_Constructor_Success() (gas: 1475646)
FactoryBurnMintERC20_decreaseApproval:test_DecreaseApproval_Success() (gas: 31340)
FactoryBurnMintERC20_getCCIPAdmin:test_getCCIPAdmin_Success() (gas: 12684)
FactoryBurnMintERC20_getCCIPAdmin:test_setCCIPAdmin_Success() (gas: 23757)
FactoryBurnMintERC20_grantMintAndBurnRoles:test_GrantMintAndBurnRoles_Success() (gas: 121084)
FactoryBurnMintERC20_grantRole:test_GrantBurnAccess_Success() (gas: 53341)
FactoryBurnMintERC20_grantRole:test_GrantMany_Success() (gas: 961332)
FactoryBurnMintERC20_grantRole:test_GrantMintAccess_Success() (gas: 94068)
FactoryBurnMintERC20_increaseApproval:test_IncreaseApproval_Success() (gas: 44345)
FactoryBurnMintERC20_mint:test_BasicMint_Success() (gas: 149777)
FactoryBurnMintERC20_mint:test_MaxSupplyExceeded_Reverts() (gas: 50681)
FactoryBurnMintERC20_mint:test_SenderNotMinter_Reverts() (gas: 11372)
FactoryBurnMintERC20_supportsInterface:test_SupportsInterface_Success() (gas: 11439)
FactoryBurnMintERC20_transfer:test_InvalidAddress_Reverts() (gas: 10707)
FactoryBurnMintERC20_transfer:test_Transfer_Success() (gas: 42449)
FeeQuoter_applyDestChainConfigUpdates:test_InvalidChainFamilySelector_Revert() (gas: 16878)
FeeQuoter_applyDestChainConfigUpdates:test_InvalidDestChainConfigDestChainSelectorEqZero_Revert() (gas: 16780)
FeeQuoter_applyDestChainConfigUpdates:test_applyDestChainConfigUpdatesDefaultTxGasLimitEqZero_Revert() (gas: 16822)
Expand Down Expand Up @@ -957,6 +986,14 @@ TokenAdminRegistry_setPool:test_setPool_Success() (gas: 36135)
TokenAdminRegistry_setPool:test_setPool_ZeroAddressRemovesPool_Success() (gas: 30842)
TokenAdminRegistry_transferAdminRole:test_transferAdminRole_OnlyAdministrator_Revert() (gas: 18103)
TokenAdminRegistry_transferAdminRole:test_transferAdminRole_Success() (gas: 49438)
TokenPoolFactory_constructor:test_constructor_Revert() (gas: 1121620)
TokenPoolFactory_createTokenPool:test_createTokenPoolLockRelease_ExistingToken_predict_Success() (gas: 12535175)
TokenPoolFactory_createTokenPool:test_createTokenPool_BurnFromMintTokenPool_Success() (gas: 6414943)
TokenPoolFactory_createTokenPool:test_createTokenPool_ExistingRemoteToken_AndPredictPool_Success() (gas: 13284620)
TokenPoolFactory_createTokenPool:test_createTokenPool_RemoteTokenHasDifferentDecimals_Success() (gas: 13291989)
TokenPoolFactory_createTokenPool:test_createTokenPool_WithNoExistingRemoteContracts_predict_Success() (gas: 13622819)
TokenPoolFactory_createTokenPool:test_createTokenPool_WithNoExistingTokenOnRemoteChain_Success() (gas: 6201644)
TokenPoolFactory_createTokenPool:test_createTokenPool_WithRemoteTokenAndRemotePool_Success() (gas: 6411396)
TokenPoolWithAllowList_applyAllowListUpdates:test_AllowListNotEnabled_Revert() (gas: 2732579)
TokenPoolWithAllowList_applyAllowListUpdates:test_OnlyOwner_Revert() (gas: 12059)
TokenPoolWithAllowList_applyAllowListUpdates:test_SetAllowListSkipsZero_Success() (gas: 23512)
Expand Down
15 changes: 7 additions & 8 deletions contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@chainlink/contracts-ccip",
"version": "1.5.0",
"version": "1.5.1",
"description": "Chainlink CCIP smart contracts",
"author": "Chainlink devs",
"license": "BUSL-1.1",
Expand All @@ -21,16 +21,13 @@
"prepare": "chmod +x .husky/prepare.sh && ./.husky/prepare.sh",
"prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder",
"publish-beta": "pnpm publish --tag beta",
"publish-prod": "npm dist-tag add @chainlink/contracts-ccip@1.5.0 latest",
"publish-prod": "npm dist-tag add @chainlink/contracts-ccip@1.5.1 latest",
"solhint:ccip": "solhint --max-warnings 0 \"./src/v0.8/ccip/**/*.sol\"",
"solhint": "solhint --max-warnings 0 \"./src/v0.8/**/*.sol\""
},
"files": [
"src/v0.8/ccip/**/*.sol",
"src/v0.8/shared/access/ConfirmedOwner.sol",
"src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol",
"src/v0.8/shared/access/OwnerIsCreator.sol",
"src/v0.8/shared/access/AuthorizedCallers.sol",
"src/v0.8/shared/access/*.sol",
"src/v0.8/shared/call/CallWithExactGas.sol",
"src/v0.8/shared/enumerable/EnumerableMapBytes32.sol",
"src/v0.8/shared/enumerable/EnumerableMapAddresses.sol",
Expand All @@ -45,7 +42,6 @@
"src/v0.8/shared/token/ERC677/BurnMintERC677.sol",
"src/v0.8/shared/util/SortedSetValidationUtil.sol",
"src/v0.8/liquiditymanager/interfaces/ILiquidityContainer.sol",
"src/v0.8/keystone/interfaces/ICapabilityConfiguration.sol",
"src/v0.8/vendor/openzeppelin-solidity",
"src/v0.8/vendor/Context.sol",
"src/v0.8/vendor/Pausable.sol",
Expand All @@ -61,7 +57,10 @@
"!src/v0.8/ccip/ocr/MultiOCR3Base.sol",
"!src/v0.8/ccip/NonceManager.sol",
"!src/v0.8/ccip/MultiAggregateRateLimiter.sol",
"!src/v0.8/ccip/capability"
"!src/v0.8/ccip/capability",
"!src/v0.8/ccip/FeeQuoter.sol",
"!src/v0.8/ccip/interfaces/encodingutils/ICCIPEncodingUtils.sol",
"!src/v0.8/ccip/rmn"
],
"pnpm": {
"_comment": "See https://github.com/ethers-io/ethers.js/discussions/2849#discussioncomment-2696454",
Expand Down
20 changes: 20 additions & 0 deletions contracts/src/v0.8/ccip/interfaces/ITokenAdminRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,24 @@ interface ITokenAdminRegistry {
/// @param localToken The token to register the administrator for.
/// @param administrator The administrator to register.
function proposeAdministrator(address localToken, address administrator) external;

/// @notice Accepts the administrator role for a token.
/// @param localToken The token to accept the administrator role for.
/// @dev This function can only be called by the pending administrator.
function acceptAdminRole(
address localToken
) external;

/// @notice Sets the pool for a token. Setting the pool to address(0) effectively delists the token
/// from CCIP. Setting the pool to any other address enables the token on CCIP.
/// @param localToken The token to set the pool for.
/// @param pool The pool to set for the token.
function setPool(address localToken, address pool) external;

/// @notice Transfers the administrator role for a token to a new address with a 2-step process.
/// @param localToken The token to transfer the administrator role for.
/// @param newAdmin The address to transfer the administrator role to. Can be address(0) to cancel
/// a pending transfer.
/// @dev The new admin must call `acceptAdminRole` to accept the role.
function transferAdminRole(address localToken, address newAdmin) external;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {FactoryBurnMintERC20} from "../../../tokenAdminRegistry/TokenPoolFactory/FactoryBurnMintERC20.sol";
import {BaseTest} from "../../BaseTest.t.sol";

contract BurnMintERC20Setup is BaseTest {
FactoryBurnMintERC20 internal s_burnMintERC20;

address internal s_mockPool = makeAddr("s_mockPool");
uint256 internal s_amount = 1e18;

address internal s_alice;

function setUp() public virtual override {
BaseTest.setUp();

s_alice = makeAddr("alice");

s_burnMintERC20 = new FactoryBurnMintERC20("Chainlink Token", "LINK", 18, 1e27, 0, s_alice);

// Set s_mockPool to be a burner and minter
s_burnMintERC20.grantMintAndBurnRoles(s_mockPool);
deal(address(s_burnMintERC20), OWNER, s_amount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_approve is BurnMintERC20Setup {
function test_Approve_Success() public {
uint256 balancePre = s_burnMintERC20.balanceOf(STRANGER);
uint256 sendingAmount = s_amount / 2;

s_burnMintERC20.approve(STRANGER, sendingAmount);

changePrank(STRANGER);

s_burnMintERC20.transferFrom(OWNER, STRANGER, sendingAmount);

assertEq(sendingAmount + balancePre, s_burnMintERC20.balanceOf(STRANGER));
}

// Reverts

function test_InvalidAddress_Reverts() public {
vm.expectRevert();

s_burnMintERC20.approve(address(s_burnMintERC20), s_amount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {FactoryBurnMintERC20} from "../../../tokenAdminRegistry/TokenPoolFactory/FactoryBurnMintERC20.sol";
import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_burn is BurnMintERC20Setup {
function test_BasicBurn_Success() public {
s_burnMintERC20.grantBurnRole(OWNER);
deal(address(s_burnMintERC20), OWNER, s_amount);

vm.expectEmit();
emit IERC20.Transfer(OWNER, address(0), s_amount);

s_burnMintERC20.burn(s_amount);

assertEq(0, s_burnMintERC20.balanceOf(OWNER));
}

// Revert

function test_SenderNotBurner_Reverts() public {
vm.expectRevert(abi.encodeWithSelector(FactoryBurnMintERC20.SenderNotBurner.selector, OWNER));

s_burnMintERC20.burnFrom(STRANGER, s_amount);
}

function test_ExceedsBalance_Reverts() public {
changePrank(s_mockPool);

vm.expectRevert("ERC20: burn amount exceeds balance");

s_burnMintERC20.burn(s_amount * 2);
}

function test_BurnFromZeroAddress_Reverts() public {
s_burnMintERC20.grantBurnRole(address(0));
changePrank(address(0));

vm.expectRevert("ERC20: burn from the zero address");

s_burnMintERC20.burn(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {FactoryBurnMintERC20} from "../../../tokenAdminRegistry/TokenPoolFactory/FactoryBurnMintERC20.sol";
import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_burnFrom is BurnMintERC20Setup {
function setUp() public virtual override {
BurnMintERC20Setup.setUp();
}

function test_BurnFrom_Success() public {
s_burnMintERC20.approve(s_mockPool, s_amount);

changePrank(s_mockPool);

s_burnMintERC20.burnFrom(OWNER, s_amount);

assertEq(0, s_burnMintERC20.balanceOf(OWNER));
}

// Reverts

function test_SenderNotBurner_Reverts() public {
vm.expectRevert(abi.encodeWithSelector(FactoryBurnMintERC20.SenderNotBurner.selector, OWNER));

s_burnMintERC20.burnFrom(OWNER, s_amount);
}

function test_InsufficientAllowance_Reverts() public {
changePrank(s_mockPool);

vm.expectRevert("ERC20: insufficient allowance");

s_burnMintERC20.burnFrom(OWNER, s_amount);
}

function test_ExceedsBalance_Reverts() public {
s_burnMintERC20.approve(s_mockPool, s_amount * 2);

changePrank(s_mockPool);

vm.expectRevert("ERC20: burn amount exceeds balance");

s_burnMintERC20.burnFrom(OWNER, s_amount * 2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {FactoryBurnMintERC20} from "../../../tokenAdminRegistry/TokenPoolFactory/FactoryBurnMintERC20.sol";
import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_burnFromAlias is BurnMintERC20Setup {
function setUp() public virtual override {
BurnMintERC20Setup.setUp();
}

function test_BurnFrom_Success() public {
s_burnMintERC20.approve(s_mockPool, s_amount);

changePrank(s_mockPool);

s_burnMintERC20.burn(OWNER, s_amount);

assertEq(0, s_burnMintERC20.balanceOf(OWNER));
}

// Reverts

function test_SenderNotBurner_Reverts() public {
vm.expectRevert(abi.encodeWithSelector(FactoryBurnMintERC20.SenderNotBurner.selector, OWNER));

s_burnMintERC20.burn(OWNER, s_amount);
}

function test_InsufficientAllowance_Reverts() public {
changePrank(s_mockPool);

vm.expectRevert("ERC20: insufficient allowance");

s_burnMintERC20.burn(OWNER, s_amount);
}

function test_ExceedsBalance_Reverts() public {
s_burnMintERC20.approve(s_mockPool, s_amount * 2);

changePrank(s_mockPool);

vm.expectRevert("ERC20: burn amount exceeds balance");

s_burnMintERC20.burn(OWNER, s_amount * 2);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {FactoryBurnMintERC20} from "../../../tokenAdminRegistry/TokenPoolFactory/FactoryBurnMintERC20.sol";
import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_constructor is BurnMintERC20Setup {
function test_Constructor_Success() public {
string memory name = "Chainlink token v2";
string memory symbol = "LINK2";
uint8 decimals = 19;
uint256 maxSupply = 1e33;

s_burnMintERC20 = new FactoryBurnMintERC20(name, symbol, decimals, maxSupply, 1e18, s_alice);

assertEq(name, s_burnMintERC20.name());
assertEq(symbol, s_burnMintERC20.symbol());
assertEq(decimals, s_burnMintERC20.decimals());
assertEq(maxSupply, s_burnMintERC20.maxSupply());

assertTrue(s_burnMintERC20.isMinter(s_alice));
assertTrue(s_burnMintERC20.isBurner(s_alice));
assertEq(s_burnMintERC20.balanceOf(s_alice), 1e18);
assertEq(s_burnMintERC20.totalSupply(), 1e18);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_decreaseApproval is BurnMintERC20Setup {
function test_DecreaseApproval_Success() public {
s_burnMintERC20.approve(s_mockPool, s_amount);
uint256 allowance = s_burnMintERC20.allowance(OWNER, s_mockPool);
assertEq(allowance, s_amount);
s_burnMintERC20.decreaseApproval(s_mockPool, s_amount);
assertEq(s_burnMintERC20.allowance(OWNER, s_mockPool), allowance - s_amount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {FactoryBurnMintERC20} from "../../../tokenAdminRegistry/TokenPoolFactory/FactoryBurnMintERC20.sol";
import {BurnMintERC20Setup} from "./BurnMintERC20Setup.t.sol";

contract FactoryBurnMintERC20_getCCIPAdmin is BurnMintERC20Setup {
function test_getCCIPAdmin_Success() public view {
assertEq(s_alice, s_burnMintERC20.getCCIPAdmin());
}

function test_setCCIPAdmin_Success() public {
address newAdmin = makeAddr("newAdmin");

vm.expectEmit();
emit FactoryBurnMintERC20.CCIPAdminTransferred(s_alice, newAdmin);

s_burnMintERC20.setCCIPAdmin(newAdmin);

assertEq(newAdmin, s_burnMintERC20.getCCIPAdmin());
}
}
Loading
Loading