From 924593cfc4c4396cfb22941a01bc772bc92f8de3 Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Mon, 8 May 2023 16:51:55 -0400 Subject: [PATCH] add payouts example --- .cspell.json | 3 +- bcmr-v2.schema.ts | 19 ++--- examples.md | 16 +++- examples/decentralized-application.json | 8 +- examples/fungible-token.json | 5 +- examples/payouts-or-dividends.json | 75 ++++++++++++++++++ readme.md | 101 +++++++++++++++++++++--- 7 files changed, 196 insertions(+), 31 deletions(-) create mode 100644 examples/payouts-or-dividends.json diff --git a/.cspell.json b/.cspell.json index 7c6b333..a473d54 100644 --- a/.cspell.json +++ b/.cspell.json @@ -11,7 +11,8 @@ "Redenomination", "TXLOCKTIME", "XAMPL", - "XAMPLZ" + "XAMPLZ", + "XFFFF" ], "flagWords": [], "ignorePaths": [], diff --git a/bcmr-v2.schema.ts b/bcmr-v2.schema.ts index fd3d80c..5631a49 100644 --- a/bcmr-v2.schema.ts +++ b/bcmr-v2.schema.ts @@ -601,6 +601,7 @@ export type IdentitySnapshot = { * - `chat` * - `forum` * - `icon-intro` + * - `migrate` * - `registry` * - `support` * @@ -694,11 +695,10 @@ export type ChainHistory = RegistryTimestampKeyedValues; /** * A timestamp-keyed map of {@link IdentitySnapshot}s documenting - * the evolution of a particular identity. Typically, the current identity - * information is the latest record when lexicographically sorted, but in cases - * where a planned migration has not yet begun (the snapshot's timestamp has not - * yet been reached), the immediately preceding record is considered the - * current identity. + * the evolution of a particular identity. The current identity information is + * the snapshot associated with the latest timestamp reached. If no timestamp + * has yet been reached, the snapshot of the oldest timestamp is considered + * current. Future-dated timestamps indicate planned migrations. * * This strategy allows wallets and other user interfaces to offer better * experiences when an identity is rebranded, a token redenominated, or other @@ -707,12 +707,9 @@ export type ChainHistory = RegistryTimestampKeyedValues; * hold; after the change, the wallet may continue to offer prominent interface * hints that the rebranded token identity was recently updated. * - * Note, only the latest two {@link IdentitySnapshot}s should be considered - * part of an identity's "current" information. E.g. even if two snapshots have - * active, overlapping migration periods (i.e. an older snapshot's - * {@link IdentitySnapshot.migrated} timestamp has not yet been reached), - * clients should only attempt to display the migration from the previous to the - * latest snapshot. + * Timestamps may be order by time via lexicographical sort. For determinism, it + * is recommended that implementations sort from newest to oldest in exported + * registry JSON files. * * If the current snapshot's {@link IdentitySnapshot.migrated} isn't specified, * the snapshot's index is a precise time at which the snapshot takes effect and diff --git a/examples.md b/examples.md index e836f4b..2f8d312 100644 --- a/examples.md +++ b/examples.md @@ -24,10 +24,24 @@ For more detail, see the [descriptions in the example](./examples/art-collection ## Decentralized Application -The `decentralized-application.json` example demonstrates how a registry might specify the structure of NFTs used by a decentralized application. +The [`decentralized-application.json` example](./examples/decentralized-application.json) demonstrates how a registry might specify the structure of NFTs used by a decentralized application. The registry includes only one identity, `Crowdfunding Example`, defined as authbase `89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c` with a matching token category ID. This decentralized application uses only one type of parsable NFT, `Pledge Receipt`, which itself contains only one field, `Pledge Value`. This field demonstrates the additional capabilities of the `number` encoding: clients are informed that `Pledge Value` can be aggregated (by addition) in views containing multiple NFTs to provide useful information to the user. For example, if the wallet holds two NFTs, one with a `Pledge Value` of `123456` and one of `654321`, the wallet can display a total of the user's pledges to this campaign in relevant user interfaces: `0.00777777 BCH`. For more detail, see the [descriptions in the example](./examples/decentralized-application.json). + +## Payouts or Dividends + +The [`payouts-or-dividends.json` example](./examples/payouts-or-dividends.json) demonstrates how a registry might publish information about fungible tokens that receive [on-chain payouts or dividends](https://bitcoincashresearch.org/t/higher-level-token-standards-using-cashtokens/912/7), either from off-chain activities (e.g. equity or debt instruments) or as part of an on-chain mechanism (e.g. payouts from a decentralized application or sidechain). The example is documented as if the client is reading the registry shortly after its `latestRevision` of 2023-04-14. + +The registry includes only one identity, `Example Payout Shares`, defined as authbase `978306aa4e02fd06e251b38d2e961f78f4af2ea6524a3e4531126776276a6af1`. + +The first issue of `Example Payout Shares` was issued as token category `978306aa4e02fd06e251b38d2e961f78f4af2ea6524a3e4531126776276a6af1` with symbol `XAMPL-23Q1`. On 2023-03-31, the asset experienced a a metadata update in which the initial token category was replaced with the current category `b1a35cadd5ddb1bd18787eeb99ee061f34b946f0db375d84caadd8ab621c10f5` and symbol `XAMPL-23Q2`. Token holders of the initial category (`978306...`) could visit the new snapshot's `migrate` URI (`https://app.example.com/payouts/2023Q1`) for guidance or assistance in exchanging `XAMPL-23Q1` tokens for the latest `XAMPL-23Q2` tokens; presumably, the new tokens are locked in an on-chain covenant with which `XAMPL-23Q1` holders can swap in their old tokens and receive both a payout for the first quarter of 2023 and the new `XAMPL-23Q2` tokens. + +Leading up to 2023-03-31, user wallets would have displayed tokens of the initial category (`978306...`) as `XAMPL`. When the new snapshot became effective, these wallets should have immediately switch to identifying `978306...` tokens as `XAMPL-23Q1`, as the category of the current snapshot (`b1a35c...`) became the new `XAMPL`. Users holding both assets would have seen them listed separately, as `XAMPL-23Q1` entitles the user to both an unpaid payout and current `XAMPL` (`XAMPL-23Q2`) tokens. + +Finally, the registry includes information for a planned future snapshot that will become current on 2023-06-30. Clients may notify users in advance if this snapshot appears to impact a user's holdings. As before, clients should consider the current tokens (category `b1a35c...`) to be `XAMPL` until the time of the migration. After the future snapshot comes into effect (`2023-06-30T00:00:00.000Z`), the current tokens should be labelled with their fully-qualified `symbol`, `XAMPL-23Q2`, while tokens of the third category (`89cad9...`) become the new `XAMPL`. + +Note, that the mechanics of this example can also be used to improve user experiences in cases where an asset is re-issued or migrated for other technical reasons: on-chain voting, fee assessment, mergers, spinoffs, resolution of contract vulnerabilities, etc. diff --git a/examples/decentralized-application.json b/examples/decentralized-application.json index de11164..7595035 100644 --- a/examples/decentralized-application.json +++ b/examples/decentralized-application.json @@ -4,7 +4,7 @@ "latestRevision": "2023-04-14T00:00:17.720Z", "registryIdentity": { "name": "Example.com Token Registry", - "description": "An example registry demonstrating how a metadata registry might publish information about a decentralized application.", + "description": "An example demonstrating how a metadata registry might publish information about a decentralized application.", "uris": { "icon": "https://example.com/registry-icon.svg", "web": "https://example.com/", @@ -15,10 +15,10 @@ "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c": { "2023-01-13T00:00:00.000Z": { "name": "Crowdfunding Campaign: Example", - "description": "This is a short description of the decentralized application; in most interfaces, it will be hidden beyond 140 characters or the first newline character.\n\nThis sentence should be hidden in user interfaces with limited space.\n\nThis crowdfunding campaign instance has a distinct name and symbol (CFC-2023-XAMPL) to make it easily distinguishable from other crowdfunding campaigns published using the same contracts.\n\nThis application uses only one type of parsable NFT, 'Pledge Receipt's, so the 'parse.bytecode' (OP_0 OP_TOALTSTACK OP_0 OP_UTXOTOKENCOMMITMENT OP_TOALTSTACK) doesn't require any OP_IF/ENDIF branches; an empty altstack item is pushed to indicate the NFT type (''), and the remaining portion of the commitment is pushed as the only field (Pledge Value).", + "description": "This is a short description of the decentralized application; in most interfaces, it will be hidden beyond 140 characters or the first newline character.\n\nThis sentence should be hidden in user interfaces with limited space.\n\nThis crowdfunding campaign instance has a distinct name and symbol (CFC2023XAMPL) to make it easily distinguishable from other crowdfunding campaigns published using the same contracts.\n\nThis application uses only one type of parsable NFT, 'Pledge Receipt's, so the 'parse.bytecode' (OP_0 OP_TOALTSTACK OP_0 OP_UTXOTOKENCOMMITMENT OP_TOALTSTACK) doesn't require any OP_IF/ENDIF branches; an empty altstack item is pushed to indicate the NFT type (''), and the remaining portion of the commitment is pushed as the only field (Pledge Value).", "token": { "category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c", - "symbol": "CFC-2023-XAMPL", + "symbol": "CFC2023XAMPL", "nfts": { "description": "Example Crowdfunding Campaign issues your wallet a receipt (NFT) when you make a pledge. Should you decide to cancel your pledge before the campaign completes, this receipt can be redeemed with the on-chain contract to receive a refund.", "fields": { @@ -38,7 +38,7 @@ "types": { "": { "name": "Pledge Receipt", - "description": "An NFT of this category with a zero-length on-chain commitment (VM number 0). Where appropriate, user interfaces may display the ticker symbol of NFTs matching this type as CFC-2023-XAMPL-0.\n\nPledge Receipts are the only type of NFT demonstrated in this example, and they contain only one field: pledgeValue.", + "description": "An NFT of this category with a zero-length on-chain commitment (VM number 0). Where appropriate, user interfaces may display the ticker symbol of NFTs matching this type as CFC2023XAMPL-0.\n\nPledge Receipts are the only type of NFT demonstrated in this example, and they contain only one field: pledgeValue.", "fields": ["pledgeValue"], "uris": { "icon": "ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg", diff --git a/examples/fungible-token.json b/examples/fungible-token.json index d5c350d..2ff5a82 100644 --- a/examples/fungible-token.json +++ b/examples/fungible-token.json @@ -15,7 +15,7 @@ "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c": { "2023-01-13T00:00:00.000Z": { "name": "Example Asset", - "description": "This is a short description of Example Asset. In most interfaces, it will be hidden beyond 140 characters or the first newline character.\n\nThis sentence should be hidden in user interfaces with limited space.\n\nNote that this snapshot was introduced on 1/13, but the 'migrated' property indicates that the migration took place over the following month. During this month, most clients should have warned users about the upcoming change, referring users to the 'web' URI in the previous snapshot for information about the change. (Some clients may also offer clients the option to dismiss the migration period and immediately use the new metadata.) At 0 UTC on 2/13, all clients should have switched their default display for this token to use the new metadata, i.e. ticker symbol XAMPL, with 6 decimal places. After the migrated timestamp, old metadata should only be displayed when providing historical context.", + "description": "This is a short description of Example Asset. In most interfaces, it will be hidden beyond 140 characters or the first newline character.\n\nThis sentence should be hidden in user interfaces with limited space.\n\nNote that this snapshot was introduced on 2023-1-13, but the 'migrated' property indicates that the migration took place over the following month. During that month, most clients should have warned users about the upcoming change, referring users to the 'migrate' URI for information about the change. (Some clients may also offer clients the option to dismiss the migration period and immediately show the new metadata.) At 0 UTC on 2023-2-13, all clients should have switched their default display for this token to use the new metadata, i.e. ticker symbol XAMPL, with 6 decimal places. After the migrated timestamp, old metadata should only be displayed when providing historical context.", "token": { "category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c", "decimals": 6, @@ -25,6 +25,7 @@ "uris": { "icon": "https://example.com/xampl-icon.svg", "web": "https://example.com/about-xampl", + "migrate": "https://blog.example.com/example-asset-is-now-XAMPL", "blog": "https://blog.example.com/", "chat": "https://chat.example.com/", "forum": "https://forum.example.com/", @@ -35,7 +36,7 @@ }, "2023-01-03T00:00:00.000Z": { "name": "Example Asset", - "description": "This is a record of an older entry for the token identity. The token was rebranded (from EXAMPLE to XAMPL) and redenominated (from 8 to 6 decimals), but the existing token category was not modified: users were not required to trade their original EXAMPLE tokens for the redenominated XAMPL tokens, their wallets simply updated the way they are displayed using the new metadata. Note, this entry has likely been updated by the issuer to provide useful historical information rather than attempting to preserve the precise contents of the old snapshot; the 'web' URI links to a blog post about XAMPL's re-denomination/re-brand, and outdated URI information is excluded. There may have been metadata updates published between this snapshot and the latest snapshot, but they've been excluded because registries should strive to present a useful history of only meaningful changes to identities. This information can be used in user interfaces to improve continuity following metadata updates or to offer historical context.", + "description": "This is a record of an older entry for the token identity. The token was rebranded (from EXAMPLE to XAMPL) and redenominated (from 8 to 6 decimals), but the existing token category was not modified: users were not required to trade their original EXAMPLE tokens for the redenominated XAMPL tokens, their wallets simply updated the way tokens are displayed using the new metadata. Note, this entry has likely been updated by the issuer to provide useful historical information rather than attempting to preserve the precise contents of the old snapshot; the 'web' URI links to a blog post about XAMPL's re-denomination/re-brand, and outdated URI information is excluded. There may have been metadata updates published between this snapshot and the latest snapshot, but they've been excluded because registries should strive to present a useful history of only meaningful changes to identities. This information can be used in user interfaces to improve continuity following metadata updates or to offer historical context.", "token": { "category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c", "decimals": 8, diff --git a/examples/payouts-or-dividends.json b/examples/payouts-or-dividends.json new file mode 100644 index 0000000..7e043da --- /dev/null +++ b/examples/payouts-or-dividends.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://cashtokens.org/bcmr-v2.schema.json", + "version": { "major": 1, "minor": 1, "patch": 0 }, + "latestRevision": "2023-04-14T00:00:17.720Z", + "registryIdentity": { + "name": "Example.com Token Registry", + "description": "An example demonstrating how a registry might publish information about fungible tokens that receive on-chain payouts or dividends, either from off-chain activities (e.g. equity or debt instruments) or as part of an on-chain mechanism (e.g. payouts from a decentralized application or sidechain).\n\nDescriptions below are written from the perspective of a client reading this registry shortly after its 'lastRevision' timestamp on 2023-04-14.", + "uris": { + "icon": "https://example.com/registry-icon.svg", + "web": "https://example.com/", + "registry": "https://example.com/.well-known/bitcoin-cash-metadata-registry.json" + } + }, + "identities": { + "978306aa4e02fd06e251b38d2e961f78f4af2ea6524a3e4531126776276a6af1": { + "2023-06-30T00:00:00.000Z": { + "name": "Example Payout Shares", + "description": "This is a description of Example Payout Shares. In most interfaces, it will be hidden beyond 140 characters or the first newline character.\n\nThis sentence should be hidden in user interfaces with limited space.\n\nThis asset entitles holders to on-chain payouts of some kind. For example, they may be shares of a company, interest-paying debt, or utility tokens of a decentralized application where holders have performed some service to the protocol (e.g. sidechain validation, liquidity provision, etc.).\n\nNote that this particular identity snapshot has not yet come into effect (assuming the client is reading the registry shortly after its 'lastRevision' timestamp on 2023-04-14); the next identity snapshot contains the current information for the identity. Instead, this snapshot indicates an update to the identity expected to happen in the future: a payout for 2023Q2, where token holders will trade in fungible tokens of the 2023Q2 category for a quarterly payout and receive an equivalent number of fungible tokens of the new, 2023Q3 category listed in this snapshot.", + "migrated": "2023-07-01T00:00:00.000Z", + "token": { + "category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c", + "decimals": 6, + "symbol": "XAMPL-23Q3" + }, + "uris": { + "icon": "https://example.com/asset-icon.svg", + "web": "https://example.com/", + "migrate": "https://app.example.com/payouts/2023Q2", + "blog": "https://blog.example.com/", + "chat": "https://chat.example.com/", + "forum": "https://forum.example.com/", + "registry": "https://example.com/.well-known/bitcoin-cash-metadata-registry.json", + "support": "https://support.example.com/", + "custom-uri-identifier": "protocol://connection-info-for-some-protocol" + } + }, + "2023-03-31T00:00:00.000Z": { + "name": "Example Payout Shares", + "description": "The current identity snapshot for this token identity (assuming the client is reading the registry shortly after its 'lastRevision' timestamp on 2023-04-14). Note that as of now, tokens of this category (b1a35ca...) should be listed by the client simply by base symbol: XAMPL. When the next snapshot's initial timestamp is reached (2023-06-30) their full symbol should be displayed, XAMPL-23Q2, as the latest category for this token identity will become XAMPL-23Q3 (89cad9...), and only tokens of that category should be referred to using the base symbol. Holders of XAMPL-23Q2 can refer to the 'migrate' URI in the next snapshot for information about acquiring the new XAMPL tokens (XAMPL-23Q3) and their 2nd quarterly payout by trading in their XAMPL-23Q2 fungible tokens.", + "migrated": "2023-04-01T00:00:00.000Z", + "token": { + "category": "b1a35cadd5ddb1bd18787eeb99ee061f34b946f0db375d84caadd8ab621c10f5", + "decimals": 6, + "symbol": "XAMPL-23Q2" + }, + "uris": { + "icon": "https://example.com/asset-icon.svg", + "web": "https://example.com/", + "migrate": "https://app.example.com/payouts/2023Q1", + "blog": "https://blog.example.com/", + "chat": "https://chat.example.com/", + "forum": "https://forum.example.com/", + "registry": "https://example.com/.well-known/bitcoin-cash-metadata-registry.json", + "support": "https://support.example.com/", + "custom-uri-identifier": "protocol://connection-info-for-some-protocol" + } + }, + "2022-12-31T00:00:00.000Z": { + "name": "Example Payout Shares (2023Q1)", + "description": "Payout shares for Example Protocol for the first quarter of 2023. These shares can be redeemed with the on-chain payout system to receive the payout and the new tokens for 2023Q2. See the linked website for details.\n\nExample note: this asset (XAMPL-23Q1, 978306...) will still be held in wallets that have not yet redeemed the 2023Q1 payout. Because the payout is held by an on-chain covenant, it's safe to delay redemption indefinitely, as funds (and the new, XAMPL-23Q2 tokens) will remain in the covenant until fully claimed by existing XAMPL-23Q1 holders. Some holders may leave XAMPL-23Q1 tokens in long-term vaults for many quarters before withdrawing, redeeming all payouts, and depositing the latest XAMPL tokens back in such vaults.", + "migrated": "2023-01-01T00:00:00.000Z", + "token": { + "category": "978306aa4e02fd06e251b38d2e961f78f4af2ea6524a3e4531126776276a6af1", + "decimals": 6, + "symbol": "XAMPL-23Q1" + }, + "uris": { + "icon": "https://example.com/asset-icon-pending-payout.svg", + "web": "https://blog.example.com/payout-2023Q1" + } + } + } + }, + "license": "CC0-1.0" +} diff --git a/readme.md b/readme.md index a350842..ec143d1 100644 --- a/readme.md +++ b/readme.md @@ -218,7 +218,11 @@ At any moment in time, only one snapshot is considered "current" for an identity Each identity in a metadata registry is represented by an `IdentityHistory` data structure, a map of ISO timestamps to [`IdentitySnapshot`](#identitysnapshots)s. `IdentityHistory` data structures allow clients to construct a timeline of the evolution of a particular identity, helping users recognize and disambiguate identities that have changed significantly since the user last interacted with that identity. -Typically, the current identity information is the latest record when the keys (timestamps) are lexicographically sorted, but in cases where a [planned migration](#handling-identity-snapshot-migrations) has not yet begun (the snapshot's timestamp has not been reached), the immediately preceding record is considered the current identity. +The current identity information is the snapshot associated with the latest timestamp reached. If no timestamp has yet been reached, the snapshot of the oldest timestamp is considered current. Future-dated timestamps indicate [planned migrations](#handling-identity-snapshot-migrations). + +This strategy allows wallets and other user interfaces to offer better experiences when an identity is rebranded, a token redenominated, or other important metadata is modified in a coordinated update. For example, a wallet may warn token holders of a forthcoming rebranding of fungible tokens they hold; after the change, the wallet may continue to offer prominent interface hints that the rebranded token identity was recently updated. + +Timestamps may be order by time via lexicographical sort. For determinism, it is recommended that implementations sort from newest to oldest in exported registry JSON files. #### Tags @@ -253,6 +257,7 @@ The following optional URI identifiers are standardized: | `chat` | A URI identifying a community chatroom for this identity or tag. | | `forum` | A URI identifying a community forum for this identity or tag. | | `icon-intro` | A URI pointing to a square, animated icon that represents this identity or tag. The animation should play once (without looping) to introduce the static icon. Transparency is supported, and icons should be suitable for display against both light and dark backgrounds. Acceptable formats are `SVG`, `AVIF`, or `WebP`. For raster formats, the recommended size is `400px` by `400px`. | +| `migrate` | (Standardized only for `IdentitySnapshot`s.) A URI identifying a resource with information about the change resulting in this snapshot. For snapshots in which `token.category` is modified (e.g. a re-issuance, vote, payout, dividend, etc.), this resource should provide guidance about how holders of the previous token category may exchange tokens for those of the new category. | | `registry` | The primary-source registry URI for this identity or tag. For DNS-resolved registries, this is the full, [Well-Known URI](#well-known-uri) from which the registry can be downloaded. For chain-resolved registries and other identities, this is the full URI of the latest registry published on-chain by the identity. For tags, The `registry` identifier should only be used when a tag represents a formal designation by a particular authority (certification, membership, ownership, etc.); when present, this URI points to the canonical registry published by that authority. Tags without this identifier are assumed to be created and applied by the containing registry. | | `support` | A URI offering user-facing support for this identity or tag. | @@ -326,6 +331,16 @@ Clients may use the `authchain` extension to rapidly update their records for a The following recommendations are made for issuers of CashTokens. +#### Ticker Symbol Selection + +A ticker `symbol` must be associated with any identity for which `token` information is specified. Symbols must contain only capital letters, numbers, and hyphens (regular expression: `^[A-Z0-9]+[-A-Z0-9]*$`). + +Within each ticker symbol, the **base symbol** is the segment of capital letters and numbers occurring prior to the first hyphen (`-`). Base symbols should be globally unique among unrelated assets, and it is recommended that base symbols be 4 to 6 characters in length (inclusive) for identities of fungible tokens or 6 to 13 characters in length (inclusive) for identities associated primarily with an NFT collection (see [Symbol Length Recommendations](#symbol-length-recommendations) for details). + +Base symbols may be shared by multiple classes of a particular asset, e.g. classes of stock (e.g. `XAMPL-A`, `XAMPL-B`), pre-dividend and post-dividend tokens (e.g. `XAMPL-23Q1`, `XAMPL-23Q2`), multiple types of non-fungible tokens within a category (e.g. `XAMPL-20231115-C-012345678`, `CAMPAIGN2023-21-100`), etc. While assets sharing a base symbol are not interchangeable, user interfaces may consolidate them into unified views by base symbol to simplify user experiences. + +Token issuers should be aware that ticker symbols are not assigned by a centralized authority. To be included in commonly-trusted registries, token issuers must achieve widespread consensus around their chosen symbol; assets with issuer-chosen symbols that are unclear or misleading may instead come to be identified by the wider ecosystem using an alternative symbol. + #### Providing for Continued Issuance of Fungible Tokens If additional fungible tokens of a category may be needed in the future, token issuers should initially mint an excess supply (e.g. the maximum supply of `9223372036854775807`) and hold the unissued tokens in the identity output with a [mutable token](https://github.com/bitjson/cashtokens#token-types) (using any `commitment` value) to indicate they are part of the [Unissued/Reserved Supply](https://github.com/bitjson/cashtokens#reserved-supply). This enables continued issuance from the identity output while maintaining the ability for light clients to verify the maximum possible [Circulating Supply](https://github.com/bitjson/cashtokens#circulating-supply). @@ -370,11 +385,11 @@ It is recommended that all supporting client software include at least one [embe When adding or updating a registry, clients should perform basic validation of the newly-received registry: -1. Using the client's existing registries, build a mapping of identity `token.symbol` values to known authbases. -2. Iterating through the newly-received registry, verify that each new `token.symbol`: - 1. Passes token symbol validation. (Regular expression: `/^[-A-Z0-9]+$/`) +1. Using the client's existing registries, build a mapping of identity `TokenCategory.symbol` values to known authbases. +2. Iterating through the newly-received registry, verify that each new `TokenCategory.symbol`: + 1. Passes token symbol validation. (Regular expression: `^[A-Z0-9]+[-A-Z0-9]*$`) 2. Maps to the same authbase in existing registries as is represented in the new registry. - 3. Does not appear on the client's list of reserved token symbols. + 3. Has a globally unique [base symbol](#ticker-symbol-selection) and does not appear on the client's list of reserved token symbols. If this validation fails, clients should either: @@ -384,11 +399,27 @@ If this validation fails, clients should either: #### Handling Identity Snapshot Migrations -Each `IdentitySnapshot` is assigned to a timestamp at which the snapshot began or will begin to take effect. If no `IdentitySnapshot.migrated` timestamp is provided, the snapshot's migration is considered **instant**: the new information should be displayed immediately after the assigned timestamp has been reached. If `IdentitySnapshot.migrated` is provided, the snapshot's migration is considered **gradual**: the migration period begins at the `IdentitySnapshot`'s initial timestamp and completes upon reaching the `IdentitySnapshot.migrated` timestamp. Clients are encouraged to surface both kinds of migrations to users. +Each `IdentitySnapshot` within an `IdentityHistory` is assigned to a timestamp at which the snapshot began or will begin to take effect. If the snapshot does not include a `migrated` timestamp, the migration is considered **instant**: the new information should be displayed immediately after the assigned timestamp has been reached. If `IdentitySnapshot.migrated` is provided, the snapshot's migration is considered **gradual**: the migration period begins at the `IdentitySnapshot`'s initial timestamp and completes upon reaching the `migrated` timestamp. -**Where possible, clients should notify users about upcoming and recent migrations that impact in-use identities.** +**Where possible, clients should notify users about upcoming and recent migrations that impact in-use identities.** In particular, changes to `name`, `uris.icon`, and token `category`, `symbol`, and `decimals` settings should be clearly highlighted for identities and assets relevant to the user. -Note that while it is technically possible for registries to encode two overlapping migrations, clients should only attempt to use information from the latest migration (between the latest and previous snapshots when timestamps are lexicographically sorted). +Note that while it is technically possible for registries to encode two overlapping migrations, clients should only attempt to use information from the latest migration (between the current and previous snapshot). + +#### Rendering Ticker Symbols + +All identities with `token` information must include a `category` and ticker `symbol` to label that identity's tokens in clients (`decimals` may optionally be included and defaults to `0`). + +Like other properties of an identity, token `category`, `symbol`, and `decimals` may all change over time as an asset is rebranded, re-denominated, re-issued, or migrated for technical reasons (e.g. on-chain voting, payouts, [dividend issuance](https://bitcoincashresearch.org/t/higher-level-token-standards-using-cashtokens/912/7), etc.). To handle these circumstances, clients should map symbols to tokens using the following strategy: + +1. Create a mapping of token categories to category metadata (`name`, `symbol`, etc.) given all metadata across all trusted registries; this includes previous identity snapshots in which an identity's `token.category` had a different value than the current snapshot. +2. Map the category of the token(s) to be identified to matching metadata. + 1. **Token `symbol`s from current snapshots should be truncated to remove content after the base symbol** (`symbol` content including and following the first hyphen). + 2. Token `symbol`s from previous snapshots should be rendered without truncation. + 3. If the wallet holds tokens of both the current snapshot and previous snapshots, the two groups of assets should be distinguished, e.g. `XAMPL` (`XAMPL-23Q2` from a current snapshot) and `XAMPL-23Q1` (tokens from a previous snapshot and not yet redeemed for a payout and the new `XAMPL-23Q2` tokens). + +Note that collections of NFTs should also be grouped in multi-asset user interfaces by these computed `symbol`s – NFTs of categories associated with current snapshots may be grouped by their base symbol (e.g. `XAMPL`), while NFTs of categories associated with older snapshots should be grouped separately by their fully-qualified `symbol` (e.g. `XAMPL-ISSUE1`). + +For a summary of expected symbol length ranges, see [Symbol Length Recommendations](#symbol-length-recommendations). #### Rendering NFTs in User Interfaces @@ -414,11 +445,26 @@ At a minimum, user interfaces displaying parsable NFTs should provide for render #### NFT Ticker Symbols -Where appropriate, user interfaces may indicate a ticker symbol for any NFT. Like ticker symbols for fungible tokens, NFT ticker symbols use only capital letters, numbers, and hyphens (regular expression: `/^[-A-Z0-9]+$/`). The ticker symbol for a particular NFT is the concatenation of it's `TokenCategory.symbol`, a hyphen (`-`), and the NFT's **type symbol**. +Where appropriate, user interfaces may indicate a ticker symbol for any NFT. If a particular NFT has no defined `name`, the name should default to the NFT's ticker symbol. -For sequential NFTs, each NFT's type symbol is the positive integer encoded (as a VM number) in that NFT's on-chain commitment. For example, an NFT of sequential NFT category `XAMPL` with commitment `0x64`/`100` has the ticker symbol `XAMPL-100`. While possible, issuing a sequential NFT with a negative or invalid VM number is generally discouraged; The type symbol of such non-numeric sequential NFTs should use the hex-encoded form, prefixed with `X` (e.g. the type symbol for commitment `0x81`/`-1` is `X81`, producing a ticker symbol of `XAMPL-X81`). +Like ticker symbols for fungible tokens, NFT ticker symbols use only capital letters, numbers, and hyphens; unless associated with a shorter [base symbol](#ticker-symbol-selection) from a fungible token category, **it is recommended that non-fungible token collections use a base symbol between 6 and 13 characters in length, inclusive** (see [Symbol Length Recommendations](#symbol-length-recommendations)). -For parsable NFTs, each NFT's type symbol is determined by the value of the bottom altstack item following evaluation of `NftCategory.parse.bytecode`; if the value is a positive VM number, that integer is the type symbol, otherwise, the value is hex-encoded and prefixed with `X`. For example, an NFT of parsable NFT category `XAMPL` matching the `NftType` at `NftCategory.parse.types` index of `7f` (VM number `127`) should be listed as `XAMPL-127`, while an NFT matching the index of `ff` (VM number `-127`) should be listed as `XAMPL-XFF`. +The full ticker symbol for a particular NFT is the concatenation of it's `TokenCategory.symbol`, a hyphen (`-`), and the **NFT ticker symbol encoding** of the NFT type's key within `NftCategory.parse.types`: if the key can be minimally-encoded as a positive VM number, the resulting number, otherwise, the hex-encoded key prefixed with `X` (see [Sequential NFT Commitment Encoding](#sequential-nft-commitment-encoding)). Test vectors are provided below given a category ticker symbol of `XAMPL`. + +| NFT Type Key | NFT Ticker Symbol | +| ---------------------------------- | ----------------- | +| `''` (empty string, VM number `0`) | `XAMPL-0` | +| `01` (VM number `1`) | `XAMPL-1` | +| `64` (VM number `100`) | `XAMPL-100` | +| `7f` (VM number `127`) | `XAMPL-127` | +| `80` (VM number `-0`) | `XAMPL-X80` | +| `81` (VM number `-1`) | `XAMPL-X81` | +| `ff` (VM number `-127`) | `XAMPL-XFF` | +| `8000` (VM number `128`) | `XAMPL-128` | +| `ff7f` (VM number `32767`) | `XAMPL-32767` | +| `ff7f` (VM number `32767`) | `XAMPL-32767` | +| `ff80` (VM number `-255`) | `XAMPL-XFF80` | +| `ffff` (VM number `-32767`) | `XAMPL-XFFFF` | ## Rationale @@ -436,6 +482,36 @@ Alternatively, this proposal could allow for relative `HTTPS` URIs, reducing the Note, many use cases that would seem to benefit from relative URIs – like art collections in which many graphics share a single fully qualified domain name (e.g. `https://example.com/1.svg`, `https://example.com/2.svg`, etc.) – are better implemented with content-addressed `IPFS` URIs (see [Publication of Static Data](#publication-of-static-data)). Content-addressed URIs combine resource resolution with data integrity, ensuring that changes in the resolved data must be accompanied by an update to the referencing registry (e.g. adding a new snapshot to the collection's identity). Additionally, clients can safely use previously-cached, content-addressed resources following registry updates, as such resources are guaranteed to have remained unchanged. +### Sequential NFT Commitment Encoding + +To improve interoperability between off-chain and on-chain use cases, this standard recommends that sequential NFTs use VM number encoding within commitments, [mapping commitment contents to NFT ticker symbols with numeric suffixes](#nft-ticker-symbols). This recommendation ensures that contracts can [read and operate on sequential NFT commitment values](https://github.com/bitjson/chip-bcmr/pull/8#issuecomment-1534127586). For example, a sequential NFT minting covenant can enforce uniqueness across all NFTs in a collection by simply incrementing a counter (`OP_1ADD`) within a covenant-tracking minting NFT, inserting the new value into the newly-minted sequential NFT. Additionally, as parsable NFTs are likely to prefix many NFT types with VM Number `0` through `16`, this convention is likely to improve consistency between ticker symbols of sequential NFTs and parsable NFTs (see [Token API single-byte bias](https://github.com/bitjson/jedex#demonstrated-concepts)). + +### Symbol Length Recommendations + +To create greater certainty around user interface requirements and capabilities, this standard recommends that: + +- [base symbols for fungible tokens](#ticker-symbol-selection) be between 4 and 6 characters in length, +- base symbols for token categories dedicated to non-fungible tokens (NFTs) be [between 6 and 13 characters in length](#nft-ticker-symbols), +- and clients [accommodate up to 26 characters for full symbols](#rendering-ticker-symbols) in asset-specific views. + +The 4-character minimum recommended length for fungible tokens is derived from widespread usage in existing markets. Fewer than 50,000 unique 3-character base symbols can exist1, and most of these symbols are already in use across one or more existing markets. While issuers of these existing assets may migrate issuance to use fungible CashTokens and carry over their existing symbols, new issuers must usually select a base symbol with a length of at least 4 characters for global uniqueness. + +By establishing maximum recommended base symbol lengths, this standard ensures wider compatibility across user interfaces; base symbols remaining within this range have the maximum likelihood of being displayed without truncation in most user interfaces. + +The 6-character minimum recommended length for NFTs is chosen to reduce overlap with base symbols of fungible tokens (which can be expected to prefer 4 or 5-character base symbols). NFTs tend to be more specialized and less commonly used as exchange media, appearing in fewer trading pairs and user interfaces requiring symbol brevity. In fact, NFT base symbols are rarely used without full qualification, e.g. `XAMPL-2354`, `BRANDNAME-100`, etc. Given this fundamental difference from fungible tokens, longer base symbols tend to be advantageous for NFTs, where clarity and differentiation (e.g. in keyword search) are more valuable than brevity in character length. Following this recommendation, many NFT collections can be visually distinguished from fungible tokens purely on the basis of symbol length. + +The 13-character maximum recommended length for base symbols is derived from the minimum recommended length of fungible token base symbols and from the distribution of word lengths in the English language. In existing markets, relatively-long base symbols are commonly used to embed trading pairs. For example, `NAMEUSDEUR` might refer to an interest in `NAME` that earns a share in the profits of `NAME`'s `USD/EUR` market. For these use cases, a 13-character base symbol can accommodate a 5-character entity symbol associated with two other 4-character base symbols, e.g. `XAMPLCORNGOLD`. Additionally, a 13-character maximum base symbol best accommodates readable symbols in the english language; approximately 96.7% of English words occupy 13 characters or less, with rapidly diminishing improvement in coverage beyond 13 characters2. + +Finally, this standard advises clients to plan for adequate space in asset-specific user interfaces to render ticker symbols of up to 26 characters in length. This limit accommodates existing ticker symbol standards (e.g. Options Clearing Corporation's 21-character option symbols following the Options Symbology Initiative), allows for the maximum value sequential NFT with a 6-character base symbol (e.g. `XAMPL1-9223372036854775807`), and provides adequate space for a variety of future standards using a 6-character base symbol, a 1 character type indicator (e.g. put, call, limit, etc.), and two 8-character data elements (e.g. a date and strike price), each separated by hyphens (`-`). + +
+Notes + +1. Maximum number of globally unique 3-character base symbols, where each character may be a capital letter or number: `(26+10)**3 = 46,656` (4-character base symbols: `(26+10)**4 = 1,679,616`; 5-character base symbols: `(26+10)**5 = 60,466,176`). +2. As of 2023, 13 characters encompass approximately 96.7% of words in the American English `wordlist` on Debian: `awk '{ print length($0); }' /usr/share/dict/words | sort -n | uniq -c`. The distribution of word lengths yields significantly diminishing returns beyond 13 characters: 9 characters covers 67.9%, 10 characters covers 79.5%, 11 covers 88.0%, 12 covers 93.6%, 13 covers 96.7%, 14 covers 98.4%, 15 covers 99.3%, 16 covers 99.7%, 17 covers 99.8%, etc. + +
+ ## Test Vectors A variety of [Bitcoin Cash Metadata Registry examples](./examples.md) are provided in this proposal. @@ -466,7 +542,8 @@ This section summarizes the evolution of this document. - Standardized parsing transaction to eliminate undefined behavior ([#7](https://github.com/bitjson/chip-bcmr/pull/7)) - Converted `identities` from an array to an object ([#7](https://github.com/bitjson/chip-bcmr/pull/7)) - Expanded guidelines for issuers and clients - - Added example registries + - Defined NFT ticker symbols ([#8](https://github.com/bitjson/chip-bcmr/pull/7)) + - Added registry examples - **v1.0.0 – 2022-10-31** ([`5b24b0ec`](https://github.com/bitjson/chip-bcmr/blob/5b24b0ec93cf9316222ab2ea2e2ffe8a9f390b12/readme.md)) - Initial publication