← Blog
October 1, 2026 · 16 min

Auditing Nevermined's nvm:erc4337: x402 with an off-chain meter

Nevermined extends x402 with a scheme called nvm:erc4337: credits, plans and ERC-4337 session keys instead of one transfer per request. We read the spec, both SDKs and the contracts, probed the live facilitator, and censused Base, Base Sepolia and Tempo. The headers are x402. The rest is not: the 402 carries no price, the facilitator signs the credential the buyer presents, and by default the meter lives in a database.

x402's exact scheme is one EIP-3009 transfer per request. The buyer signs it, any facilitator can verify it, and the chain settles it. It cannot express a prepaid pack of credits, a monthly plan or a card. Nevermined, which sells payment infrastructure for agents, built two schemes for those cases: nvm:erc4337 for stablecoins held in smart accounts, and nvm:card-delegation for cards. Both reuse x402's three headers (PAYMENT-REQUIRED, PAYMENT-SIGNATURE, PAYMENT-RESPONSE) and its verify/settle split.

It is one of the most complete attempts to put plans and credits behind a 402. That is the problem we approached from the other side with the upto scheme. And a buyer agent that finds nvm:erc4337 in an accepts array needs to know what it is being asked to trust.

Our sources, all read on 1 October 2026: the x402 Smart Accounts Extension spec (v0.3, Draft, January 2026); the card delegation spec (v0.1, Draft, February 2026); the TypeScript SDK @nevermined-io/payments 1.13.0 and the Python payments-py 1.18.0, both published on 9 September (repository HEADs 28c9f92 and 7b0d0f1); the contracts repository at c36dd03 with its deployment manifests; unauthenticated probes against the sandbox and live APIs; and an event census on three chains. The facilitator backend lives in a private repository. Anything we say about it comes from public SDK comments, the docs, or behavior we observed, and we say which.

What nvm:erc4337 puts on the wire

This is the 402 the SDK's buildPaymentRequired produces for a protected route on the live environment:

{
  "x402Version": 2,
  "resource": { "url": "/ask" },
  "accepts": [{
    "scheme":  "nvm:erc4337",
    "network": "eip155:8453",        // eip155:84532 on sandbox
    "planId":  "4331…3873",          // 256-bit plan id
    "extra":   { "version": "1", "agentId": "…", "httpVerb": "POST" }
  }],
  "extensions": {}
}

Compare it with an exact requirement in the x402 v2 spec, which carries amount, asset, payTo and maxTimeoutSeconds. None of the four is here. What a credit costs lives in the plan record on Nevermined's API. How many credits this call burns lives in the seller's middleware config, 'POST /ask': { planId, credits: 1 }, and is never sent. A buyer can look up the price of a credit. It cannot learn from the 402 what this request will cost.

The buyer answers with a PAYMENT-SIGNATURE whose payload swaps the EIP-3009 authorization for session keys:

"payload": {
  "signature": "0x0184…",
  "authorization": {
    "from": "0xD4f5…eC8c",               // buyer smart account
    "sessionKeysProvider": "zerodev",
    "sessionKeys": [
      { "id": "order",  "data": "0x20a1…" },  // buy credits when short
      { "id": "redeem", "data": "0x68e8…" }   // burn credits per request
    ]
  }
}

redeem lets the facilitator burn credits. order lets it buy more when the balance runs low. The spec lists zerodev, biconomy and safe as session key providers. Every example we found in the docs and the test suites uses zerodev. The fiat sibling, nvm:card-delegation, sets network to "stripe", "braintree" or "visa", which are not CAIP-2 identifiers, and its payload is a JWT.

The design is the mirror image of the one we audited in Smart Sessions. There, session keys never fired for x402, because exact settlement is a facilitator transaction, not a UserOperation. Here the payment is meant to be a UserOperation from the start.

Who signs the payment

In exact, the buyer's key signs and nobody else can. Nevermined's documentation says the same of its scheme: it charges "using secure, locally-signed payment authorizations", and the spec's role table says the client "signs payment authorizations locally."

The SDK does something else. getX402AccessToken signs nothing. It POSTs the plan id, the scheme and a delegationId to /api/v1/x402/permissions, authenticated with the buyer's Nevermined API key, and returns the token that comes back. The shared request builder says it in one line: the x402 and MPP mints take identical inputs, and "only the EIP-712 domain the backend signs under differs." The type file is just as direct: X402TokenVersion is the "EIP-712 struct version the backend signs the access token under."

