Review, H200 weekly market, NEAR testnet

H200 weekly · series 0Round 105, commit ends in Open the auction
Review

All three price reporters are run by one operator today, who can set any price. Nothing on chain checks a report against its data.

An AI reviewer reviewed the contract in 4 passes, against specifications 0.3, 0.4, 0.5 and 0.6. Every pass found the dollar accounting exact. Below are its findings as the final record states them (the review record, “Every finding, status under 0.6”): closed by a rule, open, and open but stated by the specification as limits it accepts. Numbers are at the deployed weekly preset. The full record is here.

Verdict, from the record
  • Weir 0.6 is fit for a public testnet with test dollars, with its reporters run by one known operator and these limits stated where users will read them. Its accounting stayed exact through four passes, and the void rule now in force cannot be moved by traders.
  • It is not fit for real dollars until, at least: the reporters are independent parties, numerous enough that one absence does not hand every bucket to a two-reporter veto; blocking a round costs something that scales with what it blocks; max_round_lock is on by default; and the contract's NEAR balance and pruning have a funded owner.
  • One fix in this review's history was itself found faulty: the 0.5 void rule. Any change made to meet the conditions above needs its own review.

What you are trusting today

The record’s “Residual risks before real money (0.6)”, as written in the review record. Its verdict makes this testnet fit only with these stated where users read them.

  • Reporters decide the fixing.
    • All three are run by one operator, who can set any price.
    • A single compromised reporter key, while another is absent, can lean and select prints (about 0.029 $ a unit, 2.1).
    • It can also force a void that pays its cheap side up to 0.299 $ a unit (3.2).
    • Nothing on chain checks a report against its published data.
  • A round can be blocked for 6.40 $, a week of rounds for about 1,075 $.
  • Agent keys: bounded only if the owner sets both guard and max_round_lock. Both are off by default.
  • Parameters: new accepts parameters that remove protections. Read get_params.
  • Storage: relies on someone pruning and sending NEAR. Nobody is paid to do either. A low balance stops reports, and with them settlement turns void.
  • No mainnet history and no upgrade path. A bug means a new contract.

Open

The weakness exists in the deployed contract today. A limit_ test passing means the weakness happens.
#FindingWhat remains, with its numberSpecTest
2.1
High
With one reporter absent, one dishonest reporter chooses which buckets print and leans each print. About 0.029 $ a unit, about 4.8% of the band: up to 1% lean per print (about 0.020 $) plus up to 42 of 168 hours dropped (about 0.009 $ at an assumed 1% hourly spread of the index; the spread was assumed, not measured).
The test shows the mechanism with ±5% hourly swings: a fixing of 2.121 against a true 2.000.
SPEC 9, 15 · limit_one_reporter_selects_which_buckets_print
9, 15limit_one_reporter_selects_which_buckets_print
3.2
High
A reporter who holds the cheap side and forces a void is paid half the band for it. Up to 0.299 $ a unit, 49.8% of the band, over the price paid; up to 0.30 $ (50%) against the true settlement. It needs 43 of 168 buckets without a print.
SPEC 10, 15 · limit_a_trading_reporter_forces_a_void_for_up_to_0_299_a_unit_at_the_deployed_bandlimit_forcing_a_void_pays_a_reporter_half_the_band_for_a_cheap_side
10, 15limit_a_trading_reporter_forces_a_void_for_up_to_0_299_a_unit_at_the_deployed_bandlimit_forcing_a_void_pays_a_reporter_half_the_band_for_a_cheap_side
2.2
High
Forcing a void paid mid-band (specification 0.4). Replaced in 0.5 by the traded-average rule (3.1), then by the centre-of-band rule in 0.6. Open as a reporter gain: 3.2.
SPEC 10 · limit_forcing_a_void_pays_a_reporter_half_the_band_for_a_cheap_side
10limit_forcing_a_void_pays_a_reporter_half_the_band_for_a_cheap_side
2.3
High
Blocking a round costs max_commits × commit_fee. (1.5, eviction was free, is open as this.) 6.40 $ a round at the 0.10 $ commit fee, about 1,075 $ a week of 168 rounds, plus capital above the largest honest lock.
At this market's parameters: 64 slots × $0.10 = $6.40 a round, $1,075.20 for all 168 rounds of a series.
SPEC 15 · limit_blocking_a_round_costs_6_40_dollars_at_the_deployed_feeaudit_eviction_griefing_now_costs_a_fee_per_slot
15limit_blocking_a_round_costs_6_40_dollars_at_the_deployed_feeaudit_eviction_griefing_now_costs_a_fee_per_slot
2.8
Low
A large grace leaves many pending buckets in the root state, and settle decides them all in one call. Recoverable through finalize_print. Open.
SPEC 9, 10 · none: found by reading, not tested
9, 10none: found by reading, not tested
2.9
Low
prune_print can delete delivery reports before a late settlement. Open.
SPEC 9 · none: found by reading, not tested
9none: found by reading, not tested
2.10
Note
The guard reference has no age check and lags 1 to 2 intervals. Open.
SPEC 6.4 · none: found by reading, not tested
6.4none: found by reading, not tested
2.11
Note
get_prints scans 4096 words and can miss prints after pruning (a view only). Open.
SPEC 12 · none: found by reading, not tested
12none: found by reading, not tested
4.1
Low
A non-reporter can cause a void only indirectly: by exhausting the contract's NEAR so that reports fail. Open. Expensive unless the balance has been allowed to run low: about 6.40 $ of fees for 8 KB of book storage.
SPEC 14, 15 · none: found by reading, not tested
14, 15none: found by reading, not tested

