Version: SuperPaymaster-5.4.0 (release v5.4.0)
This document covers the full public API as of
v5.4.0-beta.1, including V3/V4 baseline functions and all V5.x additions (agent-native gas sponsorship, x402 settlement, ERC-8004 dual-channel eligibility, and agent sponsorship policies).v5.4 god-split note: the on-chain
version()strings are nowSuperPaymaster-5.4.0andRegistry-5.4.0. The v5.4 implementation extracts the x402 settlement functions into a standaloneX402Facilitatorcontract and adds a standalonePolicyRegistry(both keep their own1.0.0version). ThesettleX402Payment*entries below remain documented for the SP-embedded path but are superseded byX402Facilitatorin v5.4.
| Field | Value |
|---|---|
| Version | SuperPaymaster-5.4.0 (release v5.4.0) |
| Sepolia Proxy | 0x030025f40d509b1a99547bAEb3795bD27F7182b7 |
| Sepolia Impl | 0x24a94572cfB6Ca6C8dE107431043556D461d8cFf |
| X402Facilitator (Sepolia) | 0x326Fc3413c8A0185b0179B971C69813B6dFD971B |
| PolicyRegistry (Sepolia) | 0x8c2488d46d5447418558c38AA6441720df656094 |
| TimelockController (Sepolia) | 0xB734df3c0A1809bc06708512363D368Ac51dF1A2 |
| MicroPaymentChannel (Sepolia) | 0x405851A141Cde827E33247d4D4089Af2814c2FF5 |
| AgentIdentityRegistry (Sepolia) | 0x8004A818BFB912233c491871b3d84c89A494BD9e |
| AgentReputationRegistry (Sepolia) | 0x8004B663056A597Dffe9eCcC1965A193B7388713 |
| EntryPoint | v0.7 — 0x0000000071727De22E5E9d8BAf0edAc6f37da032 |
| Upgrade pattern | UUPS (ERC1967Proxy) |
| Solidity | 0.8.33, optimizer 10,000 runs, Cancun EVM, via-IR |
Packed into 4 storage slots for gas efficiency.
struct OperatorConfig {
// Slot 0: HOT (validation critical)
uint128 aPNTsBalance; // gas collateral in aPNTs (cap ~3.4e38)
uint96 exchangeRate; // xPNTs:aPNTs rate (1e18 = 1:1)
bool isConfigured;
bool isPaused;
// 2 bytes remaining
// Slot 1: WARM
address xPNTsToken; // community gas token to charge users
uint32 reputation; // reputation score (max 4 billion)
uint48 minTxInterval; // minimum seconds between user ops (rate limit)
// Slot 2: COLD
address treasury; // receives user xPNTs payments
// Slot 3+: Stats
uint256 totalSpent;
uint256 totalTxSponsored;
}struct SlashRecord {
uint256 timestamp;
uint256 amount;
uint256 reputationLoss;
string reason;
SlashLevel level;
}Defines a tiered sponsorship rule for ERC-8004 registered agents.
struct AgentSponsorshipPolicy {
uint128 minReputationScore; // minimum agent reputation to qualify
uint64 sponsorshipBPS; // discount in basis points (10000 = 100% free)
uint64 maxDailyUSD; // USD cap per day (scaled by 1e6); 0 = unlimited
}enum SlashLevel {
WARNING, // reputation -10, no balance slash
MINOR, // reputation -20, 10% balance slash (BLS: capped at 30%)
MAJOR // reputation -50, full balance slash, operator paused
}Packed user state per operator (1 storage slot).
struct UserOperatorState {
uint48 lastTimestamp; // last op timestamp for rate limiting
bool isBlocked; // blacklist flag
}struct PriceCache {
int256 price; // ETH/USD price (8 decimals)
uint256 updatedAt; // unix timestamp of last update
uint80 roundId; // Chainlink round ID (0 for DVT updates)
uint8 decimals; // oracle decimals (typically 8)
}SuperPaymaster uses UUPS upgradeable storage (OZ v5.0.2 pattern):
| Slot | Variable | Notes |
|---|---|---|
| 0 | _owner (Ownable) |
traditional slot |
| 1 | _status (ReentrancyGuard) |
traditional slot |
| 2 | APNTS_TOKEN |
|
| 3 | xpntsFactory |
|
| 4 | treasury |
|
| 5 | operators mapping |
|
| 6 | userOpState mapping |
|
| 7 | sbtHolders mapping |
|
| 8 | slashHistory mapping |
|
| 9 | aPNTsPriceUSD |
default 0.02 ether |
| 10 | cachedPrice (PriceCache) |
2 slots |
| 12 | protocolFeeBPS |
default 1000 (10%) |
| 13 | BLS_AGGREGATOR |
|
| 14 | totalTrackedBalance |
|
| 15 | protocolRevenue |
|
| 16 | pendingDebts mapping |
|
| 17 | priceStalenessThreshold |
|
| 18 | oracleDecimals |
|
| V5 additions (8 new slots): | ||
| 19 | agentIdentityRegistry |
ERC-8004 agent NFT registry |
| 20 | agentReputationRegistry |
ERC-8004 reputation registry |
| 21 | facilitatorFeeBPS |
default x402 facilitator fee |
| 22 | operatorFacilitatorFees mapping |
per-operator fee override |
| 23 | x402SettlementNonces mapping |
replay prevention |
| 24 | facilitatorEarnings mapping |
operator => asset => amount |
| 25 | agentPolicies mapping |
operator sponsorship tiers |
| 26 | _agentDailySpend mapping |
daily USD spend tracker |
| 27–66 | __gap[40] |
UUPS upgrade safety (was 50, consumed 8 for V5, then 2 for immutables note) |
Note:
REGISTRY,ETH_USD_PRICE_FEED, andentryPointare immutable — stored in implementation bytecode, not proxy storage.
For ERC-4337 v0.7, the paymasterAndData field layout is:
| Paymaster (20) | VerificationGasLimit (16) | PostOpGasLimit (16) | Operator (20) | [MaxRate (32)] |
- Bytes 0–19: SuperPaymaster proxy address
- Bytes 20–51: gas limits (packed by EntryPoint v0.7)
- Bytes 52–71: operator address (
PAYMASTER_DATA_OFFSET = 52) - Bytes 72–103: optional
maxRate(uint256) — rate commitment for rug-pull protection (RATE_OFFSET = 72)
// viem example (no maxRate commitment):
const paymasterAndData = concat([
SUPERPAYMASTER_ADDRESS, // 20 bytes
pad(toHex(150000n), { size: 16, dir: 'left' }), // verificationGasLimit
pad(toHex(100000n), { size: 16, dir: 'left' }), // postOpGasLimit
OPERATOR_ADDRESS // 20 bytes
]);
// With rate commitment:
const paymasterAndData = concat([
SUPERPAYMASTER_ADDRESS,
pad(toHex(150000n), { size: 16, dir: 'left' }),
pad(toHex(100000n), { size: 16, dir: 'left' }),
OPERATOR_ADDRESS,
pad(toHex(maxExchangeRate), { size: 32, dir: 'left' }) // maxRate (uint256)
]);| Constant | Value | Description |
|---|---|---|
PRICE_CACHE_DURATION |
300 | seconds; price cache TTL reference |
MIN_ETH_USD_PRICE |
100 * 1e8 |
minimum valid Chainlink price |
MAX_ETH_USD_PRICE |
100_000 * 1e8 |
maximum valid Chainlink price |
PAYMASTER_DATA_OFFSET |
52 | operator address byte offset in paymasterAndData |
RATE_OFFSET |
72 | maxRate byte offset in paymasterAndData |
BPS_DENOMINATOR |
10000 | basis points denominator |
MAX_PROTOCOL_FEE |
2000 | 20% hardcap on protocolFeeBPS |
VALIDATION_BUFFER_BPS |
1000 | 10% safety buffer applied during validation |
MAX_FACILITATOR_FEE |
500 | 5% hardcap on x402 facilitator fees |
MAX_AGENT_POLICIES |
10 | max sponsorship policies per operator |
Called exclusively by EntryPoint during UserOperation validation.
function validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData)Access: onlyEntryPoint, nonReentrant
Validation order:
- Extract operator from
paymasterAndData[52:72] - Require
isConfigured && !isPaused - Require
isEligibleForSponsorship(userOp.sender)— SBT holder OR registered ERC-8004 agent (V5.3) - Require
!isBlockedand enforceminTxIntervalviavalidAfter - Enforce
maxRatecommitment if provided at offset 72 - Optimistic aPNTs deduction with 10% fee + 10% validation buffer
- Returns context
(xPNTsToken, xPNTsAmount, sender, aPNTsAmount, userOpHash, operator)andvalidUntil = cachedPrice.updatedAt + priceStalenessThreshold
Called by EntryPoint after UserOperation execution.
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost,
uint256 actualUserOpFeePerGas
) externalAccess: onlyEntryPoint, nonReentrant
Behavior:
- Always updates
lastTimestampfor rate limiting (even on revert mode) - Skips processing if
mode == postOpReverted - Recalculates actual aPNTs cost with protocol fee markup
- Refunds excess to operator; records xPNTs debt to user via
IxPNTsToken.recordDebt() - Falls back to
pendingDebtsif debt recording fails - Calls
_submitSponsorshipFeedback()for registered agents (F2)
Configure billing settings. Caller must hold ROLE_PAYMASTER_SUPER and ROLE_COMMUNITY in Registry.
function configureOperator(
address xPNTsToken,
address _opTreasury,
uint256 exchangeRate
) externalEvents: OperatorConfigured(operator, xPNTsToken, treasury, exchangeRate)
Deposit aPNTs as gas collateral (legacy pull mode — requires ERC-20 approval).
function deposit(uint256 amount) external nonReentrantAccess: Must hold ROLE_PAYMASTER_SUPER in Registry
Events: OperatorDeposited(operator, amount)
Deposit aPNTs on behalf of a specific operator (secure push mode).
function depositFor(address targetOperator, uint256 amount) external nonReentrantEvents: OperatorDeposited(targetOperator, amount)
ERC1363 callback — handles push-mode deposits where the token calls the receiver.
function onTransferReceived(
address,
address from,
uint256 value,
bytes calldata
) external nonReentrant returns (bytes4)Access: Only callable by APNTS_TOKEN contract
Returns: this.onTransferReceived.selector
Events: OperatorDeposited(from, value)
Withdraw aPNTs collateral.
function withdraw(uint256 amount) external nonReentrantEvents: OperatorWithdrawn(operator, amount)
Set minimum transaction interval for rate limiting.
function setOperatorLimits(uint48 _minTxInterval) externalAccess: Must hold ROLE_PAYMASTER_SUPER in Registry
Events: OperatorMinTxIntervalUpdated(operator, minTxInterval)
V5.3 dual-channel eligibility check. Returns true if the user is an SBT holder or a registered ERC-8004 agent NFT holder.
function isEligibleForSponsorship(address user) external view returns (bool)Implementation: sbtHolders[user] || isRegisteredAgent(user)
Check if an address holds at least one ERC-8004 Agent NFT in the configured identity registry.
function isRegisteredAgent(address account) external view returns (bool)Returns: false if agentIdentityRegistry == address(0) or balanceOf reverts.
Set tiered agent sponsorship policies for the calling operator. Policies should be sorted by minReputationScore descending for optimal matching. Up to MAX_AGENT_POLICIES (10) entries.
function setAgentPolicies(
ISuperPaymaster.AgentSponsorshipPolicy[] calldata policies
) externalAccess: Must hold ROLE_PAYMASTER_SUPER in Registry
Events: AgentPoliciesUpdated(operator, policyCount)
Query the effective sponsorship BPS for an agent from an operator, accounting for reputation and daily caps.
function getAgentSponsorshipRate(
address agent,
address operator
) external view returns (uint256 bps)Returns: Basis points discount (0 = no sponsorship, 10000 = 100% free). Returns 0 if agent is not registered, no matching policy, or daily cap exhausted.
⚠️ v5.4 god-split: As ofv5.4.0-beta.1, x402 settlement has been extracted into the standaloneX402Facilitatorcontract (Sepolia0x326Fc3413c8A0185b0179B971C69813B6dFD971B). The functions below document the legacy SuperPaymaster-embedded path (still present in the impl bytecode); new integrations should targetX402Facilitator, whose canonical signatures are documented in its own section. The standalonesettleX402Paymentsignature additionally takes amaxFeeargument and settles via EIP-3009receiveWithAuthorization.
Settle an x402 HTTP payment via EIP-3009 transferWithAuthorization (native USDC path, ~161K gas, 19% more efficient than Permit2).
function settleX402Payment(
address from,
address to,
address asset,
uint256 amount,
uint256 validAfter,
uint256 validBefore,
bytes32 salt,
bytes calldata signature
) external nonReentrant returns (bytes32 settlementId)Access: Caller must hold ROLE_PAYMASTER_SUPER in Registry (acts as facilitator)
C-03 recipient binding: the contract derives the EIP-3009 nonce as
nonce = keccak256(abi.encode(to, salt)). The payer signs the EIP-3009 TransferWithAuthorization
over that derived nonce (with to = SuperPaymaster), then the facilitator passes salt
(not a raw nonce). If a facilitator swaps to, a different nonce is derived and the EIP-3009
signature no longer recovers from → the transfer reverts.
Parameters:
from— payer address (must have signed the EIP-3009 authorization)to— final payee / service provider address (bound into the nonce)asset— ERC-20 token implementing EIP-3009 (e.g. USDC)amount— gross transfer amountvalidAfter,validBefore— authorization time window (EIP-3009)salt— random bytes32; combined withtoto derive the recipient-bound noncesignature— EIP-3009 authorization signature fromfromoverkeccak256(abi.encode(to, salt))
Returns: settlementId = keccak256(from, to, asset, amount, nonce) (derived nonce)
Fee: Deducts facilitatorFeeBPS (or per-operator override) from amount before forwarding net to to. Fee credited to facilitatorEarnings[msg.sender][asset].
Events: X402PaymentSettled(from, to, asset, amount, fee, nonce)
Errors: NonceAlreadyUsed, Unauthorized
Settle an x402 payment for an xPNTs token (factory-deployed) via transferFrom. Since the
SuperPaymaster holds an auto-allowance over every xPNTs holder, the consent gate lives here:
the payer must sign an EIP-712 X402PaymentAuthorization (C-02 hardening).
function settleX402PaymentDirect(
address from,
address to,
address asset,
uint256 amount,
uint256 maxFee,
uint256 validBefore,
bytes32 nonce,
bytes calldata signature
) external nonReentrant returns (bytes32 settlementId)Access: Caller must hold ROLE_PAYMASTER_SUPER, AND be on the xPNTs token's
approvedFacilitators whitelist (P0-12b). asset must be a factory-minted xPNTs (P0-12a).
Signature (C-02, required): from must sign EIP-712 X402PaymentAuthorization(address from,address to,address asset,uint256 amount,uint256 maxFee,uint256 validBefore,bytes32 nonce) over the SuperPaymaster proxy domain (name:"SuperPaymaster", version:"1"). Verified by SignatureCheckerLib (EOA + ERC-1271). The contract also enforces block.timestamp <= validBefore and fee <= maxFee.
Returns: settlementId = keccak256(from, to, asset, amount, nonce)
Events: X402PaymentSettled(from, to, asset, amount, fee, nonce)
Errors: InvalidX402Signature, X402AuthExpired, X402FeeExceedsMax, InvalidConfiguration (xpntsFactory unset), InvalidXPNTsToken, NonceAlreadyUsed, Unauthorized
Withdraw accumulated facilitator fee earnings for a given asset.
function withdrawFacilitatorEarnings(address asset) external nonReentrantAccess: Any operator who has accumulated earnings
Events: FacilitatorEarningsWithdrawn(operator, asset, amount)
Pull latest price from Chainlink oracle and update the internal cache.
function updatePrice() externalAccess: Public (typically called by keeper)
Events: PriceUpdated(price, timestamp)
Errors: OracleError — if Chainlink call fails, price out of bounds, or data is stale.
Update price via BLS/DVT consensus (Chainlink fallback path).
function updatePriceDVT(
int256 price,
uint256 updatedAt,
bytes calldata proof
) externalAccess: BLS_AGGREGATOR or owner()
Validation:
updatedAtmust be strictly greater thancachedPrice.updatedAt- Must be within 2 hours of current time
- Price must be within
[MIN_ETH_USD_PRICE, MAX_ETH_USD_PRICE] - If Chainlink is live and recent, rejects prices deviating >20% from Chainlink
Events: PriceUpdated(price, updatedAt)
Update the global SBT holder flag for a user (called by Registry on MySBT mint/burn events).
function updateSBTStatus(address user, bool status) externalAccess: REGISTRY contract only
Batch-update the user blocklist for a specific operator (called by Registry via DVT credit exhaustion sync).
function updateBlockedStatus(
address operator,
address[] calldata users,
bool[] calldata statuses
) externalAccess: REGISTRY contract only
Events: UserBlockedStatusUpdated(operator, user, isBlocked) per entry
Owner-governed slash with no BPS hardcap.
function slashOperator(
address operator,
ISuperPaymaster.SlashLevel level,
uint256 penaltyAmount,
string calldata reason
) external onlyOwnerEvents: OperatorSlashed(operator, amount, level), ReputationUpdated(operator, newScore)
BLS-consensus-triggered slash (DVT path). Enforces 30% aPNTs slash hardcap.
function executeSlashWithBLS(
address operator,
ISuperPaymaster.SlashLevel level,
bytes calldata proof
) externalAccess: BLS_AGGREGATOR only
Events: SlashExecutedWithProof(operator, level, penalty, proofHash, timestamp), OperatorSlashed(...), ReputationUpdated(...)
function getSlashHistory(address operator)
external view
returns (ISuperPaymaster.SlashRecord[] memory)function getSlashCount(address operator) external view returns (uint256)function getLatestSlash(address operator)
external view
returns (ISuperPaymaster.SlashRecord memory)Errors: NoSlashHistory
Retry recording a pending xPNTs debt that failed during postOp.
function retryPendingDebt(address token, address user) external nonReentrantEvents: PendingDebtRetried(token, user, amount)
Errors: NoPendingDebt
Admin escape hatch to clear a stuck pending debt without recording it.
function clearPendingDebt(address token, address user) external onlyOwnerEvents: PendingDebtCleared(token, user, amount)
Get remaining credit for a user under a specific xPNTs token (aPNTs units).
function getAvailableCredit(address user, address token)
external view returns (uint256)Logic: REGISTRY.getCreditLimit(user) - debtInAPNTs (floored at 0)
Get operator configuration struct.
function operators(address operator)
external view
returns (
uint128 aPNTsBalance,
uint96 exchangeRate,
bool isConfigured,
bool isPaused,
address xPNTsToken,
uint32 reputation,
uint48 minTxInterval,
address treasury,
uint256 totalSpent,
uint256 totalTxSponsored
)function sbtHolders(address user) external view returns (bool)function userOpState(address operator, address user)
external view
returns (uint48 lastTimestamp, bool isBlocked)function pendingDebts(address token, address user) external view returns (uint256)function x402SettlementNonces(bytes32 nonce) external view returns (bool)function facilitatorEarnings(address operator, address asset) external view returns (uint256)function agentPolicies(address operator, uint256 index)
external view
returns (ISuperPaymaster.AgentSponsorshipPolicy memory)function cachedPrice()
external view
returns (int256 price, uint256 updatedAt, uint80 roundId, uint8 decimals)| Function | Signature | Description |
|---|---|---|
setAPNTsToken |
(address) |
Update aPNTs token address |
setAPNTSPrice |
(uint256) |
Update aPNTs USD price (18 decimals; default 0.02 ether = $0.02) |
setProtocolFee |
(uint256 bps) |
Set protocol fee BPS (max 2000 = 20%) |
setTreasury |
(address) |
Set protocol treasury address |
setXPNTsFactory |
(address) |
Set xPNTs factory for binding verification |
setBLSAggregator |
(address) |
Set trusted BLS aggregator for DVT slash |
setOperatorPaused |
(address operator, bool paused) |
Emergency pause/unpause operator |
updateReputation |
(address operator, uint256 score) |
Manually set operator reputation score |
withdrawProtocolRevenue |
(address to, uint256 amount) |
Withdraw accumulated protocol fees |
setAgentRegistries |
(address identity, address reputation) |
Set ERC-8004 agent registries (V5) |
setFacilitatorFeeBPS |
(uint256 fee) |
Set default x402 facilitator fee BPS (max 500 = 5%) |
setOperatorFacilitatorFee |
(address operator, uint256 fee) |
Set per-operator facilitator fee override |
event OperatorDeposited(address indexed operator, uint256 amount);
event OperatorWithdrawn(address indexed operator, uint256 amount);
event OperatorConfigured(address indexed operator, address xPNTsToken, address treasury, uint256 exchangeRate);
event OperatorPaused(address indexed operator);
event OperatorUnpaused(address indexed operator);
event OperatorMinTxIntervalUpdated(address indexed operator, uint48 minTxInterval);
event UserBlockedStatusUpdated(address indexed operator, address indexed user, bool isBlocked);
event OperatorSlashed(address indexed operator, uint256 amount, SlashLevel level);
event ReputationUpdated(address indexed operator, uint256 newScore);
event TransactionSponsored(address indexed operator, address indexed user, uint256 aPNTsCost, uint256 xPNTsCost);event PriceUpdated(int256 indexed price, uint256 indexed timestamp);
event OracleFallbackTriggered(uint256 timestamp);
event APNTsPriceUpdated(uint256 oldPrice, uint256 newPrice);event SlashExecutedWithProof(
address indexed operator,
ISuperPaymaster.SlashLevel level,
uint256 penalty,
bytes32 proofHash,
uint256 timestamp
);event DebtRecordFailed(address indexed token, address indexed user, uint256 amount);
event PendingDebtRetried(address indexed token, address indexed user, uint256 amount);
event PendingDebtCleared(address indexed token, address indexed user, uint256 amount);event APNTsTokenUpdated(address indexed oldToken, address indexed newToken);
event ProtocolFeeUpdated(uint256 oldFee, uint256 newFee);
event BLSAggregatorUpdated(address indexed oldAggregator, address indexed newAggregator);
event ProtocolRevenueWithdrawn(address indexed to, uint256 amount);event AgentPoliciesUpdated(address indexed operator, uint256 policyCount);
event X402PaymentSettled(address indexed from, address indexed to, address asset, uint256 amount, uint256 fee, bytes32 nonce);
event FacilitatorFeeUpdated(uint256 oldFee, uint256 newFee);
event AgentRegistriesUpdated(address identityRegistry, address reputationRegistry);
event FacilitatorEarningsWithdrawn(address indexed operator, address indexed asset, uint256 amount);error Unauthorized();
error InvalidAddress();
error InvalidConfiguration();
error InsufficientBalance(uint256 available, uint256 required);
error DepositNotVerified();
error OracleError();
error NoSlashHistory();
error InsufficientRevenue();
error InvalidXPNTsToken();
error FactoryVerificationFailed();
error AmountExceedsUint128();
error ScoreExceedsUint32();
error NoPendingDebt();
// V5 errors
error NonceAlreadyUsed();
error InvalidFee();The MicroPaymentChannel contract is a separate deployment that provides unidirectional payment channel streaming for agent-to-service-provider micropayments. It is referenced by the x402 facilitator SDK but is not part of SuperPaymaster's on-chain code.
Sepolia address: 0xbD1807328Dd654512B13d6320C9Cc78685a405Ed
Key functions:
| Function | Description |
|---|---|
openChannel(payee, token, deposit, salt, authorizedSigner) |
Open a new channel; returns channelId |
settle(channelId, cumulativeAmount, signature) |
Payee submits a cumulative voucher to collect payment |
topUp(channelId, amount) |
Payer adds funds to an open channel |
requestClose(channelId) |
Payer initiates 15-minute dispute window |
closeChannel(channelId, cumulativeAmount, signature) |
Payee submits final voucher during close window |
withdrawAfterTimeout(channelId) |
Payer withdraws remaining funds after timeout |
getChannel(channelId) |
View channel state |
Channel voucher EIP-712 typehash:
Voucher(bytes32 channelId, uint128 cumulativeAmount)
Events: ChannelOpened, ChannelSettled, ChannelTopUp, CloseRequested, ChannelClosed, ChannelWithdrawn
Version: X402Facilitator-1.0.0 · Sepolia: 0x326Fc3413c8A0185b0179B971C69813B6dFD971B
· Type: Standalone, non-upgradeable (Ownable + ReentrancyGuard)
The v5.4 god-split extracts x402 settlement out of SuperPaymaster into this dedicated contract.
It has zero SuperPaymaster-storage dependency — it only reads external contracts (Registry role
gate, xPNTs factory whitelist, ERC-3009/ERC-20 token transfers) and owns its own copies of the four
x402 storage vars (facilitatorFeeBPS, operatorFacilitatorFees, x402SettlementNonces,
facilitatorEarnings). Operator/facilitator calls are gated on
Registry.hasRole(ROLE_PAYMASTER_SUPER, caller).
Settle an x402 HTTP payment via EIP-3009 receiveWithAuthorization (USDC-native path).
function settleX402Payment(
address from, address to, address asset, uint256 amount, uint256 maxFee,
uint256 validAfter, uint256 validBefore, bytes32 salt, bytes calldata signature
) external nonReentrant returns (bytes32 settlementId)- Access: caller must hold
ROLE_PAYMASTER_SUPERin Registry. - Recipient + fee binding (C-03): the contract derives the EIP-3009 nonce as
nonce = keccak256(abi.encode(to, maxFee, salt)). The payer signs EIP-3009ReceiveWithAuthorizationover that derived nonce (to = X402Facilitator); swappingtoormaxFeederives a different nonce → signature no longer recoversfrom→ revert. - Amount check: reverts
X402AmountMismatchif the post-transfer balance delta <amount. - Fee: deducts
getEffectiveFacilitatorFee(msg.sender)(per-operator override else globalfacilitatorFeeBPS), capped bymaxFee; netamount - feeforwarded toto, fee credited tofacilitatorEarnings[msg.sender][asset]. - Returns:
settlementId = keccak256(abi.encode(from, to, asset, amount, nonce)). - Events:
X402PaymentSettled(from, to, asset, amount, fee, nonce). - Errors:
Unauthorized,NonceAlreadyUsed,InvalidFee,X402FeeExceedsMax,X402AmountMismatch.
Settle an x402 payment for a factory-deployed xPNTs token via transferFrom (auto-allowance).
Because xPNTs carry no token-level EIP-3009 authorization, the consent gate lives here: the payer
must sign an EIP-712 X402PaymentAuthorization (C-02).
function settleX402PaymentDirect(
address from, address to, address asset, uint256 amount,
uint256 maxFee, uint256 validBefore, bytes32 nonce, bytes calldata signature
) external nonReentrant returns (bytes32 settlementId)- Access: caller must hold
ROLE_PAYMASTER_SUPER(P0-12),assetmust be a factory xPNTs (XPNTS_FACTORY.isXPNTs(asset), P0-12a), and caller must be on the xPNTs token'sapprovedFacilitatorswhitelist (P0-12b). - Signature (C-02, required):
fromsigns EIP-712X402PaymentAuthorization(address from,address to,address asset,uint256 amount,uint256 maxFee,uint256 validBefore,bytes32 nonce)over the X402Facilitator domain (name:"X402Facilitator", version:"1"), verified viaSignatureCheckerLib(EOA + ERC-1271). Enforcesblock.timestamp <= validBeforeandfee <= maxFee. - Returns:
settlementId = keccak256(abi.encode(from, to, asset, amount, nonce)). - Errors:
X402AuthExpired,InvalidX402Signature,X402FeeExceedsMax,InvalidXPNTsToken,NonceAlreadyUsed,Unauthorized,InvalidConfiguration.
| Function | Access | Purpose |
|---|---|---|
setFacilitatorFeeBPS(uint256) |
onlyOwner |
Global facilitator fee in BPS |
setOperatorFacilitatorFee(address, uint256) |
onlyOwner |
Per-operator fee override |
getEffectiveFacilitatorFee(address) → uint256 |
view | Operator override else global default |
withdrawFacilitatorEarnings(address asset) |
operator | Withdraw accrued fees for asset |
x402NonceKey(address asset, address from, bytes32 nonce) → bytes32 |
pure | Off-chain nonce-key derivation helper |
Events: FacilitatorFeeUpdated, FacilitatorEarningsWithdrawn, X402PaymentSettled.
Version: PolicyRegistry-1.0.0 · Sepolia: 0x8c2488d46d5447418558c38AA6441720df656094
· Type: Standalone, non-upgradeable, governance-gated (TimelockController + guardian)
PolicyRegistry is the single on-chain source of truth for sender-keyed, governance-gated spend
policy and DVT-trigger rules. Staked consumers (SuperPaymaster, AirAccount) read it during
validation; DVT nodes and the slash path reference the same policy so that "what a node enforced ==
what is punished". It never inspects signature wire-format — it reasons only about
(sender, target, asset, amount, selector). Governance evolves policy through the injected
TimelockController (Sepolia 0xB734df3c0A1809bc06708512363D368Ac51dF1A2) and a guardian, not via
code upgrades. ETH uses a sentinel address 0xEeee...EEeE (asset == address(0) is invalid).
function checkPolicy(address sender, address target, address asset, uint256 amount, bytes4 selector)
external view returns (PolicyDecision decision, uint256 remainingDaily)- Decision:
ALLOW/REJECT/ DVT-required — a frozen sender is a hardREJECT. Opt-in default: an unconfigured dimension imposes no constraint (remainingDaily = type(uint256).max); configured asset / contract-scope dimensions narrow it (daily/velocity windows, selector allow-list). - Pure read — consumers call this during ERC-4337 validation (ERC-7562 sender-associated storage).
function recordSpend(address sender, address target, address asset, uint256 amount, bytes4 selector)
external onlyAuthorizedConsumer- Access: only authorized consumers (staked SuperPaymaster / AirAccount; toggled by Timelock via
setConsumerAuthorization). Advances the per-asset and per-target velocity counters, rolling the window first if elapsed. EmitsSpendRecorded.
| Function | Access | Purpose |
|---|---|---|
setAssetPolicy / setContractScope |
timelock | Set per-(sender,asset) and per-(sender,target) policy |
tightenAssetPolicy / tightenContractScope |
guardian/timelock | Monotonic tighten-only fast path |
freezeSender / unfreezeSender |
guardian / timelock | Emergency hard-block a sender |
setGuardian / setConsumerAuthorization |
timelock | Rotate guardian, authorize consumers |
getAssetPolicy / getContractScope / getAssetSpend / isSelectorAllowed / isFrozen / isAuthorizedConsumer |
view | Inspection |
Note:
POLICY_REGISTRY_ADDRESShandoff to the SuperPaymaster/AirAccount consumer wiring (issue #110) and the multisig/Timelock ownership transfer are deferred to GA — see the v5.4.0-beta.1 deploy record.
| Version | Key Changes |
|---|---|
SuperPaymaster-5.4.0 (release v5.4.0) |
v5.4 GA: version() bumped 5.3.3 → 5.4.0 (Registry also 5.4.0). Same god-split implementation content as v5.4.0-beta.1 — standalone X402Facilitator-1.0.0 + PolicyRegistry-1.0.0 + TimelockController. Mainnet-ready DeployLive (X402Facilitator + Timelock + PolicyRegistry on a fresh chain). |
SuperPaymaster-5.3.3 (release v5.4.0-beta.1) |
v5.4 god-split phase 1: x402 settlement extracted to standalone X402Facilitator-1.0.0; new standalone PolicyRegistry-1.0.0 (governance-gated spend policy) + TimelockController. SP version() string unchanged (5.3.3); bump to 5.4.0 deferred to GA. |
SuperPaymaster-5.3.0 |
V5.3: ERC-8004 dual-channel sponsorship (isEligibleForSponsorship), agent sponsorship policies (F1), reputation feedback (F2), x402 EIP-3009 settlement (settleX402Payment), xPNTs direct settlement (settleX402PaymentDirect), __gap reduced 48→40 |
SuperPaymaster-5.0.0 |
V5.1: _consumeCredit() kernel, chargeMicroPayment() EIP-712, solady EIP-712, microPaymentNonces |
SuperPaymaster-4.x |
UUPS upgradeable proxy migration (ERC1967), BasePaymasterUpgradeable, initialize() |
SuperPaymaster-3.2.2 |
V3 baseline: Registry integration, Chainlink oracle, DVT/BLS slash, xPNTs factory binding, packed storage |