hackquest logo

Gapwatch

An independent verification layer for Robinhood Chain

视频

项目图片 1
项目图片 2
项目图片 3
项目图片 4

技术栈

Solidity
Python
Next
Arbitrum
Foundry
Stylus
FastAPI
Rust

描述

Overview

Gapwatch is an independent verification layer for corporate actions on Robinhood Chain's tokenized stocks. These tokens use ERC-8056 uiMultiplier for splits and dividends, so a holder's effective balance is the raw balance times the multiplier. If compliance filtering excludes a multiplier update, the transaction leaves no receipt and no logs, and a normal RPC shows nothing, as if it had never been sent. Gapwatch sees each update on the sequencer feed first, checks it against the ArbOS filter precompile and L1, and records the result on chain.

What is not trustless

Gapwatch is a visibility and verification layer, not a guarantee. A feed-level sighting is only a soft confirmation until L1 confirms it. In this submission the three signing nodes are held by the author, and the node set is fixed in V1. Robinhood's filtering logic runs in a private fork, so its exact behavior is inferred from the general Nitro mechanism. Details are in Known limitations below.

The problem

Corporate actions change what a balance means. An app that shows a post-split balance for an update that was later excluded, or misses an update entirely, leaves users and lending logic acting on wrong numbers. Generic indexers cannot flag the excluded case, because a filtered transaction has no receipt to index.

Why Robinhood Chain

