트랜잭션 수수료 대납
ABC Paymaster를 사용하면 기존 EOA 지갑 주소 그대로 트랜잭션 수수료를 대납받을 수 있습니다.
EIP-7702 기반으로 동작하며, EOA가 처음 대납 트랜잭션을 전송할 때 위임(delegation) 설정이 자동으로 처리됩니다.
이후에는 트랜잭션 파라미터만 전달하면 수수료 없이 트랜잭션을 전송할 수 있습니다.
지원 네트워크
현재 가스비 대납 서비스 지원 네트워크는 Ethereum, Kaia, Base 입니다.
테스트넷은 Ethereum Sepolia, Kaia Kairos, Base Sepolia 를 지원합니다.
Avalanche C-Chain은 이 방식을 지원하지 않습니다. SmartAccount 가이드를 참고하세요.
API 가이드
전체 흐름은 3단계입니다.
| 단계 | 메서드 | 엔드포인트 |
|---|---|---|
| 1. Sponsor | POST | /core/evm/v2/eoa/paymaster/sponsor |
| 2. Send | POST | /core/evm/v2/eoa/paymaster/send |
| 3. Receipt | GET | /core/evm/v2/eoa/paymaster/receipt |
1. Sponsor
가스대납 요청을 보내고, 서명할 데이터를 받습니다.
Request
POST /core/evm/v2/eoa/paymaster/sponsor
{
"network": "ethereum-sepolia",
"from": "0xAbC...",
"to": "0xDeF...",
"value": "0x0",
"data": "0x..."
}
| 필드 | 설명 |
|---|---|
network | 대상 네트워크 식별자 |
from | 송신자 EOA 주소 |
to | 수신자 주소 (컨트랙트 또는 EOA) |
value | 전송할 ETH 양 (wei, hex) |
data | 컨트랙트 호출 데이터. 단순 전송 시 "0x" |
Response
{
"sponsored_transaction": {
"sender": "0xAbC...",
"nonce": "0x1",
"callData": "0x...",
"callGasLimit": "0x...",
"verificationGasLimit": "0x...",
"preVerificationGas": "0x...",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x...",
"paymasterAndData": "0x...",
"signature": "0x"
},
"sign_hash": "0xabcdef...",
"authorization": {
"chainId": "0x...",
"address": "0x...",
"nonce": "0x..."
},
"authorization_hash": "0x..."
}
| 필드 | 설명 |
|---|---|
sponsored_transaction | 2단계 Send에 그대로 전달할 데이터 |
sign_hash | EOA가 서명해야 할 UserOperation 해시 |
authorization | 최초 트랜잭션에만 포함. 아래 참고 |
authorization_hash | 최초 트랜잭션에만 포함. 아래 참고 |
2. Send
서명한 데이터를 전송합니다.
Request
POST /core/evm/v2/eoa/paymaster/send
{
"network": "ethereum-sepolia",
"sponsored_transaction": { "..." },
"signature": "0x...",
"authorization": {
"chainId": "0x...",
"address": "0x...",
"nonce": "0x...",
"v": 0,
"r": "0x...",
"s": "0x..."
}
}
| 필드 | 설명 |
|---|---|
sponsored_transaction | 1. Sponsor 응답의 sponsored_transaction 그대로 전달 |
signature | sign_hash에 대한 서명값. 아래 참고 |
authorization | 최초 트랜잭션에만 포함. 아래 참고 |
sponsored_transaction의 필드를 임의로 수정하면 서명 검증에 실패합니다. 응답을 그대로 전달하세요.
Response
{
"tx_id": "0x..."
}
3. Receipt
tx_id로 트랜잭션 처리 결과를 조회합니다.
Request
GET /core/evm/v2/eoa/paymaster/receipt?tx_id={tx_id}&network={network}
트랜잭션이 처리 중이면 null이 반환될 수 있습니다. 완료까지 폴링하세요.
서명 방법
sign_hash와 authorization_hash 모두 WaaS의 MPC Sign API를 사용하여 서명합니다.
POST /v3/wallet/sign
{
"curve": "secp256k1",
"encrypted_share": "지갑 생성/복구 시 반환된 암호화된 MPC 쉐어",
"key_id": "서명에 사용할 키의 고유 식별자",
"message": "서명할 해시 (hex string)",
"secret_store": "쉐어 복호화에 사용되는 키"
}
sign_hash 서명
sign_hash는 personal_sign 방식의 서명이 필요합니다. Ethereum personal_sign 프리픽스를 적용한 해시를 계산한 뒤 /v3/wallet/sign에 전달합니다.
import { keccak256, toBytes, concat, toHex } from 'viem';
// personal_sign 프리픽스 적용
const prefix = toBytes(`\x19Ethereum Signed Message:\n32`);
const messageHash = keccak256(concat([prefix, toBytes(sponsorResponse.sign_hash)]));
const signRes = await fetch('/v3/wallet/sign', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },
body: JSON.stringify({
curve: 'secp256k1',
encrypted_share: encryptedShare,
key_id: keyId,
message: messageHash,
secret_store: secretStore,
}),
});
const { signature } = await signRes.json();
const sendSignature = `0x${signature}`;
authorization_hash 서명 (최초 트랜잭션에만 해당)
authorization과 authorization_hash는 EOA가 처음으로 수수료 대납 트랜잭션을 전송할 때만 Sponsor 응답에 포함됩니다. 이후 트랜잭션에서는 포함되지 않습니다.
message에 authorization_hash 값을 전달하고, 응답의 signature(65바이트)를 r, s, v로 분해하여 authorization 객체에 채웁니다.
const authSignRes = await fetch('/v3/wallet/sign', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },
body: JSON.stringify({
curve: 'secp256k1',
encrypted_share: encryptedShare,
key_id: keyId,
message: sponsorResponse.authorization_hash,
secret_store: secretStore,
}),
});
const { signature: authSig } = await authSignRes.json();
// 65바이트 서명(130 hex chars)을 r, s, v로 분해
const r = `0x${authSig.slice(0, 64)}`;
const s = `0x${authSig.slice(64, 128)}`;
const v = parseInt(authSig.slice(128, 130), 16) - 27; // 0 또는 1
const authorization = {
...sponsorResponse.authorization,
v,
r,
s,
};