Skip to content

Commit

Permalink
add burnFrom pools (#246)
Browse files Browse the repository at this point in the history
  • Loading branch information
RensR authored Nov 7, 2023
1 parent 7b19b40 commit 77d0fdf
Show file tree
Hide file tree
Showing 26 changed files with 5,469 additions and 106 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/solidity-foundry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
- name: Run Solhint
if: ${{ needs.changes.outputs.changes == 'true' }}
working-directory: contracts
run: pnpm lint:ccip
run: pnpm solhint

coverage:
needs: [changes]
Expand Down Expand Up @@ -111,7 +111,7 @@ jobs:
with:
update-comment: true
coverage-files: lcov.info.pruned
minimum-coverage: 98
minimum-coverage: 98.5
artifact-name: code-coverage-report
working-directory: ./contracts
github-token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion contracts/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ ccip-precommit:
forge test
make snapshot
pnpm prettier:write
pnpm lint:ccip
pnpm solhint

ccip-lcov: export FOUNDRY_PROFILE=ccip
.PHONY: ccip-lcov
Expand Down
28 changes: 18 additions & 10 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,19 @@ AggregateTokenLimiter_setAdmin:testOwnerSuccess() (gas: 18631)
AggregateTokenLimiter_setRateLimiterConfig:testOnlyOnlyCallableByAdminOrOwnerReverts() (gas: 21600)
AggregateTokenLimiter_setRateLimiterConfig:testOwnerSuccess() (gas: 41514)
AggregateTokenLimiter_setRateLimiterConfig:testTokenLimitAdminSuccess() (gas: 48156)
BurnMintERC677_burn:testPoolBurnRevertNotHealthyReverts() (gas: 58280)
BurnMintERC677_burn:testPoolBurnSuccess() (gas: 197998)
BurnMintERC677_mint:testPoolMintNotHealthyReverts() (gas: 55341)
BurnMintERC677_mint:testPoolMintSuccess() (gas: 93632)
CCIPClientExample_sanity:testExamples() (gas: 2217942)
BurnFromMintTokenPool_lockOrBurn:testPermissionsErrorReverts() (gas: 12761)
BurnFromMintTokenPool_lockOrBurn:testPoolBurnRevertNotHealthyReverts() (gas: 54151)
BurnFromMintTokenPool_lockOrBurn:testPoolBurnSuccess() (gas: 201167)
BurnMintTokenPool_lockOrBurn:testPermissionsErrorReverts() (gas: 12761)
BurnMintTokenPool_lockOrBurn:testPoolBurnRevertNotHealthyReverts() (gas: 54151)
BurnMintTokenPool_lockOrBurn:testPoolBurnSuccess() (gas: 199084)
BurnMintTokenPool_releaseOrMint:testPermissionsErrorReverts() (gas: 19990)
BurnMintTokenPool_releaseOrMint:testPoolMintNotHealthyReverts() (gas: 51240)
BurnMintTokenPool_releaseOrMint:testPoolMintSuccess() (gas: 89541)
BurnWithFromMintTokenPool_lockOrBurn:testPermissionsErrorReverts() (gas: 12761)
BurnWithFromMintTokenPool_lockOrBurn:testPoolBurnRevertNotHealthyReverts() (gas: 54151)
BurnWithFromMintTokenPool_lockOrBurn:testPoolBurnSuccess() (gas: 201193)
CCIPClientExample_sanity:testExamples() (gas: 2218000)
CallWithExactGas_callWithExactGas:test_CallWithExactGasSuccess() (gas: 22403)
CallWithExactGas_callWithExactGas:test_NoContractReverts() (gas: 10465)
CallWithExactGas_callWithExactGas:test_NoGasForCallExactCheckReverts() (gas: 14036)
Expand Down Expand Up @@ -91,7 +99,7 @@ CommitStore_verify:testPausedReverts() (gas: 18438)
CommitStore_verify:testTooManyLeavesReverts() (gas: 36830)
DefensiveExampleTest:testHappyPathSuccess() (gas: 174862)
DefensiveExampleTest:testRecovery() (gas: 399786)
E2E:testE2E_3MessagesSuccess_gas() (gas: 887381)
E2E:testE2E_3MessagesSuccess_gas() (gas: 887468)
EVM2EVMOffRamp__releaseOrMintTokens:testRateLimitErrorsReverts() (gas: 443866)
EVM2EVMOffRamp__releaseOrMintTokens:testTokenHandlingErrorReverts() (gas: 103420)
EVM2EVMOffRamp__releaseOrMintTokens:testUnsupportedTokenReverts() (gas: 18202)
Expand Down Expand Up @@ -342,9 +350,9 @@ Router_applyRampUpdates:testOffRampUpdatesWithRouting() (gas: 8494015)
Router_applyRampUpdates:testOnRampDisable() (gas: 52544)
Router_applyRampUpdates:testOnlyOwnerReverts() (gas: 12305)
Router_ccipSend:testCCIPSendLinkFeeNoTokenSuccess_gas() (gas: 110664)
Router_ccipSend:testCCIPSendLinkFeeOneTokenSuccess_gas() (gas: 189563)
Router_ccipSend:testCCIPSendLinkFeeOneTokenSuccess_gas() (gas: 189592)
Router_ccipSend:testCCIPSendNativeFeeNoTokenSuccess_gas() (gas: 122727)
Router_ccipSend:testCCIPSendNativeFeeOneTokenSuccess_gas() (gas: 201628)
Router_ccipSend:testCCIPSendNativeFeeOneTokenSuccess_gas() (gas: 201657)
Router_ccipSend:testFeeTokenAmountTooLowReverts() (gas: 61613)
Router_ccipSend:testInvalidMsgValue() (gas: 31947)
Router_ccipSend:testNativeFeeTokenInsufficientValue() (gas: 64073)
Expand Down Expand Up @@ -390,9 +398,9 @@ TokenPool_setOnRampRateLimiterConfig:testOnlyOwnerReverts() (gas: 18065)
TokenProxy_ccipSend:testCcipSendGasShouldBeZeroReverts() (gas: 17077)
TokenProxy_ccipSend:testCcipSendInsufficientAllowanceReverts() (gas: 127729)
TokenProxy_ccipSend:testCcipSendInvalidTokenReverts() (gas: 15863)
TokenProxy_ccipSend:testCcipSendNativeSuccess() (gas: 230581)
TokenProxy_ccipSend:testCcipSendNativeSuccess() (gas: 230610)
TokenProxy_ccipSend:testCcipSendNoDataAllowedReverts() (gas: 16191)
TokenProxy_ccipSend:testCcipSendSuccess() (gas: 250504)
TokenProxy_ccipSend:testCcipSendSuccess() (gas: 250528)
TokenProxy_constructor:testConstructor() (gas: 10651)
TokenProxy_getFee:testGetFeeGasShouldBeZeroReverts() (gas: 16746)
TokenProxy_getFee:testGetFeeInvalidTokenReverts() (gas: 12628)
Expand Down
2 changes: 1 addition & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder",
"publish-beta": "pnpm publish --tag beta",
"publish-prod": "npm dist-tag add @chainlink/[email protected] latest",
"lint:ccip": "solhint --config ./src/v0.8/ccip/.solhint.json --ignore-path .ccip-solhint-ignore --max-warnings 0 \"./src/v0.8/ccip/**/*.sol\""
"solhint": "solhint --config ./src/v0.8/ccip/.solhint.json --ignore-path .ccip-solhint-ignore --max-warnings 0 \"./src/v0.8/ccip/**/*.sol\""
},
"files": [
"src/v0.8/ccip/**/*.sol",
Expand Down
2 changes: 2 additions & 0 deletions contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ compileContract ccip/Router.sol
compileContract ccip/PriceRegistry.sol
compileContract ccip/pools/LockReleaseTokenPool.sol
compileContract ccip/pools/BurnMintTokenPool.sol
compileContract ccip/pools/BurnFromMintTokenPool.sol
compileContract ccip/pools/BurnWithFromMintTokenPool.sol
compileContract shared/token/ERC677/BurnMintERC677.sol
compileContract ccip/ARM.sol
compileContract ccip/ARMProxy.sol
Expand Down
1 change: 1 addition & 0 deletions contracts/src/v0.8/ccip/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"var-name-mixedcase": "off",
"func-named-parameters": "off",
"immutable-vars-naming": "off",
"no-unused-import": "error",
"func-visibility": [
"error",
{
Expand Down
33 changes: 33 additions & 0 deletions contracts/src/v0.8/ccip/pools/BurnFromMintTokenPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

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

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

/// @notice This pool mints and burns a 3rd-party token.
/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later.
/// It either accepts any address as originalSender, or only accepts whitelisted originalSender.
/// The only way to change whitelisting mode is to deploy a new pool.
/// If that is expected, please make sure the token's burner/minter roles are adjustable.
contract BurnFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables
string public constant override typeAndVersion = "BurnFromMintTokenPool 1.2.0";

constructor(
IBurnMintERC20 token,
address[] memory allowlist,
address armProxy
) TokenPool(token, allowlist, armProxy) {
// 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.approve(address(this), type(uint256).max);
}

/// @inheritdoc BurnMintTokenPoolAbstract
function _burn(uint256 amount) internal virtual override {
IBurnMintERC20(address(i_token)).burnFrom(address(this), amount);
}
}
37 changes: 4 additions & 33 deletions contracts/src/v0.8/ccip/pools/BurnMintTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
pragma solidity 0.8.19;

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

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

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

/// @notice This pool mints and burns a 3rd-party token.
/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later.
/// It either accepts any address as originalSender, or only accepts whitelisted originalSender.
/// The only way to change whitelisting mode is to deploy a new pool.
/// If that is expected, please make sure the token's burner/minter roles are adjustable.
contract BurnMintTokenPool is TokenPool, ITypeAndVersion {
contract BurnMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables
string public constant override typeAndVersion = "BurnMintTokenPool 1.2.0";

Expand All @@ -22,37 +22,8 @@ contract BurnMintTokenPool is TokenPool, ITypeAndVersion {
address armProxy
) TokenPool(token, allowlist, armProxy) {}

/// @notice Burn the token in the pool
/// @param amount Amount to burn
/// @dev The whenHealthy check is important to ensure that even if a ramp is compromised
/// we're able to stop token movement via ARM.
function lockOrBurn(
address originalSender,
bytes calldata,
uint256 amount,
uint64,
bytes calldata
) external virtual override onlyOnRamp checkAllowList(originalSender) whenHealthy returns (bytes memory) {
_consumeOnRampRateLimit(amount);
/// @inheritdoc BurnMintTokenPoolAbstract
function _burn(uint256 amount) internal virtual override {
IBurnMintERC20(address(i_token)).burn(amount);
emit Burned(msg.sender, amount);
return "";
}

/// @notice Mint tokens from the pool to the recipient
/// @param receiver Recipient address
/// @param amount Amount to mint
/// @dev The whenHealthy check is important to ensure that even if a ramp is compromised
/// we're able to stop token movement via ARM.
function releaseOrMint(
bytes memory,
address receiver,
uint256 amount,
uint64,
bytes memory
) external virtual override whenHealthy onlyOffRamp {
_consumeOffRampRateLimit(amount);
IBurnMintERC20(address(i_token)).mint(receiver, amount);
emit Minted(msg.sender, receiver, amount);
}
}
47 changes: 47 additions & 0 deletions contracts/src/v0.8/ccip/pools/BurnMintTokenPoolAbstract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

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

import {TokenPool} from "./TokenPool.sol";

abstract contract BurnMintTokenPoolAbstract is TokenPool {
/// @notice Contains the specific burn call for a pool.
/// @dev overriding this method allows us to create pools with different burn signatures
/// without duplicating the underlying logic.
function _burn(uint256 amount) internal virtual;

/// @notice Burn the token in the pool
/// @param amount Amount to burn
/// @dev The whenHealthy check is important to ensure that even if a ramp is compromised
/// we're able to stop token movement via ARM.
function lockOrBurn(
address originalSender,
bytes calldata,
uint256 amount,
uint64,
bytes calldata
) external virtual override onlyOnRamp checkAllowList(originalSender) whenHealthy returns (bytes memory) {
_consumeOnRampRateLimit(amount);
_burn(amount);
emit Burned(msg.sender, amount);
return "";
}

/// @notice Mint tokens from the pool to the recipient
/// @param receiver Recipient address
/// @param amount Amount to mint
/// @dev The whenHealthy check is important to ensure that even if a ramp is compromised
/// we're able to stop token movement via ARM.
function releaseOrMint(
bytes memory,
address receiver,
uint256 amount,
uint64,
bytes memory
) external virtual override whenHealthy onlyOffRamp {
_consumeOffRampRateLimit(amount);
IBurnMintERC20(address(i_token)).mint(receiver, amount);
emit Minted(msg.sender, receiver, amount);
}
}
33 changes: 33 additions & 0 deletions contracts/src/v0.8/ccip/pools/BurnWithFromMintTokenPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

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

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

/// @notice This pool mints and burns a 3rd-party token.
/// @dev Pool whitelisting mode is set in the constructor and cannot be modified later.
/// It either accepts any address as originalSender, or only accepts whitelisted originalSender.
/// The only way to change whitelisting mode is to deploy a new pool.
/// If that is expected, please make sure the token's burner/minter roles are adjustable.
contract BurnWithFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables
string public constant override typeAndVersion = "BurnWithFromMintTokenPool 1.2.0";

constructor(
IBurnMintERC20 token,
address[] memory allowlist,
address armProxy
) TokenPool(token, allowlist, armProxy) {
// 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.approve(address(this), type(uint256).max);
}

/// @inheritdoc BurnMintTokenPoolAbstract
function _burn(uint256 amount) internal virtual override {
IBurnMintERC20(address(i_token)).burn(address(this), amount);
}
}
4 changes: 2 additions & 2 deletions contracts/src/v0.8/ccip/pools/TokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 {
EnumerableSet.AddressSet internal s_onRamps;
/// @dev Inbound rate limits. This allows per destination chain
/// token issuer specified rate limiting (e.g. issuers may trust chains to varying
/// degress and prefer different limits)
/// degrees and prefer different limits)
mapping(address => RateLimiter.TokenBucket) internal s_onRampRateLimits;
/// @dev A set of allowed offRamps.
EnumerableSet.AddressSet internal s_offRamps;
Expand Down Expand Up @@ -115,7 +115,7 @@ abstract contract TokenPool is IPool, OwnerIsCreator, IERC165 {
}

/// @notice Get onRamp whitelist
/// @return list of onramps.
/// @return list of onRamps.
function getOnRamps() public view returns (address[] memory) {
return s_onRamps.values();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
pragma solidity ^0.8.0;

import {BurnMintTokenPool} from "../../pools/BurnMintTokenPool.sol";
import {RateLimiter} from "../../libraries/RateLimiter.sol";
import {TokenPool} from "../../pools/TokenPool.sol";
import {IBurnMintERC20} from "../../../shared/token/ERC20/IBurnMintERC20.sol";

contract MaybeRevertingBurnMintTokenPool is BurnMintTokenPool {
Expand Down
1 change: 0 additions & 1 deletion contracts/src/v0.8/ccip/test/mocks/MockCommitStore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity 0.8.19;

import {ICommitStore} from "../../interfaces/ICommitStore.sol";
import {Pausable} from "../../../vendor/Pausable.sol";

contract MockCommitStore is ICommitStore {
error PausedError();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity 0.8.19;

import {ICommitStore} from "../../interfaces/ICommitStore.sol";
import {IAny2EVMMessageReceiver} from "../../interfaces/IAny2EVMMessageReceiver.sol";
import {IPriceRegistry} from "../../interfaces/IPriceRegistry.sol";
import {IPool} from "../../interfaces/pools/IPool.sol";

import {Internal} from "../../libraries/Internal.sol";
Expand All @@ -12,7 +11,6 @@ import {PriceRegistrySetup} from "../priceRegistry/PriceRegistry.t.sol";
import {MockCommitStore} from "../mocks/MockCommitStore.sol";
import {Router} from "../../Router.sol";
import {EVM2EVMOffRamp} from "../../offRamp/EVM2EVMOffRamp.sol";
import {AggregateRateLimiter} from "../../AggregateRateLimiter.sol";
import {EVM2EVMOffRampHelper} from "../helpers/EVM2EVMOffRampHelper.sol";
import {TokenSetup} from "../TokenSetup.t.sol";
import {RouterSetup} from "../router/RouterSetup.t.sol";
Expand Down
Loading

0 comments on commit 77d0fdf

Please sign in to comment.