Skip to content

Commit

Permalink
fill in coverage gaps
Browse files Browse the repository at this point in the history
  • Loading branch information
jhweintraub committed Jul 9, 2024
1 parent 5640b46 commit 4137d04
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 23 deletions.
37 changes: 22 additions & 15 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ BurnWithFromMintTokenPool_lockOrBurn:test_ChainNotAllowed_Revert() (gas: 28675)
BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurnRevertNotHealthy_Revert() (gas: 55158)
BurnWithFromMintTokenPool_lockOrBurn:test_PoolBurn_Success() (gas: 243568)
BurnWithFromMintTokenPool_lockOrBurn:test_Setup_Success() (gas: 24260)
CCIPClientTest:test_ccipReceiveAndSendAck_Success() (gas: 331839)
CCIPClientTest:test_ccipSendAndReceiveAck_in_return_Success() (gas: 348275)
CCIPClientTest:test_HappyPath_Success() (gas: 192504)
CCIPClientTest:test_ccipReceiveAndSendAck_Success() (gas: 331795)
CCIPClientTest:test_ccipReceiver_ack_with_invalidAckMessageHeaderBytes_Revert() (gas: 438714)
CCIPClientTest:test_ccipSendAndReceiveAck_in_return_Success() (gas: 348231)
CCIPClientTest:test_ccipSend_withNonNativeFeetoken_andDestTokens_Success() (gas: 325792)
CCIPClientTest:test_ccipSend_withNonNativeFeetoken_andNoDestTokens_Success() (gas: 218834)
CCIPClientTest:test_ccipSend_withNonNativeFeetoken_andNoDestTokens_Success() (gas: 241532)
CCIPClientTest:test_send_tokens_that_are_not_feeToken_Success() (gas: 552173)
CCIPClientTest:test_ccipSend_with_NativeFeeToken_andDestTokens_Success() (gas: 376797)
CCIPClientTest:test_modifyFeeToken_Success() (gas: 74452)
CCIPClientTest:test_send_tokens_that_are_not_feeToken_Success() (gas: 552182)
CCIPConfigSetup:test_getCapabilityConfiguration_Success() (gas: 9495)
CCIPConfig_ConfigStateMachine:test__computeConfigDigest_Success() (gas: 70755)
CCIPConfig_ConfigStateMachine:test__computeNewConfigWithMeta_InitToRunning_Success() (gas: 357994)
Expand Down Expand Up @@ -96,14 +102,14 @@ CCIPReceiverTest:test_modifyRouter_Success() (gas: 26446)
CCIPReceiverTest:test_removeSender_from_approvedList_and_revert_Success() (gas: 427653)
CCIPReceiverTest:test_retryFailedMessage_Success() (gas: 454419)
CCIPReceiverTest:test_withdraw_nativeToken_to_owner_Success() (gas: 20667)
CCIPReceiverWithAckTest:test_ccipReceive_ack_message_Success() (gas: 56277)
CCIPReceiverWithAckTest:test_ccipReceive_and_respond_with_ack_Success() (gas: 331828)
CCIPReceiverWithAckTest:test_ccipReceiver_ack_with_invalidAckMessageHeaderBytes_Revert() (gas: 438737)
CCIPReceiverWithAckTest:test_ccipReceive_ack_message_Success() (gas: 56278)
CCIPReceiverWithAckTest:test_ccipReceive_and_respond_with_ack_Success() (gas: 331829)
CCIPReceiverWithAckTest:test_ccipReceiver_ack_with_invalidAckMessageHeaderBytes_Revert() (gas: 438738)
CCIPReceiverWithAckTest:test_feeTokenApproval_in_constructor_Success() (gas: 2956788)
CCIPReceiverWithAckTest:test_modifyFeeToken_Success() (gas: 74835)
CCIPSenderTest:test_ccipSend_withNonNativeFeetoken_andDestTokens() (gas: 339572)
CCIPSenderTest:test_ccipSend_withNonNativeFeetoken_andNoDestTokens() (gas: 224467)
CCIPSenderTest:test_ccipSend_with_NativeFeeToken_andDestTokens() (gas: 368160)
CCIPReceiverWithAckTest:test_modifyFeeToken_Success() (gas: 74867)
CCIPSenderTest:test_ccipSend_withNonNativeFeetoken_andDestTokens_Success() (gas: 339615)
CCIPSenderTest:test_ccipSend_withNonNativeFeetoken_andNoDestTokens_Success() (gas: 224489)
CCIPSenderTest:test_ccipSend_with_NativeFeeToken_andDestTokens_Success() (gas: 368159)
CommitStore_constructor:test_Constructor_Success() (gas: 3091326)
CommitStore_isUnpausedAndRMNHealthy:test_RMN_Success() (gas: 73420)
CommitStore_report:test_InvalidIntervalMinLargerThanMax_Revert() (gas: 28670)
Expand Down Expand Up @@ -693,9 +699,10 @@ OCR2Base_transmit:test_UnAuthorizedTransmitter_Revert() (gas: 23484)
OCR2Base_transmit:test_UnauthorizedSigner_Revert() (gas: 39665)
OCR2Base_transmit:test_WrongNumberOfSignatures_Revert() (gas: 20557)
OnRampTokenPoolReentrancy:test_OnRampTokenPoolReentrancy_Success() (gas: 380711)
PingPong_example_ccipReceive:test_CcipReceive_Success() (gas: 307350)
PingPong_example_plumbing:test_Pausing_Success() (gas: 17898)
PingPong_example_startPingPong:test_StartPingPong_Success() (gas: 234292)
PingPong_example_ccipReceive:test_CcipReceive_Success() (gas: 307328)
PingPong_example_plumbing:test_Pausing_Success() (gas: 17943)
PingPong_example_plumbing:test_typeAndVersion() (gas: 9745)
PingPong_example_startPingPong:test_StartPingPong_Success() (gas: 234270)
PriceRegistry_applyFeeTokensUpdates:test_ApplyFeeTokensUpdates_Success() (gas: 79823)
PriceRegistry_applyFeeTokensUpdates:test_OnlyCallableByOwner_Revert() (gas: 12580)
PriceRegistry_constructor:test_InvalidStalenessThreshold_Revert() (gas: 67418)
Expand Down Expand Up @@ -842,8 +849,8 @@ Router_routeMessage:test_ManualExec_Success() (gas: 35381)
Router_routeMessage:test_OnlyOffRamp_Revert() (gas: 25116)
Router_routeMessage:test_WhenNotHealthy_Revert() (gas: 44724)
Router_setWrappedNative:test_OnlyOwner_Revert() (gas: 10985)
SelfFundedPingPong_ccipReceive:test_FundingIfNotANop_Revert() (gas: 289562)
SelfFundedPingPong_ccipReceive:test_Funding_Success() (gas: 448109)
SelfFundedPingPong_ccipReceive:test_FundingIfNotANop_Revert() (gas: 289627)
SelfFundedPingPong_ccipReceive:test_Funding_Success() (gas: 448499)
SelfFundedPingPong_setCountIncrBeforeFunding:test_setCountIncrBeforeFunding() (gas: 20203)
TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_OnlyPendingAdministrator_Revert() (gas: 51085)
TokenAdminRegistry_acceptAdminRole:test_acceptAdminRole_Success() (gas: 43947)
Expand Down
20 changes: 20 additions & 0 deletions contracts/src/v0.8/ccip/applications/external/CCIPClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ contract CCIPClient is CCIPReceiver {
IERC20 public s_feeToken;

event MessageSent(bytes32 messageId);
event FeeTokenUpdated(address oldFeeToken, address newFeeToken);

constructor(address router, IERC20 feeToken) CCIPReceiver(router) {
s_feeToken = feeToken;

IERC20(s_feeToken).safeApprove(s_ccipRouter, type(uint256).max);
}

/// @notice sends a message through CCIP to the router
Expand Down Expand Up @@ -79,4 +82,21 @@ contract CCIPClient is CCIPReceiver {
isValidSender(message.sourceChainSelector, message.sender)
isValidChain(message.sourceChainSelector)
{}

function updateFeeToken(address token) external onlyOwner {
// If the current fee token is not-native, zero out the allowance to the router for safety
if (address(s_feeToken) != address(0)) {
s_feeToken.safeApprove(getRouter(), 0);
}

address oldFeeToken = address(s_feeToken);
s_feeToken = IERC20(token);

// Approve the router to spend the new fee token
if (token != address(0)) {
s_feeToken.safeIncreaseAllowance(getRouter(), type(uint256).max);
}

emit FeeTokenUpdated(oldFeeToken, token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract CCIPReceiverWithACK is CCIPReceiver {
event MessageSent(bytes32 indexed incomingMessageId, bytes32 indexed ACKMessageId);

event MessageAckReceived(bytes32 messageId);
event FeeTokenModified(address indexed oldToken, address indexed newToken);
event FeeTokenUpdated(address indexed oldToken, address indexed newToken);

enum MessageType {
OUTGOING, // Indicates that a message is being sent for the first time to its recipient.
Expand Down Expand Up @@ -61,7 +61,7 @@ contract CCIPReceiverWithACK is CCIPReceiver {
}
}

function modifyFeeToken(address token) external onlyOwner {
function updateFeeToken(address token) external onlyOwner {
// If the current fee token is not-native, zero out the allowance to the router for safety
if (address(s_feeToken) != address(0)) {
s_feeToken.safeApprove(getRouter(), 0);
Expand All @@ -75,7 +75,7 @@ contract CCIPReceiverWithACK is CCIPReceiver {
s_feeToken.safeIncreaseAllowance(getRouter(), type(uint256).max);
}

emit FeeTokenModified(oldFeeToken, token);
emit FeeTokenUpdated(oldFeeToken, token);
}

/// @notice Application-specific logic for incoming ccip messages.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {CCIPBase} from "../../../applications/external/CCIPBase.sol";
import {CCIPClient} from "../../../applications/external/CCIPClient.sol";

import {Client} from "../../../libraries/Client.sol";
import {EVM2EVMOnRampSetup} from "../../onRamp/EVM2EVMOnRampSetup.t.sol";

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

contract CCIPClientTest is EVM2EVMOnRampSetup {
event MessageFailed(bytes32 indexed messageId, bytes reason);
event MessageSucceeded(bytes32 indexed messageId);
event MessageRecovered(bytes32 indexed messageId);

CCIPClient internal s_sender;

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

s_sender = new CCIPClient(address(s_sourceRouter), IERC20(s_sourceFeeToken));

CCIPBase.ChainUpdate[] memory chainUpdates = new CCIPBase.ChainUpdate[](1);
chainUpdates[0] = CCIPBase.ChainUpdate({
chainSelector: DEST_CHAIN_SELECTOR,
allowed: true,
recipient: abi.encode(address(s_sender)),
extraArgsBytes: ""
});
s_sender.applyChainUpdates(chainUpdates);

CCIPBase.ApprovedSenderUpdate[] memory senderUpdates = new CCIPBase.ApprovedSenderUpdate[](1);
senderUpdates[0] =
CCIPBase.ApprovedSenderUpdate({destChainSelector: DEST_CHAIN_SELECTOR, sender: abi.encode(address(s_sender))});

s_sender.updateApprovedSenders(senderUpdates, new CCIPBase.ApprovedSenderUpdate[](0));
}

function test_ccipSend_withNonNativeFeetoken_andDestTokens_Success() public {
address token = address(s_sourceFeeToken);
uint256 amount = 111333333777;
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1);
destTokenAmounts[0] = Client.EVMTokenAmount({token: token, amount: amount});

// Make sure we give the receiver contract enough tokens like CCIP would.
IERC20(token).approve(address(s_sender), type(uint256).max);

Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(address(s_sender)),
data: "",
tokenAmounts: destTokenAmounts,
feeToken: s_sourceFeeToken,
extraArgs: ""
});

uint256 feeTokenAmount = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message);
uint256 feeTokenBalanceBefore = IERC20(s_sourceFeeToken).balanceOf(OWNER);

s_sender.ccipSend({destChainSelector: DEST_CHAIN_SELECTOR, tokenAmounts: destTokenAmounts, data: ""});

// Assert that tokens were transfered for bridging + fees
assertEq(IERC20(token).balanceOf(OWNER), feeTokenBalanceBefore - amount - feeTokenAmount);
}

function test_ccipSend_withNonNativeFeetoken_andNoDestTokens_Success() public {
address token = address(s_sourceFeeToken);
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0);

// Make sure we give the receiver contract enough tokens like CCIP would.
IERC20(token).approve(address(s_sender), type(uint256).max);

Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(address(s_sender)),
data: "",
tokenAmounts: destTokenAmounts,
feeToken: s_sourceFeeToken,
extraArgs: ""
});

uint256 feeTokenAmount = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message);
uint256 feeTokenBalanceBefore = IERC20(s_sourceFeeToken).balanceOf(OWNER);

