Zcash → NEAR Intents 1Click · wallet-side
A new refund address and recipient for every swap.
Every swap out of Zcash through 1Click names a refund address and a recipient in a public request. Lethe gives each swap a refund address used once and its own NEAR account, both from one seed, and tells you what still links them.
The one-time refund key for 1Click's refundTo builds on Zakura's quantum-unlinkability proposal (Oct 19) and its draft crate zakura-one-time-ivk. Lethe adds a NEAR recipient per swap from the same seed, recovery from the seed alone on both chains, a locked index book that refuses a reused recipient, opt-in amount and timing decorrelation with a linkability report, and MCP tools.
The same three swaps, two wallets
The refundTo each wallet puts on three public 1Click requests.
A wallet that reuses its address
One address on all three: anyone who reads the requests sees one wallet.
Lethe
Three addresses, three viewing keys. The addresses alone don't connect them.
Before: the first run
The first run's three requests, every field checked for what could link them.
All three links, measured
The refund address was the easy link. The recipient, the amount and the timing are the others. Each batch is scored from its real 1Click requests by the report the CLI and MCP print.
Pass 2 parts
Pass 3 parts
Sweeping re-links. Moving several per-swap accounts into one account shows them all paying it, on chain. zswap sweep-plan flags that: leave each account alone, sweep each somewhere different, or swap each back into a shielded address at another time and amount. A token-only account needs NEAR for gas, and a top-up from your own account links it too.
Why not 1Click's Confidential Intents?
1Click's own private mode (confidentiality: basic or advanced) keeps the quote request out of public view. It is worth using, and Lethe passes it through (--confidentiality). It does not replace a refund address and a recipient per swap:
- It needs a partner account1Click answers a confidential dry quote with HTTP 401 "User authentication is required for confidential intent quotes". Without
ONECLICK_JWT, Lethe says Setup required and reserves nothing. - 1Click and its solvers still see both fieldsThe refund address and the recipient go to the provider in every mode, and the deposit, refund and delivery happen on public chains. A provider-side breach, a subpoena or a future quantum attacker reading those records links every swap that reused them.
- They composeA confidential request with a one-time refund address and its own recipient hides the request and leaves nothing to link even for the provider's own records.
Found again, from the seed alone
1Click has no testnet, so a demo provider wallet paid each one-time testnet address as a stand-in refund. A compact-block scan finds each payment with its own key only.
Not yet
- BLOCKEDA real swapA live quote creates a mainnet deposit address and needs mainnet ZEC and the maintainer's sign-off. So no real 1Click refund to a one-time address has been seen yet; the quotes above are dry.
- Draft key derivation
zakura-one-time-ivk0.0.1-rc1 says it must not issue live addresses before review. Mainnet addresses appear here only in dry quotes. - Spending a refundThe one-time keys keep the account's spending authority, but this project does not spend a one-time note.
- Delivery to a per-swap NEAR account1Click accepted each fresh implicit account in a dry quote; nothing was delivered, so no sweep has been run. Amount splitting and delays are planned by Lethe, not sent on a schedule by it.
Run it
Rust, a compact-block scanner, a CLI and an MCP server. The tests call the real 1Click API with dry quotes and scan Zcash testnet.
pnpm install && pnpm build && pnpm test
pnpm --filter @zswap/cli zswap init # a seed and its index book in .demo/
pnpm --filter @zswap/cli zswap quote 0.1 nep141:wrap.near # its own refund address and NEAR recipient
pnpm --filter @zswap/cli zswap plan 0.35 nep141:wrap.near --split --window 180
ZSWAP_BOOK=/tmp/restored.json pnpm --filter @zswap/cli zswap recover <from-height> --lookup # from the seed alone
pnpm --filter @zswap/dashboard dev # the dashboard: http://localhost:5182