> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/circlefin/evm-cctp-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Transfer USDC cross-chain using CCTP protocol

## What You'll Build

In this quickstart, you'll execute a cross-chain USDC transfer from Ethereum Sepolia testnet to Avalanche Fuji testnet using Circle's Cross-Chain Transfer Protocol (CCTP). You'll learn how to interact with the CCTP smart contracts to burn USDC on the source chain and mint it on the destination chain.

## Prerequisites

Before you begin, ensure you have:

* **Node.js** installed on your system
* **USDC tokens** on Ethereum Sepolia testnet
* **RPC endpoints** for both Ethereum Sepolia and Avalanche Fuji
* **Private keys** for both source and destination addresses
* **web3.js** library installed (`npm install web3`)

## Contract Addresses

The CCTP contracts are deployed on testnet at the following addresses:

* **Ethereum Sepolia**
  * TokenMessenger: `0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5`
  * USDC: `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238`
* **Avalanche Fuji**
  * MessageTransmitter: `0xa9fb1b3009dcb79e2fe346c16a604b8fa8ae0a79`

## Setup Environment

Create a `.env` file with the following variables:

```bash theme={null}
ETH_TESTNET_RPC=<YOUR_ETH_SEPOLIA_RPC_URL>
AVAX_TESTNET_RPC=<YOUR_AVAX_FUJI_RPC_URL>
ETH_PRIVATE_KEY=<YOUR_SOURCE_ADDRESS_PRIVATE_KEY>
AVAX_PRIVATE_KEY=<YOUR_RECIPIENT_ADDRESS_PRIVATE_KEY>
RECIPIENT_ADDRESS=<YOUR_RECIPIENT_ADDRESS_ON_AVAX>
AMOUNT=<AMOUNT_TO_TRANSFER_IN_SMALLEST_UNIT>
```