s_sender.ccipSend({destChainSelector: DEST_CHAIN_SELECTOR, tokenAmounts: destTokenAmounts, data: ""});

// Assert that tokens were transfered for bridging + fees
assertEq(IERC20(token).balanceOf(OWNER), feeTokenBalanceBefore - feeTokenAmount);
}

function test_modifyFeeToken_Success() public {
// WETH is used as a placeholder for any ERC20 token
address WETH = s_sourceRouter.getWrappedNative();

vm.expectEmit();
emit IERC20.Approval(address(s_sender), address(s_sourceRouter), 0);

vm.expectEmit();
emit CCIPClient.FeeTokenUpdated(s_sourceFeeToken, WETH);

s_sender.updateFeeToken(WETH);

IERC20 newFeeToken = s_sender.s_feeToken();
assertEq(address(newFeeToken), WETH);
assertEq(newFeeToken.allowance(address(s_sender), address(s_sourceRouter)), type(uint256).max);
assertEq(IERC20(s_sourceFeeToken).allowance(address(s_sender), address(s_sourceRouter)), 0);
}

function test_ccipSend_with_NativeFeeToken_andDestTokens_Success() public {
address token = address(s_sourceFeeToken);
uint256 amount = 111333333777;
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1);
destTokenAmounts[0] = Client.EVMTokenAmount({token: token, amount: amount});

