Contents
zkFLO docs
How to use the pool, what happens every hour, what the proof does and doesn't tell you, and what can go wrong.
What zkFLO is
zkFLO is an ETH/USDG pool on Uniswap V4, on Robinhood Chain. You deposit ETH and USDG into a vault, and the vault is the only liquidity provider in the pool, so the swap fees traders pay go to the vault's depositors.
Once an hour, a private model sets two things for the pool: the swap fee, and the price range where the vault's liquidity sits. Every new setting comes with a zero-knowledge proof. The vault checks that proof onchain before it changes anything.
The proof shows that the setting came from the model we committed to, that the model read the pool's own recorded prices and nothing else, and that the result is inside fixed limits. It doesn't show that the model made a good call. The weights stay private, and the proof is what lets you check the model's work without seeing them.
Where things stand
zkFLO isn't live. No contracts are deployed and the vault isn't taking deposits. The app runs on a preview: a made-up pool, made-up prices and a wallet with play money. Nothing you do there touches a real chain.
The guides below describe how things will work once the contracts are live, and point out where the preview behaves differently. The numbers on this page are the values we're starting with. Some of them, such as the length of an epoch, depend on how long a proof takes to make, and we'll confirm them after the proving benchmark. See the roadmap.
Terms
These words mean something specific on this page and in the app.
| Term | Meaning |
|---|---|
| Epoch | One hour. Each epoch runs on one setting. |
| Setting | The fee and the liquidity range for one epoch. |
| Model | The private machine-learning model that picks each setting. |
| Model ID | A hash of the model's weights. It's public, so anyone can tell if the model changes. |
| Sample | The 360 prices the pool records during an epoch, one per 10 seconds. |
| Sealing | Hashing an epoch's sample into one value, sampleRoot, and storing it onchain. After that nobody can change the prices the model reads. |
| Prover | The operator who runs the model, makes the proofs and submits them. The Prover puts up a bond. |
| Bond | USDG the Prover locks up. Part of it is paid to depositors each time a proof is missed. |
| Settlement window | The 5 minutes after an epoch ends, during which its proof has to arrive. |
| Receipt | An onchain record the vault writes each time it applies a verified setting. |
| Fallback mode | What the pool does without a proof: a flat 0.30% fee, with liquidity spread across the full price range. |
| Share | An ERC-20 token you get for depositing. It stands for your part of everything the vault holds. |
Guides
Deposit
You deposit ETH and USDG together, in the same proportion the vault already holds them. You type the ETH amount, and the app works out how much USDG goes with it.
- Open the Vault page in the app and connect your wallet. Make sure it's on Robinhood Chain.
- Enter how much ETH you want to deposit. The app shows the USDG that goes with it and the shares you'll get.
- Approve USDG when your wallet asks. This lets the vault take the USDG for this deposit.
- Confirm the deposit in your wallet. Your shares show up on the Vault page once the transaction is in.
Deposits are capped while the list of Receipts is short. If your deposit goes over the room left under the cap, the app tells you how much room there is. We raise the cap in stages as the Receipt history grows.
In the preview, choose Use a preview wallet instead of connecting your own. It comes with play ETH and USDG.
Withdraw
You can withdraw at any time. Nobody can pause withdrawals, including us, the Guardian and governance.
- On the Vault page, enter how many shares you want to redeem.
- Check the ETH and USDG you'll get back. You receive both, in the proportion the vault holds when you withdraw.
- Confirm in your wallet.
What you get back is your share of the vault's liquidity position plus your share of anything it's holding to the side. That proportion changes as the price moves, so you may get back more ETH and less USDG than you put in, or the other way round.
The withdrawal transaction includes a minimum ETH and a minimum USDG. If the pool's price moves before your transaction lands and you'd get less than that, the transaction fails and nothing leaves the vault.
Check a Receipt yourself
You don't have to take the app's word that a setting was proven. For any Receipt, the app can run four checks, and your browser sends each question straight to the chain without going through our servers.
- Open the Receipts page and pick an epoch.
- Choose Run the four checks.
- Read the result of each check:
- The Receipt is in the vault's log. The record exists onchain and was written by the vault.
- The prices are the ones the hook sealed. The
sampleRootin the Receipt matches the one sealed for that epoch. - The model is a registered one. The Model ID in the Receipt is one the model registry knows.
- The verifier accepts the proof. Your browser hands the proof and its public inputs to the verifier contract and gets back a yes or a no.
If all four pass, that setting came from the registered model, ran on the sealed prices, and passed the same verification the vault did.
In the preview, the answers are simulated, because there's no chain to ask.
Trade against the pool
You swap with zkFLO the same way as with any Uniswap V4 pool. The fee you pay is the one set for the current epoch, which is one of 0.05%, 0.10%, 0.30%, 0.60% or 1.00%, or 0.30% while the pool is in Fallback mode. The Pool page shows the fee in force right now.
The fee changes when a new setting is applied, which happens within 5 minutes of each epoch ending. We're still checking which swap aggregators on Robinhood Chain route through pools with hooks, so until we've confirmed that, a router might not find this pool.
Concepts
One epoch, start to finish
Every epoch goes through the same steps. Times are measured from the start of the epoch.
| When | What happens | Who does it |
|---|---|---|
| 0 to 60 min | Traders swap at the current fee. The pool records one price for each 10-second slot: the price just before the first swap in that slot. A slot with no swaps takes the last recorded price. That makes 360 prices. | The pool's hook, on every swap |
| 60 min | The epoch ends. The 360 prices are hashed into sampleRoot and stored onchain. | Anyone can send this transaction |
| 60 to 65 min | The Prover runs the model on the sealed prices and submits the new setting with a proof. The previous setting stays in force meanwhile. | Prover |
| On submission | The vault checks that the proof's sampleRoot is the one sealed for that epoch, that the Model ID is the active one, that the proof verifies and that the setting is inside the limits. If all of that holds, it changes the fee, moves its liquidity into the new range without swapping, and writes a Receipt. | The vault, onchain |
| 65 min, no valid proof | The pool goes into Fallback mode and part of the Prover's bond goes to depositors. | Automatic for the fee; anyone can send the transaction that moves the liquidity |
The model's range is centred on the epoch's time-weighted average price and is set by two outputs: a fee level and a width. The new setting applies to the next epoch.
You can watch this happen in the app's preview, which can run an hour in 24 seconds and hold back a proof so you can see Fallback mode.
What the proof covers
Each proof makes one statement about values that are public and values that stay private. In short:
private the 360 prices, the model's weights
Hash(prices) = sampleRoot
Hash(weights) = modelId
Model(weights, prices) = fee, tickLower, tickUpper
fee and range are inside the limits
What you can rely on
- The setting came from the model whose weights hash to the public Model ID. Swapping the model would change the Model ID, and every Receipt shows it.
- The model read the 360 prices the pool recorded and sealed for that epoch, and no other data.
- The fee is one of the five allowed levels, and the range is inside the width and movement limits. The vault checks those limits again before moving anything.
What it can't tell you
- Whether the model is any good. A proven setting can still lose money, for example when the price moves out of the range.
- Whether the Prover acted on the setting first. The Prover knows each new setting before the chain does. A short settlement window, a cap on how far the range can move and no swaps during a rebalance all narrow that advantage. They don't remove it.
- What the model is. The weights stay private by design.
Fallback mode
Fallback mode is what the pool does when nobody has proven a setting. It's a fixed 0.30% fee with the vault's liquidity spread across the full price range. Liquidity spread over every price earns less in fees than a tight range would, but the price can never move out of it.
When it starts
- The 5-minute settlement window ends without a valid proof. The fee switches to 0.30% straight away. Moving the liquidity to the full range takes one transaction, which anyone can send, and the sender gets a small tip from the Prover's bond.
- The Guardian puts the pool into Fallback mode, for example if something looks wrong.
What it costs the Prover
Each missed epoch takes a fixed amount of USDG from the Prover's bond and adds it to the vault, which raises the value of every share. The amount isn't set yet. It has to be large enough that missing on purpose never pays. After three missed epochs in a row the model is suspended, and only governance can bring it back.
When it ends
With the next valid proof. The vault applies that setting as normal and writes a Receipt.
Where the fees go
| Who | Share of swap fees | Notes |
|---|---|---|
| Depositors | 90% | Added to the vault, so the value of each share goes up |
| Model author | 10% | Out of this, the author pays for making the proofs and for the gas to verify them |
| Protocol | 0% | Governance can raise it to at most 5%, through a timelock |
There's no zkFLO token.
Reference
Limits on the model
The first three limits are part of the proof, so a setting outside them can't be proven, and the vault checks them again before applying a setting. The rest are rules in the vault contract. These are the v1 starting values.
| Limit | Value |
|---|---|
| Fee | One of 0.05%, 0.10%, 0.30%, 0.60%, 1.00% 500, 1000, 3000, 6000, 10000 in the contracts, in hundredths of a basis point |
| Range width | From ±2% to ±40% around the epoch's time-weighted average price |
| Range movement | The centre moves by at most one range width from the previous epoch's centre |
| Swaps when rebalancing | None. Tokens that don't fit the new range stay in the vault, unused, until a later range takes them |
| Withdrawals | Always open |
| Deposits | Capped, with the cap raised in stages |
Timing
| Value | v1 setting |
|---|---|
| Epoch length | 1 hour |
| Price slot | 10 seconds, so 360 prices per epoch |
| Settlement window | 5 minutes after the epoch ends |
| Fallback fee | 0.30%, full range |
| Suspension | After 3 missed epochs in a row |
Who can do what
| Role | Can | Can't |
|---|---|---|
| Prover | Submit settings with proofs | Move funds, change the model, or skip an epoch without losing bond |
| Guardian | Put the pool into Fallback mode | Move funds, change the model, or stop withdrawals |
| Governance | Register a new model, bring back a suspended one, set the protocol fee up to 5%. All through a timelock | Touch deposits |
| Anyone | Seal an epoch's prices, and send the transaction that moves liquidity into Fallback mode | Add liquidity to the pool directly. Only the vault can |
Receipt fields
The vault emits a Receipt event each time it applies a setting.
| Field | Type | Meaning |
|---|---|---|
epoch | uint64 | The epoch whose prices the setting was computed from. Indexed |
modelId | bytes32 | Hash of the model's weights |
sampleRoot | bytes32 | Hash of the epoch's 360 sealed prices |
fee | uint24 | The new fee in hundredths of a basis point. 3000 is 0.30% |
tickLower | int24 | Lower edge of the new range, as a Uniswap tick |
tickUpper | int24 | Upper edge of the new range, as a Uniswap tick |
proofHash | bytes32 | Hash of the proof that was verified |
nav | uint256 | The vault's total value when the setting was applied |
Contracts and network
| Contract | What it does | Address |
|---|---|---|
| FloVault | Takes deposits, pays withdrawals, applies settings, writes Receipts | Not deployed |
| FloHook | The Uniswap V4 hook. Sets the fee on each swap, records prices, lets only the vault add or remove liquidity | Not deployed |
| FloVerifier | Verifies the proofs. Generated by EZKL (Halo2 with KZG commitments) | Not deployed |
| ModelRegistry | Holds the active Model ID, its author and whether it's suspended | Not deployed |
| BondManager | Holds the Prover's bond and pays it out for missed epochs | Not deployed |
| Network | Chain ID |
|---|---|
| Robinhood Chain | 4663 |
| Robinhood Chain testnet | 46630 |
Check token addresses, not names. Robinhood Chain has pools with fake tokens named after real ones, including a fake USDC. The pool only accepts the canonical ETH and USDG, whose addresses are fixed in the hook. We'll list every address here when the contracts are deployed.
More
Risks
Read these before you deposit, once deposits open.
The contracts are new
They haven't been written or audited yet. Even after an audit, a bug could lose funds. That's why deposits start capped.
Providing liquidity can lose money
When the price moves a long way in one direction, the vault ends up holding more of the asset that fell. Fees may not make up for it. This happens with or without a model.
The model can be wrong
The proof shows the model ran as committed, not that its call was right. The price can leave the range, and while it's outside, the vault's liquidity earns no fees.
Some tokens can sit unused
The vault never swaps when it moves its range. Whatever doesn't fit the new range waits in the vault and earns nothing until a later range uses it.
The Prover sees each setting first
Whoever runs the Prover knows the next fee and range before anyone else and could trade on it. The limits narrow that edge. They don't remove it.
Someone could try to move the recorded prices
The model only reads the pool's own prices. Pushing them means trading against the pool in many 10-second slots, which costs money, and the range is centred on an average rather than the last price. While the pool is small, this is cheaper to try, which is another reason deposits start capped.
Fallback mode earns less
If proofs are missed, the pool runs at 0.30% across the full range until the next valid proof. The Prover's bond pays depositors for each missed epoch, but that may not cover the fees a proven range would have earned.
Questions
Is there a zkFLO token?
No. Depositors earn swap fees through their vault shares. That's the only reward.
Can you stop me from withdrawing?
No. No role in the system can pause withdrawals, including ours.
Can you change the model without anyone noticing?
No. A new model has a different Model ID, and it can only be registered through governance's timelock. Every Receipt records the Model ID it was proven against.
Does a proof mean the pool makes money?
No. It means the setting came from the committed model, on the sealed prices, inside the limits. Whether that setting earns more than it loses is down to the model and the market.
What happens if the Prover goes offline?
Five minutes after the epoch ends, the pool switches to Fallback mode and the Prover starts losing bond to depositors. After three missed epochs in a row the model is suspended. The pool keeps working in Fallback mode, and you can still withdraw.
Why can't I see the model?
Its weights are what makes it worth running. If they were public, anyone could copy it. The proof lets you check every setting it makes without seeing them.
Where's the track record?
The list of Receipts is the track record, and it starts when the testnet run does. We won't publish backtests.
Roadmap
This is the order we're working in. We're on step 1.
- Measure proving. How long a proof takes, how big it is, and what it costs to verify on Robinhood Chain testnet. This sets the epoch length.
- Write the contracts. Hook, vault, verifier, model registry and bond, with tests.
- Build the Prover node. It seals, proves and submits every epoch.
- Build the indexer and finish the app. A preview of the app is already up.
- Run on testnet until there are 30 days of Receipts with no gaps.
- Audit, then launch on mainnet with deposits capped.
zkFLO is still in development and isn't taking deposits. Nothing in these docs is an offer or financial advice.
Home · App · X · Terms · Privacy