You've already forked chip-bcmr
expand guidelines, add examples
This commit is contained in:
+3
-1
@@ -8,8 +8,10 @@
|
|||||||
"rebranded",
|
"rebranded",
|
||||||
"rebranding",
|
"rebranding",
|
||||||
"redenominated",
|
"redenominated",
|
||||||
|
"Redenomination",
|
||||||
"TXLOCKTIME",
|
"TXLOCKTIME",
|
||||||
"XAMPL"
|
"XAMPL",
|
||||||
|
"XAMPLZ"
|
||||||
],
|
],
|
||||||
"flagWords": [],
|
"flagWords": [],
|
||||||
"ignorePaths": [],
|
"ignorePaths": [],
|
||||||
|
|||||||
+50
-23
@@ -150,27 +150,18 @@
|
|||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"$ref": "#/definitions/NftCategoryField",
|
"$ref": "#/definitions/NftCategoryField",
|
||||||
"description": "A mapping of field identifier to field definitions for the data fields that can appear in NFT commitments of this category."
|
"description": "A mapping of field identifier to field definitions for the data fields that can appear in NFT commitments of this category.\n\nCategories including only sequential NFTs (where `parse.bytecode` is undefined) should omit `fields` (or set to `undefined`)."
|
||||||
},
|
},
|
||||||
"parse": {
|
"parse": {
|
||||||
"additionalProperties": false,
|
"anyOf": [
|
||||||
"description": "Parsing and interpretation information for all NFTs of this category; this enables generalized wallets to parse and display detailed information about all NFTs held by the wallet, e.g. `BCH Pledged`, `Order Price`, `Seat Number`, `Asset Number`, `IPFS Content Identifier`, `HTTPS URL`, etc.\n\nParsing instructions are provided in the `bytecode` property, and the results are interpreted using the `types` property.",
|
{
|
||||||
"properties": {
|
"$ref": "#/definitions/SequentialNftCollection"
|
||||||
"bytecode": {
|
|
||||||
"description": "A segment of hex-encoded Bitcoin Cash VM bytecode that parses UTXOs holding NFTs of this category, identifies the NFT's type within the category, and returns a list of the NFT's field values via the altstack.\n\nThe parse `bytecode` is evaluated by instantiating and partially verifying a standardized NFT parsing transaction:\n- version: `2`\n- inputs: - 0: Spends the UTXO containing the NFT with an empty unlocking bytecode and sequence number of `0`. - 1: Spends index `0` of the empty hash outpoint, with locking bytecode set to `parse.bytecode`, unlocking bytecode `OP_1` (`0x51`) and sequence number `0`.\n- outputs: - 0: A locking bytecode of OP_RETURN (`0x6a`) and value of `0`.\n- locktime: `0`\n\nAfter input 1 of this NFT parsing transaction is evaluated, if the resulting stack is not valid (a single \"truthy\" element remaining on the stack) – or if the altstack is empty – parsing has failed and clients should represent the NFT as unable to be parsed (e.g. simply display the full `commitment` as a hex-encoded value in the user interface).\n\nOn successful parsing evaluations, the bottom item on the altstack indicates the type of the NFT according to the matching definition in `types`. If no match is found, clients should represent the NFT as unable to be parsed.\n\nFor example:\n- `00d26b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_TOALTSTACK) takes the full contents of the commitment as a fixed type; this can be used for collections of unique, \"numbered\" NFTs. (Commitments of `01`, `02`, `03`, etc.)\n- `00d2517f7c6b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_1 OP_SPLIT OP_SWAP OP_TOALTSTACK OP_TOALTSTACK) splits the commitment after 1 byte, pushing the first byte to the altstack as an NFT type and the remaining segment of the commitment as the first NFT field value.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
},
|
||||||
"types": {
|
{
|
||||||
"additionalProperties": {
|
"$ref": "#/definitions/ParsableNftCollection"
|
||||||
"$ref": "#/definitions/NftType",
|
|
||||||
"description": "A definitions for each type of NFT within the token category. NFT types are indexed by the expected hex-encoded value of the bottom altstack item following evaluation of `token.nfts.parse.bytecode`. The remaining altstack items are mapped to NFT fields according to the `fields` property of the matching NFT type."
|
|
||||||
},
|
|
||||||
"description": "A mapping of hex-encoded values to definitions of possible NFT types in this category.",
|
|
||||||
"type": "object"
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
"required": ["bytecode", "types"],
|
"description": "Parsing and interpretation information for all NFTs of this category; this enables generalized wallets to parse and display detailed information about all NFTs held by the wallet, e.g. `BCH Pledged`, `Order Price`, `Seat Number`, `Asset Number`, `IPFS Content Identifier`, `HTTPS URL`, etc.\n\nParsing instructions are provided in the `bytecode` property, and the results are interpreted using the `types` property."
|
||||||
"type": "object"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["parse"],
|
"required": ["parse"],
|
||||||
@@ -210,7 +201,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"aggregate": {
|
"aggregate": {
|
||||||
"const": "add",
|
"const": "add",
|
||||||
"description": "The `aggregate` property indicates that aggregating this field from multiple NFTs is desirable in user interfaces. For example, for a field named `BCH Pledged` where `aggregate` is `add`, the client can display a `Total BCH Pledged` in any user interface listing more than one NFT.\n\nIf specified, clients should aggregate the field from all NFTs within a view (e.g. NFTs held by a single wallet, NFTs existing in a single transaction's outputs, etc.) using the specified operation. Note, while aggregation could be performed using any commutative operation – multiplication, bitwise AND, bitwise OR, and bitwise XOR, etc. – only `add` is currently supported.",
|
"description": "The `aggregate` property indicates that aggregating this field from multiple NFTs is desirable in user interfaces. For example, for a field named `BCH Pledged` where `aggregate` is `add`, the client can display a `Total BCH Pledged` in any user interface listing more than one NFT.\n\nIf specified, clients should aggregate the field from all NFTs, of all NFT types within the category, within a particular view (e.g. NFTs held by a single wallet, NFTs existing in a single transaction's outputs, etc.) using the specified operation.\n\nNote, while aggregation could be performed using any commutative operation – multiplication, bitwise AND, bitwise OR, bitwise XOR, etc. – only `add` is currently supported.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"decimals": {
|
"decimals": {
|
||||||
@@ -230,14 +221,14 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "The expected encoding of this field when read from the parsing altstack (see `token.nft.parse`). All encoding definitions must have a `type`, and some encoding definitions allow for additional hinting about display strategies in clients.\n\nEncoding types may be set to `binary`, `boolean`, `hex`, `number`, or `utf8`:\n\n- `binary` types should be displayed as binary literals (e.g. `0b0101`)\n- `boolean` types should be displayed as `true` if exactly `0x01` or `false` if exactly `0x00`. If a boolean value does not match one of these values, clients should represent the NFT as unable to be parsed (e.g. simply display the full `commitment`).\n- `hex` types should be displayed as hex literals (e.g.`0xabcd`).\n- `https-url` types are percent encoded with the `https://` prefix omitted; they may be displayed as URIs or as activatable links.\n- `ipfs-cid` types are binary-encoded IPFS Content Identifiers; they may be displayed as URIs or as activatable links.\n- `locktime` types are `OP_TXLOCKTIME` results: integers from `0` to `4294967295` (inclusive) where values less than `500000000` are understood to be a block height (the current block number in the chain, beginning from block `0`), and values greater than or equal to `500000000` are understood to be a Median Time Past (BIP113) UNIX timestamp. (Note, sequence age is not currently supported.)\n- `number` types should be displayed according the their configured `decimals` and `unit` values.\n- `utf8` types should be displayed as utf8 strings."
|
"description": "The expected encoding of this field when read from the parsing altstack (see {@link ParsableNftCollection } ). All encoding definitions must have a `type`, and some encoding definitions allow for additional hinting about display strategies in clients.\n\nEncoding types may be set to `binary`, `boolean`, `hex`, `number`, or `utf8`:\n\n- `binary` types should be displayed as binary literals (e.g. `0b0101`)\n- `boolean` types should be displayed as `true` if exactly `0x01` or `false` if exactly `0x00`. If a boolean value does not match one of these values, clients should represent the NFT as unable to be parsed (e.g. simply display the full `commitment`).\n- `hex` types should be displayed as hex literals (e.g.`0xabcd`).\n- `https-url` types are percent encoded with the `https://` prefix omitted; they may be displayed as URIs or as activatable links.\n- `ipfs-cid` types are binary-encoded IPFS Content Identifiers; they may be displayed as URIs or as activatable links.\n- `locktime` types are `OP_TXLOCKTIME` results: integers from `0` to `4294967295` (inclusive) where values less than `500000000` are understood to be a block height (the current block number in the chain, beginning from block `0`), and values greater than or equal to `500000000` are understood to be a Median Time Past (BIP113) UNIX timestamp. (Note, sequence age is not currently supported.)\n- `number` types should be displayed according the their configured `decimals` and `unit` values.\n- `utf8` types should be displayed as utf8 strings."
|
||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"$ref": "#/definitions/Extensions",
|
"$ref": "#/definitions/Extensions",
|
||||||
"description": "A mapping of NFT field extension identifiers to extension definitions. {@link Extensions } may be widely standardized or application-specific."
|
"description": "A mapping of NFT field extension identifiers to extension definitions.\n {@link Extensions } may be widely standardized or application-specific."
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"description": "The name of this field for use in interfaces. Names longer than `20` characters may be elided in some interfaces.\n\nE.g.:\n- `BCH Pledged`\n- `Tokens Sold`\n- `Seat Number`,\n- `IPFS Content Identifier`\n- `HTTPS URL`",
|
"description": "The name of this field for use in interfaces. Names longer than `20` characters may be elided in some interfaces.\n\nE.g.:\n- `BCH Pledged`\n- `Tokens Sold`\n- `Settlement Locktime`\n- `Seat Number`,\n- `IPFS Content Identifier`\n- `HTTPS URL`",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"uris": {
|
"uris": {
|
||||||
@@ -261,10 +252,10 @@
|
|||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"$ref": "#/definitions/Extensions",
|
"$ref": "#/definitions/Extensions",
|
||||||
"description": "A mapping of NFT type extension identifiers to extension definitions. {@link Extensions } may be widely standardized or application-specific."
|
"description": "A mapping of NFT type extension identifiers to extension definitions.\n {@link Extensions } may be widely standardized or application-specific."
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"description": "A list of identifiers for fields contained in NFTs of this type. On successful parsing evaluations, the bottom item on the altstack indicates the matched NFT type, and the remaining altstack items represent NFT field contents in the order listed (where `fields[0]` is the second-to-bottom item, and the final item in `fields` is the top of the altstack).\n\nFields should be ordered by recommended importance from most important to least important; in user interfaces, clients should display fields at lower indexes more prominently than those at higher indexes, e.g. if some fields cannot be displayed in minimized interfaces, higher-importance fields can still be represented. (Note, this ordering is controlled by the bytecode specified in `token.nft.parse.bytecode`.)",
|
"description": "A list of identifiers for fields contained in NFTs of this type. On successful parsing evaluations, the bottom item on the altstack indicates the matched NFT type, and the remaining altstack items represent NFT field contents in the order listed (where `fields[0]` is the second-to-bottom item, and the final item in `fields` is the top of the altstack).\n\nFields should be ordered by recommended importance from most important to least important; in user interfaces, clients should display fields at lower indexes more prominently than those at higher indexes, e.g. if some fields cannot be displayed in minimized interfaces, higher-importance fields can still be represented. (Note, this ordering is controlled by the bytecode specified in `token.nft.parse.bytecode`.)\n\nIf this is a sequential NFT, (the category's `parse.bytecode` is undefined), `fields` should be omitted or set to `undefined`.",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -312,6 +303,26 @@
|
|||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"ParsableNftCollection": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"description": "Interpretation information for a collection of parsable NFTs, a collection in which each NFT may include additional metadata fields beyond a sequential identifier within its on-chain commitment. Note that\n {@link ParsableNftCollection } s differ from {@link SequentialNftCollection } s in that parsable collections require a parsing `bytecode` with which to inspect each NFT commitment: the type of each NFT is indexed by the hex-encoded contents the bottom item on the altstack following the evaluation of the parsing bytecode.",
|
||||||
|
"properties": {
|
||||||
|
"bytecode": {
|
||||||
|
"description": "A segment of hex-encoded Bitcoin Cash VM bytecode that parses UTXOs holding NFTs of this category, identifies the NFT's type within the category, and returns a list of the NFT's field values via the altstack. If undefined, this NFT Category includes only sequential NFTs, with only an identifier and no NFT fields encoded in each NFT's on-chain commitment.\n\nThe parse `bytecode` is evaluated by instantiating and partially verifying a standardized NFT parsing transaction:\n- version: `2`\n- inputs: - 0: Spends the UTXO containing the NFT with an empty unlocking bytecode and sequence number of `0`. - 1: Spends index `0` of the empty hash outpoint, with locking bytecode set to `parse.bytecode`, unlocking bytecode `OP_1` (`0x51`) and sequence number `0`.\n- outputs: - 0: A locking bytecode of OP_RETURN (`0x6a`) and value of `0`.\n- locktime: `0`\n\nAfter input 1 of this NFT parsing transaction is evaluated, if the resulting stack is not valid (a single \"truthy\" element remaining on the stack) – or if the altstack is empty – parsing has failed and clients should represent the NFT as unable to be parsed (e.g. simply display the full `commitment` as a hex-encoded value in the user interface).\n\nOn successful parsing evaluations, the bottom item on the altstack indicates the type of the NFT according to the matching definition in `types`. If no match is found, clients should represent the NFT as unable to be parsed.\n\nFor example: `00d2517f7c6b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_1 OP_SPLIT OP_SWAP OP_TOALTSTACK OP_TOALTSTACK) splits the commitment after 1 byte, pushing the first byte to the altstack as an NFT type identifier and the remaining segment of the commitment as the first NFT field value.\n\nIf undefined (in a {@link SequentialNftCollection } ), this field could be considered to have a default value of `00d26b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_TOALTSTACK), which takes the full contents of the commitment as a fixed type index. As such, each index of the NFT category's `types` maps a precise commitment value to the metadata for NFTs with that particular commitment. E.g. an NFT with an empty commitment (VM number 0) maps to `types['']`, a commitment of `01` (hex) maps to `types['01']`, etc. This pattern is used for collections of sequential NFTs.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"types": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"$ref": "#/definitions/NftType",
|
||||||
|
"description": "A definitions for each type of NFT within the token category. Parsable NFT types are indexed by the hex-encoded value of the bottom altstack item following evaluation of `NftCategory.parse.bytecode`. The remaining altstack items are mapped to NFT fields according to the `fields` property of the matching NFT type."
|
||||||
|
},
|
||||||
|
"description": "A mapping of hex-encoded values to definitions of possible NFT types in this category.",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["bytecode", "types"],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"Registry": {
|
"Registry": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "A Bitcoin Cash Metadata Registry is an authenticated JSON file containing metadata about tokens, identities, contract applications, and other on-chain artifacts. BCMRs conform to the Bitcoin Cash Metadata Registry JSON Schema, and they can be published and maintained by any entity or individual.",
|
"description": "A Bitcoin Cash Metadata Registry is an authenticated JSON file containing metadata about tokens, identities, contract applications, and other on-chain artifacts. BCMRs conform to the Bitcoin Cash Metadata Registry JSON Schema, and they can be published and maintained by any entity or individual.",
|
||||||
@@ -406,6 +417,22 @@
|
|||||||
"description": "A field keyed by timestamps to document the evolution of the field. Each timestamp must be provided in simplified extended ISO 8601 format, a 24-character string of format `YYYY-MM-DDTHH:mm:ss.sssZ` where timezone is zero UTC (denoted by `Z`). Note, this is the format returned by ECMAScript `Date.toISOString()`.\n\nFor example, to insert a new value: ```ts const result = { ...previousValue, [(new Date()).toISOString()]: newValue }; ```",
|
"description": "A field keyed by timestamps to document the evolution of the field. Each timestamp must be provided in simplified extended ISO 8601 format, a 24-character string of format `YYYY-MM-DDTHH:mm:ss.sssZ` where timezone is zero UTC (denoted by `Z`). Note, this is the format returned by ECMAScript `Date.toISOString()`.\n\nFor example, to insert a new value: ```ts const result = { ...previousValue, [(new Date()).toISOString()]: newValue }; ```",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"SequentialNftCollection": {
|
||||||
|
"additionalProperties": false,
|
||||||
|
"description": "Interpretation information for a collection of sequential NFTs, a collection in which each NFT includes only a sequential identifier within its on-chain commitment. Note that {@link SequentialNftCollection } s differ from\n {@link ParsableNftCollection } s in that sequential collections lack a parsing `bytecode` with which to inspect each NFT commitment: the type of each NFT is indexed by the full contents its commitment (interpreted as a positive VM integer in user interfaces).",
|
||||||
|
"properties": {
|
||||||
|
"types": {
|
||||||
|
"additionalProperties": {
|
||||||
|
"$ref": "#/definitions/NftType",
|
||||||
|
"description": "Interpretation information for each type of NFT within the token category, indexed by commitment hex. For sequential NFTs, the on-chain commitment of each NFT is interpreted as a VM number to reference its particular NFT type in user interfaces. Issuing a sequential NFT with a negative or invalid VM number is discouraged, but clients may render the commitment of such NFTs in hex-encoded form, prefixed with `X`."
|
||||||
|
},
|
||||||
|
"description": "A mapping of each NFT commitment (typically, a positive integer encoded as a VM number) to metadata for that NFT type in this category.",
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["types"],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"Tag": {
|
"Tag": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Tags allow registries to classify and group identities by a variety of characteristics. Tags are standardized within a registry and may represent either labels applied by that registry or designations by external authorities (certification, membership, ownership, etc.) that are tracked by that registry.\n\nExamples of possible tags include: `individual`, `organization`, `token`, `wallet`, `exchange`, `staking`, `utility-token`, `security-token`, `stablecoin`, `wrapped`, `collectable`, `deflationary`, `governance`, `decentralized-exchange`, `liquidity-provider`, `sidechain`, `sidechain-bridge`, `acme-audited`, `acme-endorsed`, etc.\n\nTags may be used by clients in search, discovery, and filtering of identities, and they can also convey information like accreditation from investor protection organizations, public certifications by security or financial auditors, and other designations that signal integrity and value to users.",
|
"description": "Tags allow registries to classify and group identities by a variety of characteristics. Tags are standardized within a registry and may represent either labels applied by that registry or designations by external authorities (certification, membership, ownership, etc.) that are tracked by that registry.\n\nExamples of possible tags include: `individual`, `organization`, `token`, `wallet`, `exchange`, `staking`, `utility-token`, `security-token`, `stablecoin`, `wrapped`, `collectable`, `deflationary`, `governance`, `decentralized-exchange`, `liquidity-provider`, `sidechain`, `sidechain-bridge`, `acme-audited`, `acme-endorsed`, etc.\n\nTags may be used by clients in search, discovery, and filtering of identities, and they can also convey information like accreditation from investor protection organizations, public certifications by security or financial auditors, and other designations that signal integrity and value to users.",
|
||||||
|
|||||||
+174
-131
@@ -129,8 +129,8 @@ export type Tag = {
|
|||||||
*/
|
*/
|
||||||
export type NftType = {
|
export type NftType = {
|
||||||
/**
|
/**
|
||||||
* The name of this NFT type for use in interfaces. Names longer than
|
* The name of this NFT type for use in interfaces. Names longer than `20`
|
||||||
* `20` characters may be elided in some interfaces.
|
* characters may be elided in some interfaces.
|
||||||
*
|
*
|
||||||
* E.g. `Market Order Buys`, `Limit Order Sales`, `Pledge Receipts`,
|
* E.g. `Market Order Buys`, `Limit Order Sales`, `Pledge Receipts`,
|
||||||
* `ACME Stadium Tickets`, `Sealed Votes`, etc.
|
* `ACME Stadium Tickets`, `Sealed Votes`, etc.
|
||||||
@@ -140,56 +140,53 @@ export type NftType = {
|
|||||||
/**
|
/**
|
||||||
* A string describing this NFT type for use in user interfaces.
|
* A string describing this NFT type for use in user interfaces.
|
||||||
*
|
*
|
||||||
* In user interfaces with limited space, names should be hidden
|
* In user interfaces with limited space, names should be hidden beyond the
|
||||||
* beyond the first newline character or `140` characters until
|
* first newline character or `140` characters until revealed by the user.
|
||||||
* revealed by the user.
|
|
||||||
*
|
*
|
||||||
* E.g.:
|
* E.g.:
|
||||||
* - "Receipts issued by the exchange to record details about
|
* - "Receipts issued by the exchange to record details about purchases. After
|
||||||
* purchases. After settlement, these receipts are redeemed for the
|
* settlement, these receipts are redeemed for the purchased tokens.";
|
||||||
* purchased tokens.";
|
* - "Receipts issued by the crowdfunding campaign to document the value of
|
||||||
* - "Receipts issued by the crowdfunding campaign
|
* funds pledged. If the user decides to cancel their pledge before the
|
||||||
* to document the value of funds pledged. If the user decides to
|
* campaign completes, these receipts can be redeemed for a full refund.";
|
||||||
* cancel their pledge before the campaign completes, these receipts
|
|
||||||
* can be redeemed for a full refund.";
|
|
||||||
* - "Tickets issued for events at ACME Stadium.";
|
* - "Tickets issued for events at ACME Stadium.";
|
||||||
* - Sealed ballots certified by ACME decentralized organization
|
* - Sealed ballots certified by ACME decentralized organization during the
|
||||||
* during the voting period. After the voting period ends, these
|
* voting period. After the voting period ends, these ballots must be revealed
|
||||||
* ballots must be revealed to reclaim the tokens used for voting."
|
* to reclaim the tokens used for voting."
|
||||||
*/
|
*/
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of identifiers for fields contained in NFTs of this type.
|
* A list of identifiers for fields contained in NFTs of this type. On
|
||||||
* On successful parsing evaluations, the bottom item on the
|
* successful parsing evaluations, the bottom item on the altstack indicates
|
||||||
* altstack indicates the matched NFT type, and the remaining
|
* the matched NFT type, and the remaining altstack items represent NFT field
|
||||||
* altstack items represent NFT field contents in the order listed
|
* contents in the order listed (where `fields[0]` is the second-to-bottom
|
||||||
* (where `fields[0]` is the second-to-bottom item, and the final
|
* item, and the final item in `fields` is the top of the altstack).
|
||||||
* item in `fields` is the top of the altstack).
|
|
||||||
*
|
*
|
||||||
* Fields should be ordered by recommended importance from most
|
* Fields should be ordered by recommended importance from most important to
|
||||||
* important to least important; in user interfaces, clients should
|
* least important; in user interfaces, clients should display fields at lower
|
||||||
* display fields at lower indexes more prominently than those at
|
* indexes more prominently than those at higher indexes, e.g. if some fields
|
||||||
* higher indexes, e.g. if some fields cannot be displayed in
|
* cannot be displayed in minimized interfaces, higher-importance fields can
|
||||||
* minimized interfaces, higher-importance fields can still be
|
* still be represented. (Note, this ordering is controlled by the bytecode
|
||||||
* represented. (Note, this ordering is controlled by the bytecode
|
|
||||||
* specified in `token.nft.parse.bytecode`.)
|
* specified in `token.nft.parse.bytecode`.)
|
||||||
|
*
|
||||||
|
* If this is a sequential NFT, (the category's `parse.bytecode` is
|
||||||
|
* undefined), `fields` should be omitted or set to `undefined`.
|
||||||
*/
|
*/
|
||||||
fields?: string[];
|
fields?: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of identifiers to URIs associated with this NFT type.
|
* A mapping of identifiers to URIs associated with this NFT type. URI
|
||||||
* URI identifiers may be widely-standardized or registry-specific.
|
* identifiers may be widely-standardized or registry-specific. Values must be
|
||||||
* Values must be valid URIs, including a protocol prefix (e.g.
|
* valid URIs, including a protocol prefix (e.g. `https://` or `ipfs://`).
|
||||||
* `https://` or `ipfs://`). Clients are only required to support
|
* Clients are only required to support `https` and `ipfs` URIs, but any
|
||||||
* `https` and `ipfs` URIs, but any scheme may be specified.
|
* scheme may be specified.
|
||||||
*/
|
*/
|
||||||
uris?: URIs;
|
uris?: URIs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of NFT type extension identifiers to extension
|
* A mapping of NFT type extension identifiers to extension definitions.
|
||||||
* definitions. {@link Extensions} may be widely standardized or
|
* {@link Extensions} may be widely standardized or application-specific.
|
||||||
* application-specific.
|
|
||||||
*/
|
*/
|
||||||
extensions?: Extensions;
|
extensions?: Extensions;
|
||||||
};
|
};
|
||||||
@@ -201,12 +198,13 @@ export type NftType = {
|
|||||||
export type NftCategoryField = {
|
export type NftCategoryField = {
|
||||||
[identifier: string]: {
|
[identifier: string]: {
|
||||||
/**
|
/**
|
||||||
* The name of this field for use in interfaces. Names longer than
|
* The name of this field for use in interfaces. Names longer than `20`
|
||||||
* `20` characters may be elided in some interfaces.
|
* characters may be elided in some interfaces.
|
||||||
*
|
*
|
||||||
* E.g.:
|
* E.g.:
|
||||||
* - `BCH Pledged`
|
* - `BCH Pledged`
|
||||||
* - `Tokens Sold`
|
* - `Tokens Sold`
|
||||||
|
* - `Settlement Locktime`
|
||||||
* - `Seat Number`,
|
* - `Seat Number`,
|
||||||
* - `IPFS Content Identifier`
|
* - `IPFS Content Identifier`
|
||||||
* - `HTTPS URL`
|
* - `HTTPS URL`
|
||||||
@@ -215,8 +213,8 @@ export type NftCategoryField = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A string describing how this identity uses NFTs (for use in user
|
* A string describing how this identity uses NFTs (for use in user
|
||||||
* interfaces). Descriptions longer than `160` characters may be
|
* interfaces). Descriptions longer than `160` characters may be elided in
|
||||||
* elided in some interfaces.
|
* some interfaces.
|
||||||
*
|
*
|
||||||
* E.g.:
|
* E.g.:
|
||||||
* - `The BCH value pledged at the time this receipt was issued.`
|
* - `The BCH value pledged at the time this receipt was issued.`
|
||||||
@@ -226,31 +224,30 @@ export type NftCategoryField = {
|
|||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The expected encoding of this field when read from the parsing
|
* The expected encoding of this field when read from the parsing altstack
|
||||||
* altstack (see `token.nft.parse`). All encoding definitions must
|
* (see {@link ParsableNftCollection}). All encoding definitions must have a
|
||||||
* have a `type`, and some encoding definitions allow for additional
|
* `type`, and some encoding definitions allow for additional hinting about
|
||||||
* hinting about display strategies in clients.
|
* display strategies in clients.
|
||||||
*
|
*
|
||||||
* Encoding types may be set to `binary`, `boolean`, `hex`, `number`,
|
* Encoding types may be set to `binary`, `boolean`, `hex`, `number`,
|
||||||
* or `utf8`:
|
* or `utf8`:
|
||||||
*
|
*
|
||||||
* - `binary` types should be displayed as binary literals (e.g.
|
* - `binary` types should be displayed as binary literals (e.g. `0b0101`)
|
||||||
* `0b0101`)
|
* - `boolean` types should be displayed as `true` if exactly `0x01` or
|
||||||
* - `boolean` types should be displayed as `true` if exactly `0x01`
|
* `false` if exactly `0x00`. If a boolean value does not match one of these
|
||||||
* or `false` if exactly `0x00`. If a boolean value does not match one
|
* values, clients should represent the NFT as unable to be parsed
|
||||||
* of these values, clients should represent the NFT as unable to be
|
* (e.g. simply display the full `commitment`).
|
||||||
* parsed (e.g. simply display the full `commitment`).
|
|
||||||
* - `hex` types should be displayed as hex literals (e.g.`0xabcd`).
|
* - `hex` types should be displayed as hex literals (e.g.`0xabcd`).
|
||||||
* - `https-url` types are percent encoded with the `https://` prefix
|
* - `https-url` types are percent encoded with the `https://` prefix
|
||||||
* omitted; they may be displayed as URIs or as activatable links.
|
* omitted; they may be displayed as URIs or as activatable links.
|
||||||
* - `ipfs-cid` types are binary-encoded IPFS Content Identifiers;
|
* - `ipfs-cid` types are binary-encoded IPFS Content Identifiers; they may
|
||||||
* they may be displayed as URIs or as activatable links.
|
* be displayed as URIs or as activatable links.
|
||||||
* - `locktime` types are `OP_TXLOCKTIME` results: integers from `0`
|
* - `locktime` types are `OP_TXLOCKTIME` results: integers from `0` to
|
||||||
* to `4294967295` (inclusive) where values less than `500000000` are
|
* `4294967295` (inclusive) where values less than `500000000` are
|
||||||
* understood to be a block height (the current block number in the
|
* understood to be a block height (the current block number in the chain,
|
||||||
* chain, beginning from block `0`), and values greater than or equal
|
* beginning from block `0`), and values greater than or equal to
|
||||||
* to `500000000` are understood to be a Median Time Past (BIP113)
|
* `500000000` are understood to be a Median Time Past (BIP113) UNIX
|
||||||
* UNIX timestamp. (Note, sequence age is not currently supported.)
|
* timestamp. (Note, sequence age is not currently supported.)
|
||||||
* - `number` types should be displayed according the their configured
|
* - `number` types should be displayed according the their configured
|
||||||
* `decimals` and `unit` values.
|
* `decimals` and `unit` values.
|
||||||
* - `utf8` types should be displayed as utf8 strings.
|
* - `utf8` types should be displayed as utf8 strings.
|
||||||
@@ -270,19 +267,20 @@ export type NftCategoryField = {
|
|||||||
type: 'number';
|
type: 'number';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `aggregate` property indicates that aggregating this
|
* The `aggregate` property indicates that aggregating this field from
|
||||||
* field from multiple NFTs is desirable in user interfaces. For
|
* multiple NFTs is desirable in user interfaces. For example, for a
|
||||||
* example, for a field named `BCH Pledged` where `aggregate` is
|
* field named `BCH Pledged` where `aggregate` is `add`, the client
|
||||||
* `add`, the client can display a `Total BCH Pledged` in any
|
* can display a `Total BCH Pledged` in any user interface listing
|
||||||
* user interface listing more than one NFT.
|
* more than one NFT.
|
||||||
*
|
*
|
||||||
* If specified, clients should aggregate the field from all
|
* If specified, clients should aggregate the field from all NFTs, of
|
||||||
* NFTs within a view (e.g. NFTs held by a single wallet, NFTs
|
* all NFT types within the category, within a particular view (e.g.
|
||||||
* existing in a single transaction's outputs, etc.) using the
|
* NFTs held by a single wallet, NFTs existing in a single
|
||||||
* specified operation. Note, while aggregation could be
|
* transaction's outputs, etc.) using the specified operation.
|
||||||
* performed using any commutative operation – multiplication,
|
*
|
||||||
* bitwise AND, bitwise OR, and bitwise XOR, etc. – only `add`
|
* Note, while aggregation could be performed using any commutative
|
||||||
* is currently supported.
|
* operation – multiplication, bitwise AND, bitwise OR, bitwise XOR,
|
||||||
|
* etc. – only `add` is currently supported.
|
||||||
*/
|
*/
|
||||||
aggregate?: 'add';
|
aggregate?: 'add';
|
||||||
|
|
||||||
@@ -290,86 +288,88 @@ export type NftCategoryField = {
|
|||||||
* An integer between `0` and `18` (inclusive) indicating the
|
* An integer between `0` and `18` (inclusive) indicating the
|
||||||
* divisibility of the primary unit of this token field.
|
* divisibility of the primary unit of this token field.
|
||||||
*
|
*
|
||||||
* This is the number of digits that can appear after the
|
* This is the number of digits that can appear after the decimal
|
||||||
* decimal separator in amounts. For a field with a `decimals`
|
* separator in amounts. For a field with a `decimals` of `2`, a value
|
||||||
* of `2`, a value of `123456` should be displayed as `1234.56`.
|
* of `123456` should be displayed as `1234.56`.
|
||||||
*
|
*
|
||||||
* If omitted, defaults to `0`.
|
* If omitted, defaults to `0`.
|
||||||
*/
|
*/
|
||||||
decimals?: number;
|
decimals?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unit in which this field is denominated, taking the
|
* The unit in which this field is denominated, taking the `decimals`
|
||||||
* `decimals` value into account. If representing fungible token
|
* value into account. If representing fungible token amount, this
|
||||||
* amount, this will often be the symbol of the represented
|
* will often be the symbol of the represented token category.
|
||||||
* token category.
|
|
||||||
*
|
*
|
||||||
* E.g. `BCH`, `sats`, `AcmeUSD`, etc.
|
* E.g. `BCH`, `sats`, `AcmeUSD`, etc.
|
||||||
*
|
*
|
||||||
* If not provided, clients should not represent this field as
|
* If not provided, clients should not represent this field as having
|
||||||
* having a unit beyond the field's `name`.
|
* a unit beyond the field's `name`.
|
||||||
*/
|
*/
|
||||||
unit?: string;
|
unit?: string;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* A mapping of identifiers to URIs associated with this NFT field.
|
* A mapping of identifiers to URIs associated with this NFT field. URI
|
||||||
* URI identifiers may be widely-standardized or registry-specific.
|
* identifiers may be widely-standardized or registry-specific. Values must
|
||||||
* Values must be valid URIs, including a protocol prefix (e.g.
|
* be valid URIs, including a protocol prefix (e.g. `https://` or
|
||||||
* `https://` or `ipfs://`). Clients are only required to support
|
* `ipfs://`). Clients are only required to support `https` and `ipfs` URIs,
|
||||||
* `https` and `ipfs` URIs, but any scheme may be specified.
|
* but any scheme may be specified.
|
||||||
*/
|
*/
|
||||||
uris?: URIs;
|
uris?: URIs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of NFT field extension identifiers to extension
|
* A mapping of NFT field extension identifiers to extension definitions.
|
||||||
* definitions. {@link Extensions} may be widely standardized or
|
* {@link Extensions} may be widely standardized or application-specific.
|
||||||
* application-specific.
|
|
||||||
*/
|
*/
|
||||||
extensions?: Extensions;
|
extensions?: Extensions;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A definition specifying the non-fungible token information for a
|
* Interpretation information for a collection of sequential NFTs, a collection
|
||||||
* token category.
|
* in which each NFT includes only a sequential identifier within its on-chain
|
||||||
|
* commitment. Note that {@link SequentialNftCollection}s differ from
|
||||||
|
* {@link ParsableNftCollection}s in that sequential collections lack a
|
||||||
|
* parsing `bytecode` with which to inspect each NFT commitment: the type of
|
||||||
|
* each NFT is indexed by the full contents its commitment (interpreted as a
|
||||||
|
* positive VM integer in user interfaces).
|
||||||
*/
|
*/
|
||||||
export type NftCategory = {
|
export type SequentialNftCollection = {
|
||||||
/**
|
/**
|
||||||
* A string describing how this identity uses NFTs (for use in user
|
* A mapping of each NFT commitment (typically, a positive integer encoded as
|
||||||
* interfaces). Descriptions longer than `160` characters may be elided in
|
* a VM number) to metadata for that NFT type in this category.
|
||||||
* some interfaces.
|
|
||||||
*
|
|
||||||
* E.g.:
|
|
||||||
* - "ACME DEX NFT order receipts are issued when you place orders on the
|
|
||||||
* decentralized exchange. After orders are processed, order receipts can
|
|
||||||
* be redeemed for purchased tokens or sales proceeds.";
|
|
||||||
* - "ACME Game collectable NFTs unlock unique playable content, user
|
|
||||||
* avatars, and item skins in ACME Game Online."; etc.
|
|
||||||
*/
|
*/
|
||||||
description?: string;
|
types: {
|
||||||
|
/**
|
||||||
|
* Interpretation information for each type of NFT within the token
|
||||||
|
* category, indexed by commitment hex. For sequential NFTs, the on-chain
|
||||||
|
* commitment of each NFT is interpreted as a VM number to reference its
|
||||||
|
* particular NFT type in user interfaces. Issuing a sequential NFT with a
|
||||||
|
* negative or invalid VM number is discouraged, but clients may render the
|
||||||
|
* commitment of such NFTs in hex-encoded form, prefixed with `X`.
|
||||||
|
*/
|
||||||
|
[commitmentHex: string]: NftType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of field identifier to field definitions for the data fields
|
* Interpretation information for a collection of parsable NFTs, a collection
|
||||||
* that can appear in NFT commitments of this category.
|
* in which each NFT may include additional metadata fields beyond a sequential
|
||||||
|
* identifier within its on-chain commitment. Note that
|
||||||
|
* {@link ParsableNftCollection}s differ from {@link SequentialNftCollection}s
|
||||||
|
* in that parsable collections require a parsing `bytecode` with which to
|
||||||
|
* inspect each NFT commitment: the type of each NFT is indexed by the
|
||||||
|
* hex-encoded contents the bottom item on the altstack following the evaluation
|
||||||
|
* of the parsing bytecode.
|
||||||
*/
|
*/
|
||||||
fields?: NftCategoryField;
|
export type ParsableNftCollection = {
|
||||||
|
|
||||||
/**
|
|
||||||
* Parsing and interpretation information for all NFTs of this category;
|
|
||||||
* this enables generalized wallets to parse and display detailed
|
|
||||||
* information about all NFTs held by the wallet, e.g. `BCH Pledged`,
|
|
||||||
* `Order Price`, `Seat Number`, `Asset Number`,
|
|
||||||
* `IPFS Content Identifier`, `HTTPS URL`, etc.
|
|
||||||
*
|
|
||||||
* Parsing instructions are provided in the `bytecode` property, and the
|
|
||||||
* results are interpreted using the `types` property.
|
|
||||||
*/
|
|
||||||
parse: {
|
|
||||||
/**
|
/**
|
||||||
* A segment of hex-encoded Bitcoin Cash VM bytecode that parses UTXOs
|
* A segment of hex-encoded Bitcoin Cash VM bytecode that parses UTXOs
|
||||||
* holding NFTs of this category, identifies the NFT's type within the
|
* holding NFTs of this category, identifies the NFT's type within the
|
||||||
* category, and returns a list of the NFT's field values via the
|
* category, and returns a list of the NFT's field values via the
|
||||||
* altstack.
|
* altstack. If undefined, this NFT Category includes only sequential NFTs,
|
||||||
|
* with only an identifier and no NFT fields encoded in each NFT's
|
||||||
|
* on-chain commitment.
|
||||||
*
|
*
|
||||||
* The parse `bytecode` is evaluated by instantiating and partially
|
* The parse `bytecode` is evaluated by instantiating and partially
|
||||||
* verifying a standardized NFT parsing transaction:
|
* verifying a standardized NFT parsing transaction:
|
||||||
@@ -396,33 +396,76 @@ export type NftCategory = {
|
|||||||
* `types`. If no match is found, clients should represent the NFT as
|
* `types`. If no match is found, clients should represent the NFT as
|
||||||
* unable to be parsed.
|
* unable to be parsed.
|
||||||
*
|
*
|
||||||
* For example:
|
* For example: `00d2517f7c6b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_1 OP_SPLIT
|
||||||
* - `00d26b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_TOALTSTACK) takes the full
|
* OP_SWAP OP_TOALTSTACK OP_TOALTSTACK) splits the commitment after 1 byte,
|
||||||
* contents of the commitment as a fixed type; this can be used for
|
* pushing the first byte to the altstack as an NFT type identifier and the
|
||||||
* collections of unique, "numbered" NFTs. (Commitments of `01`, `02`,
|
|
||||||
* `03`, etc.)
|
|
||||||
* - `00d2517f7c6b` (OP_0 OP_UTXOTOKENCOMMITMENT OP_1 OP_SPLIT OP_SWAP
|
|
||||||
* OP_TOALTSTACK OP_TOALTSTACK) splits the commitment after 1 byte,
|
|
||||||
* pushing the first byte to the altstack as an NFT type and the
|
|
||||||
* remaining segment of the commitment as the first NFT field value.
|
* remaining segment of the commitment as the first NFT field value.
|
||||||
|
*
|
||||||
|
* If undefined (in a {@link SequentialNftCollection}), this field could be
|
||||||
|
* considered to have a default value of `00d26b` (OP_0 OP_UTXOTOKENCOMMITMENT
|
||||||
|
* OP_TOALTSTACK), which takes the full contents of the commitment as a fixed
|
||||||
|
* type index. As such, each index of the NFT category's `types` maps a
|
||||||
|
* precise commitment value to the metadata for NFTs with that particular
|
||||||
|
* commitment. E.g. an NFT with an empty commitment (VM number 0) maps to
|
||||||
|
* `types['']`, a commitment of `01` (hex) maps to `types['01']`, etc. This
|
||||||
|
* pattern is used for collections of sequential NFTs.
|
||||||
*/
|
*/
|
||||||
bytecode: string;
|
bytecode: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of hex-encoded values to definitions of possible NFT types
|
* A mapping of hex-encoded values to definitions of possible NFT types
|
||||||
* in this category.
|
* in this category.
|
||||||
*/
|
*/
|
||||||
types: {
|
types: {
|
||||||
/**
|
/**
|
||||||
* A definitions for each type of NFT within the token category. NFT
|
* A definitions for each type of NFT within the token category. Parsable
|
||||||
* types are indexed by the expected hex-encoded value of the bottom
|
* NFT types are indexed by the hex-encoded value of the bottom altstack
|
||||||
* altstack item following evaluation of `token.nfts.parse.bytecode`.
|
* item following evaluation of `NftCategory.parse.bytecode`. The remaining
|
||||||
* The remaining altstack items are mapped to NFT fields according to
|
* altstack items are mapped to NFT fields according to the `fields`
|
||||||
* the `fields` property of the matching NFT type.
|
* property of the matching NFT type.
|
||||||
*/
|
*/
|
||||||
[bottomAltStackItemHex: string]: NftType;
|
[bottomAltstackHex: string]: NftType;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A definition specifying the non-fungible token information for a
|
||||||
|
* token category.
|
||||||
|
*/
|
||||||
|
export type NftCategory = {
|
||||||
|
/**
|
||||||
|
* A string describing how this identity uses NFTs (for use in user
|
||||||
|
* interfaces). Descriptions longer than `160` characters may be elided in
|
||||||
|
* some interfaces.
|
||||||
|
*
|
||||||
|
* E.g.:
|
||||||
|
* - "ACME DEX NFT order receipts are issued when you place orders on the
|
||||||
|
* decentralized exchange. After orders are processed, order receipts can
|
||||||
|
* be redeemed for purchased tokens or sales proceeds.";
|
||||||
|
* - "ACME Game collectable NFTs unlock unique playable content, user
|
||||||
|
* avatars, and item skins in ACME Game Online."; etc.
|
||||||
|
*/
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mapping of field identifier to field definitions for the data fields
|
||||||
|
* that can appear in NFT commitments of this category.
|
||||||
|
*
|
||||||
|
* Categories including only sequential NFTs (where `parse.bytecode` is
|
||||||
|
* undefined) should omit `fields` (or set to `undefined`).
|
||||||
|
*/
|
||||||
|
fields?: NftCategoryField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsing and interpretation information for all NFTs of this category;
|
||||||
|
* this enables generalized wallets to parse and display detailed
|
||||||
|
* information about all NFTs held by the wallet, e.g. `BCH Pledged`,
|
||||||
|
* `Order Price`, `Seat Number`, `Asset Number`,
|
||||||
|
* `IPFS Content Identifier`, `HTTPS URL`, etc.
|
||||||
|
*
|
||||||
|
* Parsing instructions are provided in the `bytecode` property, and the
|
||||||
|
* results are interpreted using the `types` property.
|
||||||
|
*/
|
||||||
|
parse: SequentialNftCollection | ParsableNftCollection;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
# Registry Examples
|
||||||
|
|
||||||
|
This directory includes several [Bitcoin Cash Metadata Registry](../readme.md) examples. Each example is briefly described below.
|
||||||
|
|
||||||
|
## Fungible Token
|
||||||
|
|
||||||
|
The [`fungible-token.json` example](./examples/fungible-token.json) demonstrates how the issuer of a single kind of fungible token might publish information about the token.
|
||||||
|
|
||||||
|
The registry includes only one identity, `Example Asset`, defined as authbase `89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c` with a matching token category ID. `Example Asset` has the ticker symbol `XAMPL` and 6 decimal places, i.e. `1,000,000` fungible tokens should be displayed as `1.000000 XAMPL`.
|
||||||
|
|
||||||
|
The registry also includes some historical background for `Example Asset` – it was first released at `2023-01-03T00:00:00.000Z`, with the ticker symbol `EXAMPLE` and 8 decimal places before being redenominated and rebranded; from a user's perspective, `1 EXAMPLE` became `100 XAMPL`. The rebranding and re-denomination migration began at `2023-01-13T00:00:00.000Z` and was considered complete at `2023-02-13T00:00:00.000Z`. The old `icon` and a `web` URI providing more information about the migration is also included.
|
||||||
|
|
||||||
|
For more detail, see the [descriptions in the example](./examples/fungible-token.json).
|
||||||
|
|
||||||
|
## Art Collection
|
||||||
|
|
||||||
|
The [`art-collection.json` example](./examples/art-collection.json) demonstrates how the issuer of a non-fungible token (NFT) art collection might publish information about each token in the collection.
|
||||||
|
|
||||||
|
The registry includes only one identity, `Example NFT Collection`, defined as authbase `89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c` with a matching token category ID.
|
||||||
|
|
||||||
|
`Example NFT Collection` has the ticker symbol `XAMPLZ` and defines metadata for 3 sequential NFTs, `Example #0` (`XAMPLZ-0`), `Example #1` (`XAMPLZ-1`), and `Example #2` (`XAMPLZ-2`). The `icon` for each NFT is published via IPFS (as [recommended](./readme.md#publication-of-static-data)), so clients may download each icon by querying IPFS or by using [HTTP Gateways](https://docs.ipfs.tech/reference/http/gateway/), e.g. [`ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg`](ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg) may be accessed via CloudFlare's gateway at [`https://cf-ipfs.com/ipfs/bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg`](https://cf-ipfs.com/ipfs/bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg).
|
||||||
|
|
||||||
|
For more detail, see the [descriptions in the example](./examples/art-collection.json).
|
||||||
|
|
||||||
|
## Decentralized Application
|
||||||
|
|
||||||
|
The `decentralized-application.json` example 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).
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"$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 registry demonstrating how the issuer of a non-fungible token (NFT) art collection might publish information about the collection.",
|
||||||
|
"uris": {
|
||||||
|
"icon": "https://example.com/registry-icon.svg",
|
||||||
|
"web": "https://example.com/",
|
||||||
|
"registry": "https://example.com/.well-known/bitcoin-cash-metadata-registry.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"identities": {
|
||||||
|
"89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c": {
|
||||||
|
"2023-01-13T00:00:00.000Z": {
|
||||||
|
"name": "Example NFT Collection",
|
||||||
|
"description": "This is a short description of the collection; 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 collection defines metadata for 3 sequential NFTs, Example #0 (XAMPLZ-0), Example #1 (XAMPLZ-1), and Example #2 (XAMPLZ-2). Note that the 'icon' for each NFT is published via IPFS, so clients may download each icon by querying IPFS or by using an IPFS HTTP Gateway.",
|
||||||
|
"token": {
|
||||||
|
"category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c",
|
||||||
|
"symbol": "XAMPLZ",
|
||||||
|
"nfts": {
|
||||||
|
"parse": {
|
||||||
|
"types": {
|
||||||
|
"": {
|
||||||
|
"name": "Example #0",
|
||||||
|
"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 XAMPLZ-0.",
|
||||||
|
"uris": {
|
||||||
|
"icon": "ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg",
|
||||||
|
"web": "https://example.com/xamplz/0/details"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"01": {
|
||||||
|
"name": "Example #1",
|
||||||
|
"description": "An NFT of this category with an on-chain commitment of 0x01 (VM number 1). Where appropriate, user interfaces may display the ticker symbol of NFTs matching this type as XAMPLZ-1.",
|
||||||
|
"uris": {
|
||||||
|
"icon": "ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/1.svg",
|
||||||
|
"web": "https://example.com/xamplz/1/details",
|
||||||
|
"custom-uri-identifier": "protocol://data-for-some-protocol"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"02": {
|
||||||
|
"name": "Example #2",
|
||||||
|
"description": "An NFT of this category with an on-chain commitment of 0x02 (VM number 2). Where appropriate, user interfaces may display the ticker symbol of NFTs matching this type as XAMPLZ-2.",
|
||||||
|
"uris": {
|
||||||
|
"icon": "ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/2.svg",
|
||||||
|
"web": "https://example.com/another/path",
|
||||||
|
"another-uri-identifier": "protocol://data-for-that-protocol"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uris": {
|
||||||
|
"icon": "https://example.com/xamplz-icon.svg",
|
||||||
|
"web": "https://example.com/about-xamplz-nfts",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"$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 registry demonstrating how a metadata registry might publish information about a decentralized application.",
|
||||||
|
"uris": {
|
||||||
|
"icon": "https://example.com/registry-icon.svg",
|
||||||
|
"web": "https://example.com/",
|
||||||
|
"registry": "https://example.com/.well-known/bitcoin-cash-metadata-registry.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"identities": {
|
||||||
|
"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).",
|
||||||
|
"token": {
|
||||||
|
"category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c",
|
||||||
|
"symbol": "CFC-2023-XAMPL",
|
||||||
|
"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": {
|
||||||
|
"pledgeValue": {
|
||||||
|
"name": "Pledge Value",
|
||||||
|
"description": "The value pledged to the campaign in Bitcoin Cash.\n\nThis example 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 with Pledge Values of 123456 and 654321, the wallet can display a total of the user's pledges to this campaign: 0.00777777 BCH.",
|
||||||
|
"encoding": {
|
||||||
|
"type": "number",
|
||||||
|
"aggregate": "add",
|
||||||
|
"decimals": 8,
|
||||||
|
"unit": "BCH"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse": {
|
||||||
|
"bytecode": "006b00cf6b",
|
||||||
|
"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.",
|
||||||
|
"fields": ["pledgeValue"],
|
||||||
|
"uris": {
|
||||||
|
"icon": "ipfs://bafybeihnmh5bkbaspp3xfdanje74pekhsklhobzzraeyywq6gcpb3iuvey/0.svg",
|
||||||
|
"web": "https://example.com/campaign",
|
||||||
|
"custom-uri-identifier": "protocol://data-for-some-protocol"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uris": {
|
||||||
|
"icon": "https://example.com/campaign-icon.svg",
|
||||||
|
"web": "https://example.com/about-campaign",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"latestRevision": "2023-04-14T00:00:17.720Z",
|
"latestRevision": "2023-04-14T00:00:17.720Z",
|
||||||
"registryIdentity": {
|
"registryIdentity": {
|
||||||
"name": "Example.com Token Registry",
|
"name": "Example.com Token Registry",
|
||||||
"description": "An example registry demonstrating how the issuer of a single fungible token might publish information about the token.",
|
"description": "An example registry demonstrating how the issuer of a single kind of fungible token might publish information about the token.",
|
||||||
"uris": {
|
"uris": {
|
||||||
"icon": "https://example.com/registry-icon.svg",
|
"icon": "https://example.com/registry-icon.svg",
|
||||||
"web": "https://example.com/",
|
"web": "https://example.com/",
|
||||||
@@ -35,10 +35,10 @@
|
|||||||
},
|
},
|
||||||
"2023-01-03T00:00:00.000Z": {
|
"2023-01-03T00:00:00.000Z": {
|
||||||
"name": "Example Asset",
|
"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 2 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 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.",
|
||||||
"token": {
|
"token": {
|
||||||
"category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c",
|
"category": "89cad9e3e34280eb1e8bc420542c00a7fcc01002b663dbf7f38bceddf80e680c",
|
||||||
"decimals": 2,
|
"decimals": 8,
|
||||||
"symbol": "EXAMPLE"
|
"symbol": "EXAMPLE"
|
||||||
},
|
},
|
||||||
"uris": {
|
"uris": {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
Maintainer: Jason Dreyzehner
|
Maintainer: Jason Dreyzehner
|
||||||
Status: Draft
|
Status: Draft
|
||||||
Initial Publication Date: 2022-10-31
|
Initial Publication Date: 2022-10-31
|
||||||
Latest Revision Date: 2022-04-13
|
Latest Revision Date: 2023-05-01
|
||||||
Version: 2.0.0-draft
|
Version: 2.0.0-draft
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
- [Benefits](#benefits)
|
- [Benefits](#benefits)
|
||||||
- [Technical Specification](#technical-specification)
|
- [Technical Specification](#technical-specification)
|
||||||
- [Rationale](#rationale)
|
- [Rationale](#rationale)
|
||||||
- [Prior Art & Alternatives](#prior-art--alternatives)
|
|
||||||
- [Test Vectors](#test-vectors)
|
- [Test Vectors](#test-vectors)
|
||||||
- [Implementations](#implementations)
|
- [Implementations](#implementations)
|
||||||
- [Feedback & Reviews](#feedback--reviews)
|
- [Feedback & Reviews](#feedback--reviews)
|
||||||
@@ -49,7 +48,7 @@ Metadata registries use an extensible JSON schema, ensuring a baseline of compat
|
|||||||
|
|
||||||
### Interpretation of NFT Commitments
|
### Interpretation of NFT Commitments
|
||||||
|
|
||||||
Registries can encode structured information about NFT commitment APIs, allowing ecosystem software to parse and understand the contents of any NFT. This enables generalized user interfaces for all NFTs, and application-specific extensions can build on this NFT parsing infrastructure to enable richer experiences – for example:
|
Registries can encode structured information about non-fungible token (NFT) commitment APIs, allowing ecosystem software to parse and understand the contents of any NFT. This enables generalized user interfaces for all NFTs, and application-specific extensions can build on this NFT parsing infrastructure to enable richer experiences – for example:
|
||||||
|
|
||||||
- A table of the user's open orders for a decentralized exchange with sums for "Total Tokens for Sale" and "Total BCH Order Value".
|
- A table of the user's open orders for a decentralized exchange with sums for "Total Tokens for Sale" and "Total BCH Order Value".
|
||||||
- A list of the user's active crowdfunding pledges with information on each campaign and a sum of "Total BCH Pledged".
|
- A list of the user's active crowdfunding pledges with information on each campaign and a sum of "Total BCH Pledged".
|
||||||
@@ -325,7 +324,39 @@ Clients may use the `authchain` extension to rapidly update their records for a
|
|||||||
|
|
||||||
### Guidelines for Token Issuers
|
### Guidelines for Token Issuers
|
||||||
|
|
||||||
If additional fungible tokens may be needed in the future, token issuers should initially mint an excess supply and hold them 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 [Reserved Supply](https://github.com/bitjson/cashtokens#reserved-supply). This enables light-client verification of the maximum possible [Circulating Supply](https://github.com/bitjson/cashtokens#circulating-supply).
|
The following recommendations are made for issuers of CashTokens.
|
||||||
|
|
||||||
|
#### 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).
|
||||||
|
|
||||||
|
#### Associating Information with NFTs
|
||||||
|
|
||||||
|
Issuers of non-fungible tokens (NFTs) can associate icons, traits, and other information with each NFT via one of two general strategies:
|
||||||
|
|
||||||
|
- **Sequential NFTs**: the on-chain commitments of the category's NFTs include only a positive integer identifier. All other metadata for each NFT is associated with the NFT's identifier by metadata registries. An `NftCategory` with an undefined `parse.bytecode` value uses sequential NFTs.
|
||||||
|
- **Parsable NFTs**: the on-chain commitments of the category's NFTs directly include parsable metadata for each NFT. The procedure for identifying and parsing the meaning of each NFT type in the category is propagated by metadata registries. An `NftCategory` with a defined `parse.bytecode` value uses parsable NFTs.
|
||||||
|
|
||||||
|
Where possible, NFT issuers should prefer to issue sequential NFTs unless the intended use case requires parsable NFTs. Sequential NFTs are more efficient than parsable NFTs, reducing the bandwidth and transaction fees required to transfer each NFT. Because all metadata beyond the NFT's identifier is stored off-chain and resolved using metadata registries, sequential NFTs have no meaningful limits on the composition or size their associated metadata. (Note, categories using sequential NFTs should not include any `NftCategory.fields` or `NftType.fields`.)
|
||||||
|
|
||||||
|
Parsable NFTs encode up to 40 bytes of metadata in their on-chain commitment. Encoding this metadata in the on-chain commitment allows each NFT to convey authenticated information to on-chain contract systems and sparsely-connected, off-chain systems at the cost of a slight increase in required transaction fees and dust output values. Parsable NFTs can encode a wide variety of NFT **fields**, data shared by all NFTs of a particular type, e.g. `BCH Pledged`, `Tokens Sold`, `Settlement Locktime`, `Seat Number`, `IPFS Content Identifier`, `HTTPS URL`, etc. Like sequential NFTs, parsable NFTs may also have additional off-chain metadata associated with each NFT type in the category.
|
||||||
|
|
||||||
|
Note that for both sequential and parsable NFTs, it's technically possible to create multiple NFTs with precisely the same commitment; these are sometimes called **semi-fungible tokens**. Often, token categories with parsable NFTs require support for these semi-fungible tokens (e.g. two pledge receipts for precisely the same BCH amount are mutually fungible), while sequential NFT use cases often require strict uniqueness. To guarantee uniqueness, token issuers may choose to either:
|
||||||
|
|
||||||
|
- Issue all NFTs in one or a few initial minting transactions (thereafter burning any minting NFTs), or
|
||||||
|
- Assign all minting NFTs for the token category to covenant contracts that enforce uniqueness in all future minting transactions.
|
||||||
|
|
||||||
|
### Guidelines for Registry Publishers
|
||||||
|
|
||||||
|
The following recommendations are made for publishers of Bitcoin Cash Metadata Registries, and this proposal includes [several example registries](./examples.md).
|
||||||
|
|
||||||
|
#### Authentication of Static Data
|
||||||
|
|
||||||
|
Registry publishers should ensure that URIs expected to reference remote, static data (e.g. `icon`, `icon-intro`, and other image or binary data) use either content-addressed `IPFS` URIs or `HTTPS` URIs referencing only domains trusted by or under the control of the registry publisher/identity. This ensures that static data remains available over time and prevents attacks in which other entities could replace static data with ambiguous, misleading, or malicious content.
|
||||||
|
|
||||||
|
#### Publication of Static Data
|
||||||
|
|
||||||
|
Where possible, registry publishers should publish static data using content-addressed `IPFS` URIs; this enables caching and deduplication across registries, more resilient resource resolution, and data integrity guarantees. Static data should be either individually-addressed or archive-addressed within an archive containing only static data required by that registry, that is, files not referenced by the registry and the registry file itself should be excluded from the archive.
|
||||||
|
|
||||||
### Guidelines for Client Software
|
### Guidelines for Client Software
|
||||||
|
|
||||||
@@ -359,9 +390,55 @@ Each `IdentitySnapshot` is assigned to a timestamp at which the snapshot began o
|
|||||||
|
|
||||||
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 latest and previous snapshots when timestamps are lexicographically sorted).
|
||||||
|
|
||||||
|
#### Rendering NFTs in User Interfaces
|
||||||
|
|
||||||
|
Metadata registry entries for identities which incorporate non-fungible tokens (NFTs) should include an `NftCategory` definition that describes how clients may ascertain the meaning of NFTs in that category. There are [two general classifications of NFTs](#associating-information-with-nfts) distinguished by how metadata is associated with each NFT: **sequential NFTs** and **parsable NFTs**. An `NftCategory` where `parse.bytecode` is `undefined` uses sequential NFTs; those with a defined `parse.bytecode` use parsable NFTs.
|
||||||
|
|
||||||
|
While some clients may support additional rendering standards for ecosystems like ticketing, access passes, crowdfunding, trading, gaming, digital art, and other application-specific verticals, a particular client is considered to fully support NFT rendering if the below, minimal rendering requirements for both **sequential NFTs** and **parsable NFTs** are supported.
|
||||||
|
|
||||||
|
##### Sequential NFTs
|
||||||
|
|
||||||
|
Sequential NFTs belong to an `NftCategory` where `parse.bytecode` is undefined. It is not necessary to evaluate any parsing bytecode to derive the meaning of sequential NFTs: each commitment value is a VM number mapping directly to an index of `parse.types`.
|
||||||
|
|
||||||
|
At minimum, user interfaces displaying sequential NFTs should provide for rendering each NFT's `name`, icon (`uris.icon`), `description`, and `web` URI (`uris.web`). It must also be possible to list and either copy or activate all other provided URIs, though clients are not expected to provide special handling for any particular URI identifiers beyond `icon` and `web` (see [URI Identifiers](#uri-identifiers)).
|
||||||
|
|
||||||
|
It is technically possible for NFTs in sequential NFT categories to contain commitments that decode to negative or invalid VM numbers; these NFTs should be considered to have a `name` equivalent to their [NFT ticker symbol](#nft-ticker-symbols) and no icon or other metadata.
|
||||||
|
|
||||||
|
##### Parsable NFTs
|
||||||
|
|
||||||
|
Parsable NFTs belong to an `NftCategory` where `parse.bytecode` is defined. To derive the meaning of a parsable NFT, clients evaluate each NFT using `parse.bytecode`, a segment of hex-encoded Bitcoin Cash VM bytecode that parses UTXOs holding NFTs of this category, identifies the NFT's type within the category (among `parse.types`), and returns a list of the NFT's field values via the altstack. Evaluation results are deterministic for the life of each UTXO, so clients can permanently store parsed metadata alongside the UTXO. See [the documentation for `ParsableNftCollection`](./bcmr-v2.schema.ts#ParsableNftCollection) for details.
|
||||||
|
|
||||||
|
Each type of parsable NFT (as specified in `parse.types`) incorporates a particular set of **fields** for that NFT type, e.g. `BCH Pledged`, `Tokens Sold`, `Settlement Locktime`, etc. (see [Associating Information with NFTs](#associating-information-with-nfts) for details); these fields can be modeled as columns in a table of NFTs of that type.
|
||||||
|
|
||||||
|
At a minimum, user interfaces displaying parsable NFTs should provide for rendering each NFT type as a grouping of NFTs (e.g. as independent tables), where the assigned fields are displayed within each grouping (e.g. as table columns). The contents of each field should be rendered according to the fields specified `encoding`, see the [the documentation for `NftCategoryField`](./bcmr-v2.schema.ts#NftCategoryField) for details.
|
||||||
|
|
||||||
|
#### 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**.
|
||||||
|
|
||||||
|
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`).
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
This section documents design decisions made in this specification.
|
||||||
|
|
||||||
|
### Use of Absolute URIs
|
||||||
|
|
||||||
|
This standard [requires all URIs to be fully qualified](#uri-identifiers), including protocol prefix (e.g. `https://` or `ipfs://`). This requirement simplifies client implementations and eliminates several authentication vulnerabilities.
|
||||||
|
|
||||||
|
Alternatively, this proposal could allow for relative `HTTPS` URIs, reducing the file size and complexity of some registries. However, this change would also entail several significant tradeoffs:
|
||||||
|
|
||||||
|
- **Resolution ambiguity**: resolution of a relative URI is typically based on the URI of the referencing resource; this would prevent a registry from maintaining its semantic meaning when downloaded or relocated.
|
||||||
|
- **Tight coupling with protocol**: relative URIs typically exclude the URI protocol identifier (`https://`, `ipfs://`, etc.) as well as the path to the resolver's working directory. A relative URI scheme would require standardizing expected client handling of relative URIs for both `HTTPS` and `IPFS`, and introduce additional ambiguity in the use of other protocols.
|
||||||
|
- **Inconsistency across registries**: while snapshots containing absolute URIs can safely be copied between registries (e.g. by pulling changes made by a token issuer into an embedded registry), relative URIs require special handling in both publisher updates and client verification.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Test Vectors
|
## Test Vectors
|
||||||
|
|
||||||
_(pending initial implementations)_
|
A variety of [Bitcoin Cash Metadata Registry examples](./examples.md) are provided in this proposal.
|
||||||
|
|
||||||
## Implementations
|
## Implementations
|
||||||
|
|
||||||
@@ -374,6 +451,10 @@ _(pending initial implementations)_
|
|||||||
- [CHIP-BCMR Issues](https://github.com/bitjson/chip-bcmr/issues)
|
- [CHIP-BCMR Issues](https://github.com/bitjson/chip-bcmr/issues)
|
||||||
- [`CHIP-BCMR: Bitcoin Cash Metadata Registries` - Bitcoin Cash Research](https://bitcoincashresearch.org/t/chip-bcmr-bitcoin-cash-metadata-registries/942)
|
- [`CHIP-BCMR: Bitcoin Cash Metadata Registries` - Bitcoin Cash Research](https://bitcoincashresearch.org/t/chip-bcmr-bitcoin-cash-metadata-registries/942)
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
Thank you to [Mathieu Geukens](https://github.com/mr-zwets), [bitcoincashautist](https://github.com/A60AB5450353F40E), and [Tom Zander](https://github.com/zander) for reviewing and contributing improvements to this proposal, providing feedback, and promoting consensus among stakeholders.
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
This section summarizes the evolution of this document.
|
This section summarizes the evolution of this document.
|
||||||
@@ -384,6 +465,8 @@ This section summarizes the evolution of this document.
|
|||||||
- Simplified registry's conception of time ([#7](https://github.com/bitjson/chip-bcmr/pull/7))
|
- Simplified registry's conception of time ([#7](https://github.com/bitjson/chip-bcmr/pull/7))
|
||||||
- Standardized parsing transaction to eliminate undefined behavior ([#7](https://github.com/bitjson/chip-bcmr/pull/7))
|
- 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))
|
- 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
|
||||||
- **v1.0.0 – 2022-10-31** ([`5b24b0ec`](https://github.com/bitjson/chip-bcmr/blob/5b24b0ec93cf9316222ab2ea2e2ffe8a9f390b12/readme.md))
|
- **v1.0.0 – 2022-10-31** ([`5b24b0ec`](https://github.com/bitjson/chip-bcmr/blob/5b24b0ec93cf9316222ab2ea2e2ffe8a9f390b12/readme.md))
|
||||||
- Initial publication
|
- Initial publication
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user