Paymaster와 가스 정책
도입
Paymaster는 "사용자에게 gas를 무료로 보이게 하는 기능"이 아니라 sponsor risk를 떠안는 contract다. ERC-4337에서 UserOperation이 paymaster 정보를 포함하면 EntryPoint는 validation 단계에서 Paymaster를 호출하고, 실행 후 postOp으로 실제 비용과 회계 처리를 마무리할 수 있다.
Stablecoin checkout에서는 Paymaster가 강력한 UX 개선을 만든다. 사용자는 ETH 없이 USDC로 결제하고, merchant나 앱이 gas를 sponsor할 수 있다. 하지만 실패한 operation도 sponsor 비용을 태울 수 있으므로 policy, deposit, stake, monitoring이 함께 설계되어야 한다.
학습 목표
- gas sponsorship의 정책과 abuse risk를 설계한다.
- paymaster 예산과 사용자 한도를 운영 지표로 만든다.
개념 설명
핵심 가정 오류
서명 권한과 지출 한도가 분리되는가
운영 상태 누락
paymaster 정책이 설명 가능한가
학습 산출물 미흡
Paymaster와 가스 정책 이해 점검
Paymaster 흐름은 validation과 post-operation accounting으로 나뉜다.
| 단계 | 역할 | 실패하면 어떻게 되는가 |
|---|---|---|
| UserOperation 작성 | paymaster address와 data를 포함 | sponsor 대상이 아니면 bundler simulation에서 거절 |
validatePaymasterUserOp | token, merchant, method, cap, risk 조건 확인 | revert하면 UserOperation reject |
| EntryPoint deposit 사용 | sponsor gas 비용이 Paymaster deposit에서 차감 | deposit 부족이면 sponsor 불가 |
| execution | account callData 실행 | execution revert도 Paymaster 비용 발생 가능 |
postOp | 실제 gas cost 정산, logging, charge 처리 | accounting mismatch 또는 griefing 위험 |
Paymaster policy는 아래 축으로 쪼개야 한다.
| 정책 축 | 예시 | 운영 지표 |
|---|---|---|
| token | USDC만 sponsor | rejectedUnsupportedToken |
| recipient | allowlisted merchant만 | rejectedMerchant |
| method | payInvoice, refundClaim만 | rejectedMethod |
| value | tx당 100 USDC, 사용자당 하루 500 USDC | capUsagePercent |
| risk | KYT pass, fraud score threshold | rejectedRisk |
| gas | max verification/call/postOp gas | gasCostPerUserOp |
| budget | day/week sponsor budget | remainingSponsorBudget |
코드로 확인하기
paymaster는 무료 gas 버튼이 아니다. 어떤 operation을 대신 지불할지 결정하는 risk engine이다. 정책은 contract와 backend 양쪽에 나뉘어 구현된다.
컨트랙트paymaster 검증에서 허용 selector 제한
function validatePaymasterUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) external returns (bytes memory context, uint256 validationData) { if (msg.sender != entryPoint) revert OnlyEntryPoint(); bytes4 selector = bytes4(userOp.callData[0:4]); if (!sponsoredSelectors[selector]) revert SelectorNotSponsored(selector); if (maxCost > maxSponsoredGasCost) revert GasCostTooHigh(maxCost); address account = userOp.sender; _consumeDailyQuota(account, maxCost); return (abi.encode(account, maxCost, userOpHash), 0);}이 코드에서 중요한 것은 paymaster가 모든 호출을 지원하지 않는다는 점이다. checkout, claim, refund처럼 제품이 설명 가능한 action만 sponsor해야 한다.
백엔드sponsorship 사전 판단
type SponsorshipPolicy = { allowedSelectors: Set<string>; dailyGasLimit: bigint; usedToday: bigint; riskScore: number;};function decideSponsorship(selector: string, estimatedCost: bigint, policy: SponsorshipPolicy) { if (!policy.allowedSelectors.has(selector)) return { allow: false, reason: "selector_blocked" }; if (policy.usedToday + estimatedCost > policy.dailyGasLimit) return { allow: false, reason: "quota_exceeded" }; if (policy.riskScore >= 80) return { allow: false, reason: "risk_score_high" }; return { allow: true, reason: "sponsored" };}backend는 사용자가 서명하기 전에 sponsorship 가능성을 알려준다. 이 단계가 없으면 사용자는 "gasless"라고 믿고 서명했는데 bundler 단계에서 거절되는 경험을 하게 된다.
인덱서sponsor budget과 거절 사유 집계
select date_trunc('hour', created_at) as bucket, rejection_reason, count(*) as rejected_ops, sum(estimated_gas_cost) as blocked_gas_costfrom paymaster_decisionswhere allowed = falsegroup by bucket, rejection_reasonorder by bucket desc;paymaster 정책은 배포 후에도 계속 조정된다. selector_blocked가 많으면 UX 설명이 부족할 수 있고, quota_exceeded가 늘면 merchant sponsor budget을 재설계해야 한다.
강의 포인트
| 관점 | 확인할 질문 | 증거로 남길 것 |
|---|---|---|
| sponsor 조건 | 어떤 UserOperation에 gas를 내줄 것인가 | paymaster policy table |
| abuse 방어 | replay, gas griefing, whitelist bypass를 어떻게 막는가 | abuse scenario table |
| accounting | gas cost와 stablecoin charge를 어떻게 reconcile하는가 | postOp ledger fields |
| stake/deposit | deposit 부족, unstake delay, sponsor pause를 어떻게 운영하는가 | budget dashboard |
| rejection UX | 사용자가 sponsor 거절 이유를 이해할 수 있는가 | rejection reason mapping |
실무 예시
merchant sponsored checkout을 생각해보자. merchant는 구매 전환율을 높이기 위해 gas를 sponsor하지만, 아무 UserOperation이나 받아주면 sponsor deposit이 공격자에게 소모될 수 있다.
| 요청 | sponsor 판단 | 이유 |
|---|---|---|
allowlisted merchant, USDC, 20 USDC, payInvoice | approve | 정책 범위 안 |
| unknown token, 20 USDC | reject | token allowlist 밖 |
| allowlisted merchant, 2,000 USDC | reject | transaction cap 초과 |
| same nonce replay | reject | replay 또는 duplicate request |
| execution revert 반복 | throttle | gas griefing 의심 |
흔한 오해와 실패 시나리오
| 오해 | 실제로 확인할 것 |
|---|---|
| Paymaster가 있으면 gas 문제가 사라진다고 본다. | gas 비용은 Paymaster deposit에서 나가며 budget과 stake가 필요하다. |
| offchain allowlist만 있으면 충분하다고 본다. | sponsor 조건은 signature, paymasterData, onchain validation과 맞아야 한다. |
| 실패 operation은 비용이 없다고 본다. | validation 또는 execution 실패도 Paymaster에 비용을 만들 수 있다. |
postOp를 logging 정도로 본다. | 실제 gas cost 정산과 abuse detection의 핵심 지점이다. |
| 사용자에게 "sponsor 실패"만 보여준다. | token, merchant, cap, risk, budget 중 어떤 조건이 실패했는지 분리해야 한다. |
실습 과제
- Paymaster와 가스 정책 이해 점검: token, merchant, method, value, risk, gas, budget 기준으로 policy table을 작성한다.
- abuse 시나리오 작성: replay abuse, gas griefing, whitelist bypass, stake draining, postOp accounting failure를 각각 방어 조건과 연결한다.
- 대시보드 지표 정의: sponsor spend, rejection rate, gas per successful payment, postOp failure, remaining deposit, cap usage를 정의한다.
- checkout 연결: stablecoin 결제 금액과 gas sponsor 비용을 하나의 reconciliation ledger로 연결하는 필드를 설계한다.
완료 기준
- paymaster 정책 축을 정의했다.
- abuse 방어 조건을 작성했다.
- 대시보드 지표를 정했다.
근거 자료
- Paymaster 가스 정책: 05-계정추상화-에이전트결제/03-Paymaster-가스-정책.md
- ERC-4337: Account Abstraction: https://eips.ethereum.org/EIPS/eip-4337
- ERC-4337 Documentation: Paymasters: https://docs.erc4337.io/paymasters/