Open, stated as limits in the specification

The specification states these and accepts them. Each test shows what is possible.
#FindingWhat remains, with its numberSpecTest
2.4
Medium
With max_round_lock unset (the default), an agent burns 1% plus the fee per round. 100 $ became 90.25 $ in 10 rounds. The deployment tool sets the limit for the agents it creates.
SPEC 15 · limit_an_account_without_a_round_lock_limit_can_be_burned
15limit_an_account_without_a_round_lock_limit_can_be_burned
2.5
Medium
Not revealing changes others' fills, which gives a last look. A look costs 1% of the lock plus 0.10 $. Invariant 6 now reads "burned stays burned".
SPEC 11, 15 · limit_last_look_a_non_reveal_changes_another_accounts_fill
11, 15limit_last_look_a_non_reveal_changes_another_accounts_fill
2.6
Low
On testnet, one account can take the whole mint cap. The next minter is refused; auctions still clear.
SPEC 8 · limit_one_account_can_take_the_whole_mint_cap
8limit_one_account_can_take_the_whole_mint_cap
2.7
Medium
Unwise parameters are still accepted. A 100% spread, 2 reporters, no fee with no forfeit, and grace equal to the period are accepted. Read get_params before trusting a deployment.
SPEC 3, 15 · limit_unwise_parameters_are_still_accepted
3, 15limit_unwise_parameters_are_still_accepted

Closed by a rule

