Nine2Five — how it works
An office on Robinhood Chain. Hold $925, take one of 200 desks, and your agent works the shift whether or not you are watching. It is paid in real assets — USDG and tokenised stock — out of a pool that is topped up by hand.
The protocol mints nothing. Every $925 that passes through it is destroyed.
1. The one-paragraph version
Holding 1,000,000 $925 qualifies an address for a desk. A desk has a graphics card, which decides how fast it mines, and a level, which decides which assets it may be paid in. Each epoch — one day — the whole floor shares a pool of real assets, split by mining power. Desks wear out and have to be refurbished. Upgrading, fitting a card and refurbishing all cost $925, and all of it is burned.
2. The two levers, and why they are separate
| Lever | Decides | Costs |
|---|---|---|
| Card | how much you are paid | 240 $925 (~$20) |
| Level | what you are paid *in* | 120 $925 (~$10) per step |
This split is the core design decision and it is worth stating why.
Mining power is zero-sum. Your share is your power divided by the floor's power. When everybody
upgrades, every share returns to where it started — so a lever that only sells power is a treadmill that extracts tokens and gives nothing back.
Breadth is not zero-sum. A level-4 desk being paid in NVDAx takes nothing from the desk beside
it. Levelling up buys a *different* reward, not a bigger slice of the same one.
So a level-1 desk with the top card out-earns a level-5 desk with the stock card. That is not a bug; it is the whole point. test_level_buys_breadth_not_power pins it.
3. The numbers
Cards
| Card | Power | Wear / epoch | Cost | Upkeep |
|---|---|---|---|---|
| Ledger 4 | 1,000 | 2.0% | issued with the desk | 117 $925 every 33 days (~$9.95) |
| Quant 8X | 1,450 | 2.4% | 240 $925 (~$20) | 170 $925 every 27 days (~$14.42) |
| Arbiter 16 Pro | 2,100 | 3.1% | 240 $925 (~$20) | 246 $925 every 21 days (~$20.88) |
Both paid cards cost the same deliberately: what separates them is upkeep, not price.
Levels
| Level | Desk | Unlocks | Cumulative menu |
|---|---|---|---|
| 1 | Hot desk | USDG, ETH | 2 assets |
| 2 | Single screen | + SPYx | 3 |
| 3 | Dual screen | + AAPLx, MSFTx | 5 |
| 4 | Trading desk | + NVDAx, TSLAx | 7 |
| 5 | Corner office | + the rest of the catalogue | all |
Each tier is cumulative. Which ticker sits in which tier is set on StockDrop and can be changed later — unlike everything in Nine2Five, which has no admin key at all.
Constants
EPOCH 1 day
SEATS 200
HOLD 1,000,000 $925 0.1% of a 1,000,000,000 supply
CONDITION_FLOOR 35%
REFURB_RATE 0.18 $925 per point of power restored
4. The maths
Mining power
condition(t) = max(0.35, 1 − wear_card × (t − refurbishedAt) / EPOCH)
power = cardPower(card) × condition
Condition falls continuously from the moment a desk is refurbished, and stops at 35%. A desk left at the floor still mines — at just over a third of what it could.
What a refurbishment costs
refurbishCost = cardPower × REFURB_RATE × (1 − condition)
Priced per point of power restored, so there is no clever moment to do it: the cost per point never changes. What changes is whether it is worth it, which depends entirely on the floor.
A tidy consequence: the recurring upkeep of a desk per day is
cardPower × REFURB_RATE × wear
The (1 − condition) and the cycle length cancel out. That is where the upkeep column above comes from.
Your share of a drop
share_i = cardPower_i × condition_i / Σ cardPower_j
payout_i = pot × share_i
Note the denominator: rated power, with no condition in it. This is not a typo and it is not the
obvious formula.
The obvious formula — dividing by condition-weighted power so shares always sum to 100% — turns wear into a transfer from worn desks to fresh ones. Here, wear is a cost the desk pays and nobody else collects: when desks are worn, the shares sum to *less* than the pot and the remainder is simply not committed. It stays in the pool and lengthens the runway.
tools/check-drops.mjs pins this: a fresh desk is paid exactly the same whether its neighbour is
worn or not.
5. The reward pool
The protocol pays in assets it does not create. The pool lives in StockDrop, is funded in USDG by hand, and is drawn down by the publisher once per epoch.
Funding
$200 a week, from two sources. The dev, and the creator fee that $925 earns on Pons. Neither is
enforced on chain; both are manual transfers.
Player fees do not fund the pool. Every $925 spent on a desk is burned outright, so the money that leaves a player's wallet leaves the supply — it does not come back as a reward.
Why it does not run out
StockDrop releases at most 5% of a bucket's free balance per day. Funded once, that decays;
topped up at a steady rate X, it settles where inflow equals outflow:
0.05 × P = X → P = 20X
A pool holds twenty days of its own income. At $28.57/day in, it settles around $571 and pays
$28.57/day forever.
| Week | Pool | Paid that week |
|---|---|---|
| 1 | $163.76 | $36.24 (18%) |
| 2 | $278.12 | $85.64 (43%) |
| 4 | $413.75 | $144.23 (72%) |
| 8 | $512.15 | $186.74 (93%) |
Two months to within 10% of the ceiling. Week one pays a third of what month three pays, and that is the mechanism working, not a shortfall. Seed the pool with $571 up front if you want it full on day one.
What to convert the USDG into
Not everything, and not on arrival. conversionPlan() in tools/publish-drops.mjs reads the levels actually sitting at the desks and prints the split every run. A desk contributes its rated power evenly across every asset its level opens, which is the *expected* demand rather than one epoch's draw — stable enough to buy against.
A floor of level-1 desks asks for nothing but tier 1. Higher tiers only start drawing budget once somebody has levelled into them, so no money is parked in a token nobody can receive.
6. Worked example — a floor of 20
7 × L1 Ledger, 3 × L1 Quant, 4 × L2 Ledger, 2 × L3 Quant, 2 × L4 Quant, 1 × L4 Arbiter, 1 × L5 Arbiter. Total rated power 25,350. Pool at steady state, paying $28.57/day.
| Desk | Share | Per day | Per month |
|---|---|---|---|
| Ledger 4 | 3.945% | $1.13 | $33.81 |
| Quant 8X | 5.720% | $1.63 | $49.03 |
| Arbiter 16 Pro | 8.284% | $2.37 | $71.01 |
Note that level does not appear. The L1 Quant and the L4 Quant earn exactly the same amount — they differ only in which tickers that amount can arrive as.
Net of upkeep
| Card | Income | Upkeep | Net |
|---|---|---|---|
| Ledger 4 | $1.13 | $0.31 | $0.82 |
| Quant 8X | $1.63 | $0.53 | $1.10 |
| Arbiter 16 Pro | $2.37 | $1.00 | $1.37 |
Payback on a card
| Card | Extra per day vs Ledger | Pays back in |
|---|---|---|
| Quant 8X | $0.51 | 40 days |
| Arbiter 16 Pro | $1.24 | 16.5 days |
7. A known problem with the card table
At the same price, the Arbiter beats the Quant on every axis: higher income, higher net, faster payback. The Quant's only advantage is a smaller absolute upkeep bill, which matters only to someone who cannot carry $1 a day.
So as the table stands, the middle card is dead and the choice it was meant to create does not happen. Two ways to fix it, neither applied yet:
1. Price them apart — Arbiter at 480 $925 instead of 240. 2. Make the Arbiter run hotter — wear at 3.94% an epoch instead of 3.1% puts its net exactly level with the Quant's, which makes the decision about appetite for upkeep rather than arithmetic.
Both are one-line changes in Nine2Five.sol, and both must happen before deploy.
8. Supply
$925 is a fixed-supply token launched on Pons. Nine2Five cannot mint, has no mint function, and holds no balance of it.
What the protocol does to supply:
- Destroys. Every upgrade, card and refurbishment is burned whole, straight to the dead address.
It never touches the contract's balance, so there is nothing to withdraw and no function to withdraw with.
- Locks up nothing. Qualifying for a desk is a balance check —
balanceOf(msg.sender) >= hold.
No transfer, no deposit, no lock. A holder keeps every token and can sell at any moment; they lose the seat, not the money.
A full floor of 200 desks represents 200,000,000 $925 held in private wallets — 20% of supply, none of it in a contract.
9. The contracts
Nine2Five.sol
Decides who has a desk, how fast it runs, and what it may be paid in. Burns fees. Pays nothing.
No owner, no admin key, no upgrade path, no withdraw. Everything in it is fixed at deploy except the desks themselves.
| Function | Who | Does |
|---|---|---|
claimSeat | anyone holding | takes a free desk |
upgrade | seated | +1 level, burns 120 $925 |
fitCard | seated | swaps the card, burns 240 $925, carries condition across |
refurbish | seated | condition back to 100%, burns the cost |
setLook | seated | character, skin, nickname |
leaveSeat | seated | gives the desk up |
evict | anyone | frees a desk whose holder has sold below the threshold |
floor / agent | view | the whole office, for drawing it and for splitting a drop |
StockDrop.sol
Holds the pool and pays it out against cumulative merkle roots.
Has roles: an owner (manages the catalogue and can withdraw unallocated balance after 48 hours), a publisher (commits roots), and a guardian (can veto a root within 30 minutes).
tierOf records which desk level each asset is reserved for. Nothing in claim reads it — and
that is deliberate. A drop is earned across an epoch at the level the desk held *then*, so a check at claim time would get both ends wrong: an address that levels up afterwards would collect an asset it never earned, and one that leaves its seat falls to level 0 and would lose a drop it did earn. The rule belongs in the publisher, and tierOf is what lets anyone audit a published tree against it. test_claim_does_not_read_the_tier exists to stop someone "fixing" this.
10. Running it
Deploy
TOKEN_925=0x… OWNER_ADDRESS=0x… PUBLISHER_ADDRESS=0x… GUARDIAN_ADDRESS=0x…
forge script script/Deploy.s.sol --rpc-url rh --broadcast
Optional: SEATS (200), HOLD_AMOUNT (1,000,000e18).
The office needs no funding. StockDrop does: send it USDG, and the stock tokens for whichever tiers the floor has reached.
Publish a drop
OFFICE=0x… DROPS=0x… node tools/publish-drops.mjs # dry run
OFFICE=0x… DROPS=0x… PUBLISHER_KEY=0x… node tools/publish-drops.mjs --publish
Writes public/drops.json (the proofs the page fetches) and tools/.drops/ledger.json.
The ledger must survive between runs. Roots are cumulative; if it is lost, the next tree promises
less than the last one did, publish() refuses the lower total, and anyone mid-claim is stranded.
.github/workflows/drops.yml runs this daily and commits the ledger back for exactly that reason —
do not swap it for a cache that can be evicted.
Watching a desk from a terminal
A holder does not have to open the app to see their agent. The panel gives them one line to paste:
curl -fsSL https://nine2five-sigma.vercel.app/watch.mjs -o watch.mjs && node watch.mjs n25_<key>
It prints the desk, its energy, mining power, share of the floor, what it earns a day, what is waiting to be pulled, and the state of the floor — refreshing on a timer until stopped.
The key is the holder's public address, base64url'd so it is short enough to paste. It is not a
secret and it cannot sign: watch.mjs makes eth_call reads and nothing else, has no wallet code in it at all, and never prompts for anything. Nothing in Nine2Five will ever ask a holder to paste a private key, and the script says so in its own header.
The script has no dependencies. It speaks JSON-RPC over fetch and decodes the handful of return values by hand, so it runs on a bare Node install with nothing to audit but the one file. Function selectors are hard-coded because that is cheaper than shipping a keccak implementation to compute four constants.
tools/check-drops.mjs checks the key against both implementations — the encoder in the page and
the decoder in the script never import each other, which is exactly where a format quietly drifts.
Checks
npm run check # 11 browser and logic checks
npm run check:drops # the publisher's money maths, no browser
cd contracts && forge test
11. Still open
- The card table — see section 7. Must be settled before deploy.
- Treasury/owner address for
StockDrop. - Which tickers sit in which tier. Changeable later, unlike the rest.
- Whether the pool is seeded to $571 on day one or allowed to ramp over two months.