The card spec is open about this. Its JWT "MUST be signed by the facilitator's private key", with iss set to the facilitator URL. The ERC-4337 spec is not, but the public error catalog fills the gap. BCK.APIKEY.0017 reads "This API key was issued without a session key for this network." BCK.X402.0035 refers to a delegation whose "linked erc4337 permission is missing session keys (burnSessionKey / orderSessionKey)." Session keys are provisioned per API key and per network, and they are held server-side.

So the working credential is the Nevermined API key. Whoever holds it can create delegations and mint tokens within their limits. That is a reasonable product: a spending account with scoped keys, run by a company. But it is not the trust model the spec describes, and a buyer cannot check its own payment without Nevermined's API.

A reusable bearer token by default

The SDK documents two token versions. Version 2 is "today's default: reusable bearer token, signature covers [from, sessionKeysProvider, sessionKeys, planId]."

Look at what sits outside that signature: the network, the seller's resource, the HTTP verb, the amount, and any nonce or expiry. Section 8.4 of the spec says "Cross-network attacks MUST be prevented by including the network in signed data." Section 8.1 says replay protection SHOULD use nonces or timestamps. The default token does neither. Its only limits are the delegation's: a ceiling in cents and a lifetime in seconds, both enforced by the facilitator. Each settle of a v2 token burns again (the SDK's own phrase is "settle always burned"). So whoever holds a leaked v2 token can buy service on any endpoint that accepts its plan, billed to the original buyer, until the delegation runs out.

Version 3 fixes the binding. It appends agentId, resourceUrl, httpVerb and a 32-byte random nonce to the signed struct, and the first settle consumes the token. A second settle fails with BCK.X402.0059. The SDK comments add three caveats:

The last caveat flips the usual risk. A spent v3 token verifies clean. The seller's handler runs, which for a gateway like ours means a full inference. Only then does settle refuse it. The Express middleware keeps a process-local map of spent tokens with a one-hour TTL, so it can refuse the next replay before the handler. Its comment admits the map "does not span processes or horizontally scaled instances." The FastAPI middleware in 1.18.0 has no such map. Every replay runs the handler, and the body is withheld afterwards.

Who sets the price

Verify and settle both take maxAmount, a number of credits, and the seller supplies it. The 402 does not carry it and the v2 token does not sign it. The Express middleware evaluates the route's credits before verify. If credits is a function, it evaluates it again after the handler and settles the second number. That is dynamic pricing in the spirit of upto, with one difference. In upto the buyer signs a maximum. Here the bounds are the plan's per-request minimum and maximum, which the contract enforces only for burns that reach the chain, plus the delegation's total.

When the balance is short, settle buys credits first. In the docs' words, "the facilitator tops up their credits automatically, up to the delegation's limit." And delegations are "plan-agnostic by default." A single $100 delegation can top up any plan the agent happens to touch. The SDK accepts planId, maxTransactions and apiKeyId to scope it. Without them, it is an open budget.

Serve first, settle after

x402 lets the server choose whether to settle before or after doing the work. We measured what settling after costs in the two-phase gap audit. Nevermined settles after. Both SDKs also serve the response when settlement fails, except for a spent v3 token.

The Python middleware says so plainly: "Log but don't fail the response if settlement fails", because "the agent already delivered the value." The Express middleware attaches whatever settle returned to payment-response and ends the response. Settlement can also fail as HTTP 200 with success: false. The SDK logs a warning there and notes that "of the in-tree consumers only the MCP paywall actually branches on success."

The spec says something different. In its steps 29 and 30, a failed redeem or order makes the server return 402 PAYMENT-FAILED. Its own warning concedes that by then "the server has already performed work." For credits, this is a risk for the seller, not the buyer. A buyer whose balance cannot cover the burn, and whose delegation cannot top it up, still gets the answer whenever verify and settle disagree.

Where the meter lives

The spec describes settlement as UserOperations executed on-chain, with a transaction hash in the receipt. The plan configuration describes a different default. Every credits plan carries a field called onchainMirror. Nevermined's CLI reference defines it: "false keeps the credit ledger off-chain (default). true mirrors each burn to the on-chain NFT1155Credits contract." Every credits-config helper in the TypeScript SDK sets it to false. API version 1.1 dropped its legacy alias, proofRequired.

So in the default configuration, a request burns credits in Nevermined's database. The session keys in the token authorize a burn that never reaches the chain unless the seller opts in. Orders, meaning credit-pack purchases and pay-as-you-go charges, do settle on-chain.

The contracts show what a mirrored burn would actually prove. NFT1155Credits.burn still takes a signature argument. Its comment says both trailing parameters "are retained for ABI stability after the EIP-712 signed-burn flow was nullified (protocol#175 / nvm-monorepo#1253) and are ignored at runtime." Authorization comes from the plan's redemptionType. A holder can always burn its own credits. ONLY_OWNER lets the plan owner, meaning the seller, burn any holder's credits. ONLY_GLOBAL_ROLE lets any holder of CREDITS_BURNER_ROLE do it.

What the chain says

The deployment manifests list the same proxy addresses on Base, Base Sepolia and Tempo. We pulled every event the registry, agreement, credit and vault contracts have emitted on Base and Tempo, and the last 30 days of credit events on the sandbox.

// Nevermined protocol contracts, snapshot 2026-10-01 (~09:30 UTC)
// Base mainnet (8453): live since 2025-11-14, v1.5.0 since 2026-06-30
AssetsRegistry    0x1B09…4Ae4   agents registered                273
                                plans registered                 396
AgreementsStore   0x1B8B…660C   agreements                       104
                                  via FiatPaymentTemplate         90   // 2 senders, both FIAT_SETTLEMENT_ROLE
                                  via EntryPoint v0.7             14   // 4 smart accounts
NFT1155Credits    0xb2F9…2d64   credit mints                      23   // incl. ExpirableV2 0xF7Fe…872F
                                credit burns                      37   // all self-burns, 8 holders
                                first / last credit event         2026-02-23 / 2026-04-28
PaymentsVault     0x47A7…EA24   USDC deposits                      6   // 3 payers, 5.81 USDC total

// Tempo mainnet (4217): same six addresses, from block 27,846,938
all contracts                   logs                              18   // upgrade/authorization events only

// Base Sepolia (sandbox): last 30 days
NFT1155Credits                  mints 56, burns 11                     // burns from 2 holders

On Base mainnet, the contracts have recorded 37 credit burns in ten and a half months. All 37 were self-burns: the operator was the holder, which is the session-key path. The last one was on 28 April. The vault has received 5.81 USDC in total. Of 104 agreements, 90 are card payments written on-chain by two accounts that hold FIAT_SETTLEMENT_ROLE. On Tempo mainnet, the same addresses exist and have recorded nothing but their own upgrades. The sandbox is busier, but not by much.

This does not measure Nevermined's traffic. With the mirror off, per-request usage leaves no on-chain trace by design, and we cannot see the database. What it measures is how little of the scheme's on-chain machinery is in use. From outside, you can verify plan registrations, records of card orders, and a handful of USDC orders. Since 28 April, not one metered request on mainnet has left an on-chain trace.

What the live registry says

The plan endpoint on the live API needs no authentication. We fetched every plan registered on Base mainnet: 396 PlanRegistered events, of which 373 resolve and 23 return 404.

// GET api.live.nevermined.app/api/v1/protocol/plans/{id}, 2026-10-01
plans resolved                   373   // 128 distinct owners
registry.credits.onchainMirror   false 372 · true 1
redemptionType                   ONLY_SUBSCRIBER 195 · ONLY_OWNER 178
price.isCrypto                   true 195 · false 178
billingModel                     credits 323 · pay-as-you-go 50
crypto plans priced in a token
  with no code on Base             56   // Polygon USDC 49, Arbitrum USDC 6, Polygon USDC.e 1
fee share to Safe 0x2020…9A90    1.0% on all 167 crypto plans that list it

Three things stand out. One plan in 373 mirrors its burns on-chain. Almost half, 178, use ONLY_OWNER, the redemption type under which a mirrored burn needs nothing from the buyer. That is the setting on the most recent plan in the sample. And 56 crypto plans, registered in March by three owners, are priced in USDC addresses from Polygon and Arbitrum that hold no contract code on Base. The registry accepted them on chain 8453 anyway.

Who can change the contracts

The protocol contracts are UUPS proxies governed by an OpenZeppelin AccessManager. Calling upgradeToAndCall on NFT1155Credits requires UPGRADE_ROLE. On Base mainnet, that role belongs to the same Safe that collects the 1% fee. It is a SafeL2 with a threshold of one signer out of three owners. One of the three is the EOA listed as owner in the deployment manifest. The AccessManager execution delay on the grant is one second.

NFT1155Credits has been upgraded three times, most recently on 30 June 2026 for v1.5.0. That is a normal setup for a young protocol. It also means that a buyer relying on the contracts' rules, such as the redemption type, the per-request clamp or the vault split, is relying on a single key, not on code that cannot change.

What Nevermined gets right

Much of the engineering is careful, and the SDK comments are unusually candid. Most of what we found, we found because someone wrote it down.

Delegations are the right primitive: a spend ceiling, an expiry, optional plan, transaction-count and API-key scope, and one budget shared by x402 and MPP. Version 3 tokens are the right fix: bound to the seller, bound to the verb, single-use. The SDK refuses to bind a resource on a v2 mint rather than merely warning. The card spec gets money routing right. Its section 6.4 says the destination account "MUST NOT be accepted from the client", and section 6.2 increments the spend counter atomically before creating the PaymentIntent.

The contracts repository also adds orderWithAuthorization to the pay-as-you-go template: a gasless order paid with an EIP-3009 authorization whose nonce is bound to the agreement id. That is the primitive exact uses. And the facilitator now enforces authentication. On 1 October, a POST to /x402/verify or /x402/settle without a key, on sandbox or live, returned 401 BCK.AUTH.0002: "Anonymous access is not permitted on this endpoint." The published 1.13.0 SDK still describes that guard as optional.

What it means for LLM4Agents

Nevermined is solving a problem we have. Per-call exact payments are clean, but they cost a signature and a settlement per request for chatty agents, and they cannot express a plan. Credits backed by a delegation budget are the obvious answer, and we already run half of it. Deposits to our gateway settle on-chain. Per-token deductions come out of an off-chain ledger. The lesson is not that off-chain is bad. It is that a payment system should say plainly which parts are on-chain.

It also names a risk for the ecosystem. nvm:erc4337 uses x402's headers and its verify/settle vocabulary, but a standard x402 client cannot pay it. There is no amount to evaluate, nothing to sign locally, and the token comes from one company's API in exchange for an account key. If schemes like this spread under the x402 name, accepts[] stops being something a buyer can reason about. Our buyer tooling must treat unknown schemes as foreign, not as x402.

And it is a checklist for our own seller side. We sell expensive calls. A replay that runs the handler before it is refused costs real inference. A settle-failure policy that serves the body is a real leak. A token without a nonce is a real exposure. Nevermined has run into all three and documented its fixes in public.

Staying on the frontier

First, keep the price in the 402. An exact requirement carries amount, asset and payTo by construction; every route we sell should keep that property. For metered calls, use upto with the maximum stated. If we add credits, the 402 must say what the call costs in credits, or at least the maximum.

Second, make single-use, resource-bound credentials the default, and reject replays at verify, before the handler runs. The spent-nonce store must be shared across instances, in Redis rather than a process map, with a TTL at least as long as the credential is valid.

Third, decide the settle-failure policy explicitly, route by route. For inference, reserve the maximum at verify and settle the actual amount afterwards. Never serve on a deterministic failure: a spent credential, an insufficient balance, a revoked delegation.

Fourth, if we ship prepaid credits, make the ledger checkable. Return a signed receipt per call that includes the remaining balance. Publish a periodic on-chain commitment of the ledger state, such as a Merkle root, so a buyer can prove a deduction without calling our API. And state on the billing page which steps are on-chain.

Fifth, borrow delegations for agents funded by API key: a spend ceiling, an expiry, plan and key scope, and per-key revocation, issued from the account owner's dashboard.

Sixth, in buyer tooling, recognize nvm:erc4337 and nvm:card-delegation by name, label them "requires a Nevermined account", and never auto-pay them. Then watch three things: SDK release notes for v3 becoming the default, the next revision of the smart-accounts spec, and whether the x402 ecosystem adopts a registry for third-party scheme names.

Pay per call, with the price in the 402

One OpenAI-compatible gateway, paid per call in USDC over x402.

Register your agent