Occulta ZK state channel layer 3 on Arbitrum, enabling near-zero-cost transactions that are fully private, powered by ZK SNARKs




Occulta is a ZK state channel layer 3 on Arbitrum, providing instant near-zero-cost transactions that are fully private, powered by ZK SNARKs.
Users deposit ETH or USDG into a shielded pool, then open payment channels funded by their private notes and pay each other off-chain over an encrypted peer-to-peer link. Opening and closing a channel look like ordinary private transfers, so nobody watching the chain can tell a channel exists, who is in it, or how much moves through it.
Links:
Demo video | |
Pitch video | |
Website | |
GitHub | |
Relayer and libp2p relay |
Contracts on Arbitrum Sepolia:
Contract | Address | Source (Github) |
|---|---|---|
Pool |
|
|
Disputes |
|
|
Groth16 verifier |
|
|
Poseidon hasher |
|
|
All four build on the shared code in contracts/core: field arithmetic, Poseidon, the Merkle tree and Groth16 verification.
The contracts are not verified on Arbiscan or Blockscout, but anyone can check them.
Why the explorers can't: we deployed the contracts with our own build script instead of cargo-stylus, the official Stylus tool, which the explorers use to verify. They rebuild the source with cargo-stylus and compare the result byte for byte with the code on chain. Both tools produce the same Wasm, but they compress it with different brotli versions, so the compressed code differs for the verifier, pool and disputes contracts. The Poseidon hasher happens to compress the same way, so it can be verified.
Why we can't redeploy: we tried to redeploy with cargo-stylus. But on 2 October 2026, about two hours after our deployment, Arbitrum paused the activation of new Stylus contracts, and new activations fail on Arbitrum Sepolia too.
How to check them:
npm run verify:contractsin the repository rebuilds all four contracts from source and checks that each one matches the code on chain, byte for byte (details in the README, under "Verify the deployed contracts").
On a public chain, every payment is a public record:
Who pays whom. Sender and receiver sit side by side in every transfer.
How much. Amounts and balances are in plain sight.
How often. Every payment is a transaction, so habits and relationships show. Each one also waits for a block and costs gas, which adds up when two parties pay each other repeatedly.
That rules out many everyday payments, such as salaries, suppliers, subscriptions or a service paid per use. State channels were the classic answer to cost and speed, but not to privacy: a classic channel is opened and closed in public, so the two parties and the amounts stay on the record.
Occulta puts state channels inside a shielded pool:
Channel funds are private notes that only a ZK proof can spend, and opening and closing a channel are ordinary-looking private transfers, so the chain shows no channel at all.
Payments are signed messages, sent end-to-end encrypted between the two wallets, so they are instant, free and never on-chain. A channel needs at most three transactions (each side funds once, one closes), whether it carries ten payments or ten thousand.
Relayers submit every pool transaction and pay its gas, in exchange for a private fee note, so the user's public address never appears.
Each side holds every state both signed, so either side can enforce the latest one on-chain. Nobody can run off with the money.
Occulta provides unique selling points compared to existing solutions:
Private state channels. Classic state channels, such as Lightning or Raiden, open and close each channel with on-chain transactions that tie the two parties together and show the amounts. Occulta funds channels from a shielded pool, and opening and closing look exactly like any other private transfer: no one can tell a channel exists, who is in it, or how it ended.
Payments at the speed of a message. On a layer 1, every payment waits for a block and pays gas. A rollup makes it cheaper and faster, but each payment is still a transaction with a fee. In an Occulta channel, a payment is final as soon as both wallets sign it: no block, no fee, as many payments as needed.
Channels that are cheap to open. Other state channels run on expensive chains, where opening and closing a channel can cost more than the small payments it carries. Occulta runs on Arbitrum, where opening and closing cost little, so a channel pays off even for a short relationship or small amounts.
In the browser, still peer-to-peer. State channels usually mean installing and running a node. Occulta runs in a website with its own built-in wallet, with nothing to install. It stays decentralized, because the website runs its own peer-to-peer node: wallets talk to each other directly through relays, keys never leave the browser, and no Occulta server holds user data.
State channels as easy as a chat app. State channels have a reputation for being hard to use. Occulta shows each channel as a conversation, inspired by messaging apps:
payments appear as messages, and peers have nicknames;
invites are a link or a QR code;
channel requests and opening progress show up in the chat.
Compared with existing approaches:
Layer 1 | Rollup | Classic state channels | Shielded pools | Occulta | |
|---|---|---|---|---|---|
On-chain transaction per payment | Yes | Yes | No | Yes | No |
A payment confirms in | A block | A sequencer confirmation | An instant | A block | An instant |
Fee per payment | Gas | Low gas | None | Gas | None |
Cost to open and close a channel | – | – | High, on a layer 1 | – | Low, on Arbitrum |
Who pays whom is visible | Yes | Yes | Yes | No | No |
Amounts are visible | Yes | Yes | Deposits and final split | No | No |
What stays visible by design: deposits into the pool and withdrawals from it (address, token, amount), and, during a dispute, that some channel is being closed. As in any shielded pool, privacy grows with the number of people using it.
Occulta combines three parts:
A shielded pool on Arbitrum holds everyone's tokens as private notes, which are spent with zero-knowledge proofs.
Two-party payment channels are funded from those notes. Payments are end-to-end encrypted, signed off-chain messages, and opening and closing a channel look like ordinary private transfers.
Relayers and libp2p relays submit transactions and carry messages, so users never expose their address or their IP address.
The flow below follows a user's money from the first deposit to the withdrawal.

