Building with Stock Tokens
Stock tokens are standard ERC-20 tokens, so building with them uses the same patterns you already know — plus onchain price feeds for real-world asset data. This guide covers what you can build, how to integrate, and how to get started.
Use cases
| Use case | What it looks like | Example |
|---|---|---|
| Portfolio & display | Show balances and live USD value | A wallet view listing a user's NVDA, AAPL, SPY tokens with real-time value and daily P&L |
| Trading | RFQ swap interfaces | An embedded widget that quotes NVDA ↔ USDG via 0x RFQ and settles onchain |
| Lending & borrowing | Stock tokens as collateral | Deposit NVDA as collateral on a lending market and borrow USDG — liquidity without selling |
| Indices & baskets | Bundle tokens into one product | An "AI basket" (NVDA, MSFT, GOOGL) that auto-values from each token's Chainlink feed |
| Yield strategies | Earn on equity exposure | A vault that supplies stock tokens to a lending market so holders earn yield on top of dividends |
| Price-aware contracts | Logic that reacts to equity prices | A contract that executes when a stock token crosses a threshold — onchain conditional logic |
| Perps & derivatives | Equity-backed leverage | Use stock tokens as margin or underlying on a perps venue |
| Global access (in eligible regions) | Equity exposure where direct access is limited | An app offering US-equity exposure to users in eligible regions |
Tokenized stocks trade via RFQ at launch. They remain standard ERC-20s and can be transferred and held in any wallet.
Integrations, contracts & APIs
Contracts
Stock tokens are ERC-20 (18 decimals). All standard token operations work without modification:
IERC20 nvda = IERC20(NVDA_TOKEN_ADDRESS);
uint256 balance = nvda.balanceOf(user);
nvda.transfer(recipient, amount);
nvda.approve(spender, amount);Stock tokens also implement ERC-8056 (Scaled UI Amount Extension), which defines the corporate-action multiplier (uiMultiplier()). The multiplier scales the effective amount without changing raw balances or total supply — balanceOf() and totalSupply() stay fixed. Stock tokens are not rebasing tokens. See the ERC-8056 spec.
Find all stock tokens and ETF addresses on the Token Contracts page.
Prices
Every stock token has a per-asset Chainlink price feed implementing the standard AggregatorV3Interface (latestRoundData(), read via the feed proxy — same interface as crypto feeds):
AggregatorV3Interface feed = AggregatorV3Interface(NVDA_PRICE_FEED);
(, int256 price, , uint256 updatedAt, ) = feed.latestRoundData();
require(price > 0 && updatedAt > 0, "Invalid price");The Chainlink price already includes the corporate-action multiplier (dividends, splits), so the value you read is the token's full price — don't apply the multiplier yourself. If you need the raw ratio, read it onchain via the token's uiMultiplier().
See Oracles & Price Feeds for feed addresses, decimals, and best practices (staleness checks, sequencer uptime).
Multiplier Conversion
Each token represents a number of underlying shares equal to its raw token amount scaled by the multiplier:
underlying shares = raw token amount × uiMultiplier ÷ 1e18uiMultiplier()is fixed-point with 18 decimals — 1e18 = 1.0.- At launch the multiplier is 1e18 (one token = one underlying share).
- When a corporate action is applied (e.g. a reinvested dividend or a stock split), the multiplier is updated and
UIMultiplierUpdatedis emitted with the effective timestamp.
Multiplier Updates
When a corporate action schedules a multiplier change ahead of time, the pending value and its effective time are readable on-chain:
interface IScaledUIAmountNewUIMultiplier {
// The pending UI multiplier scheduled to take effect at effectiveAt.
function newUIMultiplier() external view returns (uint256);
// The timestamp at which the pending multiplier becomes effective.
function effectiveAt() external view returns (uint256);
}newUIMultiplier()— the multiplier that will take effect ateffectiveAt(). Before any update is scheduled this tracks the current multiplier.effectiveAt()— the timestamp at whichnewUIMultiplier()becomes the activeuiMultiplier().
UI-Adjust Views
Rather than mutating raw balances, the token exposes UI-adjusted (underlying-share) views of balance and supply:
interface IScaledUIAmountBalances {
// UI-adjusted balance of an account (raw balance scaled by uiMultiplier).
function balanceOfUI(address account) external view returns (uint256);
// UI-adjusted total supply.
function totalSupplyUI() external view returns (uint256);
}balanceOfUI(account)— the account's balance expressed in underlying shares. Use this to display how many underlying shares a holder's tokens represent. To convert any raw amount yourself, apply the formula in Multiplier Conversion above.totalSupplyUI()— total supply expressed in underlying shares.
ERC-8056 events
The multiplier and its movements are exposed by the core ERC-8056 interface:
interface IScaledUIAmount {
// Current UI multiplier, expressed with 18 decimals (1e18 = 1.0).
function uiMultiplier() external view returns (uint256);
// Emitted when the multiplier changes (e.g. a dividend or split).
event UIMultiplierUpdated(
uint256 oldMultiplier,
uint256 newMultiplier,
uint256 effectiveAtTimestamp
);
// Emitted on a transfer, carrying both the raw value and the UI-adjusted
// (underlying-share) value.
event TransferWithScaledUI(
address indexed from,
address indexed to,
uint256 value,
uint256 uiValue
);
}- Subscribe to
UIMultiplierUpdatedto track corporate-action adjustments and the timestamp each became effective. - Use
TransferWithScaledUIto record the underlying-share value (uiValue) of each transfer alongside the raw value.
Getting started
- Pick a stock token — grab its address from Token Contracts.
- Read a balance — call
balanceOflike any ERC-20. - Read its price — call
latestRoundData()on the token's Chainlink feed. - Build — display it, swap it, use it as collateral, or compose it into your product.
Example: value a user's holdings in USD
// Returns the USD value of a user's stock token balance.
function holdingValueUsd(
IERC20 stockToken,
AggregatorV3Interface priceFeed,
address user
) external view returns (uint256) {
uint256 balance = stockToken.balanceOf(user); // 18 decimals
(, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(price > 0 && updatedAt > 0, "Invalid price"); // price: 8 decimals
// (balance * price) scaled by token + feed decimals
return (balance * uint256(price)) / 1e8;
}Next steps
- Token Contracts — all stock token & ETF addresses
- Oracles & Price Feeds — price feed integration
- Deploy a Contract — ship your contracts to Robinhood Chain