跳到主要内容

充值与提现 (Deposit & Withdraw)

ZTDX 采用链上结算机制,充值和提现均通过智能合约完成,确保资金安全和透明。

概述

充值流程

1. 准备充值

2. 向 Vault 合约转账

3. 等待区块确认(2-5个区块)

4. 余额自动更新

提现流程

1. 发起提现请求(获取后端签名)

2. 调用 Vault 合约的 withdraw() 方法

3. 等待交易确认

4. 资产转入钱包

充值 (Deposit)

准备充值

获取充值所需的合约地址和参数信息。

接口信息

  • Method: POST
  • Path: /api/v1/deposit/prepare
  • Authentication: 需要 JWT Token
  • Content-Type: application/json

请求参数

参数类型必须描述
tokenstring资产名称(如 USDT
amountstring充值数量(Decimal 字符串)

请求示例

{
"token": "USDT",
"amount": "1000.00"
}

响应示例

{
"vault_address": "0x1234567890abcdef1234567890abcdef12345678",
"token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"token_symbol": "USDT",
"token_decimals": 6,
"amount": "1000.00",
"amount_wei": "1000000000",
"estimated_gas": 100000,
"gas_price_gwei": "0.1"
}

响应字段说明

字段类型描述
vault_addressstringVault 合约地址(接收地址)
token_addressstring代币合约地址
token_symbolstring代币符号
token_decimalsnumber代币小数位数
amountstring充值数量(Decimal 字符串)
amount_weistring充值数量(Wei,最小单位)
estimated_gasnumber预估 Gas 用量
gas_price_gweistring当前 Gas 价格(Gwei)

前端充值示例

async function deposit(token, amount) {
// 1. 准备充值
const prepareResponse = await fetch('/api/v1/deposit/prepare', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`
},
body: JSON.stringify({ token, amount })
});

const {
vault_address,
token_address,
amount_wei
} = await prepareResponse.json();

// 2. 调用 ERC20 合约的 transfer 方法
const tokenContract = new ethers.Contract(
token_address,
['function transfer(address to, uint256 amount) returns (bool)'],
signer
);

const tx = await tokenContract.transfer(vault_address, amount_wei);

// 3. 等待确认
await tx.wait();

console.log('充值成功!交易哈希:', tx.hash);
return tx.hash;
}

充值历史

查询用户的充值记录。

接口信息

  • Method: GET
  • Path: /api/v1/deposit/history
  • Authentication: 需要 JWT Token

Query 参数

参数类型必须描述
limitnumber返回数量(默认 50)
offsetnumber偏移量(分页)

响应示例

{
"deposits": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"user_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb",
"token": "USDT",
"token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"amount": "1000.00",
"tx_hash": "0xabc123def456...",
"block_number": 12345678,
"confirmations": 5,
"status": "completed",
"created_at": 1704067200000,
"confirmed_at": 1704067500000
}
],
"total": 1,
"limit": 50,
"offset": 0
}

Deposit 对象字段

字段类型描述
idstring充值记录 UUID
user_addressstring用户地址
tokenstring资产符号
token_addressstring代币合约地址
amountstring充值数量(Decimal 字符串)
tx_hashstring交易哈希
block_numbernumber区块高度
confirmationsnumber确认数
statusstring状态(见下表)
created_atnumber创建时间(毫秒级时间戳)
confirmed_atnumber | null确认时间(毫秒级时间戳)

充值状态

状态说明可操作
pending待确认(等待区块确认)
completed已完成(余额已到账)
failed失败(交易回滚)

提现 (Withdraw)

发起提现请求

创建提现请求并获取后端签名,用于调用链上合约。

接口信息

  • Method: POST
  • Path: /api/v1/withdraw/request
  • Authentication: 需要 JWT Token
  • Content-Type: application/json

请求参数

参数类型必须描述
tokenstring资产名称(如 USDT
amountstring提现数量(Decimal 字符串)

请求示例

{
"token": "USDT",
"amount": "500.00"
}

响应示例

{
"id": "660e8400-e29b-41d4-a716-446655440000",
"user_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb",
"token": "USDT",
"token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"amount": "500.00",
"amount_wei": "500000000",
"nonce": 1,
"expiry": 1704153600,
"backend_signature": "0xabcdef123456...",
"vault_address": "0x1234567890abcdef...",
"status": "pending",
"created_at": 1704067200000
}

响应字段说明

字段类型描述
idstring提现请求 UUID
user_addressstring用户地址
tokenstring资产符号
token_addressstring代币合约地址
amountstring提现数量(Decimal 字符串)
amount_weistring提现数量(Wei)
noncenumber用户的提现 nonce(链上验证)
expirynumber签名过期时间(秒级时间戳,通常 24 小时后)
backend_signaturestring后端签名(用于合约验证)
vault_addressstringVault 合约地址
statusstring提现状态
created_atnumber创建时间(毫秒级时间戳)

前端提现示例

async function withdraw(token, amount) {
// 1. 发起提现请求
const response = await fetch('/api/v1/withdraw/request', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`
},
body: JSON.stringify({ token, amount })
});

