제로 가스 트랜잭션
가스 면제(Gas Waiver)를 사용하면 애플리케이션이 사용자를 대신하여 가스 요금을 지불할 수 있습니다. 사용자는 gasPrice = 0으로 트랜잭션에 서명하고, 거버넌스에 등록된 면제자가 이를 래핑하며, 검증자는 사용자에게 비용 없이 호출을 실행합니다. 이 가이드에서는 적격한 전송을 진행하고, 가스가 면제되었는지 확인하는 방법, 그리고 면제가 무엇을 다루고 무엇을 다루지 않는지 설명합니다.
구축할 내용
호스팅된 면제 서버를 통해 USDT0 전송을 제출하고, 영수증을 가져와서 gasPrice = 0을 확인하는 두 가지 스크립트 흐름입니다.
데모
1단계. 지갑 연결, 잔액이 0.01 USDT0으로 표시됩니다.
2단계. 가스 면제를 통해 트랜잭션 전송 → [실행]
3단계. 결과
tx: 0x8f3a...2d41
귀하가 지불한 가스 수수료: 0.000000 USDT0
이후 잔액: 0.01 USDT0면제가 적용되는 경우
모든 다음 조건이 충족될 때 트랜잭션이 적격하다고 간주됩니다.
- 사용자는
gasPrice = 0으로 내부 트랜잭션에 서명합니다. - 제출자는 거버넌스에 등록된 면제 주소입니다.
- 대상
to주소와 메서드 선택기가 면제자의AllowedTarget정책에 있습니다. - 래퍼는
value = 0및gasPrice = 0으로 마커 주소0x000000000000000000000000000000000000f333로 전송됩니다.
이 중 하나라도 실패하면 검증자는 내부 호출을 실행하지 않고 래퍼를 거부합니다. AllowedTarget에 나열되지 않은 계약 호출은 포함되지 않습니다. 임의의 셀프 서비스 면제는 불가능합니다. 모든 면제는 검증자 거버넌스를 통해 등록되어야 합니다.
전제 조건
- Stable 팀에서 발행한 면제 서버용 API 키.
- 면제자의
AllowedTarget정책에 등록된 대상 계약 주소 및 메서드 선택기. - 가스에 필요한 USDT0가 없는 테스트넷 사용자의 지갑.
1단계: 적격 InnerTx 서명
사용자는 gasPrice = 0으로 표준 트랜잭션에 서명합니다. 이 예제에서 호출은 USDT0 transfer이며, 이는 애플리케이션에서 처리하는 가스 흐름에 대한 일반적인 AllowedTarget입니다.
// config.ts
import { ethers } from "ethers";
import "dotenv/config";
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",
};
export const provider = new ethers.JsonRpcProvider(CONFIG.RPC_URL);
export const userWallet = new ethers.Wallet(process.env.USER_PRIVATE_KEY!, provider);// signInner.ts
import { ethers } from "ethers";
import { CONFIG, provider, userWallet } from "./config";
const usdt0 = new ethers.Contract(CONFIG.USDT0_ADDRESS, [
"function transfer(address to, uint256 amount) returns (bool)"
], provider);
const callData = usdt0.interface.encodeFunctionData("transfer", [
"0xRecipientAddress",
ethers.parseUnits("0.001", 18),
]);
const gasLimit = 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,
nonce,
chainId: CONFIG.CHAIN_ID,
};
export const signedInnerTx = await userWallet.signTransaction(innerTx);
console.log("Signed InnerTx:", signedInnerTx);npx tsx signInner.tsSigned InnerTx: 0xf8a8...c12단계: 면제 서버를 통해 제출
면제 서버는 서명된 내부 트랜잭션을 래핑하고 브로드캐스트합니다. 서버에서 발행한 API 키가 필요합니다.
// submit.ts
import { CONFIG } from "./config";
import { signedInnerTx } from "./signInner";
const response = await fetch(`${CONFIG.WAIVER_SERVER}/v1/submit`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.WAIVER_API_KEY}`,
},
body: JSON.stringify({ transactions: [signedInnerTx] }),
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let txHash = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).trim().split("\n")) {
const result = JSON.parse(line);
if (result.success) {
txHash = result.txHash;
console.log(`tx confirmed: ${txHash}`);
} else {
console.error(`tx failed: ${result.error.message}`);
}
}
}
export { txHash };npx tsx submit.tstx confirmed: 0x8f3a...2d413단계: 영수증에 가스 0이 표시되는지 확인
영수증을 가져와 effectiveGasPrice가 0인지 확인합니다. 이는 사용자가 가스를 지불하지 않았음을 증명하는 암호화 증거입니다.
// verify.ts
import { provider } from "./config";
import { txHash } from "./submit";
const receipt = await provider.getTransactionReceipt(txHash);
const gasUsed = receipt!.gasUsed;
const effectiveGasPrice = receipt!.gasPrice;
const totalFee = gasUsed * effectiveGasPrice;
console.log("Gas used: ", gasUsed.toString());
console.log("Effective gas price:", effectiveGasPrice.toString());
console.log("Gas fee paid: ", `${totalFee.toString()} USDT0 (wei-equivalent)`);npx tsx verify.tsGas used: 21000
Effective gas price: 0
Gas fee paid: 0 USDT0 (wei-equivalent)effectiveGasPrice가 0이면 트랜잭션이 등록된 면제 하에 실행되었고 사용자에게 요금이 부과되지 않았음을 확인합니다.
가스 면제가 다루지 않는 것
AllowedTarget외부의 계약: 임의의 계약 호출은 다루지 않습니다. 각 대상은 거버넌스를 통해 면제당 범위가 지정됩니다.- 사용자가 제출한 래퍼: 사용자가 직접
0x...f333에 제출하면 실패합니다. 등록된 면제 주소만 래핑할 수 있습니다. - 수수료 추출: 검증자는 내부 또는 래퍼 트랜잭션에서 0이 아닌
gasPrice를 허용하지 않습니다.
전체 정책 모델 및 면제별 범위 규칙은 가스 면제 프로토콜을 참조하세요.
다음 권장 사항
- 면제 서버 통합: 전체 API 참조, 배치 제출, 오류 코드 및 NDJSON 스트리밍.
- 자체 호스팅 가스 면제: 자체 면제 주소를 등록하고 호스팅 API 없이 래퍼를 브로드캐스트합니다.
- 가스 면제 프로토콜: 전체 사양을 읽어보세요: 마커 라우팅, 래퍼 형식, 거버넌스 제어.

