CRIUS
PricesRobinhood ChainExplainerMethodology

How Robinhood Chain Stock Token Multipliers Affect Price Data

Robinhood stock tokens use an ERC-8056 uiMultiplier for splits and dividends. What it does to prices, balances and charts, and how to convert correctly.

Crius 9 min read

Short answer: Robinhood stock tokens never change balances for a stock split or a reinvested dividend; instead each token has a uiMultiplier() (18-decimal fixed point, ERC-8056) that says how many underlying shares one token represents, so shares = balance × uiMultiplier and price per share = on-chain price per token ÷ uiMultiplier. DEX prices, trades and balances on Robinhood Chain are all per token, so whenever a multiplier is not 1, you must divide prices (or multiply balances) by it to get share-based numbers, and must not apply it again to oracle prices, which already include it.

This is the detail behind most “the on-chain price is wrong” reports about stock tokens. Take CrowdStrike: its token trades around $984, while CrowdStrike shares trade around $246. Both are right. This article explains the mechanism from the contract up, shows every multiplier change on Robinhood Chain so far, and gives code to convert correctly. Everything below was read from the chain and the Crius API in October 2026.

The mechanism: ERC-8056 Scaled UI Amount

Splitting a token the obvious way, by minting 3 more tokens for every holder in a 4-for-1 split, is expensive and breaks every pool and contract holding the token. Rebasing (changing balances silently) breaks them worse. ERC-8056, a draft standard co-authored by people from Robinhood and Superstate, takes a third route:

  • Balances and total supply never change for a corporate action. Transfers, approvals and pools keep working on raw token amounts.
  • The token exposes uiMultiplier(), an 18-decimal fixed-point number (1e18 = 1.0). The displayed amount is raw amount × uiMultiplier / 1e18.
  • When a split or reinvested dividend happens, the issuer updates the multiplier, with an effective timestamp, and emits an event.

For a stock token, “displayed amount” means underlying shares. Robinhood’s documentation states it directly: shares equal the raw token amount times uiMultiplier divided by 1e18, and every token launched at a multiplier of exactly 1e18.

What the contract actually exposes

We checked the TSLA token directly against Robinhood Chain rather than relying on the spec. TSLA is a beacon proxy (beacon 0xe10b…1b00, which the token also reports as its ACCESS_CONTROLLED_REGISTRY), and the implementation behind it (0xb354…5ae2) contains these multiplier-related functions:

Function Purpose
uiMultiplier() Current multiplier (1e18 = 1.0)
newUIMultiplier(), effectiveAt() A scheduled change and when it takes effect
updateMultiplier(uint256), updateMultiplier(uint256,uint256) Issuer-only setters (immediate, or with an effective time)
balanceOfUI(address), totalSupplyUI() Balance and supply already scaled to shares
oraclePaused(), pauseOracle(), unpauseOracle() Oracle pause around corporate actions

and these events:

event UIMultiplierUpdated(uint256 oldMultiplier, uint256 newMultiplier, uint256 effectiveAt);
event TransferWithScaledUI(address from, address to, uint256 amount, uint256 uiAmount);
event OraclePaused();
event OracleUnpaused();

The parameter names are ours, inferred from the on-chain values (old value, new value, unix time), which line up exactly with the corporate actions below. Live reads for TSLA:

Terminal window
cast call 0x322f0929c4625ed5bad873c95208d54e1c003b2d "uiMultiplier()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com
# 1000000000000000000 [1e18] (1.0)
cast call 0x322f0929c4625ed5bad873c95208d54e1c003b2d "effectiveAt()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com
# 0 (no change scheduled)

Because every Robinhood stock token shares this implementation, the same calls work on all of them.

Every multiplier change so far

We scanned Robinhood Chain from block 0 to block 96,715,759 for UIMultiplierUpdated. There were 62 updates across 50 Robinhood stock tokens. They fall into two clear groups.

Stock splits: big jumps. CrowdStrike (CRWD) went from 1.0 to 4.0, effective 2026-07-02 at 13:30 UTC, which is the US market open on the day CrowdStrike’s four-for-one split took effect, as disclosed in its 10-Q. The update was announced twice (blocks 978,630 and 1,231,096) with the same values and time.

Reinvested dividends: small steps. Most updates move the multiplier slightly above 1, consistent with dividends being reinvested into additional shares:

Token Multiplier now Updates Latest effective
Carnival (CCL) 1.021486 1 2026-08-31
iShares 0-3 Month Treasury (SGOV) 1.007187 4 (monthly) 2026-10-01
Schwab US Dividend Equity (SCHD) 1.005539 1 2026-09-24
Oracle (ORCL) 1.004763 2 2026-10-09
SPDR S&P 500 (SPY) 1.001718 1 2026-09-18
NVIDIA (NVDA) 1.000775 1 2026-09-10
Apple (AAPL) 1.000566 1 2026-08-14
Microsoft (MSFT) 1.000413 1 2026-09-11
Tesla (TSLA) 1.000000 0 n/a

