From 8a32f2cca71afbd6ddc7e1a5d7c7d1393a9f7087 Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Sun, 24 Aug 2025 21:35:48 +0200 Subject: [PATCH 1/9] add CashTokens guide --- website/docs/guides/cashtokens.md | 94 +++++++++++++++++++ website/docs/language/globals.md | 8 +- .../syntax-highlighting.md | 0 website/docs/sdk/electrum-network-provider.md | 9 ++ website/docs/sdk/network-provider.md | 9 ++ website/docusaurus.config.js | 1 + website/sidebars.js | 3 +- 7 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 website/docs/guides/cashtokens.md rename website/docs/{guides => language}/syntax-highlighting.md (100%) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md new file mode 100644 index 00000000..f421ea26 --- /dev/null +++ b/website/docs/guides/cashtokens.md @@ -0,0 +1,94 @@ +--- +title: CashTokens +sidebar_label: CashTokens +--- + +CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by each full node on the network and their transaction rules checked by each miner when constructing new blocks. CashTokens added fungible and non-fungible token primitives. +CashTokens was first proposed in February of 2022 and actived on Bitcoin Cash mainchain in May of 2023. + +:::tip +The specification for CashTokens is the ['CHIP-2022-02-CashTokens: Token Primitives for Bitcoin '](https://github.com/cashtokens/cashtokens) document. +::: + +## CashTokens Utxo data + +To understand CashTokens it is helpful to start with the layout of the UTXO data. In the `networkProvider` data from the SDK, the `token` property contains the new CashTokens fields: + +```ts +interface Utxo { + txid: string; + vout: number; + satoshis: bigint; + token?: TokenDetails; +} + +interface TokenDetails { + amount: bigint; + category: string; + nft?: { + capability: 'none' | 'mutable' | 'minting'; + commitment: string; + }; +} +``` + +Note that a UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both have the same `category` (also reffered to as "tokenId"). + +## CashTokens introspection data +While CashTokens might seem overwhelming at first, realize that in contracts you will only use it through the following introspection details + +- **`bytes tx.inputs[i].tokenCategory`** - `tokenCategory` + `tokenCapability` of a specific input. +- **`bytes tx.inputs[i].nftCommitment`** - NFT commitment data of a specific input. +- **`int tx.inputs[i].tokenAmount`** - Amount of fungible tokens of a specific input. + +and their equivalent for outputs: + +- **`bytes tx.outputs[i].tokenCategory`** - `tokenCategory` + `tokenCapability` of a specific output. +- **`bytes tx.outputs[i].nftCommitment`** - NFT commitment data of a specific output +- **`int tx.outputs[i].tokenAmount`** - Amount of fungible tokens of a specific output. + +## CashTokens Gotchas +There are two important "gotchas" to be aware of when developing with CashTokens in smart contracts for the first time + +#### 1) tokenCategory contains the nft-capability +```solidity +bytes tx.inputs[i].tokenCategory +``` + +When accessing the `tokenCategory` through introspection the result returns `0x` when that specific item does not contain tokens. If the item does have tokens it returns the `bytes32 tokenCategory`. When the item contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. + +#### 2) tokenCategory encoding + +The `tokenCategory` introspection variable returns the tokenCategory in the original unreversed order, this is unlike wallets and explorers which use the reversed byte-order. So be careful about the byte-order of `tokenCategory` when working with BCH smart contracts. + +```ts +// when using a standard encoded tokenId, reverse the hex before using it in your contract +const contract = new Contract(artifact, [reverseHex(tokenId)], { provider }) +``` + +generally not recommended to do the byte-reversal in script +```solidity + // NOT THIS + require(tx.inputs[0].tokenCategory == providedTokenId.reverse()); +``` + +#### 3) "invisibe" empty nfts +Because the nft-capability has no separate introspection item, and nothing is appended to the `tokenCategory` in case of capability `none`, empty nfts can be "invisibe" when combined with fungible tokens. + +```solidity + // Input 0 has an empty nft (commitment 0x) with providedTokenId + // If there was no empty nft the tokenCategory would return 0x + require(tx.inputs[0].nftCommitment == 0x); + require(tx.inputs[0].tokenAmount == 0); + require(tx.inputs[0].tokenCategory == providedTokenId); +``` + +contrast this with the following scenario where there is also fungible tokens of the same category: + +```solidity + // Input 0 might or might not have an empty nft (commitment 0x) with providedTokenId + // Either way, the tokenCategory would return providedTokenId + require(tx.inputs[0].nftCommitment == 0x); + require(tx.inputs[0].tokenAmount == 10); + require(tx.inputs[0].tokenCategory == providedTokenId); +``` diff --git a/website/docs/language/globals.md b/website/docs/language/globals.md index d979dbb0..a335cb5d 100644 --- a/website/docs/language/globals.md +++ b/website/docs/language/globals.md @@ -131,7 +131,7 @@ Represents the `nSequence` number of a specific input. This value of the input's bytes tx.inputs[i].tokenCategory ``` -Represents the `tokenCategory` of a specific input. Returns 0 when that specific input contains no tokens. When the input contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. +Represents the `tokenCategory` of a specific input. Returns `0x` when that specific input contains no tokens. When the input contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. :::caution The `tokenCategory` introspection variable returns the tokenCategory in the original unreversed order, this is unlike wallets and explorers which use the reversed byte-order. So be careful about the byte-order of `tokenCategory` when working with BCH smart contracts. @@ -149,7 +149,7 @@ Represents the NFT commitment data of a specific input. int tx.inputs[i].tokenAmount ``` -Represents the amount of fungible tokens of a specific input. +Represents the amount of fungible tokens of a specific input. Maximum size for a `tokenAmount` is a 64-bit integer. ### tx.outputs Represents the list of outputs of the evaluated transaction. This is an array, and cannot be used on itself. You need to access an output with a specific index and specify the properties you want to access. @@ -180,7 +180,7 @@ Represents the locking bytecode (`scriptPubKey`) of a specific output. bytes tx.output[i].tokenCategory ``` -Represents the `tokenCategory` of a specific output. Returns 0 when that specific output contains no tokens. When the output contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. +Represents the `tokenCategory` of a specific output. Returns `0x` when that specific output contains no tokens. When the output contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. :::caution The `tokenCategory` introspection variable returns the tokenCategory in the original unreversed order, this is unlike wallets and explorers which use the reversed byte-order. So be careful about the byte-order of `tokenCategory` when working with BCH smart contracts. @@ -198,7 +198,7 @@ Represents the NFT commitment data of a specific output. int tx.output[i].tokenAmount ``` -Represents the amount of fungible tokens of a specific output. +Represents the amount of fungible tokens of a specific output. Maximum size for a `tokenAmount` is a 64-bit integer. ## Constructing locking bytecode One of the main use cases of covenants is enforcing transaction outputs (where money is sent). To assist with enforcing these outputs, there is a number of `LockingBytecode` objects that can be instantiated. These locking bytecodes can then be compared to the locking bytecodes of transaction outputs. diff --git a/website/docs/guides/syntax-highlighting.md b/website/docs/language/syntax-highlighting.md similarity index 100% rename from website/docs/guides/syntax-highlighting.md rename to website/docs/language/syntax-highlighting.md diff --git a/website/docs/sdk/electrum-network-provider.md b/website/docs/sdk/electrum-network-provider.md index cbff6257..bf216424 100644 --- a/website/docs/sdk/electrum-network-provider.md +++ b/website/docs/sdk/electrum-network-provider.md @@ -57,6 +57,15 @@ interface Utxo { satoshis: bigint; token?: TokenDetails; } + +interface TokenDetails { + amount: bigint; + category: string; + nft?: { + capability: 'none' | 'mutable' | 'minting'; + commitment: string; + }; +} ``` #### Example diff --git a/website/docs/sdk/network-provider.md b/website/docs/sdk/network-provider.md index 505a81e6..72e32029 100644 --- a/website/docs/sdk/network-provider.md +++ b/website/docs/sdk/network-provider.md @@ -35,6 +35,15 @@ interface Utxo { satoshis: bigint; token?: TokenDetails; } + +interface TokenDetails { + amount: bigint; + category: string; + nft?: { + capability: 'none' | 'mutable' | 'minting'; + commitment: string; + }; +} ``` #### Example diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index e50eb37c..d98486ad 100755 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -121,6 +121,7 @@ module.exports = { { from: '/docs/sdk', to: '/docs/sdk/instantiation' }, { from: '/docs/sdk/transactions-advanced', to: '/docs/sdk/transaction-builder' }, { from: '/docs/guides', to: '/docs/guides/covenants' }, + { from: '/docs/guides/syntax-highlighting', to: '/docs/language/syntax-highlighting' }, { from: '/docs/getting-started', to: '/docs/basics/getting-started' }, { from: '/docs/examples', to: '/docs/language/examples' }, ], diff --git a/website/sidebars.js b/website/sidebars.js index 06449327..a0e85883 100755 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -18,6 +18,7 @@ module.exports = { 'language/functions', 'language/globals', 'language/examples', + 'language/syntax-highlighting' ], }, { @@ -54,8 +55,8 @@ module.exports = { type: 'category', label: 'Guides', items: [ - 'guides/syntax-highlighting', 'guides/covenants', + 'guides/cashtokens', 'guides/infrastructure', 'guides/walletconnect', 'guides/debugging', From af40acf4fddfec08b74490437327e1815e1ec2f4 Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Tue, 26 Aug 2025 09:52:06 +0200 Subject: [PATCH 2/9] add to cashtokens guide --- website/docs/guides/cashtokens.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index f421ea26..0eebd0fe 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -7,7 +7,7 @@ CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by CashTokens was first proposed in February of 2022 and actived on Bitcoin Cash mainchain in May of 2023. :::tip -The specification for CashTokens is the ['CHIP-2022-02-CashTokens: Token Primitives for Bitcoin '](https://github.com/cashtokens/cashtokens) document. +You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) or can refer to the original specification document: ['CHIP-2022-02-CashTokens: Token Primitives for Bitcoin '](https://github.com/cashtokens/cashtokens). ::: ## CashTokens Utxo data @@ -32,7 +32,15 @@ interface TokenDetails { } ``` -Note that a UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both have the same `category` (also reffered to as "tokenId"). +The `amount` field is the amount of fungible tokens on the UTXO, the `category` is the "tokenId" for the token on the UTXO. +Next we have the optional `nft` object, which will only be present if the UTXO contains an NFT. +The `nft` object has 2 properties: the `capability` and the `commitment`. The `commitment` is the data field for the NFT. +Capability `none` then refers to an immutible NFT where the commitment cannot be changes. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. + +:::note +A UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both tokens have the same `category`. +This is quite a common pattern for covenants which want to hold contract state and fungible tokens on the same UTXO. +::: ## CashTokens introspection data While CashTokens might seem overwhelming at first, realize that in contracts you will only use it through the following introspection details From c34203b069e3acccd38b4c2982d1c2865e95e66d Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Wed, 27 Aug 2025 21:21:07 +0200 Subject: [PATCH 3/9] add to CashTokens guide --- website/docs/guides/cashtokens.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 0eebd0fe..86883617 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -3,11 +3,11 @@ title: CashTokens sidebar_label: CashTokens --- -CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by each full node on the network and their transaction rules checked by each miner when constructing new blocks. CashTokens added fungible and non-fungible token primitives. +CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by all full nodes on the network and their transaction rules checked by each miner when constructing new blocks. CashTokens added fungible and non-fungible token primitives. CashTokens was first proposed in February of 2022 and actived on Bitcoin Cash mainchain in May of 2023. :::tip -You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) or can refer to the original specification document: ['CHIP-2022-02-CashTokens: Token Primitives for Bitcoin '](https://github.com/cashtokens/cashtokens). +You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) which has the full specification as well as a list of [Usage Examples](https://cashtokens.org/docs/spec/examples). ::: ## CashTokens Utxo data @@ -31,13 +31,18 @@ interface TokenDetails { }; } ``` +### Fungible Tokens The `amount` field is the amount of fungible tokens on the UTXO, the `category` is the "tokenId" for the token on the UTXO. -Next we have the optional `nft` object, which will only be present if the UTXO contains an NFT. -The `nft` object has 2 properties: the `capability` and the `commitment`. The `commitment` is the data field for the NFT. +The maximum size for a fungible token `amount` is the max signed 64-bit integer or `9223372036854775807`. + +### Non-Fungible Tokens + +The `nft` info on a UTXO will only be present if the UTXO contains an NFT. The `nft` object has 2 properties: the `capability` and the `commitment`. The `commitment` is the data field for the NFT which can is allowed to be up to 40 bytes. + Capability `none` then refers to an immutible NFT where the commitment cannot be changes. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. -:::note +:::tip A UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both tokens have the same `category`. This is quite a common pattern for covenants which want to hold contract state and fungible tokens on the same UTXO. ::: @@ -100,3 +105,13 @@ contrast this with the following scenario where there is also fungible tokens of require(tx.inputs[0].tokenAmount == 10); require(tx.inputs[0].tokenCategory == providedTokenId); ``` + +## CashTokens Genesis transactions + +A CashTokens genesis transaction is a transaction which creates a new `category` of CashTokens. To create a CashTokens genesis transaction you need a `vout0` UTXO because the txid of the UTXO will be you newly created `category`. + +The requirement for a `vout0` UTXO can mean that you might need to create a setup transaction "pre-genesis" which will create this output. The "pre-genesis" txid then is your token's `category`. + +:::tip +CashTokens Creation is illustrated very nicely by transaction diagram in the specification document in the [sectction on token categories](https://cashtokens.org/docs/spec/chip#token-categories). +::: From 64926c90e8e7b26210cf25600290a6f9199a119f Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Thu, 28 Aug 2025 10:36:05 +0200 Subject: [PATCH 4/9] some spelling fixes --- website/docs/guides/cashtokens.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 86883617..68cf40f4 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -7,7 +7,7 @@ CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by CashTokens was first proposed in February of 2022 and actived on Bitcoin Cash mainchain in May of 2023. :::tip -You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) which has the full specification as well as a list of [Usage Examples](https://cashtokens.org/docs/spec/examples). +You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) which has the full specification as well as a list of [usage examples](https://cashtokens.org/docs/spec/examples). ::: ## CashTokens Utxo data @@ -40,7 +40,7 @@ The maximum size for a fungible token `amount` is the max signed 64-bit integer The `nft` info on a UTXO will only be present if the UTXO contains an NFT. The `nft` object has 2 properties: the `capability` and the `commitment`. The `commitment` is the data field for the NFT which can is allowed to be up to 40 bytes. -Capability `none` then refers to an immutible NFT where the commitment cannot be changes. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. +Capability `none` then refers to an immutable NFT where the commitment cannot be changes. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. :::tip A UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both tokens have the same `category`. @@ -85,8 +85,8 @@ generally not recommended to do the byte-reversal in script require(tx.inputs[0].tokenCategory == providedTokenId.reverse()); ``` -#### 3) "invisibe" empty nfts -Because the nft-capability has no separate introspection item, and nothing is appended to the `tokenCategory` in case of capability `none`, empty nfts can be "invisibe" when combined with fungible tokens. +#### 3) "invisible" empty nfts +Because the nft-capability has no separate introspection item, and nothing is appended to the `tokenCategory` in case of capability `none`, empty nfts can be "invisible" when combined with fungible tokens. ```solidity // Input 0 has an empty nft (commitment 0x) with providedTokenId @@ -113,5 +113,5 @@ A CashTokens genesis transaction is a transaction which creates a new `category` The requirement for a `vout0` UTXO can mean that you might need to create a setup transaction "pre-genesis" which will create this output. The "pre-genesis" txid then is your token's `category`. :::tip -CashTokens Creation is illustrated very nicely by transaction diagram in the specification document in the [sectction on token categories](https://cashtokens.org/docs/spec/chip#token-categories). +CashTokens Creation is illustrated very nicely by transaction diagram in the specification document in the [secction on token categories](https://cashtokens.org/docs/spec/chip#token-categories). ::: From 160435563940bf4c000903c77a610cad787104aa Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Mon, 1 Sep 2025 14:38:10 +0200 Subject: [PATCH 5/9] spelling fixes after review --- website/docs/guides/cashtokens.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 68cf40f4..28f385ae 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -40,7 +40,7 @@ The maximum size for a fungible token `amount` is the max signed 64-bit integer The `nft` info on a UTXO will only be present if the UTXO contains an NFT. The `nft` object has 2 properties: the `capability` and the `commitment`. The `commitment` is the data field for the NFT which can is allowed to be up to 40 bytes. -Capability `none` then refers to an immutable NFT where the commitment cannot be changes. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. +Capability `none` then refers to an immutable NFT where the commitment cannot be changed. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. :::tip A UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both tokens have the same `category`. @@ -56,7 +56,7 @@ While CashTokens might seem overwhelming at first, realize that in contracts you and their equivalent for outputs: -- **`bytes tx.outputs[i].tokenCategory`** - `tokenCategory` + `tokenCapability` of a specific output. +- **`bytes tx.outputs[i].tokenCategory`** - `tokenCategory` + `tokenCapability` of a specific output. (see [below](#1-tokencategory-contains-the-nft-capability)). - **`bytes tx.outputs[i].nftCommitment`** - NFT commitment data of a specific output - **`int tx.outputs[i].tokenAmount`** - Amount of fungible tokens of a specific output. @@ -113,5 +113,5 @@ A CashTokens genesis transaction is a transaction which creates a new `category` The requirement for a `vout0` UTXO can mean that you might need to create a setup transaction "pre-genesis" which will create this output. The "pre-genesis" txid then is your token's `category`. :::tip -CashTokens Creation is illustrated very nicely by transaction diagram in the specification document in the [secction on token categories](https://cashtokens.org/docs/spec/chip#token-categories). +CashTokens Creation is illustrated very nicely by transaction diagram in the specification document in the [section on token categories](https://cashtokens.org/docs/spec/chip#token-categories). ::: From 86c2f16a467a7d32a5367ea37f1f56940d60d45a Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Mon, 1 Sep 2025 14:48:10 +0200 Subject: [PATCH 6/9] add to the gotchas --- website/docs/guides/cashtokens.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 28f385ae..19c6566a 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -88,6 +88,8 @@ generally not recommended to do the byte-reversal in script #### 3) "invisible" empty nfts Because the nft-capability has no separate introspection item, and nothing is appended to the `tokenCategory` in case of capability `none`, empty nfts can be "invisible" when combined with fungible tokens. +First let's consider the case where a UTXO only holds an empty NFT: + ```solidity // Input 0 has an empty nft (commitment 0x) with providedTokenId // If there was no empty nft the tokenCategory would return 0x @@ -96,7 +98,7 @@ Because the nft-capability has no separate introspection item, and nothing is ap require(tx.inputs[0].tokenCategory == providedTokenId); ``` -contrast this with the following scenario where there is also fungible tokens of the same category: +Contrast this with the scenario where a UTXO holds both an empty NFT and fungible tokens of the same category: ```solidity // Input 0 might or might not have an empty nft (commitment 0x) with providedTokenId @@ -106,6 +108,18 @@ contrast this with the following scenario where there is also fungible tokens of require(tx.inputs[0].tokenCategory == providedTokenId); ``` +Both scenarios look the same from the point of the smart contract. + +This means that a covenant UTXO holding both a minting NFT and the fungible token supply for the same token `category` cannot prevent that empty nfts are created by users when they are allowed to create a fungible token output. The possibility of these "junk" empty NFTs should be taken into account so they do not present any security problems for the contract system. + +#### 4) Explicit vs implicit burning + +CashTokens can be burned explicitly by sending them to an opreturn output, which is provably unspendable. CashTokens can also be burned implicitly, by including them in the inputs but not the outputs of a transaction. Always be mindful when adding token-carrying inputs to not forget to add the tokens in the outputs, otherwise they will be considered as an implicit burn. + +:::tip +Signing for CashTokens inputs is designed in such a way that pre-CashTokens wallets - which only know how to send and receive Bitcoin Cash - cannot spend CashTokens inputs and thus can never accidentally burn CashTokens this way. +::: + ## CashTokens Genesis transactions A CashTokens genesis transaction is a transaction which creates a new `category` of CashTokens. To create a CashTokens genesis transaction you need a `vout0` UTXO because the txid of the UTXO will be you newly created `category`. From 230dd4f0313a877e9ff7a05d6462bf0dfbcadf59 Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Mon, 1 Sep 2025 16:31:21 +0200 Subject: [PATCH 7/9] add bcmr section --- website/docs/guides/cashtokens.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 19c6566a..69e09216 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -42,7 +42,7 @@ The `nft` info on a UTXO will only be present if the UTXO contains an NFT. The ` Capability `none` then refers to an immutable NFT where the commitment cannot be changed. The `mutable` capability means the `commitment` field can change over time, usually to contain smart contract state. Lastly the `minting` capability means that the NFT can create new NFTs from the same `category`. -:::tip +:::note A UTXO can hold both an `amount` of fungible tokens as well as an `nft`, as long as both tokens have the same `category`. This is quite a common pattern for covenants which want to hold contract state and fungible tokens on the same UTXO. ::: @@ -129,3 +129,13 @@ The requirement for a `vout0` UTXO can mean that you might need to create a setu :::tip CashTokens Creation is illustrated very nicely by transaction diagram in the specification document in the [section on token categories](https://cashtokens.org/docs/spec/chip#token-categories). ::: + +## CashTokens BCMR metadata + +Although not directly related to smart contracts, BCMR metadata is important for user-facing CashTokens. This way user can see your token name, icon, description and any relevant project links directly in their wallet. Many CashTokens wallets use the [Paytaca BCMR indexer](https://bcmr.paytaca.com/) to fetch BCMR metadata info about CashTokens. + +The Paytaca BCMR indexer listens for on-chain [authchain](https://github.com/bitjson/chip-bcmr?tab=readme-ov-file#zeroth-descendant-transaction-chains) transactions which publish metadata with an opreturn publication output. These type of metadata updates are self-published on-chain identity claims. The zero-th output chain since the token gensis is the authchain, the UTXO at the "head" of this chain holds the authority to update the token's metadata. + +:::tip +For easy creation of CashTokens with BCMR metadata there is the Paytaca [CashTokens Studio](https://cashtokens.studio/) or to programmatically publish on-chain BCMR authchain updates there is the [AuthUpdate](https://github.com/mr-zwets/AuthUpdate) JS program. +::: From f2d46dda749df4a71c2fc1e5b981e594c8e87c0e Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Tue, 2 Sep 2025 09:05:26 +0200 Subject: [PATCH 8/9] fix some spelling issues --- website/docs/guides/cashtokens.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 69e09216..be331b98 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -4,7 +4,7 @@ sidebar_label: CashTokens --- CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by all full nodes on the network and their transaction rules checked by each miner when constructing new blocks. CashTokens added fungible and non-fungible token primitives. -CashTokens was first proposed in February of 2022 and actived on Bitcoin Cash mainchain in May of 2023. +CashTokens was first proposed in February of 2022 and activated on Bitcoin Cash mainchain in May of 2023. :::tip You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) which has the full specification as well as a list of [usage examples](https://cashtokens.org/docs/spec/examples). @@ -132,9 +132,9 @@ CashTokens Creation is illustrated very nicely by transaction diagram in the spe ## CashTokens BCMR metadata -Although not directly related to smart contracts, BCMR metadata is important for user-facing CashTokens. This way user can see your token name, icon, description and any relevant project links directly in their wallet. Many CashTokens wallets use the [Paytaca BCMR indexer](https://bcmr.paytaca.com/) to fetch BCMR metadata info about CashTokens. +Although not directly related to smart contracts, BCMR metadata is important for user-facing CashTokens. This way users can see your token name, icon, description and any relevant project links directly in their wallet. Many CashTokens wallets use the [Paytaca BCMR indexer](https://bcmr.paytaca.com/) to fetch BCMR metadata info about CashTokens. -The Paytaca BCMR indexer listens for on-chain [authchain](https://github.com/bitjson/chip-bcmr?tab=readme-ov-file#zeroth-descendant-transaction-chains) transactions which publish metadata with an opreturn publication output. These type of metadata updates are self-published on-chain identity claims. The zero-th output chain since the token gensis is the authchain, the UTXO at the "head" of this chain holds the authority to update the token's metadata. +The Paytaca BCMR indexer listens for on-chain [authchain](https://github.com/bitjson/chip-bcmr?tab=readme-ov-file#zeroth-descendant-transaction-chains) transactions which publish metadata with an opreturn publication output. These type of metadata updates are self-published on-chain identity claims. The zero-th output chain since the token genesis is the authchain. The UTXO at the "head" of this chain holds the authority to update the token's metadata. :::tip For easy creation of CashTokens with BCMR metadata there is the Paytaca [CashTokens Studio](https://cashtokens.studio/) or to programmatically publish on-chain BCMR authchain updates there is the [AuthUpdate](https://github.com/mr-zwets/AuthUpdate) JS program. From b41f0c1a9d8493152296211c58b81d30a06f698a Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Tue, 2 Sep 2025 10:42:13 +0200 Subject: [PATCH 9/9] Small updated to CashTokens guide and update cSpell lists --- .cspell.json | 3 +++ website/docs/guides/cashtokens.md | 44 +++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/.cspell.json b/.cspell.json index 565a20cc..c928d4b4 100644 --- a/.cspell.json +++ b/.cspell.json @@ -8,6 +8,7 @@ "algolia", "altstack", "antlr", + "authchain", "anyhedge", "anyonecanpay", "badlength", @@ -66,6 +67,7 @@ "fromaltstack", "gitattributes", "gitlab", + "gotchas", "greaterthan", "greaterthanorequal", "hardcodes", @@ -211,6 +213,7 @@ "branchup", "bchguru", "bchn", + "bcmr", "c0ffee", "cashcompiler", "cashninjas", diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index be331b98..3f0f2415 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -4,13 +4,13 @@ sidebar_label: CashTokens --- CashTokens are native tokens on Bitcoin Cash, meaning that they are validated by all full nodes on the network and their transaction rules checked by each miner when constructing new blocks. CashTokens added fungible and non-fungible token primitives. -CashTokens was first proposed in February of 2022 and activated on Bitcoin Cash mainchain in May of 2023. +CashTokens was first proposed in February of 2022 and activated on Bitcoin Cash mainnet in May of 2023. :::tip You can read more about CashTokens on [cashtokens.org](https://cashtokens.org/) which has the full specification as well as a list of [usage examples](https://cashtokens.org/docs/spec/examples). ::: -## CashTokens Utxo data +## CashTokens UTXO data To understand CashTokens it is helpful to start with the layout of the UTXO data. In the `networkProvider` data from the SDK, the `token` property contains the new CashTokens fields: @@ -31,7 +31,7 @@ interface TokenDetails { }; } ``` -### Fungible Tokens +### Fungible Tokens The `amount` field is the amount of fungible tokens on the UTXO, the `category` is the "tokenId" for the token on the UTXO. The maximum size for a fungible token `amount` is the max signed 64-bit integer or `9223372036854775807`. @@ -61,14 +61,32 @@ and their equivalent for outputs: - **`int tx.outputs[i].tokenAmount`** - Amount of fungible tokens of a specific output. ## CashTokens Gotchas -There are two important "gotchas" to be aware of when developing with CashTokens in smart contracts for the first time +There are a few important "gotchas" to be aware of when developing with CashTokens in smart contracts for the first time. #### 1) tokenCategory contains the nft-capability ```solidity bytes tx.inputs[i].tokenCategory ``` -When accessing the `tokenCategory` through introspection the result returns `0x` when that specific item does not contain tokens. If the item does have tokens it returns the `bytes32 tokenCategory`. When the item contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. +When accessing the `tokenCategory` through introspection the result returns `0x` (empty byte string) when that specific item does not contain tokens. If the item does have tokens it returns the `bytes32 tokenCategory`. When the item contains an NFT with a capability, the 32-byte `tokenCategory` is concatenated together with `0x01` for a mutable NFT and `0x02` for a minting NFT. + +If you want to check for an NFT using introspection, you have either split the `tokenCategory` from the `capability` or check the concatenation of the `tokenCategory` and `capability`. + +```solidity +// Constructor parameters: providedCategory + +// Extract the separate tokenCategory and capability +bytes32 tokenCategory, bytes capability = tx.inputs[0].tokenCategory.split(32); + +// Check that the NFT is the correct category and has a "minting" capability +require(providedCategory == tokenCategory); +require(capability == 0x02); + +// Alternatively: + +// Check by concatenating the providedCategory and capability +require(tx.inputs[0].tokenCategory == providedCategory + 0x02); +``` #### 2) tokenCategory encoding @@ -79,7 +97,7 @@ The `tokenCategory` introspection variable returns the tokenCategory in the orig const contract = new Contract(artifact, [reverseHex(tokenId)], { provider }) ``` -generally not recommended to do the byte-reversal in script +It is not recommended to do the byte-reversal in script, because this adds extra unnecessary overhead to the script. ```solidity // NOT THIS require(tx.inputs[0].tokenCategory == providedTokenId.reverse()); @@ -108,15 +126,19 @@ Contrast this with the scenario where a UTXO holds both an empty NFT and fungibl require(tx.inputs[0].tokenCategory == providedTokenId); ``` -Both scenarios look the same from the point of the smart contract. +The NFT introspection fields (`nftCommitment` and `tokenCategory`) of these UTXOs look the same to the smart contract in both of these scenarios. + +This means that a covenant UTXO holding both a minting NFT and the fungible token supply for the same token `category` cannot prevent that empty nfts are created by users when they are allowed to create a fungible token output. The possibility of these "junk" empty NFTs should be taken into account so they do not present any security problems for the contract system. -This means that a covenant UTXO holding both a minting NFT and the fungible token supply for the same token `category` cannot prevent that empty nfts are created by users when they are allowed to create a fungible token output. The possibility of these "junk" empty NFTs should be taken into account so they do not present any security problems for the contract system. +:::tip +The easiest way to prevent issues with "junk" empty NFTs is to check that only NFTs with non-empty commitments can be interacted with in the contract system. +::: #### 4) Explicit vs implicit burning -CashTokens can be burned explicitly by sending them to an opreturn output, which is provably unspendable. CashTokens can also be burned implicitly, by including them in the inputs but not the outputs of a transaction. Always be mindful when adding token-carrying inputs to not forget to add the tokens in the outputs, otherwise they will be considered as an implicit burn. +CashTokens can be burned explicitly by sending them to an OP_RETURN output, which is provably unspendable. CashTokens can also be burned implicitly, by including them in the inputs but not the outputs of a transaction. Always be mindful when adding token-carrying inputs to not forget to add the tokens in the outputs, otherwise they will be considered as an implicit burn. -:::tip +:::note Signing for CashTokens inputs is designed in such a way that pre-CashTokens wallets - which only know how to send and receive Bitcoin Cash - cannot spend CashTokens inputs and thus can never accidentally burn CashTokens this way. ::: @@ -134,7 +156,7 @@ CashTokens Creation is illustrated very nicely by transaction diagram in the spe Although not directly related to smart contracts, BCMR metadata is important for user-facing CashTokens. This way users can see your token name, icon, description and any relevant project links directly in their wallet. Many CashTokens wallets use the [Paytaca BCMR indexer](https://bcmr.paytaca.com/) to fetch BCMR metadata info about CashTokens. -The Paytaca BCMR indexer listens for on-chain [authchain](https://github.com/bitjson/chip-bcmr?tab=readme-ov-file#zeroth-descendant-transaction-chains) transactions which publish metadata with an opreturn publication output. These type of metadata updates are self-published on-chain identity claims. The zero-th output chain since the token genesis is the authchain. The UTXO at the "head" of this chain holds the authority to update the token's metadata. +The Paytaca BCMR indexer listens for on-chain [authchain](https://github.com/bitjson/chip-bcmr?tab=readme-ov-file#zeroth-descendant-transaction-chains) transactions which publish metadata with an OP_RETURN publication output. These type of metadata updates are self-published on-chain identity claims. The zero-th output chain since the token genesis is the authchain. The UTXO at the "head" of this chain holds the authority to update the token's metadata. :::tip For easy creation of CashTokens with BCMR metadata there is the Paytaca [CashTokens Studio](https://cashtokens.studio/) or to programmatically publish on-chain BCMR authchain updates there is the [AuthUpdate](https://github.com/mr-zwets/AuthUpdate) JS program.