跳到主要内容

API 接口详解

机器人调用的核心 API 接口文档,包括认证、交易、账户管理等。

概述​

做市机器人和刷量机器人都通过 Renance API 进行交易操作。所有API调用都需要经过 EIP-712 签名认证,确保安全性。

认证流程​

1. 获取 Nonce​

GET /api/v1/auth/nonce/{address}

请求示例:

# 测试网
curl "https://api.8a27.xyz/api/v1/auth/nonce/0x1234...5678"

# 主网
curl "https://api.renance.xyz/api/v1/auth/nonce/0x1234...5678"

响应示例:

{
"nonce": 12345,
"typed_data": {
"types": {
"EIP712Domain": [...],
"Login": [...]
},
"domain": {
"name": "AXBlade",
"version": "1",
"chainId": 421614,
"verifyingContract": "0xAAbdbE3F3382D8E620cDeCD9b06022b8E63bE33D"
},
"message": {
"wallet": "0x1234...5678",
"nonce": 12345,
"timestamp": 1704537600
}
}
}

2. 签名登录消息​

使用 EIP-712 标准签名登录消息:

# EIP-712 Login Type Hash
LOGIN_TYPEHASH = "Login(address wallet,uint256 nonce,uint256 timestamp)"

# Domain Separator
domain_separator = keccak(
domain_type_hash +
keccak(b"AXBlade") + # 测试网为 AXBlade,主网为 XBlade Vault
keccak(b"1") +
encode(['uint256'], [chain_id]) +
encode(['address'], [verifying_contract])
)

# Struct Hash
struct_hash = keccak(encode(
['bytes32', 'address', 'uint256', 'uint256'],
[type_hash, wallet_address, nonce, timestamp]
))

# Message Hash
message_hash = keccak(b'\x19\x01' + domain_separator + struct_hash)

# Sign
signature = account.signHash(message_hash).signature.hex()

3. 登录​

POST /api/v1/auth/login

请求体:

{
"address": "0x1234...5678",
"signature": "0xabcd...ef01",
"timestamp": 1704537600
}

响应示例:

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"address": "0x1234...5678"
}
}

后续请求: 所有后续API请求都需要在请求头中包含 token:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

交易接口​

下单​

POST /api/v1/orders

请求体:

{
"symbol": "BTCUSDT",
"side": "buy",
"order_type": "limit",
"amount": "0.001",
"price": "100000.00",
"leverage": 50,
"signature": "0xabcd...ef01",
"timestamp": 1704537600
}

参数说明:

参数类型必填说明
symbolstring是交易对(如 BTCUSDT)
sidestring是方向:buy 或 sell
order_typestring是订单类型:limit 或 market
amountstring是数量(字符串格式)
pricestring否价格(限价单必填)
leverageinteger否杠杆倍数(默认1)
signaturestring是EIP-712 签名
timestampinteger是时间戳(秒)

订单签名:

# EIP-712 CreateOrder Type Hash
CREATE_ORDER_TYPEHASH = "CreateOrder(address wallet,string symbol,string side,string orderType,string price,string amount,uint32 leverage,uint256 timestamp)"

# Struct Hash
struct_hash = keccak(encode(
['bytes32', 'address', 'bytes32', 'bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint32', 'uint256'],
[
type_hash,
wallet_address,
keccak(symbol.encode()),
keccak(side.encode()),
keccak(order_type.encode()),
keccak(price.encode()),
keccak(amount.encode()),
leverage,
timestamp
]
))

# Message Hash
message_hash = keccak(b'\x19\x01' + domain_separator + struct_hash)

# Sign
signature = account.signHash(message_hash).signature.hex()

响应示例:

{
"id": "order_123456",
"symbol": "BTCUSDT",
"side": "buy",
"type": "limit",
"amount": "0.001",
"price": "100000.00",
"status": "open",
"created_at": "2026-01-06T14:00:00Z"
}

取消单个订单​

POST /api/v1/orders/batch

请求体:

{
"order_ids": ["order_123456"],
"signature": "0xabcd...ef01",
"timestamp": 1704537600
}

取消订单签名:

# EIP-712 BatchCancelOrders Type Hash
BATCH_CANCEL_TYPEHASH = "BatchCancelOrders(address wallet,string orderIds,uint256 timestamp)"