Two practical lessons from the raw history. Multipliers are not monotonic: one ETF token went to 2.0 and back to 1.0 within minutes, presumably a correction. And updates are scheduled: the event carries an effective time, and the old multiplier applies until then.

To read a token’s history yourself (Node.js 22+; the public RPC allows at most 10,000,000 blocks per eth_getLogs, so the script walks the chain in windows):

// multiplier-history.mjs: node multiplier-history.mjs <stock token address>
const RPC = "https://rpc.mainnet.chain.robinhood.com";
// keccak256("UIMultiplierUpdated(uint256,uint256,uint256)")
const TOPIC = "0x2205df4534432b2f60654a3fdb48737ffdaf3e9edb1a498bd985bc026b15b055";
const token = process.argv[2];
async function rpc(method, params) {
const r = await fetch(RPC, { method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }) });
const j = await r.json();
if (j.error) throw new Error(j.error.message);
return j.result;
}
const head = parseInt(await rpc("eth_blockNumber", []), 16);
const STEP = 10_000_000;
const logs = [];
for (let from = 0; from <= head; from += STEP) {
const to = Math.min(head, from + STEP - 1);
logs.push(...await rpc("eth_getLogs", [{ address: token, topics: [TOPIC], fromBlock: "0x" + from.toString(16), toBlock: "0x" + to.toString(16) }]));
}
for (const l of logs) {
const w = (i) => BigInt("0x" + l.data.slice(2 + 64 * i, 2 + 64 * (i + 1)));
const [oldM, newM, effectiveAt] = [w(0), w(1), w(2)];
console.log(`block ${parseInt(l.blockNumber, 16)}: ${Number(oldM) / 1e18} -> ${Number(newM) / 1e18}`,
`effective ${new Date(Number(effectiveAt) * 1000).toISOString()}`);
}
console.log(`${logs.length} update(s)`);
$ node multiplier-history.mjs 0xea72ecca2d0f6bfa1394dbbcff85b52cd4233931 # CRWD
block 978630: 1 -> 4 effective 2026-07-02T13:30:00.000Z
block 1231096: 1 -> 4 effective 2026-07-02T13:30:00.000Z
2 update(s)
$ node multiplier-history.mjs 0x92fd66527192e3e61d4ddd13322aa222de86f9b5 # SGOV
block 4629631: 1 -> 1.0009575198909908 effective 2026-07-08T20:14:32.000Z
block 30302195: 1.0009575198909908 -> 1.0029815193467666 effective 2026-08-07T15:10:24.000Z
block 51269236: 1.0029815193467666 -> 1.005101770003215 effective 2026-09-01T00:00:26.000Z
block 76929117: 1.005101770003215 -> 1.007186931447954 effective 2026-10-01T00:10:35.000Z
4 update(s)

What the multiplier does to each kind of data

Data Unit on-chain Share-based value
DEX price, candles, trade prices USD per token divide by the multiplier
Balances, holder lists, trade amounts tokens multiply by the multiplier (or call balanceOfUI)
Total supply tokens multiply, or call totalSupplyUI()
Market cap (supply × price) same either way no conversion needed
Chainlink / Robinhood oracle price already includes the multiplier do not apply it again

Market cap is the one number that needs no care: tokens × price per token equals shares × price per share.

Prices. Pools trade tokens, so every on-chain price is per token. CRWD makes the gap obvious: its on-chain market only started trading in late August 2026, after the split, so its entire DEX history is in four-share tokens: 49 daily closes between $678 and $1,111 per token (median $960), while CrowdStrike shares traded at roughly a quarter of that. A chart of the token next to a broker’s chart of the stock is off by a factor of four, and that is correct.

Dividends. Small multipliers create small, growing gaps. NVDA at 1.000775 means its token price runs 0.08% above the per-share price; SGOV at 1.007187 means 0.7%. Over a few years of reinvested dividends the gap becomes material.

History. The multiplier changes over time, so converting a historical price needs the multiplier in effect at that time, not today’s. Use the update history above: before each effectiveAt, the old value applies.

Oracles. Robinhood’s documentation is explicit that its Chainlink feeds already include the multiplier. Dividing an oracle price by the multiplier double-counts it. During a corporate action the oracle is paused (oraclePaused() returns true); treat it as unavailable, not zero.

Converting with the Crius API

The Crius token profile marks stock tokens with "type": "stock" and includes the current multiplier as ui_multiplier. Prices, candles, trades and holder balances are reported per token, as the chain reports them, so share-based numbers are a client-side conversion. This Python script does it, and also checks the token for a scheduled change and a paused oracle through the public RPC:

