From 53ced8011688483b1b92e0f8aade0deb222b97cd Mon Sep 17 00:00:00 2001 From: George Flerovsky Date: Sun, 26 Feb 2023 20:30:51 -0500 Subject: [PATCH 1/6] Apply semantic line breaks to README See [Semantic Line Breaks](https://sembr.org/) --- CIP-????/README.md | 558 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 450 insertions(+), 108 deletions(-) diff --git a/CIP-????/README.md b/CIP-????/README.md index 5f23a233d0..debfa60e7a 100644 --- a/CIP-????/README.md +++ b/CIP-????/README.md @@ -18,55 +18,146 @@ License: CC-BY-4.0 ## Abstract -This proposal extends the CIP-25 standard for defining and updating token metadata via transaction metadata, by providing a new mechanism to update token metadata without having to mint or burn tokens, while maintaining full backward compatibility with CIP-25. The new mechanism is capable of expressing metadata updates more efficiently than CIP-25 updates. +This proposal extends the CIP-25 standard for defining +and updating token metadata via transaction metadata, +by providing a new mechanism to update token metadata +without having to mint or burn tokens, +while maintaining full backward compatibility with CIP-25. +The new mechanism is capable of expressing metadata updates +more efficiently than CIP-25 updates. ## Motivation: why is this CIP necessary? -On Cardano’s eUTxO ledger, native tokens exist without any inherently attached metadata. The ledger does not provide a direct method for preserving any information associated with an asset class of native tokens, as transactions move the tokens from one UTxO to another. - -The Media NFT Metadata Standard ([CIP-25](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0025)) proposed an indirect way to attach metadata to an asset class — using the metadata field (CBOR tag 721) of its minting transactions. When there are multiple minting transactions for the same asset class, the latest minting transaction’s metadata overrides all previous metadata defined for that asset class. - -CIP-25 is widely supported across the Cardano community by blockchain indexers, wallet providers, marketplace applications, and other stakeholders. It has served the community quite well so far, in particular for non-fungible tokens (NFTs) with static metadata (i.e. intended to be mostly immutable after minting). - -However, the CIP-25 metadata update mechanism is suboptimal because it requires tokens of the asset class to be minted or burned whenever its metadata is updated. This is incongruous with the often purely-informational purpose of metadata update transactions. While it may sometimes be convenient to combine token minting and metadata updates within the same transaction (e.g. to save on transaction fees), there is a broad range of applications where metadata updates need to be more frequent and independent from minting events. +On Cardano’s eUTxO ledger, native tokens exist +without any inherently attached metadata. +The ledger does not provide a direct method for +preserving any information associated with an asset class of native tokens, +as transactions move the tokens from one UTxO to another. + +The Media NFT Metadata Standard +([CIP-25](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0025)) +proposed an indirect way to attach metadata to an asset class — +using the metadata field (CBOR tag 721) of its minting transactions. +When there are multiple minting transactions for the same asset class, +the latest minting transaction’s metadata overrides +all previous metadata defined for that asset class. + +CIP-25 is widely supported across the Cardano community by blockchain indexers, +wallet providers, marketplace applications, and other stakeholders. +It has served the community quite well so far, +in particular for non-fungible tokens (NFTs) with static metadata +(i.e. intended to be mostly immutable after minting). + +However, the CIP-25 metadata update mechanism is suboptimal +because it requires tokens of the asset class to be minted or burned +whenever its metadata is updated. +This is incongruous with the often purely-informational +purpose of metadata update transactions. +While it may sometimes be convenient +to combine token minting and metadata updates +within the same transaction (e.g. to save on transaction fees), +there is a broad range of applications where +metadata updates need to be more frequent and independent from minting events. In practice, there are three main drawbacks to the CIP-25 update mechanism: -1. The metadata authority for an asset class must retain the ability to mint/burn more tokens of the asset class if it wants to retain the ability to post metadata updates/corrections. This compromises the token issuers’ guarantees to token holders that the token supply (or NFT collections) will not be diluted in the future. Token holders are being asked to trust that the metadata authority will not abuse its power to mint new tokens of the same asset class. -2. Executing a minting policy script to mint or burn tokens every time that an asset class’ metadata is updated incurs script execution fees, particularly if the script is Plutus-based, far exceeding the fees corresponding to the actual informational content of the metadata update. This cost can become severe for large collections of thousands of NFTs, and it may be prohibitive for implementing any dynamic fields for such large NFT collections. -3. An asset class’ metadata can only be updated as a whole. This is inefficient (both in terms of transaction fees and ledger bloat) when only a small subset of the metadata fields needs to be updated for a large collection of NFTs, and it can lead to errors if the unmodified fields are improperly copied over to the update. - -We encountered these drawbacks during the development of an NFT gaming application, where thousands of NFTs correspond to game assets with properties (e.g. horse age, physical and mental abilities) that evolve both over time and as the NFTs participate in various game-related events on-chain. These properties do not need to interact with smart contract logic directly, which makes transaction metadata a more appropriate place to define them, rather than UTxO datums with complicated state-evolution logic. - -Our NFT gaming application is leveraging the Cardano immutable ledger, rather than an off-chain database, as the single source of truth to track the evolution of the game assets’ most important properties. This reassures users that the history of metadata states for a game NFT cannot be retroactively erased or altered by the game admins — although the game admins have the authority to modify metadata going forward, they are retroactively accountable for all past updates. - -We expect that this proposal will be useful to many other applications that need to track dynamic on-chain metadata for large NFT collections, particularly in the growing NFT gaming sector. It may also be useful for artists to update on-chain metadata about their art NFTs (e.g. royalty payment receiving address) without having to burn or mint anything. +1. The metadata authority for an asset class must +retain the ability to mint/burn more tokens of the asset class +if it wants to retain the ability to post metadata updates/corrections. +This compromises the token issuers’ guarantees to token holders +that the token supply (or NFT collections) will not be diluted in the future. +Token holders are being asked to trust +that the metadata authority will not abuse its power +to mint new tokens of the same asset class. +2. Executing a minting policy script to mint or burn tokens +every time that an asset class’ metadata is updated +incurs script execution fees, particularly if the script is Plutus-based, +far exceeding the fees corresponding to +the actual informational content of the metadata update. +This cost can become severe for large collections of thousands of NFTs, +and it may be prohibitive for implementing +any dynamic fields for such large NFT collections. +3. An asset class’ metadata can only be updated as a whole. +This is inefficient (both in terms of transaction fees and ledger bloat) +when only a small subset of the metadata fields needs to be updated +for a large collection of NFTs, +and it can lead to errors +if the unmodified fields are improperly copied over to the update. + +We encountered these drawbacks during the development +of an NFT gaming application, +where thousands of NFTs correspond to game assets with properties +(e.g. horse age, physical and mental abilities) +that evolve both over time and as the NFTs participate +in various game-related events on-chain. +These properties do not need to interact with smart contract logic directly, +which makes transaction metadata a more appropriate place to define them, +rather than UTxO datums with complicated state-evolution logic. + +Our NFT gaming application is leveraging the Cardano immutable ledger, +rather than an off-chain database, +as the single source of truth to track the evolution +of the game assets’ most important properties. +This reassures users that the history of metadata states for a game NFT +cannot be retroactively erased or altered by the game admins — +although the game admins have the authority to modify metadata going forward, +they are retroactively accountable for all past updates. + +We expect that this proposal will be useful to many other applications +that need to track dynamic on-chain metadata for large NFT collections, +particularly in the growing NFT gaming sector. +It may also be useful for artists to update on-chain metadata +about their art NFTs (e.g. royalty payment receiving address) +without having to burn or mint anything. ## Specification Our proposal extends CIP-25 with a new update mechanism: - A metadata oracle can be assigned for a given policy ID. -- For a policy with a metadata oracle assigned, the metadata oracle can post CIP-???? updates to add, remove, or modify any fields of the token’s CIP-25 metadata, without needing to mint or burn any tokens in the metadata update transactions. -- The current metadata for a token can be deterministically reconstructed by starting from the latest CIP-25 update to the token, and applying the subsequent CIP-???? metadata updates in ascending blockchain transaction order. +- For a policy with a metadata oracle assigned, +the metadata oracle can post CIP-???? updates to add, remove, or modify +any fields of the token’s CIP-25 metadata, +without needing to mint or burn any tokens in the metadata update transactions. +- The current metadata for a token can be deterministically reconstructed +by starting from the latest CIP-25 update to the token, +and applying the subsequent CIP-???? metadata updates +in ascending blockchain transaction order. Both CIP-25 and CIP-???? updates affect the token metadata: -- A CIP-25 update removes all fields in the current metadata state and replaces them with a new complete definition of the metadata state. -- A CIP-???? update selectively adds, removes, or modifies fields relative to the current metadata state. It does not apply to any asset class that has not had tokens previously minted or any asset class that has not had CIP-25 metadata previously defined, before the CIP-???? update. +- A CIP-25 update removes all fields in the current metadata state +and replaces them with a new complete definition of the metadata state. +- A CIP-???? update selectively adds, removes, or modifies fields +relative to the current metadata state. +It does not apply to any asset class that has not had tokens previously minted +or any asset class that has not had CIP-25 metadata previously defined, +before the CIP-???? update. -However, we recommend that CIP-???? adopters use the new metadata update mechanism exclusively to manage all updates to their token metadata after the initial CIP-25 metadata is set. +However, we recommend that CIP-???? adopters use +the new metadata update mechanism exclusively +to manage all updates to their token metadata +after the initial CIP-25 metadata is set. -Our proposal only affects the `????` top-level CBOR tag of the metadata field in Cardano transactions. (Note: we’re using `????` as a stand-in for the CIP number that will eventually be assigned to this proposal.) +Our proposal only affects the `????` top-level CBOR tag +of the metadata field in Cardano transactions. +(Note: we’re using `????` as a stand-in for the CIP number +that will eventually be assigned to this proposal.) ### Assigning a metadata oracle for a token policy ID A token metadata oracle is defined via two addresses: -- An oracle update address that is authorized to post metadata updates. For example, this address could be controlled by a bot that automatically posts token metadata updates as needed to the blockchain. -- An oracle main address that is used to update the oracle update address. This address should be controlled via private keys held in cold storage or hardware wallets. +- An oracle update address that is authorized to post metadata updates. +For example, this address could be controlled by a bot that automatically +posts token metadata updates as needed to the blockchain. +- An oracle main address that is used to update the oracle update address. +This address should be controlled via private keys +held in cold storage or hardware wallets. -A metadata oracle can be explicitly assigned to a policy ID by setting the following metadata in a transaction that mints or burns tokens for that policy: +A metadata oracle can be explicitly assigned to a policy ID +by setting the following metadata in a transaction +that mints or burns tokens for that policy: ```json { @@ -81,13 +172,26 @@ A metadata oracle can be explicitly assigned to a policy ID by setting the follo } ``` -While a metadata oracle is not explicitly assigned to a native-script-based policy ID, the policy ID is implicitly assigned a metadata oracle with both addresses set to an address derived from the native script. Specifically, they are both set to an Enterprise address constructed by setting the payment key to the verification key from the native script and keeping the staking key empty. +While a metadata oracle is not explicitly assigned +to a native-script-based policy ID, +the policy ID is implicitly assigned a metadata oracle +with both addresses set to an address derived from the native script. +Specifically, they are both set to an Enterprise address constructed +by setting the payment key to the verification key from the native script +and keeping the staking key empty. ### Updating an oracle assignment -The metadata oracle assignment for a policy ID can be updated via a transaction signed by the oracle main address key. The transaction must only send ADA from the oracle main address to itself and must not mint any tokens, but it may contain any number of inputs and outputs. Otherwise, the transaction is ignored for the purposes of metadata oracle assignment. +The metadata oracle assignment for a policy ID can be updated +via a transaction signed by the oracle main address key. +The transaction must only send ADA from the oracle main address to itself +and must not mint any tokens, +but it may contain any number of inputs and outputs. +Otherwise, the transaction is ignored +for the purposes of metadata oracle assignment. -The schema for updating an oracle assignment is the same as for the initial assignment in the minting transaction: +The schema for updating an oracle assignment is the same as +for the initial assignment in the minting transaction: ```json { @@ -102,15 +206,27 @@ The schema for updating an oracle assignment is the same as for the initial assi } ``` -The `main_address` or the `update_address` fields for a `` can be omitted, in which case the addresses for the omitted fields remain the same for that policy ID. +The `main_address` or the `update_address` fields for a `` +can be omitted, in which case the addresses for the omitted fields +remain the same for that policy ID. -If a metadata oracle was implicitly assigned to a policy ID before the assignment update, then the implicit assignment is replaced by the new explicit assignment. +If a metadata oracle was implicitly assigned to a policy ID +before the assignment update, +then the implicit assignment is replaced +by the new explicit assignment. ### Simple metadata updates -A simple metadata update transaction must only send ADA from the oracle update address to itself and must not mint any tokens, but it may contain any number of inputs and outputs. Otherwise, the transaction is ignored for the purposes of token metadata. +A simple metadata update transaction must only send ADA +from the oracle update address to itself +and must not mint any tokens, +but it may contain any number of inputs and outputs. +Otherwise, the transaction is ignored for the purposes of token metadata. -The schema for simple metadata updates in CIP-???? is similar to the CIP-25 schema, but it is nested under `"????".simple_metadata_update` in the transaction metadata object. +The schema for simple metadata updates in CIP-???? +is similar to the CIP-25 schema, +but it is nested under `"????".simple_metadata_update` +in the transaction metadata object. ```json { @@ -126,7 +242,8 @@ The schema for simple metadata updates in CIP-???? is similar to the CIP-25 sche } ``` -To remove a metadata field, set its value explicitly to `null` in the metadata update. +To remove a metadata field, +set its value explicitly to `null` in the metadata update. ### Regex metadata updates @@ -146,11 +263,20 @@ The schema for regex metadata updates is as follows: } ``` -A regex metadata update transaction must only send ADA from the oracle update address to itself and must not mint any tokens, but it may contain any number of inputs and outputs. Otherwise, it is ignored for the purposes of token metadata. +A regex metadata update transaction must only send ADA +from the oracle update address to itself and must not mint any tokens, +but it may contain any number of inputs and outputs. +Otherwise, it is ignored for the purposes of token metadata. -The only difference from the simple metadata update is that here the token names are defined in terms of PCRE regular expressions (regex). The regex metadata update applies to any previously minted token whose policy ID matches `` and whose token name matches the `` regular expression. +The only difference from the simple metadata update is that +here the token names are defined in terms of PCRE regular expressions (regex). +The regex metadata update applies to any previously minted token +whose policy ID matches `` +and whose token name matches the `` regular expression. -For example, the following metadata update would apply to every Equine pioneer horse between `EquinePioneerHorse05000` and `EquinePioneerHorse05999`: +For example, the following metadata update would apply +to every Equine pioneer horse +between `EquinePioneerHorse05000` and `EquinePioneerHorse05999`: ```json { @@ -166,13 +292,19 @@ For example, the following metadata update would apply to every Equine pioneer h } ``` -The regular expression pattern in `` is defined according to the grammar in: +The regular expression pattern in `` +is defined according to the grammar in: -- European Computer Manufacturers Association, "ECMAScript Language Specification 5.1 Edition", ECMA Standard ECMA-262, June 2011. Section 15.10. [https://www.ecma-international.org/wp-content/uploads/ECMA-262_5.1_edition_june_2011.pdf](https://www.ecma-international.org/wp-content/uploads/ECMA-262_5.1_edition_june_2011.pdf) +- European Computer Manufacturers Association, +"ECMAScript Language Specification 5.1 Edition", +ECMA Standard ECMA-262, June 2011. Section 15.10. +[https://www.ecma-international.org/wp-content/uploads/ECMA-262_5.1_edition_june_2011.pdf](https://www.ecma-international.org/wp-content/uploads/ECMA-262_5.1_edition_june_2011.pdf) ### Tabular metadata updates -Tabular metadata updates use a condensed rectangular format to specify new values for a fixed set of fields for a large number of assets. Specifically, we use the comma-separated values (CSV) format: +Tabular metadata updates use a condensed rectangular format +to specify new values for a fixed set of fields for a large number of assets. +Specifically, we use the comma-separated values (CSV) format: ```json { @@ -184,17 +316,29 @@ Tabular metadata updates use a condensed rectangular format to specify new value } ``` -A tabular metadata update transaction must only send ADA from the oracle update address to itself and must not mint any tokens, but it may contain any number of inputs and outputs. Otherwise, it is ignored for the purposes of token metadata. +A tabular metadata update transaction must +only send ADA from the oracle update address to itself +and must not mint any tokens, +but it may contain any number of inputs and outputs. +Otherwise, it is ignored for the purposes of token metadata. -The `` must be encoded as a single bytestring containing a whole CSV table, which must follow the following rules: +The `` must be encoded as a single bytestring +containing a whole CSV table, which must follow the following rules: - The first row (i.e. the header row) contains column names. - The first column name is `tokenName`. -- Each of the other column names specifies a field name at the top level of the metadata of an asset class, or a path of dot-separated field names to reach a field nested more deeply within the metadata. +- Each of the other column names specifies a field name +at the top level of the metadata of an asset class, +or a path of dot-separated field names +to reach a field nested more deeply within the metadata. - The values in the first column are token names. -- Each row of the table specifies the values to be applied to the metadata fields corresponding to the column names for the token name specified in the first cell of that row. +- Each row of the table specifies the values to be applied +to the metadata fields corresponding to the column names +for the token name specified in the first cell of that row. -For example, in a given tabular metadata update, we could set the `` for a given policy ID to the bytestring representation of the following CSV table: +For example, in a given tabular metadata update, +we could set the `` for a given policy ID +to the bytestring representation of the following CSV table: ``` tokenName,age,stats.acceleration,stats.agility,stats.endurance,stats.speed,stats.stamina @@ -205,7 +349,9 @@ EquinePioneerHorse01040,4,19,22,21,21,50 EquinePioneerHorse09175,1,24,11,36,22,14 ``` -A tabular metadata update with the above `` applied to a given `` is equivalent to the following simple metadata update: +A tabular metadata update with the above `` +applied to a given `` +is equivalent to the following simple metadata update: ```json { @@ -270,105 +416,286 @@ A tabular metadata update with the above `` applied to a given ` Date: Sun, 26 Feb 2023 20:39:42 -0500 Subject: [PATCH 2/6] Apply CIP-86 number to all its documents Replacing all instances of "????" with "86". --- CIP-????/README.md | 116 +++++++++++++++++------------------ CIP-????/cddl/version_1.cddl | 7 +-- CIP-????/cddl/version_2.cddl | 7 +-- 3 files changed, 64 insertions(+), 66 deletions(-) diff --git a/CIP-????/README.md b/CIP-????/README.md index debfa60e7a..634e2e30d0 100644 --- a/CIP-????/README.md +++ b/CIP-????/README.md @@ -116,32 +116,32 @@ Our proposal extends CIP-25 with a new update mechanism: - A metadata oracle can be assigned for a given policy ID. - For a policy with a metadata oracle assigned, -the metadata oracle can post CIP-???? updates to add, remove, or modify +the metadata oracle can post CIP-86 updates to add, remove, or modify any fields of the token’s CIP-25 metadata, without needing to mint or burn any tokens in the metadata update transactions. - The current metadata for a token can be deterministically reconstructed by starting from the latest CIP-25 update to the token, -and applying the subsequent CIP-???? metadata updates +and applying the subsequent CIP-86 metadata updates in ascending blockchain transaction order. -Both CIP-25 and CIP-???? updates affect the token metadata: +Both CIP-25 and CIP-86 updates affect the token metadata: - A CIP-25 update removes all fields in the current metadata state and replaces them with a new complete definition of the metadata state. -- A CIP-???? update selectively adds, removes, or modifies fields +- A CIP-86 update selectively adds, removes, or modifies fields relative to the current metadata state. It does not apply to any asset class that has not had tokens previously minted or any asset class that has not had CIP-25 metadata previously defined, -before the CIP-???? update. +before the CIP-86 update. -However, we recommend that CIP-???? adopters use +However, we recommend that CIP-86 adopters use the new metadata update mechanism exclusively to manage all updates to their token metadata after the initial CIP-25 metadata is set. -Our proposal only affects the `????` top-level CBOR tag +Our proposal only affects the `86` top-level CBOR tag of the metadata field in Cardano transactions. -(Note: we’re using `????` as a stand-in for the CIP number +(Note: we’re using `86` as a stand-in for the CIP number that will eventually be assigned to this proposal.) ### Assigning a metadata oracle for a token policy ID @@ -161,7 +161,7 @@ that mints or burns tokens for that policy: ```json { - "????": { + "86": { "assign_metadata_oracle": { "": { "main_address": "", @@ -195,7 +195,7 @@ for the initial assignment in the minting transaction: ```json { - "????": { + "86": { "assign_metadata_oracle": { "": { "main_address": "", @@ -223,14 +223,14 @@ and must not mint any tokens, but it may contain any number of inputs and outputs. Otherwise, the transaction is ignored for the purposes of token metadata. -The schema for simple metadata updates in CIP-???? +The schema for simple metadata updates in CIP-86 is similar to the CIP-25 schema, -but it is nested under `"????".simple_metadata_update` +but it is nested under `86.simple_metadata_update` in the transaction metadata object. ```json { - "????": { + "86": { "simple_metadata_update": { "": { "": { @@ -251,7 +251,7 @@ The schema for regex metadata updates is as follows: ```json { - "????": { + "86": { "regex_metadata_update": { "": { "": { @@ -280,7 +280,7 @@ between `EquinePioneerHorse05000` and `EquinePioneerHorse05999`: ```json { - "????": { + "86": { "regex_metadata_update": { "30ed3d95db1d6bb2c12fc5228a2986eab4553f192a12a4607780e15b": { "^EquinePioneerHorse05\\d{3}$": { @@ -308,7 +308,7 @@ Specifically, we use the comma-separated values (CSV) format: ```json { - "????": { + "86": { "tabular_metadata_update": { "": "" } @@ -355,7 +355,7 @@ is equivalent to the following simple metadata update: ```json { - "????": { + "86": { "simple_metadata_update": { "": { "EquinePioneerHorse00000": { @@ -436,7 +436,7 @@ decreases exponentially as more and more blocks are added to the chain. To reconstruct the metadata state for a given asset class, scan through the sequence of transactions in a Cardano node’s blockchain, -applying the CIP-25 and CIP-???? updates +applying the CIP-25 and CIP-86 updates in the order that they are encountered in this sequence. If the Cardano node rolls back some blocks from the chain tip, then roll back the updates from those blocks as well. @@ -452,19 +452,19 @@ A transaction that contains a CIP-25 update may also contain an explicit oracle assignment, but these can be applied in parallel because they do not clash with each other. On the other hand, a CIP-25 update transaction -cannot contain CIP-???? token metadata updates, +cannot contain CIP-86 token metadata updates, because CIP-25 updates can only occur in minting transactions -while CIP-???? token metadata transactions can +while CIP-86 token metadata transactions can only occur in non-minting transactions. -A transaction can contain CIP-???? token metadata updates of different types, +A transaction can contain CIP-86 token metadata updates of different types, plus oracle assignment updates. In this case, apply the updates in the following sequence: -1. Apply the CIP-???? regex update. -2. Apply the CIP-???? tabular update. -3. Apply the CIP-???? simple update. -4. Apply the CIP-???? oracle assignment update. +1. Apply the CIP-86 regex update. +2. Apply the CIP-86 tabular update. +3. Apply the CIP-86 simple update. +4. Apply the CIP-86 oracle assignment update. We recommend that token metadata oracle operators not mix multiple update types in the same transaction, unless they have a clear understanding of @@ -472,7 +472,7 @@ the outcome of applying the updates in the above sequence. ### Token metadata indexer -The CIP-???? token metadata indexer begins with +The CIP-86 token metadata indexer begins with a configuration of the policy IDs for which it will be tracking metadata. Optionally, it can be configured to track metadata for all tokens on Cardano. @@ -502,10 +502,10 @@ and removes any implicit assignments that were replaced by explicit assignments. For each oracle update address currently assigned to a policy ID, the indexer monitors the blockchain for non-minting transactions that only send ADA from the oracle update address to itself -and contain CIP-???? token metadata updates. +and contain CIP-86 token metadata updates. The indexer applies these metadata updates in the order defined in [Order of application for updates](#order-of-application-for-updates). -CIP-???? metadata updates are applied to the asset classes and metadata fields +CIP-86 metadata updates are applied to the asset classes and metadata fields that they target, while keeping all other fields the same. For tabular metadata updates, the bytestring CSV value may get broken up @@ -524,11 +524,11 @@ all the metadata updates in the rolled-back blocks were applied. For compatibility with existing applications that are already relying on CIP-25 metadata indexers, -the CIP-???? indexer provides a similar API +the CIP-86 indexer provides a similar API so that those applications can get and display -the current CIP-???? token metadata +the current CIP-86 token metadata in the same way that they have been for CIP-25 metadata. -The indexer indicates that it is following the CIP-???? standard. +The indexer indicates that it is following the CIP-86 standard. ## Rationale: how does this CIP achieve its goals? @@ -536,13 +536,13 @@ We pursued the following design goals in our solution: 1. Maintain backward compatibility with CIP-25 — tokens that only use CIP-25 updates should have token metadata displayed -identically by CIP-25 indexers and CIP-???? indexers. +identically by CIP-25 indexers and CIP-86 indexers. 2. Ensure that the current token metadata can be reconstructed by only looking at the blockchain, without accessing any external resources. 3. Allow token metadata to be updated by designated authorities without minting or burning tokens. 4. Allow existing token issuers to opt into and out of -the CIP-???? update mechanism +the CIP-86 update mechanism without having to remint all of their existing tokens. 5. Allow designated authorities to securely rotate any keys that they use in their automated and networked processes @@ -550,33 +550,33 @@ in their automated and networked processes 6. Allow token metadata to be updated more efficiently than by a wholesale replacement of the entire metadata object for an asset class. 7. Minimize the time and resource usage required -for an indexer to apply CIP-25 and CIP-???? updates +for an indexer to apply CIP-25 and CIP-86 updates for an asset class and then serve the resulting token metadata to applications. 8. Gracefully handle blockchain rollbacks that modify -the sequence of CIP-25 and CIP-???? metadata updates for an asset class. +the sequence of CIP-25 and CIP-86 metadata updates for an asset class. ### Backward compatibility We maintain full backward compatibility with CIP-25: - CIP-25 updates are respected and applied in the same way -as before by the CIP-???? indexer. -- CIP-???? updates are namespaced under a different top-level CBOR tag +as before by the CIP-86 indexer. +- CIP-86 updates are namespaced under a different top-level CBOR tag than CIP-25, in order to prevent any clashes between field names, policy IDs, and token names. -- The CIP-???? indexer provides the accumulated CIP-25 -and CIP-???? metadata via the same API as the CIP-25 indexer. +- The CIP-86 indexer provides the accumulated CIP-25 +and CIP-86 metadata via the same API as the CIP-25 indexer. ### Using an assigned oracle for token metadata updates -We recognize CIP-???? updates only if they are issued +We recognize CIP-86 updates only if they are issued by an oracle currently assigned for the corresponding policy IDs. This assignment is either directly authorized by the token issuer (explicitly or implicitly) or else indirectly authorized by the token issuer via the delegated authority that the token issuer placed in the originally assigned oracle to transfer the assignment to other oracles. -Therefore, all authority to post valid CIP-???? updates about a token +Therefore, all authority to post valid CIP-86 updates about a token originates from the token issuer. An alternative design could have oracles that declare themselves @@ -590,7 +590,7 @@ based on his oracles subscriptions. This alternative approach could be interesting to allow secondary/auxiliary metadata to be defined for tokens, but it is unsuitable for the primary metadata -that CIP-25 and CIP-???? seek to manage. +that CIP-25 and CIP-86 seek to manage. ### Identifying oracles by addresses @@ -609,7 +609,7 @@ we could have identified oracles by minting policies (not the minting policies to which oracles are assigned). In this alternative design, a minting policy `X` could have an assigned oracle identified by minting policy `A`. -Under such an assignment, a CIP-???? update for a token under `X` +Under such an assignment, a CIP-86 update for a token under `X` would be valid if the update transaction consumed a utxo that contained a token of minting policy `A`. In other words, the holder of an `A` token would be allowed @@ -629,33 +629,33 @@ from a given address in an indexer than to keep track of all the people who control various authorization tokens, at a given time. -### Restricting CIP-???? update transactions +### Restricting CIP-86 update transactions -We prohibit CIP-???? oracle assignment updates and metadata updates +We prohibit CIP-86 oracle assignment updates and metadata updates from occurring in transactions that mint tokens, in order to avoid awkward clashes with CIP-25 metadata transactions. Also, it does not make sense conceptually -for CIP-25 metadata transactions to coincide with CIP-???? updates. +for CIP-25 metadata transactions to coincide with CIP-86 updates. CIP-25 should be used to define the initial metadata for an asset class, -and then CIP-???? updates should be used +and then CIP-86 updates should be used for any subsequent changes to that metadata. -We require CIP-???? updates to occur in transactions +We require CIP-86 updates to occur in transactions that only send ADA from an oracle address (main or update, as appropriate), to prevent unforeseen interactions with other mechanisms that may have negative consequences. -CIP-???? update transactions should have the singular purpose of -interacting with the CIP-???? update mechanism. +CIP-86 update transactions should have the singular purpose of +interacting with the CIP-86 update mechanism. ### Implicit oracle assignment The implicit method of assigning a metadata oracle is needed -to allow existing token issuers to opt into the CIP-???? update mechanism. +to allow existing token issuers to opt into the CIP-86 update mechanism. Their minting policies may no longer allow any more token minting or burning, which would prevent the token issuers from being able to explicitly assign an oracle via a CIP-25 update for those policies. The implicit assignment method bootstraps -the CIP-???? update mechanism for these policies. +the CIP-86 update mechanism for these policies. The implicit address is of the Enterprise address type, to avoid having to deal with staking keys. @@ -663,9 +663,9 @@ If needed, the metadata oracle operator can send ADA to the Enterprise address, and then spend it if the operator still controls the payment key of that Enterprise address. -### Opting out of CIP-???? +### Opting out of CIP-86 -Opting out of the CIP-???? update mechanism can be done +Opting out of the CIP-86 update mechanism can be done by explicitly assigning an oracle with addresses from which ADA cannot be spent (e.g. Plutus AlwaysFails). If the minting policy does not allow any more minting or burning, @@ -676,7 +676,7 @@ then this is an irreversible opt-out. When managing large collections of thousands of NFTs, one often needs to set a given field to the same value for many NFTs. Doing this individually for each NFT -via CIP-25 updates or CIP-???? simple updates is inefficient, +via CIP-25 updates or CIP-86 simple updates is inefficient, so we have proposed the regex metadata update as a succinct way to specify a mapping from multiple token names to a single metadata update. @@ -705,11 +705,11 @@ This proposal may be considered active if: 1. The solution meets the design goals listed in the [Rationale](#rationale) section to a satisfactory degree. -2. The indexer and simple tools to construct CIP-???? update transactions +2. The indexer and simple tools to construct CIP-86 update transactions (as described in the [Specification](#specification) section) are fully implemented and provided in an open-source (Apache 2.0 licensed) repository with sufficient documentation. -3. The CIP-???? metadata format, indexer, and/or indexer API +3. The CIP-86 metadata format, indexer, and/or indexer API are used by several stakeholders in the Cardano ecosystem, including dApps, blockchain explorers, analytics platforms, etc. @@ -718,7 +718,7 @@ including dApps, blockchain explorers, analytics platforms, etc. Equine and MLabs are collaborating on developing the indexer described in this CIP and the Equine NFT gaming application will be -using CIP-???? updates to manage metadata updates +using CIP-86 updates to manage metadata updates for its large collection of thousands of NFTs under multiple minting policies. We will include detailed documentation, example configurations, diff --git a/CIP-????/cddl/version_1.cddl b/CIP-????/cddl/version_1.cddl index 8126221444..6430350a25 100644 --- a/CIP-????/cddl/version_1.cddl +++ b/CIP-????/cddl/version_1.cddl @@ -1,8 +1,7 @@ -; "????" to be replaced by the CIP number, as a `uint`. -metadata = { ???? => action } +metadata = { "86" => action } ; ========================================================================= -; CIP-???? actions +; CIP-86 actions action = { @@ -108,7 +107,7 @@ record_field = text ; csv_update_simple = Union([csv_policy_object]) ; ; The effect of a `csv_update_opaque` on the token metadata state in the -; CIP-???? indexer must be the same as the effect of the corresponding +; CIP-86 indexer must be the same as the effect of the corresponding ; `csv_update_simple` (applied as a simple update action). ; ========================================================================= diff --git a/CIP-????/cddl/version_2.cddl b/CIP-????/cddl/version_2.cddl index 99e7eb15a1..d824a4ed2f 100644 --- a/CIP-????/cddl/version_2.cddl +++ b/CIP-????/cddl/version_2.cddl @@ -1,10 +1,9 @@ -; "????" to be replaced by the CIP number, as a `uint`. -metadata = { ???? => action } +metadata = { "86" => action } ; ========================================================================= -; CIP-???? actions +; CIP-86 actions -; When version 2 mode is enabled for a CIP-???? action, all `policy_id` and +; When version 2 mode is enabled for a CIP-86 action, all `policy_id` and ; `asset_name` values are interpreted as `bytes_64`, similar to CIP-25v2. action = { From afb0b38b321de33b1013c9c9e9314899cd665865 Mon Sep 17 00:00:00 2001 From: George Flerovsky Date: Sun, 26 Feb 2023 22:09:39 -0500 Subject: [PATCH 3/6] Replace CSV with JSON in tabular updates --- CIP-????/README.md | 106 +++++++++++++++++++++-------------- CIP-????/cddl/version_1.cddl | 71 ++++++----------------- 2 files changed, 80 insertions(+), 97 deletions(-) diff --git a/CIP-????/README.md b/CIP-????/README.md index 634e2e30d0..6e899275ef 100644 --- a/CIP-????/README.md +++ b/CIP-????/README.md @@ -304,13 +304,39 @@ ECMA Standard ECMA-262, June 2011. Section 15.10. Tabular metadata updates use a condensed rectangular format to specify new values for a fixed set of fields for a large number of assets. -Specifically, we use the comma-separated values (CSV) format: +Specifically, for each policy ID we provide an object +with the following three fields: + +- `field_paths` contains an array of paths pointing +to possibly-nested fields within a token metadata object. +Each of these field paths is a dot-separated list of field names +(e.g. `"images.background.sunset.url"`) +that lead from the top of the metadata object (for asset classes of the policy) +into a targeted field within that object. +- `token_names` contains an array of token names. +- `values` contains a table of values, represented by an array of arrays. +For each token name in `token_names`, +the outer array in `values` contains one element (an inner array) +of metadata values to which the fields targeted by `field_paths` +should be updated for that token name under the policy ID. +The outer array of `values` must be equal in length to `token_names` +and each inner array of `values` must be equal in length to `field_paths`. ```json { "86": { "tabular_metadata_update": { - "": "" + "": { + "field_paths": [ + "" + ], + "token_names": [ + "" + ], + "values": [ + [""] + ] + } } } } @@ -322,36 +348,43 @@ and must not mint any tokens, but it may contain any number of inputs and outputs. Otherwise, it is ignored for the purposes of token metadata. -The `` must be encoded as a single bytestring -containing a whole CSV table, which must follow the following rules: +For example, the following update would apply updates to six metadata fields +of five Equine horse NFTs: -- The first row (i.e. the header row) contains column names. -- The first column name is `tokenName`. -- Each of the other column names specifies a field name -at the top level of the metadata of an asset class, -or a path of dot-separated field names -to reach a field nested more deeply within the metadata. -- The values in the first column are token names. -- Each row of the table specifies the values to be applied -to the metadata fields corresponding to the column names -for the token name specified in the first cell of that row. - -For example, in a given tabular metadata update, -we could set the `` for a given policy ID -to the bytestring representation of the following CSV table: - -``` -tokenName,age,stats.acceleration,stats.agility,stats.endurance,stats.speed,stats.stamina -EquinePioneerHorse00000,3,34,16,18,51,33 -EquinePioneerHorse00012,2,24,48,12,32,18 -EquinePioneerHorse00315,3,33,34,41,14,31 -EquinePioneerHorse01040,4,19,22,21,21,50 -EquinePioneerHorse09175,1,24,11,36,22,14 +```json +{ + "86": { + "tabular_metadata_update": { + "": { + "field_paths": [ + "age", + "stats.acceleration", + "stats.agility", + "stats.endurance", + "stats.speed", + "stats.stamina" + ], + "token_names": [ + "EquinePioneerHorse00000", + "EquinePioneerHorse00012", + "EquinePioneerHorse00315", + "EquinePioneerHorse01040", + "EquinePioneerHorse09175" + ], + "values": [ + [3,34,16,18,51,33], + [2,24,48,12,32,18], + [3,33,34,41,14,31], + [4,19,22,21,21,50], + [1,24,11,36,22,14] + ] + } + } + } +} ``` -A tabular metadata update with the above `` -applied to a given `` -is equivalent to the following simple metadata update: +It is equivalent to the following simple metadata update: ```json { @@ -414,13 +447,6 @@ is equivalent to the following simple metadata update: } ``` -The CSV format is defined by the following standard: - -- Internet Engineering Task Force, -“Common Format and MIME Type for Comma-Separated Values (CSV) Files", -Request for Comments 4180, October 2005. -[https://www.ietf.org/rfc/rfc4180.txt](https://www.ietf.org/rfc/rfc4180.txt) - ### Order of application for updates Up to network consensus, the Cardano blockchain imposes @@ -508,11 +534,6 @@ The indexer applies these metadata updates in the order defined in CIP-86 metadata updates are applied to the asset classes and metadata fields that they target, while keeping all other fields the same. -For tabular metadata updates, the bytestring CSV value may get broken up -into an array of bytestring chunks in the CBOR representation. -When this happens, the indexer recombines the chunks into the whole CSV table -before applying the tabular metadata update. - To be able to handle blockchain rollbacks, the indexer keeps track of past metadata states for its policy IDs, going back 2160 blocks (~12 hours) from the current blockchain tip. @@ -694,8 +715,7 @@ field names/paths are defined once in the column names of a rectangular table and applied consistently for each row of updated metadata field values. Rectangular tables are a standard format used in the data analytics field -for these situations, and the CSV format is the most widely used -and interoperable rectangular data format. +for these situations. ## Path to Active diff --git a/CIP-????/cddl/version_1.cddl b/CIP-????/cddl/version_1.cddl index 6430350a25..377569a92d 100644 --- a/CIP-????/cddl/version_1.cddl +++ b/CIP-????/cddl/version_1.cddl @@ -23,7 +23,7 @@ action_simple = { * policy_id => { * asset_name => metadata_details } } action_regex = { * policy_id => { * asset_regex => metadata_details } } -action_tabular = { * policy_id => csv_update_opaque } +action_tabular = { * policy_id => tabular_metadata } ; Technically, both policy IDs and asset names should be 32 bytes long. ; However, this isn't enforced for asset names, and CIP-25v1 already treats @@ -45,7 +45,7 @@ metadata_details = ? image : text_extendable, ? mediaType : text_64, ? description : text_extendable, - ? files : [* files_details], + ? files : [ * files_details], * ( label_64 => transaction_metadatum ) } @@ -57,58 +57,21 @@ files_details = } ; ========================================================================= -; Tabular updates' CSV schema - -; Cardano ledger imposes a fixed size of 64 on text and bytestrings, -; which prevents us from directly encoding a CSV table as a single long -; text. This means that, formally, the CSV in a tabular update -; is just an opaque extendable text (i.e. a fixed text or an array of fixed -; texts). -csv_update_opaque = text_extendable - -; Informally, if we concatenate the `csv_update_opaque` value to a single long -; text, and then split it on unescaped newline characters, then it should comply -; with the following cddl schema. -; (Newline characters inside of a double-quoted field value are considered -; escaped, which means that they do no indicate the start of a new record.) -csv_update = [ header, * record ] -header = [ "tokenName", * header_field ] -record = [ text_64, * record_field ] -header_field = text -record_field = text - -; For a given `policy_id`, a `csv_update` value can be transformed into a -; `metadata_details` value as follows: -; 1. Pair each `record_field` with its corresponding `header_field` -; (i.e. like zipping two lists). -; 2. Transform each `header_field` into a `[header_field_part]` array, by -; splitting it on the '.' character. -; 3. For each pair of `record_field` and `[header_field_part]` values, construct -; the following nested object: -; csv_nested_object = -; { header_field_part[0] => -; { header_field_part[1] => -; { header_field_part[2] => -; ... -; { header_field_part[N] => `record_field` } -; ... -; } -; } -; } -; 4. For each record, combine and normalize its record fields' constructed -; nested objects into a single unified object for that record. Nest the -; unified object under the record's "tokenName" value: -; csv_record_object = { text_64 => Union([csv_nested_object]) } -; 5. Unify the `record_object`s into a single object, and then nest it under -; the `policy_id`: -; csv_policy_object = { policy_id => Union([csv_record_object]) } -; 6. Combine the `action_simple_single` values into a single `action_simple` -; value: -; csv_update_simple = Union([csv_policy_object]) -; -; The effect of a `csv_update_opaque` on the token metadata state in the -; CIP-86 indexer must be the same as the effect of the corresponding -; `csv_update_simple` (applied as a simple update action). +; Tabular format + +; The length of `token_names` must be equal to the length of the outer +; array of `values`, and the length of `field_paths` must be equal to +; the length of each inner array of `values`. +tabular_metadata = + { + field_paths: [ * field_path], + token_names: [ * asset_name], + values: [ * [ * transaction_metadatum ] ] + } + +; Each field path is a dot-separated list of field names (text_64 values). +; E.g. "images.background.sunset.url" +field_path = text_64 ; ========================================================================= ; Imported from cardano-ledger/.../babbage.cddl From 6a8da4fb7c891006858599d0b3fe188d41ca5aa1 Mon Sep 17 00:00:00 2001 From: George Flerovsky Date: Sun, 26 Feb 2023 22:10:21 -0500 Subject: [PATCH 4/6] Add tabular updates to version_2 --- CIP-????/cddl/version_2.cddl | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/CIP-????/cddl/version_2.cddl b/CIP-????/cddl/version_2.cddl index d824a4ed2f..ef1dd8f158 100644 --- a/CIP-????/cddl/version_2.cddl +++ b/CIP-????/cddl/version_2.cddl @@ -10,12 +10,12 @@ action = version = 2, ? assign_metadata_oracle : action_oracle, ? simple_metadata_update : action_simple, + ? tabular_metadata_update : action_tabular } -; Regex and tabular updates are disallowed in version 2, because it is unclear +; Regex updates are disallowed in version 2, because it is unclear ; how to apply regular expressions to non-UTF-8 bytestrings (or their -; corresponding hex encodings) and it is unclear how to embed raw bytestrings -; in CSV. +; corresponding hex encodings). action_oracle = { * policy_id => oracle_assignment } @@ -27,6 +27,8 @@ oracle_assignment = action_simple = { * policy_id => { * asset_name => metadata_details } } +action_tabular = { * policy_id => tabular_metadata } + ; Technically, both policy IDs and asset names should be 32 bytes long. ; However, this isn't enforced for asset names, and CIP-25v2 already treats ; these as `bytes_64`, so we don't enforce the more narrow `bytes_32` type @@ -56,6 +58,23 @@ files_details = src : text_extendable } +; ========================================================================= +; Tabular format + +; The length of `token_names` must be equal to the length of the outer +; array of `values`, and the length of `field_paths` must be equal to +; the length of each inner array of `values`. +tabular_metadata = + { + field_paths: [ * field_path], + token_names: [ * asset_name], + values: [ * [ * transaction_metadatum ] ] + } + +; Each field path is a dot-separated list of field names (text_64 values). +; E.g. "images.background.sunset.url" +field_path = text_64 + ; ========================================================================= ; Imported from cardano-ledger/.../babbage.cddl transaction_metadatum = From 4797911f2585ad7c6841738858b2e03854444708 Mon Sep 17 00:00:00 2001 From: George Flerovsky Date: Sun, 26 Feb 2023 22:26:01 -0500 Subject: [PATCH 5/6] Add "Versions 1 and 2" section to README --- CIP-????/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/CIP-????/README.md b/CIP-????/README.md index 6e899275ef..f009abc715 100644 --- a/CIP-????/README.md +++ b/CIP-????/README.md @@ -447,6 +447,42 @@ It is equivalent to the following simple metadata update: } ``` +### Versions 1 and 2 +Like CIP-25, CIP-86 supports two different methods of +representing policy IDs and token name: + +- In version 1, +policy IDs and token names must be expressed as text +(see [cddl/version_1.cddl](cddl/version_1.cddl)). +- In version 2, +policy IDs and token names must be expressed as raw bytes +(see [cddl/version_2.cddl](cddl/version_2.cddl)). + +By default, all CIP-86 metadata updates use version 1. +However, version 2 can be used if the `version` field of the object +under the top-level `"86"` CBOR tag is set to `2`. + +For example: + +```json +{ + "86": { + "version": 2, + "simple_metadata_update": { + "": { + "": { + "": "" + } + } + } + } +} +``` + +Regex updates are disallowed in version 2, because it is unclear +how to apply regular expressions to non-UTF-8 bytestrings (or their +corresponding hex encodings). + ### Order of application for updates Up to network consensus, the Cardano blockchain imposes From c00251280e6761b9d1f860691b6e181f9756b33c Mon Sep 17 00:00:00 2001 From: George Flerovsky Date: Sun, 26 Feb 2023 22:40:05 -0500 Subject: [PATCH 6/6] Fix indent consistency --- CIP-????/README.md | 210 ++++++++++++++++++++++----------------------- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/CIP-????/README.md b/CIP-????/README.md index f009abc715..f59cf6da1d 100644 --- a/CIP-????/README.md +++ b/CIP-????/README.md @@ -161,14 +161,14 @@ that mints or burns tokens for that policy: ```json { - "86": { - "assign_metadata_oracle": { - "": { - "main_address": "", - "update_address": "" - } - } + "86": { + "assign_metadata_oracle": { + "": { + "main_address": "", + "update_address": "" + } } + } } ``` @@ -195,14 +195,14 @@ for the initial assignment in the minting transaction: ```json { - "86": { - "assign_metadata_oracle": { - "": { - "main_address": "", - "update_address": "" - } - } + "86": { + "assign_metadata_oracle": { + "": { + "main_address": "", + "update_address": "" + } } + } } ``` @@ -230,15 +230,15 @@ in the transaction metadata object. ```json { - "86": { - "simple_metadata_update": { - "": { - "": { - "": "" - } - } + "86": { + "simple_metadata_update": { + "": { + "": { + "": "" } + } } + } } ``` @@ -251,15 +251,15 @@ The schema for regex metadata updates is as follows: ```json { - "86": { - "regex_metadata_update": { - "": { - "": { - "": "" - } - } + "86": { + "regex_metadata_update": { + "": { + "": { + "": "" } + } } + } } ``` @@ -280,15 +280,15 @@ between `EquinePioneerHorse05000` and `EquinePioneerHorse05999`: ```json { - "86": { - "regex_metadata_update": { - "30ed3d95db1d6bb2c12fc5228a2986eab4553f192a12a4607780e15b": { - "^EquinePioneerHorse05\\d{3}$": { - "age": 2 - } - } + "86": { + "regex_metadata_update": { + "30ed3d95db1d6bb2c12fc5228a2986eab4553f192a12a4607780e15b": { + "^EquinePioneerHorse05\\d{3}$": { + "age": 2 } + } } + } } ``` @@ -307,14 +307,14 @@ to specify new values for a fixed set of fields for a large number of assets. Specifically, for each policy ID we provide an object with the following three fields: -- `field_paths` contains an array of paths pointing +- `field_paths` contains an array of paths pointing to possibly-nested fields within a token metadata object. Each of these field paths is a dot-separated list of field names (e.g. `"images.background.sunset.url"`) that lead from the top of the metadata object (for asset classes of the policy) into a targeted field within that object. -- `token_names` contains an array of token names. -- `values` contains a table of values, represented by an array of arrays. +- `token_names` contains an array of token names. +- `values` contains a table of values, represented by an array of arrays. For each token name in `token_names`, the outer array in `values` contains one element (an inner array) of metadata values to which the fields targeted by `field_paths` @@ -325,8 +325,8 @@ and each inner array of `values` must be equal in length to `field_paths`. ```json { "86": { - "tabular_metadata_update": { - "": { + "tabular_metadata_update": { + "": { "field_paths": [ "" ], @@ -354,8 +354,8 @@ of five Equine horse NFTs: ```json { "86": { - "tabular_metadata_update": { - "": { + "tabular_metadata_update": { + "": { "field_paths": [ "age", "stats.acceleration", @@ -388,62 +388,62 @@ It is equivalent to the following simple metadata update: ```json { - "86": { - "simple_metadata_update": { - "": { - "EquinePioneerHorse00000": { - "age": 3, - "stats": { - "acceleration": 34, - "agility": 16, - "endurance": 18, - "speed": 51, - "stamina": 33 - } - }, - "EquinePioneerHorse00012": { - "age": 2, - "stats": { - "acceleration": 24, - "agility": 48, - "endurance": 12, - "speed": 32, - "stamina": 18 - } - }, - "EquinePioneerHorse00315": { - "age": 3, - "stats": { - "acceleration": 33, - "agility": 34, - "endurance": 41, - "speed": 14, - "stamina": 31 - } - }, - "EquinePioneerHorse01040": { - "age": 4, - "stats": { - "acceleration": 19, - "agility": 22, - "endurance": 21, - "speed": 21, - "stamina": 50 - } - }, - "EquinePioneerHorse09175": { - "age": 1, - "stats": { - "acceleration": 24, - "agility": 11, - "endurance": 36, - "speed": 22, - "stamina": 14 - } - } - } + "86": { + "simple_metadata_update": { + "": { + "EquinePioneerHorse00000": { + "age": 3, + "stats": { + "acceleration": 34, + "agility": 16, + "endurance": 18, + "speed": 51, + "stamina": 33 + } + }, + "EquinePioneerHorse00012": { + "age": 2, + "stats": { + "acceleration": 24, + "agility": 48, + "endurance": 12, + "speed": 32, + "stamina": 18 + } + }, + "EquinePioneerHorse00315": { + "age": 3, + "stats": { + "acceleration": 33, + "agility": 34, + "endurance": 41, + "speed": 14, + "stamina": 31 + } + }, + "EquinePioneerHorse01040": { + "age": 4, + "stats": { + "acceleration": 19, + "agility": 22, + "endurance": 21, + "speed": 21, + "stamina": 50 + } + }, + "EquinePioneerHorse09175": { + "age": 1, + "stats": { + "acceleration": 24, + "agility": 11, + "endurance": 36, + "speed": 22, + "stamina": 14 + } } + } } + } } ``` @@ -451,10 +451,10 @@ It is equivalent to the following simple metadata update: Like CIP-25, CIP-86 supports two different methods of representing policy IDs and token name: -- In version 1, +- In version 1, policy IDs and token names must be expressed as text (see [cddl/version_1.cddl](cddl/version_1.cddl)). -- In version 2, +- In version 2, policy IDs and token names must be expressed as raw bytes (see [cddl/version_2.cddl](cddl/version_2.cddl)). @@ -467,14 +467,14 @@ For example: ```json { "86": { - "version": 2, - "simple_metadata_update": { - "": { - "": { - "": "" - } - } + "version": 2, + "simple_metadata_update": { + "": { + "": { + "": "" + } } + } } } ```