理财 API (Earn API)
本文档介绍 AXBlade 理财服务的所有 API 接口。
合约信息
Testnet (Arbitrum Sepolia)
| 合约 | 地址 | 说明 |
|---|---|---|
| AXBladeEarn v3.1 | 0xA40b4b726980C02F6558fcA5C0a52FaA2E5A0864 | ✅ 当前使用 (修复短期利息计算) |
| AXBladeEarn v3 | 0x26763848400E108bd3095e548a6de2D01dadDCaB | 旧版本 (短期产品利息为0的bug) |
| AXBladeEarn v2 | 0xe97ec2cb6B735d791ee253f86343782d41F6FA0a | 旧版本 (durationDays) |
| Platform USDT | 0x572E474C3Cf364D085760784F938A1Aa397a8B9b | 平台统一 USDT,用户需授权给 Earn 合约 |
USDT 授权: 用户申购前需将 USDT (
0x572E...) 授权 (approve) 给 Earn 合约 (0xA40b...)
公开接口 (无需认证)
获取 EIP-712 Domain 信息
获取前端签名所需的 EIP-712 Domain 配置。
端点: GET /api/v1/earn/domain
响应示例:
{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Subscribe": [
{ "name": "user", "type": "address" },
{ "name": "productId", "type": "uint256" },
{ "name": "amount", "type": "uint256" },
{ "name": "deadline", "type": "uint256" }
]
},
"primaryType": "Subscribe",
"domain": {
"name": "AXBlade Earn",
"version": "1",
"chainId": 421614,
"verifyingContract": "0xA40b4b726980C02F6558fcA5C0a52FaA2E5A0864"
}
}
获取产品列表
端点: GET /api/v1/earn/products
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| status | string | 否 | 产品状态筛选 |
| page | number | 否 | 页码,默认 1 |
| page_size | number | 否 | 每页数量,默认 10 |
响应示例:
{
"products": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"chain_product_id": 1,
"name": "AXBlade Strategy 1st Term",
"description": "7天定期理财产品",
"annual_rate": "190.36%",
"period_rate": "3.65%",
"duration_days": 7,
"total_quota": "200000.00",
"subscribed_amount": "500.00",
"available_quota": "199500.00",
"subscription_rate": "0.25%",
"min_amount": "100.00",
"max_amount_per_user": "10000.00",
"status": "subscribing",
"is_subscribing": true,
"subscribe_start_time": 1704067200,
"subscribe_end_time": 1704153600,
"settle_time": 1704758400,
"time_remaining_seconds": 86400,
"subscriber_count": 1
}
],
"total": 10,
"page": 1,
"page_size": 10
}
获取产品详情
端点: GET /api/v1/earn/products/:id
路径参数:
| 参 数 | 类型 | 说明 |
|---|---|---|
| id | string | 产品 ID (UUID 或链上 ID) |
响应示例:
{
"id": "uuid",
"chain_product_id": 1,
"name": "AXBlade Strategy 1st Term",
"annual_rate": "190.36%",
"period_rate": "3.65%",
"duration_days": 7,
"total_quota": "200000.00",
"subscribed_amount": "500.00",
"available_quota": "199500.00",
"subscription_rate": "0.25%",
"status": "subscribing",
"is_subscribing": true,
"time_remaining_seconds": 86400,
"subscriber_count": 1
}
获取历史表现
查询已结束产品的历史收益表现。
端点: GET /api/v1/earn/performance
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | number | 否 | 返回数量,默认 10 |
响应示例:
{
"performances": [
{
"product_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "AXBlade Strategy 2nd Term",
"annual_rate": "185.00%",
"period_rate": "3.55%",
"duration_days": 7,
"total_subscribed": "200000.00",
"total_interest_paid": "7100.00",
"subscriber_count": 50,
"settled_at": 1703500000
}
]
}
认证接口
所有认证接口需要在 Header 中包含 JWT Token:
Authorization: Bearer <jwt_token>
获取我的申购列表
端点: GET /api/v1/earn/subscriptions
响应示例:
{
"subscriptions": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"product_id": "550e8400-e29b-41d4-a716-446655440000",
"chain_product_id": 1,
"product_name": "AXBlade Strategy 1st Term",
"amount": "500.00",
"nft_amount": "500000000",
"expected_return": "518.25",
"expected_interest": "18.25",
"actual_return": null,
"nft_status": "active",
"annual_rate": "190.36%",
"period_rate": "3.65%",
"duration_days": 7,
"subscribed_at": 1704100000,
"settle_time": 1704758400,
"settled_at": null,
"claimed": false,
"subscribe_tx_hash": "0xabc123..."
}
]
}
NFT 状态说明:
| 状态 | 说明 |
|---|---|
active | 持有中,等待到期 |
matured | 已到期,可领取 |
redeemed | 已领取 |
准备申购 (获取签名)
获取后端签名,用于链上申购调用。
端点: POST /api/v1/earn/subscribe/prepare
请求体:
{
"product_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": "500.00"
}
成功响应:
{
"chain_product_id": 1,
"amount": "500000000",
"deadline": 1767766918,
"signature": "0x...",
"contract_address": "0xA40b4b726980C02F6558fcA5C0a52FaA2E5A0864",
"user_address": "0x6538469807e019e05c9ec4bd158b12afb1da50f3"
}
错误响应:
{
"error": "INSUFFICIENT_QUOTA",
"message": "申购额度不足。请求: 5000.00, 剩余额度: 3000.00"
}
{
"error": "EXCEED_USER_LIMIT",
"message": "超出个人限额。请求: 15000.00, 个人限额: 10000.00, 已申购: 8000.00"
}
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| chain_product_id | number | 链上产品 ID |
| amount | string | 金额 (wei, 6 decimals) |
| deadline | number | 签名过期时间 (Unix timestamp) |
| signature | string | EIP-712 签名 |
| contract_address | string | Earn 合约地址 |
| user_address | string | 用户钱包地址 |
管理后台 API (API Key 认证)
| 方法 | 路径 | 描述 |
|---|---|---|
| POST | /api/v1/admin/earn/products | 创建产品 |
| POST | /api/v1/admin/earn/products/:id/status | 更新状态 |
| GET | /api/v1/admin/earn/products/:id/subscriptions | 查询申购数据 |
| POST | /api/v1/admin/earn/products/:id/settle | 触发结算 |
EIP-712 签名结构
Subscribe TypeHash
bytes32 public constant SUBSCRIBE_TYPEHASH = keccak256(
"Subscribe(address user,uint256 productId,uint256 amount,uint256 deadline)"
);
Domain 配置
const domain = {
name: "AXBlade Earn",
version: "1",
chainId: 421614, // Arbitrum Sepolia (或 42161 Arbitrum One)
verifyingContract: "0xA40b4b726980C02F6558fcA5C0a52FaA2E5A0864"
};
const types = {
Subscribe: [
{ name: "user", type: "address" },
{ name: "productId", type: "uint256" },
{ name: "amount", type: "uint256" },
{ name: "deadline", type: "uint256" }
]
};
const value = {
user: userAddress,
productId: 1,
amount: ethers.parseUnits("500", 6),
deadline: 1736237556
};
合约核心函数
| 函数 | 权限 | 描述 |
|---|---|---|
createProduct() | ADMIN_ROLE | 创建理财产品 |
openSubscription() | OPERATOR_ROLE | 开启申购窗口 |
activateProduct() | OPERATOR_ROLE | 激活产品(申购结束后) |
settleProduct() | OPERATOR_ROLE | 结算到期产品 |
cancelProduct() | ADMIN_ROLE | 取消产品(紧急情况) |
subscribe() | 用户 | 申购产品(需后端EIP-712签名) |
claim() | 用户 | 领取本金+利息 |
emergencyClaim() | 用户 | 紧急赎回(产品取消时) |
合约事件
event ProductCreated(
uint256 indexed productId,
string name,
uint256 annualRateBps,
uint256 durationDays,
uint256 totalQuota
);
event ProductStatusChanged(
uint256 indexed productId,
ProductStatus oldStatus,
ProductStatus newStatus
);
event Subscribed(
uint256 indexed productId,
address indexed user,
uint256 amount,
uint256 expectedReturn
);
event ProductSettled(
uint256 indexed productId,
uint256 totalPrincipal,
uint256 totalInterest
);
event Claimed(
uint256 indexed productId,
address indexed user,
uint256 principal,
uint256 interest
);
event TreasuryUpdated(
address indexed oldTreasury,
address indexed newTreasury
);
错误码
| 错误码 | 说明 |
|---|---|
PRODUCT_NOT_FOUND | 产品不存在 |
PRODUCT_NOT_SUBSCRIBING | 产品未在申购期 |
INSUFFICIENT_QUOTA | 产品额度不足 |
EXCEED_USER_LIMIT | 超出个人限额 |
BELOW_MIN_AMOUNT | 低于最小申购金额 |
SUBSCRIPTION_NOT_FOUND | 申购记录不存在 |
NOT_SETTLED | 产品未结算,无法领取 |
ALREADY_CLAIMED | 已经领取过 |
SIGNATURE_EXPIRED | 签名已过期 |
INVALID_SIGNATURE | 签名验证失败 |
PRODUCT_CANCELLED | 产品已取消 |
配置项
Testnet 环境变量 (Sepolia)
# Earn Contract (v3.1 合约,使用平台 USDT)
EARN_CONTRACT_ADDRESS=0xA40b4b726980C02F6558fcA5C0a52FaA2E5A0864
# 后端签名私钥
BACKEND_SIGNER_PRIVATE_KEY=0x117340e28aa95a23046640e34ed703c66af6607d62525bd82c60e2bc118e7904
# RPC 配置
RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
CHAIN_ID=421614
# Admin API Key
ADMIN_API_KEY=axblade-sepolia-admin-key-2026
Mainnet 环境变量 (待配置)
# Earn Contract
EARN_CONTRACT_ADDRESS=<待部署>
# RPC 配置
RPC_URL=https://arb1.arbitrum.io/rpc
CHAIN_ID=42161