The wallet derives the account's private pool keys from its own key.
The user deposits ETH or USDG into the shielded pool.
The deposit becomes private notes that only the user can spend.
The user chooses how to pay:
4a. To pay someone once, a private transfer inside the pool.
4b. To pay someone often, an invite shared privately with them, as a link or QR code.
Each side funds the channel with an ordinary-looking private transfer.
Payments are encrypted, signed messages: instant, free, as many as needed.
When the two are done, the channel closes:
8a. If both cooperate, one ordinary-looking transfer pays out both shares.
8b. If one side vanishes or cheats, the latest signed state is enforced on-chain.
The payouts come back as new private notes.
They can be withdrawn to any address, or fund the next channel.
The contract that holds everyone's tokens as sealed notes, so balances and transfers stay hidden.

A deposit sends ETH or USDG from the user's address; the pool records a note for it.
To spend notes, the wallet proves in the browser (Groth16) that it owns two and creates three: payment, change, relayer fee.
A relayer submits the proof and pays the gas.
Note contents are encrypted on-chain; every wallet reads all events and decrypts only its own.
A withdrawal pays out to any address the user enters.
How two users set up a private channel safely, without trusting each other or anyone else.

Alice sends an open request through Bob's invite. Bob is asked to approve only if she wants him to fund too.
Bob answers with a fresh channel key, his share of the channel secret, and his signature of state 0, which returns Alice's money to her if he disappears.
Alice funds the channel with an ordinary-looking private transfer.
Alice signs state 1, which includes both contributions.
Bob funds only once Alice's contribution is in the pool. Then the channel is live.
Payments are balance updates both sides sign; closing turns the final balances into private notes.

Alice proposes a new state with the balances moved, and signs it.
Bob countersigns, but only if his balance does not go down. Steps 1 and 2 repeat for every payment, with no transaction.
To close, Alice proposes a final state carrying the relayer's current fee.
Bob signs it.
Alice sends a relayer a proof that spends both contributions as the final state says.
The relayer submits it as one ordinary-looking private transfer.
Alice receives her share as a new private note.
So does Bob.
The fallback that keeps every channel self-custodial when the other side disappears or cheats.

The other side stops answering, or tries to close with an old state.
Either side submits the latest state both signed. It reveals no names and no amounts.
A dispute window of 3 to 7 days starts; the website always uses 7.
Anyone holding a newer state can still submit it before the deadline.
A newer state replaces the pending one, and the deadline does not move. An open wallet does this on its own.
After the deadline, the contract pays out the pending state.
The contract checks whether that state left out a contribution.
If it did, the contribution's owner reclaims it.
Services that submit pool transactions for users, so a user's own address never pays gas or shows up on-chain.

