Docs
What Blindfloor does, how it can fail, and how to run every part of it without us.
How it works
- You pick a stock token, an amount and a trigger price. Your browser makes a random 32-byte salt and hashes the order.
- You approve exactly the amount to the stops contract, then call
arm. Only the hash goes on chain. The stock stays in your wallet. - The reveal (trigger and salt) is either sealed to our keeper, or downloaded by you.
- When the Chainlink feed reads at or below your trigger, whoever holds the reveal calls
fire(id, trigger, salt). The contract checks the hash and the feed, pulls the stock, sells it for USDG on Uniswap V3 and sends the USDG to you. The trigger first becomes readable on chain at that moment.
There is no deposit, no pot and no leverage. The contract has no pause, no upgrade and no withdraw, and never holds funds beyond a single transaction. Try the hash without a wallet: curl -s https://blindfloor.xyz/api/seal -d '{"stock":"NVDA","trigger":200}'.
Design
Copying a hash does nothing
The hash is keccak256(abi.encode(chainid, contract, owner, stock, amount, trigger, salt)). It includes the owner, so a leaked reveal cannot open someone else's order that happens to carry the same hash. It includes the chain id and contract address, so a reveal cannot be reused on a testnet or a redeploy. The 32-byte salt means the trigger cannot be brute-forced from the hash: without it, a few million plausible prices could be tried.
Anyone can fire, so firing is harmless
fire is open to anyone who holds the reveal. The proceeds always go to the order owner, and the minimum output is set from the oracle price times your tolerance. Someone who steals a reveal can at most bring the sale forward to the moment the feed is at or below your trigger, which is what you asked for. Robinhood Chain has a single sequencer and no public mempool, so there is no same-block race on a leaked reveal.
A trigger above the current price
That is allowed: it sells at the next check. The contract cannot know, because it only sees a hash. The app warns you when you type one.
The floor comes from the feed, not the pool
The minimum output is amount times the feed price times (1 minus your tolerance). The keeper has no price parameter to get wrong. If the pool is further below the feed than your tolerance, the sale reverts and the stop stays armed; you can disarm and arm again with a wider tolerance.
Disarm and fire in the same block
The sequencer orders them; the second one reverts. If the stop fires first, the sale stands.
Keeper
In the default mode our keeper decrypts your trigger in memory to watch it; the chain, bots and other traders never see it. Use Keep-it-myself if you don't want us to either.
So the honest version is: our keeper sees your trigger; the chain doesn't. Your browser seals the reveal to the keeper's public key (an X25519 sealed box) before it leaves the page. The server checks the reveal against the on-chain hash, stores only the ciphertext, and keeps the decrypted value in memory while the stop is armed. It is dropped when the stop fires or is disarmed. Logs record the stop id and whether the check passed, never the trigger. No endpoint returns a reveal.
In Keep-it-myself mode, nothing is sent to the server. You get blindfloor-reveal-<id>.json and fire it with the CLI, with a manual call on Blockscout, or by handing the file to the keeper later from the app.
If our server is down, the contract keeps working: Keep-it-myself stops and Blockscout can still fire. If the keeper wallet runs low on gas the status shows it, and you can fire yourself.
The keeper checks every 15 seconds. For each stop it holds a reveal for, it fires only when the feed is fresh and at or below the trigger, and it first checks your balance and allowance and simulates the call. Statuses you may see: sealed, self, watching, waiting_balance, waiting_allowance, feed_paused, swap_failed, usdg_paused, frozen.
Feeds
Prices come from Chainlink feeds on Robinhood Chain (8 decimals). Feeds update when the price moves 0.5% or at least once a day; outside those windows a stop waits. The contract rejects a feed reading older than 26 hours, or with a non-positive answer; the stop stays armed. An old updatedAt does not mean a wrong price: while a feed is alive, its last value is within 0.5% of the market.
Weekend behaviour has not been verified as of 28 Oct 2026. The keeper records every feed update time; the Saturday and Sunday readings go here once checked. Until then, assume a stop can wait through a weekend.
Prices on this site are labelled live, unofficial.
Gaps
If the price gaps below your trigger, the stop sells at the feed price at that moment, within your tolerance. If the pool pays less than the feed price minus your tolerance, the transaction reverts and the stop stays armed, so a stop can miss. You then choose: wait, or disarm and arm again with a wider tolerance.
CLI watcher
For Keep-it-myself stops, a small watcher polls the feed every 15 seconds and sends fire when it is due, using your own key:
mkdir blindfloor-watch && cd blindfloor-watch
curl -O https://blindfloor.xyz/cli/blindfloor-watch.mjs -O https://blindfloor.xyz/cli/package.json
npm install
node blindfloor-watch.mjs reveal.json --rpc https://robinhood-rpc.publicnode.com --pk $PRIVATE_KEY
Two files, one dependency (viem). Read the script before you run it: it only reads your reveal file and talks to the RPC you pass.
The key only pays gas; the USDG goes to the stop's owner. Without the CLI, call fire yourself. The values are in your reveal file; trigger is in 8 decimals, so $200.00 is 20000000000:
cast send $STOPS "fire(uint256,uint256,bytes32)" $ID $TRIGGER $SALT \
--rpc-url https://robinhood-rpc.publicnode.com --private-key $PRIVATE_KEY
Or open the contract on Blockscout, tab Write contract, fire. A call made while the feed is above your trigger simply reverts (AboveTrigger) and costs only gas.
API
JSON over HTTPS, CORS open, 10 requests per second per IP. A 429 means wait 2 seconds. Armed orders never carry a trigger in any response.
GET /api/stats
curl -s https://blindfloor.xyz/api/stats
POST /api/seal
Simulation of what the chain would see. A real hash also binds your address and the contract.
curl -s https://blindfloor.xyz/api/seal -d '{"stock":"NVDA","trigger":200}'
curl -s https://blindfloor.xyz/api/seal -d '{"stock":"NVDA","trigger":200,"amount":0.5}'
GET /api/ledger
curl -s "https://blindfloor.xyz/api/ledger?limit=10"
curl -s "https://blindfloor.xyz/api/ledger?stock=NVDA&status=fired&limit=10"
GET /api/stops
curl -s "https://blindfloor.xyz/api/stops?owner=0xYourAddress"
POST /api/reveal
box is the base64 sealed box the app builds in your browser from the reveal JSON and the keeper's public key. The example shows the shape only.
curl -s https://blindfloor.xyz/api/reveal -d '{"id":1,"box":"BASE64_SEALED_BOX"}'
Replies: {"ok":true,"status":"sealed"} or {"ok":false,"error":"bad_reveal"} (also not_armed, already_sealed, bad_request, no_contract).
GET /api/keeper
curl -s https://blindfloor.xyz/api/keeper
GET /api/price
curl -s "https://blindfloor.xyz/api/price?stock=NVDA"
GET /api/proof
curl -s https://blindfloor.xyz/api/proof
GET /api/healthz
curl -s https://blindfloor.xyz/api/healthz
GET /token.json
curl -s https://blindfloor.xyz/token.jsonContract
- BlindfloorStops
- —
- Chain
- Robinhood Chain, id 4663, RPC
https://robinhood-rpc.publicnode.com - Source
- Verified on Sourcify (exact match)
- ABI
- stops-abi.json
- Constants
- feed max age 26 hours · tolerance 50 to 500 bps · minimum amount 0.001 · 2 stops per wallet, 10 if you hold at least 1,000,000 of the token
- Swap
- Uniswap V3 SwapRouter02
0xCaf681a66D020601342297493863E78C959E5cb2, output USDG0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
Allowlist (fixed at deploy)
| Stock | Token | Chainlink feed |
|---|
Interface: arm(stock, amount, slipBps, commit), disarm(id), fire(id, trigger, salt), commitOf(owner, stock, amount, trigger, salt), maxStopsOf(user), minOutOf(id, answer).
Token
Holding 1,000,000 or more raises your stops from 2 to 10. Part of the creator fees is used to buy and burn the token by hand. Not automatic, no amount promised. Every burn is listed below.
The token does not change how a stop fires, and the stops contract holds no funds. Until the token exists, every wallet has a limit of 2.
| Burn tx | Tokens burned | When (UTC) | Note |
|---|---|---|---|
| Loading… | |||
Risks
- A stop can miss: the feed can pause, the pool can be thin, the price can gap past your tolerance.
- In the default mode our keeper can see your trigger. Keep-it-myself removes that.
- Anyone holding your reveal can fire your stop once the feed is at or below the trigger. Keep the file to yourself.
- USDG is issued by a third party and can be paused or frozen; a stop then cannot pay out until it resumes.
- A stop needs your balance and allowance to be in place when it fires. Move the stock or revoke the allowance and it waits.
- The contract is new and has not had an outside security review. Use amounts you can lose.
- Stock tokens are issued by Robinhood and are not shares; not available to US persons.
Cancel without this site
Disarm (no reveal needed, only the wallet that armed it): open the stops contract on Blockscout, Write contract, disarm(id). If the stop fires in the same block, the sale stands.
Revoke: open the stock token on Blockscout, Write contract, approve(spender = stops contract, value = 0). With no allowance the stop cannot fire, even though it still shows as armed.
cast send $STOPS "disarm(uint256)" $ID --rpc-url https://robinhood-rpc.publicnode.com --private-key $PRIVATE_KEY
cast send $STOCK "approve(address,uint256)" $STOPS 0 --rpc-url https://robinhood-rpc.publicnode.com --private-key $PRIVATE_KEYFAQ
Do I deposit my stock?
No. You approve exactly the amount your stops can sell. It leaves your wallet only in the transaction that sells it.
Who sees my trigger?
On chain, nobody until it fires. In the default mode our keeper decrypts it in memory. In Keep-it-myself mode, only you.
What do I get when it fires?
USDG, at the feed price at that moment minus up to your tolerance, sent straight to your wallet.
What if I lose the reveal file?
The stop cannot fire without it. Disarm it and arm a new one.
Which stocks?
The nine Robinhood stock tokens listed under Contract. The list is fixed in the contract.
What does it cost?
Gas only, about a cent on Robinhood Chain. Blindfloor takes no fee from your sale.