I made an Ark wallet with a unilateral exit that a user can actually use.

hoppe2

2. August 2026August 2, 2026 · 14 Min. Lesezeit14 min read

This is just a vibe-coded proof-of-concept wallet. Don't put any meaningful amount of money in it.

I saw the recent argument that in these "easy" Lightning-alternative wallets — the ones that advertise no channels, no liquidity to manage; Spark being the main one, and broadly Ark too — the unilateral exit is just talk. I think Ark has the better trust model among them, but even there nobody had actually built an exit a user can really use. I'd been working on an app out of the same frustration, so I finished a prototype and I'm sharing it.

https://github.com/tajava2006/arkade-nwc-bridge

I built the UX so a user can actually unilaterally exit by clicking, not by running some complicated CLI.

  1. A pre-signed exit transaction needs CPFP, so you need an address the client fully controls. Instead of some separate address, I derive a plain taproot address from the client's own private key and let you top it up as exit fuel. Unlike a normal ARK address (whether an Ark address or an onchain boarding address), this one has no extra script — it's unlocked by a single private key only — so the client can freely use it for CPFP.

  2. To minimize the exit right's dependence on the server, I made this a long-running daemon rather than an ordinary client. To fully own the exit right you have to grab the proof from the server at the exact moment your balance changes, and back it up. If you don't get the proof, you just treat the money as not received — so at that point the ownership question is demoted to a UX question. The long-running daemon persists the proof automatically on every balance change, so at the actual moment of exit the server isn't needed. For what it's worth: as far as I know, on a statechain like Spark even a proof (recovery bundle) you've already fetched can later become invalid depending on what the server does, so you have to keep refreshing it — whereas in Ark a proof you've captured once is immutable until that VTXO expires, so Ark is better on this point. That said, Ark also mints new proofs on every receive/change/refresh, so if you want a fully current sync you end up needing to keep it running anyway — so I wouldn't call it an overwhelming advantage over Spark.

  3. Obviously, since change (a new VTXO) also comes back when you send, not just when you receive, the exit right is synced the moment any balance change happens.

  4. The exit right is a pre-signed transaction tree, and a lot of VTXOs end up sharing the same transaction as their proof. So for storage efficiency, when they overlap I only store one copy.

  5. By "syncing the proofs" I mean there's GC logic that automatically drops proofs you no longer need after spending, so storage doesn't grow without bound. And of course it doesn't delete pre-signatures that other proofs still need — it only picks out and deletes the pre-signatures needed exclusively by the spent VTXO.

  6. The "spent, no longer needed" call is not just taking the server's word for it — it demands proof that I spent it. That is: if I hold a VTXO with an exit right and the server claims I spent it, the client asks the server to produce the spend transaction signed by my key, verifies it, and only deletes on a match. So it correctly detects and syncs the case where you use the same private key in a separate Arkade wallet, without false alarms. If the server can't produce it, the proof is immediately quarantined and preserved permanently.

  7. The opposite thing a server could pull — claiming it credited you a VTXO but withholding the proof — is also surfaced on the dashboard right away.

  8. You can exit each VTXO separately, so you can pick only the economically worthwhile ones. It shows how many onchain transactions it'll take in total and the estimated exit cost, so you don't pay unnecessary exit fees, and because there's an address for CPFP, if fees spike mid-exit and your mining priority slips you can quickly bump it via RBF. You can watch the progress as a graph.

  9. The default exit destination is the taproot exit-fuel address from #1. It's an address you already fully control, but if you want a different one you can register it, and to prevent copy-paste mistakes it only sends to an address that has passed a challenge.

  10. I hate making users set up env vars, so it runs with just git clone -> bun install -> bun run dev, no special .env needed.

  11. Everything used for the exit is identical across Arkade, so at fresh start you can enter which Ark to use — if you already have an Ark wallet, put in that server and private key and it instantly syncs the proofs of every VTXO in that wallet. So it's compatible with the official arkade.money. (One catch: official arkade.money has delegation ON by default, and turning delegation on adds a tapleaf that changes the ARK address even for the same key — so only funds on a delegation-off address land on the same address and sync.)

  12. It boots fine even when the server is down. Everything else stops working, but the exit function still works. Of course, to actually exit you need the proofs to have already been synced beforehand — you can't leave it off, have the server go down, and then use this to escape.

