The full story behind building Onyx with FHE
What is Onyx?
Onyx is a confidential payment streaming protocol where balances and flow rates remain encrypted onchain.
We built it during the Zama Builder Villa (March 30 to April 2, 2026), an invitation-only retreat focused on fully homomorphic encryption in production-grade EVM systems.
Our thesis was simple: payment streams are one of the most useful primitives in DeFi, but today every salary stream, vesting schedule, and grant disbursement is publicly visible by default.
Onyx keeps composability and verifiability while adding real confidentiality for institutions, teams, and individuals.
Why FHE? The privacy gap in streaming protocols
Most existing privacy approaches in DeFi fall into two families:
- ·ZK-based systems: strong for validity proofs and unlinkability, but shared mutable private state is harder to model for long-lived apps.
- ·Offchain confidential compute: useful, but trust often depends on enclave operators and attestation assumptions.
FHE gives us a different model: smart contracts can compute directly on encrypted values without exposing plaintext during execution.
For a streaming protocol, it means Onyx can keep deposits and withdrawals encrypted while still deriving stream state from public timing data.
Architecture overview

Onyx is built around three layers: confidential token transfer, stream accounting logic, and a frontend encryption flow.
Plaintext is never exposed onchain. Authorized users can decrypt relevant values locally in the browser.
Core smart contract: the stream
Each stream stores timing fields in cleartext, while financial state stays encrypted as euint values.
Instead of storing a visible flow rate, the contract computes streamed amounts over time from encrypted deposit plus public timestamps.
pragma solidity ^0.8.27;
import {FHE, euint64, euint128, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
import {IERC7984} from "@openzeppelin/confidential-contracts/interfaces/IERC7984.sol";
contract OnyxStream {
struct Stream {
address sender;
address recipient;
address token;
uint64 startTime;
uint64 endTime;
euint64 eDeposit;
euint64 eWithdrawn;
}
mapping(uint256 => Stream) private streams;
uint256 public nextStreamId;
}The frontend sends an encrypted deposit and an input proof. The stream contract stores encrypted accounting values, while the ERC-7984 token handles confidential transfer.
Computing the withdrawable amount
The key computation is encrypted all the way through: streamed amount minus already withdrawn amount.
function _computeStreamed(Stream storage s) internal returns (euint64) {
if (block.timestamp < s.startTime) return FHE.asEuint64(0);
if (block.timestamp >= s.endTime) return s.eDeposit;
euint128 deposit128 = FHE.asEuint128(s.eDeposit);
uint128 elapsed = uint128(block.timestamp - s.startTime);
uint128 duration = uint128(s.endTime - s.startTime);
return FHE.asEuint64(FHE.div(FHE.mul(deposit128, elapsed), duration));
}
function _computeWithdrawable(Stream storage s) internal returns (euint64) {
euint64 streamed = _computeStreamed(s);
return FHE.sub(streamed, s.eWithdrawn);
}Design note: time remains public while amounts remain encrypted. That constraint keeps computation cheaper and compatible with current FHEVM division rules.
Frontend encryption flow
Onyx uses Zama's React SDK wrapper to encrypt deposit amounts in the browser before any transaction is sent.
import { useEncrypt } from "@zama-fhe/react-sdk";
const encrypt = useEncrypt();
const encrypted = await encrypt.mutateAsync({
values: [{ value: depositAmount, type: "euint64" }],
contractAddress: STREAM_ADDRESS,
userAddress,
});
const eDeposit = toHex(encrypted.handles[0]);
const inputProof = toHex(encrypted.inputProof);The proof binds ciphertext input to the user wallet and target contract, which prevents replay in another contract context.
What can slow down this kind of project
1. Arithmetic constraints can dictate architecture
Not all FHE operations have the same cost profile. Ciphertext-vs-ciphertext operations are generally more expensive, and division has stricter constraints.
2. ACL complexity on encrypted handles
Permissions live on ciphertext handles, and new handles are produced after FHE operations. Permissions do not carry over automatically.
That creates a subtle failure mode: math can be correct but a resulting handle can still be unusable if ACL grants are missing.
3. End-to-end testing remains harder than standard Solidity
Local tooling has improved, but real E2E still requires validating the full encrypted pipeline: frontend encryption, relayer behavior, reencryption flow, signing, and testnet conditions.
Why this matters for DeFi

Confidentiality and composability are not mutually exclusive in the FHEVM model. Contracts can still compose around encrypted handles without decrypting values.
Technical overview

Know more about Onyx
If you want to go deeper, read the original build thread and check the visual recap below.

We are continuing to iterate on confidential payments. If you have ideas, use-cases, or integrations to discuss, reach out.

