The coin
Every Stook coin is a clone (EIP-1167) of one small contract, StookCoin. It is an ordinary ERC-20 with 18 decimals and a fixed supply of 1,000,000,000, all of which starts inside the coin itself. The coin is also its own market: a constant-product curve between the coins it holds and a reserve of ETH that starts with a virtual 1 ETH. So a new coin has a price from its first block (a market cap of 1 ETH), and there is no other pool to route around its fee.
Buying sends ETH in; the fee is taken first, the rest goes to the curve, and you receive coinReserve × net ÷ (ethReserve + net) coins, rounded down. Selling is the mirror image, and the fee is taken from the ETH that comes out. The page computes both with the contract’s own integer formulas and sends a minimum 1% below its quote.
The fee
Chosen at launch, from 0.25% to 10% in steps of 0.25%, and stored in the coin. No function changes it: there is no owner and no admin, in the coin or in the factory. Every buy and every sell pays it, in ETH. None of it goes to the creator or to Stook.
The basket, and how it keeps its weights
A coin’s basket is one to four tokenized stocks, each with a weight in basis points: at least 10% each, adding up to exactly 100%, no stock twice. Each stock is bought through one Uniswap USDG pool, whose fee tier is chosen at launch (the launch page picks the deepest). All of it is fixed when the coin launches.
Fees are swapped one stock at a time, which keeps every trade to a single swap’s gas. Each swap goes to the stock with the least ETH spent on it for its weight, the smallest ethSpent ÷ weight, with ties going to the stock listed first. The chosen stock’s ratio rises by at most one swap over its weight, and it was the lowest, so the gap between the most- and least-bought stock (in ETH per unit of weight) never exceeds one swap over the smallest weight. After any number of swaps, the ETH spent on each stock is within one swap of its share. What the holdings are worth then moves with the stocks, as in any basket. The coin exposes the choice as nextLeg(), and the coin page shows it.
If a stock’s swap is refused (its pool is off its average price, below), that stock rests for ten minutes: the next trades buy the rest of the basket with everything waiting, then the rested stock is asked again. If every stock is resting, the least-bought one is tried anyway, so a fee is never stuck behind a rule.
The fair-price guard
Each fee is swapped inside the same transaction: ETH → USDG in Uniswap’s WETH/USDG 0.01% pool, then USDG → the chosen stock in its pool. Before swapping, the coin reads both pools’ time-weighted average price (30 minutes; if a very busy pool has overwritten that much history, 10 minutes, then 2) and sets the swap’s minimum to what the averages say the ETH is worth, less both pools’ fees and 2%.
A price pushed inside the current block carries no weight in an average, so an attacker who moves a pool and then triggers a stook purchase gets nothing: the swap fails its minimum, the ETH stays in the coin as pendingEth, that stock rests, and the next trade (or anyone calling convert) buys the rest of the basket. The trade itself always goes through.
One refusal is deliberate: a transaction with too little gas left for the swap reverts with NeedsMoreGas instead of quietly deferring the fee. Wallets estimate the smallest gas at which a transaction does not revert; without that refusal, estimates would starve every purchase.
Burning for the stook
Anyone holding coins can burn them with redeem and receive exactly held × coins ÷ totalSupply of every stock in the basket, plus the same share of any fee ETH still waiting. The share is taken over the whole supply, including coins still in the curve, so nobody can take more than their fraction, and every burn leaves each remaining coin backed by at least as much of every stock as before. A burn may name a minimum for each stock; the page sends 1% under its quote. The coin page shows whether burning or selling pays more for your amount.
The picture and links
The picture (cropped square and shrunk to under 16 KB in your browser), the description and up to three links are ABI-encoded and stored as the code of a tiny contract (SSTORE2) when the coin launches: 24 KB at most. meta() returns them byte for byte. Nothing depends on a server.
The contracts
| What | Address |
|---|---|
| StookFactory | 0x25da54bF6643CFEd008d60319daD6a16F87dC498 |
| StookCoin implementation | 0x6fa1b3402F18Ec440B0768BD55eEE3be9712Cc7A |
| CREATE2 deployer (Arachnid’s, deterministic) | 0x4e59b44847b379578588920ca78fbf26c0b4956c |
| Uniswap SwapRouter02 | 0xCaf681a66D020601342297493863E78C959E5cb2 |
| WETH / USDG 0.01% pool | 0x52e65B17fB6E5BA00Ed806f37Afcd2DaA50271Ca |
The factory’s address is keccak256(0xff ++ deployer ++ salt ++ keccak256(initCode)), with salt 0x6259bd3c2e5221357575d3e1949276a9ad932a756dfb6699086368a42ad48c2d and init-code hash 0xac665fbcf6f4f9bbdf7bb4cd230f0d7e49b19b14af72365e2703faa862e1f67e. So the address is the code: anyone can deploy it, and whoever does puts exactly this code there. The launch page does it for you: if the factory is not there yet, your wallet first sends that one deployment, then your launch. Source: StookFactory.sol, StookCoin.sol, and Uniswap’s TickMath.sol; solc 0.8.26, optimizer 1000 runs, via-IR, Cancun. Status right now: checking…
How it was tested
Every property below runs on a private fork of live Robinhood Chain (anvil, started fresh at the newest block for each property): the real CREATE2 deployer deploys the factory, coins launch on baskets of real stock tokens, and every stook purchase swaps through the real Uniswap pools, in the state they are in right now. Nothing is broadcast and nothing is mocked. Expected numbers are computed in the test from the formulas written out there, never by asking the contract.
The last run: 12/12 properties and 270 checks passed against live state (30 Sep 2026), for the factory at 0x25da54bF6643CFEd008d60319daD6a16F87dC498.
| # | Property | Checks |
|---|---|---|
| P1 | The factory lands at the address its code fixes, with the implementation beside it | 7 |
| P2 | Launch refuses every bad input with the error named for it, and accepts a good one | 29 |
| P3 | Buys and sells pay exactly the curve and the fee, and every fee reaches the stook | 62 |
| P4 | A round trip never makes money, and everyone can always sell back | 20 |
| P5 | The stook only buys near the average price; a pushed pool defers the buy until it is not | 15 |
| P6 | The price read is Uniswap's, in both token orders and every fee tier, and the swap matches the quoter NVDA through its 0.05% pool: 0.005 ETH bought 0.058512 NVDA, 16 bp below what the 30-minute average said (the floor allows 206 bp below) SPCX through its 0.05% pool: 0.005 ETH bought 0.088890 SPCX, 16 bp below what the 30-minute average said (the floor allows 206 bp below) TSLA through its 0.3% pool: 0.005 ETH bought 0.037719 TSLA, 40 bp below what the 30-minute average said (the floor allows 231 bp below) MSTR through its 1% pool: 0.005 ETH bought 0.086443 MSTR, 108 bp below what the 30-minute average said (the floor allows 301 bp below) | 17 |
| P7 | Redeeming pays exactly the holder's share of EVERY stock in the basket and nothing more | 44 |
| P8 | Too little gas is refused outright rather than silently deferring the fee | 6 |
| P9 | A coin keeps its picture and links on chain, byte for byte | 6 |
| P10 | The coin is an ordinary ERC-20, refuses stray ETH, and cannot be re-initialised | 8 |
| P11 | Each fee buys the stock furthest behind its weight, so the basket keeps the weights it was given 12 swaps; ETH per leg 0.0719 / 0.0499 / 0.0260 for weights 50/30/20 | 47 |
| P12 | A stock whose pool is pushed is passed over for ten minutes, and the rest of the basket keeps buying | 9 |
Then a sabotage sweep plants 22 bugs, one at a time, in copies of the contracts — the fair-price guard removed, the 30-minute average swapped for the spot price, a basket that buys the most-bought stock instead of the least, weights ignored, a refused stock never put to rest, a burn that pays out only the first stock, weights that need not add up to 100%, a buy that rounds one coin the wrong way — and requires the property named for each one to fail. 22/22 were caught by the property named for them.
And in a real browser: headless Chrome drove these pages with a test wallet against a private copy of the live chain — one click on the launch page deployed the factory and launched a coin with a picture on a TSLA 50 / NVDA 30 / SPCX 20 basket at 3%; three buys each put their fee into the stock the weight rule named; then a sale, a burn that paid a share of every stock, and the board. 6/6 journeys, 45 checks, each outcome read back from the chain by the harness itself (30 Sep 2026).
Risks
- Unaudited. The contracts are small and tested, not audited.
- Coins can go to zero. The stook gives each coin a floor only as high as the stock it holds; a coin can trade far above it and fall back to it.
- The weights are of ETH spent, not of value. The basket buys to its weights; afterwards each holding rises and falls with its stock, and nothing sells one stock to buy another.
- Stocks fall, and Robinhood controls its stock tokens. Robinhood can pause, block or burn its tokenized stocks. A paused stock cannot be bought (fees wait) or paid out (burns revert until it resumes).
- Thin pools make a stock wait. If a stock’s pool is too thin for a fair fill, that stock rests and the others are bought; if every pool is off, fees accumulate as ETH until one is not. Burns still pay that ETH out pro rata.