Skip to content

Commit 87a69b0

Browse files
committed
test: add SubscriptionTest for validating subscription fee payment process of the optional chain, which is the key functionality in this system
1 parent 7fc7cb1 commit 87a69b0

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import {Test, console} from "forge-std/Test.sol";
5+
import {CCIPLocalSimulator, IRouterClient, LinkToken, BurnMintERC677Helper} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
6+
import {Subscription} from "src/Subscription.sol";
7+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8+
import {Sender} from "src/Sender.sol";
9+
import {Receiver} from "src/Receiver.sol";
10+
import {ReceiverSignedMessage} from "src/library/ReceiverSignedMessage.sol";
11+
import {MockCheckBalance} from "test/mocks/MockCheckBalance.sol";
12+
import {Vm} from "forge-std/Vm.sol";
13+
import {Relayer} from "src/Relayer.sol";
14+
15+
contract SubscriptionTest is Test {
16+
CCIPLocalSimulator public ccipLocalSimulator;
17+
uint64 public destinationChainSelector;
18+
BurnMintERC677Helper public CCIPBnM;
19+
address user;
20+
uint256 userPrivateKey;
21+
22+
uint256 constant AMOUNT_CCIPBNM = 1e18;
23+
IRouterClient destinationRouter;
24+
address linkAddress;
25+
26+
Subscription public subscription;
27+
// To simulate the check balance contracts
28+
MockCheckBalance public checkBalance;
29+
Sender public sender;
30+
Receiver public receiver;
31+
Relayer public relayer;
32+
33+
function setUp() public {
34+
(user, userPrivateKey) = makeAddrAndKey("user");
35+
ccipLocalSimulator = new CCIPLocalSimulator();
36+
37+
(
38+
uint64 chainSelector,
39+
IRouterClient sourceRouter,
40+
IRouterClient _destinationRouter,
41+
,
42+
LinkToken link,
43+
BurnMintERC677Helper ccipBnM,
44+
45+
) = ccipLocalSimulator.configuration();
46+
47+
destinationChainSelector = chainSelector;
48+
CCIPBnM = ccipBnM;
49+
destinationRouter = _destinationRouter;
50+
linkAddress = address(link);
51+
52+
uint64[] memory subscriptionChainsSelector = new uint64[](1);
53+
subscriptionChainsSelector[0] = destinationChainSelector;
54+
55+
vm.startPrank(user);
56+
checkBalance = new MockCheckBalance();
57+
sender = new Sender(address(sourceRouter), linkAddress);
58+
receiver = new Receiver(address(destinationRouter), linkAddress);
59+
subscription = new Subscription(
60+
subscriptionChainsSelector,
61+
address(CCIPBnM),
62+
address(CCIPBnM),
63+
address(sourceRouter),
64+
address(checkBalance),
65+
address(sender),
66+
address(receiver)
67+
);
68+
relayer = new Relayer(
69+
address(destinationRouter),
70+
linkAddress,
71+
address(subscription)
72+
);
73+
74+
// transfer the ownership to subscription contract first as the set up
75+
checkBalance.setSubscriptionAsOwner(address(subscription));
76+
sender.setSubscriptionAsOwner(address(subscription));
77+
vm.stopPrank();
78+
}
79+
80+
function testPaySubscriptionFeeforOptionalChainSuccessUpdateTheMapping()
81+
public
82+
{
83+
ccipLocalSimulator.requestLinkFromFaucet(address(sender), 20 ether);
84+
ccipLocalSimulator.requestLinkFromFaucet(address(receiver), 20 ether);
85+
86+
deal(address(CCIPBnM), user, AMOUNT_CCIPBNM);
87+
88+
ReceiverSignedMessage.SignedMessage
89+
memory signedMessage = ReceiverSignedMessage.SignedMessage({
90+
chainSelector: destinationChainSelector,
91+
user: user,
92+
token: address(CCIPBnM),
93+
amount: AMOUNT_CCIPBNM,
94+
transferContract: address(receiver),
95+
router: address(destinationRouter),
96+
nonce: 0,
97+
expiry: block.timestamp + 1 days
98+
});
99+
100+
bytes32 digest = receiver.getMessageHash(signedMessage);
101+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, digest);
102+
103+
bytes memory encodedSignedMessage = abi.encode(
104+
user,
105+
signedMessage,
106+
v,
107+
r,
108+
s
109+
);
110+
111+
// approve the transfer first
112+
vm.prank(user);
113+
CCIPBnM.approve(address(receiver), AMOUNT_CCIPBNM);
114+
115+
// 1. token transferred
116+
vm.recordLogs();
117+
subscription.paySubscriptionFeeforOptionalChain(
118+
address(CCIPBnM),
119+
destinationChainSelector,
120+
encodedSignedMessage
121+
);
122+
Vm.Log[] memory entries = vm.getRecordedLogs();
123+
124+
// The event MessageReceived is the 13th event
125+
uint64 optionalChain = uint64(uint256(entries[12].topics[1]));
126+
address paymentTokenForOptionalChain = bytes32ToAddress(
127+
entries[12].topics[2]
128+
);
129+
address signer = bytes32ToAddress(entries[12].topics[3]);
130+
131+
assertEq(optionalChain, destinationChainSelector);
132+
assertEq(paymentTokenForOptionalChain, address(CCIPBnM));
133+
assertEq(signer, user);
134+
135+
// 2. The Relayer listen for MessageReceived event and send the message to the Subscription contract
136+
bytes memory performData = abi.encode(
137+
optionalChain,
138+
paymentTokenForOptionalChain,
139+
signer
140+
);
141+
142+
relayer.performUpkeep(performData);
143+
144+
// 3. The Subscription contract receives the message and update the s_subscriberToSubscription mapping
145+
assertEq(
146+
subscription.getSubscriberToSubscription(user).optionalChain,
147+
destinationChainSelector
148+
);
149+
assertEq(
150+
subscription
151+
.getSubscriberToSubscription(user)
152+
.paymentTokenForOptionalChain,
153+
address(CCIPBnM)
154+
);
155+
}
156+
157+
/*//////////////////////////////////////////////////////////////
158+
HELPER FUNCTIONS
159+
//////////////////////////////////////////////////////////////*/
160+
function bytes32ToAddress(bytes32 _address) public pure returns (address) {
161+
return address(uint160(uint256(_address)));
162+
}
163+
}

0 commit comments

Comments
 (0)