Documentation
Integration.
Everything the interface needs to run on real data, and exactly what it does when it can't.
- Data mode
- onchain
- Registry
- set
- Env errors
- 0
Environment
Every network-facing value is read once in lib/chain/config.ts and validated with zod. Components never read process.env.
| Variable | Current | Required | Notes |
|---|---|---|---|
| NEXT_PUBLIC_RPC_URL | https://rpc.mainnet.chain.robinhood.com | no | JSON-RPC endpoint. Defaults to the public Robinhood Chain mainnet RPC. |
| NEXT_PUBLIC_CHAIN_ID | 4663 | no | EVM chain id. 4663 mainnet · 46630 testnet. |
| NEXT_PUBLIC_EXPLORER_URL | https://robinhoodchain.blockscout.com | no | Blockscout origin used for address / tx / block links. |
| NEXT_PUBLIC_QIRO_REGISTRY_ADDRESS | 0x78B36f1141Be113459a1640b4F92Fe63eEb87576 | yes | QiroRegistry address (contracts/src/QiroRegistry.sol). Unset → labelled demo data. |
| NEXT_PUBLIC_QIRO_SETTLEMENT_ADDRESS | unset | no | Separate settlement contract. Unset → the registry’s own settle()/getSettlement() are used. |
| NEXT_PUBLIC_QIRO_DEPLOY_BLOCK | 48200286 | no | Registry deploy block — narrows provenance log scans. |
| NEXT_PUBLIC_REFERENCE_API_URL | /api | no | Reference service origin. Defaults to this app’s /api (app/api/references/[id]), which resolves yahoo:<SYMBOL> locators. |
| NEXT_PUBLIC_AGE_FRESH_SECONDS | 300 | no | FRESH upper bound (seconds). |
| NEXT_PUBLIC_AGE_AGING_SECONDS | 1800 | no | AGING upper bound / STALE lower bound (seconds). |
| NEXT_PUBLIC_ALIGNMENT_TOLERANCE_BPS | 10 | no | Max claim/reference value difference for ALIGNED. |
Copy .env.example to .env.local. NEXT_PUBLIC_* values are inlined at build time — rebuild after changing them.
Registry interface
contracts/src/QiroRegistry.sol — compiled with solc 0.8.24 (paris) by `npm run compile` in contracts/, tested with Hardhat in contracts/hh. The generated ABI is lib/contracts/abis/qiro-registry.ts; normalisers live in lib/contracts/registry.ts, writes in lib/contracts/writes.ts. Deploy + seed from your own wallet with public/deploy.html (served locally at /deploy.html).
One contract carries both surfaces: the registry (claims, references) and settlement (settle / getSettlement). Reserve references are computed live from an ERC-20 balance; external references store a locator that the interface resolves through the adapter for their source.
| Function | Returns | Fields |
|---|---|---|
| registerClaim(ClaimInput) | bytes32 | Permissionless. msg.sender becomes the issuer; timestamp/block are taken from the chain. registerClaims(ClaimInput[]) batches. |
| updateClaim(bytes32, value, quantity, state) | — | Issuer only. Posts a new observation of the claim. |
| registerReference(label, source, sourceName, asset, valueDecimals, locator) | bytes32 | External reference (api/oracle/manual) with an off-chain locator such as yahoo:AAPL. registerReferences([...]) batches. |
| registerReserveReference(label, sourceName, asset, token, holder) | bytes32 | Reserve reference: the live ERC-20 balance of holder — computed on every read, never stale. |
| settle(bytes32 claimId) | bool | Permissionless on-chain settlement for reserve-backed claims: compares claim value with the live balance within toleranceBps; moves state to settled / disputed. |
| claimCount() · claimIdAt(i) · referenceCount() · referenceIdAt(i) | uint256 · bytes32 | Enumeration; the UI lists the newest 200. |
| getClaim(bytes32) | Claim | id · label · issuer · asset · assetSymbol · assetName · assetType · value · valueDecimals · quantity · quantityDecimals · timestamp · blockNumber · referenceId · state |
| getReference(bytes32) · referenceLocator(bytes32) · reserveOf(bytes32) | Reference · string · (token, holder) | id · label · source · sourceName · asset · value · valueDecimals · timestamp · blockNumber · status |
| getSettlement(bytes32) | Settlement | expectedValue · referenceValue · valueDecimals · state · settledAt |
Events (provenance timeline)
- ClaimCreated(bytes32 claimId, address issuer, address asset, uint256 value, bytes32 referenceId)
- ClaimUpdated(bytes32 claimId, uint256 value, uint256 quantity, uint8 state)
- ReferenceRegistered(bytes32 referenceId, address owner, uint8 source, string locator)
- ReferenceLinked(bytes32 claimId, bytes32 referenceId)
- ReferenceUpdated(bytes32 referenceId, uint256 value, uint64 timestamp)
- SettlementAligned(bytes32 claimId, bytes32 referenceId, uint256 expectedValue, uint256 referenceValue)
- SettlementMismatch(bytes32 claimId, bytes32 referenceId, uint256 expectedValue, uint256 referenceValue)
- SettlementStateChanged(bytes32 claimId, uint8 state)
Enums — assetType 0 cash-equivalent · 1 treasury · 2 equity · 3 commodity · 4 credit · 5 fund. state 0 pending · 1 active · 2 settled · 3 disputed. source 0 onchain · 1 api · 2 oracle · 3 manual. status 0 unknown · 1 available · 2 unavailable. Human ids (qUSD-04291) map to bytes32 as right-padded UTF-8, or keccak256 when longer than 32 bytes.
Writing from the interface
/claims/new registers a claim from the connected wallet: asset metadata and quantity are read live from the ERC-20 (totalSupply), the value from the live reference at issuance. Claim pages expose settle() for reserve-backed claims and issuer-only state changes. Every write is simulated first so reverts surface before the wallet prompt.
Reference adapters
lib/references/{onchain,api,oracle,manual}.ts. Each adapter resolves a locator into the same Reference shape or reports an explicit failure: not-configured · not-found · unavailable · malformed · error.
- onchainconfigured
Registry (on-chain)
- apiconfigured
Reference API (HTTPS)
- oracleconfigured
Oracle feed (AggregatorV3)
- manualnot configured
Manual attestation
With a registry configured the registry record is authoritative: reserve references come straight from the chain; external ones are fetched through the adapter named by their source using the stored locator (api → yahoo:SYMBOL via the reference route; oracle → an AggregatorV3 feed address), falling back to the last observation the reference owner posted on-chain. Without a registry the adapters are walked in order onchain → api → manual.
Reference API payload
GET {NEXT_PUBLIC_REFERENCE_API_URL}/references/{id}?locator=yahoo:AAPL
{
"referenceId": "PX-AAPL",
"source": "Yahoo Finance",
"asset": "AAPL",
"value": "314.5800", // string | number | null
"currency": "USD",
"decimals": 4,
"timestamp": 1787860801, // the exchange's regularMarketTime (unix seconds | ISO-8601 | null)
"blockNumber": null,
"transactionHash": null,
"status": "available", // available | unavailable | unknown
"basis": "unit", // unit → multiplied by the claim quantity · total → compared directly
"metadata": { "exchange": "NasdaqGS", "marketState": "CLOSED", "locator": "yahoo:AAPL" }
}Validated with zod; anything that does not match is REFERENCE UNAVAILABLE (malformed), never a guessed value.
Age engine
age = now − last observation, computed on one shared 1s clock (hooks/useNow.ts). A missing timestamp is AGE UNKNOWN, not 0.
Formats — human 18s · 02m 41s · 1h 18m · 2d 04h; clock hh:mm:ss. AGE DELTA = |claim age − reference age| with direction (claim older / reference older / in sync). Requires both timestamps.
Alignment
lib/settlement/alignment.ts. Evaluated in order: no reference → REFERENCE UNAVAILABLE · unknown reference timestamp → UNKNOWN · claim pending → PENDING · reference stale → STALE REFERENCE · missing value / currency mismatch → UNKNOWN · |Δ| > 10 bps → VALUE MISMATCH · disputed → PENDING · otherwise ALIGNED.
Money is compared as fixed-point bigint scaled to the larger decimals; no floating point until display.
Demo isolation
lib/data/demo.ts is the only file that knows demo records exist. It implements the same ClaimSource interface as lib/data/onchain.ts and is selected in lib/data/source.ts purely on whether the registry address is set.
- · Every demo record carries origin: 'demo' and renders a DEMO badge.
- · Demo block numbers and addresses never link to the explorer.
- · Demo timestamps are offsets from first load, so ages are real elapsed time — the feed does not fabricate new activity.
- · Chain lookups (tx hash, address) and the network indicator always hit the live RPC, even in demo mode.
Trust rule
Qiro never fabricates financial verification.
- Reference cannot be fetched → REFERENCE UNAVAILABLE
- Timestamp cannot be determined → AGE UNKNOWN
- Settlement cannot be verified → UNVERIFIED / UNKNOWN
- Data is not from a real source → DEMO