Circle USYC 백서

Circle USYC

원문

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

| XDC | 0 bps (0%) | </Tab> </Tabs> ## Standard Transfer fee switch Some blockchains support a Standard Transfer fee switch, which enables enforcing a minimum fee during a CCTP Standard Transfer. * Some deployments of the `TokenMessengerV2` contract include a fee switch that enforces a minimum onchain fee. This fee is collected during USDC minting in a Standard Transfer. See tables below for supported blockchains. * `TokenMessengerV2` contracts with fee switch support include the `getMinFeeAmount` function, which calculates and returns the minimum fee required for a given burn amount, in units of the `burnToken`. <Note> **Important:** Calling `getMinFeeAmount` on a blockchain that uses an older `TokenMessengerV2` contract (without fee switch support) results in an error. Refer to the tables below to determine which contract version is deployed on each EVM blockchain. </Note> ### `TokenMessenger` contracts without fee switch support | Source blockchain | Contract source code | | - | - | | Arbitrum | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Avalanche | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Base | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Codex | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Ethereum | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Linea | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | OP Mainnet | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Polygon PoS | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Sonic | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | Unichain | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | | World Chain | [`7d70310`](https://github.com/circlefin/evm-cctp-contracts/pull/57/commits/7d703109a2cfcb3f76375fef5f1a97f03c447b94) | ### `TokenMessenger` contracts with fee switch support | Source blockchain | Contract source code | | - | - | | Sei | [`2f9a2ba`](https://github.com/circlefin/evm-cctp-contracts/commit/2f9a2ba993b96a442c75bf21b3cb6d6292d81439) | ## Fee optimization strategies To minimize fees while maximizing transfer speed: * **Choose the right method**: Use Fast Transfer when speed is critical and Standard Transfer when cost optimization is the priority. * **Monitor allowance**: For high-volume applications, monitor the [Fast Transfer allowance](/cctp/concepts/fast-transfer-allowance) and switch to Standard Transfer when it's low. * **Batch transfers**: If you're making multiple transfers, consider batching them during periods when Fast Transfer allowance is high. * **Set appropriate `maxFee`**: Always retrieve the current fee before initiating a transfer and set `maxFee` with a buffer to account for minor fluctuations. ## Charging your own application fee The fees described on this page are protocol fees collected by Circle. The CCTP protocol has no built-in mechanism for adding a custom application or marketplace fee to a transfer. To collect a fee from your users on top of the protocol fee, either use [Bridge Kit's custom fee support](https://docs.arc.io/app-kit/tutorials/bridge/collect-bridge-fee) or implement the fee yourself in a smart contract or backend alongside the CCTP call (for example, by wrapping `depositForBurn` in a contract that also transfers a fee to your treasury). # Finality and block confirmations Source: https://developers.circle.com/cctp/concepts/finality-and-block-confirmations Block confirmation requirements and attestation timing for CCTP Before signing an attestation, Circle waits for blockchain transactions to achieve the appropriate level of transaction finality. The required finality level depends on whether you use Fast Transfer or Standard Transfer. * Fast Transfer: Attestations are issued after the transaction is confirmed and included in a block, typically in seconds. Because of the faster finality time, Fast Transfers are subject to a global allowance to mitigate reorganization risks. * Standard Transfer: Attestations are issued after hard finality, when the transaction is unlikely to be reversed by a chain reorganization, typically in minutes. ## Fast Transfer attestation times The table below shows the average time for attestations to become available when using Fast Transfer (`minFinalityThreshold` ≤ 1000): | Source blockchain | Block confirmations | Average time | | - | - | - | | **Ethereum** | 2 | \~20 seconds | | **Arbitrum** | 1 | \~8 seconds | | **Base** | 1 | \~8 seconds | | **Codex** | 1 | \~8 seconds | | **EDGE** | 1 | \~8 seconds | | **Ink** | 1 | \~8 seconds | | **Linea** | 1 | \~8 seconds | | **Morph** | 1 | \~8 seconds | | **OP Mainnet** | 1 | \~8 seconds | | **Plume** | 1 | \~8 seconds | | **Solana** | 2-3 | \~8 seconds | | **Starknet** | 4 | \~20 seconds | | **Unichain** | 1 | \~8 seconds | | **World Chain** | 1 | \~8 seconds | | **X Layer** | 1 | \~8 seconds | <Note> **Blockchains without Fast Transfer:** Some blockchains don't support Fast Transfer as a source blockchain because their standard attestation times are already fast. For those [CCTP supported blockchains](/cctp/concepts/supported-chains-and-domains) where Fast Transfer is disabled as a source, use Standard Transfer instead. </Note> ## Standard Transfer attestation times The table below shows the average time for attestations to become available when using Standard Transfer (`minFinalityThreshold` ≥ 2000): | Source blockchain | Block confirmations | Average time | | - | - | - | | **Aptos** | 1 | \~0.5 seconds | | **Ethereum** | \~65 | \~15-19 minutes | | **Arbitrum** | \~65 ETH blocks | \~15-19 minutes | | **Arc** | 1 | \~0.5 seconds | | **Avalanche** | 1 | \~8 seconds | | **Base** | \~65 ETH blocks | \~15-19 minutes | | **BNB Smart Chain** | 3 | \~2 seconds | | **Codex** | \~65 ETH blocks | \~15-19 minutes | | **Cronos** | 1 | \~0.5 seconds | | **EDGE** | \~65 ETH blocks | \~16-21 minutes | | **HyperEVM** | 1 | \~5 seconds | | **Injective** | 1 | \~0.65 seconds | | **Ink** | \~65 ETH blocks | \~30 minutes | | **Linea** | 1 | \~6-32 hours | | **Monad** | 1 | \~5 seconds | | **Morph** | \~65 ETH blocks | \~20-30 minutes | | **OP Mainnet** | \~65 ETH blocks | \~15-19 minutes | | **Pharos** | 1 | \~7 seconds | | **Plasma** | 3 | \~1 second | | **Plasma Testnet** | 3 | \~1 second | | **Plume** | \~65 ETH blocks | \~15-19 minutes | | **Polygon PoS** | 2-3 | \~8 seconds | | **Sei** | 1 | \~5 seconds | | **Solana** | 32 | \~25 seconds | | **Sonic** | 1 | \~8 seconds | | **Starknet** | \~65 ETH blocks | \~2 to 4 hours | | **Stellar** | 1 | \~5 seconds | | **Sui** | 1 | \~0.5 seconds | | **Unichain** | \~65 ETH blocks | \~15-19 minutes | | **World Chain** | \~65 ETH blocks | \~15-19 minutes | | **X Layer** | \~65 ETH blocks | \~15-19 minutes | | **XDC** | 3 | \~10 seconds | ## Layer 2 finality Layer 2 (L2) blockchains built on Ethereum publish transaction data in batches to Ethereum Layer 1. The finality characteristics of L2 chains depend on when batches are posted and when those batches achieve finality on Ethereum L1. <CardGroup> <Card title="OP Stack chains" icon="layer-group"> OP Stack-based chains (including Base, OP Mainnet, World Chain, and X Layer) post state updates using [EIP-4844](https://www.eip4844.com/) blob transactions approximately every 15 minutes. Circle waits for the Ethereum L1 block containing the batch to finalize, which typically takes \~65 blocks (15-19 minutes) after the batch is posted. </Card> <Card title="Linea" icon="clock"> Linea has a longer finality period compared to other L2 chains. Standard Transfer on Linea typically requires 6-32 hours before attestations become available. </Card> <Card title="Starknet" icon="clock"> Starknet finality depends on when the zk-rollup proof is posted to Ethereum and when its corresponding L1 block finalizes. Attestations for Standard Transfer typically become available 2–4 hours after the burn, with most transfers closer to 3 hours. </Card> </CardGroup> ## Solana finality Solana uses a different finality model: <CardGroup> <Card title="Fast Transfer" icon="bolt"> Circle waits for the block to be confirmed (votes from validators representing over two-thirds of total stake). This typically takes 2-3 blocks (\~8 seconds). </Card> <Card title="Standard Transfer" icon="shield-check"> Circle waits for block finality, which takes 32 blocks (\~25 seconds). </Card> </CardGroup> # Circle Forwarding Service for CCTP Source: https://developers.circle.com/cctp/concepts/forwarding-service Forward destination chain mints to simplify crosschain transfers The Circle Forwarding Service is a service for CCTP that simplifies integration by removing the need for you to run multichain infrastructure. This can improve user experience for crosschain transfers by ensuring reliability and eliminating the need to handle destination chain gas fees. ## How it works A CCTP transfer without the Forwarding Service is a three-step process: 1. Create a transaction to burn USDC on the source chain and wait for Circle to sign an attestation. 2. Request an attestation from the Circle API. 3. Create a transaction to mint USDC on the destination chain. This process requires you to have a wallet that can sign transactions on the source and destination chains, and native tokens for paying the transaction gas fee on both chains. You use the Forwarding Service by including a forward request in the hook data of the burn transaction on the source chain. Circle validates the hook data, signs the attestation, and broadcasts the mint transaction on the destination chain for you, removing the need for you to handle the transaction on the destination chain. For a full example of how to use the Forwarding Service, see [Transfer USDC with the Forwarding Service](/cctp/howtos/transfer-usdc-with-forwarding-service). ### Hook format The hook data for Forwarding Service begins with the reserved magic bytes `cctp-forward` followed by versioning and payload fields. It comes in two formats: version 1, the composable format that can carry multiple hooks, and version 0, the single-hook format. #### Version 1 Hook data is one or more composable frames concatenated. Each frame has a fixed 32-byte header followed by its payload: | Bytes | Type | Data | | - | - | - | | 0-23 | `bytes24` | Hook name (magic). For forwarding, this is `cctp-forward` | | 24-27 | `uint32` | Version, set to `1` | | 28-31 | `uint32` | Payload length in bytes (`n`) | | 32 to 32+n | `bytes` | Payload (`n` bytes) | You can chain multiple frames, such as a `cctp-forward` hook plus your own hook carrying your custom data. Custom data still goes in its own dedicated frame, not appended as raw bytes to the `cctp-forward` frame. Every frame in a version 1 blob must use version 1, and the blob must contain only complete frames. Appending raw, unframed bytes is rejected. To attach your own hook alongside forwarding, append it as another complete frame after the `cctp-forward` frame: ```typescript theme={null} import { concat, stringToHex, toHex } from "viem"; // cctp-forward hook, framed with the shared 32-byte header const forwardHook = concat([ stringToHex("cctp-forward", { size: 24 }), // hook name toHex(1, { size: 4 }), // version toHex(0, { size: 4 }), // payload length (no payload) ]); // A custom hook, framed the same way. The destination ignores it unless it // recognizes the hook name; replace it with a hook your destination supports. const customHook = concat([ stringToHex("my-custom-hook", { size: 24 }), // hook name toHex(1, { size: 4 }), // version toHex(4, { size: 4 }), // payload length (4 bytes) "0xdeadbeef", // payload ]); const hookData = concat([forwardHook, customHook]); ``` #### Version 0 Version 0 is the single-hook format. You can append your own custom hook data after Circle's reserved space. Forwarding Service doesn't support forwarding to wrapper contracts (for example, when `destinationCaller` is set). | Bytes | Type | Data | | - | - | - | | 0-23 | `bytes24` | `cctp-forward` | | 24-27 | `uint32` | Version, set to `0` | | 28-31 | `uint32` | Length of additional Circle hook data, set to `0` | | 32-51 | `any` | Developer-defined hook data | If no additional integrator hook data is required, a static hex string can be used for the forwarding hook data: ```javascript theme={null} // Includes magic bytes ("cctp-forward") + hook version (0) + empty data length (0) const forwardHookData = "0x636374702d666f72776172640000000000000000000000000000000000000000"; ``` ### Solana `mintRecipient` When the destination blockchain is Solana, the `mintRecipient` parameter in `depositForBurnWithHook` must be the recipient's USDC [Associated Token Account (ATA)](https://spl.solana.com/associated-token-account) address, not the recipient's wallet address. Unlike EVM destinations where `mintRecipient` is the wallet address, Solana requires the address of the SPL token account that will hold the minted USDC. You can derive the ATA address from the recipient owner address and the USDC mint address using the [`getAssociatedTokenAddressSync`](https://solana-labs.github.io/solana-program-library/token/js/functions/getAssociatedTokenAddressSync.html) function from the `@solana/spl-token` library. In Solana terms, the recipient owner can be an on-curve public key controlled by a keypair, such as a wallet public key, or an off-curve Program Derived Address (PDA) controlled by a Solana program. If the owner might be off-curve, pass `true` for `allowOwnerOffCurve`: ```typescript theme={null} import { getAssociatedTokenAddressSync } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; const recipientOwner = new PublicKey("RecipientOwnerAddress"); const USDC_MINT = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"); // Solana devnet const recipientAta = getAssociatedTokenAddressSync( USDC_MINT, recipientOwner, true, ); const mintRecipient = `0x${Buffer.from(recipientAta.toBytes()).toString("hex")}`; ``` ### Solana hook data for ATA creation If the recipient does not have an existing USDC ATA, you can request the Forwarding Service to create it by encoding additional fields in the hook data. The extended hook data format is: | Bytes | Type | Data | | - | - | - | | 0-23 | `bytes24` | `cctp-forward` | | 24-27 | `uint32` | Version, set to `0` | | 28-31 | `uint32` | Length of additional Circle hook data, set to `33` | | 32 | `uint8` | `1` (request ATA creation) | | 33-64 | `bytes32` | Recipient owner address for the ATA (on-curve or off-curve) | | 65+ | `any` | Developer-defined hook data | When using this format, `mintRecipient` must be the ATA derived from the recipient owner address in bytes 33-64 and the USDC mint. The recipient owner can be an on-curve public key controlled by a keypair or an off-curve PDA. Forwarding Service validates that these values match before creating the account. The following example shows how to construct the extended hook data for Solana with ATA creation: ```typescript theme={null} import { PublicKey } from "@solana/web3.js"; // Magic bytes "cctp-forward" padded to 24 bytes const magicBytes = Buffer.alloc(24); magicBytes.write("cctp-forward", "utf-8"); // Version (uint32, big-endian) = 0 const version = Buffer.alloc(4); // Length of additional Circle hook data (uint32, big-endian) = 33 const length = Buffer.alloc(4); length.writeUInt32BE(33); // ATA creation flag = 1 const ataFlag = Buffer.from([1]); // Recipient owner address for the ATA (32 bytes) const recipientOwner = new PublicKey("RecipientOwnerAddress"); const ownerBytes = Buffer.from(recipientOwner.toBytes()); const forwardHookData = "0x" + Buffer.concat([magicBytes, version, length, ataFlag, ownerBytes]).toString( "hex", ); ``` If the recipient already has a USDC ATA and no ATA creation is needed, use the same static hook data as EVM: ```typescript theme={null} // Includes magic bytes ("cctp-forward") + hook version (0) + empty data length (0) const forwardHookData = "0x636374702d666f72776172640000000000000000000000000000000000000000"; ``` ## Fees and execution The Forwarding Service charges a fee for each transfer, in addition to the CCTP protocol fee. The Forwarding Service fee charged is to cover gas costs on the destination chain and a small service fee. The Forwarding Service prioritizes fast execution and quotes gas dynamically. If gas used is less than gas needed for execution, the remainder is spent as an additional priority fee where they are supported. On all chains, a higher fee provides a safety buffer for successful transaction delivery on the destination chain. Circle does not refund for excess gas and does not keep the excess gas, except in cases where excess priority fees are rejected. <Note> Instead of deducting the Forwarding Service fee from the transfer, you can pay it [upfront](/cctp/concepts/upfront-fees) on the source blockchain so the recipient receives the full transfer amount on the destination blockchain. See [Supported blockchains](/cctp/concepts/supported-chains-and-domains) for which sources support upfront fees. </Note> The [`depositForBurnWithHook`](/cctp/references/contract-interfaces#depositforburnwithhook) transaction includes a `maxFee` parameter. When using the Forwarding Service, this parameter should be set to a value that is large enough to cover the CCTP protocol fee and the Forwarding Service fee. Because the gas budget for the destination chain comes from a USDC fee on the source chain, choosing a lower `maxFee` results in a lower priority fee on the destination chain. A higher `maxFee` results in a higher priority fee on the destination chain and can result in faster confirmation. The Forwarding Service charges a service fee for each transfer: | Forwarding route | Service fee (USDC) | | - | - | | HyperCore deposits | \$0.20 | | HyperCore withdrawals to Ethereum | \$1.20 | | HyperCore withdrawals to Solana | \$0.50 | | HyperCore withdrawals to all other supported chains | \$0.20 | | All other forwarding destinations | \$0.05 | <Warning> If the `maxFee` parameter is insufficient to cover the both Fast Transfer protocol fee and the Forwarding Service fee, CCTP will prioritize forwarding execution over Fast Transfer. This means that the transfer will execute as a Standard Transfer with the Forwarding Service. </Warning> ### Solana fees When forwarding to Solana, the Forwarding Service fee includes both a gas component and a rent component. Rent covers the cost of creating onchain accounts required by each transfer. The fee estimate API returns `forwardFee` values that already include rent, so no additional calculation is needed on your part. If the recipient does not already have a USDC [Associated Token Account (ATA)](https://spl.solana.com/associated-token-account) on Solana, the transfer will fail. To have the Forwarding Service create the ATA, you must: 1. Pass `includeRecipientSetup=true` when calling the fee estimate API so the returned `forwardFee` covers the ATA creation cost. 2. Encode the ATA creation fields in the [hook data](#solana-hook-data-for-ata-creation) of the burn transaction. ```http theme={null} GET /v2/burn/USDC/fees/{sourceDomain}/{destDomain}?forward=true&includeRecipientSetup=true ``` For full details
프로젝트로 돌아가기