Skip to content
Docs / Whitepaper

Overview

Ordeo whitepaper: encrypted notes, a yield vault and a fee flow

The full design in one document: how notes and proofs work, how the vault turns trading fees into holder yield, how the bundler keeps addresses off a spend, and what the system does not protect against.


On this page

Abstract and motivation

Abstract

Ordeo pairs a public ERC-20, $ORDEO, with a shielded pool on Robinhood Chain. Inside the pool, balances are encrypted notes: Poseidon commitments in a Merkle tree of depth 24, spent with UltraHonk zero-knowledge proofs made in the browser. ORDEO notes are counted in vault shares, so donations to the vault raise the value of every note at once. The donations are funded by the Pons creator fee on ORDEO trades, which is routed to a fixed 50/50 splitter whose holders’ half buys ORDEO for the vault. A bundler can submit spends so the user’s address does not appear on them. The contracts have no owner, no admin, no proxy and no pause; every parameter is set once, at construction.

Motivation

On a public chain, holding a token means publishing your balance. Anyone who learns one of your addresses can see what you hold, when you bought, who you paid and who paid you. For a payment that is like handing over your bank statement with every receipt.

The usual answers each give something up. A separate private chain splits liquidity. A mixer with fixed denominations makes everyday payments awkward. A private balance that earns nothing gives holders no reason to keep funds inside it, and an empty pool protects nobody.

Ordeo tries a narrower goal. Keep the token public and tradable where it already trades. Give holders a private place to keep it that supports any amount, private payments between people, payment links and private sales. Make that place the only one where holder yield accrues, so that the privacy set grows for a reason people care about.

System design

The system has four on-chain parts and two off-chain helpers.

PartRole
OrdeoShroudHolds ETH and ORDEO behind notes; verifies proofs; runs the vault and its fees.
OrdeoAttestorVerifies UltraHonk proofs produced by the transact circuit.
OrdeoKeyLedgerMaps an ordinary address to a public note key and viewing key.
OrdeoYieldSplitReceives the Pons creator share and splits it 50/50, team and holders.
OrdeoTradeDeskTurns ETH into an ORDEO note in one step, and sells notes for ETH on the live Pons venue.
Bundler (off-chain)Submits spends for users and is paid a flat ORDEO fee from their notes.
Harvester (off-chain)Moves fees through the splitter, buys ORDEO for the vault, funds the bundler.

Poseidon hash libraries and the verifier’s own libraries complete the set. Contracts are written for Foundry with solc 0.8.28. None of them has an owner, an admin role, a proxy, an upgrade path or a pause switch. Fee rates, the token, the verifier and the fee sink are constructor arguments and cannot change after deployment.

Deployment order follows the dependencies. The libraries, the verifier, the registry and the splitter come first, because the splitter must exist when the token launches: Pons records it as the token’s creator fee recipient at launch. Then ORDEO launches. The pool and router come after, since they need the token address. A script, npm run verify-deployment, checks each deployed address against this build: bytecode, immutables, constructor arguments and linked libraries.

Nothing is deployed yet. Until it is, the app runs in practice mode.

Notes and proofs

Commitments and nullifiers

A note commits to an asset, a value and an owner:

C = Poseidon(asset, value, Poseidon(pk, blinding))
nullifier = Poseidon(C, leafIndex, sk)
pk = Poseidon(sk)

Asset 0 is ETH, with the value in wei. Asset 1 is ORDEO, with the value in vault shares. The blinding is random, so notes that share an owner and a value still look unrelated. The nullifier depends on the note’s position in the tree, which makes it unique per note, and on the spending key, so only the owner can produce it.

Encryption

Each new note’s contents are encrypted to the recipient’s viewing key with x25519 and a fresh ephemeral key, HKDF-SHA256 and ChaCha20-Poly1305. The plaintext is 65 bytes (asset, value, blinding) and the ciphertext is 114 bytes. A wallet finds its notes by trying to decrypt each new ciphertext. Shield notes carry a value of zero in the ciphertext, because the pool computes the value itself from what it received; the wallet reads the value from the Shielded event in the same transaction.

The circuit

