Skip to content

Commit

Permalink
CCIP-4269 - Make registered TokenPools swappable (#15293)
Browse files Browse the repository at this point in the history
* store hash of (dest chain selector, remote pool) to do lookup

* fill in coverage gaps and fix zero address check

* remove comment

* Update gethwrappers

* reduce test code

* add test multiple remote pools

* improve tests

* rename and rm EVM specific checks

* [Bot] Update changeset file with jira issues

* fix offchain tests

* use mapping over set

* move remotePools mapping to RemoteChainConfig

* use bytes32 set over bytes mapping

* fix ci

* allow multiple pools on remote chain config

* support different decimals (#15310)

* use calldata over memory, extract isRemotePool

* support different decimals

* pass in decimals

* add tests

* fix liqman, tests, gen code

* fix offchain

* add comment, changeset and fix lint

* turn off 1.4 test

* CCIP-4331 update factory contract to use new arbitrary token decimals in constructor (#15337)

* support different decimals

* pass in decimals

* add tests

* gen wrappers

* update factory contract to use new arbitrary token decimals in constructor

* snapshot fix

---------

Co-authored-by: Rens Rooimans <github@rensrooimans.nl>

---------

Co-authored-by: Rens Rooimans <github@rensrooimans.nl>
Co-authored-by: Josh <josh.weintraub@smartcontract.com>
Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
Co-authored-by: Josh Weintraub <26035072+jhweintraub@users.noreply.github.com>
  • Loading branch information
5 people authored Nov 22, 2024
1 parent 978ece5 commit ea7b4bb
Show file tree
Hide file tree
Showing 81 changed files with 3,219 additions and 2,394 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-keys-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

allow different decimals on different chains for token pools
13 changes: 0 additions & 13 deletions .github/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1109,19 +1109,6 @@ runner-test-matrix:
test_cmd: cd integration-tests/ccip-tests/smoke && go test ccip_test.go -test.run ^TestSmokeCCIPForBidirectionalLane$ -timeout 30m -count=1 -test.parallel=1 -json
test_env_vars:
E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2

- id: ccip-smoke-1.4-pools
path: integration-tests/ccip-tests/smoke/ccip_test.go
test_env_type: docker
runs_on: ubuntu-latest
triggers:
- PR E2E CCIP Tests
- Merge Queue E2E CCIP Tests
- Nightly E2E Tests
test_cmd: cd integration-tests/ccip-tests/smoke && go test ccip_test.go -test.run ^TestSmokeCCIPForBidirectionalLane$ -timeout 30m -count=1 -test.parallel=1 -json
test_env_vars:
E2E_TEST_SELECTED_NETWORK: SIMULATED_1,SIMULATED_2
test_config_override_path: integration-tests/ccip-tests/testconfig/tomls/contract-version1.4.toml

- id: ccip-smoke-usdc
path: integration-tests/ccip-tests/smoke/ccip_test.go
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/solidity-foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
{ "name": "functions", "setup": { "run-coverage": false, "min-coverage": 98.5, "run-gas-snapshot": true, "run-forge-fmt": false }},
{ "name": "keystone", "setup": { "run-coverage": true, "min-coverage": 72.8, "run-gas-snapshot": false, "run-forge-fmt": false }},
{ "name": "l2ep", "setup": { "run-coverage": true, "min-coverage": 61.0, "run-gas-snapshot": true, "run-forge-fmt": false }},
{ "name": "liquiditymanager", "setup": { "run-coverage": true, "min-coverage": 46.3, "run-gas-snapshot": true, "run-forge-fmt": false }},
{ "name": "liquiditymanager", "setup": { "run-coverage": true, "min-coverage": 44, "run-gas-snapshot": true, "run-forge-fmt": false }},
{ "name": "llo-feeds", "setup": { "run-coverage": true, "min-coverage": 49.3, "run-gas-snapshot": true, "run-forge-fmt": false }},
{ "name": "operatorforwarder", "setup": { "run-coverage": true, "min-coverage": 55.7, "run-gas-snapshot": true, "run-forge-fmt": false }},
{ "name": "shared", "setup": { "run-coverage": true, "extra-coverage-params": "--no-match-path='*CallWithExactGas*' --ir-minimum", "min-coverage": 32.6, "run-gas-snapshot": true, "run-forge-fmt": false }},
Expand Down
10 changes: 10 additions & 0 deletions contracts/.changeset/modern-mayflies-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@chainlink/contracts': patch
---

allow multiple remote pools per chain selector


PR issue: CCIP-4269

Solidity Review issue: CCIP-3966
5 changes: 5 additions & 0 deletions contracts/.changeset/ninety-lions-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/contracts': patch
---

Update token pool factory to support new token pool design with arbitrary decimals #bugfix
402 changes: 205 additions & 197 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions contracts/gas-snapshots/liquiditymanager.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ LiquidityManager_addLiquidity:test_addLiquiditySuccess() (gas: 279198)
LiquidityManager_rebalanceLiquidity:test_InsufficientLiquidityReverts() (gas: 206764)
LiquidityManager_rebalanceLiquidity:test_InvalidRemoteChainReverts() (gas: 192374)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess() (gas: 9141798)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess_AlreadyFinalized() (gas: 8942122)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_MultiStageFinalization() (gas: 8937262)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_NativeRewrap() (gas: 8865000)
LiquidityManager_rebalanceLiquidity:test_rebalanceLiquiditySuccess() (gas: 382946)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPoolsSuccess_AlreadyFinalized() (gas: 9246772)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_MultiStageFinalization() (gas: 9241912)
LiquidityManager_rebalanceLiquidity:test_rebalanceBetweenPools_NativeRewrap() (gas: 9169653)
LiquidityManager_rebalanceLiquidity:test_rebalanceLiquiditySuccess() (gas: 382928)
LiquidityManager_receive:test_receive_success() (gas: 21182)
LiquidityManager_removeLiquidity:test_InsufficientLiquidityReverts() (gas: 184959)
LiquidityManager_removeLiquidity:test_OnlyFinanceRoleReverts() (gas: 10872)
LiquidityManager_removeLiquidity:test_removeLiquiditySuccess() (gas: 236379)
LiquidityManager_removeLiquidity:test_removeLiquiditySuccess() (gas: 236361)
LiquidityManager_setCrossChainRebalancer:test_OnlyOwnerReverts() (gas: 17005)
LiquidityManager_setCrossChainRebalancer:test_ZeroAddressReverts() (gas: 21669)
LiquidityManager_setCrossChainRebalancer:test_ZeroChainSelectorReverts() (gas: 13099)
Expand All @@ -19,7 +19,7 @@ LiquidityManager_setFinanceRole:test_OnlyOwnerReverts() (gas: 10987)
LiquidityManager_setFinanceRole:test_setFinanceRoleSuccess() (gas: 21836)
LiquidityManager_setLocalLiquidityContainer:test_OnlyOwnerReverts() (gas: 11030)
LiquidityManager_setLocalLiquidityContainer:test_ReverstWhen_CalledWithTheZeroAddress() (gas: 10621)
LiquidityManager_setLocalLiquidityContainer:test_setLocalLiquidityContainerSuccess() (gas: 3479905)
LiquidityManager_setLocalLiquidityContainer:test_setLocalLiquidityContainerSuccess() (gas: 3784709)
LiquidityManager_setMinimumLiquidity:test_OnlyOwnerReverts() (gas: 10925)
LiquidityManager_setMinimumLiquidity:test_setMinimumLiquiditySuccess() (gas: 36389)
LiquidityManager_withdrawERC20:test_withdrawERC20Reverts() (gas: 180396)
Expand Down
5 changes: 3 additions & 2 deletions contracts/src/v0.8/ccip/pools/BurnFromMintTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok
contract BurnFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
using SafeERC20 for IBurnMintERC20;

string public constant override typeAndVersion = "BurnFromMintTokenPool 1.5.0";
string public constant override typeAndVersion = "BurnFromMintTokenPool 1.5.1";

constructor(
IBurnMintERC20 token,
uint8 localTokenDecimals,
address[] memory allowlist,
address rmnProxy,
address router
) TokenPool(token, allowlist, rmnProxy, router) {
) TokenPool(token, localTokenDecimals, allowlist, rmnProxy, router) {
// Some tokens allow burning from the sender without approval, but not all do.
// To be safe, we approve the pool to burn from the pool.
token.safeIncreaseAllowance(address(this), type(uint256).max);
Expand Down
5 changes: 3 additions & 2 deletions contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import {TokenPool} from "./TokenPool.sol";
/// If that is expected, please make sure the token's burner/minter roles are adjustable.
/// @dev This contract is a variant of BurnMintTokenPool that uses `burn(amount)`.
contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
string public constant override typeAndVersion = "BurnMintTokenPool 1.5.0";
string public constant override typeAndVersion = "BurnMintTokenPool 1.5.1";

constructor(
IBurnMintERC20 token,
uint8 localTokenDecimals,
address[] memory allowlist,
address rmnProxy,
address router
) TokenPool(token, allowlist, rmnProxy, router) {}
) TokenPool(token, localTokenDecimals, allowlist, rmnProxy, router) {}

/// @inheritdoc BurnMintTokenPoolAbstract
function _burn(
Expand Down
15 changes: 11 additions & 4 deletions contracts/src/v0.8/ccip/pools/BurnMintTokenPoolAbstract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ abstract contract BurnMintTokenPoolAbstract is TokenPool {

emit Burned(msg.sender, lockOrBurnIn.amount);

return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""});
return Pool.LockOrBurnOutV1({
destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector),
destPoolData: _encodeLocalDecimals()
});
}

/// @notice Mint tokens from the pool to the recipient
Expand All @@ -35,11 +38,15 @@ abstract contract BurnMintTokenPoolAbstract is TokenPool {
) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
_validateReleaseOrMint(releaseOrMintIn);

// Calculate the local amount
uint256 localAmount =
_calculateLocalAmount(releaseOrMintIn.amount, _parseRemoteDecimals(releaseOrMintIn.sourcePoolData));

// Mint to the receiver
IBurnMintERC20(address(i_token)).mint(releaseOrMintIn.receiver, releaseOrMintIn.amount);
IBurnMintERC20(address(i_token)).mint(releaseOrMintIn.receiver, localAmount);

emit Minted(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount);
emit Minted(msg.sender, releaseOrMintIn.receiver, localAmount);

return Pool.ReleaseOrMintOutV1({destinationAmount: releaseOrMintIn.amount});
return Pool.ReleaseOrMintOutV1({destinationAmount: localAmount});
}
}
5 changes: 3 additions & 2 deletions contracts/src/v0.8/ccip/pools/BurnWithFromMintTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ contract BurnWithFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion

