DocsPrivacy guide
Gas for fresh wallets. Keep it that way.
Stamp removes two links: the transaction that would fund a fresh address from your main wallet, and, for the relay, the link between a purchase and the stamps it later sees used. Everything else is up to how you use the address. This page lists, plainly, what can still link it to you and what to do about each.
Hidden, and not hidden#
Hidden
- The funding link
- No ETH is ever sent to the fresh address, so no top-up transaction ties it to the wallet that would have paid for its gas.
- Which purchase paid, even for the relay
- The relay signs blinded serials at purchase and sees unblinded ones at use; nothing in either links them. Every stamp of one 30-day window shares one key and one metadata string, so a stamp says nothing about its booklet.
- Your key
- Generated where you run the app or the agent, and kept there. The relay never sees it.
Not hidden
- Your purchase
- Buying a booklet is a public transaction: StampsBought names the buyer, the count and the price.
- What the fresh address receives
- Assets sent to the fresh address link their sender to it, publicly and permanently. Every operation is public on chain: sender, recipient, token, amount and time.
- Your IP address, twice
- The relay sees the IP address at purchase (stamp_issue) and at use (stamp_sendUserOperation). It keeps no logs, but you are trusting that.
- Your timing
- A purchase followed at once by the first operation sent with a stamp pairs the two in time, for the relay and for anyone reading the chain.
What to do
- Wait before using fresh stamps.
- Buy several at once and use them over time.
- Reach the relay over Tor or a VPN.
- Hidden: the funding link. A fresh address never receives ETH for gas, so no top-up transaction connects it to the wallet that would have paid. Its first operation carries its own EIP-7702 delegation, applied by the bundler's transaction.
- Hidden, from the relay too: which purchase a stamp came from. The relay blind-signs serials it cannot see, under a key and metadata shared by every stamp of a 30-day window. When a stamp comes back, its serial and signature match no purchase it ever handled. The published pins stop a relay from issuing you a key or window of your own.
- Not hidden: what the fresh address receives. Whoever sends assets to it is linked to it, publicly and permanently. Every operation is public on chain: sender, recipient, token, amount and time.
- Not hidden: your IP address, at purchase and at use. The relay sees it when you ask for issuance (
stamp_issue) and when you send (stamp_sendUserOperation). It keeps no logs, but you are trusting that. - Not hidden: timing. Buying a booklet and using a stamp right away pairs the two in time, for the relay (same IP,
minutes apart) and for anyone reading the chain (a
StampsBoughtfollowed by a firstStampCancelled).
What to do: wait before using fresh stamps; buy in batches (several stamps at once, used over days and weeks); reach the relay over Tor or a VPN, or run your own relay. Never use stamps for the buyer wallet's own operations.
Threat model#
An observer reads the public ledger, and the relay operator sees what reaches the relay. Stamps remove the funding transaction from the ledger, and the purchase-to-stamp link from the relay's view.
ClearThe funding link
Funding a fresh address from your main wallet writes the link on the public ledger. Anyone can read it, forever.
SealedNo funding tx
A stamp pays instead. The main wallet stays public but is never connected to the fresh address by a funding transaction: there is no such link to read.
Do and don't#
- Do create a fresh key for each identity, in a browser profile you don't use with your main wallet.
- Do export the key and your stamps (encrypted) before closing the tab, and burn the identity when you are done.
- Do buy stamps well before you need them, and several at a time.
- Don't connect your main wallet in the session that uses the fresh address. Buying stamps with it is fine; do it in another session, at another time.
- Don't send ETH or tokens to the fresh address from your main wallet, and don't send them straight back. Either writes the link stamps exist to avoid.
- Don't use a unique amount. Round, common amounts blend in;
1,337.42doesn't. - Do use Tor or a VPN, or your own relay, if the relay operator seeing your IP address matters to you.
What leaks where#
| Leak | Mitigation |
|---|---|
| IP address at the relay | One relay for everyone, no IP or body logs, zero retention. It still sees the IP at purchase (stamp_issue) and at use (stamp_sendUserOperation). Use Tor or a VPN, or run your own relay. |
| Purchase ↔ stamp at the relay | Blind signatures: the relay signs blinded serials and later sees unblinded ones; nothing in either links them. Only IP and timing can. |
| Timing | Buying then using at once pairs the two in time. Wait before using fresh stamps, buy several and spend them over days. Relay-held delay (delaySec) and batch windows blur the time on chain. |
| Tagged stamps | Issuer keys and the window policy are pinned in the deployment record; the SDK refuses any other key or window, so a relay cannot hand one buyer a key of their own. |
| Payer using stamps for its own ops | The purchase names the payer publicly; the agent refuses to use a stamp for a payer's own operations. |
| IP address at the chain RPC | Reads keyed to your address go only to the relay, and only when you ask. The chain RPC gets address-free reads. |
| EntryPoint nonce key | Always key 0. viem's default key embeds Date.now(), your clock to the millisecond, on-chain; the relay rejects it. |
| PoW nonce patterns | Mined stamps: each mining chunk starts at a random nonce. |
| Gas and fee fingerprint | Gas limits from fixed per-template profiles, fees from the epoch: every op of one template and one postage in one epoch looks the same, except PVG, shared per quote. |
| Delegate | Everyone shares one delegate, Simple7702Account v0.9, enforced on-chain. |
| Authorization nonce | Visible on-chain; a fresh key has 0. An imported key with history is already linkable by that history. |
| Browser | Never connect your main wallet in the session that uses fresh addresses. Burn the identity when done. A separate browser profile helps. |
| Assets and amounts | Whoever sends assets to a fresh address is publicly linked to it. Avoid unique amounts and round trips back to your main wallet. |
Reads keyed to your address#
A balance or nonce read tells whoever answers it that someone at your IP cares about that address. So:
- The app sends every address-keyed read (
pow_getAccountState) to the relay, which already sees your sends, and only when you import a key, press Refresh, or after a failed send (one read to resync the nonces). Nothing is polled. - A fresh key needs no read at all: its state is known locally (
freshAccountState), and confirmed ops advance it. - The chain RPC only receives public, address-free reads: epoch parameters, stats and logs.
stamp_getIssueris address-free too. - The epoch is polled on a fixed 60-second interval. A send normally builds from the last poll, so no read precedes it. When the SDK must read the relay itself first (it waited for the next epoch and refetches it, its own first fetch without a shared poller, a recipient check, or a resync after a failure), it waits a random time (up to 20 s, less near the end of an epoch) before mining or sending, so the relay can't time your send from that read.
Relay-held delay#
pow_sendUserOperation and stamp_sendUserOperation take a delaySec: the relay holds your op for up to the epoch's
remaining window before forwarding it, so its time on chain is not the time you sent it. In the app: Privacy options →
Relay-held delay. In the SDK: delaySec.
Exposure states#
Everything Stamp shows is in one of three states, always named in words, never by colour alone: clear values anyone can read, masked values only their owner can reveal, and sealed values that never exist or are never shown.
| State | Means | Used for | How to reveal it |
|---|---|---|---|
| Clear | Anyone can read it. | Transaction and userOp hashes, blocks, epochs, explorer links, “0 ETH · never funded”, gas paid, work in bits, and the review table before you send. | Nothing to reveal: it is public. |
| Masked | Hidden from anyone looking at the screen. The owner can reveal it. | In the app: your address, balances, amounts and counterparties. On /stats: the senders of sponsored ops (public on-chain, not displayed). | Press and hold “Hold to reveal”, or press Space or Enter on it. It reseals when you let go, change screens, switch windows or hide the tab. Private view off shows values in full after a confirm. |
| Sealed | Never exists, or is never shown. | The funding transaction (never sent). Your private key: Copy without display, or Show once for 10 s. | It can't be revealed: there is nothing behind it, or it leaves only through Copy or Show once. |
- Address · masked
- Example address: 0x9c4e2b7fd01e88a0…a041 (hidden on screen)
- Balance · masked
- Example balance: 1,204 (hidden on screen) NVDA
- Funding transaction · sealed
- Never sent
- Private key · sealed
- Private key, never shown
Revealing is always your act, and always brief: nothing lifts on hover or focus, copying never reveals, and every value reseals when you let go, change screens, switch windows or hide the tab. The mask hides values from people looking at your screen; it does not hide anything on-chain. What is on-chain is public, which is why the funding link is sealed rather than masked: it is never written at all.
Nonce key 0#
Standard ERC-4337 tooling (including viem's default) puts Date.now() in the EntryPoint nonce key, which publishes your
clock, to the millisecond, on-chain. The SDK always uses key 0 and the relay rejects any other key.
Fingerprints#
Gas limits come from fixed per-template profiles and fees from the epoch, so every op of one template and one postage in one epoch is identical apart from the shared PVG quote. Everyone delegates to the same Simple7702Account v0.9. A stamp op shows only its nullifier, a hash nobody else can tie to a purchase. The miner starts each chunk at a random nonce, so solutions reveal nothing about your device.
The browser#
The key is generated in the tab and held in memory. Encrypted persistence is optional: AES-GCM under a key derived from your passphrase (PBKDF2-SHA256, 600,000 iterations), stored in IndexedDB without the address in the clear. The export of your stamps uses the same scheme. Burn identity erases the key, the saved copy and the session's activity.