Skip to content

Commit b7cefee

Browse files
committed
add SepoliaSender contract for cross-chain message sending functionality
1 parent f8db962 commit b7cefee

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

src/SepoliaSender.sol

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.26;
3+
4+
import {IRouterClient} from "@chainlink/contracts/src/v0.8/ccip/interfaces/IRouterClient.sol";
5+
import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol";
6+
import {Client} from "@chainlink/contracts/src/v0.8/ccip/libraries/Client.sol";
7+
import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";
8+
9+
/// @title SepoliaSender
10+
/// @author Luo Yingjie
11+
/// @notice This contract will send a cross chain message from Sepolia to Amoy to trigger the token transfer
12+
contract SepoliaSender is OwnerIsCreator {
13+
// Custom errors to provide more descriptive revert messages.
14+
error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees); // Used to make sure contract has enough balance.
15+
16+
// Event emitted when a message is sent to another chain.
17+
event MessageSent(
18+
bytes32 indexed messageId, // The unique ID of the CCIP message.
19+
uint64 indexed destinationChainSelector, // The chain selector of the destination chain.
20+
address receiver, // The address of the receiver on the destination chain.
21+
bytes signedMessage, // The signed message that approves the token transfer
22+
address feeToken, // the token address used to pay CCIP fees.
23+
uint256 fees // The fees paid for sending the CCIP message.
24+
);
25+
26+
IRouterClient private s_router;
27+
28+
LinkTokenInterface private s_linkToken;
29+
30+
/// @notice Constructor initializes the contract with the router address.
31+
/// @param _router The address of the router contract.
32+
/// @param _link The address of the link contract.
33+
constructor(address _router, address _link) {
34+
s_router = IRouterClient(_router);
35+
s_linkToken = LinkTokenInterface(_link);
36+
}
37+
38+
// This function should send the data which includes the amount to send, token address, and receiver address, and sign the approval of the token transfer
39+
function sendMessage(
40+
uint64 destinationChainSelector,
41+
address receiver,
42+
bytes calldata signedMessage // This is the signed message that approves the token transfer
43+
) external onlyOwner returns (bytes32 messageId) {
44+
// Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
45+
Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
46+
receiver: abi.encode(receiver), // ABI-encoded receiver address
47+
data: abi.encode(signedMessage),
48+
tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array indicating no tokens are being sent
49+
extraArgs: Client._argsToBytes(
50+
// Additional arguments, setting gas limit and allowing out-of-order execution.
51+
// Best Practice: For simplicity, the values are hardcoded. It is advisable to use a more dynamic approach
52+
// where you set the extra arguments off-chain. This allows adaptation depending on the lanes, messages,
53+
// and ensures compatibility with future CCIP upgrades. Read more about it here: https://docs.chain.link/ccip/best-practices#using-extraargs
54+
Client.EVMExtraArgsV2({
55+
gasLimit: 200_000, // Gas limit for the callback on the destination chain
56+
allowOutOfOrderExecution: true // Allows the message to be executed out of order relative to other messages from the same sender
57+
})
58+
),
59+
// Set the feeToken address, indicating LINK will be used for fees
60+
feeToken: address(s_linkToken)
61+
});
62+
63+
// Get the fee required to send the message
64+
uint256 fees = s_router.getFee(
65+
destinationChainSelector,
66+
evm2AnyMessage
67+
);
68+
69+
if (fees > s_linkToken.balanceOf(address(this)))
70+
revert NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees);
71+
72+
// approve the Router to transfer LINK tokens on contract's behalf. It will spend the fees in LINK
73+
s_linkToken.approve(address(s_router), fees);
74+
75+
// Send the message through the router and store the returned message ID
76+
messageId = s_router.ccipSend(destinationChainSelector, evm2AnyMessage);
77+
78+
// Emit an event with message details
79+
emit MessageSent(
80+
messageId,
81+
destinationChainSelector,
82+
receiver,
83+
signedMessage,
84+
address(s_linkToken),
85+
fees
86+
);
87+
88+
// Return the message ID
89+
return messageId;
90+
}
91+
}

0 commit comments

Comments
 (0)