Every spend goes through a single circuit, transact. It is written in Noir, and Barretenberg proves it with UltraHonk. It takes up to two input notes and makes exactly two output notes. It has ten public inputs: root, asset, two nullifiers, two commitments, exit value, transfer fee, fee rate and the external data hash. It proves that:

  • each non-zero input is in the tree under the given root, with its index range-checked to 24 bits;
  • the prover knows the spending key behind both inputs;
  • the nullifiers are computed correctly;
  • value is conserved: the inputs equal the outputs plus the exit value plus the transfer fee, and every value fits in 120 bits so nothing can wrap around;
  • the transfer fee is at least the pool’s rate applied to the value sent to other keys;
  • the external data hash binds the recipient, the bundler, the bundler’s fee, both ciphertexts, the chain id and the pool address.

Because the external data is bound into the proof, nobody who sees a proof in flight can redirect its payout or raise its fee. The pool also rejects a spend that pays a bundler unless that bundler is the one sending it, so a proof cannot be copied and raced.

Setup and proving

UltraHonk needs no project-specific trusted setup. It uses the public Aztec Ignition SRS, and the site serves the first 217 points itself. Proofs are built in a browser web worker; on a laptop, each takes a few seconds. A build check, circuits/build.sh --check, confirms that the committed verifier and circuit artifact match a fresh build.

The tree

The tree has depth 24, room for 16,777,216 notes. Only the last 100 roots are kept by the pool; a proof built on an older root has to be redone. The tree is append-only: notes are never removed, only nullified.

Vault, yield and fee flow

Shares instead of tokens

When ORDEO is shielded, the pool mints vault shares for it and the note records the shares. The vault’s backing is the ORDEO the pool holds for notes. One share is worth backing divided by total shares, with a fixed virtual offset of 1,000,000 shares that guards against the first depositor inflating the price.

Anyone can call donate to add ORDEO to the backing without minting shares. Every note then holds the same number of shares, each worth more. Rounding always favours the vault: shares are minted down, share value is computed down and fees are rounded up, so the share price can never fall. Tests check this invariant.

Pool fees

The pool charges a shield fee, a transfer fee on value sent to another key and a flat unshield fee. The values proposed for deployment are 0.10% to shield, 0.10% on a private transfer and a flat 0.0001 ETH or 10 ORDEO to unshield. The owner confirms them before deployment and they are fixed after. The code refuses any rate above 5%.

Fees paid in ORDEO stay in the vault and become yield for every note. Fees paid in ETH accrue in the pool, and anyone can sweep them to the splitter.

The Pons creator fee

Before graduation, every bonding-curve trade on Pons pays a fee; after it, the same holds for trades in the Uniswap v4 pool that carries the Pons hook. A slice of that fee is the creator share, and it belongs to whichever address is registered as the token’s creator fee recipient. At launch that recipient is OrdeoYieldSplit. The exact rates are fixed by Pons at launch and shown on the site once the token exists.

Only trades that pay this fee feed the system. That includes trades on the curve, on the Pons pool and through any aggregator or router whose route passes through one of them. A trade in a separate pool that someone creates without the Pons hook pays nothing to Ordeo.

Splitting and harvesting

The splitter has one rule: everything it receives is split 50% to the team and 50% to the holders’ address. It can claim fees from the Pons escrow, ask Pons to move pending fees into that escrow, split and pay. A side that refuses ETH keeps its share waiting. The splitter has no function that hands the creator fee role on to anyone else.

The holders’ address is the harvester’s wallet. About every four hours, at a randomised time, the harvester sweeps the pool’s ETH fees to the splitter, runs the split, tops the bundler up if its gas is low, and then buys ORDEO through buyAndDonate in small batches with random gaps. Each buy has a minimum output based on a reference median price less 1.5%, and the round is skipped when ORDEO is more than 3% dearer than that reference.

The harvester is an operated job and not an open on-chain function because a buy that anyone can trigger at a moment of their choosing is easy to sandwich. The cost is a trust point, covered below.

The bundler

If a user sends their own spend, their wallet pays gas and shows up as the sender, which defeats the point of a private payment. The bundler solves that. It is an off-chain HTTP service that receives a finished proof and submits it from its own address. In return the user pays it a flat ORDEO fee, deducted from the spent notes.

The proof names the bundler and its fee, so the bundler has no way to alter the recipient, the amount or the fee. It can only submit or refuse. Anyone can run one; the app tries the bundlers it knows in order.

Pricing