Each audit test keeps the reviewer's attack and asserts that it now fails.
#FindingRule that closes itSpecTest
1.1
Critical
One reporter ratcheted the index through the two-report mean. No move rule, and a full set of reports gives the median.
SPEC 9 · audit_one_reporter_can_no_longer_steer_the_index
9audit_one_reporter_can_no_longer_steer_the_index
1.2
High
One silent reporter froze the index after a large move. No move limit.
SPEC 9 · audit_a_silent_reporter_can_no_longer_freeze_the_index
9audit_a_silent_reporter_can_no_longer_freeze_the_index
1.3
Critical on testnet
Minting u64::MAX sets made clear overflow and froze the series. The mint cap.
SPEC 8 · audit_open_sets_can_no_longer_overflow
8audit_open_sets_can_no_longer_overflow
1.4
High
A function-call agent forfeited an account's balance to an accomplice.Partly closed Closed for theft: forfeits are burned. The burning that remains is 2.4, below.
SPEC 7.3 · audit_function_call_agent_can_no_longer_drain_a_guarded_account
7.3audit_function_call_agent_can_no_longer_drain_a_guarded_account
1.6
Medium
Changing the guard after commit cancelled an order for free. The commit records the guard and its reference.
SPEC 6.2, 6.4 · audit_guard_change_after_commit_no_longer_cancels
6.2, 6.4audit_guard_change_after_commit_no_longer_cancels
1.7
Medium
Out-of-order reports bypassed the move limit. Nothing compares prints.
SPEC 9 · audit_reporting_out_of_order_no_longer_bypasses_anything
9audit_reporting_out_of_order_no_longer_bypasses_anything
1.8
Medium
Storage the contract pays for grew without bound.Partly closed Results keep only fills, and anyone may prune. Still open: nobody is paid to prune or to send NEAR.
SPEC 14, 15 · audit_storage_bytes_per_object_and_pruning
14, 15audit_storage_bytes_per_object_and_pruning
1.9
Note
Missing parameter checks, a missing event, the bonus pool, an unchecked add in the mock.Partly closed Closed, except some parameter checks (2.7, below).
SPEC 3 · none
3none
2.7
Medium
Dangerous parameters were accepted.Partly closed A quorum below a majority is now refused. The rest is still accepted: see 2.7 under the stated limits.
SPEC 3 · audit2_a_quorum_of_one_in_three_is_refused
3audit2_a_quorum_of_one_in_three_is_refused
3.1
Critical
The 0.5 void payout, the traded average, was set by wash trades: a trading reporter took 19.40 $ of a counterparty's 20 $. Specification 0.6: a void pays band/2 on each side, and no rule reads the traded fields.
SPEC 10 · audit3_wash_trades_no_longer_set_the_void_payoutaudit4_a_non_reporter_cannot_move_the_void_payout
10audit3_wash_trades_no_longer_set_the_void_payoutaudit4_a_non_reporter_cannot_move_the_void_payout

Also examined and found sound, by reading: the dollar accounting on every path in all four passes, including the 0.6 void path; clearing; the commit hash; access controls; and that a pruned bucket cannot come back (audit2_a_pruned_bucket_cannot_come_back). The list is in the record.

A fix that was itself found faulty

The record’s history, one pass at a time.
PassSpecWhat it found
10.3One reporter could ratchet the index and one could freeze it; clear could overflow; forfeits could be steered to an accomplice; eviction was free; the guard could be changed after commit. Specification 0.4 closed most of these.
20.4With one reporter absent, a single dishonest reporter could force a void settlement at mid-band. Specification 0.5 replaced that rule.
30.5The 0.5 rule paid a void series at the average price its own auctions had traded at. That fix was faulty: wash trades between two accounts of one person, in a round they alone used, set the average anywhere in the band for the price of commit fees. A trading reporter took 19.40 $ of a counterparty's 20 $.
40.6Specification 0.6 withdrew the 0.5 rule and pays a void series half the band on each side. The pass found that rule sound against traders who are not reporters: wash trades, mints, merges and transfers moved the payout by nothing. What remains is a reporter's gain (2.1, 3.2) and an indirect path through the contract's NEAR balance (4.1).
Who reviewed it, and what it was not
  • An AI reviewer (Claude, an Anthropic model) that did not write the contract. It is not an audit by a security firm and not a formal verification; no person has signed it off. This page makes no claim that the contract is secure.
  • In scope: the Weir contract, against the specification. Out of scope: the TypeScript packages and this site, the reporters’ code and data sources, any real token, NEAR beyond what near-sdk 5.30.1 documents, the behaviour of real participants, deployment and key management.
  • The reviewer’s tests run in near-sdk’s mocked VM; the real wasm was not run for the review, and gas was measured only for clear. A finding that is absent means only that it was not found.

Contract against the engine

Run by the authors on a local test node

Third run, on specification 0.6, 2026-11-12: 264 of 264 checks passed. The contract’s wasm and the TypeScript model agreed on 1,315 state changes, 2,315 views and 61 refusals. It ran on one local near-sandbox node (near-sandbox 2.14.0), not on a public network, so it says nothing about latency, RPC limits, the real USDC or live index sources. The authors ran it, not the reviewer.