# 拼接订单ID
order_ids_str = ','.join(order_ids)

# Struct Hash
struct_hash = keccak(encode(
['bytes32', 'address', 'bytes32', 'uint256'],
[type_hash, wallet_address, keccak(order_ids_str.encode()), timestamp]
))

# Message Hash 和签名同上

批量取消订单​

同样使用 /api/v1/orders/batch 端点,但 order_ids 数组包含多个订单ID:

{
"order_ids": ["order_123456", "order_123457", "order_123458"],
"signature": "0xabcd...ef01",
"timestamp": 1704537600
}

账户接口​

查询余额​

GET /api/v1/account/balances

请求头:

Authorization: Bearer {token}

响应示例:

{
"balances": [
{
"asset": "USDT",
"token": "USDT",
"total": "50000.00",
"frozen": "15000.00",
"available": "35000.00"
}
]
}

查询持仓​

GET /api/v1/account/positions

查询参数:

  • symbol (可选): 过滤特定交易对

响应示例:

{
"positions": [
{
"symbol": "BTCUSDT",
"side": "long",
"size": "0.5",
"entry_price": "100000.00",
"mark_price": "100500.00",
"unrealized_pnl": "250.00",
"leverage": 50
}
]
}

查询挂单​

GET /api/v1/account/orders

查询参数:

  • status: 订单状态(如 open)
  • symbol (可选): 过滤特定交易对

响应示例:

{
"orders": [
{
"id": "order_123456",
"symbol": "BTCUSDT",
"side": "buy",
"type": "limit",
"amount": "0.001",
"price": "99950.00",
"filled": "0.000",
"status": "open",
"created_at": "2026-01-06T14:00:00Z"
}
]
}

充值提现接口​

充值准备​

POST /api/v1/deposit/prepare

请求体:

{
"amount": "10000.00",
"currency": "USDT",
"token": "USDT"
}

说明:

  • 通知后端即将进行链上充值
  • 实际充值需要调用链上 Vault 合约的 deposit() 方法

提现请求​

POST /api/v1/withdraw/request

请求体:

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

响应示例:

{
"withdraw_id": "withdraw_123456",
"backend_signature": "0xabcd...ef01",
"expiry": 1704541200,
"vault_address": "0xAAbdbE3F3382D8E620cDeCD9b06022b8E63bE33D"
}

说明:

  • 获取后端签名后,需要调用链上 Vault 合约的 withdraw() 方法
  • 提现完成后调用确认接口

提现确认​

POST /api/v1/withdraw/{withdraw_id}/confirm

请求体:

{
"tx_hash": "0x1234...5678"
}

市场数据接口​

K线数据​

GET /api/v1/markets/{symbol}/candles

查询参数:

  • period: K线周期(如 1m, 5m, 1h, 1d)

响应示例:

[
{
"timestamp": 1704537600,
"open": "100000.00",
"high": "100500.00",
"low": "99800.00",
"close": "100200.00",
"volume": "1234.56"
}
]

代码示例​

Python 完整示例​

import requests
from eth_account import Account
from eth_utils import keccak
from eth_abi import encode

class RenanceClient:
def __init__(self, base_url, private_key):
self.base_url = base_url
self.account = Account.from_key(private_key)
self.address = self.account.address
self.session = requests.Session()
self.token = None

def login(self):
# 1. 获取 nonce
nonce_url = f"{self.base_url}/api/v1/auth/nonce/{self.address.lower()}"
resp = self.session.get(nonce_url)
data = resp.json()
nonce = data['nonce']
timestamp = data['typed_data']['message']['timestamp']

# 2. 签名
signature = self.sign_login(nonce, timestamp)

# 3. 登录
login_url = f"{self.base_url}/api/v1/auth/login"
resp = self.session.post(login_url, json={
"address": self.address.lower(),
"signature": signature,
"timestamp": timestamp
})

self.token = resp.json()['token']
self.session.headers.update({
'Authorization': f'Bearer {self.token}'
})

def place_order(self, symbol, side, order_type, amount, price=None, leverage=1):
import time
timestamp = int(time.time())

# 签名订单
signature = self.sign_order(
symbol, side, order_type, amount, price, leverage, timestamp
)

