Reading chain state
Reading chain state
Documentation
SonixFi accumulates protocol fees and routes them into supported tokenized stock assets. Eligible $SNXFI holders receive pro-rata distributions directly on-chain according to transparent snapshot and distribution rules.
SonixFi is an on-chain tokenized-stock distribution protocol on Robinhood Chain. Protocol fees are paid in ETH to the fee router, forwarded to the treasury, converted into a basket of tokenized stock tokens through allowlisted swap adapters, and distributed to eligible $SNXFI holders through Merkle claims. Every step is a contract call that emits an event.
| Contract | Responsibility |
|---|---|
| SonixToken | Fixed-supply ERC-20 with permit. No minter, no owner. |
| SonixRegistry | Address book of every module; the frontend resolves addresses from it. |
| SonixFeeRouter | Receives fees and forwards them to the treasury in the same transaction. |
| SonixTreasury | Holds ETH, buys allowlisted assets through allowlisted adapters with on-chain slippage, size and deadline limits; forwards assets to the distributor. |
| SonixBasketManager | Asset list, target weights in basis points (enabled set must equal 10 000), max allocation, adapter and pool per asset. |
| SonixSnapshot | Eligibility rules (minimum balance, exclusions, hold duration, cycle interval) and the record of every locked snapshot. |
| SonixDistributor | Merkle-claim distribution with bitmap claim tracking, funding checks and post-deadline sweep. |
| SonixExecutor | Cycle state machine driven by the keeper; captures failed purchases as events instead of aborting the cycle. |
ACCUMULATING → LOCK SNAPSHOT → READ HOLDERS → CALCULATE ALLOCATION → BUY BASKET ASSETS → GENERATE MERKLE TREE → PUBLISH ROOT → CLAIMS OPEN → CYCLE COMPLETE
nextSnapshotTime) is due and the basket is balanced.minBalance are removed; an optional minimum hold duration is enforced against transfer history.minOut must satisfy the on-chain slippage floor and the trade must fit maxTradeAmount and the deadline.allocation = units × walletBalance / totalEligibleBalance. Integer remainders stay in the distributor as dust.Rules live in SonixSnapshot and are read by the interface: minBalance, the exclusion list, minHoldDuration and cycleInterval. The holder page explains, in words, why a wallet is ELIGIBLE, NOT ELIGIBLE, SNAPSHOT PENDING or CONTRACT UNAVAILABLE.
Leaves are keccak256(bytes.concat(keccak256(abi.encode(index, account, amounts)))) with types ["uint256","address","uint256[]"] — the OpenZeppelin StandardMerkleTree encoding — so proofs from @openzeppelin/merkle-tree verify on-chain unchanged. Anyone can download /api/distributions/:id/allocation and recompute the root.
Tokenized stocks on Robinhood Chain trade in Uniswap v4 pools against native ETH (fee 5%, tick spacing 1000, no hook), and in some cases in V2/V3 pools against WETH or USDG. The interface reads v4 state through extsload on the PoolManager and shows the deepest reliable venue as POOL PRICE. USDG-equivalent values go through the WETH/USDG pool. A REFERENCE PRICE appears only when a feed is configured. Thin pools are labelled LOW LIQUIDITY; empty ones PRICE UNRELIABLE.
Every value shows its source and age. Values older than the stale threshold are labelled STALE DATA; missing values are shown as — with the reason.
| Role | Holder | Can |
|---|---|---|
| DEFAULT_ADMIN_ROLE | Multisig | Allowlists, limits, pause/unpause, sweep, abort cycle. |
| TREASURY_MANAGER_ROLE | Multisig | Allowlist assets, route stray tokens. |
| BASKET_MANAGER_ROLE | Multisig | Add/remove assets, set weights. |
| EXECUTOR_ROLE | Executor contract → keeper | Drive the cycle; nothing else. |
| PAUSER_ROLE | Multisig | Pause the treasury and distributor. |
GET /api/protocol chain, contracts, cycle, snapshot, basket, indexer status GET /api/metrics every headline number with source + status GET /api/assets basket with live pool prices GET /api/assets/:address one asset (address or symbol) with venues and history GET /api/treasury balances, allocation, ledger GET /api/distributions paged cycles (filter=ALL|SUCCESS|FAILED|PENDING) GET /api/distributions/:id one cycle with per-wallet allocation GET /api/distributions/:id/allocation full Merkle allocation file GET /api/holders/:address position, eligibility, received, claimable GET /api/claim/:address claimable leaves with verified proofs GET /api/price/:asset pool price of any token, or "snxfi" POST /api/quote ETH → token quote (BigInt exact for V2, single-range for V3/v4) GET /api/health rpc / database / indexer liveness POST /api/rpc same-origin JSON-RPC relay (read methods + sendRawTransaction)
Every endpoint answers { data, source, blockNumber, timestamp, status }.