How this works

The mechanism, the event, the one threshold that separates a dividend from a split, and the contract that can freeze all of it.

A tokenised stock never pays you anything

The stock tokens on Robinhood Chain are ERC-20s with an ERC-8056 multiplier bolted on. Your raw balance is fixed. When the underlying pays a dividend or splits, the token does not mint, airdrop or transfer anything — it raises a single number, and every balance is reinterpreted through it:

what you actually own
shares = balanceOf(you) * uiMultiplier() / 1e18

That is the whole mechanism, and it has an awkward consequence: a dividend and a 4-for-1 split are the same write to the same storage slot. Nothing on chain labels which one happened. Decoding those writes is what Dolphin Tools does.

The event

UIMultiplierUpdated
event UIMultiplierUpdated(
  uint256 oldMultiplier,
  uint256 newMultiplier,
  uint256 effectiveAt
);

topic0 = 0x2205df4534432b2f60654a3fdb48737ffdaf3e9edb1a498bd985bc026b15b055

All three fields are unindexed, so the topic filter is the only filter, and one eth_getLogs over the full range returns every corporate action that has ever happened on this chain. That call currently returns 18 logs.

Telling a dividend from a split

classification
ratio = newMultiplier / oldMultiplier

ratio  < 1                    -> reverse    (reverse split, or an action undone)
ratio == 1                    -> noop
ratio  > 1, delta <= 300 bps  -> dividend   (reinvested distribution)
ratio  > 1, delta >  300 bps  -> split

The 3% line is not a guess and not a close call. Across the whole history of the chain the largest dividend is +2.1486% and the smallest split is +100% — a gap of more than a factor of forty. If an action ever lands inside that gap, the classification is unsafe and this page will say so rather than quietly pick a side.

The threshold biases towards calling something a split. A split is absorbed by the holder; a dividend is a distribution. Anything downstream that routes money should treat dividend as a claim and split as a restatement of the same claim.

Restatements and reversals

Two events with the same token, same oldMultiplier, same newMultiplier and same effectiveAt are one corporate action announced twice. Both are kept — the log is what the chain said — and the earlier one points at the later through supersededBy. There are 2 such pairs. Deduplicate on supersededBy === null for distinct actions; there are 16 of those.

One action was reversed: WEEK doubled and then halved back fifteen minutes later on 23 June 2026. It is in the feed as a split followed by a reverse, because that is what happened.

The registry

Every token is a BeaconProxy over one implementation, and the beacon is also the roles registry and the blocklist. That is why eco risk exists: one contract can rewrite, halt or freeze all of them, and it has used two of those three powers already — 2 upgrades and 1 chain-wide halt, plus 175 addresses on the blocklist.

the registry's whole log
curl -s -X POST https://rpc.mainnet.chain.robinhood.com \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getLogs","params":[{
        "fromBlock":"0x0","toBlock":"latest",
        "address":"0xe10b6f6B275de231345c20D14Ab812db62151b00"}]}' | jq '.result | length'

Replay

The feed is ordered by (blockNumber, logIndex) and nothing else. Replaying it from the start, taking each event's newMultiplier as the new state, lands exactly on what uiMultiplier() returns today for all 11 tokens that have ever moved. The indexer asserts it on every run and exits non-zero if it fails, so a build that cannot reproduce chain state does not ship. Action ids are <txHash>:<logIndex> and are stable across re-indexes.

Schema

one action
{
  "id": "0x17717969d77a298b876c0c3c735b6367ee1f75e1906f67953a6a30dc35cc442e:72",
  "token": {
    "symbol": "F",
    "name": "Ford Motor • Robinhood Token",
    "address": "0x25C288E6D899b9BC30160965aD9644c67e73bE0C"
  },
  "kind": "dividend",
  "oldMultiplier": "1.000000000000000000",
  "newMultiplier": "1.000145502866134027",
  "ratio": "1.000145502866134027",
  "deltaPct": "0.014550",
  "effectiveAt": "2026-09-02T15:10:26.000Z",
  "announcedAt": "2026-09-02T15:00:41.000Z",
  "noticeSeconds": 585,
  "blockNumber": 52665452,
  "logIndex": 72,
  "txHash": "0x17717969d77a298b876c0c3c735b6367ee1f75e1906f67953a6a30dc35cc442e",
  "supersededBy": null
}

API

No key, no auth, no rate limit, CORS open to everything.

endpoints
GET /api/actions            every action, newest first
GET /api/actions?token=SGOV filter by symbol
GET /api/actions?kind=split filter by kind (dividend | split | reverse)
GET /api/actions?limit=5    cap the result
GET /api/tokens             every tracked token and its current multiplier
GET /api/summary            the counts on the landing page

GET /data/feed.json         the whole thing, as the indexer wrote it
example
curl -s https://echocalls.vercel.app/api/actions?token=SGOV | jq '.actions[0]'

What this is not

It deploys no contracts and holds nothing, so there is nothing to audit and no counterparty. It does not price anything and knows nothing the chain does not already say out loud. Everything it publishes is recomputable — see verify.