Skip to content

Commit

Permalink
CCIP-4723 Feat/burn to zero address pool (#15804)
Browse files Browse the repository at this point in the history
* Begin adding tests and fix Hybrid Pool bug

* add test for HybridUSDCTokenPool bugfix and changeset

* [Bot] Update changeset file with jira issues

* linter fix

* more tests

* finish filling in coverage gaps

* fix CI issues

* every time I encounter a snapshot issue like this a little part of me dies inside

* remove transferLiquidity function. Liquidity transfers can be manual

* fixes

* fix gas snapshotting from merge

* fix comments, add new error, and fix snapshot

* remove transferLiquidity function from hybrid USDC token pool

* test renaming

* streamine provideLiquidity authorization check

* update rebalancer system for unsiloed chains

* update snapshot

* Update Liquidity functions and update function for ease of use

* Fix naming and other additional security checks

* new error code and test cleanup

* fix snapshot changes from develop merge

* fix test linting

* fix comments

---------

Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
  • Loading branch information
1 parent a3042b6 commit 46ef625
Show file tree
Hide file tree
Showing 19 changed files with 1,225 additions and 142 deletions.
10 changes: 10 additions & 0 deletions contracts/.changeset/chilly-news-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@chainlink/contracts': minor
---

#feature Add two new pool types: Siloed-LockRelease and BurnToAddress and fix bug in HybridUSDCTokenPool for transferLiqudity #bugfix


PR issue: CCIP-4723

Solidity Review issue: CCIP-3966
56 changes: 37 additions & 19 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ BurnMintTokenPool_lockOrBurn:test_PoolBurn() (gas: 236872)
BurnMintTokenPool_lockOrBurn:test_Setup() (gas: 17819)
BurnMintTokenPool_releaseOrMint:test_PoolMint() (gas: 102527)
BurnMintWithLockReleaseFlagTokenPool_lockOrBurn:test_LockOrBurn_CorrectReturnData() (gas: 237292)
BurnToAddressMintTokenPool_lockOrBurn:test_LockOrBurn() (gas: 257956)
BurnToAddressMintTokenPool_releaseOrMint:test_releaseOrMint() (gas: 126048)
BurnToAddressMintTokenPool_setOutstandingokens:test_setOutstandingTokens() (gas: 37793)
BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn() (gas: 239012)
BurnWithFromMintTokenPool_lockOrBurn:test_Setup() (gas: 24169)
CCIPClientExample_sanity:test_ImmutableExamples() (gas: 2073613)
Expand Down Expand Up @@ -126,12 +129,11 @@ FeeQuoter_updateTokenPriceFeeds:test_SingleFeedUpdate() (gas: 53171)
FeeQuoter_updateTokenPriceFeeds:test_ZeroFeeds() (gas: 12471)
FeeQuoter_validateDestFamilyAddress:test_ValidEVMAddress() (gas: 6767)
FeeQuoter_validateDestFamilyAddress:test_ValidNonEVMAddress() (gas: 6492)
HybridLockReleaseUSDCTokenPool_TransferLiquidity:test_transferLiquidity() (gas: 167013)
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_PrimaryMechanism() (gas: 130356)
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism() (gas: 140104)
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_PrimaryMechanism() (gas: 130339)
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism() (gas: 140169)
HybridLockReleaseUSDCTokenPool_lockOrBurn:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 202967)
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206218)
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 260387)
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206350)
HybridLockReleaseUSDCTokenPool_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 260423)
LockReleaseTokenPool_canAcceptLiquidity:test_CanAcceptLiquidity() (gas: 3222607)
LockReleaseTokenPool_lockOrBurn:test_LockOrBurnWithAllowList() (gas: 72828)
LockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint() (gas: 217898)
Expand Down Expand Up @@ -348,6 +350,22 @@ Router_recoverTokens:test_RecoverTokens() (gas: 52668)
Router_routeMessage:test_routeMessage_AutoExec() (gas: 38071)
Router_routeMessage:test_routeMessage_ExecutionEvent() (gas: 153593)
Router_routeMessage:test_routeMessage_ManualExec() (gas: 31120)
SiloedLockReleaseTokenPool_lockOrBurn:test_lockOrBurn_SiloedFunds() (gas: 76874)
SiloedLockReleaseTokenPool_lockOrBurn:test_lockOrBurn_UnsiloedFunds() (gas: 76104)
SiloedLockReleaseTokenPool_provideLiqudity:test_ProvideLiquidity_LegacyProvideLiquiditySelector() (gas: 91873)
SiloedLockReleaseTokenPool_provideLiqudity:test_ProvideLiquidity_SiloedChain() (gas: 82416)
SiloedLockReleaseTokenPool_provideLiqudity:test_ProvideLiquidity_UnsiloedChain() (gas: 84036)
SiloedLockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint_RevertsWhen_InsufficientLiquidity_SiloedChain() (gas: 110002)
SiloedLockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint_RevertsWhen_InsufficientLiquidity_UnsiloedChain() (gas: 115718)
SiloedLockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint_SiloedChain() (gas: 262340)
SiloedLockReleaseTokenPool_releaseOrMint:test_ReleaseOrMint_UnsiloedChain() (gas: 263392)
SiloedLockReleaseTokenPool_setRebalancer:test_setRebalancer_UnsiloedChains() (gas: 24429)
SiloedLockReleaseTokenPool_setRebalancer:test_setSiloRebalancer() (gas: 32165)
SiloedLockReleaseTokenPool_updateSiloDesignations:test_updateSiloDesignations() (gas: 105825)
SiloedLockReleaseTokenPool_withdrawLiqudity:test_withdrawLiquidity_RevertsWhen_LegacyFunctionSelectorUnauthorized() (gas: 18244)
SiloedLockReleaseTokenPool_withdrawLiqudity:test_withdrawLiquidity_SiloedFunds() (gas: 70948)
SiloedLockReleaseTokenPool_withdrawLiqudity:test_withdrawLiquidity_UnsiloedFunds_LegacyFunctionSelector() (gas: 76391)
SiloedLockReleaseTokenPool_withdrawLiqudity:test_withdrawSiloedLiquidity_UnsiloedFunds() (gas: 71945)
TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole() (gas: 44236)
TokenAdminRegistry_addRegistryModule:test_addRegistryModule() (gas: 67093)
TokenAdminRegistry_getAllConfiguredTokens:test_getAllConfiguredTokens_outOfBounds() (gas: 11363)
Expand Down Expand Up @@ -387,22 +405,22 @@ TokenPool_parseRemoteDecimals:test_parseRemoteDecimals() (gas: 14030)
TokenPool_parseRemoteDecimals:test_parseRemoteDecimals_NoDecimalsDefaultsToLocalDecimals() (gas: 9705)
TokenPool_removeRemotePool:test_removeRemotePool() (gas: 188402)
TokenPool_setRateLimitAdmin:test_SetRateLimitAdmin() (gas: 37630)
USDCBridgeMigrator_BurnLockedUSDC:test_PrimaryMechanism() (gas: 130520)
USDCBridgeMigrator_BurnLockedUSDC:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303986)
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism() (gas: 140171)
USDCBridgeMigrator_BurnLockedUSDC:test_PrimaryMechanism() (gas: 130502)
USDCBridgeMigrator_BurnLockedUSDC:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303967)
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism() (gas: 140236)
USDCBridgeMigrator_BurnLockedUSDC:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203330)
USDCBridgeMigrator_cancelMigrationProposal:test_cancelExistingCCTPMigrationProposal() (gas: 56117)
USDCBridgeMigrator_provideLiquidity:test_PrimaryMechanism() (gas: 130538)
USDCBridgeMigrator_provideLiquidity:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303986)
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism() (gas: 140260)
USDCBridgeMigrator_cancelMigrationProposal:test_cancelExistingCCTPMigrationProposal() (gas: 56100)
USDCBridgeMigrator_provideLiquidity:test_PrimaryMechanism() (gas: 130520)
USDCBridgeMigrator_provideLiquidity:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303967)
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism() (gas: 140325)
USDCBridgeMigrator_provideLiquidity:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203331)
USDCBridgeMigrator_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206251)
USDCBridgeMigrator_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 260440)
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_destChain() (gas: 142763)
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_homeChain() (gas: 505520)
USDCBridgeMigrator_updateChainSelectorMechanism:test_PrimaryMechanism() (gas: 130520)
USDCBridgeMigrator_updateChainSelectorMechanism:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303968)
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism() (gas: 140260)
USDCBridgeMigrator_releaseOrMint:test_OnLockReleaseMechanism() (gas: 206383)
USDCBridgeMigrator_releaseOrMint:test_incomingMessageWithPrimaryMechanism() (gas: 260476)
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_destChain() (gas: 142853)
USDCBridgeMigrator_releaseOrMint:test_unstickManualTxAfterMigration_homeChain() (gas: 505540)
USDCBridgeMigrator_updateChainSelectorMechanism:test_PrimaryMechanism() (gas: 130502)
USDCBridgeMigrator_updateChainSelectorMechanism:test_lockOrBurn_then_BurnInCCTPMigration() (gas: 303949)
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism() (gas: 140325)
USDCBridgeMigrator_updateChainSelectorMechanism:test_onLockReleaseMechanism_thenSwitchToPrimary() (gas: 203312)
USDCTokenPool_lockOrBurn:test_LockOrBurn() (gas: 128094)
USDCTokenPool_releaseOrMint:test_ReleaseOrMintRealTx() (gas: 260189)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ abstract contract BurnMintTokenPoolAbstract is TokenPool {
/// @dev The _validateReleaseOrMint check is an essential security check
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
) public virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
_validateReleaseOrMint(releaseOrMintIn);

