We refreshed our doc site!

Bookmarked links may have changed

Read release notes

Stablecoins

Solana Programs

CCTP Programs on the Solana blockchain

Overview

Solana CCTP programs are written in Rust and leverage the Anchor framework. The Solana CCTP protocol implementation is split into two programs: MessageTransmitter and TokenMessengerMinter. TokenMessengerMinter encapsulates the functionality of both TokenMessenger and TokenMinter contracts on EVM chains. To ensure alignment with EVM contracts' logic and state, and to facilitate future upgrades and maintenance, the code and state of Solana programs reflect the EVM counterparts as closely as possible.

Mainnet Program Addresses

ProgramDomainAddress
MessageTransmitter5CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd
TokenMessengerMinter5CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3

Devnet Program Addresses

ProgramDomainAddress
MessageTransmitter5CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd
TokenMessengerMinter5CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3

The Solana CCTP source code is available at https://github.com/circlefin/solana-cctp-contracts/. The interface below serves as a reference for permissionless messaging functions exposed by the programs.

Interface

The interface below serves as a reference for permissionless messaging functions exposed by the TokenMessengerMinter and MessageTransmitter programs. The full IDLs can be found here and here.

Please see the instruction rust files or quick-start for PDA information.

TokenMessengerMinter

depositForBurn

Deposits and burns tokens from sender to be minted on destination domain. Minted tokens will be transferred to mintRecipient.

Parameters

FieldTypeDescription
amountu64Amount of tokens to deposit and burn.
destinationDomainu32Destination domain identifier.
mintRecipientPubkeyPublic Key of token account mint recipient on destination domain. Address should be the 32 byte version of the hex address in base58. See Additional Notes on mintRecipient section for more information.

depositForBurnWithCaller

The same as depositForBurn but with an additional parameter, destinationCaller. This parameter specifies which address has permission to call receiveMessage on the destination domain for the message.

Parameters

FieldTypeDescription
amountu64Amount of tokens to deposit and burn.
destinationDomainu32Destination domain identifier.
mintRecipientPubkeyPublic Key of mint recipient on destination domain. Address should be converted to base58. See [Mint Recipient for Solana as Source Chain Transfers](Mint Recipient for Solana as Source Chain Transfers)
destinationCallerPubkeyPublic Key of caller on destination domain. Address should be converted to base58.

replaceDepositForBurn

Replace a BurnMessage to change the mint recipient and/or destination caller. Allows the sender of a previous BurnMessage (created by depositForBurn or depositForBurnWithCaller) to send a new BurnMessage to replace the original. The new BurnMessage will reuse the amount and burn token of the original, without requiring a new deposit.

This is useful in situations where the user specified an incorrect address and has no way to safely mint the previously burned USDC.

Parameters

FieldTypeDescription
originalMessageVec<u8>Original message bytes (to replace).
originalAttestationVec<u8>Original attestation bytes.
newDestinationCallerPubkeyThe new destination caller, which may be the same as the original destination caller, a new destination caller, or an empty destination caller, indicating that any destination caller is valid. Address should be converted to base58.
newMintRecipientPubkeyThe new mint recipient, which may be the same as the original mint recipient, or different. Address should be converted to base58.

MessageTransmitter

receiveMessage

Messages with a given nonce can only be broadcast successfully once for a pair of domains. The message body of a valid message is passed to the specified recipient for further processing.

Parameters

FieldTypeDescription
messageVec<u8>Message bytes.
attestationVec<u8>Signed attestation of message.

Remaining Accounts

If the receiveMessage instruction is being called with a deposit for burn message that will be received by the TokenMessengerMinter, additional remainingAccounts are required so they can be passed with the CPI to TokenMessengerMinter#handleReceiveMessage:

