Created
May 12, 2025 14:15
-
-
Save rgottleber/6ccec30654cd251807d71b3007d4003a to your computer and use it in GitHub Desktop.
CCIP-JS SDK example for use in Remix
This file contains hidden or 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
| // Import necessary libraries | |
| import * as CCIP from '@chainlink/ccip-js' // Chainlink CCIP library for cross-chain interactions | |
| import { createWalletClient, custom} from 'viem' // Ethereum interaction library | |
| import { sepolia } from 'viem/chains' // Testnet chain configurations | |
| // Constants for the CCIP routers, tokens, and other parameters | |
| // Router addresses are specific to each network | |
| // See https://docs.chain.link/ccip/directory/ for more details | |
| const sepoliaRouterAddress = '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59' // Sepolia network router | |
| // Token addresses on Sepolia network | |
| const usdcTokenAddress = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238' // USDC token on Sepolia | |
| const linkTokenAddress = '0x779877A7B0D9E8603169DdbD7836e478b4624789' // LINK token on Sepolia | |
| // Chain selectors are unique identifiers for each blockchain in CCIP | |
| const fujiChainSelector = '14767482510784806043' // Identifier for Fuji network | |
| // Amount of USDC to transfer (100000 = 0.1 USDC with 6 decimals) | |
| const amtUSDC = 100000n // 0.1 USDC | |
| // Global variables | |
| let ccipClient; // CCIP client for cross-chain operations | |
| let walletClient; // Wallet client for blockchain interactions | |
| let destinationAccountAddress; // User's wallet address | |
| /** | |
| * Initialize global variables and connections | |
| * This function sets up the CCIP client and wallet connection | |
| */ | |
| async function initializeGlobals() { | |
| console.log('starting'); | |
| // Create a CCIP client connected to Sepolia network | |
| ccipClient = CCIP.createClient({ chain: sepolia }) | |
| console.log('creating wallet'); | |
| // Get reference to the user's browser wallet (MetaMask) | |
| const provider = window.ethereum; | |
| // Request to switch to Sepolia network if not already on it | |
| await provider.request({ | |
| method: 'wallet_switchEthereumChain', | |
| params: [{ chainId: '0xaa36a7' }], // Sepolia Chain ID in hex | |
| }); | |
| // Request user's wallet accounts | |
| // Note: Remix IDE JS connects via remix-project-org.github.io | |
| const accounts = await provider.request({ method: 'eth_requestAccounts' }); | |
| destinationAccountAddress = accounts[0]; | |
| // Create a wallet client with the user's account | |
| walletClient = createWalletClient({ | |
| chain: sepolia, | |
| transport: custom(provider), | |
| account: { | |
| address: destinationAccountAddress, | |
| type: 'json-rpc' // This is important for MetaMask integration | |
| } | |
| }) | |
| } | |
| /** | |
| * Approve the CCIP router to spend tokens on behalf of the user | |
| * This is necessary before tokens can be transferred across chains | |
| */ | |
| async function approveRouter() { | |
| console.log('Calling approveRouter() for USDC') | |
| // Approve Router to transfer USDC tokens on user's behalf | |
| await ccipClient.approveRouter({ | |
| client: walletClient, | |
| routerAddress: sepoliaRouterAddress, | |
| tokenAddress: usdcTokenAddress, | |
| amount: amtUSDC, | |
| waitForReceipt: true, // Wait for transaction to be confirmed | |
| }) | |
| console.log('Calling approveRouter() for LINK') | |
| // Approve Router to transfer LINK tokens (for fees) on user's behalf | |
| await ccipClient.approveRouter({ | |
| client: walletClient, | |
| routerAddress: sepoliaRouterAddress, | |
| tokenAddress: linkTokenAddress, | |
| amount: 1000000000000000000n, // 1 LINK (with 18 decimals) | |
| waitForReceipt: true, | |
| }) | |
| } | |
| /** | |
| * Check if the router has allowance to spend user's tokens | |
| * Returns the amount of tokens the router is allowed to spend | |
| */ | |
| async function confirmAllowance() { | |
| console.log("Calling getAllowance() for USDC") | |
| // Get current allowance for USDC tokens | |
| return await ccipClient | |
| .getAllowance({ | |
| client: walletClient, | |
| account: walletClient.account.address, | |
| routerAddress: sepoliaRouterAddress, | |
| tokenAddress: usdcTokenAddress, | |
| }) | |
| } | |
| /** | |
| * Transfer USDC tokens from Sepolia to Fuji network | |
| * Uses LINK tokens to pay for the transaction fees | |
| */ | |
| async function transferTokensPayLINK() { | |
| console.log("Calling transferTokens() for USDC") | |
| // Initiate cross-chain token transfer | |
| return await ccipClient | |
| .transferTokens({ | |
| client: walletClient, | |
| routerAddress: sepoliaRouterAddress, // Source chain router | |
| tokenAddress: usdcTokenAddress, // Token to transfer | |
| destinationChainSelector: fujiChainSelector, // Target chain | |
| destinationAccount: destinationAccountAddress, // Recipient address | |
| amount: amtUSDC, // Amount to transfer | |
| feeTokenAddress: linkTokenAddress, // Token used to pay fees | |
| }) | |
| } | |
| /** | |
| * Main function that orchestrates the cross-chain transfer process | |
| */ | |
| async function main() { | |
| // Step 1: Set up connections and clients | |
| await initializeGlobals() | |
| // Step 2: Approve the router to spend tokens | |
| await approveRouter() | |
| // Step 3: Verify the router has allowance to spend tokens | |
| const allowance = await confirmAllowance() | |
| console.log("ALLOWANCE: ", allowance) | |
| // Step 4: Perform the cross-chain token transfer | |
| const resp = await transferTokensPayLINK() | |
| // Step 5: Log the transaction details | |
| console.log(resp.messageId); | |
| // Create a URL to track the CCIP message | |
| const messageUrl = `https://ccip.chain.link/#/side-drawer/msg/${resp.messageId}` | |
| console.log("MESSAGE TRANSACTION: ", messageUrl) | |
| } | |
| // Execute the main function and catch any errors | |
| main().catch((e) => { console.log("ERROR: ", e) }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment