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 即可与合约进行交互。

本指南涵盖了通过豁免服务器 API 进行的集成。

前提条件

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

Waiver Server

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

授权: Bearer <your-api-key>

概述

集成流程分为三个步骤:

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

步骤 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。