Skip to content

Commit 8a32f2c

Browse files
committed
add CashTokens guide
1 parent d878d06 commit 8a32f2c

7 files changed

Lines changed: 119 additions & 5 deletions

File tree

website/docs/guides/cashtokens.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
title: CashTokens
3+
sidebar_label: CashTokens
4+
---
5+
6+
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.
7+
CashTokens was first proposed in February of 2022 and actived on Bitcoin Cash mainchain in May of 2023.
8+
9+
:::tip
10+
The specification for CashTokens is the ['CHIP-2022-02-CashTokens: Token Primitives for Bitcoin '](https://github.com/cashtokens/cashtokens) document.
11+
:::
12+
13+
## CashTokens Utxo data
14+
15+
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:
16+
17+
```ts
18+
interface Utxo {
19+
txid: string;
20+
vout: number;
21+
satoshis: bigint;
22+
token?: TokenDetails;
23+
}
24+
25+
interface TokenDetails {
26+
amount: bigint;
27+
category: string;
28+
nft?: {
29+
capability: 'none' | 'mutable' | 'minting';
30+
commitment: string;
31+
};
32+
}
33+
```
34+
35+
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").
36+
37+
## CashTokens introspection data
38+
While CashTokens might seem overwhelming at first, realize that in contracts you will only use it through the following introspection details
39+
40+
- **`bytes tx.inputs[i].tokenCategory`** - `tokenCategory` + `tokenCapability` of a specific input.
41+
- **`bytes tx.inputs[i].nftCommitment`** - NFT commitment data of a specific input.
42+
- **`int tx.inputs[i].tokenAmount`** - Amount of fungible tokens of a specific input.
43+
44+
and their equivalent for outputs:
45+
46+
- **`bytes tx.outputs[i].tokenCategory`** - `tokenCategory` + `tokenCapability` of a specific output.
47+
- **`bytes tx.outputs[i].nftCommitment`** - NFT commitment data of a specific output
48+
- **`int tx.outputs[i].tokenAmount`** - Amount of fungible tokens of a specific output.
49+
50+
## CashTokens Gotchas
51+
There are two important "gotchas" to be aware of when developing with CashTokens in smart contracts for the first time
52+
53+
#### 1) tokenCategory contains the nft-capability
54+
```solidity
55+
bytes tx.inputs[i].tokenCategory
56+
```
57+
58+
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.
59+
60+
#### 2) tokenCategory encoding
61+
62+
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.
63+
64+
```ts
65+
// when using a standard encoded tokenId, reverse the hex before using it in your contract
66+
const contract = new Contract(artifact, [reverseHex(tokenId)], { provider })
67+
```
68+
69+
generally not recommended to do the byte-reversal in script
70+
```solidity
71+
// NOT THIS
72+
require(tx.inputs[0].tokenCategory == providedTokenId.reverse());
73+
```
74+
75+
#### 3) "invisibe" empty nfts
76+
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.
77+
78+
```solidity
79+
// Input 0 has an empty nft (commitment 0x) with providedTokenId
80+
// If there was no empty nft the tokenCategory would return 0x
81+
require(tx.inputs[0].nftCommitment == 0x);
82+
require(tx.inputs[0].tokenAmount == 0);
83+
require(tx.inputs[0].tokenCategory == providedTokenId);
84+
```
85+
86+
contrast this with the following scenario where there is also fungible tokens of the same category:
87+
88+
```solidity
89+
// Input 0 might or might not have an empty nft (commitment 0x) with providedTokenId
90+
// Either way, the tokenCategory would return providedTokenId
91+
require(tx.inputs[0].nftCommitment == 0x);
92+
require(tx.inputs[0].tokenAmount == 10);
93+
require(tx.inputs[0].tokenCategory == providedTokenId);
94+
```

website/docs/language/globals.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Represents the `nSequence` number of a specific input. This value of the input's
131131
bytes tx.inputs[i].tokenCategory
132132
```
133133

134-
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.
134+
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.
135135

136136
:::caution
137137
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.
149149
int tx.inputs[i].tokenAmount
150150
```
151151

152-
Represents the amount of fungible tokens of a specific input.
152+
Represents the amount of fungible tokens of a specific input. Maximum size for a `tokenAmount` is a 64-bit integer.
153153

154154
### tx.outputs
155155
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.
180180
bytes tx.output[i].tokenCategory
181181
```
182182

183-
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.
183+
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.
184184

185185
:::caution
186186
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.
198198
int tx.output[i].tokenAmount
199199
```
200200

201-
Represents the amount of fungible tokens of a specific output.
201+
Represents the amount of fungible tokens of a specific output. Maximum size for a `tokenAmount` is a 64-bit integer.
202202

203203
## Constructing locking bytecode
204204
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.
File renamed without changes.

website/docs/sdk/electrum-network-provider.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ interface Utxo {
5757
satoshis: bigint;
5858
token?: TokenDetails;
5959
}
60+
61+
interface TokenDetails {
62+
amount: bigint;
63+
category: string;
64+
nft?: {
65+
capability: 'none' | 'mutable' | 'minting';
66+
commitment: string;
67+
};
68+
}
6069
```
6170

6271
#### Example

website/docs/sdk/network-provider.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ interface Utxo {
3535
satoshis: bigint;
3636
token?: TokenDetails;
3737
}
38+
39+
interface TokenDetails {
40+
amount: bigint;
41+
category: string;
42+
nft?: {
43+
capability: 'none' | 'mutable' | 'minting';
44+
commitment: string;
45+
};
46+
}
3847
```
3948

4049
#### Example

website/docusaurus.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ module.exports = {
121121
{ from: '/docs/sdk', to: '/docs/sdk/instantiation' },
122122
{ from: '/docs/sdk/transactions-advanced', to: '/docs/sdk/transaction-builder' },
123123
{ from: '/docs/guides', to: '/docs/guides/covenants' },
124+
{ from: '/docs/guides/syntax-highlighting', to: '/docs/language/syntax-highlighting' },
124125
{ from: '/docs/getting-started', to: '/docs/basics/getting-started' },
125126
{ from: '/docs/examples', to: '/docs/language/examples' },
126127
],

website/sidebars.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = {
1818
'language/functions',
1919
'language/globals',
2020
'language/examples',
21+
'language/syntax-highlighting'
2122
],
2223
},
2324
{
@@ -54,8 +55,8 @@ module.exports = {
5455
type: 'category',
5556
label: 'Guides',
5657
items: [
57-
'guides/syntax-highlighting',
5858
'guides/covenants',
59+
'guides/cashtokens',
5960
'guides/infrastructure',
6061
'guides/walletconnect',
6162
'guides/debugging',

0 commit comments

Comments
 (0)