-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Matt Yang <[email protected]> Co-authored-by: Rens Rooimans <[email protected]>
- Loading branch information
1 parent
fd840fa
commit 2ed2a07
Showing
10 changed files
with
1,629 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
contracts/src/v0.8/ccip/applications/SelfFundedPingPong.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {PingPongDemo} from "./PingPongDemo.sol"; | ||
import {Client} from "../libraries/Client.sol"; | ||
import {Router} from "../Router.sol"; | ||
import {EVM2EVMOnRamp} from "../onRamp/EVM2EVMOnRamp.sol"; | ||
|
||
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract SelfFundedPingPong is PingPongDemo { | ||
// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables | ||
string public constant override typeAndVersion = "SelfFundedPingPong 1.2.0"; | ||
|
||
event Funded(); | ||
event CountIncrBeforeFundingSet(uint8 countIncrBeforeFunding); | ||
|
||
// Defines the increase in ping pong count before self-funding is attempted. | ||
// Set to 0 to disable auto-funding, auto-funding only works for ping-pongs that are set as NOPs in the onRamp. | ||
uint8 private s_countIncrBeforeFunding; | ||
|
||
constructor(address router, IERC20 feeToken, uint8 roundTripsBeforeFunding) PingPongDemo(router, feeToken) { | ||
// PingPong count increases by 2 for each round trip. | ||
s_countIncrBeforeFunding = roundTripsBeforeFunding * 2; | ||
} | ||
|
||
function _respond(uint256 pingPongCount) internal override { | ||
if (pingPongCount & 1 == 1) { | ||
emit Ping(pingPongCount); | ||
} else { | ||
emit Pong(pingPongCount); | ||
} | ||
|
||
fundPingPong(pingPongCount); | ||
|
||
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ | ||
receiver: abi.encode(s_counterpartAddress), | ||
data: abi.encode(pingPongCount), | ||
tokenAmounts: new Client.EVMTokenAmount[](0), | ||
extraArgs: "", | ||
feeToken: address(s_feeToken) | ||
}); | ||
Router(getRouter()).ccipSend(s_counterpartChainSelector, message); | ||
} | ||
|
||
/// @notice A function that is responsible for funding this contract. | ||
/// The contract can only be funded if it is set as a nop in the target onRamp. | ||
/// In case your contract is not a nop you can prevent this function from being called by setting s_countIncrBeforeFunding=0. | ||
function fundPingPong(uint256 pingPongCount) public { | ||
// If selfFunding is disabled, or ping pong count has not reached s_countIncrPerFunding, do not attempt funding. | ||
if (s_countIncrBeforeFunding == 0 || pingPongCount < s_countIncrBeforeFunding) return; | ||
|
||
// Ping pong on one side will always be even, one side will always to odd. | ||
if (pingPongCount % s_countIncrBeforeFunding <= 1) { | ||
EVM2EVMOnRamp(Router(getRouter()).getOnRamp(s_counterpartChainSelector)).payNops(); | ||
emit Funded(); | ||
} | ||
} | ||
|
||
function getCountIncrBeforeFunding() external view returns (uint8) { | ||
return s_countIncrBeforeFunding; | ||
} | ||
|
||
function setCountIncrBeforeFunding(uint8 countIncrBeforeFunding) public onlyOwner { | ||
s_countIncrBeforeFunding = countIncrBeforeFunding; | ||
emit CountIncrBeforeFundingSet(countIncrBeforeFunding); | ||
} | ||
} |
File renamed without changes.
107 changes: 107 additions & 0 deletions
107
contracts/src/v0.8/ccip/test/applications/SelfFundedPingPong.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
pragma solidity 0.8.19; | ||
|
||
import {SelfFundedPingPong} from "../../applications/SelfFundedPingPong.sol"; | ||
import {EVM2EVMOnRampSetup} from "../onRamp/EVM2EVMOnRampSetup.t.sol"; | ||
import {EVM2EVMOnRamp} from "../../onRamp/EVM2EVMOnRamp.sol"; | ||
import {Client} from "../../libraries/Client.sol"; | ||
|
||
import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract SelfFundedPingPongDappSetup is EVM2EVMOnRampSetup { | ||
event Ping(uint256 pingPongs); | ||
event Pong(uint256 pingPongs); | ||
event CountIncrBeforeFundingSet(uint8 countIncrBeforeFunding); | ||
|
||
SelfFundedPingPong internal s_pingPong; | ||
IERC20 internal s_feeToken; | ||
uint8 internal constant s_roundTripsBeforeFunding = 0; | ||
|
||
address internal immutable i_pongContract = address(10); | ||
|
||
function setUp() public virtual override { | ||
EVM2EVMOnRampSetup.setUp(); | ||
|
||
s_feeToken = IERC20(s_sourceTokens[0]); | ||
s_pingPong = new SelfFundedPingPong(address(s_sourceRouter), s_feeToken, s_roundTripsBeforeFunding); | ||
s_pingPong.setCounterpart(DEST_CHAIN_ID, i_pongContract); | ||
|
||
uint256 fundingAmount = 5e18; | ||
|
||
// set ping pong as an onRamp nop to make sure that funding runs | ||
EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](1); | ||
nopsAndWeights[0] = EVM2EVMOnRamp.NopAndWeight({nop: address(s_pingPong), weight: 1}); | ||
s_onRamp.setNops(nopsAndWeights); | ||
|
||
// Fund the contract with LINK tokens | ||
s_feeToken.transfer(address(s_pingPong), fundingAmount); | ||
} | ||
} | ||
|
||
/// @notice #ccipReceive | ||
contract SelfFundedPingPong_ccipReceive is SelfFundedPingPongDappSetup { | ||
event Funded(); | ||
|
||
function test_FundingSuccess() public { | ||
Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ | ||
messageId: bytes32("a"), | ||
sourceChainSelector: DEST_CHAIN_ID, | ||
sender: abi.encode(i_pongContract), | ||
data: "", | ||
destTokenAmounts: new Client.EVMTokenAmount[](0) | ||
}); | ||
|
||
uint8 countIncrBeforeFunding = 5; | ||
|
||
vm.expectEmit(); | ||
emit CountIncrBeforeFundingSet(countIncrBeforeFunding); | ||
|
||
s_pingPong.setCountIncrBeforeFunding(countIncrBeforeFunding); | ||
|
||
vm.startPrank(address(s_sourceRouter)); | ||
for (uint256 pingPongNumber = 0; pingPongNumber <= countIncrBeforeFunding; ++pingPongNumber) { | ||
message.data = abi.encode(pingPongNumber); | ||
if (pingPongNumber == countIncrBeforeFunding - 1) { | ||
vm.expectEmit(); | ||
emit Funded(); | ||
vm.expectCall(address(s_onRamp), ""); | ||
} | ||
s_pingPong.ccipReceive(message); | ||
} | ||
} | ||
|
||
function test_FundingIfNotANopReverts() public { | ||
EVM2EVMOnRamp.NopAndWeight[] memory nopsAndWeights = new EVM2EVMOnRamp.NopAndWeight[](0); | ||
s_onRamp.setNops(nopsAndWeights); | ||
|
||
uint8 countIncrBeforeFunding = 3; | ||
s_pingPong.setCountIncrBeforeFunding(countIncrBeforeFunding); | ||
|
||
vm.startPrank(address(s_sourceRouter)); | ||
Client.Any2EVMMessage memory message = Client.Any2EVMMessage({ | ||
messageId: bytes32("a"), | ||
sourceChainSelector: DEST_CHAIN_ID, | ||
sender: abi.encode(i_pongContract), | ||
data: abi.encode(countIncrBeforeFunding), | ||
destTokenAmounts: new Client.EVMTokenAmount[](0) | ||
}); | ||
|
||
// because pingPong is not set as a nop | ||
vm.expectRevert(EVM2EVMOnRamp.OnlyCallableByOwnerOrAdminOrNop.selector); | ||
s_pingPong.ccipReceive(message); | ||
} | ||
} | ||
|
||
/// @notice #setCountIncrBeforeFunding | ||
contract SelfFundedPingPong_setCountIncrBeforeFunding is SelfFundedPingPongDappSetup { | ||
function test_setCountIncrBeforeFunding() public { | ||
uint8 c = s_pingPong.getCountIncrBeforeFunding(); | ||
|
||
vm.expectEmit(); | ||
emit CountIncrBeforeFundingSet(c + 1); | ||
|
||
s_pingPong.setCountIncrBeforeFunding(c + 1); | ||
uint8 c2 = s_pingPong.getCountIncrBeforeFunding(); | ||
assertEq(c2, c + 1); | ||
} | ||
} |
Oops, something went wrong.