Learn how to add cross-chain USDC transfers to your Telegram bot using Circle’s CCTP V2 smart contracts.

In 2024, Telegram became a programmable platform, transforming how people use it for services and transactions. With over 900 million monthly active users and 400 million interacting with bots every month, Telegram has quickly become a hotspot for digital commerce.
One key feature gaining traction is Telegram’s Bot Payment API, which allows users to sell digital goods and services directly within chats. By integrating traditional payment providers, the API streamlines in-chat transactions for commerce use cases.
But when it comes to peer-to-peer (P2P) payments or cross-border transfers, Telegram’s built-in options still fall short, especially for users looking to move value quickly and securely, without being held back by borders or incompatible systems.
That’s where Circle CCTP (Cross-Chain Transfer Protocol) V2 comes in. By enabling fast, secure USDC transfers across blockchains, CCTP V2 enhances Telegram’s payments experience. Combined with Circle Wallets and the Bot Payment API, developers can now build bots that support cross-chain USDC transfers directly in Telegram — no browser, wallet extension, or bridging UI required.
In this guide, you'll learn how to build a Telegram bot that performs a cross-chain transaction with USDC from Ethereum to Avalanche using CCTP V2 and Circle Wallets.

Imagine a scenario where Alice, a freelancer, needs to send USDC to Bob, her client, who lives in a different country. Alice holds USDC on Ethereum, but Bob prefers to receive it on Avalanche because of lower transaction fees.
Without a solution like CCTP V2, Alice would face friction when trying to send USDC to Bob, as they each use wallets on different blockchains. This typically requires manual bridging or coordination around which chain to use. These mismatches add unnecessary steps for peer-to-peer transfers. CCTP V2 solves this by enabling fast, chain-agnostic transfers - so funds can move between chains without users needing to align on the same wallet infrastructure.
With CCTP V2 integrated into a Telegram bot, Alice can easily initiate a transfer directly from her Ethereum wallet, and Bob will receive the USDC in his Avalanche wallet. The transfer happens seamlessly within Telegram, cutting down on complexities and transaction delays.
In this tutorial, you will:
- Set up a Telegram bot that integrates with CCTP V2 and Circle Wallets
- Implement a USDC transfer from Ethereum to Avalanche
- Manage wallet creation, funding, and execution of transfers using Telegram commands
Setting Up Your Environment
1. Create a Circle Developer Account
To interact with Circle Wallets, you'll need a Circle API Key and Entity Secret.
- Sign up at Circle Developer Console
- Obtain your API Key
- Generate your Entity Secret
2. Fork the Replit Template
3. Configure Your Secrets in Replit
Go to your forked Replit and head to the Secrets Manager. Add the following environment variables:
CIRCLE_API_KEY = your_circle_api_key_here
CIRCLE_ENTITY_SECRET = your_circle_entity_secret_here
TELEGRAM_BOT_TOKEN = your_telegram_bot_token_here
4. Create a Telegram Bot Token via BotFather
- Open Telegram and start a chat with BotFather
- Use the command /newbot and follow the prompts
- Choose a bot name (make sure it ends with _bot)
Copy the Bot Token provided and paste it into your Replit secrets
5. Run Your Bot
Now that your bot is configured, click the “Run” button above your Replit environment.
- Head over to your Telegram bot link and type /start to see the available commands.
How Circle Wallets Work
Circle Wallets allow developers to create and manage wallets programmatically, enabling USDC transfers across different blockchain networks.
1. Initiating the Circle Wallets SDK
The first step is to initialize the Circle Wallets SDK. This connects your application to Circle’s infrastructure using your API key and Entity Secret. Once initialized, the SDK lets you create wallets and interact with them via the SDK.
const {
initiateDeveloperControlledWalletsClient,
} = require("@circle-fin/developer-controlled-wallets");
this.walletSDK = initiateDeveloperControlledWalletsClient({
apiKey: config.circle.apiKey,
entitySecret: config.circle.entitySecret,
});
entitySecret is encrypted before being passed into SDK calls. Each sensitive operation (e.g., signing) will require an encrypted version of it, called entitySecretCiphertext.
2. Creating a Wallet Set
A wallet set is a collection of wallets grouped under a single label. This step allows you to organize and manage wallets within your application. You’ll need to create a wallet set before generating individual wallets.
const walletSetResponse = await this.walletSDK.createWalletSet({
name: "WalletSet 1",
});
3. Creating Circle Wallet
With the wallet set in place, you can now create individual wallets on supported blockchains like Ethereum, Avalanche, and more.
const walletData = await this.walletSDK.createWallets({
idempotencyKey: uuidv4(),
blockchains: [currentNetwork.name],
accountType: accountType,
walletSetId: walletSetResponse.data?.walletSet?.id ?? "",
});
How CCTP V2 Works
1. Approving USDC Spending
Before the transfer can begin, Alice must approve the spending of USDC from her wallet. This step is required because the bot interacts with a smart contract that needs permission to move Alice’s tokens.
The approve function allows the token messenger contract to spend a specified amount of USDC on her behalf.
const entitySecretCiphertext = await this.walletSDK.generateEntitySecretCiphertext();
const approveTxResponse = await this.walletSDK.createContractExecutionTransaction({
walletId: walletId,
entitySecretCiphertext: entitySecretCiphertext,
contractAddress: sourceConfig.usdc,
abiFunctionSignature: "approve(address,uint256)",
abiParameters: [sourceConfig.tokenMessenger, usdcAmount.toString()],
fee: {
type: "level",
config: {
feeLevel: "LOW",
},
},
});
2. Burning USDC on Ethereum
Once the approval is granted, the bot proceeds to burn the USDC on Ethereum Sepolia using the depositForBurn function. This operation initiates the cross-chain transfer process.
Burning USDC on the source chain signals Circle to prepare for minting the same amount on the destination chain - in this case, Avalanche Fuji.
const burnEntitySecretCiphertext = await this.walletSDK.generateEntitySecretCiphertext();
const burnTxResponse = await this.walletSDK.createContractExecutionTransaction({
walletId: walletId,
entitySecretCiphertext: burnEntitySecretCiphertext,
contractAddress: sourceConfig.tokenMessenger,
abiFunctionSignature:
"depositForBurn(uint256,uint32,bytes32,address,bytes32,uint256,uint32)",
abiParameters: [
usdcAmount.toString(),
CCTP.domains[destinationNetwork].toString(),
mintRecipientAddressInBytes32,
sourceConfig.usdc,
"0x0000000000000000000000000000000000000000000000000000000000000000",
maxFee.toString(),
"1000",\
],
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
3. Waiting for Attestation
After the burn transaction is submitted, the bot waits for Circle’s attestation service to validate the event.
This attestation proves that the USDC was burned on Ethereum and includes the necessary payload to mint it on Avalanche. The bot polls the attestation endpoint until the message status becomes complete.
async waitForAttestation(srcDomainId, transactionHash) {
const maxAttempts = 30; // 5 minutes total with 10-second intervals
let attempts = 0;
try {
while (attempts < maxAttempts) {
attempts++;
const url = `https://iris-api-sandbox.circle.com/v2/messages/${srcDomainId}?transactionHash=${transactionHash}`;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Bearer ${config.circle.apiKey}`,
"Content-Type": "application/json",
},
});
if (response.data?.messages?.[0]?.status === "complete") {
const { message, attestation } = response.data?.messages[0];
return { message, attestation };
}
} catch (error) {
if (error.response?.status === 404) {
console.log(
`Attempt ${attempts}/${maxAttempts}: Attestation not ready yet`,
);
} else {
throw error;
}
}
await new Promise((resolve) => setTimeout(resolve, 10000));
}
throw new Error("Timeout waiting for attestation");
} catch (error) {
console.error(`Failed to get attestation: ${error}`);
throw error;
}
}
4. Minting USDC on Avalanche
Once the attestation is received, the bot submits a receiveMessage transaction on Avalanche Fuji. This triggers the minting of the corresponding USDC on the destination chain. This is where Bob’s Avalanche wallet will receive the transferred USDC, completing the cross-chain transfer.
const receiveEntitySecretCiphertext =
await this.walletSDK.generateEntitySecretCiphertext();
const receiveTxResponse =
await this.walletSDK.createContractExecutionTransaction({
walletId: destinationWalletId,
entitySecretCiphertext: receiveEntitySecretCiphertext,
contractAddress: destinationConfig.messageTransmitter,
abiFunctionSignature: "receiveMessage(bytes,bytes)",
abiParameters: [attestation.message, attestation.attestation],
fee: {
type: "level",
config: {
feeLevel: "MEDIUM",
},
},
});
Wrapping Up
This tutorial illustrates just one implementation of CCTP V2 and Circle Wallets, enabling seamless cross-chain USDC transfers directly within Telegram. Through this combination, you can provide a streamlined experience for users, making peer-to-peer payments and cross-border transactions faster, cheaper, and more efficient. As Telegram’s Payment API evolves, it can serve as an inspiration to further enhance this experience, enabling native, in-chat USDC transfers and further simplifying the process.
Try It Yourself
Get started with Circle’s Developer Services by creating a Developer Account and check out the special offer below.
We’re excited to offer $100 in credits towards Circle's developer services - sign up today to automatically receive yours. That’s enough for about one month of 2,000 active wallets, 200,000 Smart Contract Platform API calls, or $95 in sponsored network fees.
- Check out our CCTP docs to get started today!
- Fork the Template : Telegram Bot on Replit
- Join the Community : Circle Developer Discord