s_sender.updateFeeToken(address(0));

// Make sure we give the receiver contract enough tokens like CCIP would.
IERC20(token).approve(address(s_sender), type(uint256).max);

Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: abi.encode(address(s_sender)),
data: "",
tokenAmounts: destTokenAmounts,
extraArgs: "",
feeToken: address(s_sourceFeeToken)
});

uint256 feeTokenAmount = s_sourceRouter.getFee(DEST_CHAIN_SELECTOR, message);
uint256 tokenBalanceBefore = IERC20(token).balanceOf(OWNER);
uint256 nativeFeeTokenBalanceBefore = OWNER.balance;

s_sender.ccipSend{value: feeTokenAmount}({
destChainSelector: DEST_CHAIN_SELECTOR,
tokenAmounts: destTokenAmounts,
data: ""
});

// Assert that native fees are paid successfully and tokens are transferred
assertEq(IERC20(token).balanceOf(OWNER), tokenBalanceBefore - amount, "Tokens were not successfully delivered");
assertEq(
OWNER.balance, nativeFeeTokenBalanceBefore - feeTokenAmount, "Native fee tokens were not successfully forwarded"
);
}

function test_HappyPath_Success() public {
bytes32 messageId = keccak256("messageId");
address token = address(s_destFeeToken);
uint256 amount = 111333333777;
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1);
destTokenAmounts[0] = Client.EVMTokenAmount({token: token, amount: amount});

