Skip to content

Instantly share code, notes, and snippets.

@rgottleber
Created May 12, 2025 14:15
Show Gist options
  • Select an option

  • Save rgottleber/6ccec30654cd251807d71b3007d4003a to your computer and use it in GitHub Desktop.

Select an option

Save rgottleber/6ccec30654cd251807d71b3007d4003a to your computer and use it in GitHub Desktop.
CCIP-JS SDK example for use in Remix
// 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