Overview
The CCTP V1 Aptos smart contract implementation is written in Move. The Aptos CCTP V1 implementation is split into two packages: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 the Aptos packages reflect the EVM
counterparts as closely as possible.
The key difference with Aptos packages from EVM and other CCTP V1
implementations is the receive message flow. Since the Move language uses static
dispatch and requires all dependencies to be available at compile time, the
MessageTransmitter receive_message function cannot call into the receiver
package (e.g. TokenMessengerMinter for USDC transfers). The workaround for
this limitation is that callers of message_transmitter::receive_message must
also atomically (in the same transaction or
move script) call into the
receiver package’s handle_receive_message function with a Receipt object
returned from receive_message, and then pass a Receipt object back into the
message_transmitter::complete_receive_message function to complete the message
and destroy the Receipt object. Please see the interface and examples below
for more information on this flow.
Below are the known package IDs and object IDs on Aptos testnet and mainnet.
Testnet
Package IDs
Object IDs
CCTP V1 and Stablcoin use a shared package
AptosExtensions deployed at
0xb75a74c6f8fddb93fdc00194e2295d8d5c3f6a721e79a2b86884394dcc554f8f
Mainnet
Package IDs
Object IDs
The shared package
AptosExtensions is deployed at
0x98bce69c31ee2cf91ac50a3f38db7b422e3df7cdde9fe672ee1d03538a6aeae0
Interface
The Aptos CCTP V1 source code is available on GitHub. The interface below serves as a reference for permissionless messaging functions exposed by the programs.TokenMessengerMinter
deposit_for_burn
Burns passed inFungibleAsset from sender to be minted on the destination
domain. Minted tokens will be transferred to mint_recipient on the destination
chain. The mint_recipient can be an account address or a store address. The
deposit_for_burn interface and functionality is very similar to the EVM
implementation. The asset parameter is the key difference due to how passing
tokens around on Aptos works. The asset parameter is Aptos Fungible Asset type,
defining information of the token to deposit and burn.
Nonce reserved for the message is returned, but it is not required to do
anything with this return value, they are returned for convenience.
Parameters
deposit_for_burn_with_caller
The same as deposit_for_burn, but with an additional parameter:destination_caller. This parameter specifies which address has permission to
call receiveMessage on the destination domain for the message.
Parameters
Destination Caller NotesIf the
destination_caller does not represent a valid address, then it will not
be possible to broadcast the message on the destination domain. This is an
advanced feature, and the standard deposit_for_burn should be preferred for
use cases where a specific destination caller is not required.Note: If destination is a non-Move chain, destination_caller address should
be converted to hex and passed in using the @0x123 address format.replace_deposit_for_burn_with
Replace a BurnMessage to change the mint recipient and/or destination caller. Allows the sender of a previous BurnMessage (created bydeposit_for_burn or
deposit_for_burn_with_caller) to send a new BurnMessage to replace the
original.
Remarks:
- Only the sender of the original
deposit_for_burntransaction has access to callreplace_deposit_for_burn - The new
BurnMessagewill reuse the amount and burn token of the original, without requiring a newFAdeposit. - The resulting mint will supersede the original mint, as long as the original mint has not confirmed yet onchain.
- A valid attestation is required before calling this function.
- This is useful in situations where the user specified an incorrect address and has no way to safely mint the previously burned USDC.
handle_receive_message
Handles an incoming message that has already been verified bymessage_transmitter, and mints USDC to the recipient for valid messages. This
function can only be called with a mutable reference to a Receipt object,
which can only be created via a call with a valid message to the
message_transmitter::receive_message function. In this function
MessageTransmitter::complete_receive_message is called with the Receipt to
emit the MessageReceived event to complete the message and destroy Receipt.
This should be called in a single transaction after calling
message_transmitter::receive_message. EOAs can execute these functions in a
single
Move script.
See the Examples section for the entire flow of receiving a message.
Parameters
MessageTransmitter
receive_message
Receives a message emitted from a source chain. Messages with a givennonce
can only be received once for a (sourceDomain, destinationDomain) pair.
This function returns a Receipt struct
(Hot Potato)
after validating the attestation and marking the nonce as used. In order to
destroy the Receipt and complete the message, in a single transaction,
handle_receive_message() must be called with the Receipt in the receiver
package. EOAs can execute these functions in a single
Move script.
Returns a Receipt object that must be handled by the intended receiving
package, and completed via a complete_receive_message call before the
transaction ends. See the
Examples section for
more information on receiving USDC transfers.
Parameters
complete_receive_message
Completes the message by emitting aMessageReceived event for a receipt and
destroying the receipt.
Parameters
send_message
Sends a message to the destination domain and recipient. Nonce reserved for the message is returned, but it is not required to do anything with this struct, it is returned for convenience. Remarks:- For USDC transfers, this function is called directly by the
TokenMessengerMinterpackage indeposit_for_burn().
send_message_with_caller
This is the same as send_message, except thereceive_message call on the
destination domain must be called by destination_caller.
Parameters
Destination Caller NotesIf the
destination_caller does not represent a valid address, then it will not
be possible to broadcast the message on the destination domain. This is an
advanced feature, and the standard deposit_for_burn should be preferred for
use cases where a specific destination caller is not required.Note: If destination is a non-Move chain, destination_caller address should
be converted to hex and passed in using the @0x123 address format.replace_message
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. Remarks:- Only the sender of the original deposit_for_burn transaction has access to
call
replace_message
Additional Notes
Using Move Scripts
Pre-compiled scripts for executingdeposit_for_burn and
handle_receive_message are available in the
repo
for testnet and mainnet. Alternatively, the scripts can be compiled from the
source code. The documentation can be found on the official Aptos
website.
Mint Recipient Addresses for Aptos as Source Chain
Outgoing mint recipient addresses from Aptos are passed as Aptos address types and can be treated the same as abytes32 mint recipient parameter on EVM
implementations.
Mint Recipient Addresses for Aptos as Destination Chain
Aptos mint recipient addresses from other chains should be treated the same as a hexbytes32 parameter.