Circle USYC 백서

Circle USYC

원문

bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 75쪽 중 41쪽

For more information, see the [Bridge](https://docs.arc.io/app-kit/bridge) capability in Arc App Kits. ## Changes to smart contracts CCTP uses new smart contracts with different names, addresses, and interfaces. You must update your integration to use the new contracts and their new function signatures. ### Contract name and address changes All legacy contracts have V2 equivalents deployed at new addresses: | Legacy contract | V2 contract | Documentation | | - | - | - | | `TokenMessenger` | `TokenMessengerV2` | [V2 Interface](/cctp/evm-smart-contracts#tokenmessengerv2) | | `MessageTransmitter` | `MessageTransmitterV2` | [V2 Interface](/cctp/evm-smart-contracts#messagetransmitterv2) | | `TokenMinter` | `TokenMinterV2` | [V2 Addresses](/cctp/evm-smart-contracts#tokenminterv2-mainnet) | | `Message` | `MessageV2` | [V2 Addresses](/cctp/evm-smart-contracts#messagev2-mainnet) | <Important> **Important**: V2 contracts are deployed at different addresses than V1 contracts. See the [CCTP Contract Addresses](/cctp/evm-smart-contracts#mainnet-contract-addresses) for the complete list of mainnet and testnet addresses. </Important> ### `TokenMessengerV2` changes **Modified functions:** * `depositForBurn()` now requires three additional parameters: * `destinationCaller` (bytes32) - Address that can call `receiveMessage` on destination * `maxFee` (uint256) - Maximum fee for Fast Transfer in units of burn token * `minFinalityThreshold` (uint32) - Minimum finality level (1000 for Fast, 2000 for Standard) **New functions:** * `depositForBurnWithHook()` - Enables custom logic execution on destination chain using hook data * `getMinFeeAmount()` - Calculates minimum fee for Standard Transfer (on supported chains only) **Removed functions:** * `depositForBurnWithCaller()` - Use `destinationCaller` parameter in `depositForBurn()` instead * `replaceDepositForBurn()` - No V2 equivalent available ### Contract source code Full contract source code is available on GitHub: * [CCTP EVM Contracts](https://github.com/circlefin/evm-cctp-contracts) - Main repository * [Contract ABIs](https://github.com/circlefin/evm-cctp-contracts/tree/master/docs/abis/cctp/v2) - Interface definitions ## API migration guide CCTP streamlines the API workflow by combining message retrieval and attestation into single calls, while introducing new endpoints for features like Fast Transfer monitoring and re-attestation. ### Workflow changes The API eliminates the need to extract the message emitted by the onchain transaction: **Legacy workflow:** 1. Get the transaction receipt from the onchain transaction 2. Find the MessageSent event in the transaction receipt 3. Hash the message bytes emitted by the MessageSent event 4. Call `/v1/attestations/{messageHash}` to get an attestation **V2 workflow:** 1. Call `/v2/messages/{sourceDomainId}` with transaction hash or nonce to get message, attestation, and decoded data #### Legacy workflow example ```javascript theme={null} import { createPublicClient, http } from "viem"; import { sepolia } from "viem/chains"; // V1 requires multiple steps to extract message and get attestation const burnTxHash = "0x1234..."; // Transaction hash from depositForBurn // Step 1: Get the transaction receipt from the onchain transaction const client = createPublicClient({ chain: sepolia, transport: http(), }); const transactionReceipt = await client.getTransactionReceipt({ hash: burnTxHash, }); // Step 2: Find the MessageSent event in the transaction receipt const eventTopic = keccak256(toBytes("MessageSent(bytes)")); const log = transactionReceipt.logs.find((l) => l.topics[0] === eventTopic); const messageBytes = decodeAbiParameters([{ type: "bytes" }], log.data)[0]; // Step 3: Hash the message bytes emitted by the MessageSent event const messageHash = keccak256(messageBytes); // Step 4: Call attestation API with the message hash let attestationResponse = { status: "pending" }; while (attestationResponse.status !== "complete") { const response = await fetch( `https://iris-api-sandbox.circle.com/attestations/${messageHash}`, ); attestationResponse = await response.json(); await new Promise((r) => setTimeout(r, 2000)); } const attestation = attestationResponse.attestation; // Now you can use messageBytes and attestation to call receiveMessage ``` #### V2 workflow example ```javascript theme={null} // V2 gets message and attestation in a single call const sourceDomainId = 0; // Ethereum mainnet const transactionHash = "0x1234..."; // Single step: Get message, attestation, and decoded data const response = await fetch( `https://iris-api.circle.com/v2/messages/${sourceDomainId}?transactionHash=${transactionHash}`, ); const data = await response.json(); // All data available in single response const message = data.messages[0].message; const attestation = data.messages[0].attestation; const decodedMessage = data.messages[0].decodedMessage; // Now you can use message and attestation to call receiveMessage // You can also access decoded fields without manual parsing console.log(`Amount: ${decodedMessage.decodedMessageBody.amount}`); console.log(`Recipient: ${decodedMessage.decodedMessageBody.mintRecipient}`); ``` ### Endpoint migration mapping | Legacy endpoint | V2 replacement | Migration notes | | - | - | - | | `GET /v1/attestations/{messageHash}` | `GET /v2/messages/{sourceDomainId}?transactionHash={hash}` | Combined into messages endpoint with enhanced response | | `GET /v1/messages/{sourceDomainId}/{transactionHash}` | `GET /v2/messages/{sourceDomainId}?transactionHash={hash}` | Enhanced with decoded data and attestation | | `GET /v1/publicKeys` | `GET /v2/publicKeys` | Multi-version support, backward compatible | ### New V2-only endpoints V2 introduces additional endpoints for advanced features: | Endpoint | Purpose | Use case | | - | - | - | | `POST /v2/reattest/{nonce}` | Re-attest messages for edge cases | Handle expired Fast Transfer burns or finality changes | | `GET /v2/fastBurn/USDC/allowance` | Monitor Fast Transfer allowance | Check remaining Fast Transfer capacity in real-time | | `GET /v2/burn/USDC/fees/{sourceDomainId}/{destDomainId}` | Get current transfer fees | Calculate fees before initiating transfers | ### Message data changes V2 message responses now include the decoded message data and attestation: #### V1 messages response ```json theme={null} { "messages": [ { "attestation": "0xdc485fb2f9a8f68c871f4ca7386dee9086ff9d4387756990c9c4b9280338325252866861f9495dce3128cd524d525c44e8e7b731dedd3098a618dcc19c45be1e1c", "message": "0x00000000000000050000000300000000000194c2...", "eventNonce": "9682" } ] } ``` #### V2 messages response ```json theme={null} { "messages": [ { "message": "0x00000000000000050000000300000000000194c2...", "eventNonce": "9682", "attestation": "0x6edd90f4a0ad0212fd9fbbd5058a25aa8ee10ce77e4fc143567bbe73fb6e164f384a3e14d350c8a4fc50b781177297e03c16b304e8d7656391df0f59a75a271f1b", "decodedMessage": { "sourceDomain": "7", "destinationDomain": "5", "nonce": "569", "sender": "0xca9142d0b9804ef5e239d3bc1c7aa0d1c74e7350", "recipient": "0xb7317b4EFEa194a22bEB42506065D3772C2E95EF", "destinationCaller": "0xf2Edb1Ad445C6abb1260049AcDDCA9E84D7D8aaA", "messageBody": "0x00000000000000050000000300000000000194c2...", "decodedMessageBody": { "burnToken": "0x4Bc078D75390C0f5CCc3e7f59Ae2159557C5eb85", "mintRecipient": "0xb7317b4EFEa194a22bEB42506065D3772C2E95EF", "amount": "5000", "messageSender": "0xca9142d0b9804ef5e239d3bc1c7aa0d1c74e7350" } }, "cctpVersion": 2, "status": "complete" } ] } ``` <Note> On Stellar, USDC precision and address encoding differ from other CCTP-supported blockchains. For inbound transfers, use [`CctpForwarder`](/cctp/references/stellar#use-cctpforwarder-for-stellar-recipients) so funds reach the correct recipient. See [CCTP on Stellar](/cctp/references/stellar). </Note> # Transfer USDC between Aptos and Arc Source: https://developers.circle.com/cctp/quickstarts/transfer-usdc-aptos-arc Build scripts to transfer USDC between Aptos Testnet and Arc Testnet using CCTP Transfer USDC between Aptos Testnet and Arc Testnet using the CCTP burn-attest-mint flow. <Note> Move scripts for Aptos CCTP transfers are precompiled. They are located in the [aptos-cctp GitHub repository](https://github.com/circlefin/aptos-cctp/tree/master/typescript/example/precompiled-move-scripts/testnet/v2). You submit these compiled scripts directly instead of compiling Move source code. </Note> Pick the tab that matches the direction of your transfer. <Tabs> <Tab title="Aptos to Arc"> This quickstart demonstrates how to transfer USDC from Aptos Testnet to Arc Testnet using CCTP. You use the [`@aptos-labs/ts-sdk`](https://github.com/aptos-labs/aptos-ts-sdk) library to submit the precompiled Aptos Move script that burns USDC, and [`viem`](https://viem.sh/) to mint USDC on Arc Testnet. When you finish, you will have executed a full burn-attest-mint flow. You should be comfortable using a terminal and Node.js. Familiarity with Aptos transactions and basic EVM usage helps you follow and adapt the script. Examples use Arc Testnet as the destination, but you can use any [supported EVM blockchain](/cctp/concepts/supported-chains-and-domains). <Note> The Aptos burn transaction uses a precompiled Move script. Download `deposit_for_burn.mv` from the [aptos-cctp repository](https://github.com/circlefin/aptos-cctp/tree/master/typescript/example/precompiled-move-scripts/testnet/v2) and place it in a `precompiled-move-scripts` directory in your project. </Note> ## Prerequisites Before you begin this tutorial, ensure you have: * Installed [Node.js v22.18+](https://nodejs.org/) * Prepared an Aptos Testnet wallet with the private key available * Funded your account with testnet APT from the [Aptos Faucet](https://aptos.dev/en/network/faucet) for transaction fees * Funded your account with Aptos Testnet USDC from the [Circle Faucet](https://faucet.circle.com) * Prepared an EVM wallet with the private key available for Arc Testnet * Added the Arc Testnet network to your wallet ([network details](https://docs.arc.io/arc/references/connect-to-arc#wallet-setup)) * Funded your wallet with Arc Testnet ETH for gas fees ## Step 1: Set up the project This step shows you how to prepare your project and environment. ### 1.1. Set up your development environment Create a new directory, initialize and set up a new Node.js project, and install the required dependencies: ```bash Shell theme={null} mkdir cctp-aptos-to-arc cd cctp-aptos-to-arc npm init -y npm pkg set type=module npm pkg set scripts.start="node --env-file=.env --import=tsx index.ts" npm install @aptos-labs/ts-sdk viem npm install --save-dev tsx typescript @types/node ``` Copy [`deposit_for_burn.mv`](https://github.com/circlefin/aptos-cctp/tree/master/typescript/example/precompiled-move-scripts/testnet/v2/deposit_for_burn.mv) into `precompiled-move-scripts/` in your project. The full script in Step 4 reads the bytecode relative to `index.ts`. ### 1.2. Initialize and configure the project <Tip> This step is optional. It helps prevent missing types in your IDE or editor. </Tip> Create a `tsconfig.json` file: ```shell theme={null} npx tsc --init ``` Then, update the `tsconfig.json` file: ```shell theme={null} cat <<'EOF' > tsconfig.json { "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "types": ["node"] } } EOF ``` ### 1.3. Configure environment variables Open `.env` in your editor and add: ```text theme={null} APTOS_PRIVATE_KEY=YOUR_APTOS_PRIVATE_KEY EVM_PRIVATE_KEY=YOUR_EVM_PRIVATE_KEY ``` * `APTOS_PRIVATE_KEY` is the private key for the Aptos Testnet account used to sign the burn transaction. * `EVM_PRIVATE_KEY` is the private key for the EVM wallet used to receive USDC on Arc Testnet. <Tip> Open `.env` in your editor rather than writing values with shell commands, and add `.env` to your `.gitignore`. This prevents credentials from leaking into your shell history or version control. </Tip> The `npm run start` command loads variables from `.env` using Node.js native env-file support. <Warning> This example uses one or more private keys for local testing. In production, use a secure key management solution and never expose or share private keys. </Warning> ## Step 2: Configure the script Define the Aptos and Arc Testnet parameters, then configure the wallet clients for each chain. ### 2.1. Define configuration constants The script predefines the USDC address, CCTP contract address, transfer amount, and CCTP domain IDs. ```ts TypeScript theme={null} import { Account, AccountAddress, Aptos, AptosConfig, Ed25519PrivateKey, Network, U32, U64, } from "@aptos-labs/ts-sdk"; import { readFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; import { createPublicClient, createWalletClient, http, parseAbi, type Address, type Hex, } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { arcTestnet } from "viem/chains"; type Attestation = { message: string; attestation: string; }; const APTOS_DOMAIN = 9; // Source domain ID for Aptos Testnet const APTOS_USDC_ADDRESS = "0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832"; const BYTECODE_PATH = fileURLToPath( new URL("./precompiled-move-scripts/deposit_for_burn.mv", import.meta.url), ); const EVM_DESTINATION_DOMAIN = 26; // Arc Testnet const MESSAGE_TRANSMITTER_V2_ADDRESS = "0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275" as Address; const AMOUNT = 1_000_000; // 1 USDC, 6 decimals const MAX_FEE = 0; const MIN_FINALITY_THRESHOLD = 2_000; // Standard Transfer const IRIS_API_URL = "https://iris-api-sandbox.circle.com"; ``` `APTOS_DOMAIN` is the source domain used when requesting the attestation. The destination domain for Arc Testnet is 26. Aptos Testnet USDC uses 6 decimals, so `1_000_000` represents 1 USDC. ### 2.2. Set up wallet clients The EVM clients connect to Arc Testnet with `viem`. The Aptos client uses the Aptos Testnet network and signs transactions with the Aptos account's Ed25519 private key. ```ts TypeScript theme={null} const evmPrivateKey = process.env.EVM_PRIVATE_KEY; const aptosPrivateKey = process.env.APTOS_PRIVATE_KEY; if (!evmPrivateKey || !aptosPrivateKey) { throw new Error("Set EVM_PRIVATE_KEY and APTOS_PRIVATE_KEY in .env"); } const account = privateKeyToAccount(evmPrivateKey as Hex); const walletClient = createWalletClient({ account, chain: arcTestnet, transport: http(), }); const publicClient = createPublicClient({ chain: arcTestnet, transport: http(), }); const aptosClient = new Aptos(new AptosConfig({ network: Network.TESTNET })); const aptosAccount = Account.fromPrivateKey({ privateKey: new Ed25519PrivateKey(aptosPrivateKey), }); const messageTransmitterAbi = parseAbi([ "function receiveMessage(bytes message, bytes attestation) returns (bool success)", ]); ``` ### 2.3. Format the Arc recipient for Aptos The Aptos `deposit_for_burn.mv` script accepts the mint recipient as an Aptos address. An EVM address is converted to a 32-byte Aptos address by left-padding it with zeros. ```ts TypeScript theme={null} function toAptosAddress(evmAddress: Address): AccountAddress { return AccountAddress.from(`0x${evmAddress.slice(2).padStart(64, "0")}`); } ``` ## Step 3: Implement the transfer logic This step implements the core transfer logic: burn on Aptos, poll for an attestation, then mint on Arc. The precompiled Aptos script withdraws USDC from the account's primary fungible store, so there is no separate approval transaction. ### 3.1. Burn USDC on Aptos Submit `deposit_for_burn.mv` to burn USDC on Aptos Testnet. The script submits the following parameters: * **Burn amount**: The amount of USDC to burn in Aptos subunits (6 decimals) * **Destination domain**: The target blockchain for minting USDC (26 for Arc Testnet) * **Mint recipient**: The EVM wallet address that receives minted USDC on Arc, represented as a 32-byte Aptos address * **Destination caller**: The Aptos zero address, allowing any caller to submit the receive transaction on Arc * **Burn token**: The Aptos Testnet USDC address * **Max fee**: The maximum [fee](/cctp/concepts/fees) allowed for the transfer * **Finality threshold**: `2000` for a [Standard Transfer](/cctp/concepts/finality-and-block-confirmations#standard-transfer-attestation-times) ```ts TypeScript theme={null} async function burnUSDC() { console.log(`Burning ${AMOUNT / 1_000_000} USDC on Aptos...`); const bytecode = new Uint8Array(await readFile(BYTECODE_PATH)); const transaction = await aptosClient.transaction.build.simple({ sender: aptosAccount.accountAddress, data: { bytecode, functionArguments: [ new U64(AMOUNT), new U32(EVM_DESTINATION_DOMAIN), toAptosAddress(account.address), AccountAddress.from("0x0"), AccountAddress.from(APTOS_USDC_ADDRESS), new U64(MAX_FEE), new U32(MIN_FINALITY_THRESHOLD), ], }, }); const pendingTransaction = await aptosClient.signAndSubmitTransaction({ signer: aptosAccount, transaction, }); const depositForBurnTransaction = await aptosClient.waitForTransaction({ transactionHash: pendingTransaction.hash, }); console.log( `Deposit-for-burn confirmed: https://explorer.aptoslabs.com/txn/${depositForBurnTransaction.hash}`, ); return depositForBurnTransaction; } ``` ### 3.2. Retrieve attestation Retrieve the attestation required to complete the CCTP transfer by calling Circle's attestation API. * Call Circle's [`GET /v2/messages`](/api-reference/cctp/all/get-messages-v2) API endpoint to retrieve the attestation. * Pass `APTOS_DOMAIN` for the `sourceDomain` path parameter, using the [CCTP domain](/cctp/concepts/supported-chains-and-domains#domain-identifiers) for Aptos Testnet (9). * Pass the Aptos transaction hash returned by `burnUSDC` as `transactionHash`. ```ts TypeScript theme={null} async function retrieveAttestation( transactionHash: string, ): Promise<Attestation> { const url = `${IRIS_API_URL}/v2/messages/${APTOS_DOMAIN}?transactionHash=${transactionHash}`; console.log(`Polling Iris V2 for attestation: ${url}`); while (true) { const response = await fetch(url); if (!response.ok && response.status !== 404) { throw new Error(`Iris V2 returned HTTP ${response.status}`); } if (response.ok) { const data = (await response.json()) as { messages?: Array<{ message?: string; attestation?: string }>; }; const result = data.messages?.[0]; if ( result?.message && result.attestation && result.attestation !== "PENDING" ) { return { message: result.message, attestation: result.attestation, }; } } await new Promise((resolve) => setTimeout(resolve, 2_000)); } } ``` ### 3.3. Mint USDC on Arc Call the [`receiveMessage` function](/cctp/references/contract-interfaces#receivemessage) from the [`MessageTransmitterV2` contract](/cctp/references/contract-addresses) deployed on Arc Testnet. * Pass the signed attestation and message bytes as parameters. * The contract verifies the attestation and mints USDC to the recipient encoded in the CCTP message. ```ts TypeScript theme={null} async function mintUSDC({ message, attestation }: Attestation) { console.log("Receiving the message on EVM..."); const receiveTransactionHash = await walletClient.writeContract({ account, chain: arcTestnet, address: MESSAGE_TRANSMITTER_V2_ADDRESS, abi: messageTransmitterAbi, functionName: "receiveMessage", args: [message as Hex, attestation as Hex], }); const receiveReceipt = await publicClient.waitForTransactionReceipt({ hash: receiveTransactionHash, }); if (receiveReceipt.status !== "success") { throw new Error( `EVM receive transaction reverted: ${receiveTransactionHash}`,
프로젝트로 돌아가기