Silt

A collection of 2,000 pieces that cannot be bought at issuance, released one every fifteen minutes to a randomly chosen holder, each redeemable for an equal share of a reserve.

01 Summary

Silt inverts how a collection is distributed. There is no mint page, no allowlist, and no price. You hold the token, and four times an hour the protocol picks one holder and issues them a grain.

The chance of being picked is your share of the eligible supply. Hold more, get picked more often. Hold nothing, never get picked. Nothing is staked, locked, or claimed. Holding is the entire action.

Trading fees accumulate into a reserve contract that backs the collection. Any grain can be burned at any moment to withdraw its share of that reserve, and every grain is worth exactly the same amount regardless of what it looks like.

The result is a collection whose floor is arithmetic rather than sentiment, distributed by a process nobody can front-run, on contracts nobody can change.

02 The token

The token is a standard ERC-20 with one addition: it maintains a Fenwick tree of every eligible balance inside itself, updated on any balance change. A holder who received tokens by plain transfer is entered exactly like one who bought.

Eligible supply

Odds are computed against eligible supply, not total supply. A fixed set of infrastructure addresses is permanently excluded from the draw and from the denominator, set as immutable constructor arguments with no way to add or remove one later: the Uniswap v4 PoolManager, the zero and burn addresses, the reserve, draw and collection contracts, Permit2, the Universal Router and the Position Manager.

Pool liquidity therefore never wins a grain, and burned supply never dilutes anyone's odds.

03 The draw

One draw fires every fifteen minutes. The schedule advances from the scheduled time rather than from when the previous draw settled, so it cannot drift no matter how late a draw is called.

Two phases

A draw resolves in two transactions. This is not a convenience. A single transaction that resolved from a past block hash would let anyone simulate the outcome first and submit only when they personally won.

requestDraw()
  freezes all balances at this block
  records requestBlock
  the outcome does not yet exist

settleDraw()
  called at least 2 blocks later
  seed drawn from blockhash(requestBlock + 2)
  winner and tier resolved
  grain minted directly to the winner

Because balances freeze at request time, buying after a request has landed does not enter that round. It enters the next one.

Why the freeze is load-bearing

The seed becomes public one block before settlement. If the settler could read live balances, they would see the seed, compute the winning index, move tokens to place themselves there, and settle. The freeze is the only thing standing between this design and a total break.

It is implemented as an epoch-tagged delta overlay on the tree rather than a copy. While a snapshot is open, every balance change also writes the inverse change into a parallel tree stamped with the current epoch; reading tree plus delta reconstructs the distribution as it stood at the freeze. Stale deltas from a previous epoch read as zero and are never cleared, which is what makes opening a snapshot constant time rather than proportional to the holder count.

Selecting the winner

The seed produces a random point in the range zero to eligible supply, and a binary descent over the tree returns the holder whose cumulative range contains it. The lookup is logarithmic. The protocol never iterates over the holder set, so a draw costs the same with two hundred holders as with two hundred thousand.

A wallet holding 3% of eligible supply wins approximately 3% of all grains ever issued. Nothing compounds, decays, or resets.

Empty rounds

If eligible supply is zero when a draw fires, no grain is minted. If the collection is already complete, the draw still fires and still emits — it simply stops minting.

04 Randomness

Chainlink VRF is not deployed on this chain, and neither is Chainlink Automation. What went live at launch was Data Feeds, Data Streams and CCIP, none of which produce verifiable randomness. block.prevrandao returns a constant on the Arbitrum stack. The seed therefore has to come from chain state, and the keeper has to be an incentivised third party rather than a scheduling service.

seed = keccak256(
    blockhash(requestBlock + 2),
    previousSeed,
    round,
    slotResets
)

The block hash does not exist at the moment of the request, so the requester cannot know the outcome. It is fixed once that block is produced, so the settler cannot grind it by waiting. No value from the settling block enters the seed, which closes the only remaining avenue for manipulation by timing.

On block numbers. On the Arbitrum stack, Solidity's block.number is the Ethereum L1 block number, not the chain's own. We verified this empirically before choosing the window. The unit is therefore ~12 seconds, not the chain's ~100 ms block time: the settle opens about 24 seconds after a request and stays open for roughly 50 minutes. Reading the explorer's height and assuming it is block.number would have sized that window at 25 seconds, which no keeper could reliably hit.

We state the tradeoff plainly rather than describing this as provably fair. This chain runs a centralised sequencer with no public mempool, so block hashes are not miner-manipulable the way they are on a proof-of-stake layer one, and pending transactions are not visible to searchers. But the sequencer operator could in principle bias a draw. There is no economic reason for them to do so, and the full seed inputs are emitted in the settle event so anyone can reconstruct and verify every draw that has ever happened.

05 The collection

2,000 grains. At one every fifteen minutes the collection completes in 500 hours, which is 20 days and 20 hours from the first draw — assuming every scheduled slot actually settles. Missed slots are skipped rather than fired back to back, so that date is a floor, not a promise.

Token ids are dense and sequential: the first grain ever issued is #1. The id encodes issuance order and nothing else.

Tiers

TierCountOdds
Common80040.0%
Rare60030.0%
Epic40020.0%
Gold1608.0%
Green402.0%

The tier is rolled at the moment of issuance, from the settled seed, against whatever is still unminted. The multiset above is fixed at deploy and the final composition is exact — but the order is unknowable, including to us. There is no table anyone can read to find out which future draw is the valuable one.

