Skip to main content
Transfer USDC between Sui Testnet and Arc Testnet using the CCTP burn-attest-mint flow.
On Sui, CCTP V2 burn and mint are multi-step programmable transactions. Each flow calls TokenMessengerMinterV2, MessageTransmitterV2, and the StablecoinHandler in one transaction. Package and State object IDs differ from CCTP V1 on Sui.These examples treat Sui USDC as a SIP-58 address balance: burn redeems with 0x2::coin::redeem_funds, and after mint a follow-up 0x2::coin::send_funds folds the public_transfer’d Coin into the recipient address balance. Omit these two transactions if you’re not using SIP-58 balances.
Pick the tab that matches the direction of your transfer.
This quickstart demonstrates how to transfer USDC from Sui Testnet to Arc Testnet using CCTP. You use the @mysten/sui SDK to burn USDC on Sui, and viem to mint USDC on Arc Testnet. When you finish, you will have executed a full burn-attest-mint flow.You should be comfortable using a terminal and Node.js. Familiarity with Sui programmable transactions and basic EVM usage helps you follow and adapt the script. Examples use Arc Testnet as the destination, but you can use any supported EVM blockchain.
This script uses SuiGrpcClient, which requires a Sui gRPC endpoint (not JSON-RPC). The default fullnode URL below serves gRPC on port 443.

Prerequisites

Before you begin this tutorial, ensure you have:
  • Installed Node.js v22.6+
  • Prepared a Sui Testnet wallet with the private key available (suiprivkey1…)
    • Funded your account with testnet SUI for transaction fees
    • Funded your account with Sui Testnet USDC from the Circle Faucet as an address balance (SIP-58). If USDC is only held as Coin objects, deposit it first with 0x2::coin::send_funds.
  • Prepared an EVM wallet with the private key available for Arc Testnet
    • Added the Arc Testnet network to your wallet (network details)
    • Funded your wallet with Arc Testnet ETH for gas fees

Step 1: Set up the project

This step shows you how to prepare your project and environment.

1.1. Set up your development environment

Create a new directory, initialize and set up a new Node.js project, and install the required dependencies:
Shell

1.2. Initialize and configure the project

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. Configure environment variables

Open .env in your editor and add:
  • EVM_PRIVATE_KEY is the private key for the EVM wallet used to receive USDC on Arc Testnet.
  • SUI_PRIVATE_KEY is the Sui Testnet private key (suiprivkey1…) used to sign the burn transaction.
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 Sui and Arc Testnet parameters, then configure the wallet clients for each chain.

2.1. Define configuration constants

The script predefines the Sui CCTP V2 package and State object IDs, the Arc MessageTransmitterV2 address, the transfer amount, and the CCTP domain IDs.
TypeScript
SUI_DOMAIN is the source domain used when requesting the attestation. The destination domain for Arc Testnet is 26. Sui Testnet USDC uses 6 decimals, so 1_000_000 represents 1 USDC.

2.2. Set up wallet clients

The EVM clients connect to Arc Testnet with viem. The Sui client uses SuiGrpcClient on Testnet and signs transactions with an Ed25519 keypair decoded from SUI_PRIVATE_KEY.
TypeScript

2.3. Format the Arc recipient for Sui

Sui deposit_for_burn accepts the mint recipient as a 32-byte address. An EVM address is converted by left-padding it with zeros.
TypeScript

Step 3: Implement the transfer logic

This step implements the core transfer logic: burn on Sui, poll for an attestation, then mint on Arc. The Sui burn is a single programmable transaction: redeem from the SIP-58 address balance, then the CCTP Move calls.

3.1. Burn USDC on Sui

Build a programmable transaction that:
  1. Calls 0x2::coin::redeem_funds to unwrap the burn amount from the sender’s address balance into a Coin<USDC>
  2. Calls deposit_for_burn on TokenMessengerMinterV2
  3. Calls handler::burn on the StablecoinHandler
  4. Calls complete_burn to emit the CCTP message
You specify the following parameters for deposit_for_burn:
  • Burn amount: The amount of USDC to burn in Sui subunits (6 decimals)
  • Destination domain: The target blockchain for minting USDC (26 for Arc Testnet)
  • Mint recipient: The EVM wallet address that receives minted USDC on Arc, left-padded to 32 bytes
  • Destination caller: The zero address, allowing any caller to submit the receive transaction on Arc
  • Max fee: The maximum fee allowed for the transfer
  • Finality threshold: 2000 for a Standard Transfer
TypeScript

3.2. Retrieve attestation

Retrieve the attestation required to complete the CCTP transfer by calling Circle’s attestation API.
  • Call Circle’s GET /v2/messages API endpoint to retrieve the attestation.
  • Pass SUI_DOMAIN for the sourceDomain path parameter, using the CCTP domain for Sui (8).
  • Pass the Sui transaction digest returned by burnUSDC as transactionHash. Sui digests are base58 and must not include a 0x prefix.
TypeScript

3.3. Mint USDC on Arc

Call the receiveMessage function from the MessageTransmitterV2 contract deployed on Arc Testnet.
  • Pass the signed attestation and message bytes as parameters.
  • The contract verifies the attestation and mints USDC to the recipient encoded in the CCTP message.
TypeScript

Step 4: Full script

Create an index.ts file in your project directory and paste the full script following.
index.ts

Step 5: Test the script

Run the following command to execute the script:
Shell
When the transfer finishes, the console logs a completion message and the relevant transaction hashes. Successful output looks similar to the following:
Shell
Attestation polling can take several minutes depending on network conditions and the finality threshold you chose. The script retries every 2 seconds with no timeout, so allow the process to continue while Iris prepares the attestation.
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.