While I was at it I improved usability a bit too:

  1. I made it easy to empty out. With other wallets it's annoying that you can't cleanly deal with leftover dust change, so here you can make an invoice for the right amount to leave with zero change on the Lightning side, or hit the max button on an onchain send to take everything out cleanly.

  2. I implemented a sub-dust (under 330) atomic swap. sub-dust LN swaps themselves were supported first by the Second team's bark, but that one is non-atomic (server-trusted) — there's no pre-signed commitment underneath, so if a swap only half-completes the server can end up taking the sub-dust HTLC. Making it atomic via a pre-signed split is, I believe, a first in the Ark camp.

To unpack the background: a sub-dust amount can't be a standalone onchain output in the first place (Bitcoin dust rule). Ark's unilateral exit means pulling out onchain without the server's permission, and for sub-dust that onchain claim is below dust so it vanishes. That's why Arkade blocks a sub-dust VTXO from being spent offchain until a refresh — in a UTXO model where coins merge and split, if you just let it be spent it would contaminate the exit rights of other perfectly good VTXOs. (Note this is a per-implementation policy choice, not a protocol hard-lock. bark, by contrast, has a policy of never merging VTXOs, so it doesn't have this contamination, and it lets you spend sub-dust relatively freely and supports sub-dust LN — but it pays for that with fragmentation, since change only ever keeps splitting.)

A normal atomic swap sends the target amount to an intermediate script address for the claimer to sweep later, and that sub-dust output can't be swept before a refresh, so it gets stuck at exactly that step. That's why the official implementation's minimum swap amount is 330.

I implemented sub-dust atomic swaps by not sending the swap's target sub-dust VTXO to the intermediate script address, but instead sending a larger, normal dust+ amount and having it split on claim. This obviously requires changing the server, and it doesn't work on the official Arkade wallet — only on my own Ark server.

Here are actual screenshots of the wallet.

image
image
image
image
image
image

https://mempool.space/tx/b663386e24ee1419dba26306e1cf58ada2875d0d9c29bdc4c3801b272b543bd3

https://mempool.space/tx/eb5cfb1111c447187b7866b5981af991852e944d4ffb3545ec638ea604b9c33a

image

A lot of people dislike Ark, but I think Lightning has too many limits for an individual to run, so I think an alternative is needed. If the problem were simply that it's hard, I'd have become a Lightning purist too. But the problem isn't difficulty — with Lightning, block space is consumed exclusively for a single person, in proportion to the number of people. Given that block space is hard-capped while the number of people only grows, complete self-custody becomes close to impossible. The day when a single onchain transaction fee is more than a person can afford over an entire lifetime's income will inevitably come. Broadly, there are three proposed ways to ease this:

  1. Hard-fork to increase block space itself.
  2. Offload to sidechains (merged-mined, drivechains, etc.).
  3. Statechains. The Spark cited in this post belongs here.
  4. A shared-utxo model like Ark.

I don't know Spark (statechains) precisely so I'm cautious, but here's why I think Ark is better on the trust model. In Ark the operator can never move my funds with its own keys alone. The exception is a VTXO I just received (not yet refreshed) being double-spent by the operator colluding with that VTXO's previous owner — and that disappears the moment I refresh once and cut the chain of previous owners, i.e. ownership is fully confirmed on every refresh. As far as I understand it, on statechains this "cut the history to confirm" concept is weak, so operator trust lingers longer (not certain). So despite Ark's few downsides, I think #4 has the fewest side effects among the solutions proposed so far, and I've tried to contribute by implementing, as a PoC, a solution to those downsides.

Thanks to the economies of scale in the model's structure, if Ark takes off, the Ark server can run a sustainable business while refreshing very often, the user gets ownership confirmed on every refresh, and because many people participate at once the onchain fee is suppressed to nothing or a negligible level. The ASP sustains its business by charging only for leaving its own Ark.

Take a look and I'd appreciate a review.

#1#4

Written by

Comments

No comments yet. Start the conversation.