Bringing confidential compute to Avalanche
Most of what happens on-chain today is public by default. For a lot of the things our ecosystem wants to build next — confidential payments, private DeFi, sealed-bid auctions, on-chain games with hidden state — that's a dealbreaker. This is our engineering take on closing that gap natively, on infrastructure we control, with no dependency on any outside operator.
1. What we set out to prove
The enabling technology is Fully Homomorphic Encryption (FHE) — it lets a computer do math on numbers while they stay encrypted. Applied to a chain, that means balances and inputs can live on-chain as ciphertext, and the network can still verify a transaction is valid without ever seeing the values. Zama's FHEVM is the most mature implementation, and it's open source.
So we asked a simple question: can Avalanche run this entire stack itself? Not as a hosted service someone else operates and meters — but built from source, on our own boxes, serving our own chains. The answer is yes, and this site is the proof:
2. The core idea: math on locked boxes
You don't need cryptography background for the intuition. FHE is like being able to add up the contents of two sealed boxes and get a third sealed box with the correct sum — without ever opening any of them. The chain can update an encrypted balance, check an encrypted amount is affordable, and move encrypted funds, all without learning a single number.
The way users interact with it mirrors a casino:
Cash = public USDC
Ordinary tokens. Balances and payments are fully visible, like everything on C-Chain today.
Buy chips = deposit
You move public USDC into the confidential pool. The deposit amount is public (you're handing over visible cash) — but once inside, it becomes an encrypted balance.
Play = confidential transfer
Move value inside the pool. Everyone sees a transfer happened; nobody sees the amount.
Cash out = withdraw
Convert back to public USDC. The amount you take out becomes public again at the boundary.
3. What's private, and what isn't
We're precise about this, because over-promising privacy is how people get hurt. Here's exactly what each action exposes on-chain:
| Action | Sender | Receiver | Amount |
|---|---|---|---|
| Deposit (shield) | visible | — | public |
| Confidential send | visible | visible | |
| Private send | visible | ||
| Withdraw (unshield) | visible | — | public |
Deposit/withdraw amounts are public because they're the on/off-ramp between public and confidential funds — the same boundary every privacy system has. Everything inside the pool is encrypted.
How "private send" hides the receiver
A private transfer doesn't touch only you and the recipient — it touches every member of the pool at once, handing each an encrypted amount. Almost all are encrypted zeros; only the real recipient's is the actual amount. Since every entry is ciphertext, an observer can't tell which one was real — the recipient is hidden in the crowd, and privacy scales with the size of that crowd. (You still sign the transaction, so your own address shows as the sender; hiding that too needs zero-knowledge spend proofs, which we'll come back to.)
4. The stack we run
Here's the system we self-host, component by component. The useful split: the chains remember state and agree on order, the coprocessor does the encrypted math, the KMS guards the key, and the relayer + listeners are the plumbing that connects them.
Host chain — Avalanche (Fuji / C-Chain / any L1)
This is the Avalanche chain our confidential token is deployed on and where users transact. It deliberately does not do the FHE math — that's far too heavy for an EVM. Instead the contract records what math to perform and emits it as events ("subtract handle A from B, add to C"), and stores each balance as a handle — a 32-byte pointer to a ciphertext, never a plaintext number. It also holds the ACL: the on-chain record of who may decrypt which handle.
Coprocessor
The host chain writes down instructions; the coprocessor carries them out. It watches the chain, picks up the symbolic FHE operations, performs the real fully-homomorphic computation on the ciphertexts, and writes the encrypted results back. It's the most resource-hungry part of the stack (CPU + lots of RAM for keys), and it's several specialized workers:
Every result it produces is signed so the rest of the protocol can trust it came from an authorized operator. In our benchmarking this caps around ~20 TPS per instance on CPU (no GPU path in v0.11 yet), which is exactly why we scale it horizontally rather than vertically.
KMS — Key Management Service
The KMS holds the FHE secret key — the one thing that can turn a ciphertext back into a number. It decrypts only when the gateway confirms the requester is authorized by the ACL. Two modes: user-decryption (reveal to one person — the "Reveal" button) and public-decryption (make a value public — used at withdraw). Every decryption is signed so apps can verify it.
In this demo it's a single node holding the whole key — fine for a testnet proof. In production we'd split the key across many nodes so no single operator ever holds it; decryption then requires a quorum. That's the single most important hardening step, and §6 is about how we'd run it across our validator set.
Gateway chain
A blockchain separate from the host chain that acts as the protocol's source of truth for everything cross-cutting. It's deliberately not the host chain because it's meant to be a shared hub: many host chains (Fuji plus other L1s) plug into one gateway, so key management and decryption logic live in one place rather than being copied onto every chain. It runs:
In our setup this is its own node (chain 54321), kept local — it is not the host chain, and it's the piece that most naturally becomes its own Avalanche L1.
FHE.allow(handle, you) writes the permission
there, and it's the single source of truth. On a decryption request, the gateway/KMS side reads that ACL
across chains (our kms-connector is configured per host chain with its RPC + ACL address). Miss that
config and decryption fails with "No ACL contract config found for chain id …" — a real gotcha we hit.
When the host and gateway are the same chain (§7), there's no cross-chain read at all.Relayer
What apps actually talk to. It serves the FHE public keys (so a browser can encrypt), turns inputs into handles + proofs, and forwards decryption requests to the gateway/KMS. It holds no secrets and no special trust — it's a convenience and scaling layer. Anyone can run one, you can run many, and the worst a bad relayer can do is refuse service.
Listeners, connectors & storage
The host-listener/poller and gw-listener watch the chains and feed events to the coprocessor; the kms-connector bridges the KMS to the gateway (posts signed decryptions, reads ACL). None are trust-critical on their own. Rounding it out: object storage (MinIO/S3) for the large ciphertext + key blobs that don't fit on-chain, and Postgres for the coprocessor's and relayer's working state.
5. The lifecycle of a transaction
Every confidential action has the same shape: encrypt → submit → compute → (later) decrypt.
We built an interactive view that walks each action through these phases, station by station:
▶ Open the interactive transaction flow6. Making it ours: decentralizing on an Avalanche L1
What's running today is centralized — one box, one key holder, one coprocessor. That's the right scope for a proof of concept, but it means users are trusting us. The whole point of doing this on Avalanche is to spread that trust across the validator set we already operate. One rule guides the design:
What becomes what
| Piece | Today (our box) | Decentralized form |
|---|---|---|
| Gateway chain | single node | ✅ Becomes an Avalanche L1 — validators run its consensus |
| Host chain | Fuji | Already ours — C-Chain or any L1 |
| KMS | 1 node, whole key | 🔑 Validator job — key split across validators (threshold MPC) |
| Coprocessor | 1 instance | 🧮 Validator job — many operators compute + co-sign |
| Relayer / storage / DB | single | 🟡 Stay services — can be many; not consensus-critical |
So exactly one thing becomes a new chain — the gateway. The KMS and coprocessor don't become chains; they become the two big jobs our validators perform. Everything else is plumbing.
How we'd ship it
Launch an Avalanche L1 as the gateway
Spin up a new L1 (Subnet) with a validator set and deploy the gateway contracts (GatewayConfig, Decryption, KMS/input verification) on it. Repoint the coprocessor's gw-listener, the kms-connector, and the relayer at this L1 instead of our dev node.
Run the KMS in threshold mode across validators
Rebuild the KMS in its threshold (MPC) configuration, one node per validator/committee member, and run the distributed key-generation ceremony so the secret key is born already split — no one ever holds it whole. Register the signer set + quorum in the gateway and in each host chain's KMS verifier.
Run multiple coprocessors with co-signing
Have validators with the hardware run the FHE coprocessor; register their signers with a threshold so a result is only accepted when enough agree. Compute stays off-chain (too heavy for a VM) — what's decentralized is who runs it and the requirement that they agree.
Keep the edge as managed services
Relayers, object storage (S3), and databases (RDS) run as ordinary services — one or many. They hold no secrets, so they don't need consensus.
7. Should Fuji itself be the gateway?
There's a tempting shortcut: skip the dedicated gateway L1 and deploy the gateway contracts directly on Fuji C-Chain. Fuji is already secured by our full validator set, so there's no new chain to bootstrap. It's a real option — but it changes the trade-offs, and it hits two audiences differently.
For Fuji-native confidential apps (host = gateway)
- No cross-chain bridging. The ACL and decryption coordination sit on the same chain as the app, so there's none of the cross-chain "wait for the permission to read across" step — the trickiest part of a multichain setup. Reveals and withdrawals settle faster and more reliably.
- Top-tier security, free. Coordination is protected by C-Chain's validators, no separate chain to secure.
- But the load lands on C-Chain. Every decryption request and verification becomes a C-Chain transaction — paying AVAX gas and competing with all other C-Chain activity. Fine at small scale; meaningful at ecosystem scale.
For onboarding other Avalanche L1s (Fuji as the shared gateway)
- Fast onboarding. A new L1 deploys its host contracts on itself and just registers with the Fuji gateway — it doesn't run its own gateway or KMS, it reuses ours.
- Cross-chain comes back. Each L1 is now a host chain talking to a remote gateway (Fuji), so every one pays the cross-chain ACL-read cost we just avoided for Fuji-native apps.
- C-Chain becomes the shared bottleneck. Every confidential L1's coordination traffic funnels through Fuji, concentrating the ecosystem's privacy load and gas on C-Chain — and coupling every L1 to its congestion.
8. Where this goes
The proof is done: the full stack runs on our infrastructure and serves real confidential transactions on Fuji. The path from here is incremental and each step stands on its own:
- Threshold KMS across a validator committee — the headline decentralization and trust win.
- Multiple coprocessors with co-signing, scaled horizontally as confidential TPS grows (and a GPU build when we want the ~4× throughput it brings).
- A gateway L1 so C-Chain and every Avalanche L1 can offer confidentiality through one coordinated hub.
- Production hardening — managed Postgres/S3, multiple relayers behind TLS, hot-key handling via a KMS signer — so it's an ecosystem service, not a single box.
The strategic takeaway is the simple one: confidential compute can be a native Avalanche capability that we own outright — no external operator in the critical path, extensible to the whole ecosystem.
Built on Zama's open-source FHEVM, self-hosted by Avalanche. This is a testnet proof of concept on Fuji using mock USDC.