Skip to content

Commit

Permalink
add token pool factory (#1553)
Browse files Browse the repository at this point in the history
  • Loading branch information
RensR authored Nov 29, 2024
1 parent bca2fe0 commit eb419a0
Show file tree
Hide file tree
Showing 25 changed files with 1,917 additions and 8 deletions.
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/[email protected].0 latest",
"publish-prod": "npm dist-tag add @chainlink/[email protected].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

0 comments on commit eb419a0

Please sign in to comment.