关于 orderly-deposit-withdraw
面向在 Orderly Network 上实现存款、提现和内部转账的开发者技能,涵盖钱包签名、代币授权、跨链流程及资产历史,并介绍 React Hooks 与 REST API。
主要功能
- 01存款流程与代币授权
- 02通过钱包签名提现
- 03处理跨链提现
- 04在 Orderly 账户间转账
- 05查询资产历史与测试网水龙头
适用场景
- 01构建存款界面
- 02实现跨链提现
- 03管理抵押品存款
- 04在 Orderly 账户间转移资金
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ orderlynetwork/ skills --skill orderly-deposit-withdraw安装后在 Claude Code 中输入/orderly-deposit-withdraw确认已加载。
详细介绍
Orderly Network:充值与提现
本技能介绍如何在 Orderly Network 上充值和提现资产,包括代币授权、金库交互、跨链操作和内部转账。
使用场景
- 构建充值/提现界面
- 管理抵押品
- 实现跨链转账
- 处理代币授权
- 在账户之间进行内部转账
前提条件
- 已连接 Web3 钱包(EVM 或 Solana)
- 支持的链上有代币
- 了解 ERC20 approve 模式
- 用于 API 操作的 Ed25519 密钥对
支持的链和代币
| 链 | Chain ID | USDC | USDT |
|---|---|---|---|
| Arbitrum | 42161 | ✅ | ✅ |
| Optimism | 10 | ✅ | ✅ |
| Base | 8453 | ✅ | ✅ |
| Ethereum | 1 | ✅ | ✅ |
| Mantle | 5000 | ✅ | - |
| Solana | 900900900 | ✅ | - |
注意:使用 GET /v1/public/chain_info?broker_id={your_broker_id} 动态获取支持的链
充值流程
1. Fetch chain information
2. Check if token is native (ETH, SOL)
3. Approve token allowance (if not native)
4. Calculate brokerHash and tokenHash
5. Get deposit fee from vault
6. Execute deposit transaction with fee
7. Wait for confirmation
8. Balance updates on Orderly
React SDK:完整充值工作流
步骤 1:获取链信息
import { useChain } from "@orderly.network/hooks";
function DepositForm() {
const { chains, isLoading } = useChain("USDC");
if (isLoading) return <div>Loading chains...</div>;
return (
<select>
{chains.map(chain => (
<option key={chain.id} value={chain.id}>
{chain.name}
</option>
))}
</select>
);
}
步骤 2:获取充值元数据
import { useDeposit } from '@orderly.network/hooks';
function DepositInfo({ tokenSymbol }: { tokenSymbol: string }) {
const { symbol, address, decimals, chainId, network } = useDeposit(tokenSymbol);
if (!address) return <div>Loading deposit info...</div>;
return (
<div>
<h3>Deposit {symbol}</h3>
<p><strong>Network:</strong> {network}</p>
<p><strong>Chain ID:</strong> {chainId}</p>
<p><strong>Contract Address:</strong> {address}</p>
<p><strong>Decimals:</strong> {decimals}</p>
<div className="mt-2 text-xs text-gray-500">
Send only {symbol} to this address on {network}.
</div>
</div>
);
}
步骤 3:通过合约执行充值
import { ethers } from 'ethers';
const ERC20_ABI = [
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)',
'function balanceOf(address owner) view returns (uint256)',
];
const VAULT_ABI = [
'function deposit(tuple(bytes32 accountId, bytes32 brokerHash, bytes32 tokenHash, uint256 tokenAmount) input) external payable',
'function getDepositFee(address account, tuple(bytes32 accountId, bytes32 brokerHash, bytes32 tokenHash, uint256 tokenAmount) input) view returns (uint256)',
];
async function depositUSDC(
provider: ethers.BrowserProvider,
amount: string,
vaultAddress: string,
usdcAddress: string,
brokerId: string,
orderlyAccountId: string
) {
const signer = await provider.getSigner();
const userAddress = await signer.getAddress();
// 1. Approve token
const usdc = new ethers.Contract(usdcAddress, ERC20_ABI, signer);
const amountWei = ethers.parseUnits(amount, 6);
const allowance = await usdc.allowance(userAddress, vaultAddress);
if (allowance < amountWei) {
console.log('Approving USDC...');
const approveTx = await usdc.approve(vaultAddress, ethers.MaxUint256);
await approveTx.wait();
console.log('Approved');
}
// 2. Calculate hashes
const encoder = new TextEncoder();
const brokerHash = ethers.keccak256(encoder.encode(brokerId));
const tokenHash = ethers.keccak256(encoder.encode('USDC'));
// 3. Create deposit input struct
const depositInput = {
accountId: orderlyAccountId,
brokerHash: brokerHash,
tokenHash: tokenHash,
tokenAmount: amountWei,
};
// 4. Get deposit fee
const vault = new ethers.Contract(vaultAddress, VAULT_ABI, signer);
const depositFee = await vault.getDepositFee(userAddress, depositInput);
console.log('Deposit fee:', ethers.formatEther(depositFee), 'ETH');
// 5. Execute deposit with fee
console.log('Depositing...');
const depositTx = await vault.deposit(depositInput, { value: depositFee });
const receipt = await depositTx.wait();
console.log('Deposited:', receipt.hash);
return receipt;
}
REST API:获取充值信息
// Get supported tokens with collateral factors
GET /v1/public/token
// Response
{
"success": true,
"data": {
"rows": [
{
"token": "USDC",
"decimals": 6,
"address": {
"42161": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
"10": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
},
"collateral_factor": 1.0
},
{
"token": "USDT",
"decimals": 6,
"address": { ... },
"collateral_factor": 0.95
}
]
}
}
// Get chain info
GET /v1/public/chain_info?broker_id={broker_id}
// Get vault balance (TVL)
GET /v1/public/vault_balance
了解抵押系数
每种代币都有一个抵押系数(0.0 到 1.0),用于确定你的充值金额中有多少计入交易抵押品:
| 代币 | 抵押系数 | $1000 充值 = 抵押品价值 |
|---|---|---|
| USDC | 1.0(100%) | $1000 |
| USDT | 0.95(95%) | $950 |
| 其他 | 不定 | 取决于风险评估 |
示例:如果你充值了 $1000 USDT,抵押系数为 0.95,那么在保证金计算中,只有 $950 计为抵押品。
提现流程
1. Get withdrawal nonce
2. Sign withdrawal message with EIP-712 (EVM) or Ed25519 (Solana)
3. Submit signed request with Ed25519 API auth
4. Wait for processing
5. Receive tokens on chain
React SDK:useWithdraw Hook
import { useWithdraw } from '@orderly.network/hooks';
function WithdrawForm() {
const { withdraw, isLoading } = useWithdraw();
const [amount, setAmount] = useState('');
const [destination, setDestination] = useState('');
const handleWithdraw = async () => {
try {
await withdraw({
symbol: "USDC",
amount: "50",
address: "0x123...",
chainId: 1, // Ethereum Mainnet
network: "ethereum"
});
alert('Withdrawal initiated!');
} catch (error) {
console.error('Withdrawal failed:', error);
}
};
return (
<div className="withdraw-form">
<input
type="text"
placeholder="Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<input
type="text"
placeholder="Destination Address"
value={destination}
onChange={(e) => setDestination(e.target.value)}
/>
<button onClick={handleWithdraw} disabled={isLoading}>
{isLoading ? 'Processing...' : 'Withdraw'}
</button>
</div>
);
}
REST API:提现
步骤 1:获取提现 Nonce
GET /v1/withdraw_nonce
// Requires Ed25519 API authentication
// Response
{
"success": true,
"data": {
"withdraw_nonce": 123456
}
}
步骤 2:创建 EIP-712 签名(EVM)
// Use ON-CHAIN domain for withdrawals (see EIP-712 Domain Configuration in orderly-api-authentication)
const domain = {
name: 'Orderly',
version: '1',
chainId: 42161,
verifyingContract: '0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203', // Mainnet Ledger
// verifyingContract: '0x1826B75e2ef249173FC735149AE4B8e9ea10abff' // Testnet Ledger
};
const types = {
Withdraw: [
{ name: 'brokerId', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'receiver', type: 'address' },
{ name: 'token', type: 'string' },
{ name: 'amount', type: 'uint256' },
{ name: 'withdrawNonce', type: 'uint64' },
{ name: 'timestamp', type: 'uint64' },
],
};
const withdrawMessage = {
brokerId: 'woofi_dex',
chainId: 42161,
receiver: '0xTargetAddress',
token: 'USDC',
amount: '10000000', // 10 USDC (6 decimals)
withdrawNonce: withdrawNonceFromStep1,
timestamp: Date.now(),
};
// Sign with wallet
const signature = await wallet.signTypedData(domain, types, withdrawMessage);
步骤 3:提交提现请求
POST /v1/withdraw_request
Body: {
"message": withdrawMessage,
"signature": "0x..."
}
// Requires Ed25519 API authentication
// Response
{
"success": true,
"data": {
"withdraw_id": 123
},
"timestamp": 1702989203989
}
Solana 提现
import { DefaultSolanaWalletAdapter } from '@orderly.network/default-solana-adapter';
import { signAsync } from '@noble/ed25519';
const walletAdapter = new DefaultSolanaWalletAdapter();
await walletAdapter.active({
address: solanaAddress,
provider: {
signMessage: async (msg: Uint8Array) => {
return await signAsync(msg, privateKey);
}
}
});
const withdrawMessage = await walletAdapter.generateWithdrawMessage({
brokerId: BROKER_ID,
receiver: solanaAddress,
token: 'USDC',
amount: '1000',
timestamp: Date.now(),
nonce: withdrawNonceFromStep1,
});
const signature = await walletAdapter.signMessage(withdrawMessage.message);
// Submit withdrawal request
POST /v1/withdraw_request
Body: {
"message": withdrawMessage.message,
"signature": signature,
"userAddress": solanaAddress,
"verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
// "verifyingContract": "0x1826B75e2ef249173FC735149AE4B8e9ea10abff"
}
Headers: { Standard Orderly Auth Headers }
跨链提现
当目标链与充值资金所在的链不同时,需要进行跨链提现。
POST /v1/withdraw_request
Body: {
"message": {
...withdrawMessage,
"chainId": 10,
"allowCrossChainWithdraw": true
},
"signature": signature,
"userAddress": walletAddress,
"verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication
// Response
{
"success": true,
"data": {
"withdraw_id": 123
},
"timestamp": 1702989203989
}
错误代码 22:如果需要跨链提现时 allowCrossChainWithdraw 为 false,API 将返回:
{
"success": false,
"code": 22,
"message": "Cross-chain withdrawal required for this withdrawal request"
}
内部转账
在 Orderly 账户之间即时转移资金,无需支付 Gas 费。
React SDK:useInternalTransfer Hook
import { useInternalTransfer } from '@orderly.network/hooks';
function TransferFunds() {
const [amount, setAmount] = useState('');
const { transfer, isLoading } = useInternalTransfer();
const handleTransfer = async () => {
try {
await transfer({
token: 'USDC',
amount: parseFloat(amount),
});
alert('Transfer successful!');
} catch (e) {
console.error(e);
}
};
return (
<div>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount"
/>
<button
onClick={handleTransfer}
disabled={isLoading || !amount}
>
{isLoading ? 'Transferring...' : 'Transfer'}
</button>
</div>
);
}
REST API:内部转账(v2)
内部转账可在 Orderly 账户之间即时转移资金,无需支付 Gas 费。
允许的场景:
- 主账户和子账户之间
- 同一钱包组内的账户之间
- 所有经纪商下的同一地址之间
- 已加入白名单的账户之间
- 同一已加入白名单的经纪商下的账户之间
EVM(EIP-712 签名)
// Step 1: Get transfer nonce (Ed25519 API auth)
GET /v1/transfer_nonce
// Requires Ed25519 API authentication
// Response
{
"success": true,
"data": {
"transfer_nonce": "789012"
}
}
// Step 2: Sign EIP-712 message with wallet (ON-CHAIN domain - see orderly-api-authentication)
const receiverHex = receiverAddress.startsWith('0x')
? receiverAddress.slice(2)
: receiverAddress;
const receiverBytes32 = ethers.zeroPadValue('0x' + receiverHex, 32);
const domain = {
name: 'Orderly',
version: '1',
chainId: 42161,
verifyingContract: '0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203' // Mainnet Ledger
};
const types = {
InternalTransfer: [
{ name: 'receiver', type: 'bytes32' },
{ name: 'token', type: 'string' },
{ name: 'amount', type: 'uint256' },
{ name: 'transferNonce', type: 'uint64' }
]
};
const message = {
receiver: receiverBytes32,
token: 'USDC',
amount: ethers.parseUnits('100', 6),
transferNonce: Number(transferNonce)
};
const signature = await wallet.signTypedData(domain, types, message);
// Step 3: Submit to v2 endpoint
POST /v2/internal_transfer
Body: {
"message": {
"receiver": "0xReceiverAddress",
"token": "USDC",
"amount": "100",
"transferNonce": "789012",
"chainId": "42161",
"chainType": "EVM"
},
"signature": "0x...",
"userAddress": "0xSenderAddress",
"verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication
Solana(Adapter 签名)
import { DefaultSolanaWalletAdapter } from '@orderly.network/default-solana-adapter';
// Step 1: Get transfer nonce
GET /v1/transfer_nonce
// Requires Ed25519 API authentication
// Step 2: Generate and sign message with adapter
const walletAdapter = new DefaultSolanaWalletAdapter();
// ... initialize adapter ...
const transferMessage = await walletAdapter.generateTransferMessage({
receiver: receiverAddress,
token: 'USDC',
amount: '100',
transferNonce: nonceFromStep1,
chainId: 900900900,
chainType: 'SOL'
});
const signature = await walletAdapter.signMessage(transferMessage.message);
// Step 3: Submit to v2 endpoint
POST /v2/internal_transfer
Body: {
"message": {
"receiver": "SolanaReceiverAddress",
"token": "USDC",
"amount": "100",
"transferNonce": "789012",
"chainId": "900900900",
"chainType": "SOL"
},
"signature": signature,
"userAddress": solanaAddress,
"verifyingContract": "0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203"
}
// Requires Ed25519 API authentication
重要说明:
- 每次转账前都必须获取转账 nonce(通过 Ed25519 身份验证)
- 实际转账需要钱包签名(EVM 使用 EIP-712,Solana 使用 adapter)
- 用于 EVM 签名的接收方地址必须转换为 bytes32
- Delegate Signer 账户无法使用内部转账
- v2 端点包含可选的 memo 字段
资产历史记录
// Get deposit/withdrawal history
GET /v1/asset/history?token=USDC&side=DEPOSIT&page=1&size=20
// Requires Ed25519 API authentication
// Query parameters (all optional):
// token: USDC
// side: DEPOSIT | WITHDRAW
// status: NEW | CONFIRM | PROCESSING | COMPLETED | FAILED | PENDING_REBALANCE
// start_t: timestamp (e.g., 1702989203989)
// end_t: timestamp (e.g., 1702989203989)
// page: 1 (default)
// size: 20 (default)
// Response
{
"success": true,
"data": {
"rows": [
{
"id": "tx_123",
"token": "USDC",
"side": "DEPOSIT",
"amount": "1000.00",
"status": "COMPLETED",
"tx_hash": "0x...",
"chain_id": 42161,
"created_at": 1699123456
}
],
"total": 50
}
}
测试网水龙头
// Get testnet USDC (testnet only)
POST /v1/faucet/usdc
Headers: { Standard Orderly Auth Headers }
// EVM Testnet
POST https://testnet-operator-evm.orderly.org/v1/faucet/usdc
// Solana Testnet
POST https://testnet-operator-sol.orderly.org/v1/faucet/usdc
// Each account can use faucet max 5 times
// EVM: 1000 USDC per request
// Solana: 100 USDC per request
获取合约地址
import { getContractAddresses } from '@orderly.network/contracts';
// EVM chains
const addresses = getContractAddresses('arbitrum', 'mainnet');
// {
// vault: '0x816f722424B49Cf1275cc86DA9840Fbd5a6167e9',
// usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
// usdt: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9'
// }
// Or fetch dynamically
GET /v1/public/chain_info?broker_id={broker_id}
验证合约地址
| 环境 | 验证合约地址 |
|---|---|
| 主网 | 0x6F7a338F2aA472838dEFD3283eB360d4Dff5D203 |
| 测试网 | 0x1826B75e2ef249173FC735149AE4B8e9ea10abff |
常见问题
“授权额度不足”错误
- 充值前调用
approve() - 检查授权交易是否已确认
- 确保授权的是金库地址,而不是代币地址
“余额不足”错误
- 检查钱包余额,而不只是 Orderly 余额
- 确保代币小数位数正确
- 考虑存款手续费(如 ETH 等原生代币)
提现卡在 “PROCESSING” 状态
- 查看
/v1/asset/history中的状态更新 - 如果卡住超过 24 小时,请联系支持团队
“需要跨链提现”错误(代码 22)
- 在请求正文中设置
allow_cross_chain_withdrawal: true - 目标链与存款链不同
- 预计处理时间会更长
无法进行内部转账
- Delegate Signer 账户无法使用此功能
- 确保账户符合允许的转账场景
- 检查 Ed25519 身份验证是否已正确签名
EIP-712 签名不匹配
常见错误:
withdrawNonce类型应为uint64,而非uint256token类型应为string,而非address- 消息中缺少
fee字段 - 验证合约地址错误
- 域中的链 ID 错误
相关技能
- orderly-api-authentication - Ed25519 API 身份验证和 EIP-712 签名
- orderly-sdk-react-hooks - React SDK 钩子参考
- orderly-onboarding - 账户设置和入门指南
评论
支持 Markdown