Stock tokens here apply dividends and stock splits as changes to one multiplier, with no transfer and no event you can act on. The Cleave ledger is one small contract per token that watches that number, classifies every change as one or the other, and refuses anything that fits neither. Everything in the table below is read from Robinhood Chain as this page loads. Any contract can read the same thing.
| Token | Live multiplier | Last change | Scheduled | Supply on chain | Dividend index | Split factor | Ledger |
|---|---|---|---|---|---|---|---|
| Reading Robinhood Chain… | |||||||
Multiplier, last change, schedule and supply come from the stock token itself. Dividend index, split factor and status come from its Cleave ledger once one is deployed. SYNCED: the index is current. PENDING: a change is recorded but not yet classified, treat the index as stale. HELD: it fits neither band and the guardian is resolving it in public. NOT COVERED: no ledger yet.
A dividend is a multiplier rise of up to 3%. A split is a clean integer ratio at least 20% from 1. The bands cannot overlap, so the keeper cannot tag a dividend as a split or a split as a dividend. The contract re-checks every tag.
A change that fits neither band is held, not classified. isSynced() stays false until the guardian resolves it through a two-day public delay. A market reading that flag has a circuit breaker it did not have to build.
The token schedules its next multiplier with an effectiveAt timestamp. The ledger surfaces that schedule, so a value-changing event is visible before it lands rather than after.
// DividendLedger, one per stock token. All views, no fees, no key. function isSynced() returns (bool) // false while a change awaits classification function dividendIndex() returns (uint256) // cumulative dividend growth, 1e18 = 1.0 function splitFactor() returns (uint256) // cumulative split ratio, 1e18 = 1.0 function dividendIndexAt(uint256 ts) returns (uint256) // the index as of a past timestamp function checkpointCount() returns (uint256) function checkpointAt(uint256 i) returns (uint64 timestamp, uint192 index) function pending() returns (bool exists, bool collapsed, uint64 ts, uint256 oldM, uint256 newM) function scheduled() returns (uint256 newMultiplier, uint256 effectiveAt) // Collateral value that survives a stock split: // value = rawBalance * feed // Chainlink Robinhood feeds price the token, multiplier inside // value = rawBalance * uiMultiplier() * pricePerShare // a per-share feed needs the multiplier // and pause the market while isSynced() is false.
The issuer sets newUIMultiplier() and effectiveAt() days ahead; the ledger shows SCHEDULED. When effectiveAt passes, the token's multiplier flips on the next read and isSynced() returns false in the same block, because it compares the live value to the last classified one every call. A correctly wired market is never open inside the window: no liquidations, no new borrows, no collateral withdrawals until the keeper classifies. For a dividend, resume. For a split, resume once your price feed reflects the new share count.
Anything outside both bands cannot be tagged by the keeper. The guardian proposes an ordered decomposition (for example, a 2:1 split then a 0.5% dividend); its hash is committed on chain with an execution time two days out, and it only executes if its product reproduces the observed change. Holders can exit through merge the whole time; it never depends on the index. Every classification is a checkpoint, readable forever.
The contract validates every tag, so a keeper fault can cost gas but cannot move value. A token can have a ledger without having a series, so coverage does not depend on there being a market for it.