npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@chainlink/ccip-js

v0.2.1

Published

Downloads

181

Readme

CCIP-JS

CCIP-JS is a TypeScript library that provides a client for managing cross-chain token transfers that use Chainlink's Cross-Chain Interoperability Protocol (CCIP) routers. The library utilizes types and helper functions from Viem.

To learn more about CCIP, refer to the CCIP documentation.

Table of Contents

Why CCIP-JS?

CCIP-JS provides ready-to-use typesafe methods for every step of the token transfer process. Although you can do a CCIP token transfer simply by calling the ccipSend function, there are multiple steps that need to be done beforehand, such as:

  • checking allowances
  • approving transfers
  • retrieving supported lanes
  • retrieving lane limits
  • retrieving fee amounts and fee tokens

Additionally, after the transfer, you may need to check the transfer status.

Features

  • Token Approvals: Approve tokens for cross-chain transfers.
  • Allowance Checks: Retrieve the allowance for token transfers.
  • Rate Limits: Get rate refill limits for lanes.
  • Fee Calculation: Calculate the fee required for transfers.
  • Token Transfers: Transfer tokens across chains.
  • Transfer Status: Retrieve the status of a transfer by transaction hash.

Installation

To install the package, use the following command:

npm install @chainlink/ccip-js viem

Or with Yarn:

yarn add @chainlink/ccip-js viem

Or with PNPM:

pnpm add @chainlink/ccip-js viem

Usage

This example code covers the following steps:

  • Initialize CCIP-JS Client for mainnet
  • Approve tokens for transfer
  • Get fee for the transfer
  • Send the transfer through CCIP using one of the following options for fee payment:
    • Using the native token fee
    • Using the provided supported token for fee payment
import * as CCIP from '@chainlink/ccip-js'
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

// Initialize CCIP-JS Client for mainnet
const ccipClient = CCIP.createClient()
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(),
})
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum!),
})

// Approve Router to transfer tokens on user's behalf
const { txHash, txReceipt } = await ccipClient.approveRouter({
  client: walletClient,
  routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  amount: 1000000000000000000n,
  waitForReceipt: true,
})

console.log(`Transfer approved. Transaction hash: ${txHash}. Transaction receipt: ${txReceipt}`)

// Get fee for the transfer
const fee = await ccipClient.getFee({
  client: publicClient,
  routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  amount: 1000000000000000000n,
  destinationAccount: '0x1234567890abcdef1234567890abcdef12345678',
  destinationChainSelector: '1234',
})

console.log(`Fee: ${fee.toLocaleString()}`)

// Variant 1: Transfer via CCIP using native token fee
const { txHash, messageId } = await client.transferTokens({
  client: walletClient,
  routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  amount: 1000000000000000000n,
  destinationAccount: '0x1234567890abcdef1234567890abcdef12345678',
  destinationChainSelector: '1234',
})

console.log(`Transfer success. Transaction hash: ${txHash}. Message ID: ${messageId}`)

// Variant 2: Transfer via CCIP using the provided supported token for fee payment
const { txHash, messageId } = await client.transferTokens({
  client: walletClient,
  routerAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  tokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
  amount: 1000000000000000000n,
  destinationAccount: '0x1234567890abcdef1234567890abcdef12345678',
  destinationChainSelector: '1234',
  feeTokenAddress: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdef',
})

API Reference

Client

An object containing methods for cross-chain transfer management. Refer to Client methods for more information about each method.

export interface Client {
  approveRouter(options: {
    client: Viem.WalletClient
    routerAddress: Viem.Address
    tokenAddress: Viem.Address
    amount: bigint
    waitForReceipt?: boolean
    writeContractParameters?: Partial<{
      gas: bigint
      gasPrice: bigint
      nonce: number
    }>
    waitForTransactionReceiptParameters?: Partial<{
      confirmations: number
      pollingInterval: number
    }>
  }): Promise<{ txHash: Viem.Hash; txReceipt?: Viem.TransactionReceipt }>