Rarity is cosmetic. A Green redeems for exactly the same share of the reserve as a Common. Tier changes what a grain is worth on the secondary market and nothing else. This is deliberate: weighting redemption by rarity would turn the reserve into a claim that some holders could raid faster than others.

06 The reserve

The reserve holds a single immutable backing asset and grows from trading fees. It exposes exactly three outward paths, each callable only by one immutable address:

  • Redemption payouts, callable only by the collection contract
  • Buyback funding, callable only by the buyback module
  • The keeper bounty, payable only out of a segregated pool

There is no withdraw function, no sweep, no rescue and no owner. Anyone reading the source can confirm in under a minute that there is no other door.

Keepers are paid out of inflow, never out of principal

A quarter of a percent of every deposit is set aside into a segregated pool, and the settler bounty can only ever be paid from that pool. The figure holders redeem against — backing() — excludes it entirely. No sequence of calls lets a keeper touch principal.

This matters more at four draws an hour than at one. A design that pays a flat bounty out of the reserve itself is a perpetual leak: the reserve has to earn its own bounty back forever, and a protocol whose volume dies bleeds its principal to bots indefinitely. Segregating the pool removes that failure mode completely.

If the pool runs dry the draw still settles — it simply pays nothing, and the next deposit refills it. A missing bounty must never be able to halt the protocol.

07 Redemption

Any holder can burn a grain at any time and withdraw its share of the reserve.

share  = backing × 90% ÷ liveSupply
payout = min(share, backing × 5%)

Ten percent of every redemption stays behind. Supply falls by one grain while the reserve falls by less than one share, which means backing per remaining grain rises on every single exit. Whoever leaves pays a toll to whoever stays.

The five percent cap only binds while fewer than nineteen grains are live. It exists to stop the very first redeemer from walking away with most of an almost-empty pot, and it is dormant thereafter.

Burns are permanent

A burned id can never be reissued. Its slot in the collection stays empty forever, and the total that can ever exist only falls. The 2,000 counter tracks issuance, not live pieces, so early redemptions do not shorten the collection.

There is no cooldown. A grain can be redeemed the moment it is won.

What happens if everyone redeems immediately

Nothing breaks, and this is worth being precise about because it is the first objection anyone raises. The fee phase is gated on grains issued, not grains live, so mass redemption cannot starve the reserve during issuance. With fee inflow f per interval and bounty b, the reserve converges to 19 × (f − b) rather than draining — and each redeemer collects roughly one interval's worth of fees, which is exactly one grain's fair share.

The real cost of that scenario is not the reserve. It is that the collection ends with nothing live in it.

08 Keepers

Both calls are permissionless and the settler is paid from the bounty pool, so a draw fires whether or not we are online.

A settle window can genuinely be missed, so an expired round can be released and the slot retried without losing it. But that recovery path is also an attack surface: request, peek at the seed once the block hash exists, settle only if you won, otherwise let the window lapse and re-roll the same slot. Left unbounded, a small holder could capture far more grains than their balance entitles them to.

A slot may therefore be reset at most three times. After that it is abandoned, the clock moves on, and no grain is minted for it. A griefer gets a small, bounded number of rolls rather than unlimited ones, honest operation is unaffected — three consecutive missed fifty-minute windows is already pathological — and every reset is on the public record.

09 Ownerlessness

There is nothing to govern and nobody to trust.

  • No admin functions of any kind, in any contract
  • No fee setter, no pause, no upgrade path, no proxy
  • No multisig and no timelock, because there is nothing for either to control
  • Every parameter is an immutable or a compile-time constant
  • The metadata base is written to contract code at deploy — there is no setter to call and no owner to renounce
  • All contracts verified before launch, including the draw and the fee router

There is no renounceOwnership step after deploy, because no contract ever has an owner to begin with. A design that ships an owner and promises to give it up later is asking for trust it does not have to ask for.

Every draw emits its full seed inputs, the eligible supply at snapshot, the winner and their balance. After a few hundred draws anyone can reconstruct the distribution independently and check it against the stated odds. That record exists whether or not anyone asks for it.

10 Risks

Stated plainly, because a paper that lists none is not worth reading.

The reserve is not a guarantee

Backing per grain is arithmetic, but it is arithmetic over a reserve that only grows if people trade. A token nobody trades accumulates nothing. The floor is real and it can be small.

Ownerlessness cuts both ways

Nothing can be changed, which includes bugs. There is no pause, no upgrade and no recovery. The contracts should be read before use, not trusted because a paper describes them.

The chain can refuse to execute us

This chain runs compliance filtering inside the sequencer: transactions are simulated before sequencing and an authorised filterer can force a specific transaction to fail, including one force-included through the L1 delayed inbox. Immutable contracts do not survive a chain that can decline to run them. Our contracts cannot be changed by anyone, including us — but they can be made unreachable by the chain operator, and no amount of on-chain design fixes that.

Randomness is sequencer-derived

As described in section four, the seed comes from chain state rather than from a verifiable randomness service, because none is deployed here. The sequencer operator could in principle bias a draw.

Keepers are a liveness dependency

Nobody is obliged to call the draw. The bounty makes it worth doing and the calls are open to anyone, but if every keeper stops at once, draws stop until someone starts again. Nothing is lost when this happens — the schedule does not drift and the reserve is untouched — but the collection takes longer to complete.

The collection ends

After 2,000 grains the token's remaining function changes. That is a structural change in what holding means, and the market will price it before it happens rather than on the day.

11 Contracts

All addresses published and verified at launch. See the live state page for the current list.