启用免Gas交易
Gas Waiver 可以在 Stable 上启用免 Gas 交易。通过 Gas Waiver,应用程序可以代表用户支付 Gas 费用,因此用户无需持有 USDT0 即可与合约进行交互。
本指南涵盖了通过豁免服务器 API 进行的集成。
前提条件
- 由 Stable 团队颁发的 Waiver Server API 密钥
- 目标合约地址必须在豁免的
AllowedTarget策略中注册
Waiver Server
基础 URL:- 主网:待定
- 测试网:
https://waiver.testnet.stable.xyz
授权: Bearer <your-api-key>
概述
集成流程分为三个步骤:
- 构建 InnerTx:用户使用
gasPrice = 0签署交易。 - 提交到 Waiver Server:将已签名的交易提交到 Waiver Server API。
- 处理响应:豁免服务器封装并广播交易。处理流式结果并将交易哈希显示给用户。
步骤 1:创建用户的 InnerTx
用户使用 gasPrice = 0 签署标准交易。to 地址和方法选择器必须得到豁免的 AllowedTarget 策略的允许。
// config.ts
export const CONFIG = {
RPC_URL: "https://rpc.testnet.stable.xyz",
CHAIN_ID: 2201, // 988 for mainnet
WAIVER_SERVER: "https://waiver.testnet.stable.xyz",
USDT0_ADDRESS: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9",
};import { ethers } from "ethers";
import { CONFIG } from "./config";
const provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
const usdt0 = new ethers.Contract(CONFIG.USDT0_ADDRESS, [
"function transfer(address to, uint256 amount) returns (bool)"
], provider);
const callData = usdt0.interface.encodeFunctionData("transfer", [
recipientAddress,
ethers.parseUnits("0.01", 18)
]);
const gasEstimate = await provider.estimateGas({
from: userWallet.address,
to: CONFIG.USDT0_ADDRESS,
data: callData,
});
const nonce = await provider.getTransactionCount(userWallet.address);
const innerTx = {
to: CONFIG.USDT0_ADDRESS,
data: callData,
value: 0,
gasPrice: 0,
gasLimit: gasEstimate,
nonce: nonce,
chainId: CONFIG.CHAIN_ID,
};
const signedInnerTx = await userWallet.signTransaction(innerTx);步骤 2:提交到 Waiver Server
import { CONFIG } from "./config";
const API_KEY = process.env.WAIVER_API_KEY;
const response = await fetch(`${CONFIG.WAIVER_SERVER}/v1/submit`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${API_KEY}`,
},
body: JSON.stringify({
transactions: [signedInnerTx],
}),
});批量提交
您可以在单个请求中提交多个已签名的交易:
body: JSON.stringify({
transactions: [signedTx1, signedTx2, signedTx3],
})每个结果行都包含一个 index 字段,对应于交易在数组中的位置。
步骤 3:处理响应
响应以 NDJSON(换行符分隔的 JSON)流式传输。每行对应一个已提交的交易。
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const lines = decoder.decode(value).trim().split("\n");
for (const line of lines) {
const result = JSON.parse(line);
if (result.success) {
console.log(`tx ${result.index} confirmed: ${result.txHash}`);
} else {
console.error(`tx ${result.index} failed: ${result.error.message}`);
}
}
}{"index": 0, "id": "abc123", "success": true, "txHash": "0x..."}{"index": 1, "id": "def456", "success": false, "error": {"code": "VALIDATION_FAILED", "message": "invalid signature"}}错误代码
| 代码 | 描述 |
|---|---|
PARSE_ERROR | 无法解析交易 |
INVALID_REQUEST | 请求正文格式错误 |
BATCH_SIZE_EXCEEDED | 批量大小超出允许的最大值 |
VALIDATION_FAILED | 交易验证失败(例如,签名无效,不允许的目标) |
BROADCAST_FAILED | 无法广播到链上 |
RATE_LIMITED | 超出速率限制 |
QUEUE_FULL | 服务器队列已满 |
TIMEOUT | 请求超时 |
API参考
GET /v1/health
健康检查端点。认证:无。
POST /v1/submit
提交一批已签名的内部交易。认证:必需(Bearer)。
请求正文:{
"transactions": ["0x<signedInnerTx1>", "0x<signedInnerTx2>"]
}响应以 NDJSON 形式流式传输。每行对应一个已提交的交易索引。
GET /v1/submit
用于流式提交的 WebSocket 接口。认证:必需(Bearer)。
主要收获
- Gas Waiver 是一种服务器端集成:您的后端将已签名的用户交易提交到 Waiver Server。用户从不直接与 Waiver Server 交互。
- 用户始终签署 InnerTx,以保持签名的完整性。豁免无法修改用户的交易。
- 目标合约必须在豁免的
AllowedTarget列表中。
下一步建议
- 零 Gas 交易:查看以演示为中心的流程以及如何验证收据上的零 Gas。
- 自托管 Gas Waiver:运行您自己的豁免,无需托管 API。
- Gas Waiver 协议:完整的包装器交易规范和治理模型。
- Stable SDK:使用类型化客户端签署用户交易,然后将其提交到 Waiver Server。

