Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
RensR committed Nov 28, 2024
1 parent 4b4cfaf commit 4df439a
Show file tree
Hide file tree
Showing 55 changed files with 4,282 additions and 306 deletions.
620 changes: 317 additions & 303 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

28 changes: 25 additions & 3 deletions contracts/src/v0.8/ccip/test/helpers/TokenPoolHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,49 @@ import {Pool} from "../../libraries/Pool.sol";
import {TokenPool} from "../../pools/TokenPool.sol";

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

contract TokenPoolHelper is TokenPool {
using EnumerableSet for EnumerableSet.Bytes32Set;

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

function lockOrBurn(
Pool.LockOrBurnInV1 calldata lockOrBurnIn
) external view override returns (Pool.LockOrBurnOutV1 memory) {
) external override returns (Pool.LockOrBurnOutV1 memory) {
_validateLockOrBurn(lockOrBurnIn);

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

function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) external pure override returns (Pool.ReleaseOrMintOutV1 memory) {
) external override returns (Pool.ReleaseOrMintOutV1 memory) {
_validateReleaseOrMint(releaseOrMintIn);

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

function encodeLocalDecimals() external view returns (bytes memory) {
return _encodeLocalDecimals();
}

function parseRemoteDecimals(
bytes memory sourcePoolData
) external view returns (uint256) {
return _parseRemoteDecimals(sourcePoolData);
}

function calculateLocalAmount(uint256 remoteAmount, uint8 remoteDecimals) external view returns (uint256) {
return _calculateLocalAmount(remoteAmount, remoteDecimals);
}

function onlyOnRampModifier(
uint64 remoteChainSelector
) external view {
Expand Down
21 changes: 21 additions & 0 deletions contracts/src/v0.8/ccip/test/helpers/USDCTokenPoolHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

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

import {ITokenMessenger} from "../../pools/USDC/ITokenMessenger.sol";
import {USDCTokenPool} from "../../pools/USDC/USDCTokenPool.sol";

contract USDCTokenPoolHelper is USDCTokenPool {
constructor(
ITokenMessenger tokenMessenger,
IBurnMintERC20 token,
address[] memory allowlist,
address rmnProxy,
address router
) USDCTokenPool(tokenMessenger, token, allowlist, rmnProxy, router) {}

function validateMessage(bytes memory usdcMessage, SourceTokenDataPayload memory sourceTokenData) external view {
return _validateMessage(usdcMessage, sourceTokenData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {Pool} from "../../../libraries/Pool.sol";
import {RateLimiter} from "../../../libraries/RateLimiter.sol";
import {TokenPool} from "../../../pools/TokenPool.sol";
import {BurnFromMintTokenPoolSetup} from "./BurnFromMintTokenPoolSetup.t.sol";

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

contract BurnFromMintTokenPool_lockOrBurn is BurnFromMintTokenPoolSetup {
function test_setup_Success() public view {
assertEq(address(s_burnMintERC20), address(s_pool.getToken()));
assertEq(address(s_mockRMN), s_pool.getRmnProxy());
assertEq(false, s_pool.getAllowListEnabled());
assertEq(type(uint256).max, s_burnMintERC20.allowance(address(s_pool), address(s_pool)));
assertEq("BurnFromMintTokenPool 1.5.1", s_pool.typeAndVersion());
}

function test_PoolBurn_Success() public {
uint256 burnAmount = 20_000e18;

deal(address(s_burnMintERC20), address(s_pool), burnAmount);
assertEq(s_burnMintERC20.balanceOf(address(s_pool)), burnAmount);

vm.startPrank(s_burnMintOnRamp);

vm.expectEmit();
emit RateLimiter.TokensConsumed(burnAmount);

vm.expectEmit();
emit IERC20.Transfer(address(s_pool), address(0), burnAmount);

vm.expectEmit();
emit TokenPool.Burned(address(s_burnMintOnRamp), burnAmount);

bytes4 expectedSignature = bytes4(keccak256("burnFrom(address,uint256)"));
vm.expectCall(address(s_burnMintERC20), abi.encodeWithSelector(expectedSignature, address(s_pool), burnAmount));

s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
originalSender: OWNER,
receiver: bytes(""),
amount: burnAmount,
remoteChainSelector: DEST_CHAIN_SELECTOR,
localToken: address(s_burnMintERC20)
})
);

assertEq(s_burnMintERC20.balanceOf(address(s_pool)), 0);
}

// Should not burn tokens if cursed.
function test_PoolBurnRevertNotHealthy_Revert() public {
s_mockRMN.setGlobalCursed(true);
uint256 before = s_burnMintERC20.balanceOf(address(s_pool));
vm.startPrank(s_burnMintOnRamp);

vm.expectRevert(TokenPool.CursedByRMN.selector);
s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
originalSender: OWNER,
receiver: bytes(""),
amount: 1e5,
remoteChainSelector: DEST_CHAIN_SELECTOR,
localToken: address(s_burnMintERC20)
})
);

assertEq(s_burnMintERC20.balanceOf(address(s_pool)), before);
}

function test_ChainNotAllowed_Revert() public {
uint64 wrongChainSelector = 8838833;
vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, wrongChainSelector));
s_pool.releaseOrMint(
Pool.ReleaseOrMintInV1({
originalSender: bytes(""),
receiver: OWNER,
amount: 1,
localToken: address(s_burnMintERC20),
remoteChainSelector: wrongChainSelector,
sourcePoolAddress: _generateSourceTokenData().sourcePoolAddress,
sourcePoolData: _generateSourceTokenData().extraData,
offchainTokenData: ""
})
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {BurnFromMintTokenPool} from "../../../pools/BurnFromMintTokenPool.sol";
import {BurnMintSetup} from "../BurnMintTokenPool/BurnMintSetup.t.sol";

contract BurnFromMintTokenPoolSetup is BurnMintSetup {
BurnFromMintTokenPool internal s_pool;

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

s_pool = new BurnFromMintTokenPool(
s_burnMintERC20, DEFAULT_TOKEN_DECIMALS, new address[](0), address(s_mockRMN), address(s_sourceRouter)
);
s_burnMintERC20.grantMintAndBurnRoles(address(s_pool));

_applyChainUpdates(address(s_pool));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {BurnMintERC20} from "../../../../shared/token/ERC20/BurnMintERC20.sol";
import {Router} from "../../../Router.sol";
import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol";
import {TokenPool} from "../../../pools/TokenPool.sol";
import {RouterSetup} from "../../router/RouterSetup.t.sol";

contract BurnMintSetup is RouterSetup {
BurnMintERC20 internal s_burnMintERC20;
address internal s_burnMintOffRamp = makeAddr("burn_mint_offRamp");
address internal s_burnMintOnRamp = makeAddr("burn_mint_onRamp");

address internal s_remoteBurnMintPool = makeAddr("remote_burn_mint_pool");
address internal s_remoteToken = makeAddr("remote_token");

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

s_burnMintERC20 = new BurnMintERC20("Chainlink Token", "LINK", 18, 0, 0);
}

function _applyChainUpdates(
address pool
) internal {
bytes[] memory remotePoolAddresses = new bytes[](1);
remotePoolAddresses[0] = abi.encode(s_remoteBurnMintPool);

TokenPool.ChainUpdate[] memory chainsToAdd = new TokenPool.ChainUpdate[](1);
chainsToAdd[0] = TokenPool.ChainUpdate({
remoteChainSelector: DEST_CHAIN_SELECTOR,
remotePoolAddresses: remotePoolAddresses,
remoteTokenAddress: abi.encode(s_remoteToken),
outboundRateLimiterConfig: _getOutboundRateLimiterConfig(),
inboundRateLimiterConfig: _getInboundRateLimiterConfig()
});

BurnMintTokenPool(pool).applyChainUpdates(new uint64[](0), chainsToAdd);

Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1);
onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_burnMintOnRamp});
Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1);
offRampUpdates[0] = Router.OffRamp({sourceChainSelector: DEST_CHAIN_SELECTOR, offRamp: s_burnMintOffRamp});
s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {Pool} from "../../../libraries/Pool.sol";
import {RateLimiter} from "../../../libraries/RateLimiter.sol";
import {BurnMintTokenPool} from "../../../pools/BurnMintTokenPool.sol";
import {TokenPool} from "../../../pools/TokenPool.sol";
import {BurnMintSetup} from "./BurnMintSetup.t.sol";

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

