updateUIMultiplier(2e18, effectiveAt) on a B20 Asset. Raw balanceOf, totalSupply, and transfer amounts stay unchanged. Wallets and indexers read the post-split share count through balanceOfUI and related views once effectiveAt passes.
Real-world asset (RWA) tokenization is one of many use cases for the B20 Asset standard. The examples on this page use a stock token for illustration; the same flows apply to other asset types.Tokenized securities examples shown for illustration. Base is a general-purpose blockchain; issuance and compliance are the responsibility of the issuer under applicable law.
Demo
New to B20? See the B20 Token Standard for the concepts and a full launch walkthrough. These samples target
base-std@be6d045, viem@2.55.11, and Base Foundry v1.1.1.How the Multiplier Works
RawbalanceOf is unchanged by a split. The multiplier changes only the derived UI view:
The multiplier is an 18-decimal WAD:
1e18 is 1.0. A 2-for-1 split uses 2e18. A 1-for-2 reverse split uses 5e17. Integer division rounds down, so the rounding loss is confined to the scaled view and is at most one unit of the scaled amount. The raw-side difference on a round trip through fromUIAmount can be larger when the multiplier is below 1e18. Prefer 18 decimals for stock tokens to keep that effect small.
Schedule and Verify the Split Multiplier
Only an account holdingOPERATOR_ROLE may call updateUIMultiplier, cancelUIMultiplierUpdate, or updateMultiplier. Any other caller reverts AccessControlUnauthorizedAccount. The token created in Create an Asset Token grants OPERATOR_ROLE to the deployer.
UIMultiplierUpdated(oldMultiplier, newMultiplier, effectiveAtTimestamp). That event fires when the update is recorded, not when the multiplier becomes active.
Read the Live Pending State
Read the pending state
updateUIMultiplier while a pending update is live reverts UIMultiplierUpdateExists. To replace a pending update, cancel first, then reschedule.
Confirm After the Effective Time
Whenblock.timestamp >= effectiveAt, uiMultiplier() returns the new multiplier. Maturation emits no event. Do not wait for a second event at the flip.
uiMultiplier() returns 2e18 at or after effectiveAt. Raw balanceOf stays unchanged. balanceOfUI doubles.Cancel a Pending Update
Call beforeeffectiveAt to discard the pending split:
Cancel the pending update
UIMultiplierUpdateCancelled(cancelledMultiplier, cancelledEffectiveAt). Calling with no live pending update, including after maturity, reverts UIMultiplierUpdateDoesNotExist.
To cancel and reschedule atomically, wrap both calls in announce:
Cancel and reschedule in one announcement
Emergency Override
UseupdateMultiplier(newMultiplier) only when a pending update is wrong and you cannot wait for effectiveAt. It applies the value immediately and clears any pending update. The interface guarantees that it emits both MultiplierUpdated and UIMultiplierUpdated. The base-std stock-split guide documents the sequence as:
MultiplierUpdated is deprecated. Process only UIMultiplierUpdated to avoid handling the same update twice.
Integrator Rules
- Listen for
UIMultiplierUpdated, not the deprecatedMultiplierUpdated. - If
effectiveAtTimestamp > block.timestamp, treat the update as pending until that time. - Maturation emits nothing. Do not wait for a second event at the flip.
- On
UIMultiplierUpdateCancelled, discard the pending update. - Detect a live pending update with
effectiveAt() > block.timestamp. Do not checkeffectiveAt() == 0.
Common Errors
See Also
Announce a Stock Dividend
Record an onchain distribution announcement.
Pause Transfers
Halt transfers in an emergency.