Statement of harvested lots · Robinhood Chain testnet 46630

The ledger that books your losses — and refuses the rebuy.

Every December, Maya leaves a paper loss on the table: banking it means picking which of 64 tax lots to sell, then policing a 30-day wash-sale calendar. HarvestBot's agent does it on Robinhood Chain, with the ledger in Rust on Arbitrum Stylus, under a mandate it cannot exceed: 9 lots harvested, the rebuy reverted by the contract, the lying agent slashed 200 mUSDC.

no wallet neededreal AMZN, NFLX, PLTR Stock TokensStylus ledger: 2.87× less gas81 + 6 tests
AMZN · lot ledgerbeat 1 · HIFO
LotBasis/shMark mockGain/(loss)
63455.00412.30(3.34)
62453.81412.30(3.24)
61452.62412.30(3.15)
60451.43412.30(3.06)
59450.24412.30(2.96)
58449.05412.30(2.87)
57447.86412.30(2.78)
56446.67412.30(2.68)
55part445.48412.30(0.45)
54444.29412.30open
53443.10412.30open
Banked($24.53)
faucet scale: 0.078125 AMZN per lot · realized on chain
Buy back AMZN · beat 2
Walled · reverteduntil 15 Oct 2026
reading tx 0x689c…6123 from chain 46630… explorer ↗
2.87×
less L2 gas for a 64-lot HIFO harvest, Stylus vs Solidity (3.1× at 128)
81 + 6
Foundry tests + Stylus native tests, 3 fuzz properties
forge test · cargo test
0 · 0
Slither high · medium findings, the rest triaged
3 of 3
demo beats executed as real transactions on chain 46630
($3,140.00)
the 10-share seed scenario in forge test, exact on both engines (not an on-chain amount)
forge test --match-test seed_scenario
The one flow, as it happened on chain

Harvest, refuse, slash — three transactions, one agent key.

The agent only ever signs a decision. The contracts recompute everything from scratch and either execute it or revert it. Here is each beat, with the chain's own words.

Beat 1block
119,852,570

Nine lots banked.

The agent signs a HIFO decision: sell 0.638501 AMZN, lots 63 → 55, rotate into NFLX. The router lets its key call exactly one function. The mandate recomputes the picks on the Stylus ledger, matches them, realizes ($24.531251) at the oracle mark and opens a 30-day window.

0x689c4d34…6123 ↗
Beat 2block
119,852,786

The rebuy, refused. reverted

Forty seconds later the same agent tries NFLX → AMZN. The contract reverts WashSaleViolation(owner, AMZN, AMZN, 1792060681): the way back is walled until 15 Oct 2026. The agent cannot route around it; the chain says no.

0xd486468e…cf10 ↗
Beat 3blocks
125,092,578
125,092,624

The rogue trade, slashed.

The agent signs AMZN → PLTR, a trade the substitute map never sanctioned. It reverts OffMapSubstitute, and the signed envelope becomes evidence: challenge() cuts 20 % of the bond, 20 to the challenger, 180 to the owner.

bond 1,000.00 → 800.00 mUSDC at block 125,092,624 mock usdc

attempt 0xd8b6e577…94c8 ↗ · challenge 0x6b896b6e…7dcb ↗
zsh — harvestbotreal runs · 27 Sep 2026
$ forge test --match-test seed_scenario
[PASS] test_seed_scenario_realizes_exactly_minus_3140_dollars() (gas: 1433602)
Suite result: ok. 1 passed; 0 failed; 0 skipped

$ forge test
Ran 8 test suites in 236.92ms (741.71ms CPU time):
81 tests passed, 0 failed, 0 skipped (81 total tests)

$ cast call 0xEff7B46049fC677F58264e0ebb19dF1a39195a21 \
    'computeHarvest(address,address,uint256,uint256)(uint64[],int256)' \
    0x72cd…557b 0x5884…9E02 638501157098660213 412300000 \
    --rpc-url https://rpc.testnet.chain.robinhood.com
[55, 54, 53, 52, 51, 50, 49, 48, 47]
-18321704 [-1.832e7]
The Stylus ledger, asked at block 125,053,420: beat 1 already sold lots 63–56 and part of 55, so it now answers with the next nine.

Stylus vs Solidity · same chain, same inputs, identical output

The bookkeeping runs in Rust, and costs 2.87× less L2 gas.

A 9-lot HIFO harvest over 64 open lots: 1,456,905 gas on Solidity, 507,993 on the Stylus ledger. The ratio grows with the portfolio: 3.1× at 128 lots.

Measured with Arbitrum's own NodeInterface.gasEstimateComponents on chain 46630. The Stylus program is uncached on this testnet, so every call pays the WASM start-up floor (near parity at 8 lots): these are worst-case Stylus numbers. The spec had guessed ~8×; the chain says 2.87×, and that is the number we report.

