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

Pool to support rebasing mint call #1490

Merged
merged 4 commits into from
Oct 8, 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
5 changes: 4 additions & 1 deletion contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ BurnMintTokenPool_releaseOrMint:test_PoolMint_Success() (gas: 112391)
BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28842)
BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55271)
BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 244050)
BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24170)
BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24168)
BurnWithFromMintTokenPool_releaseOrMint:test_Setup_Success() (gas: 24547)
BurnWithFromMintTokenPool_releaseOrMint:test_releaseOrMint_NegativeMintAmount_reverts() (gas: 93840)
BurnWithFromMintTokenPool_releaseOrMint:test_releaseOrMint_Success() (gas: 93423)
CCIPClientExample_sanity:test_ImmutableExamples_Success() (gas: 2052431)
CCIPHome__validateConfig:test__validateConfigLessTransmittersThanSigners_Success() (gas: 334693)
CCIPHome__validateConfig:test__validateConfigSmallerFChain_Success() (gas: 466117)
Expand Down
1 change: 1 addition & 0 deletions contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ compileContract ccip/pools/BurnWithFromMintTokenPool.sol
compileContract ccip/pools/LockReleaseTokenPoolAndProxy.sol
compileContract ccip/pools/BurnMintTokenPoolAndProxy.sol
compileContract ccip/pools/BurnWithFromMintTokenPoolAndProxy.sol
compileContract ccip/pools/BurnWithFromMintRebasingTokenPool.sol
compileContract ccip/pools/TokenPool.sol


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

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

import {Pool} from "../libraries/Pool.sol";
import {BurnWithFromMintTokenPool} from "./BurnWithFromMintTokenPool.sol";

/// @notice This pool mints and burns a 3rd-party token.
/// @dev This contract is a variant of BurnMintTokenPool that uses `burn(from, amount)`.
/// @dev This contract supports minting tokens that do not mint the exact amount they are asked to mint. This can be
/// used for rebasing tokens. NOTE: for true rebasing support, the lockOrBurn method must also be updated to support
/// relaying the correct amount.
contract BurnWithFromMintRebasingTokenPool is BurnWithFromMintTokenPool {
error NegativeMintAmount(uint256 amountBurned);

string public constant override typeAndVersion = "BurnWithFromMintRebasingTokenPool 1.5.0";

constructor(
IBurnMintERC20 token,
address[] memory allowlist,
address rmnProxy,
address router
) BurnWithFromMintTokenPool(token, allowlist, rmnProxy, router) {}

/// @notice Mint tokens from the pool to the recipient
/// @dev The _validateReleaseOrMint check is an essential security check
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
_validateReleaseOrMint(releaseOrMintIn);

uint256 balancePre = IBurnMintERC20(address(i_token)).balanceOf(releaseOrMintIn.receiver);

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

uint256 balancePost = IBurnMintERC20(address(i_token)).balanceOf(releaseOrMintIn.receiver);

// Mint should not reduce the number of tokens in the receiver, if it does it will revert the call.
if (balancePost < balancePre) {
revert NegativeMintAmount(balancePre - balancePost);
}

emit Minted(msg.sender, releaseOrMintIn.receiver, balancePost - balancePre);

return Pool.ReleaseOrMintOutV1({destinationAmount: balancePost - balancePre});
}
}
6 changes: 4 additions & 2 deletions contracts/src/v0.8/ccip/pools/BurnWithFromMintTokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/tok
contract BurnWithFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion {
using SafeERC20 for IBurnMintERC20;

string public constant override typeAndVersion = "BurnWithFromMintTokenPool 1.5.0";

constructor(
IBurnMintERC20 token,
address[] memory allowlist,
Expand All @@ -35,4 +33,8 @@ contract BurnWithFromMintTokenPool is BurnMintTokenPoolAbstract, ITypeAndVersion
function _burn(uint256 amount) internal virtual override {
IBurnMintERC20(address(i_token)).burn(address(this), amount);
}

function typeAndVersion() external pure virtual override returns (string memory) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other way of writing it cannot be overridden

return "BurnWithFromMintTokenPool 1.5.0";
}
}
27 changes: 27 additions & 0 deletions contracts/src/v0.8/ccip/test/helpers/ERC20RebasingHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

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