# 下单
url = f"{self.base_url}/api/v1/orders"
resp = self.session.post(url, json={
"symbol": symbol,
"side": side.lower(),
"order_type": order_type.lower(),
"amount": str(amount),
"price": str(price) if price else None,
"leverage": leverage,
"signature": signature,
"timestamp": timestamp
})

return resp.json()

def get_balances(self):
url = f"{self.base_url}/api/v1/account/balances"
resp = self.session.get(url)
return resp.json()

def cancel_all_orders(self, symbol=None):
# 获取挂单
orders = self.get_open_orders(symbol)
order_ids = [o['id'] for o in orders]

if not order_ids:
return

# 批量取消
import time
timestamp = int(time.time())
signature = self.sign_batch_cancel(order_ids, timestamp)

url = f"{self.base_url}/api/v1/orders/batch"
resp = self.session.post(url, json={
"order_ids": order_ids,
"signature": signature,
"timestamp": timestamp
})

return resp.json()

EIP-712 签名详解​

Domain Separator​

所有签名都使用相同的 Domain Separator:

domain_type_hash = keccak(
b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
)

domain_separator = keccak(
domain_type_hash +
keccak(b"AXBlade") + # 测试网为 AXBlade,主网为 XBlade Vault
keccak(b"1") + # version
encode(['uint256'], [421614]) + # chainId (Arbitrum Sepolia)
encode(['address'], ["0xAAbdbE3F3382D8E620cDeCD9b06022b8E63bE33D"]) # verifyingContract
)

签名流程​

  1. 计算 Type Hash: 对类型字符串进行 keccak256
  2. 计算 Struct Hash: 编码所有参数并 keccak256
  3. 计算 Message Hash: keccak256(0x19 0x01 + domain_separator + struct_hash)
  4. 签名: 使用私钥对 Message Hash 进行签名

类型定义​

操作Type Hash
登录Login(address wallet,uint256 nonce,uint256 timestamp)
下单CreateOrder(address wallet,string symbol,string side,string orderType,string price,string amount,uint32 leverage,uint256 timestamp)
取消单个订单CancelOrder(address wallet,string orderId,uint256 timestamp)
批量取消BatchCancelOrders(address wallet,string orderIds,uint256 timestamp)

错误处理​

常见错误码​

状态码说明解决方法
401未授权重新登录获取 token
400参数错误检查请求参数格式
403签名验证失败检查签名逻辑和参数
429请求过多降低请求频率
500服务器错误稍后重试

错误响应示例​

{
"error": "INSUFFICIENT_BALANCE",
"message": "Insufficient balance for order",
"code": 400
}

最佳实践​

1. 会话管理​

class SessionManager:
def __init__(self, base_url, auth_manager):
self.session = requests.Session()
self.is_logged_in = False

def get_session(self):
if not self.is_logged_in:
self.login()
return self.session

2. 自动重试​

def api_call_with_retry(func, max_retries=3):
for i in range(max_retries):
try:
return func()
except requests.exceptions.RequestException as e:
if i == max_retries - 1:
raise
time.sleep(2 ** i) # 指数退避

3. 签名缓存​

# 对于相同参数的签名可以缓存
signature_cache = {}

def get_or_create_signature(params):
cache_key = hash(frozenset(params.items()))
if cache_key not in signature_cache:
signature_cache[cache_key] = sign(params)
return signature_cache[cache_key]

4. 批量操作​

# 批量取消订单,每次最多20个
CHUNK_SIZE = 20

for i in range(0, len(order_ids), CHUNK_SIZE):
chunk = order_ids[i:i + CHUNK_SIZE]
cancel_orders(chunk)
time.sleep(0.1) # 限流保护

安全注意事项​

⚠️ 重要提示:

  1. 私钥安全:

    • 永远不要在代码中硬编码私钥
    • 使用环境变量或加密配置文件
    • 定期轮换私钥
  2. 签名验证:

    • 确保 Domain Separator 参数正确
    • 时间戳误差不超过 ±30秒
    • 验证签名格式(0x + 130字符)
  3. 请求安全:

    • 使用 HTTPS 连接
    • 验证服务器证书
    • 不要在日志中打印敏感信息
  4. 限流保护:

    • 遵守 API 限流规则
    • 实现指数退避重试
    • 批量操作添加延迟

相关文档​