contract BurnMintTokenPoolSetup is BurnMintSetup {
BurnMintTokenPool internal s_pool;

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

s_pool = new BurnMintTokenPool(
s_burnMintERC20, DEFAULT_TOKEN_DECIMALS, new address[](0), address(s_mockRMN), address(s_sourceRouter)
);
s_burnMintERC20.grantMintAndBurnRoles(address(s_pool));

_applyChainUpdates(address(s_pool));
}
}

contract BurnMintTokenPool_lockOrBurn is BurnMintTokenPoolSetup {
function test_Setup_Success() public view {
assertEq(address(s_burnMintERC20), address(s_pool.getToken()));
assertEq(address(s_mockRMN), s_pool.getRmnProxy());
assertEq(false, s_pool.getAllowListEnabled());
assertEq("BurnMintTokenPool 1.5.1", s_pool.typeAndVersion());
}

function test_PoolBurn_Success() public {
uint256 burnAmount = 20_000e18;

deal(address(s_burnMintERC20), address(s_pool), burnAmount);
assertEq(s_burnMintERC20.balanceOf(address(s_pool)), burnAmount);

vm.startPrank(s_burnMintOnRamp);

vm.expectEmit();
emit RateLimiter.TokensConsumed(burnAmount);

vm.expectEmit();
emit IERC20.Transfer(address(s_pool), address(0), burnAmount);

vm.expectEmit();
emit TokenPool.Burned(address(s_burnMintOnRamp), burnAmount);

bytes4 expectedSignature = bytes4(keccak256("burn(uint256)"));
vm.expectCall(address(s_burnMintERC20), abi.encodeWithSelector(expectedSignature, burnAmount));

s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
originalSender: OWNER,
receiver: bytes(""),
amount: burnAmount,
remoteChainSelector: DEST_CHAIN_SELECTOR,
localToken: address(s_burnMintERC20)
})
);

assertEq(s_burnMintERC20.balanceOf(address(s_pool)), 0);
}

// Should not burn tokens if cursed.
function test_PoolBurnRevertNotHealthy_Revert() public {
s_mockRMN.setGlobalCursed(true);
uint256 before = s_burnMintERC20.balanceOf(address(s_pool));
vm.startPrank(s_burnMintOnRamp);

vm.expectRevert(TokenPool.CursedByRMN.selector);
s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
originalSender: OWNER,
receiver: bytes(""),
amount: 1e5,
remoteChainSelector: DEST_CHAIN_SELECTOR,
localToken: address(s_burnMintERC20)
})
);

assertEq(s_burnMintERC20.balanceOf(address(s_pool)), before);
}

function test_ChainNotAllowed_Revert() public {
uint64 wrongChainSelector = 8838833;

vm.expectRevert(abi.encodeWithSelector(TokenPool.ChainNotAllowed.selector, wrongChainSelector));
s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
originalSender: OWNER,
receiver: bytes(""),
amount: 1,
remoteChainSelector: wrongChainSelector,
localToken: address(s_burnMintERC20)
})
);
}
}
Loading

0 comments on commit 4df439a

Please sign in to comment.