bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 75쪽 중 36쪽
quickstart](cctp/quickstarts/transfer-usdc-ethereum-to-arc).
<Note>
Fast Transfers from Ethereum to HyperEVM incur a protocol fee and a dynamic
forwarding fee for the HyperEVM chain relay transaction. Fast Transfer is the
default for transfers from Ethereum to HyperCore.
</Note>
## Steps
Use the following steps to transfer USDC from Ethereum to HyperCore.
### Step 1. Get CCTP fees from the API
Query the CCTP API for the fees for transferring USDC from Ethereum to
HyperCore. This value is passed to the `maxFee` parameter in the
`depositForBurnWithHook` transaction. The following is an example request to the
CCTP using source domain 0 (Ethereum) and destination domain 19 (HyperEVM):
```shell theme={null}
curl --request GET \
--url 'https://iris-api-sandbox.circle.com/v2/burn/USDC/fees/0/19?forward=true&hyperCoreDeposit=true' \
--header 'Content-Type: application/json'
```
**Response:**
```json theme={null}
[
{
"finalityThreshold": 1000, // fast transfer
"minimumFee": 1, // in basis points
"forwardFee": {
"low": 211203,
"med": 216109, // 0.216109 USDC
"high": 221014
}
},
{
"finalityThreshold": 2000, // standard transfer
"minimumFee": 0,
"forwardFee": {
"low": 211203,
"med": 216109, // 0.216109 USDC
"high": 221014
}
}
]
```
### Step 2. Calculate the USDC amounts minus fees
There is a protocol fee to deposit USDC from Ethereum to HyperEVM and a dynamic
forwarding fee for the HyperEVM chain relay transaction. The CCTP fast transfer
fee is 1 basis point (0.01%) of the transfer amount. The forwarding fee is 0.20
USDC (`0_200_000` subunits) plus a dynamic destination chain gas fee. For a 10
USDC transfer from Ethereum to HyperCore, the protocol fee is 0.001 USDC (10
USDC × 0.0001) and an example forwarding fee is 0.216109 USDC, for a total fee
of 0.217109 USDC.
Because the protocol fee scales with the transfer amount and the forwarding fee
is dynamic, you must recalculate `maxFee` for each transfer. For a programmatic
approach, see [`calculateMaxFee`](/cctp/concepts/fees#maximum-fee-parameter) on
the fees page.
### Step 3. Approve the USDC transfer
To allow the `TokenMessengerV2` contract to transfer the USDC on your behalf,
you need to approve the transfer. This is done by calling the `approve` function
on the USDC contract.
You can see an example of this contract call in the
[Ethereum CCTP V2 example on GitHub](https://github.com/circlefin/solana-cctp-contracts/blob/9f8cf26d059cf8927ae0a0b351f3a7a88c7bdade/examples/v2/evm.ts#L63).
### Step 4. Sign and broadcast a `depositForBurnWithHook` transaction on the `TokenMessengerV2` contract
Create a `depositForBurnWithHook` transaction for the `TokenMessengerV2`
contract with the following parameters:
* `amount`: The amount of USDC to transfer
* `destinationDomain`: 19 (HyperEVM)
* `mintRecipient`: The address of the `CctpForwarder` contract on HyperEVM
* `burnToken`: The address of the USDC contract on the source chain
* `destinationCaller`: The address of the `CctpForwarder` contract on HyperEVM
* `maxFee`: The protocol fee + forwarding fee calculated in Step 2
* `minFinalityThreshold`: `1000` (Fast Transfer)
* `hookData`: The hook data to call the `CctpForwarder` contract on HyperEVM
<Warning>
Always set both `mintRecipient` and `destinationCaller` to the `CctpForwarder`
[contract address](/cctp/references/hypercore-contract-addresses) on HyperEVM
when you transfer USDC to HyperCore.
* If `destinationCaller` is wrong, the forwarder cannot complete the transfer.
* If `mintRecipient` is wrong, the minted USDC is not sent to the forwarder.
In either case, funds become permanently stuck and **cannot be recovered**.
</Warning>
The `hookData` is the data to execute the forwarder to HyperCore. The following
is an example of the hook data:
```ts TypeScript theme={null}
/**
* Generate CCTP forwarder hook data for HyperCore
*
* Hook Data Format:
* Field Bytes Type Index
* magicBytes 24 bytes24 0 ASCII prefix "cctp-forward", followed by padding
* version 4 uint32 24
* dataLength 4 uint32 28
* hyperCoreMintRecipient 20 address 32 EVM address - optional, included if requesting a deposit to HyperCore
* hyperCoreDestinationDex 4 uint32 52 The destinationDexId on HyperCore (0 for perp and uint32.max for spot)
*/
function encodeForwardHookData(
hyperCoreMintRecipient?: `0x${string}`,
hyperCoreDestinationDex: number = 0,
): `0x${string}` {
// Validate hex prefix if recipient provided
if (hyperCoreMintRecipient && !hyperCoreMintRecipient.startsWith("0x")) {
throw new Error("Address must start with 0x");
}
// Magic bytes: "cctp-forward" (12 chars) padded to 24 bytes with zeros
const magic = "cctp-forward";
const magicHex = Buffer.from(magic, "utf-8").toString("hex").padEnd(48, "0");
// Version: uint32 = 0 (4 bytes, big-endian)
const version = "00000000";
if (!hyperCoreMintRecipient) {
// No recipient: dataLength = 0, return header only (32 bytes)
const dataLength = "00000000";
return `0x${magicHex}${version}${dataLength}`;
}
// With recipient: dataLength = 24 (20 bytes address + 4 bytes dex)
const dataLength = "00000018"; // 24 in hex
// Address: 20 bytes (remove 0x prefix)
const address = hyperCoreMintRecipient.slice(2).toLowerCase();
// Destination DEX: uint32 big-endian
// 0 = perps, 4294967295 (0xFFFFFFFF) = spot
const dex = (hyperCoreDestinationDex >>> 0).toString(16).padStart(8, "0");
return `0x${magicHex}${version}${dataLength}${address}${dex}`;
}
```
You can see an example of this contract call in the
[Ethereum V2 example on GitHub](https://github.com/circlefin/solana-cctp-contracts/blob/9f8cf26d059cf8927ae0a0b351f3a7a88c7bdade/examples/v2/evm.ts#L105)
Once the deposit transaction is confirmed, the USDC is minted on HyperEVM and
automatically forwarded to your address on HyperCore.
By default (when `hyperCoreDestinationDex` is `0`), deposits credit the perps
balance on HyperCore. To deposit to the spot balance, set
`hyperCoreDestinationDex` to `4294967295` (uint32 max value).
# Transfer USDC from HyperEVM to HyperCore
Source: https://developers.circle.com/cctp/howtos/transfer-usdc-from-hyperevm-to-hypercore
This guide shows how to transfer USDC from HyperEVM to HyperCore using the
`CoreDepositWallet` contract.
<Tip>
**Tip:** The `CoreDepositWallet` contract provides `deposit`, `depositFor`,
and `depositWithAuth` methods. This guide uses `deposit`. All methods accept a
`destinationDex` parameter (`0` for perps, `4294967295` for spot). See the
[CoreDepositWallet contract
interface](/cctp/references/coredepositwallet-contract-interface) for detailed
information.
</Tip>
## Prerequisites
Before you begin, ensure that you've:
* Installed [Node.js v22.18+](https://nodejs.org/)
* Prepared an EVM testnet wallet with the private key available
* Funded your wallet with HyperEVM testnet USDC from the
[Circle Faucet](https://faucet.circle.com)
* Created a new Node project and installed dependencies:
```bash theme={null}
npm install viem
npm install -D typescript @types/node
```
* Created a `.env` file with required environment variable:
```text theme={null}
PRIVATE_KEY=0x...
```
## Steps
Use the following steps to transfer USDC from HyperEVM to HyperCore.
### Step 1. Approve the `CoreDepositWallet` to spend USDC
Approve the `CoreDepositWallet` contract to transfer USDC on your behalf:
```ts TypeScript theme={null}
const hash = await walletClient.writeContract({
address: USDC_ADDRESS,
abi: USDC_ABI,
functionName: "approve",
args: [CORE_DEPOSIT_WALLET, amount],
});
await publicClient.waitForTransactionReceipt({ hash });
```
### Step 2. Call the `deposit` function
Call the `deposit` function with your desired amount and destination:
```ts TypeScript theme={null}
const hash = await walletClient.writeContract({
address: CORE_DEPOSIT_WALLET,
abi: CORE_DEPOSIT_WALLET_ABI,
functionName: "deposit",
args: [amount, destinationDex], // 0 = perps, 4294967295 = spot
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
```
The `deposit` function transfers USDC from your account to the
`CoreDepositWallet` and credits your HyperCore balance.
## Full example code
The following is a complete example of how to transfer USDC from HyperEVM to
HyperCore.
```ts script.ts expandable theme={null}
/**
* Script: Call CoreDepositWallet.deposit on HyperEVM
* - Approves USDC spending
* - Calls deposit(amount, destinationDex)
*/
import {
createWalletClient,
createPublicClient,
http,
parseUnits,
formatUnits,
type Address,
type Hex,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { hyperliquidEvmTestnet } from "viem/chains";
// -------- Configuration --------
const config = {
privateKey: (process.env.PRIVATE_KEY || "0x") as Hex,
// Contract addresses (HyperEVM Testnet)
coreDepositWallet: "0x0B80659a4076E9E93C7DbE0f10675A16a3e5C206" as Address,
usdcToken: "0x2B3370eE501B4a559b57D449569354196457D8Ab" as Address,
// Transfer parameters
amount: "2", // USDC amount to deposit
// HyperCore destination (0 = perps, 4294967295 = spot)
destinationDex: 0,
};
// -------- Main Function --------
async function main() {
// Validate private key
if (!config.privateKey || config.privateKey === "0x") {
throw new Error("Set PRIVATE_KEY");
}
// Setup account and clients
const account = privateKeyToAccount(config.privateKey);
const publicClient = createPublicClient({
chain: hyperliquidEvmTestnet,
transport: http(),
});
const walletClient = createWalletClient({
chain: hyperliquidEvmTestnet,
transport: http(),
account,
});
const amount = parseUnits(config.amount, 6);
console.log("User:", account.address);
console.log("CoreDepositWallet:", config.coreDepositWallet);
console.log("USDC:", config.usdcToken);
console.log("Amount (USDC):", config.amount);
console.log(
"Destination DEX:",
config.destinationDex === 0 ? "perps" : "spot",
);
// Check USDC balance
const balance = await publicClient.readContract({
address: config.usdcToken,
abi: [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "account", type: "address" }],
outputs: [{ name: "", type: "uint256" }],
},
],
functionName: "balanceOf",
args: [account.address],
});
if (balance < amount) {
throw new Error(
`Insufficient USDC: have ${formatUnits(balance, 6)}, need ${config.amount}`,
);
}
// Check current allowance
const currentAllowance = await publicClient.readContract({
address: config.usdcToken,
abi: [
{
name: "allowance",
type: "function",
stateMutability: "view",
inputs: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
],
outputs: [{ name: "", type: "uint256" }],
},
],
functionName: "allowance",
args: [account.address, config.coreDepositWallet],
});
// Step 1: Approve if needed
if (currentAllowance < amount) {
console.log("\nApproving USDC spending...");
const hash = await walletClient.writeContract({
address: config.usdcToken,
abi: [
{
name: "approve",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "spender", type: "address" },
{ name: "amount", type: "uint256" },
],
outputs: [{ name: "", type: "bool" }],
},
],
functionName: "approve",
args: [config.coreDepositWallet, amount],
});
console.log("Approve tx hash:", hash);
await publicClient.waitForTransactionReceipt({ hash });
console.log("Approval confirmed");
} else {
console.log("\nSufficient allowance already exists");
}
// Step 2: Deposit
console.log("\nDepositing USDC to HyperCore...");
const hash = await walletClient.writeContract({
address: config.coreDepositWallet,
abi: [
{
name: "deposit",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "amount", type: "uint256" },
{ name: "destinationDex", type: "uint32" },
],
outputs: [],
},
],
functionName: "deposit",
args: [amount, config.destinationDex],
});
console.log("Deposit tx hash:", hash);
// Wait for transaction receipt
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("Status:", receipt.status === "success" ? "SUCCESS" : "FAILED");
console.log(
"Block:",
receipt.blockNumber,
"\nGas Used:",
receipt.gasUsed.toString(),
);
}
// Run
main().catch((error) => {
console.error("Error:", error.message);
process.exit(1);
});
```
Run the script:
```bash theme={null}
node --env-file=.env script.ts
```
# Transfer USDC from Solana to HyperCore
Source: https://developers.circle.com/cctp/howtos/transfer-usdc-from-solana-to-hypercore
This guide shows how to transfer USDC from Solana to HyperCore using the
`TokenMessengerV2` contract. Solana's CCTP implementation does not have the
`depositForBurnWithAuth`, and there is no `CctpExtension` contract for Solana.
As such, transfers from Solana to HyperCore follow the standard CCTP flow, with
the addition of hook data to call the `CctpForwarder` contract on HyperEVM.
This guide does not provide full example code for the transfer to HyperCore from
Solana.
<Note>
Fast Transfers from Solana to HyperEVM incur a protocol fee and a dynamic
forwarding fee for the HyperEVM chain relay transaction. Fast Transfer is the
default for transfers from Solana to HyperCore.
</Note>
## Steps
Use the following steps to transfer USDC from Solana to HyperCore.
### Step 1. Get CCTP fees from the API
Query the CCTP API for the fees for transferring USDC from Solana to HyperCore.
This value is passed to the `maxFee` parameter in the `depositForBurnWithHook`
transaction. The following is an example request to the CCTP using source domain
5 (Solana) and destination domain 19 (HyperEVM):
```shell theme={null}
curl --request GET \
--url 'https://iris-api-sandbox.circle.com/v2/burn/USDC/fees/5/19?forward=true&hyperCoreDeposit=true' \
--header 'Content-Type: application/json'
```
**Response:**
```json theme={null}
[
{
"finalityThreshold": 1000, // fast transfer
"minimumFee": 1, // in basis points
"forwardFee": {
"low": 211203,
"med": 216109, // 0.216109 USDC
"high": 221014
}
},
{
"finalityThreshold": 2000, // standard transfer
"minimumFee": 0,
"forwardFee": {
"low": 211203,
"med": 216109, // 0.216109 USDC
"high": 221014
}
}
]
```
### Step 2. Calculate the USDC amounts minus fees
There is a protocol fee to deposit USDC from Solana to HyperEVM and a dynamic
forwarding fee for the HyperEVM chain relay transaction. The CCTP fast transfer
fee is 1 basis point (0.01%) of the transfer amount. The forwarding fee is 0.20
USDC (`0_200_000` subunits) plus a dynamic destination chain gas fee. For a 10
USDC transfer from Solana to HyperCore, the protocol fee is 0.001 USDC (10 USDC
× 0.0001) and an example forwarding fee is 0.216109 USDC, for a total fee of
0.217109 USDC.
Because the protocol fee scales with the transfer amount and the forwarding fee
is dynamic, you must recalculate `maxFee` for each transfer. For a programmatic
approach, see [`calculateMaxFee`](/cctp/concepts/fees#maximum-fee-parameter) on
the fees page.
### Step 3. Sign and broadcast a `depositForBurnWithHook` transaction on the `TokenMessengerV2` contract
Create a `depositForBurnWithHook` transaction for the `TokenMessengerV2`
contract with the following parameters:
* `amount`: The amount of USDC to transfer
* `destinationDomain`: 19 (HyperEVM)
* `mintRecipient`: The address of the `CctpForwarder` contract on HyperEVM
* `destinationCaller`: The address of the `CctpForwarder` contract on HyperEVM
* `maxFee`: The protocol fee + forwarding fee calculated in Step 2
* `minFinalityThreshold`: `1000` (Fast Transfer)
* `hookData`: The hook data to call the `CctpForwarder` contract on HyperEVM
<Warning>
Always set both `mintRecipient` and `destinationCaller` to the `CctpForwarder`
[contract address](/cctp/references/hypercore-contract-addresses) on HyperEVM
when you transfer USDC to HyperCore.
* If `destinationCaller` is wrong, the forwarder cannot complete the transfer.
* If `mintRecipient` is wrong, the minted USDC is not sent to the forwarder.
In either case, funds become permanently stuck and **cannot be recovered**.
</Warning>
The `hookData` is the data to execute the forwarder to HyperCore. The following
is an example of the hook data:
```ts TypeScript theme={null}
/**
* Generate CCTP forwarder hook data for HyperCore
*
* Hook Data Format:
* Field Bytes Type Index
* magicBytes 24 bytes24 0 ASCII prefix "cctp-forward", followed by padding
* version 4 uint32 24
* dataLength 4 uint32 28
* hyperCoreMintRecipient 20 address 32 EVM address - optional, included if requesting a deposit to HyperCore
* hyperCoreDestinationDex 4 uint32 52 The destinationDexId on HyperCore (0 for perp and uint32.max for spot)
*/
function encodeForwardHookData(
hyperCoreMintRecipient?: `0x${string}`,
hyperCoreDestinationDex: number = 0,
): `0x${string}` {
// Validate hex prefix if recipient provided
if (hyperCoreMintRecipient && !hyperCoreMintRecipient.startsWith("0x")) {
throw new Error("Address must start with 0x");
}
// Magic bytes: "cctp-forward" (12 chars) padded to 24 bytes with zeros
const magic = "cctp-forward";
const magicHex = Buffer.from(magic, "utf-8").toString("hex").padEnd(48, "0");
// Version: uint32 = 0 (4 bytes, big-endian)
const version = "00000000";
if (!hyperCoreMintRecipient) {
// No recipient: dataLength = 0, return header only (32 bytes)
const dataLength = "00000000";
return `0x${magicHex}${version}${dataLength}`;
}
// With recipient: dataLength = 24 (20 bytes address + 4 bytes dex)
const dataLength = "00000018"; // 24 in hex
// Address: 20 bytes (remove 0x prefix)
const address = hyperCoreMintRecipient.slice(2).toLowerCase();
// Destination DEX: uint32 big-endian
// 0 = perps, 4294967295 (0xFFFFFFFF) = spot
const dex = (hyperCoreDestinationDex >>> 0).toString(16).padStart(8, "0");
return `0x${magicHex}${version}${dataLength}${address}${dex}`;
}
```
For full example code calling the `depositForBurnWithHook` function, see the
[Solana CCTP V2 example on GitHub](https://github.com/circlefin/solana-cctp-contracts/blob/9f8cf26d059cf8927ae0a0b351f3a7a88c7bdade/examples/v2/solana.ts#L94).
Once the deposit transaction is confirmed, the USDC is minted on HyperEVM and
automatically forwarded to your address on HyperCore.
By default (when `hyperCoreDestinationDex` is `0`), deposits credit the perps
balance on HyperCore. To deposit to the spot balance, set
`hyperCoreDestinationDex` to `4294967295` (uint32 max value).
# How-to: Transfer USDC with the Forwarding Service
Source: https://developers.circle.com/cctp/howtos/transfer-usdc-with-forwarding-service
Transfer USDC Crosschain with the Circle Forwarding Service
This guide shows how to transfer USDC crosschain using the
[Circle Forwarding Service](/cctp/concepts/forwarding-service). This example
shows a transfer from Base Sepolia to Avalanche Fuji, but you can use the same
steps to transfer to any supported
[destination blockchain](/cctp/concepts/supported-chains-and-domains), including
Solana.
When you use the Forwarding Service, Circle handles the mint transaction on the
destination blockchain, eliminating the need for you to hold native tokens for
gas on the destination blockchain or run multichain infrastructure.
## Prerequisites
Before you start, ensure you have:
* Installed [Node.js v22.18+](https://nodejs.org/)
* Created a TypeScript project and installed the `viem` package.
* Created a wallet with the private key available on the source chain.
* Funded the wallet with testnet USDC and native tokens for gas fees on the
source chain.
* Created a `.env` file with your private key and recipient address.
## Steps
Use the following steps to transfer USDC with the Forwarding Service.
### Step 1. Get CCTP fees from the API
Query the CCTP API for the fees for transferring USDC from Base Sepolia to
Avalanche Fuji. This value is passed to the `maxFee` parameter in the
`depositForBurnWithHook` transaction. The following is an example reque프로젝트로 돌아가기