HOPPER
§1 · Bin & Draw

The Bin and the Draw

A Bin holds one pool asset. A Draw is a claim on a Bin. The first is a level and the second is a share of it — and neither of them is a rate, which is the distinction the rest of this site is about.

§1.1

What a Draw is worth

One Draw redeems for totalAssets / totalSupply of the underlying. Fee income harvested into the Bin raises totalAssets, so it raises what every Draw is worth, at once and without anybody claiming anything.

That figure is read from the contract rather than quoted by a feed, and it is computed from a number held in storage rather than from the Bin's token balance. The difference matters: if the share price were balanceOf(bin) / totalSupply, then anybody could move it by sending tokens to the Bin, and a first depositor could move it far enough to round the second one down to nothing.

ERC-4626 functions implemented
6 of 16
Share price source
contract storage
Virtual shares
yes — the inflation-attack defence
Protocol cut of harvested fees
10%
Hard cap on that cut, in code
20%
Lockup
none
Deployed
no

It is not ERC-4626, and the row above says so with a number. The accounting is ERC-4626's and the conversions are its conversions, but the interface is not complete: maxDeposit, previewDeposit, maxMint, previewMint, mint, maxWithdraw, previewWithdraw, withdraw, maxRedeem, previewRedeem are absent. That distinction is worth a line because it is a real failure mode in this category — a front page asserting the standard while maxDeposit reverts. The build enumerates the interface from the compiled ABI rather than describing it.

§1.2

The lexicon

A silo has better words for this product than the product does, because a silo has had two hundred years to find them. Each of these replaces something.

Bin

replaces the vault

Where the asset sits.

An ERC-4626 bin holding one pool asset. Deposits are accounted in storage rather than read off the balance, so a stray transfer into it moves nobody's share price. The bin is a LEVEL: it says how much is there and nothing whatever about how fast it can leave.

Draw

replaces the share token

A claim on the bin, in your wallet.

A plain ERC-20. One Draw is worth totalAssets / totalSupply of the underlying, and harvested fees raise that number for every holder at once. It is called a draw because that is what it is: the right to draw from the bin, not the material itself.

Gate

replaces the withdrawal function

The orifice. The only thing that sets the rate.

Every quantity that governs how fast the bin can pay lives here — and none of them is the size of the bin. This is the site's whole argument in one word: the gate does not know how much is above it, and neither does the flow through it.

Chute

replaces private execution

Where the order goes, unbroadcast.

Orders are expressed as intents and filled by solvers rather than sitting in a public mempool waiting to be read. A chute is the enclosed run between the gate and the truck: the material is moving, and it is not moving in the open.

Arch

replaces nothing — there is no word for this in the category

The exit stopping while the bin is full.

Grains at an orifice can lock into a self-supporting arch and the flow stops, with the silo still full and nothing broken. It is not a failure of the material and not a failure of the gate; it is a property of the ratio between them. Below about five grain widths it is not rare, it is expected.

Repose

replaces nothing

The part of the bed that is at rest right now.

At any instant most of a discharging bin is not moving: a narrow channel runs from the surface to the orifice and the rest stands still, with the boundary migrating inward as the level drops. It is not stuck — it will move eventually — and that is exactly the difference between YOU WILL GET IT and YOU CAN HAVE IT NOW. On a balance sheet the two are the same row. THIS ENTRY USED TO SAY SOMETHING ELSE, and the something else was wrong: that a flat-bottomed bin cannot empty itself and keeps a heap standing at its angle of repose. It is a real phenomenon and this model does not show it — the bin drains to a single layer of grains on the floor. The word was kept and the claim was replaced by the one the measurement supports.

Freeboard

replaces the safety margin

The gap between the fill line and the rim.

Silo codes require it because a bin filled to the rim has nowhere to put a surge. Here it is the distance between what the protocol says it can pay and what it can actually pay — and unlike a buffer it is meant to be PUBLISHED rather than held. It is the number this site says is missing from the category, and the number this site does not have either. The Bin is deployed on a test network with no depositors and no fee income, and freeboard on an empty bin nobody is trying to leave is not a measurement of anything.

Ullage

replaces the disclaimer

The empty part, stated as a volume.

Ullage is how much of a container is NOT full. This site publishes its own: which parts run, which parts are code that has never been deployed, and which parts are a sentence. A blanket "experimental" notice is true and tells a reader nothing about which part to trust.

§1.3

What the contract guarantees

Four properties. Each is executed against compiled bytecode on an EVM by tools/verify-contract.mjs, and then the contract is deliberately broken four ways to check the suite notices.

A first depositor cannot round the next one to zero

The classic ERC-4626 inflation attack: deposit one wei, donate a large balance, and the second depositor's shares round down to nothing. Virtual shares and virtual assets shift the arithmetic so the attacker pays more than the victim loses.

A donation does not reprice anybody

Assets are tracked in storage, not read off the token balance. Sending tokens to the Bin makes them the Bin's and changes no share price, which is what stops the price being a function of who transferred what to it last.

The protocol cut is capped in code

The cut taken from harvested fees cannot be raised above the hard cap by the owner or by anybody else, because the cap is a constant and the setter reverts above it. A cap in a comment is not a cap.

The owner cannot take the backing

The sweep function can only move assets in excess of what holders are owed. It computes that excess from the same storage figure the share price is computed from, so it cannot be tricked into considering principal to be surplus.

What the build actually did

The contract is compiled with solc 0.8.26, optimizer 200 runs and its properties are executed by placing the compiled bytecode at a scratch address inside a single eth_call against a public Base node. An eth_call state override is a free, keyless EVM — the assertions below are not claims about the source, they are that bytecode running.

Compiled size, Bin
4,487 bytes
Functions in the ABI
22
Properties executed
4
Broken vaults that had to fail
4
run() returned
0xFF
A round trip on 10 harvested into 100, at a 10% cut
8.99%
Deployed
Base Sepolia only — a test network
Deployed where money is real
no
Audited
no
  • PASS the contract compiles with no errorsBin 4487 bytes, 2 warnings
  • PASS all four properties hold on an EVMrun() returned 0xFF
  • PASS all four sabotaged vaults are caughteach property was re-run against a vault with that defence removed
  • PASS a round trip returns principal plus its share of the harvest8.99% on 10 harvested into 100 at a 10% cut
  • PASS the ERC-4626 interface is enumerated, not asserted6 of 16 functions present; missing maxDeposit, previewDeposit, maxMint, previewMint, mint, maxWithdraw, previewWithdraw, withdraw, maxRedeem, previewRedeem
  • PASS no function in the ABI can move the backing to a chosen address22 functions, none of them

A suite that has only ever seen working code has not been tested. So each property is run twice: once against the Bin, where it must hold, and once against a vault with exactly that defence removed, where it must fail. If a broken vault passes, run() reverts naming it — a property that cannot fail is not a property. The four broken vaults are in contracts/BinTest.sol and each is four lines.