"""Per-share price of a Robinhood stock token: DEX price per token / uiMultiplier, plus any scheduled change."""
import os, sys, time, requests
API = "https://api.crius.sh/v1/robinhood"
RPC = "https://rpc.mainnet.chain.robinhood.com"
HEADERS = {"x-api-key": os.environ["CRIUS_API_KEY"]}
# ERC-8056 / Robinhood stock token getters (no arguments).
SELECTORS = {"uiMultiplier": "0xa60bf13d", "newUIMultiplier": "0xdc767007", "effectiveAt": "0x97a4064f", "oraclePaused": "0x7706ba52"}
def eth_call(token: str, selector: str) -> int:
body = {"jsonrpc": "2.0", "id": 1, "method": "eth_call", "params": [{"to": token, "data": selector}, "latest"]}
return int(requests.post(RPC, json=body, timeout=10).json()["result"], 16)
def per_share(token: str) -> dict:
profile = requests.get(f"{API}/token/{token}", headers=HEADERS, timeout=15).json()["data"]
if profile.get("type") != "stock":
raise SystemExit(f"{token} is not a Robinhood stock token")
price = profile["price"]["usd"] # USD per token (raw unit), from on-chain pools
mult = float(profile["ui_multiplier"]) # shares per token
on_chain = {name: eth_call(token, sel) for name, sel in SELECTORS.items()}
pending = None
if on_chain["effectiveAt"] > time.time():
pending = {"new_multiplier": on_chain["newUIMultiplier"] / 1e18, "effective_at": on_chain["effectiveAt"]}
return {
"symbol": profile["symbol"],
"usd_per_token": price,
"ui_multiplier": mult,
"usd_per_share": price / mult,
"confidence": profile["price"]["confidence"],
"oracle_paused": bool(on_chain["oraclePaused"]),
"pending_change": pending,
}
for t in sys.argv[1:]:
print(per_share(t))
$ python3 stock_price.py 0xea72ecca2d0f6bfa1394dbbcff85b52cd4233931 0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec 0x322f0929c4625ed5bad873c95208d54e1c003b2d
{'symbol': 'CRWD', 'usd_per_token': 984.669, 'ui_multiplier': 4.0, 'usd_per_share': 246.16725, 'confidence': 'medium', 'oracle_paused': False, 'pending_change': None}
{'symbol': 'NVDA', 'usd_per_token': 211.28, 'ui_multiplier': 1.00077515916, 'usd_per_share': 211.11635122652098, 'confidence': 'high', 'oracle_paused': False, 'pending_change': None}
{'symbol': 'TSLA', 'usd_per_token': 371.633, 'ui_multiplier': 1.0, 'usd_per_share': 371.633, 'confidence': 'high', 'oracle_paused': False, 'pending_change': None}

For holder data, multiply each balance from the holders API by the same multiplier, or call balanceOfUI(address) on the token for a single wallet.

A checklist for stock-token integrations

  1. Identify the token by address, and check the Crius profile’s type is stock.
  2. Decide your unit. Per token for anything that trades on-chain (swaps, slippage, pool analytics); per share for anything compared with the stock market.
  3. Divide prices, multiply balances by ui_multiplier, and never do either to an oracle price.
  4. Use the multiplier in effect at the time for historical conversions; build it from UIMultiplierUpdated events.
  5. Watch for scheduled changes via newUIMultiplier() and effectiveAt(), and for oraclePaused() around corporate actions.
  6. Watch liquidity too. CRWD’s source pool held about $1,500 at time of writing (confidence: medium); a correct conversion of a thin market is still a thin market.

FAQ

What is uiMultiplier on a Robinhood stock token? The number of underlying shares one token represents, in 18-decimal fixed point. It starts at 1.0 and is updated for stock splits and reinvested dividends instead of changing balances (ERC-8056 Scaled UI Amount).

Why is the CRWD token price four times CrowdStrike’s share price? CrowdStrike split four-for-one on July 2, 2026, and the CRWD token’s multiplier went from 1.0 to 4.0, so each token now represents four shares and trades at four times the per-share price.

Does the Crius API adjust prices for the multiplier? Crius reports prices, candles, trades and balances per token, exactly as they trade on-chain, and includes the current ui_multiplier on the token profile so you can convert to per-share values.

Should I apply the multiplier to Chainlink prices? No. Robinhood’s documentation says its oracle feeds already include it; applying it again double-counts.

How often do multipliers change? Rarely for most stocks: 62 updates across 50 tokens since mainnet launch, mostly small dividend steps, with Treasury ETFs updating monthly and occasional large jumps for splits.

Sources

Build with Crius

Robinhood Chain data in one API

Free plan with 1M credits a month. Your key in under a minute.

Related APIs

More from the blog