Author: ZAN Team
This article is only for technical sharing and does not constitute any investment advice.
Does BTC also have its own smart contracts?
Recently, in the Bitcoin ecosystem, Fractal BTC finally launched the mainnet in September after going through multiple testnets. One of the major features of Fractal is that it has the ability of "smart contracts", and almost at the same time as the launch of the mainnet, it launched a new token protocol CAT20. What technical ingenuity does CAT20 have? What can we learn?
Fractal Bitcoin
Before understanding CAT20, we need to briefly understand Fractal Bitcoin. Their relationship is like that of ERC20 and ETH. The CAT20 protocol is deployed on Fractal Bitcoin.
Fractal Bitcoin, also known as fractal Bitcoin, is a "second-layer" network that is fully compatible with BTC. Compared with BTC, its block confirmation time is faster, taking only 1 minute. Its basic principle is simply as its name suggests, that is, the BTC network is copied several times, and each chain will process transactions. With more nodes that can process transactions, the speed will naturally be faster. However, the specific details, such as how different chains communicate with each other, are not very clear at present, and there is no official technical documentation for reference.
If only a second-layer chain transaction is faster, it seems that there is nothing exciting. However, the OP_CAT opcode, which was abandoned by BTC for security reasons a long time ago, was enabled in Fractal, which raised the capabilities of Fractal Bitcoin to a higher level. Some people say that OP_CAT can make BTC have the ability of smart contracts, which gives more room for imagination.
Now, someone has implemented a protocol similar to ERC20 on Fractal Bitcoin.
Why OP_CAT was abandoned and why it can be used on Fractal Bitcoin can be discussed later. Here we focus on CAT20.
CAT Protocol
The following content refers to the white paper: Introduction | CAT Protocol (https://catprotocol.org/)
and the github repository:
GitHub - CATProtocol/cat-token-box: A monorepo for packages implementing CAT protocol (https://github.com/CATProtocol/cat-token-box)
With the underlying OP_CAT support, the corresponding protocol, CAT Protocol, will soon be available. At present, a protocol that is already running is the CAT20 protocol, and a corresponding panel has been added on Unisat: https://explorer.unisat.io/fractal-mainnet/cat20.
When you see the name CAT20, you should be able to react. It should be similar to ERC20. Compared with the mature ERC20 protocol, it is very convenient for everyone to deploy a token. How does CAT20 achieve a life cycle similar to ERC20?
Deploy
Before deployment, users need to specify their wallet address and basic information of the token. The basic information of the token is similar to that of ERC20:
There will be some differences. CAT20 can set the number of pre-mining and each Mint limit. Of course, ERC20 can also achieve these capabilities through the capabilities of the contract.
During the deployment phase, two transactions will be initiated, which can be considered as two stages: "commit" and "reveal". Quoting the official diagram, the deployment stages are as follows:
In the "commit" stage, the basic information of the token, such as the name and symbol of the token, will be written into the output script of the transaction. The hashId of the transaction initiated in the "commit" stage will be used as the logo of the token to distinguish other tokens.
You can see that the utxo of this transaction "bc1pucq...ashx" corresponds to commit. Then the remaining two transactions pointing to "bc1pszp...rehc4", the first one is used to pay the gas fee in the "reveal" stage below, and the other one is for change.
In the "reveal" stage, you can see that there are two utxo inputs, corresponding to the first two outputs in the previous commit stage. This transaction will first output an OP_RETURN, in which the Hash of the initial state of CAT20 will be saved. After that, a Minter will be output, which will play an important role in the subsequent Mint process and is used to maintain the state changes of the Mint process.
Looking back at the entire Deploy process, "commit" and "reveal" follow the two steps of commit and reveal commonly used on the blockchain. It is a common way to deploy projects. Some data of the project will only be revealed in the "reveal" stage.
Mint
Let's first look at the Mint Token, the transaction is like this.
As can be seen in the figure above, the Mint process has the following characteristics.
The input of mint is a minter, which is initially generated during deploy.
Each mint has one and only one minter as input, and any number of minters as output (a little bit problematic)
Each mint has one and only one token (a little bit problematic)
The order of output is required, minter must be followed by token
Knowing the process of mint, we can actually find some special cases that will make the entire mint process interesting.
For example, minter as the output of mint transaction can be 1, multiple or even 0. If you set it to 1 each time you mint, the number of minters available in the entire network will remain the same (1), which will make the mint crowded and everyone will need to grab this minter. To avoid this, you need to set the number of minters output each time to be greater than 1, so that after minting, everyone will have more and more minters available.
However, each additional minter output means that you need to pay an extra utxo. For economic reasons, more people will be happy to set the minter to 0, which will inevitably make the minter deflationary. This requires some people to make contributions and voluntarily pay for the extra minters.
In the V2 version, the default is to generate two minters, and the status of the two minters will be as close as possible.
Transaction construction
Some friends may have discovered a problem, that is, why can minter's utxo be used to construct transactions? To understand this problem, you need to analyze the source code of the "contract".
1. Reveal utxo
First, we analyze the transaction in the reveal process, and we find that it uses the output commit of the previous transaction as input. Why can we use a utxo that is not our address to construct the input of the transaction?
According to common sense, a private key corresponds to a public key, and the public key derives the address. When verifying whether an input utxo is valid, it is generally determined by comparing the signature and whether it is consistent with the original transaction after decryption with the public key. This part of the logic is written in the Bitcoin script. So we can cleverly rewrite the logic of the script, and the public and private key pairs written in the script are our own addresses, so that we can control the utxo of two different addresses.
Looking at the source code, we can know what happened:
There will be another problem here, that is, one private key corresponds to one public key, so why is the generated commit address different from our address? Here you can see from the source code
That is to say, our private key will adjust the public key according to an ISSUE_PUBKEY, which is also a feature of the P2TR address.
2. Minter utxo
During the reveal process, we use different utxo as input, but in fact the encryption key is the same, that is, the private key of the deployer. But in the minter stage, everyone can use these utxo as input. How is this done?
This part I guess is the ability of OP_CAT mentioned earlier, that is, the ability of smart contracts. Each minter is a smart contract. However, the source code of this part is not public at present, and the specific implementation is not known for the time being.
Transaction status (V2)
In minter, the status is also retained. This status exists in two places: one is in the OP_RETURN of the transaction output, and the other is stored in the smart contract, that is, the Minter and Token mentioned above.
The Hash of the current transaction output status is stored in OP_RETURN, and the number of remaining mints of the token is stored in the contract. After each mint, the mint quantity of the newly generated minter will be equal to the remaining number of mints divided by two. Represented by a diagram:
At the end, the remaining number of all minters is 0.
Back to the original diagram, in addition to Minter being a smart contract, the generated Token is also a smart contract, that is, CAT20. CAT20 has two basic states: quantity and the address of the owner of the Token. You can see that unlike the previous BRC20 or inscription, your CAT20 is not on the UTXO of your address.
Transfer
When constructing a transfer, the number of tokens in the input and output of the transaction needs to be consistent. Of course, there can be multiple different tokens in the same transaction, as long as the number of inputs and outputs of different tokens is consistent.
Burn
If you want to burn the token, just transfer the token to a normal address.
Summary
You can see that all operations are constructed by the user himself, which is very flexible, so a lot of verification logic needs to be done in the contract part. Some of the vulnerabilities that have been exposed so far are also due to negligence in the verification logic.
Such a design can have some advantages:
If you want to find the holding status of all tokens, you only need to check the utxo of the token, and there is no need to continue to check upwards.
If you want to check the current status of mint, you can search for transactions with cat in the data of OP_RETURN.