  getAllowance(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    tokenAddress: Viem.Address
    account: Viem.Address
  }): Promise<bigint>

  getOnRampAddress(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    destinationChainSelector: string
  }): Promise<Viem.Address>

  getSupportedFeeTokens(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    destinationChainSelector: string
  }): Promise<Viem.Address[]>

  getLaneRateRefillLimits(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    destinationChainSelector: string
  }): Promise<RateLimiterState>

  getTokenRateLimitByLane(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    supportedTokenAddress: Viem.Address
    destinationChainSelector: string
  }): Promise<RateLimiterState>

  getFee(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    destinationAccount: Viem.Address
    destinationChainSelector: string
    amount: bigint
    tokenAddress: Viem.Address
    feeTokenAddress?: Viem.Address
    message?: string
  }): Promise<bigint>

  getTokenAdminRegistry(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    destinationChainSelector: string
    tokenAddress: Viem.Address
  }): Promise<Viem.Address>

  isTokenSupported(options: {
    client: Viem.Client
    routerAddress: Viem.Address
    destinationChainSelector: string
    tokenAddress: Viem.Address
  }): Promise<boolean>

  transferTokens(options: {
    client: Viem.WalletClient
    routerAddress: Viem.Address
    destinationChainSelector: string
    amount: bigint
    destinationAccount: Viem.Address
    tokenAddress: Viem.Address
    feeTokenAddress?: Viem.Address
    message?: string
    writeContractParameters?: Partial<{
      gas: bigint
      gasPrice: bigint
      nonce: number
    }>
    waitForTransactionReceiptParameters?: Partial<{
      confirmations: number
      pollingInterval: number
    }>
  }): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }>

  sendCCIPMessage(options: {
    client: Viem.WalletClient
    routerAddress: Viem.Address
    destinationChainSelector: string
    destinationAccount: Viem.Address
    feeTokenAddress?: Viem.Address
    message: string
    writeContractParameters?: Partial<{
      gas: bigint
      gasPrice: bigint
      nonce: number
    }>
    waitForTransactionReceiptParameters?: Partial<{
      confirmations: number
      pollingInterval: number
    }>
  }): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }>

  getTransferStatus(options: {
    client: Viem.Client
    destinationRouterAddress: Viem.Address
    sourceChainSelector: string
    messageId: Viem.Hash
    fromBlockNumber?: bigint
  }): Promise<TransferStatus | null>

  getTransactionReceipt(options: { client: Viem.Client; hash: Viem.Hash }): Promise<Viem.TransactionReceipt>
}

createClient

createClient(): Client

Creates a Client object.

RateLimiterState

Represents the state of a rate limiter using a token bucket algorithm.

interface RateLimiterState {
  // Current number of tokens that are in the bucket. This represents the available capacity for requests.
  tokens: bigint
  // Timestamp in seconds of the last token refill, allows tracking token consumption over time. This is designed to be accurate for over 100 years.
  lastUpdated: number
  // Indicates whether the rate limiting feature is enabled or disabled.
  isEnabled: boolean
  // Maximum number of tokens that can be in the bucket, representing the total capacity of the limiter.
  capacity: bigint
  // The rate at which tokens are refilled in the bucket, measuer in tokes per second.
  rate: bigint
}

DynamicConfig

Configuration settings for dynamic aspects of cross-chain transfers.

The DynamicConfig type defines the structure of an object that holds various dynamic configuration parameters for cross-chain transactions. These settings are used to control the behavior and limits of transfers, such as gas calculations, data availability, and message size constraints.

type DynamicConfig = {
  // The address of the router responsible for handling the cross-chain transfers. This address is used to route the transaction through the correct path.
  router: Viem.Address
  // The maximum number of tokens that can be included in a single message. This parameter limits the token batch size in cross-chain transfers to prevent overly large transactions.
  maxNumberOfTokensPerMsg: number
  // The amount of gas required per byte of payload on the destination chain. This parameter is used to calculate the total gas needed based on the size of the payload being transferred.
  destGasPerPayloadByte: number
  // The additional gas required on the destination chain to account for data availability overhead. This value is used to ensure that enough gas is allocated for the availability of the data being transferred.
  destDataAvailabilityOverheadGas: number
  // The overhead in gas that is added to the destination chain to account for base transaction costs. This value helps ensure that the transaction has enough gas to cover additional overhead on the destination chain.
  destGasOverhead: number
  // The gas cost per byte of data availability on the destination chain. This parameter contributes to the overall gas calculation for data availability during the transfer.
  destGasPerDataAvailabilityByte: number
  // The multiplier in basis points (bps) applied to the data availability gas cost. This value is used to adjust the cost of data availability by applying a scaling factor.
  destDataAvailabilityMultiplierBps: number
  // The address of the price registry used to obtain pricing information for gas and other costs during the transfer. This registry helps ensure that the correct prices are applied to the transaction.
  priceRegistry: Viem.Address
  // The maximum number of data bytes that can be included in a single message. This parameter limits the size of the data payload to prevent excessive data in one transfer.
  maxDataBytes: number
  // The maximum gas limit that can be applied to a single message. This parameter ensures that the transaction does not exceed a certain gas threshold, preventing overly costly operations.
  maxPerMsgGasLimit: number
}

OffRamp

Represents the off-ramp configuration for a cross-chain transfer.

type OffRamp = {
  // The address of the off-ramp contract on the destination blockchain.
  offRamp: Viem.Address
  // The selector for the source chain.
  sourceChainSelector: bigint
}

TransferStatus

Represents the transaction status of a cross-chain transfer.

enum TransferStatus {
  Untouched = 0,
  InProgress = 1,
  Success = 2,
  Failure = 3,
}

Client Methods

approveRouter

Approve the CCIP router to spend tokens for transfers and fees on behalf of the user. Returns the transaction hash and optionally the transaction receipt.

Q: Why an approval is needed? A: For a cross-chain transfer the CCIP router contract needs to withdraw the tokens amount and fee from the sender's wallet. The user should review and approve the parameters of the transfer before executing it.

