Circle USYC 백서

Circle USYC

원문

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

": "refundAddress", "type": "address", "internalType": "address payable" } ] } ], "outputs": [], "stateMutability": "payable" }, { "type": "function", "name": "feeManager", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "getFee", "inputs": [ { "name": "signedQuote", "type": "bytes", "internalType": "bytes" } ], "outputs": [ { "name": "totalFee", "type": "uint256", "internalType": "uint256" }, { "name": "feeToken", "type": "address", "internalType": "address" } ], "stateMutability": "pure" }, { "type": "function", "name": "getFee", "inputs": [ { "name": "decodedQuote", "type": "tuple", "internalType": "struct IFeeManager.Quote", "components": [ { "name": "items", "type": "tuple[]", "internalType": "struct IFeeManager.QuoteItem[]", "components": [ { "name": "quoteType", "type": "string", "internalType": "string" }, { "name": "quoteArgsHash", "type": "bytes32", "internalType": "bytes32" }, { "name": "amount", "type": "uint256", "internalType": "uint256" } ] }, { "name": "expiry", "type": "uint256", "internalType": "uint256" }, { "name": "feeToken", "type": "address", "internalType": "address" }, { "name": "nonce", "type": "uint256", "internalType": "uint256" }, { "name": "signature", "type": "bytes", "internalType": "bytes" } ] } ], "outputs": [ { "name": "totalFee", "type": "uint256", "internalType": "uint256" }, { "name": "feeToken", "type": "address", "internalType": "address" } ], "stateMutability": "pure" }, { "type": "function", "name": "initialize", "inputs": [ { "name": "owner_", "type": "address", "internalType": "address" } ], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "owner", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "pendingOwner", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "proxiableUUID", "inputs": [], "outputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], "stateMutability": "view" }, { "type": "function", "name": "renounceOwnership", "inputs": [], "outputs": [], "stateMutability": "pure" }, { "type": "function", "name": "tokenMessenger", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "transferOwnership", "inputs": [ { "name": "newOwner", "type": "address", "internalType": "address" } ], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "upgradeToAndCall", "inputs": [ { "name": "newImplementation", "type": "address", "internalType": "address" }, { "name": "data", "type": "bytes", "internalType": "bytes" } ], "outputs": [], "stateMutability": "payable" }, { "type": "event", "name": "Initialized", "inputs": [ { "name": "version", "type": "uint64", "indexed": false, "internalType": "uint64" } ], "anonymous": false }, { "type": "event", "name": "OwnershipTransferStarted", "inputs": [ { "name": "previousOwner", "type": "address", "indexed": true, "internalType": "address" }, { "name": "newOwner", "type": "address", "indexed": true, "internalType": "address" } ], "anonymous": false }, { "type": "event", "name": "OwnershipTransferred", "inputs": [ { "name": "previousOwner", "type": "address", "indexed": true, "internalType": "address" }, { "name": "newOwner", "type": "address", "indexed": true, "internalType": "address" } ], "anonymous": false }, { "type": "event", "name": "Upgraded", "inputs": [ { "name": "implementation", "type": "address", "indexed": true, "internalType": "address" } ], "anonymous": false }, { "type": "error", "name": "AddressEmptyCode", "inputs": [ { "name": "target", "type": "address", "internalType": "address" } ] }, { "type": "error", "name": "DenylistedCaller", "inputs": [ { "name": "caller", "type": "address", "internalType": "address" } ] }, { "type": "error", "name": "ERC1967InvalidImplementation", "inputs": [ { "name": "implementation", "type": "address", "internalType": "address" } ] }, { "type": "error", "name": "ERC1967NonPayable", "inputs": [] }, { "type": "error", "name": "EmptyHookData", "inputs": [] }, { "type": "error", "name": "FailedCall", "inputs": [] }, { "type": "error", "name": "ForwardFeeWithoutHook", "inputs": [] }, { "type": "error", "name": "ForwardHookWithoutFee", "inputs": [] }, { "type": "error", "name": "InvalidInitialization", "inputs": [] }, { "type": "error", "name": "NotInitializing", "inputs": [] }, { "type": "error", "name": "OwnableInvalidOwner", "inputs": [ { "name": "owner", "type": "address", "internalType": "address" } ] }, { "type": "error", "name": "OwnableUnauthorizedAccount", "inputs": [ { "name": "account", "type": "address", "internalType": "address" } ] }, { "type": "error", "name": "RemoteTokenMessengerNotSet", "inputs": [ { "name": "destinationDomain", "type": "uint32", "internalType": "uint32" } ] }, { "type": "error", "name": "RenounceDisabled", "inputs": [] }, { "type": "error", "name": "SafeERC20FailedOperation", "inputs": [ { "name": "token", "type": "address", "internalType": "address" } ] }, { "type": "error", "name": "SignedQuoteDecodeFailed", "inputs": [] }, { "type": "error", "name": "StringsInsufficientHexLength", "inputs": [ { "name": "value", "type": "uint256", "internalType": "uint256" }, { "name": "length", "type": "uint256", "internalType": "uint256" } ] }, { "type": "error", "name": "TooManyQuoteItems", "inputs": [ { "name": "length", "type": "uint256", "internalType": "uint256" } ] }, { "type": "error", "name": "UUPSUnauthorizedCallContext", "inputs": [] }, { "type": "error", "name": "UUPSUnsupportedProxiableUUID", "inputs": [{ "name": "slot", "type": "bytes32", "internalType": "bytes32" }] }, { "type": "error", "name": "UnsupportedQuoteVersion", "inputs": [{ "name": "version", "type": "uint8", "internalType": "uint8" }] }, { "type": "error", "name": "ZeroFeeManagerAddress", "inputs": [] }, { "type": "error", "name": "ZeroTokenMessengerAddress", "inputs": [] } ] ``` </Accordion> Burns through `TokenMessengerWithFees` emit the standard [`DepositForBurn`](#depositforburn-event) and [`MessageSent`](#messagesent) events from the underlying contracts. ## Events ### DepositForBurn Emitted when USDC is burned on the source blockchain. **Parameters** | Field | Type | Indexed | Description | | - | - | - | - | | `nonce` | `uint64` | Yes | Unique message identifier | | `burnToken` | `address` | Yes | Address of token burned | | `amount` | `uint256` | No | Burn amount | | `depositor` | `address` | Yes | Address of depositor | | `mintRecipient` | `bytes32` | No | Mint recipient address on destination domain | | `destinationDomain` | `uint32` | No | Destination domain identifier | | `destinationTokenMessenger` | `bytes32` | No | Address of TokenMessenger contract on destination domain | | `destinationCaller` | `bytes32` | No | Authorized caller of `receiveMessage` on destination domain | | `maxFee` | `uint256` | No | Maximum fee for the transfer | | `minFinalityThreshold` | `uint32` | No | Minimum finality threshold at which burn will be attested | ### MessageSent Emitted when a message is sent from the source blockchain. **Parameters** | Field | Type | Indexed | Description | | - | - | - | - | | `message` | `bytes` | No | Raw bytes of message | ### MessageReceived Emitted when a message is received on the destination blockchain. **Parameters** | Field | Type | Indexed | Description | | - | - | - | - | | `caller` | `address` | Yes | Address that called `receiveMessage` | | `sourceDomain` | `uint32` | Yes | Source domain identifier | | `nonce` | `uint64` | Yes | Unique message identifier | | `sender` | `bytes32` | No | Address of message sender on source domain | | `messageBody` | `bytes` | No | Message body | ### MintAndWithdraw Emitted when USDC is minted on the destination blockchain. **Parameters** | Field | Type | Indexed | Description | | - | - | - | - | | `mintRecipient` | `address` | Yes | Address receiving minted USDC | | `amount` | `uint256` | No | Amount minted | | `mintToken` | `address` | Yes | Address of token minted | # CoreDepositWallet contract interface Source: https://developers.circle.com/cctp/references/coredepositwallet-contract-interface The `CoreDepositWallet` contract on HyperEVM allows you to deposit USDC from HyperEVM to HyperCore. This topic describes the contract interface and the available deposit functions. <Warning> To move USDC from HyperEVM to HyperCore, always call one of the `CoreDepositWallet` deposit functions (`deposit`, `depositFor`, or `depositWithAuth`). Only USDC is supported. Sending USDC or any tokens directly to the `CoreDepositWallet` contract address doesn't trigger a deposit on HyperCore. The funds are permanently stuck. </Warning> ## Deposit functions The `CoreDepositWallet` provides three entry points for depositing USDC from HyperEVM into HyperCore. All deposits credit a user's balance on HyperCore, on either the perps or spot DEX. ### `deposit` function The `deposit` function transfers USDC from the caller's address and credits the same address on HyperCore, after the caller has approved the `CoreDepositWallet` to spend their tokens. **Signature:** ```solidity theme={null} deposit(uint256 amount, uint32 destinationDex); ``` **Parameters:** | Parameter | Value | | - | - | | `amount` | The USDC amount being deposited from HyperEVM to HyperCore | | `destinationDex` | The HyperCore destination `dex` index. Accepted values are:<br />- `0` → default perps DEX<br />- `type(uint32).max` → spot DEX | **Token pull:** Uses `transferFrom(msg.sender, address(this), amount)` → requires prior ERC-20 approve from the `msg.sender` to the core deposit wallet. **Who is credited:** The `msg.sender` of the transaction on HyperEVM is credited on HyperCore. **Examples:** ERC-20 Approval: ```shell theme={null} # approve the CoreDepositWallet to spend 100 USDC from the sender cast send <USDC_ADDRESS> "approve(address,uint256)" <CORE_DEPOSIT_WALLET_ADDRESS> 100000000 \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` Depositing to the perps DEX: ```shell theme={null} # Deposit 100 USDC to the perps DEX (destinationDex = 0) cast send <CORE_DEPOSIT_WALLET_ADDRESS> "deposit(uint256,uint32)" 100000000 0 \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` Depositing to the spot DEX: ```shell theme={null} # Deposit 100 USDC to the spot DEX (destinationDex = uint32.max) cast send <CORE_DEPOSIT_WALLET_ADDRESS> "deposit(uint256,uint32)" 100000000 4294967295 \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` <Note> **Note:** If the destination DEX value is not supported (spot or perps), the deposit is credited to the sender's spot balance. </Note> ### `depositFor` function The `depositFor` function transfers USDC from the caller but credits a specified recipient address on HyperCore. This allows deposits on behalf of another user. **Signature:** ```solidity theme={null} depositFor(address recipient, uint256 amount, uint32 destinationId); ``` **Parameters:** | Parameter | Value | | - | - | | `recipient` | The recipient address on HyperCore | | `amount` | The USDC amount being deposited from HyperEVM to HyperCore | | `destinationId` | The HyperCore destination `dex` index. Accepted values are:<br />- `0` → default perps DEX<br />- `type(uint32).max` → spot DEX | **Token pull:** Uses `transferFrom(msg.sender, address(this), amount)` → requires prior ERC-20 approve from the `msg.sender` to the core deposit wallet. **Who is credited:** The recipient address passed to the function is credited on HyperCore. **Examples:** ERC-20 Approval: ```shell theme={null} # approve the CoreDepositWallet to spend 100 USDC from the sender cast send <USDC_ADDRESS> "approve(address,uint256)" <CORE_DEPOSIT_WALLET_ADDRESS> 100000000 \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` Depositing to the perps DEX: ```shell theme={null} # Deposit 100 USDC to the perps DEX (destinationDex = 0) cast send <CORE_DEPOSIT_WALLET_ADDRESS> "depositFor(address,uint256,uint32)" <RECIPIENT_ADDRESS> 100000000 0 \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` Depositing to the spot DEX: ```shell theme={null} # Deposit 100 USDC to the spot DEX (destinationDex = uint32.max) cast send <CORE_DEPOSIT_WALLET_ADDRESS> "depositFor(address,uint256,uint32)" <RECIPIENT_ADDRESS> 100000000 4294967295 \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` <Note> **Note:** If the destination DEX value is not supported (spot or perps), the deposit is credited to the recipient's spot balance. </Note> ### `depositWithAuth` function The `depositWithAuth` function allows depositing USDC using a pre-signed ERC-3009 authorization. This enables a deposit where the token transfer is authorized offchain and executed onchain without requiring a prior approve call. **Signature:** ```solidity theme={null} depositWithAuth(uint256 amount, uint256 authValidAfter, uint256 authValidBefore, bytes32 authNonce, uint8 v, bytes32 r, bytes32 s, uint32 destinationDex); ``` **Parameters:** * `amount`: The USDC amount being deposited from HyperEVM to HyperCore * `authValidAfter`, `authValidBefore`, `authNonce`, `v`, `r`, `s`: EIP-3009-style authorization fields for `receiveWithAuthorization` * `destinationDex`: The HyperCore destination `dex` index. Accepted values are: * `0` → default perps DEX * `type(uint32).max` → spot DEX **Token pull:** Calls `token.receiveWithAuthorization(...)`, no prior approve needed. **Who is credited:** The `msg.sender` which has to match the `from` address from the `receiveWithAuthorization` is credited on HyperCore. **receiveWithAuthorization details:** * **ERC:** ERC-3009 * **Function Signature:** `ReceiveWithAuthorization` * **Parameters:** | Parameter | Value | | - | - | | `from` | The payer's address (`authorizer`) has to match the `msg.sender` of the `depositWithAuth` function | | `to` | The `CoreDepositWallet` address (payee) | | `value` | The auth amount | | `validAfter` | The time after which this is valid (Unix time) | | `validBefore` | The time before which this is valid (Unix time) | | `nonce` | Unique nonce | | `v` | v of the signature | | `r` | r of the signature | | `s` | s of the signature | **Example:** The example below illustrates how to generate an ERC-3009 authorization: ```javascript theme={null} #!/usr/bin/env node const ethers = require("ethers"); const PRIVATE_KEY = process.env.PRIVATE_KEY; const wallet = new ethers.Wallet(PRIVATE_KEY); const provider = new ethers.JsonRpcProvider( process.env.RPC_URL || "https://rpc.hyperliquid-testnet.xyz/evm", ); const usdcAddress = "<USDC_CONTRACT_ADDRESS>"; const coreDepositWallet = "<CORE_DEPOSIT_WALLET_ADDRESS>"; const EIP712_PREFIX = "0x1901"; const amount = ethers.parseUnits("100", 6); // 100 USDC const nonce = ethers.hexlify(ethers.randomBytes(32)); const validAfter = 0; const validBefore = Math.floor(Date.now() / 1000) + 3600; // valid for 1 hour // Minimal ABI for DOMAIN_SEPARATOR const USDC_ABI = [ { inputs: [], name: "DOMAIN_SEPARATOR", outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }], stateMutability: "view", type: "function", }, ]; async function getDomainSeparator(usdc) { try { return await usdc.DOMAIN_SEPARATOR(); } catch { const domain = { name: "USD Coin", version: "2", chainId: await provider.getNetwork().then((n) => n.chainId), verifyingContract: await usdc.getAddress(), }; return ethers.TypedDataEncoder.hashDomain(domain); } } async function main() { const usdc = new ethers.Contract(usdcAddress, USDC_ABI, provider); const domainSeparator = await getDomainSeparator(usdc); const structHash = ethers.keccak256( ethers.AbiCoder.defaultAbiCoder().encode( [ "bytes32", "address", "address", "uint256", "uint256", "uint256", "bytes32", ], [ ethers.keccak256( ethers.toUtf8Bytes( "ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)", ), ), wallet.address, coreDepositWallet, amount, validAfter, validBefore, nonce, ], ), ); const digest = ethers.keccak256( ethers.concat([EIP712_PREFIX, domainSeparator, structHash]), ); const signer = new ethers.SigningKey(PRIVATE_KEY); const sig = signer.sign(digest); console.log("Authorization parameters:"); console.log({ amount: amount.toString(), validAfter, validBefore, nonce, v: sig.v, r: sig.r, s: sig.s, }); } main().catch(console.error); ``` The example below illustrates how to call the `depositWithAuth` function with the authorization data: ```shell theme={null} cast send <CORE_DEPOSIT_WALLET_ADDRESS> \ "depositWithAuth(uint256,uint256,uint256,bytes32,uint8,bytes32,bytes32,uint32)" \ <AMOUNT> 0 1735660000 0x<NONCE> <V> 0x<R> 0x<S> <DEST_DEX_ID> \ --private-key $PRIVATE_KEY \ --rpc-url $RPC_URL ``` <Note> **Note:** If the destination DEX value is not supported (spot or `perp`), the deposit is credited to the `authorizer's` spot balance. </Note> # HyperCore CCTP-enablement contract addresses Source: https://developers.circle.com/cctp/references/hypercore-contract-addresses CCTP has additional contracts beyond the standard protocol to enable transfers to HyperCore. The following sections describe the functions and addresses of these contracts. * **`CctpExtension`**: (Arbitrum only) Responsible for transferring USDC from Arbitrum to HyperCore. * **`CctpExtensionV2`**: (Arbitrum only) Enables sponsored CCTP deposits from Arbitrum to HyperCore. A relayer submits `batchSponsorDepositForBurn` on behalf of users who signed EIP-3009 `ReceiveWithAuthorization` offchain. See [CctpExtensionV2 Contract Interface](/cctp/references/cctp-extension-v2-contract-interface). * **`CctpForwarder`**: (HyperEVM only) Responsible for forwarding USDC from HyperEVM to HyperCore in a CCTP transfer from any non-HyperEVM domain. * **`CoreDepositWallet`**: (HyperEVM only) Responsible for [depositing USDC into HyperCore](/cctp/references/coredepositwallet-contract-interface). This page contains the contract addresses for the HyperCore CCTP-enablement contracts. For the contract addresses for core CCTP contracts, see [EVM contracts and interfaces](/cctp/evm-smart-contracts), [Solana Contracts and Interfaces](/cctp/solana-programs), and [Starknet contracts and interfaces](/cctp/starknet-contracts). ## Mainnet contract addresses ### CctpExtension: Mainnet | Chain | [Domain](/cctp/cctp-supported-blockchains#cctp-supported-domains) | Address | | - | - | - | | **Arbitrum** | 3 | [`0xA95d9c1F655341597C94393fDdc30cf3c08E4fcE`](https://arbiscan.io/address/0xA95d9c1F655341597C94393fDdc30cf3c08E4fcE) | ### CctpExtensionV2: Mainnet | Chain | [Domain](/cctp/cctp-supported-blockchains#cctp-supported-domains) | Address | | - | - | - | | **Arbitrum** | 3 | [`0x3289e443a95B28Bcedacc4B33C689b0C9b84ffAB`](https://arbiscan.io/address/0x3289e443a95B28Bcedacc4B33C689b0C9b84ffAB) | <Note> `CctpExtensionV2` is separate from `CctpExtension`. For sponsored relayer deposits, see [Transfer USDC from Arbitrum to HyperCore with CctpExtensionV2](/cctp/howtos/transfer-usdc-from-arbitrum-to-hypercore-with-cctp-extension-v2). For self-submitted burns, see [Transfer USDC from Arbitrum to HyperCore](/cctp/howtos/transfer-usdc-from-arbitrum-to-hypercore). </Note> ### CctpForwarder: Mainnet | Chain | [Domain](/cctp/cctp-supported-blockchains#cctp-supported-domains) | Address | | - | - | - | | **HyperEVM** | 19 | [`0xb21D281DEdb17AE5B501F6AA8256fe38C4e45757`](https://hy
프로젝트로 돌아가기