// Calculate the local amount
Expand Down
103 changes: 103 additions & 0 deletions contracts/src/v0.8/ccip/pools/BurnToAddressMintTokenPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol";

import {Pool} from "../libraries/Pool.sol";
import {BurnMintTokenPoolAbstract} from "./BurnMintTokenPoolAbstract.sol";
import {TokenPool} from "./TokenPool.sol";

import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";

/// @notice This pool mints and burns a 3rd-party token by sending tokens to an address which is unrecoverable.
/// @dev The pool is designed to have an immutable burn address. If the tokens at the burn address become recoverable,
/// for example, a quantum computer calculating a private key for the zero address, the pool will need to be replaced
/// with a new pool with a different burn address.
contract BurnToAddressMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
using SafeERC20 for IERC20;

event OutstandingTokensSet(uint256 newMintedTokenAmount, uint256 oldMintedTokenAmount);

error InsufficientOutstandingTokens();

string public constant override typeAndVersion = "BurnToAddressTokenPool 1.5.1";

/// @notice The address where tokens are sent during a call to lockOrBurn, functionally burning but without decreasing
/// total supply. This address is expected to have no ability to recover the tokens sent to it, and will thus be locked forever.
/// This can be either an EOA without a corresponding private key, or a contract which does not have the ability to transfer the tokens.
address public immutable i_burnAddress;

