Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/base-chain/specs/upgrades/beryl/b20-playground.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ A new user shouldn't have to buy ETH first. With `permit`, they sign an approval
<B20FlowDemo flow="permit" />
</Visibility>

## The stock splits
## Multipliers

A 2-for-1 split is one call. Every balance doubles without a migration or a new contract. Dividends run inside a public announcement window, so the disclosure lives onchain next to the action. The token also stores identifiers like a CUSIP. See the [Asset variant](/base-chain/specs/upgrades/beryl/b20#asset) in the spec.
An underlying real-world asset may undergo events that change its redemption ratio - a corporate action, a distribution, or accrued yield. Updating the multiplier reflects these events onchain instantly, so holder balances track the underlying without a rebase, airdrop, or migration.

One B20 token does not permanently equal one unit of the underlying. After a redemption-ratio update, the multiplier might move to `1.02`, meaning one token is redeemable for 1.02 units of the underlying. Helper functions (`scaledBalanceOf`, `toScaledBalance`, `toRawBalance`) convert between raw token units and the scaled redeemable quantity. See the [Asset variant](/base-chain/specs/upgrades/beryl/b20#asset) in the spec.

<Visibility for="humans">
<B20FlowDemo flow="equity" />
Expand All @@ -62,3 +64,7 @@ A 2-for-1 split is one call. Every balance doubles without a migration or a new
The upgrade that introduces B20 and native token features.
</Card>
</CardGroup>

<Info>
Coinbase tokenized equities are only available to persons in eligible jurisdictions outside of the U.S. Tokenized securities examples shown for illustration. Base is a general-purpose blockchain; issuance and compliance are the responsibility of the issuer under applicable law.
</Info>
22 changes: 11 additions & 11 deletions docs/snippets/B20PlaygroundDemo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export const B20FlowDemo = ({ flow }) => {
const isEquity = kind === "EQUITY";
s.token = {
variant: isEquity ? "ASSET" : "STABLECOIN",
name: isEquity ? "Acme Corp Class A" : "Acme Dollar",
name: isEquity ? "Acme Asset" : "Acme Dollar",
symbol: isEquity ? "ACME" : "aUSD",
multiplier: 1.0,
extraMetadata: isEquity ? { cusip: "38259P508" } : {},
extraMetadata: isEquity ? { ref: "ACME-A-2024" } : {},
};
const roleMap = {};
["DEFAULT_ADMIN_ROLE", "MINT_ROLE", "BURN_BLOCKED_ROLE"].forEach(r => { roleMap[r] = ["Issuer"]; });
Expand Down Expand Up @@ -148,7 +148,7 @@ export const B20FlowDemo = ({ flow }) => {
case "announceBatchMint": {
const { recipients, amt, id } = op;
if (!roleHas("OPERATOR_ROLE", me)) return entries.push(revert("AccessControlUnauthorizedAccount", `${me} lacks OPERATOR_ROLE`));
entries.push(emit("Announcement", `id ${id} · dividend batchMint`, 0));
entries.push(emit("Announcement", `id ${id} · distribution batchMint`, 0));
recipients.forEach((r, i) => {
s.balances[r] = (s.balances[r] || 0) + amt;
entries.push(emit("Transfer", `0x0 → ${r} · ${amt}`, i + 1));
Expand Down Expand Up @@ -288,27 +288,27 @@ export const B20FlowDemo = ({ flow }) => {
},

equity: {
title: "A share of stock, onchain",
title: "An asset with a changing redemption ratio, onchain",
erc20: "On plain ERC-20: a rebasing token is a custom contract, and disclosures live offchain.",
readout: "scaled",
steps: [
{
label: "ACME lists onchain with its security identifiers.",
label: "ACME lists onchain with its reference identifiers.",
action: "Create token",
run: (s) => {
createInSim(s, "EQUITY");
return {
entries: [
info("createB20", `asset · ACME · ${tokenAddress("ASSET", "ACME")}`),
info("initCalls", 'updateExtraMetadata("cusip", "38259P508")'),
info("initCalls", 'updateExtraMetadata("ref", "ACME-A-2024")'),
],
caption: "One factory call, with no contract to write or audit.",
};
},
},
{
label: "Shareholders hold ACME.",
action: "Distribute shares",
label: "Holders receive ACME.",
action: "Distribute tokens",
run: (s) => ({
entries: runOps(s, [
{ as: "Issuer", type: "mint", to: "Alice", amt: 100 },
Expand All @@ -317,15 +317,15 @@ export const B20FlowDemo = ({ flow }) => {
}),
},
{
label: "The board declares a 2-for-1 split.",
action: "Run the split",
label: "A corporate action changes the redemption ratio.",
action: "Update the multiplier",
run: (s) => ({
entries: runOps(s, [{ as: "Issuer", type: "updateMultiplier", value: 2.0 }]),
caption: "Every balance doubles in one call, without a migration or a new contract.",
}),
},
{
label: "A dividend goes out with public disclosure.",
label: "A distribution goes out with public disclosure.",
action: "Announce & distribute",
run: (s) => ({
entries: runOps(s, [{ as: "Issuer", type: "announceBatchMint", recipients: ["Alice", "Bob"], amt: 10, id: 7 }]),
Expand Down
Loading