Account NamePDA SeedsPDA ProgramIdisSigner?isWritable?Description
token_messenger["token_messenger"]tokenMessengerMinterfalsefalseTokenMessenger Program Account
remote_token_messenger["remote_token_messenger", sourceDomainId]tokenMessengerMinterfalsefalseRemote token messenger account where the remote token messenger address is stored for the given source domain id
token_minter["token_minter"]tokenMessengerMinterfalsetrueTokenMinter Program Account
local_token["local_token", localTokenMint.publicKey]tokenMessengerMinterfalsetrueLocal token account where the information for the local token (e.g. USDCSOL) being minted is stored
token_pair["token_pair", sourceDomainId, sourceTokenInBase58]tokenMessengerMinterfalsefalseToken pair account where the info for the local and remote tokens are stored. sourceTokenInBase58 is the remote token that was burned converted into base58 format.
user_token_accountN/AN/AfalsetrueUser token account that will receive the minted tokens. This address must match the mintRecipient from the source chain depositForBurn call.
custody_token_account["custody", localTokenMint.publicKey]tokenMessengerMinterfalsetrueCustody account that holds the pre-minted USDCSOL that can be minted for CCTP usage.
SPL.token_program_idN/AN/AfalsefalseThe native SPL token program ID.
token_program_event_authority["__event_authority"]tokenMessengerMinterfalsefalseEvent authority account for the TokenMessengerMinter program. Needed to emit Anchor CPI events.
programN/AN/AfalsefalseProgram id for the TokenMessengerMinter program.

sendMessage

Sends a message to the destination domain and recipient. Emits a MessageSent event which will be attested by Circle’s attestation service.

Parameters

FieldTypeDescription
destinationDomainu32Destination domain identifier.
recipientPubkeyAddress to handle message body on destination domain.
messageBodyVec<u8>Application-specific message to be handled by recipient.

sendMessageWithCaller

Same as sendMessage but with an additional parameter, destinationCaller. This parameter specifies which address has permission to call receiveMessage on the destination domain for the message.

Parameters

FieldTypeDescription
destinationDomainu32Destination domain identifier.
recipientPubkeyAddress of message recipient on destination domain.
destinationCallerPubkeyAddress of caller on the destination domain. Address should be converted to base58.
messageBodyVec<u8>Application-specific message to be handled by recipient.

replaceMessage

Replace a message with a new message body and/or destination caller. The originalAttestation must be a valid attestation of originalMessage, produced by Circle’s attestation service.

Parameters

FieldTypeDescription
originalMessageVec<u8>Original message to replace.
originalAttestationVec<u8>Attestation of originalMessage.
newMessageBodyVec<u8>New message body of replaced message.
newDestinationCallerPubkeyThe new destination caller, which may be the same as the original destination caller, a new destination caller, or an empty destination caller (bytes32(0) or PublicKey.default, indicating that any destination caller is valid). Address should be converted to base58.

reclaimEventAccount

Closes the given event account and reclaims the paid rent in SOL back to the event_rent_payer account. This instruction can only be called by the event_rent_payer account that paid the rent when the message was sent.

Parameters

FieldTypeDescription
attestationVec<u8>Valid attestation for the message stored in the account. This is required to ensure the attestation service has processed and stored the message before it is deleted.

Additional Notes

Mint Recipient for Solana as Destination Chain Transfers

When calling depositForBurn on a non-Solana chain with Solana as the destination, the mintRecipient should be a hex encoded USDC token account address. The token account_ must exist at the time receiveMessage is called on Solana_ or else this instruction will revert. An example of converting an address from Base58 to hex taken from the Solana quickstart tutorial in Typescript can be seen below:

Typescript
import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
import { hexlify } from "ethers";

const solanaAddressToHex = (solanaAddress: string): string => hexlify(bs58.decode(solanaAddress));

Mint Recipient for Solana as Source Chain Transfers

When specifying the mintRecipient for Solana depositForBurn instruction calls, the address must be given as the 32 byte version of the hex address in base58 format. An example taken fro the Solana quickstart tutorial in Typescript can be seen below:

Typescript
import { getBytes } from 'ethers';
import { PublicKey } from '@solana/web3.js';

const evmAddressToBytes32 = (address: string): string => `0x000000000000000000000000${address.replace("0x", "")}`;

const evmAddressToBase58PublicKey = (addressHex: string): PublicKey => new PublicKey(getBytes(evmAddressToBytes32(addressHex)));

Program Events

Program events like DepositForBurn , MintAndWithdraw , and MessageReceived are emitted as Anchor CPI events. This means a self-CPI is made into the program with the serialized event as instruction data so it is persisted in the transaction and can be fetched later on as needed. More information can be seen in the Anchor implementation PR, and an example of reading CPI events can be seen here.

MessageSent events are different, as they are stored in accounts. Please see the MessageSent Event Storage section for more info.

Did this page help you?
© 2023-2024 Circle Technology Services, LLC. All rights reserved.