approveRouter(options: {
  client: Viem.WalletClient
  routerAddress: Viem.Address
  tokenAddress: Viem.Address
  amount: bigint
  waitForReceipt?: boolean
  writeContractParameters?: Partial<{
    gas: bigint
    gasPrice: bigint
    nonce: number
  }>
  waitForTransactionReceiptParameters?: Partial<{
    confirmations: number
    pollingInterval: number
  }>
}): Promise<{ txHash: Viem.Hash; txReceipt?: Viem.TransactionReceipt }>

getAllowance

Retrieves the allowance of a specified account for a cross-chain transfer.

getAllowance(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  tokenAddress: Viem.Address
  account: Viem.Address
}): Promise<bigint>

getOnRampAddress

Retrieves the onRamp contract address from a router contract.

getOnRampAddress(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  destinationChainSelector: string
}): Promise<Viem.Address>

getSupportedFeeTokens

Gets a list of supported tokens which can be used to pay the fees for the cross-chain transfer.

getSupportedFeeTokens(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  destinationChainSelector: string
}): Promise<Viem.Address[]>

getLaneRateRefillLimits

Retrieves the aggregated rate refill limits for the specified lane. Returns a promise that resolves to RateLimiterState object.

getLaneRateRefillLimits(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  destinationChainSelector: string
}): Promise<RateLimiterState>

getTokenRateLimitByLane

Retrieves the rate refill limits for the specified token. Returns a promise that resolves to RateLimiterState object.

getTokenRateLimitByLane(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  supportedTokenAddress: Viem.Address
  destinationChainSelector: string
}): Promise<RateLimiterState>

getFee

Gets the fee required for the cross-chain transfer.

getFee(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  destinationAccount: Viem.Address
  destinationChainSelector: string
  amount: bigint
  tokenAddress: Viem.Address
  feeTokenAddress?: Viem.Address
  message?: string
}): Promise<bigint>

Get the fee required for the cross-chain transfer and/or sending cross-chain message.

getTokenAdminRegistry

Retrieve the token admin registry contract address from an onRamp contract

getTokenAdminRegistry(options: {
  client: Viem.Client
  routerAddress: Viem.Address
  destinationChainSelector: string
  tokenAddress: Viem.Address
}): Promise<Viem.Address>

isTokenSupported

Check if the token is supported on the destination chain. The call, and all configs are made on the source chain.

isTokenSupported(options: {
  client: Viem.Client
  routerAddress: Viem.Address // router address on source chain.
  destinationChainSelector: string
  tokenAddress: Viem.Address // token address on source chain.
}): Promise<boolean>

transferTokens

Initiates the token transfer and returns the transaction hash, cross-chain transfer message ID and transaction receipt.

transferTokens(options: {
  client: Viem.WalletClient
  routerAddress: Viem.Address
  destinationChainSelector: string
  amount: bigint
  destinationAccount: Viem.Address
  tokenAddress: Viem.Address
  feeTokenAddress?: Viem.Address
  message?: string
  writeContractParameters?: Partial<{
    gas: bigint
    gasPrice: bigint
    nonce: number
  }>
  waitForTransactionReceiptParameters?: Partial<{
    confirmations: number
    pollingInterval: number
  }>
}): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }>

Initiates the token transfer and returns the transaction hash, cross-chain transfer message ID and transaction receipt.

sendCCIPMessage

Send arbitrary message through CCIP

sendCCIPMessage(options: {
  client: Viem.WalletClient
  routerAddress: Viem.Address
  destinationChainSelector: string
  destinationAccount: Viem.Address
  feeTokenAddress?: Viem.Address
  message: string
  writeContractParameters?: Partial<{
  gas: bigint
  gasPrice: bigint
  nonce: number
}>
waitForTransactionReceiptParameters?: Partial<{
  confirmations: number
  pollingInterval: number
}>
}): Promise<{ txHash: Viem.Hash; messageId: Viem.Hash; txReceipt: Viem.TransactionReceipt }>

getTransferStatus

Retrieves the status of a cross-chain transfer based on the message ID. Returns a promise that resolves to TransferStatus object or null

getTransferStatus(options: {
  client: Viem.Client
  destinationRouterAddress: Viem.Address
  sourceChainSelector: string
  messageId: Viem.Hash
  fromBlockNumber?: bigint
}): Promise<TransferStatus | null>

getTransactionReceipt

Retrieves the transaction receipt based on the transaction hash. Returns a promise that resolves to TransactionReceipt object.

getTransactionReceipt(options: { client: Viem.Client; hash: Viem.Hash }): Promise<Viem.TransactionReceipt>

Development

Build

pnpm i -w
pnpm build-ccip-js

Running tests

pnpm i -w
anvil
pnpm test

Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/my-feature).
  3. Commit your changes (git commit -m 'Add some feature').
  4. Push to the branch (git push origin feature/my-feature).
  5. Open a pull request.

License

CCIP-JS is available under the MIT license.