Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

启用免Gas交易

Gas Waiver 可以在 Stable 上实现免 Gas 交易。通过 Gas Waiver,应用程序可以代表用户支付 Gas 费用,因此用户无需持有 USDT0 即可与合约进行交互。

本指南涵盖了托管的 Waiver Server API,Stable 在其中操作 Waiver,您可以使用 API 密钥进行身份验证。要通过治理注册您自己的 Waiver 地址,请参阅选择集成路径。

前提条件

  • 由 Stable 团队颁发的 Waiver Server API 密钥
  • 目标合约地址必须在 Waiver 的 AllowedTarget 策略中注册

Waiver Server

基础 URL:
  • 主网:待定
  • 测试网:https://waiver.testnet.stable.xyz

授权: Bearer <您的API密钥>

概述

集成流程分三步:

  1. 构建 InnerTx:用户使用 gasPrice = 0 签署交易。
  2. 提交到 Waiver Server:将签名的交易提交到 Waiver Server API。
  3. 处理响应:Waiver 服务器包装并广播交易。处理流式结果并将交易哈希显示给用户。

步骤 1:创建用户的 InnerTx

用户签署一个 gasPrice = 0 的标准交易。to 地址和方法选择器必须经过 Waiver 的 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,从而保持签名的完整性。Waiver 无法修改用户的交易。
  • 目标合约必须在 Waiver 的 AllowedTarget 列表中。

下一步推荐