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

自托管燃气费代付

自托管燃气费代付让您可以操作自己的代付基础设施,而不是使用托管的Waiver Server API。您通过链上治理注册一个代付地址,然后直接向网络广播封装器交易。

本指南涵盖注册代付地址、收集已签名的用户交易、构建封装器交易以及广播它们。

有关托管Waiver Server API集成路径,请参阅启用免燃气费交易

先决条件

  • 一个通过验证者治理在链上注册的代付地址。
  • 为您的目标合约配置了AllowedTarget策略。

概览

自托管流程:

  1. 收集已签名的 InnerTx,其中用户的gasPrice = 0
  2. 构建 WrapperTx:RLP 编码 InnerTx 并将其封装在发送到标记地址的交易中。
  3. 通过 eth_sendRawTransaction 广播 WrapperTx。

步骤 1:收集用户的 InnerTx

用户签署一个gasPrice = 0的交易。to地址和方法选择器必须与您的代付的AllowedTarget策略匹配。

// config.ts
export const CONFIG = {
  RPC_URL: "https://rpc.testnet.stable.xyz",
  CHAIN_ID: 2201, // 988 for mainnet
  MARKER_ADDRESS: "0x000000000000000000000000000000000000f333",
  USDT0_ADDRESS: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9",
};
// collectInnerTx.ts
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:构建 WrapperTx

RLP 编码已签名的 InnerTx 并将其封装在发送到标记地址的交易中。gasLimit必须覆盖内部执行和封装开销。

// constructWrapper.ts
import { ethers } from "ethers";
import { CONFIG } from "./config";
 
const innerTxBytes = ethers.decodeRlp(signedInnerTx);
const rlpEncoded = ethers.encodeRlp(innerTxBytes);
 
const waiverNonce = await provider.getTransactionCount(waiverWallet.address);
 
const wrapperTx = {
  to: CONFIG.MARKER_ADDRESS,
  data: rlpEncoded,
  value: 0,
  gasPrice: 0,
  gasLimit: (gasEstimate * 12n / 10n) * 2n,  // ~2x inner gas for overhead
  nonce: waiverNonce,
  chainId: CONFIG.CHAIN_ID,
};
 
const signedWrapperTx = await waiverWallet.signTransaction(wrapperTx);

步骤 3:广播

通过标准 JSON-RPC 提交已签名的 WrapperTx。

// broadcast.ts
const txHash = await provider.send("eth_sendRawTransaction", [signedWrapperTx]);
console.log("Wrapper tx broadcast:", txHash);
 
const receipt = await provider.waitForTransaction(txHash);
console.log("Confirmed:", receipt.status === 1);
Wrapper tx broadcast: 0x...
Confirmed: true

主要收获

  • 自托管代付需要通过链上验证者治理注册一个代付地址。
  • WrapperTx 会发送到标记地址 (0x...f333),数据为 RLP 编码的 InnerTx。
  • InnerTx 和 WrapperTx 的 gasPrice 都必须为 0,且 value 也必须为 0

下一步建议