Cover photo

Wallet Proof a Novel Concept for x402-based Services

Follow-up to the mnemospark launch: how the custom REST surface decides who is calling before JSON, payment headers, or storage logic run.

While I was wiring the mnemospark backend APIs, I kept bumping into this idea: this stack expects callers to interact with payment via an Ethereum wallet and address. So, if that is where the road ultimately goes, why let traffic in to wander around into JSON bodies and storage flows before anyone had even shown they could present a valid wallet?

If you cannot prove control of a wallet at the door, the service should not behave as if you are ready to complete the rest of the flow and it should not burn additional trust and cycles on you.

That is the practical origin of wallet proof: treat it as the API’s handshake layer, the same role session establishment plays in older stacks, except the handshake is verifiable at the edge, bound to this HTTP call, and aligned with how the API Gateway and Lambdas are wired into the mnemospark-backend.

Thus the “wallet proof” concept was born:

The wallet proof is a cryptographically signed value that authenticates the client to the mnemospark-backend. The client proves it holds the private key for a given wallet address by signing a structured request payload. The backend verifies the signature and can reject requests with invalid or expired proofs.

So how does it work?

Architecture at a high level (three bands)

  1. Client — the mnemospark client holds a EVM compatible wallet and can sign transactions and produce a X-Wallet-Signature.

  2. Edge — API Gateway fronts the REST API. A Lambda request authorizer runs on protected invocations: decode X-Wallet-Signature, verify cryptography, enforce freshness and replay rules, then return allow with wallet context or deny. Only after allow does traffic reach the route Lambda.

  3. Application — Handlers for quotes, storage, payment settlement, persistence (DynamoDB and object storage) rely on the authorizer outcome to scope data by wallet, not to re-invent trust from ad-hoc headers alone.

An OpenAPI v3.2 spec file sits beside this as the published contract: paths, methods, error shapes and the wallet proof security scheme.

Deeper dive

1. Request payload

A mnemospark request payload is built from:

Field

Description

method

HTTP method, normalized to uppercase (e.g. GET, POST, DELETE).

path

Request path, normalized: leading slash, no trailing slash, no query/fragment (e.g. /price-storage, /storage/upload, /storage/ls).

walletAddress

EVM address (checksummed) of the wallet that will sign.

nonce

32-byte cryptographically random hex string (0x + 64 hex chars). From createNonce() in src/nonce.ts.

timestamp

Unix time in seconds (string).

2. EIP-712 typed data signing

The payload is signed as EIP-712 typed data (see EIP-712), using types that align with Solidity and OpenZeppelin EIP712 (v4 encoding).

3. Header envelope and encoding

The X-Wallet-Signature header value is a base64-encoded JSON object with:

Field

Description

payloadB64

Base64-encoded JSON of the MnemosparkRequest payload.

signature

The EIP-712 signature (hex string).

address

The signer’s address (must match payload’s walletAddress).

So: X-Wallet-Signature: base64(JSON.stringify({ payloadB64, signature, address })).

Where it is used

mnemospark app

Plugin / OpenClaw (via proxy)

The local mnemospark proxy holds the wallet key. When the plugin sends a storage or price-storage request, the proxy builds the payload for the requested method and path, signs it with the configured wallet, and forwards the request to the backend with X-Wallet-Signature set

Backend authentication

The wallet proof is sent on every request to mnemospark backend APIs that are scoped to a wallet:

  • Price / storage quote: POST /price-storage

  • Upload: POST /storage/upload

  • List: POST /storage/ls

  • Download: POST /storage/download

  • Delete: POST /storage/delete

The client sets the HTTP header:

X-Wallet-Signature: <base64 envelope>

The backend:

  • Decodes the envelope and payload.

  • Verifies the EIP-712 signature for the given domain and message.

  • Can enforce nonce/timestamp (e.g. reject expired or replay).

  • Returns 403 with wallet_proof_invalid (or similar) when the proof is missing, malformed, or fails verification.

The wallet proof is used to authenticate the caller as the holder of that wallet and to bind the signature to method, path and nonce/timestamp.

How this stacks with x402 payments

The same OpenAPI surface documents HTTP 402 and payment headers for USDC / x402-style flows. Those layers are intentionally separate:

  • Wallet proof answers who is calling.

  • Payment artifacts answer whether they paid for the resource.

A caller can be cryptographically identified and still receive 402 Payment Required until settlement headers satisfy the payment path.

Limits and forward ideas

Wallet proof is gate keeping, not a full zero-trust program: it does not fix operator key custody on the machine that signs, and it does not stop a legitimate wallet from misbehaving after it is admitted. What it does do is give you a clean, repeatable eligibility decision at the edge that opens room for stricter, more creative policy before expensive work runs.

The obvious next layers are identity-aware screening at the same choke point, wallet deny-lists where your risk model calls for them, and where your legal and compliance posture requires it. Such as hooks into sanctions and AML workflows that evaluate addresses against authoritative lists (for example OFAC and UN consolidated screening expectations) so high-risk traffic can be refused early.