Skip to main content
This guide demonstrates how to transfer USDC from Solana Devnet to Arc Testnet using CCTP. You use the Solana Kit library to interact with Solana CCTP programs, and viem to mint USDC on Arc Testnet.
Use Bridge Kit to simplify crosschain transfers with CCTP.This quickstart shows how to transfer USDC from to using a manual CCTP integration. The example is for learning or for developers who need a manual integration.To streamline this, use Bridge Kit to transfer USDC in just a few lines of code.

Prerequisites

Before you begin, ensure that you’ve:
  • Installed Node.js v22.6+
  • Prepared a Solana wallet and have the private key array available
  • Funded your Solana wallet with the following testnet tokens:
  • Prepared an EVM testnet wallet with the private key available
  • Funded your EVM wallet with Arc Testnet USDC from the Circle Faucet if you choose the direct mint path below, because the destination wallet must pay gas to call receiveMessage

Step 1. Set up the project

1.1. Create the project and install dependencies

1.2. Configure TypeScript (optional)

This step is optional. It helps prevent missing types in your IDE or editor.
Create a tsconfig.json file:
Then, update the tsconfig.json file:

1.3. Set environment variables

Open .env in your editor and add:
  • SOLANA_PRIVATE_KEY is the private key array for the Solana Devnet wallet that signs the source-chain burn transaction.
  • EVM_PRIVATE_KEY is used to derive the Arc recipient address. The direct-mint path also uses this key to submit the destination mint on Arc.
Open .env in your editor rather than writing values with shell commands, and add .env to your .gitignore. This prevents credentials from leaking into your shell history or version control.
The npm run start command loads variables from .env using Node.js native env-file support.
This example uses one or more private keys for local testing. In production, use a secure key management solution and never expose or share private keys.

Step 2: Configure the script

Define the configuration constants for interacting with Solana and Arc Testnet.

2.1. Setup chains and wallets

The script predefines the program addresses, transfer amount, and other parameters:
TypeScript

Step 3: Implement the transfer logic

The following sections outline the core transfer logic from Solana to Arc. For simplicity, this quickstart uses the same Arc wallet as the recipient and, in the direct-mint path, the wallet that submits receiveMessage. In production, these can be different addresses. In the two examples provided, the path diverges at the Solana burn instruction:
  • Direct mint uses deposit_for_burn, then retrieves an attestation and calls receiveMessage on Arc.
  • Forwarding Service uses deposit_for_burn_with_hook, then lets Circle handle the destination-side mint on Arc.

3.1. Get forwarding fees and calculate the burn amount

Before you burn USDC with the Forwarding Service, query the CCTP fee endpoint with forward=true. The forwarding fee is dynamic, so fetch it immediately before the transfer. The returned maxFee must cover both the CCTP protocol fee and the forwarding fee.
TypeScript

3.2. Burn USDC with the forwarding service hook

Use deposit_for_burn_with_hook on Solana. The forwarding hook data tells Circle to handle the destination-side receiveMessage call on Arc.
TypeScript

3.3. Verify the forwarded mint

After the burn is confirmed, poll the Iris API until it returns a forwardTxHash. That hash is the Arc destination mint transaction submitted by Circle. In the forwarding path, forwardTxHash is the completion signal for the destination-side mint. You do not need to retrieve an attestation and call receiveMessage yourself.
TypeScript

Step 4: Complete script

Create a index.ts file in your project directory and populate it with the complete code below for the path you want to test.
index.ts

Step 5: Test the script

Run the following command to execute the script:
Shell
Once the script runs and the transfer is finalized, a confirmation message is logged in the console.
Rate limit: The attestation service rate limit is 40 requests per second. If you exceed this limit, the service blocks all API requests for the next five minutes and returns an HTTP 429 (Too Many Requests) response.