Four pieces of the chain make this possible. The sequencer feed (wss://feed.mainnet.chain.robinhood.com) shows an update before L1 confirms it, so there is a gap to verify. The ArbOS filter precompile (0x74, isTransactionFiltered) tells an excluded transaction apart from one that was never sent. As of mid-September the filterer address had made about 6,092 filter calls in roughly six weeks (about 150 a day, from its on-chain transaction count), so filtering is frequent, not rare. The ERC-8056 stock tokens carry the corporate-action mechanism itself, with balanceOfUI = raw balance x uiMultiplier / 1e18. Stylus hosts the 2-of-3 signature check in Rust. We found no clear gas advantage for this small workload, since ecrecover is a precompile either way.

Architecture and data flow

Sequencer feed -> filter (target address and selector) -> filter verifier -> L1 confirmer -> Reference Model -> SQLite -> FastAPI -> frontend.

1. Detected: the updateMultiplier call (selector 0xbad60f18 on mainnet) is decoded from the live feed.

2. Verified: isTransactionFiltered() on the precompile shows whether the transaction was filtered.

3. Confirmed: the L1 UIMultiplierUpdated event matches, and an independent Python Reference Model (SHA-256 sealed) agrees on the result.

Token discovery is automatic. Gapwatch reads the token factory event (204 deployments scanned), monitors the 203 that pass a ticker check, and holds the one that fails (PEACH_DEFI_1) in a review list.

How it uses the chain

The feed connection needs only an Arbitrum-Feed-Client-Version header and no API key. GapwatchRegistryV2 requires 2-of-3 node signatures for recordVerification, resolveChallenge and reportDiscrepancy. Each function signs its own domain-separated digest, and tests cover replay across functions, contracts and chains. The Stylus ConsensusVerifier recovers signers, rejects malleable (high-S) signatures and counts each signer once. Anyone can dispute a record by staking a bond (0.0005 ETH, 24 hour window). The challenge window is capped at 365 days, because a self-audit found that an unbounded value would lock all bonds. MockLendingPool, a demo consumer, pauses liquidation when a discrepancy is reported.

Verify it yourself in five minutes

1. Open the live app and look up 0xd4eb21209c4d6093f80b5b84f5c45cc093ea14a3 to compare raw and effective NVDA balance.

2. Open the Audit Log. Backfilled events are labeled as replayed, and the whole log can be downloaded as JSON or CSV.

3. On Robinhood Chain mainnet, call getVerification(0x4ac23f2e58e2c4962dcd701c2beff581e87f3995152a29d527c07a3afd67d956) on GapwatchRegistryV2.

4. Clone the repo and run forge test in contracts/. All 104 tests pass.

Deployed contracts

Robinhood Chain mainnet (chainId 4663)

GapwatchRegistryV2: 0x88395FE9Ce32494C9a054F794bE6EbaE5Ec9203d

ConsensusVerifier (Stylus/Rust): 0x6A7061D3f754BB594ed5092815Ae9bD71DD185b7

MockLendingPool (demo): 0x0118058C907797e37176cE1b8B5c088F4ea515d8

Robinhood Chain Testnet (chainId 46630)

GapwatchRegistry: 0x53f10f96e3F6443e67Af2F1b01144B7e325f006d

MockLendingPool (demo): 0xF764f545B4e6fF8755EEDEE64A0CFCf2Ec08a671

Some of these addresses match contracts from an unrelated project on another chain (same deployer nonce), so read each address with its chain.

Known limitations

- The automated detection pipeline writes to the testnet V1 registry. Mainnet V2 writes are human-signed on purpose, to keep node keys off the server.

- The feed listener's continuous run time is still short, so its stability has been observed only over a limited period.

- The multiplier-updater role holder is confirmed as authorized by its behavior, but not confirmed as an official Robinhood address.

- The node set is fixed in V1 and has no governance path for replacing a node.

- The NVDA mainnet record was written on 09-18 with an earlier Reference Model hash formula. It is immutable, so recomputing its hash with the current code will not match (documented in the README).

- MockLendingPool has no unpause path, and a newer clean record supersedes an older discrepancy flag. It is a demo consumer only.

- Three Rust unit tests are ignored because of a stylus-sdk TestVM mock limitation. The same paths were checked with real on-chain calls and a Solidity mirror.

What was built

- A detection pipeline on a VPS, with SQLite and a FastAPI backend

- The Registry with challenge bond, GapwatchRegistryV2 with 2-of-3 consensus, the Stylus/Rust ConsensusVerifier and the demo MockLendingPool

- 104/104 Foundry tests, 7/7 events independently recomputed by the Reference Model with zero mismatches, and three rounds of self-audit

- The first real 2-of-3 mainnet record for a real NVDA multiplier event, plus six backfilled events (SOXX, TSM, PR, HPE, CRM, SPY)

- As of 09-29, 20 corporate-action events recorded: 13 caught live by the feed listener and 7 replayed manually (six backfilled events plus NVDA)

- A frontend with landing page, balance lookup, live event monitor and audit log

Roadmap (not built)

- A governance path for replacing a signing node.

- Automated mainnet writes that do not require node keys on a server.

本次黑客松进展

Built from scratch during the buildathon (new repo, 09-14 to 10-04).

- A detection pipeline (sequencer feed listener, filter engine, filter verifier, L1 confirmer, Reference Model, SQLite and FastAPI) runs continuously on a VPS.

- Token discovery reads the factory event: 204 deployments scanned, 203 tokens monitored, 1 held for review. Six real historical events were backfilled (SOXX, TSM, PR, HPE, CRM, SPY).

- The contracts are a Registry with challenge bond, GapwatchRegistryV2 with 2-of-3 consensus, a Stylus/Rust ConsensusVerifier and a demo MockLendingPool. They pass 104/104 Foundry tests and 7/7 sealed reference-model checks, and went through three rounds of self-audit.

- On mainnet, V2, ConsensusVerifier and MockLendingPool are deployed. A real 2-of-3 record was written for the NVDA multiplier event (tx 0x17039804fb90dd5f6633080225bc2ff0ad080df6854377b83909b5d5da317697), and every mainnet write has human sign-off.

- The frontend (landing page, balance lookup, live monitor, audit log) and the demo video are shipped.

融资状态

No funding raised. Self-funded solo developer project.

队长
TTSUNG YU CHIU
项目链接
部署生态
Arbitrum OneArbitrum One
赛道
InfraDeFiRWA