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.
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.
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:
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: - 0 → default perps DEX - 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:
# 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:
# 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:
# 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: If the destination DEX value is not supported (spot or perps), the
deposit is credited to the sender’s spot balance.
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:
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: - 0 → default perps DEX - 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:
# 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:
# 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:
# 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: If the destination DEX value is not supported (spot or perps), the
deposit is credited to the recipient’s spot balance.
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:
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:
#!/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:
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: If the destination DEX value is not supported (spot or perp), the
deposit is credited to the authorizer's spot balance.