<Steps>
  <Step title="Approve USDC">
    First, approve the TokenMessenger contract to spend your USDC tokens on Ethereum Sepolia.

    ```javascript theme={null}
    const Web3 = require('web3');
    const web3 = new Web3(process.env.ETH_TESTNET_RPC);

    const USDC_ETH_CONTRACT_ADDRESS = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238';
    const ETH_TOKEN_MESSENGER_CONTRACT_ADDRESS = '0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5';

    // USDC contract ABI (approve function)
    const usdcAbi = [{
      "inputs": [{"name": "spender", "type": "address"}, {"name": "amount", "type": "uint256"}],
      "name": "approve",
      "outputs": [{"name": "", "type": "bool"}],
      "type": "function"
    }];

    const usdcEthContract = new web3.eth.Contract(usdcAbi, USDC_ETH_CONTRACT_ADDRESS);
    const amount = process.env.AMOUNT;

    // Estimate gas
    const approveTxGas = await usdcEthContract.methods
      .approve(ETH_TOKEN_MESSENGER_CONTRACT_ADDRESS, amount)
      .estimateGas();

    // Send approval transaction
    const approveTx = await usdcEthContract.methods
      .approve(ETH_TOKEN_MESSENGER_CONTRACT_ADDRESS, amount)
      .send({ gas: approveTxGas });

    console.log('Approval transaction:', approveTx.transactionHash);
    ```
  </Step>

  <Step title="Call depositForBurn">
    Initiate the cross-chain transfer by calling `depositForBurn` on the TokenMessenger contract. This burns USDC on Ethereum and emits a message for the destination chain.

    ```javascript theme={null}
    const AVAX_DESTINATION_DOMAIN = 1; // Avalanche domain ID

    // Convert recipient address to bytes32 format
    const destinationAddressInBytes32 = web3.eth.abi.encodeParameter(
      'address',
      process.env.RECIPIENT_ADDRESS
    );

    // TokenMessenger contract ABI (depositForBurn function)
    const tokenMessengerAbi = [{
      "inputs": [
        {"name": "amount", "type": "uint256"},
        {"name": "destinationDomain", "type": "uint32"},
        {"name": "mintRecipient", "type": "bytes32"},
        {"name": "burnToken", "type": "address"}
      ],
      "name": "depositForBurn",
      "outputs": [{"name": "nonce", "type": "uint64"}],
      "type": "function"
    }];

    const ethTokenMessengerContract = new web3.eth.Contract(
      tokenMessengerAbi,
      ETH_TOKEN_MESSENGER_CONTRACT_ADDRESS
    );

    // Execute depositForBurn
    const burnTx = await ethTokenMessengerContract.methods
      .depositForBurn(
        amount,
        AVAX_DESTINATION_DOMAIN,
        destinationAddressInBytes32,
        USDC_ETH_CONTRACT_ADDRESS
      )
      .send();

    console.log('Burn transaction:', burnTx.transactionHash);
    ```
  </Step>

  <Step title="Retrieve Message Bytes from Logs">
    Extract the `messageBytes` from the `MessageSent` event emitted during the burn transaction. Hash these bytes to prepare for attestation.

    ```javascript theme={null}
    // Get transaction receipt
    const transactionReceipt = await web3.eth.getTransactionReceipt(
      burnTx.transactionHash
    );

    // Find the MessageSent event
    const eventTopic = web3.utils.keccak256('MessageSent(bytes)');
    const log = transactionReceipt.logs.find((l) => l.topics[0] === eventTopic);

    // Decode the message bytes from event data
    const messageBytes = web3.eth.abi.decodeParameters(['bytes'], log.data)[0];

    // Hash the message bytes
    const messageHash = web3.utils.keccak256(messageBytes);

    console.log('Message hash:', messageHash);
    ```
  </Step>

  <Step title="Poll Attestation Service">
    Query Circle's attestation service using the message hash. The service will return a signature once the attestation is complete.

    <Note>
      The attestation service is rate-limited. Please limit requests to less than 1 per second.
    </Note>

    ```javascript theme={null}
    let attestationResponse = { status: 'pending' };

    while (attestationResponse.status !== 'complete') {
      const response = await fetch(
        `https://iris-api-sandbox.circle.com/attestations/${messageHash}`
      );
      attestationResponse = await response.json();
      
      if (attestationResponse.status === 'complete') {
        console.log('Attestation received!');
        break;
      }
      
      // Wait 2 seconds before next poll
      await new Promise(r => setTimeout(r, 2000));
    }

    const attestationSignature = attestationResponse.attestation;
    ```
  </Step>

  <Step title="Call receiveMessage on Destination Chain">
    Finally, call `receiveMessage` on the MessageTransmitter contract on Avalanche Fuji to mint USDC on the destination chain.

    ```javascript theme={null}
    const avaxWeb3 = new Web3(process.env.AVAX_TESTNET_RPC);
    const AVAX_MESSAGE_TRANSMITTER_CONTRACT_ADDRESS = 
      '0xa9fb1b3009dcb79e2fe346c16a604b8fa8ae0a79';

    // MessageTransmitter contract ABI (receiveMessage function)
    const messageTransmitterAbi = [{
      "inputs": [
        {"name": "message", "type": "bytes"},
        {"name": "attestation", "type": "bytes"}
      ],
      "name": "receiveMessage",
      "outputs": [{"name": "success", "type": "bool"}],
      "type": "function"
    }];

    const avaxMessageTransmitterContract = new avaxWeb3.eth.Contract(
      messageTransmitterAbi,
      AVAX_MESSAGE_TRANSMITTER_CONTRACT_ADDRESS
    );

    // Execute receiveMessage with message bytes and signature
    const receiveTx = await avaxMessageTransmitterContract.methods
      .receiveMessage(messageBytes, attestationSignature)
      .send();

    console.log('Receive transaction:', receiveTx.transactionHash);
    console.log('USDC successfully transferred to', process.env.RECIPIENT_ADDRESS);
    ```
  </Step>
</Steps>

## Next Steps

Congratulations! You've successfully transferred USDC cross-chain using CCTP. Here are some things to explore next:

* Try transferring between different chain pairs
* Explore the contract interfaces in detail
* Learn about the security model and attestation process
* Integrate CCTP into your own applications