// Make sure we give the receiver contract enough tokens like CCIP would.
deal(token, address(s_sender), amount);

// The receiver contract will revert if the router is not the sender.
vm.startPrank(address(s_sourceRouter));

vm.expectEmit();
emit MessageSucceeded(messageId);

s_sender.ccipReceive(
Client.Any2EVMMessage({
messageId: messageId,
sourceChainSelector: DEST_CHAIN_SELECTOR,
sender: abi.encode(address(s_sender)), // correct sender
data: "",
destTokenAmounts: destTokenAmounts
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,38 @@ contract CCIPClientTest is EVM2EVMOnRampSetup {
s_sender.updateApprovedSenders(senderUpdates, new CCIPBase.ApprovedSenderUpdate[](0));
}

function test_ccipReceiver_ack_with_invalidAckMessageHeaderBytes_Revert() public {
bytes32 messageId = keccak256("messageId");
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0);

// The receiver contract will revert if the router is not the sender.
vm.startPrank(address(s_sourceRouter));

// Payload with incorrect ack message header should revert
CCIPReceiverWithACK.MessagePayload memory payload = CCIPReceiverWithACK.MessagePayload({
version: "",
data: abi.encode("RANDOM_BYTES", messageId),
messageType: CCIPReceiverWithACK.MessageType.ACK
});

// Expect the processing to revert from invalid Ack Message Header
vm.expectEmit();
emit MessageFailed(messageId, abi.encodeWithSelector(bytes4(CCIPReceiverWithACK.InvalidAckMessageHeader.selector)));

s_sender.ccipReceive(
Client.Any2EVMMessage({
messageId: messageId,
sourceChainSelector: destChainSelector,
sender: abi.encode(address(s_sender)),
data: abi.encode(payload),
destTokenAmounts: destTokenAmounts
})
);

// Check that message status is failed
assertEq(s_sender.getMessageStatus(messageId), 1);
}

function test_ccipReceiveAndSendAck_Success() public {
bytes32 messageId = keccak256("messageId");
bytes32 ackMessageId = 0x37ddbb21a51d4e07877b0de816905ea806b958e7607d951d307030631db076bd;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ contract CCIPReceiverWithAckTest is EVM2EVMOnRampSetup {
emit IERC20.Approval(address(s_receiver), address(s_sourceRouter), 0);

vm.expectEmit();
emit CCIPReceiverWithACK.FeeTokenModified(s_sourceFeeToken, WETH);
emit CCIPReceiverWithACK.FeeTokenUpdated(s_sourceFeeToken, WETH);

s_receiver.modifyFeeToken(WETH);
s_receiver.updateFeeToken(WETH);

IERC20 newFeeToken = s_receiver.s_feeToken();
assertEq(address(newFeeToken), WETH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ contract CCIPSenderTest is EVM2EVMOnRampSetup {
s_sender.applyChainUpdates(chainUpdates);
}

function test_ccipSend_withNonNativeFeetoken_andDestTokens() public {
function test_ccipSend_withNonNativeFeetoken_andDestTokens_Success() public {
address token = address(s_sourceFeeToken);
uint256 amount = 111333333777;
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1);
Expand Down Expand Up @@ -63,7 +63,7 @@ contract CCIPSenderTest is EVM2EVMOnRampSetup {
assertEq(IERC20(token).balanceOf(OWNER), feeTokenBalanceBefore - amount - feeTokenAmount);
}

function test_ccipSend_withNonNativeFeetoken_andNoDestTokens() public {
function test_ccipSend_withNonNativeFeetoken_andNoDestTokens_Success() public {
address token = address(s_sourceFeeToken);
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](0);

Expand Down Expand Up @@ -92,7 +92,7 @@ contract CCIPSenderTest is EVM2EVMOnRampSetup {
assertEq(IERC20(token).balanceOf(OWNER), feeTokenBalanceBefore - feeTokenAmount);
}

function test_ccipSend_with_NativeFeeToken_andDestTokens() public {
function test_ccipSend_with_NativeFeeToken_andDestTokens_Success() public {
address token = address(s_sourceFeeToken);
uint256 amount = 111333333777;
Client.EVMTokenAmount[] memory destTokenAmounts = new Client.EVMTokenAmount[](1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,8 @@ contract PingPong_example_plumbing is PingPongDappSetup {

assertTrue(s_pingPong.isPaused());
}

function test_typeAndVersion() public view {
assertEq(s_pingPong.typeAndVersion(), "PingPongDemo 1.3.0");
}
}

0 comments on commit 4137d04

Please sign in to comment.