Solidity twin, L2 gasStylus (Rust → WASM), L2 gasopen lots on the left

Full table with p50/p95 latency →


Six contracts, one bound

The agent signs. The contracts check every line.

TaxLotLedger, in Rust on Stylus

Records every lot, picks the highest-basis lots for a sale, prorates the last one and computes the realized loss deterministically. It is the thing the mandate checks the agent's claim against, and it runs as WASM on Robinhood Chain.

computeHarvestrealize0xEff7…5a21stylus

WashSaleGuard

For the 2,592,000 seconds after a harvest, any rebuy of the asset or its identical cluster reverts.

WashSaleViolation

HarvestMandate

Owner custody. Re-validates signature, nonce, deadline, map, window, HIFO and the loss at the oracle mark.

EIP-712mock oracle

SubstituteMap

Owner-curated pairs the agent may rotate into. AMZN ⇄ NFLX is sanctioned; PLTR is not, which is why beat 3 reverted.

isSubstituteOffMapSubstitute

ExecutionRouter

The agent key reaches one target, one selector, value 0, with an expiry and a daily rate limit.

1 selector

AgentBond

Anyone holding an envelope the mandate must reject can slash it: 20 %, a tenth of that as bounty.

challenge()mock usdc
Why only Robinhood Chain + Stylus

A tax-lot ledger belongs on the chain that issues the stock.

The assets are Robinhood's.

The mandate holds real testnet Stock Tokens from the official faucet: AMZN 0x5884…9E02, NFLX 0x3b82…8C93, PLTR 0x1FBE…98d0.

The ledger is WASM.

cargo stylus deploy put the Rust ledger on chain 46630; ArbWasm.stylusVersion() = 3. Remove Stylus and the HIFO scan costs 2.87× more.

The venue is not real, and says so.

There is no Stock-Token AMM on the testnet, so the rotation runs through a fixed-rate venue labeled mock. The tokens are real; the venue is not.


Honest by design

What it is not, in the repo's own words.

“The oracle mark, the swap venue and the bond's USDC are MOCK contracts, labeled in name and symbol.”
— JUDGE.md · Honest limitations
“Re-buys are walled for the 30 days after a harvest; the 30-day look-back before the sale is not enforced yet.”
— JUDGE.md · Honest limitations · Wash-sale scope
“The spec had guessed ~8×; the chain says 2.9×–3.1× — we report the chain.”
— JUDGE.md · Honest limitations

Questions a judge would ask

Before you click Verify.

Is this really on chain, or a replay?
All three beats are real transactions on Robinhood Chain testnet (46630), signed by an agent key that is different from the owner's. The verify page fetches their receipts from the public RPC in your browser, decodes the events, and re-asks the contracts their current state. No wallet, no server of ours in between.
What is mocked?
Three things, each labeled MOCK in its name and symbol: the oracle mark (AMZN $412.30), the fixed-rate swap venue, and the USDC the bond is posted in. There is no Stock-Token AMM and no USDC on the testnet. The AMZN, NFLX and PLTR tokens are Robinhood's own faucet Stock Tokens, and the ledger, mandate, guard, router and bond are the real contracts.
Why is the on-chain loss only $24.53?
The faucet hands out 5 of each Stock Token per day, so the on-chain portfolio is the 64-lot shape at 1/128 scale (0.078125 AMZN per lot). The same engine on 10 shares per lot realizes exactly ($3,140.00), pinned in contracts/test/fixtures/expected.json and asserted by forge test and cargo test. Real small numbers on chain, the big number in the deterministic test.
Why 2.87× and not a bigger number?
Because that is what the chain measured. The Stylus program is uncached on this testnet (no ArbOS cache manager yet), so each call pays a WASM start-up floor, and both engines pay the same cold storage reads per lot. Stylus wins on the comparison loop, so the ratio grows with portfolio size: 1.05× at 8 lots, 2.87× at 64, 3.1× at 128.
Can the agent withdraw my tokens?
No. The agent's key can call exactly one function through the router, and the mandate's withdraw path is owner-only. A test pins it: test_owner_can_withdraw_and_the_agent_cannot_stop_it. If the agent signs something the mandate must reject, that signature is enough to slash its bond.
Is there an AI in it?
No. The agent is rule-v1: deterministic HIFO plus the first allowed substitute, with a hash of a plain-text rationale. We say "agent", not "AI". The point is the bound, not the brain: whatever decides, the contracts recompute it.
How do I check it myself?
Open the verify page, or clone the repo and run forge test (81) and cargo test (6) offline, then one read-only cast call against the deployed Stylus ledger. Commands are in DEMO.md.

Don't take the page's word for it. Ask the chain.