constructor(
IBurnMintERC20 token,
uint8 localTokenDecimals,
address[] memory allowlist,
address rmnProxy,
address router
) TokenPool(token, allowlist, rmnProxy, router) {
) TokenPool(token, localTokenDecimals, allowlist, rmnProxy, router) {
// Some tokens allow burning from the sender without approval, but not all do.
// To be safe, we approve the pool to burn from the pool.
token.safeIncreaseAllowance(address(this), type(uint256).max);
Expand All @@ -37,6 +38,6 @@ contract BurnWithFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion
}

function typeAndVersion() external pure virtual override returns (string memory) {
return "BurnWithFromMintTokenPool 1.5.0";
return "BurnWithFromMintTokenPool 1.5.1";
}
}
20 changes: 14 additions & 6 deletions contracts/src/v0.8/ccip/pools/LockReleaseTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion

event LiquidityTransferred(address indexed from, uint256 amount);

string public constant override typeAndVersion = "LockReleaseTokenPool 1.5.0";
string public constant override typeAndVersion = "LockReleaseTokenPool 1.5.1";

/// @dev Whether or not the pool accepts liquidity.
/// External liquidity is not required when there is one canonical token deployed to a chain,
Expand All @@ -35,11 +35,12 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion

constructor(
IERC20 token,
uint8 localTokenDecimals,
address[] memory allowlist,
address rmnProxy,
bool acceptLiquidity,
address router
) TokenPool(token, allowlist, rmnProxy, router) {
) TokenPool(token, localTokenDecimals, allowlist, rmnProxy, router) {
i_acceptLiquidity = acceptLiquidity;
}

Expand All @@ -52,7 +53,10 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion

emit Locked(msg.sender, lockOrBurnIn.amount);

return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""});
return Pool.LockOrBurnOutV1({
destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector),
destPoolData: _encodeLocalDecimals()
});
}

/// @notice Release tokens from the pool to the recipient
Expand All @@ -62,12 +66,16 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
_validateReleaseOrMint(releaseOrMintIn);

// Calculate the local amount
uint256 localAmount =
_calculateLocalAmount(releaseOrMintIn.amount, _parseRemoteDecimals(releaseOrMintIn.sourcePoolData));

// Release to the recipient
getToken().safeTransfer(releaseOrMintIn.receiver, releaseOrMintIn.amount);
getToken().safeTransfer(releaseOrMintIn.receiver, localAmount);

emit Released(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount);
emit Released(msg.sender, releaseOrMintIn.receiver, localAmount);

return Pool.ReleaseOrMintOutV1({destinationAmount: releaseOrMintIn.amount});
return Pool.ReleaseOrMintOutV1({destinationAmount: localAmount});
}

/// @inheritdoc IERC165
Expand Down
Loading

0 comments on commit ea7b4bb

Please sign in to comment.