The fee is the larger of a 25 ORDEO floor and the estimated gas cost with a 70% margin, valued in ORDEO at a reference price. The reference is a rolling median of quotes sampled every five minutes over six hours, ignoring the newest minute, with at least 12 samples. It is never the price of the moment, which makes the fee hard to push around with a single trade. Until it has enough samples, the bundler refuses work paid in ORDEO.

Refusals and limits

The bundler dry-runs every transaction before sending and refuses anything that would fail, anything paying less than its gas, notes already spent or in flight, unknown roots and sales that expire within a minute. It rate-limits by address range, backs off when its transactions revert and pauses itself if reverted gas passes a threshold. It never logs request bodies, proofs or keys.

Where its fees go

By default the bundler gives the ORDEO it earns to the vault as donations in batches, so private payment fees also end up with shielded holders. Its ETH for gas comes from the harvester, out of the holders’ half.

Funds never depend on a bundler. Every spend can be sent from the user’s own wallet.

Key derivation

Users should not have to manage a second seed phrase. Ordeo derives note keys from a single wallet signature on a fixed message in EIP-4361 (Sign-In with Ethereum) format. The message is bound to ordeo.tech, to Robinhood Chain (4663), to a fixed nonce and issue time, and lists the pool address as a resource. Because nothing in it changes, a given wallet returns an identical signature every time, on every device.

seed     = r || s of the signature
           (+ optional PBKDF2-SHA256, 200,000 rounds, of a passphrase)
keys     = HKDF-SHA256(seed, salt "ordeo-note-keys-v1")
sk       = spending key, reduced into the BN254 field
view     = x25519 viewing key
pk       = Poseidon(sk)

Not every wallet signs deterministically; some hardware and smart wallets do not. On first use the app requests two signatures and checks that they match. A wallet that returns different ones cannot be used, since its keys would change from one session to the next.

The keys exist only in the page’s memory. Reloading forgets them and signing again restores them. They are never stored and never sent.

A private address is ordeo: followed by the public note key and the public viewing key, 64 hex characters each. Registering in OrdeoKeyLedger publishes the same pair against the wallet’s address, which lets other people pay that address privately.

Threat summary, limitations and open work

Visible versus hidden

ActionVisibleHidden
Shield from a walletThat wallet and the amountWho owns the new note
Buy in the appYour wallet, ETH paid, ORDEO bought, that it went into a new noteLater use of that note
Register keysYour address and its public note keyYour notes and balance
Private send via bundlerThe bundler as sender, its flat ORDEO feeSender, recipient, amount, which notes
UnshieldDestination address and amountThe notes it came from
SaleETH recipient and amountWhich notes were sold

Your address stays off a private payment, unshield or sale only when the bundler submits it. Sent from your own wallet, the same spend shows your address as the sender.

Threats

  • Small privacy set. The set counts every note in the tree, and at launch it is small. Timing and amount patterns can link a deposit to a withdrawal. Vary both, leave through fresh addresses and keep ORDEO shielded.
  • Phishing for the signature. A site that gets the same signature can derive the same keys. Because the SIWE message names the domain, a wallet shows a warning if a different site requests it. The optional passphrase means a leaked signature alone is not enough, but a forgotten passphrase means lost notes.
  • Creator fee redirect. The Pons owner can change a token’s creator fee recipient, but only after a public three-day timelock, visible on chain while pending. A redirect would stop new yield. It cannot touch notes or the vault.
  • Harvester key. Whoever holds the harvester key controls the holders’ half until it is spent. It receives fees only as they arrive, so its balance stays small.
  • Bundler refusal. A bundler can refuse service but cannot take or redirect funds. Users can always submit themselves.
  • Price moves on a sale. A sale carries a minimum ETH output bound by the proof. If the price falls below it, the whole transaction reverts and the notes stay unspent.

Limitations

  • Registering keys publicly links an address to a note key. It is optional.
  • Buying and shielding from a wallet reveal that wallet and the amount going in.
  • Wallets that do not sign deterministically cannot be used.
  • Proofs built on a root older than the last 100 insertions have to be redone.
  • Buy and sell through the router revert while a launch is graduating from the curve to the pool.
  • Yield depends on trading volume that pays the Pons creator fee, and can be zero.

Open work

  • Deploy the contracts in order and publish each address once checked by the verification script.
  • Launch ORDEO on Pons with the splitter as creator fee recipient, then fill in the token’s live fee rates.
  • Bring up more independent bundlers so no single operator is needed for private sends.
  • Grow the privacy set; every note added makes every other note harder to trace.