How a Paymaster Decides Who Gets Free Gas: Our Testnet Pilot With 0xgasless
A paymaster pays the gas fee for someone else's transaction. That is the appeal of ERC-4337 for onboarding: a new user or an AI agent can act without first holding the chain's native token. It is also an open invitation. A paymaster that sponsors every wallet that asks gets scripted wallets, and its deposit drains as fast as someone can write a loop.
We ran a technical pilot on Avalanche Fuji testnet with 0xGasless, which runs ERC-4337 account-abstraction infrastructure (bundler, paymaster, smart accounts) and x402 payment rails on Avalanche, and showed it live on September 30, 2026. It tests one answer to "who do we sponsor?": the accounts that hold a valid KUMPLY attestation, and nobody else. This post covers what ran, three transactions you can check yourself, one ERC-7562 detail that trips up on-chain versions of this check, and what the pilot does not prove.
The problem: free gas attracts everything
Gating sponsorship needs a signal with three properties. It has to be cheap to check on every request. It has to expire or be revocable, so a bad actor does not keep access forever. And it has to attach to the address that actually sends the UserOperation, which in ERC-4337 is the smart account (userOp.sender), not the EOA that controls it. An allowlist maintained by hand covers none of that at scale. An attestation read from a public contract covers all three.
How the pilot is wired
0xgasless runs a sponsorship service in front of its paymaster. For UserOperations submitted under the pilot's API key on Fuji, the service calls KUMPLY's verify() for the smart account and approves sponsorship only when the account holds a valid attestation. Which tier it requires is their policy to set. The approval travels with the UserOperation as paymaster data, and on-chain their paymaster contract checks that the approval is genuine before the EntryPoint runs the operation.
One point worth being exact about: that on-chain paymaster does not read KUMPLY. It is a VerifyingSingletonPaymaster (source verified on Routescan), and its bytecode contains neither our AttestationStore address nor the selectors for verify() or attestations(). The KUMPLY check happens in the service. The chain only confirms the service's approval.
The demo, in three transactions
During the live demo, 0xgasless's service refused to sponsor a control wallet that had no attestation. We then issued a Tier 5 attestation to it, and the same wallet was sponsored. The refusal happened inside their service, so it has no transaction. What the chain does show is that the control account had no attestation at block 58,898,378, one block before we issued it. The three moments that did land on-chain are all on Fuji, and you can check each one. All times are UTC, September 30, 2026.
1. Sponsored operation, original pilot wallet. Block 58,898,127, 15:05:58. A UserOperation from the smart account that already held a demo Tier 5 attestation (issued on September 25), paid for by the paymaster: 0xc3b40a...2b70. Status success. The paymaster paid 0.000191 AVAX in test gas.
2. Attestation issued to the control wallet. Block 58,898,379, 15:14:04. One AttestationIssued event, with the control account 0x91a0...Bf33 as the subject, Tier 5, expiry September 30, 2027: 0x084e7d...d8aa. Status success. Issued by KUMPLY's verifier key, on our side.
3. Sponsored operation, control wallet. Block 58,898,490, 15:17:38. The same account, now sponsored: 0x4a4490...d9b1. Status success. The paymaster paid 0.000480 AVAX in test gas.
One detail in transaction 3 is easy to miss. The control account did not exist yet. That UserOperation carried initCode, and the logs include an AccountDeployed event, so the account was deployed by the very operation that was sponsored. We had issued the attestation to an address that was still counterfactual, and it worked because the attestation is keyed by address, not by whether code lives there yet. It also shows why the check has to run on userOp.sender: that is the address that exists in the UserOperation before anything is deployed.
The ERC-7562 catch: verify() can't run inside validation
The pilot checks KUMPLY off-chain, so it never hit this. A team that wants the check inside its own paymaster will. ERC-7562 defines the validation rules bundlers enforce on UserOperations, and rule OP-011 blocks a list of opcodes during validation, including TIMESTAMP. KUMPLY's verify() compares the expiry with block.timestamp, which is exactly that opcode. Call it from validatePaymasterUserOp and a bundler that enforces ERC-7562 will reject the operation.
The fix is to read the raw record instead. attestations(sender) returns the stored fields without comparing time, and the paymaster hands the expiry to the EntryPoint as validUntil in the returned validationData. The EntryPoint enforces that time check itself, outside validation. In our example, kumplyValidationData(sender) returns the rejection value when there is no attestation, when it was revoked, or when the tier is not exactly the required one, and otherwise packs the expiry into validUntil. The tier match is exact on purpose: tiers 1 to 3 are a ladder for people, while 4 (business) and 5 (agent) are separate categories.
The example is KumplyPaymasterCheck.sol, with 10 Hardhat tests that pass today. They cover a never-attested account, a person tier, a business, revocation, an expired record, a re-issue, and a trace check that the function never executes TIMESTAMP, unlike verify(). It is example code, not audited, and the pilot does not use it. The integration notes live at kumply.xyz/developers#paymasters.
Where this stands
This is a technical pilot on testnet. The attestations in it are demo attestations: KUMPLY's team issued them directly on Fuji, and there is no identity verification behind them. No document check, no business check, no review of the agent. On-chain, a pilot Tier 5 only records that KUMPLY marked that address as Tier 5 until an expiry. Please don't read it as proof of who operates an agent. Nothing from this pilot touches Mainnet C-Chain, and as of October 8, 2026, the mainnet AttestationStore reports zero attestations issued.
On October 2, 0xgasless said publicly on X: "Excited to support teams like @KumplyAvax with 0xgasless infrastructure, sponsored transactions and agentic payment rails." We appreciate it. Mainnet is something we expect to discuss with them next, but nothing is scheduled. Before any attestation is issued there, we plan to move our issuing keys to stronger custody and to run identity checks through a production provider.
If you want to build something similar
Gate on the account that sends the UserOperation, not its owner. Decide whether your check lives off-chain, like the pilot, or on-chain, and if it is on-chain, read attestations(sender) and return the expiry as validUntil. Match the tier exactly instead of using a "greater than or equal" comparison. Keep any approval you sign short-lived and never longer than the attestation's expiry, so a revocation takes effect quickly. Then start on Fuji, where reading verify() is free.
The guide is at kumply.xyz/developers, and the TypeScript SDK is on npm as @kumply/sdk (version 1.3.0 at the time of writing).
Sources: ERC-7562, 0xgasless on X, October 2, 2026, sponsored operation, original wallet, attestation to the control wallet, sponsored operation, control wallet.