/// @notice Minted Tokens is a safety mechanism to ensure that more tokens cannot be sent out of the bridge
/// than were originally sent in via CCIP. On incoming messages the value is increased, and on outgoing messages,
/// the value is decreased. For pools with existing tokens in circulation, the value may not be known at deployment
/// time, and thus should be set later using the setoutstandingTokens() function.
uint256 internal s_outstandingTokens;

/// @dev Since burnAddress is expected to make the tokens unrecoverable, no check for the zero address needs to be
/// performed, as it is a valid input.
constructor(
IBurnMintERC20 token,
uint8 localTokenDecimals,
address[] memory allowlist,
address rmnProxy,
address router,
address burnAddress
) TokenPool(token, localTokenDecimals, allowlist, rmnProxy, router) {
i_burnAddress = burnAddress;
}

/// @notice Mint tokens from the pool to the recipient, updating the internal accounting for an outflow of tokens.
/// @dev If the amount of tokens to be
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) public virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
// When minting tokens, the local outstanding supply increases. These tokens will be burned
// when they are sent back to the pool on an outgoing message.
s_outstandingTokens += releaseOrMintIn.amount;

return super.releaseOrMint(releaseOrMintIn);
}

/// @inheritdoc BurnMintTokenPoolAbstract
/// @notice Tokens are burned by sending to an address which can never transfer them,
/// making the tokens unrecoverable without reducing the total supply.
function _burn(
uint256 amount
) internal virtual override {
if (amount > s_outstandingTokens) {
revert InsufficientOutstandingTokens();
}

// When tokens are burned, the amount outstanding decreases. This ensures that more tokens cannot be sent out
// of the bridge than were originally sent in via CCIP.
s_outstandingTokens -= amount;

getToken().safeTransfer(i_burnAddress, amount);
}

/// @notice Returns the address where tokens are sent during a call to lockOrBurn
/// @return burnAddress the address which receives the tokens.
function getBurnAddress() public view returns (address burnAddress) {
return i_burnAddress;
}

/// @notice Return the amount of tokens which were minted by this contract and not yet burned.
/// @return outstandingTokens The amount of tokens which were minted by this token pool and not yet burned.
function getOutstandingTokens() public view returns (uint256 outstandingTokens) {
return s_outstandingTokens;
}

/// @notice Set the amount of tokens which were minted by this contract and not yet burned.
/// @param amount The new amount of tokens which were minted by this token pool and not yet burned.
function setOutstandingTokens(
uint256 amount
) external onlyOwner {
uint256 currentOutstandingTokens = s_outstandingTokens;

s_outstandingTokens = amount;

emit OutstandingTokensSet(amount, currentOutstandingTokens);
}
}
Loading

0 comments on commit 46ef625

Please sign in to comment.