This is CCTP V1 version. For the latest version, see CCTP.
Do not reuse keys As a security measure, these scripts should only be used
on a testnet for testing purposes. It is not recommended to reuse private keys
across mainnet and testnet.
deposit_for_burn() (full runnable script can be found in
the sui-cctp repository):
Javascript
Copy
Ask AI
// Create DepositForBurn tx
const depositForBurnTx = new Transaction();
// Split USDC to send in depositForBurn call
const ownedCoins = await client.getAllCoins({owner: signer.toSuiAddress()})
const usdcStruct = ownedCoins.data.find(c => c.coinType.includes(usdcId));
if (!usdcStruct || Number(usdcStruct.balance) < USDC_AMOUNT) {
throw new Error("Insufficient tokens in wallet to initiate transfer.");
}
const [coin] = depositForBurnTx.splitCoins(
usdcStruct.coinObjectId,
[USDC_AMOUNT]
);
// Create the deposit_for_burn move call
depositForBurnTx.moveCall({
target: `${tokenMessengerMinterId}::deposit_for_burn::deposit_for_burn`,
arguments: [
depositForBurnTx.object(coin), // Coin<USDC>
depositForBurnTx.pure.u32(DESTINATION_DOMAIN), // destination_domain
depositForBurnTx.pure.address(evmUserAddress), // mint_recipient
depositForBurnTx.object(tokenMessengerMinterStateId), // token_messenger_minter state
depositForBurnTx.object(messageTransmitterStateId), // message_transmitter state
depositForBurnTx.object("0x403"), // deny_list id, fixed address
depositForBurnTx.object(treasuryId) // treasury object Treasury<USDC>
],
typeArguments: [`${usdcId}::usdc::USDC`],
});
// Broadcast the transaction
console.log("Broadcasting sui deposit_for_burn tx...");
const depositForBurnOutput = await executeTransactionHelper({
client: client,
signer: signer,
transaction: depositForBurnTx,
});
assert(!depositForBurnOutput.errors);
console.log(`deposit_for_burn transaction successful: 0x${depositForBurnOutput.digest} \n`);
// Get USDC balance changes (optional)
const suiUsdcBalanceChange = depositForBurnOutput.balanceChanges?.find(b => b.coinType.includes(usdcId))
const balances = await client.getAllBalances({ owner: signer.toSuiAddress() });
const usdcBalance = balances.find(b => b.coinType.includes(usdcId))?.totalBalance;
// Get the message emitted from the tx
const messageRaw: Uint8Array = (depositForBurnOutput.events?.find((event) =>
event.type.includes("send_message::MessageSent")
)?.parsedJson as any).message;
const messageBuffer = Buffer.from(messageRaw);
const messageHex = `0x${messageBuffer.toString("hex")}`;
const messageHash = web3.utils.keccak256(messageHex);
console.log(`Message hash: ${messageHash}`);
receive_message() (full runnable script can be found in the
sui-cctp repository):
Javascript
Copy
Ask AI
// Create receiveMessage transaction
const receiveMessageTx = new Transaction();
// Add receive_message move call to MessageTransmitter
const [receipt] = receiveMessageTx.moveCall({
target: `${messageTransmitterId}::receive_message::receive_message`,
arguments: [
receiveMessageTx.pure.vector(
"u8",
Buffer.from(evmBurnTx.message.replace("0x", ""), "hex"),
), // message as byte array
receiveMessageTx.pure.vector(
"u8",
Buffer.from(attestation.replace("0x", ""), "hex"),
), // attestation as byte array
receiveMessageTx.object(messageTransmitterStateId), // message_transmitter state
],
});
// Add handle_receive_message call to TokenMessengerMinter with Receipt from receive_message call
const [stampReceiptTicketWithBurnMessage] = receiveMessageTx.moveCall({
target: `${tokenMessengerMinterId}::handle_receive_message::handle_receive_message`,
arguments: [
receipt, // Receipt object returned from receive_message call
receiveMessageTx.object(tokenMessengerMinterStateId), // token_messenger_minter state
receiveMessageTx.object("0x403"), // deny list, fixed address
receiveMessageTx.object(treasuryId), // usdc treasury object Treasury<T>
],
typeArguments: [`${usdcId}::usdc::USDC`],
});
// Add deconstruct_stamp_receipt_ticket_with_burn_message call
const [stampReceiptTicket] = receiveMessageTx.moveCall({
target: `${tokenMessengerMinterId}::handle_receive_message::deconstruct_stamp_receipt_ticket_with_burn_message`,
arguments: [stampReceiptTicketWithBurnMessage],
});
// Add stamp_receipt call
const [stampedReceipt] = receiveMessageTx.moveCall({
target: `${messageTransmitterId}::receive_message::stamp_receipt`,
arguments: [
stampReceiptTicket, // Receipt ticket returned from deconstruct_stamp_receipt_ticket_with_burn_message call
receiveMessageTx.object(messageTransmitterStateId), // message_transmitter state
],
typeArguments: [
`${tokenMessengerMinterId}::message_transmitter_authenticator::MessageTransmitterAuthenticator`,
],
});
// Add complete_receive_message call to MessageTransmitter with StampedReceipt from stamp_receipt call.
// Receipt and StampedReceipt are Hot Potatoes so they must be destroyed for the
// transaction to succeed.
receiveMessageTx.moveCall({
target: `${messageTransmitterId}::receive_message::complete_receive_message`,
arguments: [
stampedReceipt, // Stamped receipt object returned from handle_receive_message call
receiveMessageTx.object(messageTransmitterStateId), // message_transmitter state
],
});
// Broadcast the transaction
console.log("Broadcasting Sui receive_message tx...");
const receiveMessageOutput = await executeTransactionHelper({
client: client,
signer: signer,
transaction: receiveMessageTx,
});
WHAT’S NEXT