DEX 交易 API(0x Swap)
通过 0x 协议进行去中心化交易所交易。跨 100+ DEX 的最优执行路由,支持 Permit2 和 Gasless V2。需要认证(API Key 或 x402 支付)。按 $0.001/次 计费,执行交换另需链上 gas。
认证
两种方式均支持 — 所有请求通过 x402 链上结算:
| 方式 | Header | 描述 |
|---|---|---|
| API Key | Authorization: Bearer sk-... | 平台自动从你的 HD 钱包签名 x402 |
| 私钥(x402) | 通过 SDK 自动 | Agent 直接从自身钱包签名 x402 |
详见 Agent 支付(x402) 了解完整详情。
基础 URL
https://api.jarvisclaw.ai/v1/marketplace/dex定价
认证(API Key 或 x402)是访问 DEX 端点的必要条件。
| 范围 | 价格 |
|---|---|
| 所有 DEX 端点,任意方法 | $0.001/次 |
除按次费用外,提交交易还需支付标准链上 gas。Gasless 交换可以消除受支持代币的 gas 部分,但不免除 按次调用费。
这些端点并非免费
早前文档和部分价格列表称 DEX 交易免费,这是不准确的 —— 每次 DEX 调用都会计费。精确扣费金额 请读响应中的 price.amount。
关于链
这里有两个容易混淆的概念:
| 链 | |
|---|---|
| 支付 —— 你如何为这次调用付费 | Base(eip155:8453)和 Solana,均为 USDC |
交换目标 —— 你传入的 chainId | 透传给上游 0x 路由 |
无论在哪条链上交换,付费都发生在 Base 或 Solana。网关不维护自己的交换目标链白名单 —— chainId 是直接透传的,哪些值可用由上游路由在调用时决定。8453(Base)已验证。 需要其他链请直接发送并检查响应,不要假定支持。
端点
| 方法 | 端点 | 描述 | 价格 |
|---|---|---|---|
| GET | /price | 参考交换价格(无承诺) | $0.001/次 |
| GET | /quote | 确认报价,含 calldata + Permit2 数据 | $0.001/次 |
| POST | /gasless/submit | 提交已签名的 gasless 交换 | $0.001/次 |
| GET | /gasless/status/:tradeHash | 跟踪 gasless 交换状态 | $0.001/次 |
| POST | /swap/permit2/quote | Permit2 交换报价 | $0.001/次 |
| POST | /swap/permit2/execute | 执行 Permit2 交换 | $0.001/次 |
获取价格
GET /v1/marketplace/dex/price
获取代币交换的参考价格,无需承诺。用于 UI 显示或交易前检查。
参数
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
sellToken | string | 是 | 要卖出的代币合约地址 |
buyToken | string | 是 | 要买入的代币合约地址 |
sellAmount | string | 是 | 以基本单位表示的卖出数量(例如 1000000 = 1 USDC) |
chainId | integer | 是 | 目标链 ID |
taker | string | 否 | Taker 钱包地址(用于更准确的报价) |
响应
{
"buyAmount": "415000000000000000",
"sellAmount": "1000000000",
"price": "0.000415",
"sources": [
{"name": "Uniswap_V3", "proportion": "0.8"},
{"name": "SushiSwap", "proportion": "0.2"}
],
"gas": "150000",
"estimatedGas": "150000"
}获取报价
GET /v1/marketplace/dex/quote
获取确认报价,含可执行的 calldata 和 Permit2 签名数据。
参数
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
sellToken | string | 是 | 要卖出的代币合约地址 |
buyToken | string | 是 | 要买入的代币合约地址 |
sellAmount | string | 是 | 以基本单位表示的卖出数量 |
chainId | integer | 是 | 目标链 ID |
taker | string | 是 | Taker 钱包地址 |
响应
{
"buyAmount": "415000000000000000",
"sellAmount": "1000000000",
"to": "0x...",
"data": "0x...",
"value": "0",
"gas": "200000",
"permit2": {
"eip712": { "...EIP-712 typed data..." }
}
}提交 Gasless 交换
POST /v1/marketplace/dex/gasless/submit
提交已签名的 gasless 交换。需要对报价的 Permit2 EIP-712 数据进行签名。
请求体
{
"trade": { "...完整的报价对象..." },
"signature": "0xYourEIP712Signature..."
}响应
{
"tradeHash": "0x9f8e7d6c5b4a3210...",
"status": "submitted"
}查询 Gasless 交换状态
GET /v1/marketplace/dex/gasless/status/:tradeHash
响应
{
"tradeHash": "0x9f8e7d6c5b4a3210...",
"status": "confirmed",
"txHash": "0xabc123...",
"blockNumber": 12345678
}状态值: submitted → pending → confirmed | failed
代码示例
# 获取参考价格
curl "https://api.jarvisclaw.ai/v1/marketplace/dex/price?\
chainId=8453&\
sellToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&\
buyToken=0x4200000000000000000000000000000000000006&\
sellAmount=1000000000&\
taker=0xYourWalletAddress" \
-H "Authorization: Bearer sk-your-api-key"
# 提交 gasless 交换
curl -X POST https://api.jarvisclaw.ai/v1/marketplace/dex/gasless/submit \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"trade": { "...报价对象..." },
"signature": "0xYourEIP712Signature..."
}'
# 检查 gasless 交换状态
curl "https://api.jarvisclaw.ai/v1/marketplace/dex/gasless/status/0x9f8e7d6c5b4a3210" \
-H "Authorization: Bearer sk-your-api-key"import requests
import time
BASE = "https://api.jarvisclaw.ai/v1/marketplace/dex"
HEADERS = {"Authorization": "Bearer sk-your-api-key"}
# Base 链上的代币地址
USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
WETH_BASE = "0x4200000000000000000000000000000000000006"
# 1. 获取 1000 USDC -> WETH 的参考价格
resp = requests.get(f"{BASE}/price", headers=HEADERS, params={
"chainId": 8453,
"sellToken": USDC_BASE,
"buyToken": WETH_BASE,
"sellAmount": "1000000000", # 1000 USDC (6 位小数)
})
price = resp.json()
print(f"买入数量: {price['buyAmount']} wei WETH")
print(f"路由经过: {[s['name'] for s in price['sources']]}")
# 2. 获取确认报价
resp = requests.get(f"{BASE}/quote", headers=HEADERS, params={
"chainId": 8453,
"sellToken": USDC_BASE,
"buyToken": WETH_BASE,
"sellAmount": "1000000000",
"taker": "0xYourWalletAddress",
})
quote = resp.json()
# 3. 签名 EIP-712 permit2 数据(需要 eth_account)
# signature = sign_eip712(quote["permit2"]["eip712"], private_key)
# 4. 提交 gasless 交换
resp = requests.post(f"{BASE}/gasless/submit", headers=HEADERS, json={
"trade": quote,
"signature": "0x<your-eip712-signature>",
})
trade_hash = resp.json()["tradeHash"]
# 5. 轮询状态
while True:
resp = requests.get(f"{BASE}/gasless/status/{trade_hash}", headers=HEADERS)
status = resp.json()
if status["status"] == "confirmed":
print(f"交换已确认!TX: {status['txHash']}")
break
elif status["status"] == "failed":
print("交换失败")
break
time.sleep(2)from jarvisclaw import MarketplaceClient
# x402 Agent — 自动使用 USDC 支付 $0.001/次 的调用费
client = MarketplaceClient(private_key="0x<agent-wallet-private-key>")
USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
WETH_BASE = "0x4200000000000000000000000000000000000006"
# 获取价格($0.001 — 通过 x402 结算)
price = client.call("dex", "/price", params={
"chainId": 8453,
"sellToken": USDC_BASE,
"buyToken": WETH_BASE,
"sellAmount": "1000000000",
})
print(f"价格: {price['buyAmount']} wei 对应 1000 USDC")
# 获取确认报价
quote = client.call("dex", "/quote", params={
"chainId": 8453,
"sellToken": USDC_BASE,
"buyToken": WETH_BASE,
"sellAmount": "1000000000",
"taker": "0xYourWalletAddress",
})
# 提交 gasless 交换(外部签名 permit2 后)
result = client.call("dex", "/gasless/submit", method="POST", json={
"trade": quote,
"signature": "0x<signed-permit2>",
})
print(f"交易哈希: {result['tradeHash']}")
# 轮询状态
status = client.call("dex", f"/gasless/status/{result['tradeHash']}")
print(f"状态: {status['status']}")package main
import (
"context"
"fmt"
"time"
jarvisclaw "github.com/api-jarvisclaw/go-sdk/v2"
)
func main() {
mc, _ := jarvisclaw.NewMarketplaceClient(jarvisclaw.WithAPIKey("sk-your-api-key"))
ctx := context.Background()
usdcBase := "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
wethBase := "0x4200000000000000000000000000000000000006"
// 1. 获取参考价格
price, err := mc.Call(ctx, "dex", "/price", jarvisclaw.WithParams(map[string]string{
"chainId": "8453",
"sellToken": usdcBase,
"buyToken": wethBase,
"sellAmount": "1000000000",
}))
if err != nil {
panic(err)
}
fmt.Printf("买入数量: %s wei\n", price["buyAmount"])
// 2. 获取确认报价
quote, err := mc.Call(ctx, "dex", "/quote", jarvisclaw.WithParams(map[string]string{
"chainId": "8453",
"sellToken": usdcBase,
"buyToken": wethBase,
"sellAmount": "1000000000",
"taker": "0xYourWalletAddress",
}))
if err != nil {
panic(err)
}
// 3. 签名 EIP-712 数据(外部处理)
// signature := signEIP712(quote["permit2"], privateKey)
// 4. 提交 gasless 交换
result, _ := mc.Post(ctx, "dex", "/gasless/submit", map[string]interface{}{
"trade": quote,
"signature": "0x<signed-permit2>",
})
fmt.Printf("交易哈希: %s\n", result["tradeHash"])
// 5. 轮询状态
for {
status, _ := mc.Call(ctx, "dex", "/gasless/status/"+result["tradeHash"].(string), nil)
if status["status"] == "confirmed" {
fmt.Printf("已确认!TX: %s\n", status["txHash"])
break
}
time.Sleep(2 * time.Second)
}
}package main
import (
"context"
"fmt"
jarvisclaw "github.com/api-jarvisclaw/go-sdk/v2"
)
func main() {
// x402 Agent 钱包 — 通过 Base 上的 USDC 按调用付费 (Chain ID 8453)
mc, _ := jarvisclaw.NewMarketplaceClient(jarvisclaw.WithPrivateKey("0x<evm-private-key>"))
ctx := context.Background()
usdcBase := "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
wethBase := "0x4200000000000000000000000000000000000006"
// 获取参考价格
price, _ := mc.Call(ctx, "dex", "/price", jarvisclaw.WithParams(map[string]string{
"chainId": "8453",
"sellToken": usdcBase,
"buyToken": wethBase,
"sellAmount": "1000000000",
}))
fmt.Printf("1000 USDC -> %s wei WETH\n", price["buyAmount"])
// 获取报价
quote, _ := mc.Call(ctx, "dex", "/quote", jarvisclaw.WithParams(map[string]string{
"chainId": "8453",
"sellToken": usdcBase,
"buyToken": wethBase,
"sellAmount": "1000000000",
"taker": "0xYourWalletAddress",
}))
// 签名 + 提交 gasless 交换
result, _ := mc.Post(ctx, "dex", "/gasless/submit", map[string]interface{}{
"trade": quote,
"signature": "0x<signed-permit2>",
})
fmt.Printf("已提交: %s\n", result["tradeHash"])
}错误
网关层错误是扁平结构 —— 只有一个 error 字符串,没有嵌套 code:
{ "error": "service 'dex' is at capacity, please retry in a moment" }| HTTP | 含义 | 处理 |
|---|---|---|
| 401 | 缺少或无效的 API Key / x402 签名 | 检查 Authorization,或让 SDK 重新签名 |
| 402 | 结算失败 —— 无法支付本次调用 | 充值后重试 |
| 403 | 余额不足 | 充值,或检查你的支出限额 |
| 404 | 路径不存在 | 对照上面的端点表核对 |
| 429 | 并发请求过多 | 退避后重试 |
| 502 | 上游路由失败 | 重试;持续失败说明上游宕机 |
| 503 | 服务暂时不可用 | 稍后重试 |
交换本身的失败 —— 流动性不足、报价过期、代币地址无效、签名验证失败、未知 tradeHash、 或 chainId 不受支持 —— 由上游 0x 路由抛出并原样透传,其结构和文案是上游的,不是我们的。
限制
- 支付仅限 Base 或 Solana — 无论在哪条链上交换,调用费都以 Base 或 Solana 上的 USDC 支付
- 交换目标链由上游路由决定 —
chainId透传给上游;8453已验证,其他链取决于路由 - Gasless 需要 Permit2 — 只有获得 Permit2 批准的代币才能使用 gasless 交换(大多数 ERC-20 代币支持)
- 报价 30 秒过期 — 报价具有时效性;请及时签名并提交
- 无限价订单 — 仅支持即时市价交换;不支持条件或定时订单
- 大额交易的价格影响 — 超过 $100k 的交换可能因 DEX 流动性深度而产生显著滑点
- 需要代币地址 — 使用合约地址而非符号;在目标链上验证地址
- 仅基本单位 — 所有数量都使用代币的最小面额(USDC 为 6 位小数,ETH/WETH 为 18 位小数)
- 无部分成交 — 整笔交易执行或回滚;不支持部分执行