Skip to content

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 caseWhat it looks likeExample
Portfolio & displayShow balances and live USD valueA wallet view listing a user's NVDA, AAPL, SPY tokens with real-time value and daily P&L
TradingRFQ swap interfacesAn embedded widget that quotes NVDA ↔ USDG via 0x RFQ and settles onchain
Lending & borrowingStock tokens as collateralDeposit NVDA as collateral on a lending market and borrow USDG — liquidity without selling
Indices & basketsBundle tokens into one productAn "AI basket" (NVDA, MSFT, GOOGL) that auto-values from each token's Chainlink feed
Yield strategiesEarn on equity exposureA vault that supplies stock tokens to a lending market so holders earn yield on top of dividends
Price-aware contractsLogic that reacts to equity pricesA contract that executes when a stock token crosses a threshold — onchain conditional logic
Perps & derivativesEquity-backed leverageUse stock tokens as margin or underlying on a perps venue
Global access (in eligible regions)Equity exposure where direct access is limitedAn 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 ÷ 1e18
  • uiMultiplier() 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 UIMultiplierUpdated is 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 at effectiveAt(). Before any update is scheduled this tracks the current multiplier.
  • effectiveAt() — the timestamp at which newUIMultiplier() becomes the active uiMultiplier().

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 UIMultiplierUpdated to track corporate-action adjustments and the timestamp each became effective.
  • Use TransferWithScaledUI to record the underlying-share value (uiValue) of each transfer alongside the raw value.

Getting started

  1. Pick a stock token — grab its address from Token Contracts.
  2. Read a balance — call balanceOf like any ERC-20.
  3. Read its price — call latestRoundData() on the token's Chainlink feed.
  4. 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