自托管燃气费代付
自托管燃气费代付让您可以操作自己的代付基础设施,而不是使用托管的Waiver Server API。您通过链上治理注册一个代付地址,然后直接向网络广播封装器交易。
本指南涵盖注册代付地址、收集已签名的用户交易、构建封装器交易以及广播它们。
有关托管Waiver Server API集成路径,请参阅启用免燃气费交易。
先决条件
- 一个通过验证者治理在链上注册的代付地址。
- 为您的目标合约配置了
AllowedTarget策略。
概览
自托管流程:
- 收集已签名的 InnerTx,其中用户的
gasPrice = 0。 - 构建 WrapperTx:RLP 编码 InnerTx 并将其封装在发送到标记地址的交易中。
- 通过
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。