contract ERC20RebasingHelper is ERC20 {
uint16 public s_multiplierPercentage = 100;
bool public s_mintShouldBurn = false;

constructor() ERC20("Rebasing", "REB") {}

function mint(address to, uint256 amount) external {
if (!s_mintShouldBurn) {
_mint(to, amount * s_multiplierPercentage / 100);
return;
}
_burn(to, amount * s_multiplierPercentage / 100);
}

function setMultiplierPercentage(uint16 multiplierPercentage) external {
s_multiplierPercentage = multiplierPercentage;
}

function setMintShouldBurn(bool mintShouldBurn) external {
s_mintShouldBurn = mintShouldBurn;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

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

import {Pool} from "../../libraries/Pool.sol";
import {BurnWithFromMintRebasingTokenPool} from "../../pools/BurnWithFromMintRebasingTokenPool.sol";
import {BurnMintSetup} from "./BurnMintSetup.t.sol";

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

contract BurnWithFromMintRebasingTokenPoolSetup is BurnMintSetup {
BurnWithFromMintRebasingTokenPool internal s_pool;
ERC20RebasingHelper internal s_rebasingToken;

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

s_rebasingToken = new ERC20RebasingHelper();

s_pool = new BurnWithFromMintRebasingTokenPool(
IBurnMintERC20(address(s_rebasingToken)), new address[](0), address(s_mockRMN), address(s_sourceRouter)
);

_applyChainUpdates(address(s_pool));

deal(address(s_rebasingToken), OWNER, 1e18);

vm.startPrank(s_burnMintOffRamp);
}
}

contract BurnWithFromMintTokenPool_releaseOrMint is BurnWithFromMintRebasingTokenPoolSetup {
function test_Setup_Success() public view {
assertEq(address(s_rebasingToken), address(s_pool.getToken()));
assertEq(address(s_mockRMN), s_pool.getRmnProxy());
assertEq(false, s_pool.getAllowListEnabled());
assertEq(type(uint256).max, s_rebasingToken.allowance(address(s_pool), address(s_pool)));
assertEq("BurnWithFromMintRebasingTokenPool 1.5.0", s_pool.typeAndVersion());
}

function test_releaseOrMint_Success() public {
uint256 amount = 1000;
uint256 balancePre = s_rebasingToken.balanceOf(address(OWNER));

Pool.ReleaseOrMintOutV1 memory releaseOrMintOut = s_pool.releaseOrMint(_getReleaseOrMintIn(amount));

assertEq(amount, releaseOrMintOut.destinationAmount);
assertEq(balancePre + amount, s_rebasingToken.balanceOf(address(OWNER)));
}

function testFuzz_releaseOrMint_rebasing_success(uint16 multiplierPercentage) public {
uint256 amount = 1000;
uint256 expectedAmount = amount * multiplierPercentage / 100;
Copy link
Collaborator

@elatoskinas elatoskinas Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: if percentage is 0, this should still succeed right because balancePost == balancePre and balancePost < balancePre does not fail?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is balancePost == balancePre desirable in the pool? It's basically a no balance increase

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, I would call that case valid. The check is mostly there so 1) don't allow theft and 2) to not revert with an underflow when calculating the returned value

s_rebasingToken.setMultiplierPercentage(multiplierPercentage);

uint256 balancePre = s_rebasingToken.balanceOf(address(OWNER));

Pool.ReleaseOrMintOutV1 memory releaseOrMintOut = s_pool.releaseOrMint(_getReleaseOrMintIn(amount));

assertEq(expectedAmount, releaseOrMintOut.destinationAmount);
assertEq(balancePre + expectedAmount, s_rebasingToken.balanceOf(address(OWNER)));
}

function test_releaseOrMint_NegativeMintAmount_reverts() public {
uint256 amount = 1000;
s_rebasingToken.setMintShouldBurn(true);

vm.expectRevert(abi.encodeWithSelector(BurnWithFromMintRebasingTokenPool.NegativeMintAmount.selector, amount));

s_pool.releaseOrMint(_getReleaseOrMintIn(amount));
}

function _getReleaseOrMintIn(uint256 amount) internal view returns (Pool.ReleaseOrMintInV1 memory) {
return Pool.ReleaseOrMintInV1({
originalSender: bytes(""),
receiver: OWNER,
amount: amount,
localToken: address(s_rebasingToken),
remoteChainSelector: DEST_CHAIN_SELECTOR,
sourcePoolAddress: abi.encode(s_remoteBurnMintPool),
sourcePoolData: "",
offchainTokenData: ""
});
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ GETH_VERSION: 1.13.8
burn_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnFromMintTokenPool/BurnFromMintTokenPool.bin 1e60c28ad796a220a38043b369dec8d9bffe23e1c7d9895760e30672872afd06
burn_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPool/BurnMintTokenPool.bin 3e8e3358f0bb520af069a7d37ea625940a88461a54418b1d5925eabced8c74df
burn_mint_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnMintTokenPoolAndProxy/BurnMintTokenPoolAndProxy.bin 717c079d5d13300cf3c3ee871c6e5bf9af904411f204fb081a9f3b263cca1391
burn_with_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin 6333d0314d0bd29e75ea5e05fe62a4516ade0c6db91c30b6f93645035db52ed8
burn_with_from_mint_rebasing_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.bin ec9b95105a33de14b078c1261d9cd9d6f3011e940a819b52f11a963951d4bb89
burn_with_from_mint_token_pool: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPool/BurnWithFromMintTokenPool.bin 734c2a0ea8f1224b5f01ed849410209e74b4e3427e8bfddb8ff5dd8ead5f2d8d
burn_with_from_mint_token_pool_and_proxy: ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPoolAndProxy/BurnWithFromMintTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintTokenPoolAndProxy/BurnWithFromMintTokenPoolAndProxy.bin 1ed5c299f928529081dc01b7a46db2b5e6728001767863495a7675d8db99a9e2
ccip_encoding_utils: ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.abi ../../../contracts/solc/v0.8.24/ICCIPEncodingUtils/ICCIPEncodingUtils.bin 2e6fa009821d30a24efdc9f9d14b2269fb9a51cb7d536ea8b2d29d97dd8b591c
ccip_home: ../../../contracts/solc/v0.8.24/CCIPHome/CCIPHome.abi ../../../contracts/solc/v0.8.24/CCIPHome/CCIPHome.bin c570b86f8ae7697d3478e70625e06f0f682e75c1f0b7538f4b6f581b0d294b2b
Expand Down
1 change: 1 addition & 0 deletions core/gethwrappers/ccip/go_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package ccip
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.abi ../../../contracts/solc/v0.8.24/LockReleaseTokenPoolAndProxy/LockReleaseTokenPoolAndProxy.bin LockReleaseTokenPoolAndProxy lock_release_token_pool_and_proxy
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.abi ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.bin TokenPool token_pool
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.abi ../../../contracts/solc/v0.8.24/USDCTokenPool/USDCTokenPool.bin USDCTokenPool usdc_token_pool
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.abi ../../../contracts/solc/v0.8.24/BurnWithFromMintRebasingTokenPool/BurnWithFromMintRebasingTokenPool.bin BurnWithFromMintRebasingTokenPool burn_with_from_mint_rebasing_token_pool

// Helpers
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.abi ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.bin MockV3Aggregator mock_v3_aggregator_contract
Expand Down
Loading