How it works

Four calls, and arithmetic on the block number

Nothing in the path needs a keeper you trust, a solver you cannot watch, or a price somebody else publishes. The share price rises between transactions because it is computed, not because anyone sends one.

01

Deposit

You send the asset and are issued shares at the price of that block. The assets go into a storage counter. The contract never reads its own balanceOf, so a stray transfer cannot move anybody's price.

02

Harvest

Fee income arrives. Anyone may deliver it — the only power a caller has is to give the vault money. The protocol cut is taken once, capped in the contract at 2000 bps, and the rest goes to locked.

03

Unlock

Every block, a fixed slice of the locked balance becomes an asset. No transaction makes this happen; locked() is a subtraction on block.number.

04

Redeem

Burn shares, take the underlying at the price of that block. No queue, no epoch, no withdrawal delay — and now no reward for choosing the block.

The release

Nine lines, and all of it

function locked() public view returns (uint256) {
    uint64 to_ = _to;
    uint256 b = block.number;
    if (b >= to_) return 0;
    uint64 from_ = _from;
    if (b <= from_) return _stream;
    return _stream - (_stream * (b - from_)) / (to_ - from_);
}

function totalAssets() public view returns (uint256) {
    return _pool - locked();
}

_pool is everything the vault owns. _stream is the size of the release running right now, and it starts again at every harvest carrying whatever of the previous one had not been released. totalAssets() — the only number ERC-4626 prices anything from — is the difference.

That is the whole mechanism. There is no accrual loop, no checkpoint, no keeper, and no state written between harvests: a share is worth more at block n+1 than at block n because a subtraction says so.

The contract also publishes dirtyPricePerShare() beside pricePerShare(), so the gap between what has been credited and what is held is a number you can read rather than a claim you have to take.

Both are executed on Robinhood Chain every build — P9, P10 and P11 on the contract page are the three that pin this behaviour down.

The picture

The hero is a drainage model, and the build checks it

A landscape that draws a pretty tree is decoration. This one is solved: a fractal surface, its depressions filled by priority-flood, D8 flow directions, one accumulation sweep, a support threshold, Strahler orders, and the basin masked to the single largest mouth. computed

Held to the literature, not to taste

QuantityThis landscapePublishedVerdict
Bifurcation ratio Rb4.283–5inside
Length ratio Rl1.811.5–3.5inside
Area ratio Ra4.783–6inside
Hack's exponent0.5280.5–0.6inside

Ranges from Horton (1945) and Strahler (1957) for the three ratios and Hack (1957) for the exponent — the literature's, not ranges chosen to contain this answer.

And across 24 landscapes, not one

Seeded landscapes inside all four bands19 of 24
… of those with 4 Strahler orders0 of 1
… of those with 5 Strahler orders18 of 21
… of those with 6 Strahler orders1 of 2
Channel segments in the one you are looking at7,344
Cells draining to its mouth93,708

The failures are not hidden and they are not surprising: a Horton ratio is a geometric mean of two or three numbers, and a small basin does not have enough orders to estimate one. The single order-4 landscape in the sweep is among the failures.

Uniform rain, one pulse

peak, 3.89× the mean time after the rain →
Silt bars: the basin's width function — how many cells sit at each flow distance from the mouth. Water line: the hydrograph a uniform rainfall produces, routed with a linear reservoir on top of the travel time. Every cell receives exactly the same rain at exactly the same instant, and the gauge still records a peak 3.89× the mean with a long recession behind it. Nothing about the input was lumpy.