": "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프로젝트로 돌아가기