bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 75쪽 중 65쪽
n chain, the event account can be closed and have the SOL reclaimed to
the `event_rent_payer` account. This is done by calling the
[`reclaim_event_account`](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/instructions/reclaim_event_account.rs)
instruction. This can only be called by the `event_rent_payer` account from when
the message was sent.
Details on the message format can be found on the
[Message Format page](/cctp/v1/message-format).
</Note>
### [depositForBurnWithCaller](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/token-messenger-minter/src/token_messenger/instructions/deposit_for_burn_with_caller.rs)
The same as `depositForBurn` but with an additional parameter,
`destinationCaller`. This parameter specifies which address has permission to
call `receiveMessage` on the destination domain for the message.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| amount | u64 | Amount of tokens to deposit and burn. |
| destinationDomain | u32 | Destination domain identifier. |
| mintRecipient | Pubkey | Public Key of mint recipient on destination domain. *Address should be converted to base58.* See \[Mint Recipient for Solana as Source Chain Transfers]\(Mint Recipient for Solana as Source Chain Transfers) |
| destinationCaller | Pubkey | Public Key of caller on destination domain. *Address should be converted to base58.* |
### [replaceDepositForBurn](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/token-messenger-minter/src/token_messenger/instructions/replace_deposit_for_burn.rs)
Replace a `BurnMessage` to change the mint recipient and/or destination caller.
Allows the sender of a previous `BurnMessage` (created by `depositForBurn` or
`depositForBurnWithCaller`) to send a new `BurnMessage` to replace the original.
The new `BurnMessage` will reuse the amount and burn token of the original,
without requiring a new deposit.
This is useful in situations where the user specified an incorrect address and
has no way to safely mint the previously burned USDC.
<Note>
**Note on replaceDepositForBurn**
Only the owner account of the original depositForBurn has access to call
replaceDepositForBurn. The resulting mint will supersede the original mint, as
long as the original mint has not confirmed yet onchain. When using a
third-party app/bridge that integrates with CCTP V1 to burn and mint USDC, it is
the choice of the app/bridge if and when to replace messages on behalf of users.
When sending USDC to smart contracts, be aware of the functionality that those
contracts have and their respective trust model.
</Note>
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| originalMessage | Vec\<u8> | Original message bytes (to replace). |
| originalAttestation | Vec\<u8> | Original attestation bytes. |
| newDestinationCaller | Pubkey | The new destination caller, which may be the same as the original destination caller, a new destination caller, or an empty destination caller, indicating that any destination caller is valid. *Address should be converted to base58.* |
| newMintRecipient | Pubkey | The new mint recipient, which may be the same as the original mint recipient, or different. *Address should be converted to base58.* |
### MessageTransmitter
### [receiveMessage](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/instructions/receive_message.rs)
Messages with a given nonce can only be broadcast successfully once for a pair
of domains. The message body of a valid message is passed to the specified
recipient for further processing.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| message | Vec\<u8> | Message bytes. |
| attestation | Vec\<u8> | Signed attestation of message. |
**Remaining Accounts**
If the `receiveMessage` instruction is being called with a deposit for burn
message that will be received by the `TokenMessengerMinter`, additional
`remainingAccounts` are required so they can be passed with the CPI to
`TokenMessengerMinter#handleReceiveMessage`:
| Account Name | PDA Seeds | PDA ProgramId | isSigner? | isWritable? | Description |
| :- | :- | :- | :- | :- | :- |
| `token_messenger` | `["token_messenger"]` | tokenMessengerMinter | false | false | TokenMessenger Program Account |
| `remote_token_messenger` | `["remote_token_messenger", sourceDomainId]` | tokenMessengerMinter | false | false | Remote token messenger account where the remote token messenger address is stored for the given source domain id |
| `token_minter` | `["token_minter"]` | tokenMessengerMinter | false | true | TokenMinter Program Account |
| `local_token` | `["local_token", localTokenMint.publicKey]` | tokenMessengerMinter | false | true | Local token account where the information for the local token (e.g. USDCSOL) being minted is stored |
| `token_pair` | `["token_pair", sourceDomainId, sourceTokenInBase58]` | tokenMessengerMinter | false | false | Token pair account where the info for the local and remote tokens are stored. `sourceTokenInBase58` is the remote token that was burned converted into base58 format. |
| `user_token_account` | N/A | N/A | false | true | User token account that will receive the minted tokens. This address **must** match the mintRecipient from the source chain depositForBurn call. |
| `custody_token_account` | `["custody", localTokenMint.publicKey]` | tokenMessengerMinter | false | true | Custody account that holds the pre-minted USDCSOL that can be minted for CCTP V1 usage. |
| `SPL.token_program_id` | N/A | N/A | false | false | The native SPL token program ID. |
| `token_program_event_authority` | `["__event_authority"]` | tokenMessengerMinter | false | false | Event authority account for the TokenMessengerMinter program. Needed to emit Anchor CPI events. |
| `program` | N/A | N/A | false | false | Program id for the TokenMessengerMinter program. |
### [sendMessage](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/instructions/send_message.rs)
Sends a message to the destination domain and recipient. Emits a `MessageSent`
event which will be attested by Circle's attestation service.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| destinationDomain | u32 | Destination domain identifier. |
| recipient | Pubkey | Address to handle message body on destination domain. |
| messageBody | Vec\<u8> | Application-specific message to be handled by recipient. |
### [sendMessageWithCaller](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/instructions/send_message_with_caller.rs)
Same as `sendMessage` but with an additional parameter, `destinationCaller`.
This parameter specifies which address has permission to call `receiveMessage`
on the destination domain for the message.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| destinationDomain | u32 | Destination domain identifier. |
| recipient | Pubkey | Address of message recipient on destination domain. |
| destinationCaller | Pubkey | Address of caller on the destination domain. *Address should be converted to base58.* |
| messageBody | Vec\<u8> | Application-specific message to be handled by recipient. |
### [replaceMessage](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/instructions/replace_message.rs)
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.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| originalMessage | Vec\<u8> | Original message to replace. |
| originalAttestation | Vec\<u8> | Attestation of originalMessage. |
| newMessageBody | Vec\<u8> | New message body of replaced message. |
| newDestinationCaller | Pubkey | The new destination caller, which may be the same as the original destination caller, a new destination caller, or an empty destination caller (bytes32(0) or `PublicKey.default`, indicating that any destination caller is valid). *Address should be converted to base58.* |
### [reclaimEventAccount](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/instructions/reclaim_event_account.rs)
Closes the given event account and reclaims the paid rent in SOL back to the
`event_rent_payer` account. This instruction can only be called by the
`event_rent_payer` account that paid the rent when the message was sent.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| attestation | Vec\<u8> | Valid attestation for the message stored in the account. This is required to ensure the attestation service has processed and stored the message before it is deleted. |
<Warning>
**Warning**
Once this instruction is executed for a message account, the message can no
longer be read onchain. We recommend not calling this instruction until the
message has been received on the destination chain. If the message is lost
before receiving the message, it can be fetched from the attestation service
using the [messages endpoint](/api-reference/cctp/all/get-messages).
</Warning>
## Additional Notes
### Mint Recipient for Solana as Destination Chain Transfers
When calling `depositForBurn` on a non-Solana chain with Solana as the
destination, the `mintRecipient` should be a **hex encoded USDC token account
address**. The token account\* must exist at the time `receiveMessage` is called
on Solana\* or else this instruction will revert. An example of converting an
address from Base58 to hex taken from the Solana quickstart tutorial in
TypeScript can be seen below:
```typescript TypeScript theme={null}
import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes";
import { hexlify } from "ethers";
const solanaAddressToHex = (solanaAddress: string): string =>
hexlify(bs58.decode(solanaAddress));
```
### Mint Recipient for Solana as Source Chain Transfers
When specifying the `mintRecipient` for Solana `deposit_for_burn` instruction
calls, the address must be given as the 32 byte version of the hex address in
base58 format. An example taken from the Solana quickstart tutorial in
TypeScript can be seen below:
```typescript TypeScript theme={null}
import { getBytes } from "ethers";
import { PublicKey } from "@solana/web3.js";
const evmAddressToBytes32 = (address: string): string =>
`0x000000000000000000000000${address.replace("0x", "")}`;
const evmAddressToBase58PublicKey = (addressHex: string): PublicKey =>
new PublicKey(getBytes(evmAddressToBytes32(addressHex)));
```
### Program Events
Program events like
[DepositForBurn](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/token-messenger-minter/src/token_messenger/events.rs#L35-L45)
,
[MintAndWithdraw](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/token-messenger-minter/src/token_messenger/events.rs#L47-L52)
, and
[MessageReceived](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/token-messenger-minter/src/token_messenger/events.rs#L47-L52)
are emitted as Anchor CPI events. This means a self-CPI is made into the program
with the serialized event as instruction data so it is persisted in the
transaction and can be fetched later on as needed. More information can be seen
in the
[Anchor implementation PR](https://github.com/coral-xyz/anchor/pull/2438), and
an example of reading CPI events can be seen in the
[solana-cctp-contracts repository](https://github.com/circlefin/solana-cctp-contracts/blob/master/tests/utils.ts#L62-L111).
[MessageSent](https://github.com/circlefin/solana-cctp-contracts/blob/master/programs/message-transmitter/src/events.rs#L49-L55)
events are different, as they are stored in accounts. Please see the
[MessageSent Event Storage section](#depositforburn) for more info.
# CCTP Sui packages and interfaces V1
Source: https://developers.circle.com/cctp/v1/sui-packages
Packages for CCTP V1 support on the Sui blockchain
<Warning>
**This is CCTP V1 (Legacy) version. For the latest version, see [CCTP](/cctp)**.
</Warning>
## Overview
The CCTP V1 Sui smart contract implementation is written in
[Move](https://sui.io/move). The Sui 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 Sui packages reflect the EVM counterparts as closely
as possible.
There are a few key differences with Sui packages from EVM and other CCTP V1
implementations:
### Receive Message Flow
Since the Move language does not have interfaces, the
`message_transmitter::receive_message()` function cannot call directly into the
receiver package (e.g. `TokenMessenger` for USDC transfers). The workaround for
this limitation is that callers of `receive_message()` must also atomically (in
the same
[Programmable Transaction Block (PTB)](https://docs.sui.io/concepts/transactions/prog-txn-blocks))
call into the receiver package's `handle_receive_message()` function with a
`Receipt` struct, call `stamp_receipt()` with the `StampReceiptTicket` struct
returned from `handle_receive_message()`, and then pass the `StampedReceipt`
back into the `message_transmitter::complete_receive_message()` function to
complete the message and destroy the `Receipt` object. This flow ensures
atomicity and guarantees message receipt by the receiver packages. Please see
the interface and examples below for more information on this flow.
### Interacting with TokenMessengerMinter from other Packages
On Sui, when a package is upgraded, the new version is deployed with a new
package ID. This means if another package is directly calling a version-gated
function, when the package is upgraded, the dependent packages must also be
upgraded. To address this, all CCTP V1 functions that are intended to be called
from a dependent package follow a `Ticket` struct pattern. In this pattern, the
dependent package can call non version-gated `create_ticket()` functions with
the intended function parameters, including an `Auth` struct (used to uniquely
identify the package), and receive back a `Ticket` struct. This struct can then
be returned from the dependent package and used in a PTB to call the intended
CCTP V1 function. This allows integrators to securely integrate with CCTP V1
functions from their packages, and only have to update PTBs when CCTP V1
packages are upgraded rather than having to upgrade their packages as well. For
more information, see the functions below with the `_with_package_auth` suffix.
### Testnet
#### Package IDs
| Package | [Domain](/cctp/v1/supported-domains) | Address |
| :- | :- | :- |
| MessageTransmitter | 8 | `0x4931e06dce648b3931f890035bd196920770e913e43e45990b383f6486fdd0a5` |
| TokenMessengerMinter | 8 | `0x31cc14d80c175ae39777c0238f20594c6d4869cfab199f40b69f3319956b8beb` |
#### Shared Object IDs
| Object | Object ID |
| :- | :- |
| MessageTransmitterState | `0x98234bd0fa9ac12cc0a20a144a22e36d6a32f7e0a97baaeaf9c76cdc6d122d2e` |
| TokenMessengerMinterState | `0x5252abd1137094ed1db3e0d75bc36abcd287aee4bc310f8e047727ef5682e7c2` |
| USDC Treasury Object | `0x7170137d4a6431bf83351ac025baf462909bffe2877d87716374fb42b9629ebe` |
Branch with testnet
[Automated Address Management](https://docs.sui.io/develop/manage-packages/move-package-management):
[github.com/circlefin/sui-cctp/tree/testnet](https://github.com/circlefin/sui-cctp/tree/testnet).
### Mainnet
#### Package IDs
| Package | [Domain](/cctp/v1/supported-domains) | Address |
| :- | :- | :- |
| MessageTransmitter | 8 | `0x08d87d37ba49e785dde270a83f8e979605b03dc552b5548f26fdf2f49bf7ed1b` |
| TokenMessengerMinter | 8 | `0x2aa6c5d56376c371f88a6cc42e852824994993cb9bab8d3e6450cbe3cb32b94e` |
#### Shared Object IDs
| Object | Object ID |
| :- | :- |
| MessageTransmitterState | `0xf68268c3d9b1df3215f2439400c1c4ea08ac4ef4bb7d6f3ca6a2a239e17510af` |
| TokenMessengerMinterState | `0x45993eecc0382f37419864992c12faee2238f5cfe22b98ad3bf455baf65c8a2f` |
| USDC Treasury Object | `0x57d6725e7a8b49a7b2a612f6bd66ab5f39fc95332ca48be421c3229d514a6de7` |
Branch with mainnet
[Automated Address Management](https://docs.sui.io/develop/manage-packages/move-package-management):
[github.com/circlefin/sui-cctp/tree/mainnet](https://github.com/circlefin/sui-cctp/tree/mainnet).
## Interface
The Sui CCTP V1 source code is
[available on GitHub](https://github.com/circlefin/sui-cctp/).
The interface below serves as a reference for permissionless messaging functions
exposed by the programs.
### TokenMessengerMinter
#### [deposit\_for\_burn](https://github.com/circlefin/sui-cctp/blob/004950f742a161b6acfe2331630233ac3de0f3ad/packages/token_messenger_minter/sources/deposit_for_burn.move#L106)
Burns passed in tokens from sender to be minted on destination domain. Minted
tokens will be transferred to `mint_recipient` on the destination chain. The
`deposit_for_burn` interface and functionality is very similar to the EVM
implementation. The coins parameter is the key difference due to how passing
tokens around on Sui works. `message_transmitter_state`, `deny_list`, and
`treasury` parameters are all shared objects.
**Remarks:**
* Intended to be called directly by EOA (rather than a dependent package). The
initiating EOA will be the "owner" (e.g. message sender) of the message and
have the ability to call `replace_deposit_for_burn()` to update the
`mint_recipient` or `destination_caller`. If the calling EOA is not trusted by
the mint recipient or destination caller,
`deposit_for_burn_with_package_auth()` should be called instead with the
integrating package owning the message.
* The generic type T is the coin's one-time witness
([OTW](https://move-book.com/programmability/one-time-witness.html)) type for
the specific coin type to be burned.
* `BurnMessage` and `Message` structs are returned, but it is not required to do
anything with these structs; they are returned for convenience.
**Parameters**
| Field | Type | Description |
| :- | :- | :- |
| coins | `Coin<T>` | Coin of type T to be burned. Full amount in coins will be burned. |
| destination\_domain | `u32` | Destination domain identifier. |
| mint\_recipient | `address` | Address of mint recipient on destination domain. *Note: If destination is a non-Move chain,* `mint_recipient` *address should be converted to hex and passed in using the @0x123 address format.* |
| state | `&State` | Shared State object for the `TokenMessengerMinter` package. |
| message\_transmitter\_state | `&mut MessageTransmitterState` | Shared State object for the `MessageTransmitter` package. |
| deny\_list | `&DenyList` | DenyList shared object for the stablecoin token T. Constant address: `0x403` |
| treasury | `&mut Treasury<T>` | Treasury shared object for the stablecoin token T. |
| ctx | `&TxContext` | `TxContext` for the transaction. |
#### [deposit\_for\_burn\_with\_package\_auth](https://github.com/circlefin/sui-cctp/blob/004950f742a161b6acfe2331630233ac3de0f3ad/packages/token_messenger_minter/sources/deposit_for_burn.move#L143)
Same as `deposit_for_burn()`, but intended to be called with an `Auth` struct
from a dependent package. The calling package will be the "owner" (e.g.
message\_sender) of the message and have the ability to call
`replace_deposit_for_burn_with_package_auth()` to update the mint recipient or
destination caller. This would be similar to a wrapper contract on EVM chains
calling into `TokenMessenger` and being the message sender. Direct callers
(where EOAs are trusted and should be the owner) should use `deposit_for_burn()`
instead.
**Remarks:**
* This function uses a `DepositForB프로젝트로 돌아가기