Circle USYC 백서

Circle USYC

원문

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

nMessengerMinter` package. | ### [send\_message\_with\_caller](https://github.com/circlefin/sui-cctp/blob/004950f742a161b6acfe2331630233ac3de0f3ad/packages/message_transmitter/sources/send_message.move#L85) Same as `send_message()` but with an additional parameter, `destination_caller`. This parameter specifies which address has permission to call `receive_message()` on the destination domain for the message. **Parameters** | Field | Type | Description | | :- | :- | :- | | send\_message\_with\_caller\_ticket | `SendMessageWithCallerTicket<Auth>` | A struct containing the necessary information to send a message created via `create_send_message_with_caller_ticket()`. | | state | `&mut State` | Shared `State` object for the `TokenMessengerMinter` package. | ### [replace\_message](https://github.com/circlefin/sui-cctp/blob/004950f742a161b6acfe2331630233ac3de0f3ad/packages/message_transmitter/sources/send_message.move#L115https://github.com/circlefin/sui-cctp-private/blob/master/packages/message_transmitter/sources/send_message.move#L146) Replace a message with a new message body and/or destination caller. The `original_attestation` must be a valid attestation of `original_message`, produced by Circle's attestation service. **Remarks:** * The sender package of the replaced message must be the same as the caller of the original message. This is identified using the `Auth` generic parameter. See [stamp\_receipt](#stamp_receipt) for more info on `Auth` structs. **Parameters** | Field | Type | Description | | :- | :- | :- | | replace\_message\_ticket | `ReplaceMessageTicket<Auth>` | A struct containing the necessary information to send a message created via `create_replace_message_ticket()`. | | state | `&mut State` | Shared `State` object for the `TokenMessengerMinter` package. | ## Additional Notes ### Destination Callers for Sui as Destination Chain Destination caller is a message field that specifies which address has permission to call `receive_message()` on the destination domain for the given message. On Sui this can either be an EOA (use `receive_message()`) or an `Auth` struct address for a package (use `receive_message_with_package_auth()`). Using a package destination caller allows integrators to run any atomic action in the same transaction that the message is received in. In order to determine the address to use for the destination caller field for Sui destination messages, please call `message_transmitter::auth::auth_caller_identifier()` with your `Auth` struct type. <Note> In order to use a package destination caller with Sui destination messages, integrators must create an `Auth` struct in their own package. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated `Auth` struct. Integrators should be careful to not expose these structs to the public or else messages with their package as destination caller could be received by others. An example can be found in `TokenMessengerMinter` [on GitHub](https://github.com/circlefin/sui-cctp/blob/004950f742a161b6acfe2331630233ac3de0f3ad/packages/token_messenger_minter/sources/message_transmitter_authenticator.move). </Note> ### Mint Recipient Addresses for Sui as Source Chain Outgoing mint recipient addresses from Sui are passed as Sui address types and can be treated the same as a `bytes32` mint recipient parameter on EVM implementations. ### Mint Recipient Addresses for Sui as Destination Chain Sui mint recipient addresses from other chains should be treated the same as a hex `bytes32` parameter. ### CCTP V1 Package Upgrades and Versioning CCTP V1 packages on Sui are upgradable. Public functions like `deposit_for_burn()`, `receive_message()`, etc. are version-gated. This means if the CCTP V1 packages are upgraded, the old versions of these functions will no longer be callable. Because of this, we do not recommend calling these functions directly from packages, and instead recommend calling the create ticket functions (not version-gated) directly from dependent packages, returning the created `Ticket` from your package, and then calling the main public function (e.g. `deposit_for_burn()` or `receive_message()`) from a PTB. By using the create ticket functions, dependent packages can securely set the parameters and `Auth` struct for the function call from within the package, and only have to update PTBs when CCTP V1 packages are upgraded. ### Integrating with CCTP V1 Sui from other Packages Integrating with the CCTP V1 Sui packages from other packages is different from non-Sui implementations. Rather than directly wrapping the CCTP-Sui packages like one would in Solidity, on Sui packages should interact with CCTP V1 packages in a more composable way. Third party packages should follow the `Ticket` pattern with a dedicated and private `Auth` struct as described below. #### Private Auth Structs `Auth` structs are used throughout the CCTP V1 packages in functions intended to be called from other dependent packages. The `auth_caller_identifier()` function is used to uniquely identify other packages by hashing the full object type of the type passed in. Any struct that implements the drop trait can be used as an authenticator, but it is recommended to use a dedicated auth struct. Calling contracts should be careful to not expose these structs to the public or else messages from their package could be forged. An example can be found in `TokenMessengerMinter` [on GitHub](https://github.com/circlefin/sui-cctp/blob/004950f742a161b6acfe2331630233ac3de0f3ad/packages/token_messenger_minter/sources/message_transmitter_authenticator.move). #### Ticket Pattern The `Ticket` pattern is a pattern used in CCTP-Sui that enables the composability of CCTP V1 with third-party packages. The pattern enables a third-party integrator (package) to create a `Ticket` ("hot potato") for a designated operation directly in their package without having to upgrade their packages with future CCTP V1 upgrades. Only PTBs would need to be updated. `Ticket` structs contain parameters for specific interactions with CCTP V1 packages. They can only be created from and consumed by the CCTP V1 packages in a specific interaction, and do not have drop or store abilities, so must be used in the PTB where they are created. They also contain an `Auth` field that should only be created by the third-party package. The calling PTB should handle the `Ticket` by calling the relevant CCTP V1 package, which will recognize the third-party integrator as the action initiator. The following public functions (designed for third-party integrators, EOAs should use the entry versions) are implemented following the `Ticket` pattern. Each of them creates or consumes their own specific `Ticket` type: **`message_transmitter`:** * `receive_message_with_package_auth()` * `stamp_receipt()` **`token_messenger_minter`:** * `deposit_for_burn_with_package_auth()` * `deposit_for_burn_with_caller_with_package_auth()` * `replace_deposit_for_burn_with_package_auth()` For example, a typical workflow in a PTB to replace a deposit by an integrator would be: 1. The integrating package calls `create_replace_deposit_for_burn_ticket()` with an `Auth` struct it defined, and returns this ticket. 2. The PTB calls `deposit_for_burn_with_caller_with_package_auth()` with the ticket on behalf of the integrator. 3. `token_messenger_minter` will validate if the type hash of `Auth` matches the original sender in the burn message. #### PTB Function Call Ordering Due to the composability of Sui and PTBs, along with the `Ticket` pattern, there is no guarantee of ordering of calls within PTBs. The `Ticket` pattern introduces behaviors similar to asynchronous functions in ordinary programming contexts: when an integrator creates a ticket and returns it to the PTB, it is signaling an intention to execute the logic function, and the properties of the Move type system carry the guarantee that the function will indeed be eventually executed before the end of the transaction. However, no guarantee is given regarding the relative order of execution: the PTB is free to consume the tickets in any order it sees fit. While this has no security implications on the internal coherence of CCTP V1 itself, integrators should carefully evaluate whether their own logic is somehow dependent on a specific order of execution of the CCTP V1 functions. For example, a PTB could create 5 `DepositForBurnTicket` structs and execute them in any order. Similarly on the Sui destination side, 5 messages could be received in `MessageTransmitter`, and then received (and thus the USDC minted) in `TokenMessengerMinter` in a completely different order. If any pre or post actions are taken in third party packages, these could also come in an unexpected ordering, so this scenario should be handled accordingly in third party packages. #### Ticket Pattern Examples An example of this with receiving `deposit_for_burn()` messages on Sui can be seen below. This example assumes the `destination_caller` for the message is set to the auth address for your package's `Auth` struct. ```javascript JavaScript theme={null} // Prepare the ReceiveMessageTicket by calling create_receive_message_ticket() from within your package. let receive_msg_ticket = your_package::prepare_receive_message_ticket(message, attestation); // Receive the message on MessageTransmitter. let receipt = message_transmitter::receive_message_with_package_auth(receive_msg_ticket, ...); // Pass the Receipt into TokenMessengerMinter to mint the USDC. let ticket_with_burn_message = token_messenger_minter::handle_receive_message(receipt, ...); // In your package you can call deconstruct_stamp_receipt_ticket_with_burn_message to deconstruct the ticket // and burn_message and securely take some action with the burn_message (e.g. swap some tokens, send them somewhere, etc.) let stamp_receipt_ticket = your_package::take_some_action(ticket_with_burn_message, ...); // Stamp the receipt let stamped_receipt = message_transmitter::stamp_receipt(stamp_receipt_ticket); // Complete the message and destroy the StampedReceipt message_transmitter::complete_receive_message(stamped_receipt); ``` A similar example can be seen on the `deposit_for_burn()` side: ```javascript JavaScript theme={null} // Prepare the DepositForBurnWithCallerTicket by calling create_deposit_for_burn_with_caller_with_package_auth // directly from your package with the input parameters and your Auth struct. Integrators can also take other // actions here as needed. let deposit_for_burn_ticket = your_package::prepare_deposit_for_burn_ticket(coins, ...); // Call deposit for burn and burn the USDC let (burn_message, message) = token_messenger_minter::deposit_for_burn_with_caller_with_package_auth( deposit_for_burn_ticket, ... ); // Optionally, take some other action in your package based on the output message. // Note that BurnMessage and Message have the copy ability so the possibility of them being copied should be // handled in third party packages if post-actions are needed. your_package::post_deposit_for_burn(burn_message, message, ...); ``` # CCTP chain domains V1 Source: https://developers.circle.com/cctp/v1/supported-domains Mapping of supported CCTP V1 domains <Warning> **This is CCTP V1 version. For the latest version, see [CCTP](/cctp)**. </Warning> A **domain** is a Circle-issued identifier for a blockchain where CCTP V1 contracts are deployed. Domains do not map to any existing public chain ID. ## CCTP V1 Domains | Domain | Name | | :- | :- | | 0 | Ethereum | | 1 | Avalanche | | 2 | OP | | 3 | Arbitrum | | 4 | Noble | | 5 | Solana | | 6 | Base | | 7 | Polygon PoS | | 8 | Sui | | 9 | Aptos | | 10 | Unichain | # Transfer USDC on devnet between Solana and other chains using CCTP V1 Source: https://developers.circle.com/cctp/v1/transfer-testnet-usdc-between-solana-devnet Explore this tutorial for transferring USDC between Solana devnet and other testnets via CCTP V1 <Warning> **This is CCTP V1 version. For the latest version, see [CCTP](/cctp)**. </Warning> To get started with CCTP V1 on Solana devnet, follow the [example scripts](https://github.com/circlefin/solana-cctp-contracts/tree/master/examples). The examples use [Solana web3.js](https://www.npmjs.com/package/@solana/web3.js) and [Anchor](https://www.npmjs.com/package/@project-serum/anchor) to transfer USDC to and from an account on Solana devnet and an address on an external blockchain. As a security measure, these scripts should only be used for devnet testing. You should not reuse private keys across devnet and mainnet. # Transfer USDC on testnet between Aptos and Base using CCTP V1 Source: https://developers.circle.com/cctp/v1/transfer-usdc-on-testnet-from-aptos-to-base Explore this tutorial for transferring USDC between Aptos testnet and Base Sepolia Testnet via CCTP V1 <Warning> **This is CCTP V1 version. For the latest version, see [CCTP](/cctp)**. </Warning> To get started with CCTP V1 on Aptos testnet, follow the example scripts provided [on GitHub](https://github.com/circlefin/aptos-cctp/tree/master/typescript/example). The examples use the [Aptos SDK](https://www.npmjs.com/package/@aptos-labs/ts-sdk), to transfer USDC to and from an address on Aptos testnet and an address on an external blockchain. <Warning> **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. </Warning> Summary of calling `deposit_for_burn()` (full runnable script can be found in the aptos-cctp repository): ```ts theme={null} // Aptos Testnet Stablecoin object const BURN_TOKEN = "0x69091fbab5f7d635ee7ac5098cf0c1efbe31d68fec0f2cd565e8d168daf52832"; const aptosClient = new Aptos(new AptosConfig({ network: Network.TESTNET })); const userAccount = Account.fromPrivateKey({ privateKey: new Ed25519PrivateKey(APTOS_PRIVATE_KEY), }); // Create a transaction with deposit for burn script const buffer = readFileSync( "typescript/example/precompiled-move-scripts/testnet/deposit_for_burn.mv", ); const bytecode = Uint8Array.from(buffer); const amount = new U64(1); const destinationDomain = new U32(6); const burnToken = AccountAddress.from(BURN_TOKEN); const mintRecipient = AccountAddress.from(evmSigner.address); const functionArguments: Array<any> = [ amount, destinationDomain, mintRecipient, burnToken, ]; const transaction = await aptosClient.transaction.build.simple({ sender: userAccount.accountAddress, data: { bytecode, functionArguments, }, }); const pendingTxn = await aptosClient.signAndSubmitTransaction({ signer: userAccount, transaction, }); const depositForBurnTx = await aptosClient.waitForTransaction({ transactionHash: pendingTxn.hash, }); console.log( `Deposit for burn transaction completed successfully: https://explorer.aptoslabs.com/txn/${depositForBurnTx.hash}`, ); // Fetch the event data from the transaction const messageSentEvent = ( depositForBurnTx as UserTransactionResponse ).events.find( (e: any) => e.type === `${MESSAGE_TRANSMITTER_PACKAGE_ID}::message_transmitter::MessageSent`, ); ``` Summary of calling `receive_message()` (full runnable script can be found in the aptos-cctp repository): ```ts theme={null} const bytecode = Uint8Array.from( fs.readFileSync( "typescript/example/precompiled-move-scripts/testnet/handle_receive_message.mv", ), ); const functionArguments: Array<any> = [ MoveVector.U8(messageBytes as Buffer), MoveVector.U8(attestationSignature), ]; const transaction = await aptosClient.transaction.build.simple({ sender: userAccount.accountAddress, data: { bytecode, functionArguments, }, }); const pendingTxn = await aptosClient.signAndSubmitTransaction({ signer: userAccount, transaction, }); const receiveMessageTx = await aptosClient.waitForTransaction({ transactionHash: pendingTxn.hash, }); console.log( `Receive message transaction completed successfully: https://explorer.aptoslabs.com/txn/${receiveMessageTx.hash}`, ); ``` # Transfer USDC on testnet from Ethereum to Avalanche using CCTP V1 Source: https://developers.circle.com/cctp/v1/transfer-usdc-on-testnet-from-ethereum-to-avalanche Explore this script to transfer USDC on testnet between two EVM-compatible chains via CCTP V1 <Warning> **This is CCTP V1 version. For the latest version, see [CCTP](/cctp)**. </Warning> This guide demonstrates how to use the [viem](https://viem.sh/) framework and the [CCTP V1 API](/cctp/v1/cctp-apis) in a simple script that enables a user to transfer USDC from a wallet address on the **Ethereum Sepolia testnet** to another wallet address on the **Avalanche Fuji testnet**. To get started with CCTP V1, follow the example script provided [on GitHub](https://github.com/circlefin/evm-cctp-contracts/blob/d1c24577fb627b08483dc42e4d8a37a810b369f7/docs/index.js). The example uses [web3.js](https://web3js.readthedocs.io/en/v1.8.1/getting-started.html) to transfer USDC from a wallet address on Ethereum Sepolia testnet to another wallet address on Avalanche Fuji testnet. The script has five steps: 1. In this first step, you initiate a transfer of USDC from one blockchain to another, and specify the recipient wallet address on the destination chain. This step approves the Ethereum Sepolia **TokenMessenger** contract to withdraw USDC from the provided Ethereum Sepolia wallet address. ```javascript JavaScript theme={null} const approveTx = await usdcEthContract.methods .approve(ETH_TOKEN_MESSENGER_CONTRACT_ADDRESS, amount) .send({ gas: approveTxGas }); ``` 2. In this second step, you facilitate a burn of the specified amount of USDC on the source chain. This step executes the `depositForBurn` function on the Ethereum Sepolia **TokenMessenger** contract deployed on [Sepolia testnet](https://sepolia.etherscan.io/address/0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5). ```javascript JavaScript theme={null} const burnTx = await ethTokenMessengerContract.methods .depositForBurn( amount, AVAX_DESTINATION_DOMAIN, destinationAddressInBytes32, USDC_ETH_CONTRACT_ADDRESS, ) .send(); ``` 3. In this third step, you make sure you have the correct message and hash it. This step extracts `messageBytes` emitted by the **MessageSent** event from `depositForBurn` transaction logs and hashes the retrieved `messageBytes` using the **keccak256** hashing algorithm. ```javascript JavaScript theme={null} const transactionReceipt = await web3.eth.getTransactionReceipt( burnTx.transactionHash, ); const eventTopic = web3.utils.keccak256("MessageSent(bytes)"); const log = transactionReceipt.logs.find((l) => l.topics[0] === eventTopic); const messageBytes = web3.eth.abi.decodeParameters(["bytes"], log.data)[0]; const messageHash = web3.utils.keccak256(messageBytes); ``` 4. In this fourth step, you request the attestation from Circle, which provides authorization to mint the specified amount of USDC on the destination chain. This step polls the attestation service to acquire the signature using the `messageHash` from the previous step. <Note> **Rate Limit** The attestation service rate limit is 35 requests per second. If you exceed 35 requests per second, the service blocks all API requests for the next 5 minutes and returns an HTTP 429 response. </Note> ```javascript JavaScript theme={null} 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)); } ``` 5. In this final step, you enable USDC to be minted on the destination chain. This step calls the `receiveMessage` function on the Avalanche Fuji **MessageTransmitter** contract to receive USDC at the Avalanche Fuji wallet address. ```javascript JavaScript theme={n
프로젝트로 돌아가기