The wallet sends the relayer a proof whose third output is a fee note for the relayer.
The relayer checks its fee note, simulates the transaction, submits it and pays the gas. It cannot change anything without breaking the proof.
The relayer receives its fee note, privately. Anyone can run a relayer with the desktop client.
How two wallets reach each other directly, without revealing their IP addresses or using a public directory.

Bob reserves a slot on a libp2p relay.
Bob shares an invite privately, as a link or QR code. It holds the relay address, his peer ID and his shielded address, but never his IP address.
Alice dials Bob through the relay.
The relay forwards an end-to-end encrypted connection.
Every channel message travels over it: opening, payments and closing.
The two ways to run Occulta, both built on the same framework.
Website: its own built-in wallet with a standard recovery phrase, password-encrypted storage, and export and import.
Desktop client: a CLI and a local RPC API; it can also run a relayer and a libp2p relay.
RPC endpoints: the user's own come first, with public ones as a fallback.
Private payments on Arbitrum itself. Users and apps get private, high-frequency payments without leaving Arbitrum or bridging to another chain like Aztec, Monero, or ZCash.
Gasless, instant transactions. Payments inside a channel settle the moment both sides sign: no block to wait for, no gas, no fee per payment. Users only pay to open/close channels.
Micropayments. Payments this fast and this cheap make true pay-per-use possible on Arbitrum: an AI agent paying an API per request, or a cloud service billing compute as it is used, with everything settled privately in a single transaction at the end.
More USDG at work. The pool holds and moves Paxos USDG alongside ETH, giving the stablecoin a private and instant payment rail.
Occulta fits best when the same two parties pay each other often, when who pays whom or how much is worth hiding, and when the receiver can be online to sign each payment, as any service is.
Use case | Who pays whom | Why Occulta |
|---|---|---|
Pay-as-you-go infrastructure | A user pays for GPU or cloud compute by the minute, or for RPC nodes, VPN bandwidth or storage | Paying by the minute is impossible on-chain, where every payment waits for a block and pays gas. In a channel, payments are instant and free, so the user pays as the service is delivered: neither side risks more than one small payment, and if the provider stops, the payments stop. |
AI agents buying APIs | An agent pays a model, data or tool provider per request | An agent pays for each request the moment it makes it, with no gas and no block to wait for. It also hides which providers an agent uses and what it spends. |
Content and creators | A reader pays per article, a viewer per minute of streaming, a fan tips during a live stream | No one wants to pay a fee on every donation to a creator they support, yet on-chain every tip pays gas and waits for a block. In a channel, tips, articles and minutes of streaming are paid instantly and with no fee, however small or frequent. Who a person supports, and what they read or watch, stays private. |
Games | A player pays the game server for in-game items or per match | Games are full of small purchases: items, upgrades, a fee per match. If each one waits for a block and pays gas, it interrupts the game. In a channel they are instant and free, as quick as a click, and no one else can see what a player spends. |
Local stores | A regular customer pays their café, grocery store or canteen every day | The customer tops up a channel once, like a store card, then pays instantly at the counter with no fee. Unlike a store card, unspent money comes back to the customer, and no one sees who shops where. |
Business and supplier settlement in USDG | A business pays a supplier, a platform pays its affiliates or ad publishers | Supplier lists, volumes and prices are trade secrets that a public chain would otherwise show to competitors. |
Freelancers and contractors | A client pays per hour or per milestone | The freelancer's income and client list stay off the public record. |
Trading counterparties | Two desks settle with each other in both directions | Both sides can fund a channel and payments go either way, so the two can net out without showing positions or flows. |
Smart contracts: Arbitrum Stylus (Rust, stylus-sdk, alloy)
ZK: Circom, Groth16 on BN254, snarkjs, Poseidon
Signatures: EdDSA on Baby Jubjub
Encryption: X25519, HKDF-SHA256, XChaCha20-Poly1305 (noble)
Core utils: TypeScript, viem
P2P: libp2p (circuit relay v2, WebSockets, Noise, Yamux)
Web UI: React, Vite
Desktop client: Node.js, Express
Network and tokens: Arbitrum Sepolia, ETH, Paxos USDG
This project is fully built from scratch during Arbitrum Open House Singapore: Online Buildathon.
No funds raised yet.