수수료 위임은 트랜잭션 서명자와 가스 납부자를 분리하는 StableNet 전용 트랜잭션 타입(0x16)입니다. 표준 EVM 체인에는 존재하지 않는 기능입니다.
수수료 위임이란?
표준 EVM에서는 트랜잭션을 보낸 계정이 가스를 납부합니다. StableNet은 여기에 수수료 납부자(Fee Payer) 역할을 추가합니다. 수수료 납부자는 가스 비용만 부담하고, 발신자는 전송 금액만 책임집니다.
| 역할 | 책임 |
|---|
발신자 (From) | 트랜잭션 의도 서명, 전송 금액 제공, 유효한 nonce 보유 |
수수료 납부자 (FeePayer) | 가스 납부 동의 서명, gasLimit × gasPrice 납부, 미사용 가스 환불 수령 |
두 계정 모두 서명해야 합니다. 어느 쪽 서명도 생략할 수 없습니다.
수수료 위임은 네트워크에 Applepie 포크가 활성화되어 있어야 사용할 수 있습니다. 포크 이전에 제출된 0x16 타입 트랜잭션은 즉시 거부됩니다.
동작 방식
발신자가 트랜잭션에 서명
EIP-1559 스타일 트랜잭션을 구성하고 type을 0x16으로 설정합니다. 페이로드에 feePayer 주소를 추가한 후, 발신자 개인 키로 서명하여 v, r, s를 생성합니다.{
"type": "0x16",
"chainId": "0x2052",
"nonce": "0x1",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"maxFeePerGas": "0x9184e72a000",
"maxPriorityFeePerGas": "0x5f5e100",
"value": "0xde0b6b3a7640000",
"input": "0x",
"accessList": [],
"feePayer": "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
}
수수료 납부자가 서명
수수료 납부자는 부분 서명된 트랜잭션을 받아 내용을 확인한 후 서명하여 fv, fr, fs를 생성합니다.
완성된 트랜잭션 브로드캐스트
두 서명 세트가 모두 포함된 트랜잭션을 eth_sendRawTransaction으로 제출합니다. 노드는 양쪽 서명을 모두 검증한 후 멤풀에 수용합니다.
노드의 처리 흐름:
- 잔액 검증: 수수료 납부자는
gasLimit × gasPrice 이상, 발신자는 value 이상 보유해야 함
- 실행:
tx.origin은 항상 발신자 — 스마트 컨트랙트는 수수료 위임 여부를 감지할 수 없음
- 환불: 미사용 가스는 발신자가 아닌 수수료 납부자에게 환불됨
ethers.js 예제
아래는 ethers.js v6을 사용한 2-서명 흐름 예제입니다.
// requires: npm install ethers (v6)
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://api.test.stablenet.network");
const senderWallet = new ethers.Wallet(SENDER_PRIVATE_KEY, provider);
const feePayerWallet = new ethers.Wallet(FEE_PAYER_PRIVATE_KEY, provider);
// 1. 미서명 트랜잭션 구성 (type 0x16)
const unsignedTx = {
type: 0x16,
chainId: 0x205B,
nonce: await provider.getTransactionCount(senderWallet.address),
to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
value: ethers.parseEther("1.0"),
gas: 30400n,
maxFeePerGas: ethers.parseUnits("10000", "gwei"),
maxPriorityFeePerGas: ethers.parseUnits("100", "gwei"),
feePayer: feePayerWallet.address,
input: "0x",
accessList: [],
};
// 2. 발신자 서명
const senderSig = await senderWallet.signTransaction(unsignedTx);
// 3. 디코딩 후 수수료 납부자 서명 추가
const decoded = ethers.Transaction.from(senderSig);
const feePayerSig = await feePayerWallet.signTransaction(decoded);
// 4. 브로드캐스트
const txResponse = await provider.broadcastTransaction(feePayerSig);
const receipt = await txResponse.wait();
console.log("블록 확정:", receipt.blockNumber);
수수료 납부자는 서명 전 to, value, data, gas 값을 반드시 검증하세요. 의도하지 않은 작업에 대한 가스를 납부하지 않도록 주의해야 합니다.
활용 사례
- 가스리스 온보딩 — 신규 사용자가 WKRC 없이 dApp과 상호작용 가능
- 수수료 보조 — 서비스 백엔드 계정이 특정 사용자 액션(첫 민팅, 일일 클레임 등)의 가스를 대납
- 배치 릴레이어 — 단일 수수료 납부자 지갑이 다수 사용자의 트랜잭션을 처리
개발자 혜택
- 사용자 지갑 UX 변경 없음 — 발신자는 일반 트랜잭션과 동일하게 서명; 가스 처리 구조는 사용자에게 투명
tx.origin은 항상 발신자 — msg.sender 또는 tx.origin을 확인하는 기존 컨트랙트가 수정 없이 동작
- 잔액 격리 — 수수료 납부자 자금은 가치 이전에 사용될 수 없으며 가스 비용만 위험에 노출됨
- 미사용 가스 자동 환불 — 수수료 납부자가 환불을 자동으로 수령하여 과납 비용 절감
관련 페이지
시스템 개요
StableNet 아키텍처에서 수수료 위임의 위치.
가스 및 수수료
기본 수수료 정책, 팁 한도 및 수수료 계산 방식.