const {
id: withdrawId,
amount_wei,
nonce,
expiry,
backend_signature,
vault_address,
token_address
} = await response.json();

// 2. 调用 Vault 合约的 withdraw 方法
const vaultContract = new ethers.Contract(
vault_address,
[
'function withdraw(address token, uint256 amount, uint256 nonce, uint256 expiry, bytes signature) external'
],
signer
);

const tx = await vaultContract.withdraw(
token_address,
amount_wei,
nonce,
expiry,
backend_signature
);

// 3. 等待交易确认
await tx.wait();

// 4. 通知后端交易已确认
await fetch(`/api/v1/withdraw/${withdrawId}/confirm`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`
},
body: JSON.stringify({ tx_hash: tx.hash })
});

return tx.hash;
}

查询提现详情

查询单个提现请求的详细信息。

接口信息

  • Method: GET
  • Path: /api/v1/withdraw/:id
  • Authentication: 需要 JWT Token

路径参数

参数类型必须描述
idstring提现记录 UUID

响应示例

{
"id": "660e8400-e29b-41d4-a716-446655440000",
"user_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb",
"token": "USDT",
"token_address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"amount": "500.00",
"amount_wei": "500000000",
"nonce": 1,
"expiry": 1704153600,
"backend_signature": "0xabcdef123456...",
"tx_hash": "0xdef456abc123...",
"status": "completed",
"created_at": 1704067200000,
"confirmed_at": 1704067500000
}

提现历史

查询用户的提现记录列表。

接口信息

  • Method: GET
  • Path: /api/v1/withdraw/history
  • Authentication: 需要 JWT Token

Query 参数

参数类型必须描述
limitnumber返回数量(默认 50)
offsetnumber偏移量(分页)
statusstring过滤状态(可选)

响应示例

{
"withdrawals": [
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"user_address": "0x742d35cc6634c0532925a3b844bc9e7595f0beb",
"token": "USDT",
"amount": "500.00",
"nonce": 1,
"expiry": 1704153600,
"backend_signature": "0xabcdef...",
"tx_hash": "0xdef456...",
"status": "completed",
"created_at": 1704067200000,
"confirmed_at": 1704067500000
}
],
"total": 1,
"limit": 50,
"offset": 0
}

取消提现

取消尚未确认的提现请求。只能取消状态为 pending 的提现。

接口信息

  • Method: DELETE
  • Path: /api/v1/withdraw/:id/cancel
  • Authentication: 需要 JWT Token

路径参数

参数类型必须描述
idstring提现记录 UUID

响应示例

{
"success": true,
"message": "提现已取消,资金已退回",
"withdrawal": {
"id": "660e8400-e29b-41d4-a716-446655440000",
"status": "cancelled",
"cancelled_at": 1704067300000
}
}
注意
  • 只能取消状态为 pending 的提现
  • 已经在链上确认的提现无法取消
  • 签名过期的提现会自动取消

确认提现

提现交易上链后,通知后端更新状态。

接口信息

  • Method: POST
  • Path: /api/v1/withdraw/:id/confirm
  • Authentication: 需要 JWT Token
  • Content-Type: application/json

路径参数

参数类型必须描述
idstring提现记录 UUID

请求参数

参数类型必须描述
tx_hashstring链上交易哈希

请求示例

{
"tx_hash": "0xdef456abc123789..."
}

响应示例

{
"success": true,
"message": "提现已确认",
"withdrawal": {
"id": "660e8400-e29b-41d4-a716-446655440000",
"tx_hash": "0xdef456abc123789...",
"status": "processing",
"confirmed_at": 1704067500000
}
}

提现状态说明

状态描述可取消
pending等待用户调用合约✅ 是
processing交易已提交,等待确认❌ 否
completed提现成功❌ 否
failed提现失败(资金退回)❌ 否
cancelled已取消❌ 否
expired签名已过期(资金退回)❌ 否

智能合约 ABI

Vault 合约

deposit

// 用户无需直接调用,只需向 Vault 转账即可

withdraw

function withdraw(
address token,
uint256 amount,
uint256 nonce,
uint256 expiry,
bytes calldata signature
) external

参数说明

  • token: 代币合约地址
  • amount: 提现数量(Wei)
  • nonce: 用户的提现 nonce
  • expiry: 签名过期时间
  • signature: 后端签名

限制和费用

充值

  • 最小充值: 10 USDT
  • Gas 费用: 由用户支付(通常 0.1-0.5 USD)
  • 确认时间: 2-5 个区块(约 30-150 秒)
  • 限制: 无充值次数限制

提现

  • 最小提现: 10 USDT
  • Gas 费用: 由用户支付
  • 签名有效期: 24 小时
  • 限制: 每24小时最多 10 次提现

最佳实践

1. 检查余额

// 提现前检查可用余额
const { available } = await getBalance('USDT');

if (parseFloat(available) < parseFloat(withdrawAmount)) {
alert('可用余额不足');
return;
}

2. 处理签名过期

// 检查签名是否过期
const now = Math.floor(Date.now() / 1000);

if (withdrawal.expiry < now) {
// 取消过期的提现
await cancelWithdrawal(withdrawal.id);

// 重新发起提现
const newWithdrawal = await requestWithdraw(token, amount);
}

3. 监控交易状态

// 等待交易确认并更新状态
const tx = await vaultContract.withdraw(...);

// 监听事件
tx.wait().then(receipt => {
console.log('提现成功!');
confirmWithdrawal(withdrawalId, tx.hash);
}).catch(error => {
console.error('提现失败:', error);
// 资金会自动退回
});

4. 错误处理

try {
const txHash = await withdraw('USDT', '500');
alert('提现成功!交易哈希: ' + txHash);
} catch (error) {
if (error.code === 'INSUFFICIENT_BALANCE') {
alert('余额不足');
} else if (error.code === 'SIGNATURE_EXPIRED') {
alert('签名已过期,请重新发起提现');
} else if (error.code === 'USER_REJECTED') {
alert('用户取消了交易');
} else {
alert('提现失败: ' + error.message);
}
}

常见问题

Q: 充值需要多久到账?

A: 通常需要 2-5 个区块确认,约 30-150 秒。Arbitrum 网络速度较快,一般 1 分钟内即可到账。

Q: 为什么提现需要后端签名?

A: 后端签名用于验证提现请求的合法性,防止未授权提现。这是混合模式(链下订单 + 链上结算)的安全保障。

Q: 签名过期后资金会丢失吗?

A: 不会。签名过期后,提现状态会自动变为 expired,资金会退回到您的可用余额。

Q: 可以取消已提交的提现吗?

A: 如果提现状态为 pending(尚未链上确认),可以取消。一旦链上确认(processingcompleted),则无法取消。

Q: Gas 费用如何计算?

A: Gas 费用由链上网络决定。通常:

  • 充值(ERC20 transfer): ~100,000 gas
  • 提现(Vault withdraw): ~150,000 gas
  • Arbitrum 上 gas 费用通常低于 1 USD

相关文档