From 01bda31267406c4e73e11b3a91bbee320f1f2e93 Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Sat, 27 Sep 2025 16:39:45 +0200 Subject: [PATCH 1/2] add to cashtokens guide --- website/docs/guides/cashtokens.md | 88 +++++++++++++++++++++++-------- website/docs/guides/covenants.md | 3 +- 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/website/docs/guides/cashtokens.md b/website/docs/guides/cashtokens.md index 3f0f2415..fa524440 100644 --- a/website/docs/guides/cashtokens.md +++ b/website/docs/guides/cashtokens.md @@ -60,10 +60,31 @@ and their equivalent for outputs: - **`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 Use cases + +The Jedex has a section on novel "[demonstrated concepts](https://github.com/bitjson/jedex#demonstrated-concepts)" enabled by CashTokens. The Jedex overview serves as the best reference to-date for the new possibilities enabled by CashTokens. + +:::tip +The [Jedex demo](https://github.com/bitjson/jedex) also introduces very advanced concepts on multi-threading and MEV avoidance through batching. This core feature of 'joint-execution' is how the DEX got its name. +::: + +Below we'll create a short list of the use cases which will be the most important to know about: + +- **Covenant tracking tokens** - this is what enables unique authentication of contract deployments +- **Commitment-based state management** - this is what `mutable` nfts are extremely useful for +- **Depository covenants/token pools** - which we would call token sidecars +- **Role tokens** - these are authentication tokens for admins +- **Token-unlocked covenants** - this concept has also been called "pay-to-nft" +- **Redeemable NFTs** - `immutable` nfts can carry state as a receipt which can be returned later for payout +- **Coupled covenants/logic offloading** - which we would call sidecar functions +- **Spin-off covenants** - the idea that contract create regular helper contracts to perform some task + ## CashTokens Gotchas -There are a few 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. We'll separate them on gotchas on the contract side, and gotchas on the transaction building side. + +### Contract Gotchas -#### 1) tokenCategory contains the nft-capability +#### tokenCategory contains the nft-capability ```solidity bytes tx.inputs[i].tokenCategory ``` @@ -73,37 +94,37 @@ When accessing the `tokenCategory` through introspection the result returns `0x` 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 + // Constructor parameters: providedCategory -// Extract the separate tokenCategory and capability -bytes32 tokenCategory, bytes capability = tx.inputs[0].tokenCategory.split(32); + // 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); + // Check that the NFT is the correct category and has a "minting" capability + require(providedCategory == tokenCategory); + require(capability == 0x02); -// Alternatively: + // Alternatively: -// Check by concatenating the providedCategory and capability -require(tx.inputs[0].tokenCategory == providedCategory + 0x02); + // Check by concatenating the providedCategory and capability + require(tx.inputs[0].tokenCategory == providedCategory + 0x02); ``` -#### 2) tokenCategory encoding +#### protect the minting capability -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. +If a covenant contains a `minting` NFT then all outputs should be carefully accounted for in the contract logic to not accidentally allow to mint extra new NFTs. -```ts -// when using a standard encoded tokenId, reverse the hex before using it in your contract -const contract = new Contract(artifact, [reverseHex(tokenId)], { provider }) -``` - -It is not recommended to do the byte-reversal in script, because this adds extra unnecessary overhead to the script. +For a variable number of outputs you could use the following construction: ```solidity - // NOT THIS - require(tx.inputs[0].tokenCategory == providedTokenId.reverse()); + // Optionally create bch-change output at outputIndex 5 + if (tx.outputs.length > 5) { + require(tx.outputs[5].tokenCategory == 0x, "Invalid BCH change output - should not hold any tokens"); + } + + // Don't allow more outputs to prevent minting extra NFTs + require(tx.outputs.length <= 6, "Invalid number of outputs - should have 6 at most"); ``` -#### 3) "invisible" empty nfts +#### "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: @@ -134,7 +155,28 @@ This means that a covenant UTXO holding both a minting NFT and the fungible toke 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 +### Transaction Building Gotchas + +#### 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 }) +``` + +#### Combined BCH + CashTokens UTXOs + +Most end-user CashTokens wallets expect CashTokens UTXOs to only hold a tiny amount of BCH like 1000 sats. Deviating from the expectation might cause unforeseen problems with user's wallets. + +:::tip +You can hard code in your contract that any user token output should have exactly `1000` sats, this avoids possible complicating freedom during transaction building. +::: + +However when constructing a transaction with user owned UTXOs, you should always make sure to check whether you handle the edge case of users with combined BCH + CashTokens UTXOs correctly in change output handling both for BCH and the tokens. + +#### Explicit vs implicit burning 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. diff --git a/website/docs/guides/covenants.md b/website/docs/guides/covenants.md index 35a978db..659bb42b 100644 --- a/website/docs/guides/covenants.md +++ b/website/docs/guides/covenants.md @@ -322,10 +322,9 @@ All outputs of the `PooledFunds` contract need to be carefully controlled in the With contracts holding minting NFTs, all outputs need to be carefully controlled in the covenant contract code, so no additional (minting) NFTs can un-intentionally be created in other outputs. ::: - ## Conclusion We have discussed the main uses for covenants as they exist on Bitcoin Cash today. We've seen how we can achieve different use cases by combining transaction output restrictions to `P2SH` and `P2PKH` outputs. We also touched on more advanced subjects such as keeping local state in NFTs. Covenants and CashTokens are the **main differentiating factor** for BCH smart contracts when compared to BTC, while keeping the same **efficient, atomic verification**. -Keeping local state in NFTs and issuing NFTs as receipts are two strategies which can be used to create much more sophisticated decentralized applications such as the AMM-style DEX named [Jedex](https://blog.bitjson.com/jedex-decentralized-exchanges-on-bitcoin-cash/). +Keeping local state in NFTs and issuing NFTs as receipts are two strategies which can be used to create much more sophisticated decentralized applications. You can read more of these advanced CashTokens use cases in our [dedicated guide](/docs/guides/cashtokens#cashtokens-usecases)! [bitcoin-covenants]: https://fc16.ifca.ai/bitcoin/papers/MES16.pdf From 7292cc48a75ca25fd2c55deeb0e0c9101d8e0299 Mon Sep 17 00:00:00 2001 From: Mathieu Geukens Date: Sat, 27 Sep 2025 20:42:02 +0200 Subject: [PATCH 2/2] small typo fixes --- website/docs/guides/adversarial.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/guides/adversarial.md b/website/docs/guides/adversarial.md index 607a5694..79964c40 100644 --- a/website/docs/guides/adversarial.md +++ b/website/docs/guides/adversarial.md @@ -37,7 +37,7 @@ For an adversarial attack to pull off this time-sensitive attack, he would requi ### Late Double Spends -In the case of an late double spend (which does not try to exploit a race condition) the adversarial actor need help from a miner. +In the case of a late double spend (which does not try to exploit a race condition) the adversarial actor need help from a miner. Either the adversarial actor needs to convince the miners to abandon their first seen rule or he needs to be mining himself to be able to construct his own block. :::caution @@ -100,7 +100,7 @@ Adversarial analysis should take into account that "first-seen rule" is just a c ### Specialized Block-Builders -As described in the section on "stale-state arbitrage" economic actors ay be incentivized to strategically create a competing transaction chain which takes advantage of an older price state/ratio which has not yet been confirmed in the blockchain. Although miners are not specialized in the optimal construction of DeFi transactions in a block, miner would over time be likely to team up with teams/companies creating this type of software for them. +As described in the section on "stale-state arbitrage" economic actors may be incentivized to strategically create a competing transaction chain which takes advantage of an older price state/ratio which has not yet been confirmed in the blockchain. Although miners are not specialized in the optimal construction of DeFi transactions in a block, miner would over time be likely to team up with teams/companies creating this type of software for them. :::note Ethereum with its large amount of MEV has already seen the emergence of specialized 'block builder' as a new class of relevant economic actors separate from the block proposer (who signs the block).