充值与提现 (Deposit & Withdraw)
本文档介绍 AXBlade 平台的充值和提现流程。
合约地址
| 合约 | 地址 | 网络 |
|---|---|---|
| AXBladeVault | 0xFDe43f8e6e082975d246844DEF4fE8E704403d43 | Arbitrum Sepolia |
| USDT | 0x572E474C3Cf364D085760784F938A1Aa397a8B9b | Arbitrum Sepolia |
Chain ID: 421614
RPC: https://sepolia-rollup.arbitrum.io/rpc
API 接口概览
| 功能 | 方法 | 路径 | 认证 |
|---|---|---|---|
| 准备充值 | POST | /deposit/prepare | 是 |
| 充值历史 | GET | /deposit/history | 是 |
| 请求提现 | POST | /withdraw/request | 是 |
| 提现历史 | GET | /withdraw/history | 是 |
| 提现详情 | GET | /withdraw/:id | 是 |
| 取消提现 | DELETE | /withdraw/:id/cancel | 是 |
| 确认提现 | POST | /withdraw/:id/confirm | 是 |
充值流程
流程图
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 客户端 │────▶│ 准备充值 │────▶│ Approve │────▶│ Deposit │
│ │ │ API │ │ USDT │ │ 合约 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │
▼ ▼
┌──────────┐ ┌─ ─────────┐
│ 链上交易 │ │ 事件监听 │
│ 确认 │ │ 更新余额 │
└──────────┘ └──────────┘
Step 1: 准备充值
端点: POST /api/v1/deposit/prepare
请求体:
{
"token": "USDT",
"amount": "100.00"
}
响应:
{
"contract_address": "0xFDe43f8e6e082975d246844DEF4fE8E704403d43",
"token_address": "0x572E474C3Cf364D085760784F938A1Aa397a8B9b",
"amount": "100.00",
"estimated_gas": 100000
}
Step 2: 授权 USDT
const usdt = new ethers.Contract(tokenAddress, [
'function approve(address,uint256) returns (bool)',
'function allowance(address,address) view returns (uint256)'
], signer);
const amountWei = ethers.parseUnits('100', 6);
const allowance = await usdt.allowance(userAddress, vaultAddress);
if (allowance < amountWei) {
const approveTx = await usdt.approve(vaultAddress, ethers.MaxUint256);
await approveTx.wait();
}
Step 3: 调用合约充值
const vault = new ethers.Contract(vaultAddress, [
'function deposit(uint256,bytes32)'
], signer);
const amountWei = ethers.parseUnits('100', 6);
const referralCode = ethers.ZeroHash; // 无推荐码
const tx = await vault.deposit(amountWei, referralCode);
await tx.wait();
console.log('充值成功!');
提现流程
流程图
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 客户端 │────▶│ 请求提现 │────▶│ 获取签名 │────▶│ 调用合约 │
│ │ │ API │ │ 后端签名 │ │ withdraw │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 余额检查 │ │ EIP-712 │ │ 签名验证 │
│ PnL验证 │ │ 签名 │ │ 转账USDT │
└──────────┘ └──────────┘ └──────────┘
Step 1: 请求提现
端点: POST /api/v1/withdraw/request
请求体:
{
"token": "USDT",
"amount": "50.00"
}
成功响应:
{
"withdraw_id": "550e8400-e29b-41d4-a716-446655440000",
"token": "0x572e474c3cf364d085760784f938a1aa397a8b9b",
"amount": "50.00",
"backend_signature": "0x1234...abcd",
"nonce": 1,
"expiry": 1703581200,
"vault_address": "0xFDe43f8e6e082975d246844DEF4fE8E704403d43"
}
余额不足响应:
{
"error": "余额不足。请求提现: 50.00, 实际可提: 30.00 (可用: 40.00, 未实现盈亏: -10.00)。原因: 2 个持仓, 未实现亏损 10.00"
}
Step 2: 调用合约提现
const vault = new ethers.Contract(vaultAddress, [
'function withdraw(address,uint256,uint256,uint256,bytes)'
], signer);
const tx = await vault.withdraw(
withdrawResponse.token,
ethers.parseUnits('50', 6),
withdrawResponse.nonce,
withdrawResponse.expiry,
withdrawResponse.backend_signature
);
await tx.wait();
console.log('提现成功!');
Step 3: 确认提现 (可选)
端点: POST /api/v1/withdraw/:id/confirm
{
"tx_hash": "0xabc123..."
}
提现状态
| 状态 | 说明 |
|---|---|
pending | 等待处理 |
signed | 已签名,等待用户提交链上交易 |
submitted | 用户已提交链上交易 |
confirmed | 链上交易已确认 |
cancelled | 已取消 |
failed | 失败 |
取消提现
只有 signed 状态的提现可以取消:
端点: DELETE /api/v1/withdraw/:id/cancel
历史记录查询
充值历史
端点: GET /api/v1/deposit/history
响应:
{
"deposits": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"token": "USDT",
"amount": "100.00",
"tx_hash": "0xabc123...",
"status": "confirmed",
"created_at": 1703577600
}
]
}
提现历史
端点: GET /api/v1/withdraw/history
响应:
{
"withdrawals": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "USDT",
"amount": "50.00",
"nonce": 1,
"expiry": 1703581200,
"backend_signature": "0x1234...abcd",
"tx_hash": "0xdef456...",
"status": "confirmed",
"created_at": 1703578800
}
]
}
完整代码示例
import { ethers } from 'ethers';
const API_BASE = 'https://api.axblade.io/api/v1';
const VAULT_ADDRESS = '0xFDe43f8e6e082975d246844DEF4fE8E704403d43';
const USDT_ADDRESS = '0x572E474C3Cf364D085760784F938A1Aa397a8B9b';
// 充值
async function deposit(signer: ethers.Signer, token: string, amount: string): Promise<void> {
const userAddress = await signer.getAddress();
// 1. 准备充值
const prepareRes = await fetch(`${API_BASE}/deposit/prepare`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ token: 'USDT', amount })
});
const { contract_address, token_address } = await prepareRes.json();
// 2. 检查 并授权 USDT
const usdt = new ethers.Contract(token_address, [
'function approve(address,uint256) returns (bool)',
'function allowance(address,address) view returns (uint256)'
], signer);
const amountWei = ethers.parseUnits(amount, 6);
const allowance = await usdt.allowance(userAddress, contract_address);
if (allowance < amountWei) {
const approveTx = await usdt.approve(contract_address, ethers.MaxUint256);
await approveTx.wait();
}
// 3. 调用充值
const vault = new ethers.Contract(contract_address, [
'function deposit(uint256,bytes32)'
], signer);
const tx = await vault.deposit(amountWei, ethers.ZeroHash);
await tx.wait();
console.log('充值成功!');
}
// 提现
async function withdraw(signer: ethers.Signer, token: string, amount: string): Promise<void> {
// 1. 请求提现签名
const withdrawRes = await fetch(`${API_BASE}/withdraw/request`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ token: 'USDT', amount })
});
if (!withdrawRes.ok) {
const error = await withdrawRes.json();
throw new Error(error.error);
}
const {
withdraw_id,
token: tokenAddress,
backend_signature,
nonce,
expiry,
vault_address
} = await withdrawRes.json();
// 2. 调用合约提现
const vault = new ethers.Contract(vault_address, [
'function withdraw(address,uint256,uint256,uint256,bytes)'
], signer);
const amountWei = ethers.parseUnits(amount, 6);
const tx = await vault.withdraw(
tokenAddress,
amountWei,
nonce,
expiry,
backend_signature
);
const receipt = await tx.wait();
// 3. 确认提现
await fetch(`${API_BASE}/withdraw/${withdraw_id}/confirm`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ tx_hash: receipt.hash })
});
console.log('提现成功!');
}
错误处理
| 错误 | 说明 | 解决方案 |
|---|---|---|
TIMESTAMP_EXPIRED | 签名时间戳过期 | 重新获取 nonce 并签名 |
SIGNATURE_INVALID | 签名验证失败 | 检查签名参数和钱包地址 |
INSUFFICIENT_BALANCE | 余额不足 | 检查可用余额 |
WITHDRAWAL_NOT_FOUND | 提现记录不存在 | 检查提现 ID |
INVALID_STATUS | 状态不正确 | 只能取消 signed 状态的提现 |
测试网资源
- Arbitrum Sepolia ETH Faucet: https://faucet.arbitrum.io/
- Vault 合约: https://sepolia.arbiscan.io/address/0xFDe43f8e6e082975d246844DEF4fE8E704403d43
- USDT 合约: https://sepolia.arbiscan.io/address/0x572E474C3Cf364D085760784F938A1Aa397a8B9b