diff --git a/README.md b/README.md index aaba2fa1e..58a803ac8 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,42 @@ # Website -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. +This documentation website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. ### Installation ``` -$ yarn +yarn ``` ### Local Development +Start a local development server and open a browser window. +Most changes are reflected live without having to restart the server. + ``` -$ yarn start +yarn start ``` -This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. - ### Build +This command generates static content in the `/build` directory and can be served using a static content hosting service like S3 or GitHub Pages. + ``` -$ yarn build +yarn build ``` -This command generates static content into the `build` directory and can be served using any static contents hosting service. - ### Deployment Using SSH: ``` -$ USE_SSH=true yarn deploy +USE_SSH=true yarn deploy ``` Not using SSH: ``` -$ GIT_USER= yarn deploy +GIT_USER= yarn deploy ``` -If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. +If you are using GitHub Pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. diff --git a/docs/advanced/how-to-build-an-oracle-service.md b/docs/advanced/how-to-build-an-oracle-service.md index b2ed7ae06..16b152b11 100644 --- a/docs/advanced/how-to-build-an-oracle-service.md +++ b/docs/advanced/how-to-build-an-oracle-service.md @@ -143,7 +143,7 @@ async getPrice(@Param('base') base: string, @Param('query') query: string) { According to the previous introduction, you can add more APIs to your oracle as needed, such as obtaining BSV chain info, etc., which will not be covered here. -## 4. Use oralce data in a smart contract +## 4. Use oracle data in a smart contract In [this tutorial](../tutorials/oracle.md), we introduce how to verify and use oracle data in smart contracts. @@ -153,4 +153,4 @@ To verify signatures in smart contracts, we need to install the `scrypt-ts-lib` npm install scrypt-ts-lib ``` -Then add the contract under folder `src/contracts`. Here we also use the [PriceBet](https://github.com/sCrypt-Inc/oracle-demo/blob/master/src/contracts/priceBet.ts) contract. You can refer to file [priceBet.e2e-spec.ts](https://github.com/sCrypt-Inc/oracle-demo/blob/master/src/contracts/priceBet.ts) for a complete test code. +Then add the contract under folder `/src/contracts`. Here we also use the [PriceBet](https://github.com/sCrypt-Inc/oracle-demo/blob/master/src/contracts/priceBet.ts) contract. You can refer to file [priceBet.e2e-spec.ts](https://github.com/sCrypt-Inc/oracle-demo/blob/master/src/contracts/priceBet.ts) for a complete test code. diff --git a/docs/bitcoin-basics/bitcoin-basics.md b/docs/bitcoin-basics/bitcoin-basics.md index 054a5d61b..675683a15 100644 --- a/docs/bitcoin-basics/bitcoin-basics.md +++ b/docs/bitcoin-basics/bitcoin-basics.md @@ -3,9 +3,9 @@ sidebar_position: 1 --- # Bitcoin Basics -If you are new to Bitcoin development, we recommend exploring the resources listed in this section. While not an absolute prerequisite, going over these guides will provide a clearer understanding of key concepts and make it easier to begin developing sCrypt smart contracts. +If you are new to Bitcoin development, we recommend exploring the resources listed in this section. While not a required prerequisite, going over these guides will help you develop a better understanding of key concepts and make it easier to begin developing sCrypt smart contracts. -If you are already familiar with the basics of Bitcoin, you can skip ahead to the [How to Write a Contract section](../how-to-write-a-contract/how-to-write-a-contract.md). +If you are already familiar with the basics of Bitcoin, feel free to skip ahead to the [How to Write a Contract](../how-to-write-a-contract/how-to-write-a-contract.md) section. ## Useful Resources for Learning Bitcoin Fundamentals diff --git a/docs/bitcoin-basics/bsv.md b/docs/bitcoin-basics/bsv.md index 4be447d05..e43711b36 100644 --- a/docs/bitcoin-basics/bsv.md +++ b/docs/bitcoin-basics/bsv.md @@ -3,15 +3,15 @@ sidebar_position: 1 --- # The BSV submodule -sCrypt exports a submodule named `bsv` which is an interface that helps you manage low-level things for the Bitcoin blockchain, such as creating key pairs, building, signing and serializing Bitcoin transactions and more. +sCrypt exports a submodule named `bsv` which is an interface that helps you manage low-level things for the Bitcoin blockchain, such as creating key pairs, building, signing and serializing Bitcoin transactions, and more. -In the context of sCrypt, it is mainly used for managing key pairs and defining custom transaction builders, as demonstrated in [this section](../how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md). +In the context of sCrypt, the `bsv` submodule is used primarily for managing key pairs and defining custom transaction builders, as demonstrated in the [How to Write a Contract](../how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md) section. The goal of this section is to guide you through the basics of using the `bsv` submodule. ## Importing -You can import the `bsv` submodule like so: +You can import the `bsv` submodule like this: ```ts import { bsv } from 'scrypt-ts' @@ -19,40 +19,45 @@ import { bsv } from 'scrypt-ts' ## Private Keys -A private key object is essentially just a wrapper around a 256-bit integer. +A `PrivateKey` object is basically a wrapper around a 256-bit integer. -You can generate a Bitcoin private key from a random value: +You can generate a Bitcoin private key (for `mainnet`) from a random value like this: ```ts const privKey = bsv.PrivateKey.fromRandom() // Same as: const privKey = bsv.PrivateKey.fromRandom(bsv.Network.mainnet) ``` -This will generate a private key for the Bitcoin main network. To create a key for the test network (also referred to as "testnet"), do the following instead: +To create a private key for the test network (also referred to as `testnet`), do the following instead: ```ts const privKey = bsv.PrivateKey.fromRandom(bsv.Networks.testnet) ``` -The main difference between a mainnet and a testnet key is how they get serialized. Check out [this page](https://wiki.bitcoinsv.io/index.php/Wallet_Import_Format_(WIF)) which explains this in detail. +The main difference between a mainnet and a testnet key is how they get serialized. +Check out this [BitcoinSV Wiki page on WIFs](https://wiki.bitcoinsv.io/index.php/Wallet_Import_Format_(WIF)) which explains the differences in more detail. + +You can also create `PrivateKey` objects from serialized keys like this: -You can also create key object from serialized keys: ```ts const privKey = bsv.PrivateKey.fromWIF('cVDFHtcTU1wn92AkvTyDbtVqyUJ1SFQTEEanAWJ288xvA7TEPDcZ') const privKey2 = bsv.PrivateKey.fromString('e3a9863f4c43576cdc316986ba0343826c1e0140b0156263ba6f464260456fe8') ``` -You can see the decimal value of the private key the following way: +See the decimal value of the private key the following way: + ```ts console.log(privKey.bn.toString()) ``` -> **Warning** -> Private keys should be carefully stored and never be publicly revealed. Otherwise it may lead to loss of funds. +:::warning +Private keys should be carefully stored and never be publicly revealed. Otherwise it may lead to loss of funds. +::: + ## Public Keys -A public key is a key that is derived from a private key and can be shared publicly. Mathematically, a public key is a point on the default elliptic curve that Bitcoin uses, named [`SECP256K1`](https://wiki.bitcoinsv.io/index.php/Secp256k1). It is the curve's base point multiplied by the value of the private key. +A public key is derived from a private key and can be shared publicly. Mathematically, a public key is a point on the default elliptic curve that Bitcoin uses, named [`SECP256K1`](https://wiki.bitcoinsv.io/index.php/Secp256k1). It is the curve's base point multiplied by the value of the private key. You can get the public key corresponding to a private key the following way: @@ -61,11 +66,10 @@ const privKey = bsv.PrivateKey.fromRandom(bsv.Networks.testnet) const pubKey = privKey.toPublicKey() ``` -Same as with private key you can serialize and deserialize public keys: +Similar to a private key, you can serialize and deserialize public keys: ```ts const pubKey = bsv.PublicKey.fromHex('03a687b08533e37d5a6ff5c8b54a9869d4def9bdc2a4bf8c3a5b3b34d8934ccd17') - console.log(pubKey.toHex()) // 03a687b08533e37d5a6ff5c8b54a9869d4def9bdc2a4bf8c3a5b3b34d8934ccd17 ``` @@ -76,15 +80,15 @@ You can get a Bitcoin address from either the private key or the public key: ```ts const privKey = bsv.PrivateKey.fromRandom(bsv.Networks.testnet) - const pubKey = privKey.toPublicKey() +const pubKey = privKey.toPublicKey() - console.log(privKey.toAddress().toString()) - // mxRjX2uxHHmS4rdSYcmCcp2G91eseb5PpF - console.log(pubKey.toAddress().toString()) - // mxRjX2uxHHmS4rdSYcmCcp2G91eseb5PpF +console.log(privKey.toAddress().toString()) +// mxRjX2uxHHmS4rdSYcmCcp2G91eseb5PpF +console.log(pubKey.toAddress().toString()) +// mxRjX2uxHHmS4rdSYcmCcp2G91eseb5PpF ``` -Read [this wiki page](https://wiki.bitcoinsv.io/index.php/Bitcoin_address) for more information on how Bitcoin addresses get constructed. +Read this [BitcoinSV wiki page](https://wiki.bitcoinsv.io/index.php/Bitcoin_address) for more information on how Bitcoin addresses are constructed. ## Hash Functions @@ -107,16 +111,17 @@ The hash functions available in the `bsv` submodule are: | ripemd160 | 20 bytes | The RIPEMD160 hash. | | sha256ripemd160 | 20 bytes | The RIPEMD160 hash of the SHA256 hash. Used in Bitcoin addresses. | -Note however, that these [bsv.js hash functions](https://github.com/moneybutton/bsv/blob/master/lib/hash.js) should not be confused with [sCrypt's native hash functions](https://scrypt.io/docs/reference/#hashing-functions). These functions cannot be used in a smart contract method. +Note however, that these [bsv.js hash functions](https://github.com/moneybutton/bsv/blob/master/lib/hash.js) should not be confused with [sCrypt's native hash functions](https://docs.scrypt.io/reference/#hashing-functions). These functions cannot be used in a smart contract method. ## Constructing Transactions -The `bsv` submodule offers a flexible system for constructing Bitcoin transactions. Users are able to define scripts, transaction inputs and outputs, and a whole transaction including its metadata. For a complete description of Bitcoins transaction format, please read [this wiki page](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions). +The `bsv` submodule offers a flexible system for constructing Bitcoin transactions. Users are able to define scripts, transaction inputs and outputs, and a whole transaction including its metadata. For a complete description of Bitcoin's transaction format, please read the [BitcoinSV wiki page](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions). As an exercise let's construct a simple [P2PKH](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#Pay_to_Public_Key_Hash_.28P2PKH.29) transaction from scratch and sign it. -> **Note:** -> As you will notice further in these docs, most of these steps won't be needed in a regular smart contract development workflow as sCrypt already does a lot of heavy lifting for you. This section serves more as a deeper look on what is happening under the hood. +:::note +As you will notice further in these docs, most of these steps won't be needed in a regular smart contract development workflow as sCrypt already does a lot of heavy lifting for you. This section serves more as a deeper look on what is happening under the hood. +::: You can create an empty transaction like this: ```ts @@ -149,7 +154,7 @@ tx.addOutput( ) ``` -Notice how the output value is 100 satoshis less than the value of the UTXO we're unlocking. This difference is the [transaction fee](https://wiki.bitcoinsv.io/index.php/Transaction_fees) (sometimes also called the miner fee). The transaction fees are picked up by miners when they mine a block, so adding a transaction fee basically acts as an incentive for miners to include your transaction in a block. +Notice how the output value is 100 satoshis less than the value of the UTXO we're unlocking. This difference is the [transaction fee](https://wiki.bitcoinsv.io/index.php/Transaction_fees) (sometimes also called the "miner fee"). The transaction fees are collected by miners when they mine a block, so adding a transaction fee basically acts as an incentive for miners to include your transaction in a block. The amount of transaction fee you should pay depends on the fee rate and the bytes of the transaction. By adding an additional output to the transaction, we can control how much the transaction fee is actually paid. This output is called the change output. By adjusting the amount of change output, we can pay as little transaction fees as possible while meeting the needs of miners. @@ -167,25 +172,26 @@ tx.feePerKb(50) ### Signing -OK, now that we have the transaction constructed, it's time to sign it. First, we need to seal the transaction, so it will be ready to sign. Then we call the `sign` function, which takes the private key that can unlock the UTXO we passed to the `from` function. In our example, this is the private key that corresponds to the address `n4fTXc2kaKXHyaxmuH5FTKiJ8Tr4fCPHFy`: +Now that we have the transaction constructed, it's time to sign it. First, we need to seal the transaction, so it will be ready to sign. Then we call the `sign` function, which takes the private key that can unlock the UTXO we passed to the `from` function. In our example, this is the private key that corresponds to the address `n4fTXc2kaKXHyaxmuH5FTKiJ8Tr4fCPHFy`: ```ts tx = tx.seal().sign('cNSb8V7pRt6r5HrPTETq2Li2EWYEjA7EcQ1E8V2aGdd6UzN9EuMw') ``` -Viola! That's it. This will add the necessary data to the transaction's input script. That being the signature along with the public key of our signing key. +Viola! That's it. This will add the necessary data to the transaction's input script: the signature and the public key of our signing key. Now our transaction is ready to be posted to the blockchain. -Now our transaction is ready to be posted to the blockchain. You can serialize the transaction the following way: +You can serialize the transaction like this: ```ts console.log(tx.serialize()) ``` -For broadcasting, you can use any provider you like. For demo purposes you can simply paste the serialized transaction [here](https://test.whatsonchain.com/broadcast). +To broadcast a transaction, you can use any provider you like. +For demo and test purposes you can paste the serialized transaction [here](https://test.whatsonchain.com/broadcast). ### OP_RETURN Scripts -In case you would like to put some arbitrary data on-chain, without any locking logic, you can use transaction outputs with an [OP_RETURN](https://wiki.bitcoinsv.io/index.php/OP_RETURN) script. +If you want to post some arbitrary data on-chain, without any locking logic, you can use transaction outputs with an [OP_RETURN](https://wiki.bitcoinsv.io/index.php/OP_RETURN) script. An example of an OP_RETURN script written in ASM format is this: @@ -193,7 +199,8 @@ An example of an OP_RETURN script written in ASM format is this: OP_FALSE OP_RETURN 734372797074 ``` -In effect, the opcodes `OP_FALSE OP_RETURN` will make the script unspendable. After them we can insert arbitrary chunks of data. The `734372797074` is actually the string `sCrypt` encoded as an `utf-8` hexadecimal string. +The opcodes `OP_FALSE OP_RETURN` will make the script unspendable. After them we can insert arbitrary chunks of data. +The `734372797074` is actually the string `sCrypt` encoded as an `utf-8` hexadecimal string. ```js console.log(Buffer.from('sCrypt').toString('hex')) @@ -211,7 +218,7 @@ The `bsv` submodule offers a convenient function to construct such scripts: const opRetScript: bsv.Script = bsv.Script.buildSafeDataOut(['Hello', 'from', 'sCrypt']) ``` -We can add the resulting `bsv.Script` object to an output as we showed [above](#constructing-transactions). +We can add the resulting `bsv.Script` object to an output as shown [above](#constructing-transactions). ## ECIES @@ -257,4 +264,4 @@ In this example, `recipientPrivateKey` is the private key of the recipient (the ## References - Take a look at the full [`bsv` submodule reference](../reference/modules/bsv) for a full list of what functions it provides. -- As the `bsv` submodule is based on MoneyButton's library implementation, take a look at their [video tutorial series](https://www.youtube.com/watch?v=bkGiCjYBpJE&list=PLwj1dNv7vWsMrjrWeiQEelbKTI3Lrmvqp&index=1). Although do keep in mind that some things might be slightly different as it's an old series. +- As the `bsv` submodule is based on MoneyButton's library implementation, take a look at their [video tutorial series](https://www.youtube.com/watch?v=bkGiCjYBpJE&list=PLwj1dNv7vWsMrjrWeiQEelbKTI3Lrmvqp&index=1). Please note that some things might be slightly different as the videos are now at least 5 years old. diff --git a/docs/ethereum-devs.md b/docs/ethereum-devs.md index dbaa83fef..eccdfe01c 100644 --- a/docs/ethereum-devs.md +++ b/docs/ethereum-devs.md @@ -6,7 +6,7 @@ sidebar_position: 12 # Smart contracts on Bitcoin vs Ethereum Bitcoin and Ethereum are both layer-1 blockchains with [fully programmable smart contracts](https://xiaohuiliu.medium.com/turing-machine-on-bitcoin-7f0ebe0d52b1). -However, their designs fundamentally differ. +However, their designs are fundamentally different. Ethereum is a global state machine, whose state consists of all smart contracts deployed on it. Each transaction is an input to the state machine, transitioning it to the next state according to the rules defined in the smart contract the transaction calls. The design imposes severe limitations on scalability, since transactions must be sequentially processed due to potential race conditions. @@ -28,13 +28,13 @@ Detailed side-by-side comparison can be found [here](ttps://xiaohuiliu.medium.co # Smart contract development on Bitcoin vs Ethereum -Besides unboundedly scalable fundation, Bitcoin also offers superior smart cotnract developer experience. +In addition to an unboundedly scalable foundation, Bitcoin also offers a superior smart cotnract developer experience. The table below shows a comparison of popular Ethereum development tools and their counterparts in the Bitcoin ecosystem. There are two noticeable differences. -1. Bitcoin smart contract is written in TypeScript, one of the most popular programming languages tens of millions of Web2 developers are already familiar with. They do not have to learn a new niche programming language like Solidity, placing a high barrier to entry. They can reuse all of their favoriate tools, such as Visual Studio Code, [WebStorm](https://www.jetbrains.com/webstorm/), and NPM. -2. Ethereum's development tools are **fragmented**. They are developed by different entities, who are often competitors. There is disincentive to make them more interoperable, thus they don't communicate with each other well. By contrast, sCrypt takes a more holistic and systematic approach. It builds a unified full-stack platform that encompasses most tools, from programming language, to framework/libraries. Developed synergistically, they are fully compatible with each other, greatly simplifing and streamlining development process. +1. Bitcoin smart contract is written in TypeScript, one of the most popular programming languages tens of millions of Web2 developers are already familiar with. They do not have to learn a new niche programming language like Solidity, placing a high barrier to entry. They can reuse their favorite tools, such as [Visual Studio Code](https://code.visualstudio.com/), [WebStorm](https://www.jetbrains.com/webstorm/), and NPM. +1. Ethereum's development tools are **fragmented**. They are developed by different entities, who are often competitors. There are disincentives to make them more interoperable, thus they don't communicate with each other well. By contrast, sCrypt takes a more holistic and systematic approach. It builds a unified full-stack platform that encompasses most tools, from programming language, to framework/libraries. Developed synergistically, they are fully compatible with each other, greatly simplifying and streamlining the development process. || Ethereum | Bitcoin | @@ -47,11 +47,11 @@ There are two noticeable differences. | Wallet | [MetaMask](https://metamask.io/) | [Panda](https://github.com/Panda-Wallet/panda-wallet) | | Block Explorer | [Etherscan](https://etherscan.io/) | [WhatsOnChain](https://whatsonchain.com/) | -[^1]: Visual Studio Code can also be used for Solidity with various extentions. However, its support is extremely limited compared to that of sCrypt, a TypeScript DSL, which is supported out of box without any extension. For example, [VS Code debugger](./how-to-debug-a-contract.md) has first-class comprehensive support for sCrypt, but does not suppport Solidity. +[^1]: Visual Studio Code can also be used for Solidity with various extensions. However, its support is extremely limited compared to that of sCrypt, a TypeScript DSL, which is supported out of box without any extension. For example, [VS Code debugger](./how-to-debug-a-contract.md) has first-class comprehensive support for sCrypt, but does not suppport Solidity. ## Example Code -Let's compare a counter smart contract between Solidity and sCrypt. +Let's compare a `Counter` smart contract between Solidity and sCrypt. ```js pragma solidity >=0.7.0 <0.9.0; diff --git a/docs/how-to-deploy-and-call-a-contract/call-deployed.md b/docs/how-to-deploy-and-call-a-contract/call-deployed.md index 63017e3b8..4df4360ac 100644 --- a/docs/how-to-deploy-and-call-a-contract/call-deployed.md +++ b/docs/how-to-deploy-and-call-a-contract/call-deployed.md @@ -6,39 +6,43 @@ sidebar_position: 5 ## Overview In this tutorial, we will interact with a deployed smart contract by calling its public method, in a separate process or by a different party. -We need to create an instance corresponding to the deployed contract on chain. + +To do this, we need to create a smart contract instance that corresponds to the deployed contract on chain. ## The Smart Contract -We will reuse [the `Counter` contract](../how-to-write-a-contract/stateful-contract.md#create-a-stateful-contract). +We will reuse the stateful `Counter` contract [from a previous step](../how-to-write-a-contract/stateful-contract#create-a-stateful-contract). ```ts export class Counter extends SmartContract { - - @prop(true) - count: bigint - - constructor(count: bigint) { - super(...arguments) - this.count = count - } - - @method() - public incrementOnChain() { - // Increment counter. - this.increment() - - // Ensure next output will contain this contracts code with - // the updated count property. - const amount: bigint = this.ctx.utxo.value - const outputs: ByteString = this.buildStateOutput(amount) + this.buildChangeOutput() - assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch') - } - - @method() - increment(): void { - this.count++ - } + // stateful + @prop(true) + count: bigint + + constructor(count: bigint) { + super(...arguments) + this.count = count + } + + @method() + public incrementOnChain() { + // Increment counter. + this.increment() + + // Ensure next output will contain this contracts code with + // the updated count property. + // And make sure balance in the contract does not change + const amount: bigint = this.ctx.utxo.value + // outputs containing the latest state and an optional change output + const outputs: ByteString = this.buildStateOutput(amount) + this.buildChangeOutput() + // verify unlocking tx has the same outputs + assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch') + } + + @method() + increment(): void { + this.count++ + } } ``` diff --git a/docs/how-to-deploy-and-call-a-contract/deploy-cli.md b/docs/how-to-deploy-and-call-a-contract/deploy-cli.md index fe89f1112..84545c8bf 100644 --- a/docs/how-to-deploy-and-call-a-contract/deploy-cli.md +++ b/docs/how-to-deploy-and-call-a-contract/deploy-cli.md @@ -39,7 +39,7 @@ dotenv.config() // See https://scrypt.io/docs/bitcoin-basics/bsv/#private-keys const privateKey = bsv.PrivateKey.fromWIF(process.env.PRIVATE_KEY) -// Prepare signer. +// Prepare signer. // See https://scrypt.io/docs/how-to-deploy-and-call-a-contract/#prepare-a-signer-and-provider const signer = new TestWallet(privateKey, new DefaultProvider()) @@ -69,9 +69,9 @@ main() Upon a successful execution you should see an output like the following: ``` -Demoproject contract deployed: 15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd +Demoproject contract deployed: 15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd ``` -You can take a look at the deployed smart contract using the [WhatsOnChain block explorer](https://test.whatsonchain.com/tx/15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd). -In our example, the first output contains the compiled smart contract code. -It is indexed using the hash (double SHA-256) of the script: [eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def](https://test.whatsonchain.com/script/eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def) +You can take a look at the deployed smart contract using the [WhatsOnChain block explorer](https://test.whatsonchain.com/tx/15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd). +In our example, the first output contains the compiled smart contract code. +It is indexed using the hash (double SHA-256) of the script: [eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def](https://test.whatsonchain.com/script/eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def). diff --git a/docs/how-to-deploy-and-call-a-contract/faucet.md b/docs/how-to-deploy-and-call-a-contract/faucet.md index b9de690d9..0285f4ee1 100644 --- a/docs/how-to-deploy-and-call-a-contract/faucet.md +++ b/docs/how-to-deploy-and-call-a-contract/faucet.md @@ -6,7 +6,7 @@ sidebar_position: 4 It is highly recommended to test your contract on the [testnet](https://test.whatsonchain.com/) after passing local tests. It ensures that a contract can be successfully deployed and invoked as expected on the blockchain. -Before deploy and call a contract, you need to have a funded address: +Before you deploy and call a contract, you need to have a funded address: 1. Create a new project. Skip this step if you have already created a project: @@ -23,14 +23,18 @@ npm install npm run genprivkey ``` -The command will generate a private key and store it in a `.env` file in our project's root directory. It also outputs the [Bitcoin address](https://wiki.bitcoinsv.io/index.php/Bitcoin_address) corresponding to our private key. +The command generates a private key and stores it in a `.env` file in the project's root directory. +It also outputs the [Bitcoin address](https://wiki.bitcoinsv.io/index.php/Bitcoin_address) corresponding to the private key. -3. Fund the private key's address with some testnet coins. You could use this [faucet](https://scrypt.io/faucet) to receive test coins. +3. Fund the private key's address with some testnet coins. You can use this [faucet](https://scrypt.io/faucet) to receive testnet coins. ![faucet](../../static/img/faucet.gif) -### Use the Panda Wallet -If you have [Panda wallet](https://chromewebstore.google.com/detail/panda-wallet/mlbnicldlpdimbjdcncnklfempedeipj) installed, you can easily use the testnet private key as follows : +### Use the Yours Wallet + +In March 2024, Panda Wallet was rebranded to [Yours Wallet](https://github.com/yours-org/yours-wallet/). + +If you have [Yours Wallet](https://chromewebstore.google.com/detail/panda-wallet/mlbnicldlpdimbjdcncnklfempedeipj) installed, you can easily use the testnet private key as follows : ![](../../static/img/panda.gif) ### Use the Sensilet Wallet diff --git a/docs/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md b/docs/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md index 38c1602a1..d0f162b9b 100644 --- a/docs/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md +++ b/docs/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md @@ -31,9 +31,9 @@ class DemoContract extends SmartContract { // ... // customize the deployment tx by overriding `SmartContract.buildDeployTransaction` method - override async buildDeployTransaction(utxos: UTXO[], amount: number, + override async buildDeployTransaction(utxos: UTXO[], amount: number, changeAddress?: bsv.Address | string): Promise { - + const deployTx = new bsv.Transaction() // add p2pkh inputs for paying tx fees .from(utxos) @@ -57,7 +57,7 @@ class DemoContract extends SmartContract { } ``` -You may visit the [full code](https://github.com/sCrypt-Inc/boilerplate/blob/f63c37038a03bc51267e816d9441969d3e1d2ece/src/contracts/auction.ts#L100-L127) for more details. +See the [full code](https://github.com/sCrypt-Inc/boilerplate/blob/f63c37038a03bc51267e816d9441969d3e1d2ece/src/contracts/auction.ts#L100-L127) for more details. ## Call Tx @@ -126,7 +126,7 @@ static bidTxBuilder( satoshis: current.balance, }) ) - + // build change output if (options.changeAddress) { // build change output @@ -153,7 +153,7 @@ The tx builder will return an object: - `tx`: the unsigned transaction of our method call. - `atInputIndex`: the index of the input which will reference the smart contract UTXO. -- `nexts`: an array of objects that represent the contract's next instance(s). +- `nexts`: an array of objects that represent the contract's next instance(s). When we are calling a [stateful smart contract](../how-to-write-a-contract/stateful-contract.md), we have to define the next instance of our contract. This instance will contain updated states. As we can see, first a new instance is created using the current instance's `next()` function. The new instance's `bidder` property is then updated. This new instance is then included in the 0-th output of the new transaction and goes into the `nexts` array of the returned object. diff --git a/docs/how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md b/docs/how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md index cf4707707..7e1748715 100644 --- a/docs/how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md +++ b/docs/how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md @@ -13,13 +13,13 @@ After you've finished writing a contract, you can deploy and call it. But first, ### Compile and Load Contract -First, you compile the contract using CLI: +First, compile the contract using the CLI: ```ts -npx scrypt-cli@latest compile +npx scrypt-cli compile ``` -This will create an artifact json file of your contract in the `artifacts` folder. +This will create an artifact json file of your contract in the `/artifacts` folder. -Next you call [loadArtifact](../how-to-write-a-contract/built-ins.md#loadartifact) load the json file, so you have a smart contract class ready to be instantiated. +Next, call [loadArtifact](../how-to-write-a-contract/built-ins.md#loadartifact) to load the json file, so you have a smart contract class ready to be instantiated. ```ts import artifact from '../artifacts/mycontract.json' @@ -44,7 +44,7 @@ sCrypt already has a few built-in providers: * `DefaultProvider`: The default provider is the safest, easiest way to begin developing on Bitcoin, and it is also robust enough for use in production. It can be used in testnet as well as mainnet. -* See full list of providers [here](../reference/classes/Provider.md#hierarchy). +* See a list of [available providers](../reference/classes/Provider.md#hierarchy). You can initialize these providers like this: @@ -52,11 +52,9 @@ You can initialize these providers like this: let dummyProvider = new DummyProvider(); // mainnet - let provider = new DefaultProvider(); // testnet - let provider = new DefaultProvider(bsv.Networks.testnet); ``` @@ -105,12 +103,10 @@ instance.to.tx instance.to.inputIndex ``` - This section could be summarized as the diagram below: ![](../../static/img/contract_tx.svg) - ## Prepare a Signer and Provider A signer and a provider must be connected to a contract instance before deployment and call. When we are ready to deploy the contract to the testnet/mainnet, we need a real provider like [DefaultProvider](#provider). @@ -122,23 +118,20 @@ const signer = new TestWallet(privateKey, new DefaultProvider(network)); The `privateKey` must have enough coins. Learn how to fund it on a testnet using a [faucet](./faucet). -Then just connect it to your contract instance like this: +Then, connect it to your contract instance like this: ```ts await instance.connect(signer); ``` :::note -`TestWallet` is just a `Signer` provided by sCrypt for testing. In a real production environment (Mainnet), you should use `PandaSigner`, `SensiletSigner`, `DotwalletSigner`, `TAALSigner`. +`TestWallet` is just a `Signer` provided by sCrypt for testing. In a real production environment (mainnet), you should use `PandaSigner`, `SensiletSigner`, `DotwalletSigner`, `TAALSigner`. See [here](../how-to-integrate-a-frontend/how-to-integrate-a-frontend.md) how to use them. ::: - - ## Contract Deployment -To deploy a smart contract, simply call its `deploy` method: - +To deploy a smart contract, call its `deploy` method, like this: ```ts // construct a new instance of `MyContract` @@ -175,7 +168,7 @@ let instance = new MyContract(); console.log(typeof instance.methods.foo) // output `function` ``` -This function is designed to invoke the corresponding `@method` of the same name on chain, meaning calling it will spend the previous contract UTXO in a new transaction. You can call it like this: +This function is designed to invoke the corresponding `@method` of the same name on chain, meaning: calling it will spend the previous contract UTXO in a new transaction. You can call it like this: ```ts // Note: `instance.methods.foo` should be passed in all arguments and in the same order that `instance.foo` would take. @@ -185,14 +178,11 @@ This function is designed to invoke the corresponding `@method` of the same name const { tx, atInputIndex } = await instance.methods.foo(arg1, arg2, options); ``` - -What actually happens during the call is the following. +What actually happens during the call is the following: 1. Build an unsigned transaction by calling the tx builder, which can be a default or a customized one introduced in [this section](./how-to-customize-a-contract-tx.md), for a public `@method`. - -2. Use the instance's signer to sign the transaction. Note that `instance.foo` could be invoked during this process in order to get a valid unlocking script for the input. - -3. Use the instance's connected provider to send the transaction. +1. Use the instance's signer to sign the transaction. Note that `instance.foo` could be invoked during this process in order to get a valid unlocking script for the input. +1. Use the instance's connected `provider` to send the transaction. #### MethodCallOptions @@ -229,37 +219,40 @@ export interface MethodCallOptions { ``` The major differences between here and [local tests](../how-to-test-a-contract.md#run-tests) are: -1. the contract needs to be deployed first; -2. the contract instance is connected to a real provider, which broadcasts transactions to the blockchain. + +1. the contract needs to be deployed first +1. the contract instance is connected to a real provider, which broadcasts transactions to the blockchain. #### next The `next` property within the `MethodCallOptions` interface in sCrypt is used to specify the subsequent contract instance(s) produced in the outputs of the method calling transaction in a stateful contract. This property allows for the chaining of stateful contract calls within a single transaction. -The transaction builder uses the passed instance(s) to construct outputs of the contract call transaction. +The transaction builder uses the passed instance(s) to construct outputs of the contract call transaction. -When writing a [custom call transaction builder](https://github.com/sCrypt-Inc/scryptTS-docs/pull/how-to-customize-a-contract-tx.md#call-tx) we can access the instance like the following : +When writing a [custom transaction builder](https://github.com/sCrypt-Inc/scryptTS-docs/pull/how-to-customize-a-contract-tx.md#call-tx) we can access the instance like: ```ts - static unlockTxBuilder( - current: Demo, - options: MethodCallOptions, - ... - ): Promise { - const next = options.next as StatefulNext - - ... +static unlockTxBuilder( + current: Demo, + options: MethodCallOptions, + ... + ): Promise { + const next = options.next as StatefulNext + + ... } ``` - ### Create a smart contract instance from a transaction + To interact with a deployed smart contract (i.e., calling its public methods), we need its contract instance corresponding to its latest state on chain, stateful or not. When testing on testnet, we usually put a contract's deployment and its calling (note there could be multiple calls if the contract is stateful) in the same process for convenience, so that we don't need to manage the internal state of the instance manually, because it's always consistent with the transactions on chain. -In reality, a contract's deployment and its call, and its different calls in the case of a stateful contract, may well be in separate processes. For example, the deployment party is different from the calling party, or multiple parties call it. If so, we need to create a contract instance from an on-chain transaction that represents its latest state, before we can call its method. +In a production context, a contract's deployment and its call, and its different calls in the case of a stateful contract, may well be in separate processes. For example, the deployment party is different from the calling party, or multiple parties call it. If so, we need to create a contract instance from an on-chain transaction that represents its latest state, before we can call its method. Typically, we only know the [TXID](https://wiki.bitcoinsv.io/index.php/TXID) of the transaction containing the instance. We can create an instance in two steps: + 1. Using TXID, we retrieve the full transaction by calling [getTransaction](../reference/classes/Provider.md#gettransaction) of the [connected provider](../reference/classes/Signer.md#connectedprovider) of the signer. -2. We can create an contract instance from a transaction's by calling [fromTx()](../how-to-write-a-contract/built-ins.md#fromtx). +1. We can create an contract instance from a transaction's by calling [fromTx()](../how-to-write-a-contract/built-ins.md#fromtx). + ```ts // 1) fetch a transaction from txid const tx = await signer.connectedProvider.getTransaction(txId) @@ -316,19 +309,16 @@ const { tx: callTx } = await p2pkh.methods.unlock( ); console.log('contract called: ', callTx.id); - ``` -When `p2phk.method.unlock` is called, the option contains `pubKeyOrAddrToSign`, requesting a signature against `publicKey`. +When `p2pkh.method.unlock` is called, the option contains `pubKeyOrAddrToSign`, requesting a signature against `publicKey`. The first argument is a signature, which can be obtained in a callback function. The function takes a list of signatures requested in `pubKeyOrAddrToSign` and find the one signature to the right public key/address. In general, if your `@method` needs `Sig`-typed arguments, you could obtain them as follows: 1. Ensure that the `pubKeyOrAddrToSign` contains all public keys/addresses corresponding to these `Sig`s; - -2. Replace each `Sig` argument with a callback function that filters to the right `Sig` from the full list of signature in `sigResps`. - +1. Replace each `Sig` argument with a callback function that filters to the right `Sig` from the full list of signature in `sigResps`. ## Example @@ -366,12 +356,10 @@ const { tx: callTx } = await p2pkh.methods.unlock( ); console.log('contract called: ', callTx.id); - ``` More examples can be found [here](https://github.com/sCrypt-Inc/boilerplate/tree/master/tests/). - ### Running the code The deployment and call code is wrapped into a simple NPM command: @@ -381,7 +369,7 @@ npm run testnet ``` Make sure you fund your address before running this command. -After a successful run you should see something like the following: +After a successful run you should see something like: ``` P2PKH contract deployed: f3f372aa25f159efa93db8c51a4eabbb15935358417ffbe91bfb78f4f0b1d2a3 @@ -390,6 +378,6 @@ P2PKH contract called: dc53da3e80aadcdefdedbeb6367bb8552e381e92b226ab1dc3dc9b33 These are the TXIDs of the transaction which deployed the smart contract and then the one which called its method. You can see the transactions using a [block explorer](https://test.whatsonchain.com/tx/f3f372aa25f159efa93db8c51a4eabbb15935358417ffbe91bfb78f4f0b1d2a3). - ### Customize Transactions -Deploying and calling a contract builds transactions with a certain format, which suffices for many cases. In cases where the tx format does not work for you and you need to customize it, please refer to [this section](./how-to-customize-a-contract-tx.md). + +Deploying and calling a contract builds transactions with a certain format, which suffices for many cases. In cases where the tx format does not work for you and you need to customize it, please proceed to the [next section](./how-to-customize-a-contract-tx.md). diff --git a/docs/how-to-integrate-a-frontend/how-to-integrate-a-frontend.md b/docs/how-to-integrate-a-frontend/how-to-integrate-a-frontend.md index 6270f7aa8..4dff3dfad 100644 --- a/docs/how-to-integrate-a-frontend/how-to-integrate-a-frontend.md +++ b/docs/how-to-integrate-a-frontend/how-to-integrate-a-frontend.md @@ -92,7 +92,7 @@ npm create svelte@latest helloworld ![](../../static/img/create-svelte-app.png) :::note -Currently, we support frontend frameworks [React](https://react.dev), [Next.js](https://nextjs.org/), [Vue](https://vuejs.org/), [Angular](https://angular.io/), and [Svelte](https://svelte.dev/). We anticipate to add supports for other frameworks over time. +Currently, we support frontend frameworks [React](https://react.dev), [Next.js](https://nextjs.org/), [Vue](https://vuejs.org/), [Angular](https://angular.io/), and [Svelte](https://svelte.dev/). We anticipate to add support for other frameworks over time. ::: ### Step 2 @@ -104,7 +104,7 @@ cd helloworld npx scrypt-cli init ``` -This installs all the dependencies and configures the contract development environment. +This command installs the dependencies and configures the contract development environment. After this, we are ready to go! ## Load Contract @@ -162,7 +162,7 @@ You will integrate [Panda](https://chromewebstore.google.com/detail/panda-wallet You can refer to this [guide](../advanced/how-to-add-a-signer.md) to add support for other wallets. ::: -To request access to the wallet, you can use its `requestAuth` method. +To request access to the wallet, you can use its `requestAuth` method. ```ts const provider = new DefaultProvider({ diff --git a/docs/how-to-verify-a-contract.md b/docs/how-to-verify-a-contract.md index ab54897d5..ece2fd86d 100644 --- a/docs/how-to-verify-a-contract.md +++ b/docs/how-to-verify-a-contract.md @@ -8,14 +8,15 @@ You will learn how to verify smart contracts on [WhatsOnChain](https://whatsonch By verifying your smart contract on WoC, anyone can view its source code and interact with it confidently. Let's get started! -To start with the verification process, we need to first deploy a smart contract. Let us use the ["Hello World" tutorial](./tutorials/hello-world.md) as an example. After you complete the tutorial, you should get the ID of the deployment transaction such as [`a34d4e45a9108b5b9da4faf4f086e9ef36b79466383bd7a22ff2c7f6a562546c`](https://test.whatsonchain.com/tx/a34d4e45a9108b5b9da4faf4f086e9ef36b79466383bd7a22ff2c7f6a562546c). +To start with the verification process, we need to first deploy a smart contract. Let us use the ["Hello World" tutorial](./tutorials/hello-world.md) as an example. +After you complete the tutorial, you should get the TXID of the deployment transaction such as [`a34d4e45a9108b5b9da4faf4f086e9ef36b79466383bd7a22ff2c7f6a562546c`](https://test.whatsonchain.com/tx/a34d4e45a9108b5b9da4faf4f086e9ef36b79466383bd7a22ff2c7f6a562546c). -If you take a look at the transaction on WoC, you'll see that the first output contains a script identified by the hash `eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def`, which contains your contract in script format. +If you view the transaction on WoC, you'll see that the first output contains a script identified by the hash `eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def`, which contains your contract, in script format. ![](../static/img/verify-tx-out.png) -This hash is referred to as the `scriptHash`. It's essentially just a `sha256` hash value of the deployed contracts locking script, encoded in a little-endian hex format. It is commonly used as an index by block explorers. You can also get this value locally, via the contract instance's `scriptHash` property: +This hash is referred to as the `scriptHash`. It's essentially just a `sha256` hash value of the deployed contract's locking script, encoded in a little-endian hex format. It is commonly used as an index by block explorers. You can also get this value locally, via the contract instance's `scriptHash` property: ```ts console.log(instance.scriptHash) @@ -23,7 +24,7 @@ console.log(instance.scriptHash) ``` :::note -The scriptHash value can vary due to factors like the current property values and the number of times the contract has been updated, leading to inconsistencies in its value. +The scriptHash value can vary due to factors like the current property values and the number of times the contract has been updated, leading to inconsistencies in its value. ::: You can submit and verify sCrypt source code that belongs to a specific script hash. @@ -60,7 +61,7 @@ Now, every time someone opens the `sCrypt` tab on [the script hash page](https:/ ## 2. Using CLI -The same process can be done using the [sCrypt CLI](https://www.npmjs.com/package/scrypt-cli). +The same verification process can be done using the [sCrypt CLI](https://www.npmjs.com/package/scrypt-cli). You can verify the deployed smart contracts script using the `verify` command: ```sh @@ -72,7 +73,7 @@ The first positional argument is the script hash of the deployed contract and th Using the `network` option, you can specify on which network the contract is deployed. This defaults to `test`, indicating the Bitcoin testnet: ```sh -npx scrypt-cil verify --network main +npx scrypt-cli verify --network main ``` You can also specify the version of sCrypt used during verification. By default, the command will use the version specified in `package.json`: @@ -88,4 +89,3 @@ npx scrypt-cli verify eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a343 ``` Upon execution, the designated contract code undergoes verification on sCrypt's servers. If successful, the outcome will be [displayed on WoC](https://test.whatsonchain.com/script/eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def), under the "sCrypt" tab, just like above. - diff --git a/docs/how-to-write-a-contract/built-ins.md b/docs/how-to-write-a-contract/built-ins.md index d268ce7a1..54fc300f1 100644 --- a/docs/how-to-write-a-contract/built-ins.md +++ b/docs/how-to-write-a-contract/built-ins.md @@ -2,11 +2,11 @@ sidebar_position: 4 --- -# Built-ins +# Built-in functions ## Global Functions -The following functions come with `sCrypt`. +The following functions come with sCrypt: ### Assert @@ -20,7 +20,7 @@ assert(false, 'hello') // throws Error('Execution failed, hello') ### Fill -- `fill(value: T, length: number): T[length] ` Returns an `FixedArray` with all `size` elements set to `value`, where `value` can be any type. +- `fill(value: T, length: number): T[length] ` Returns an `FixedArray` with all `size` elements set to `value`, where `value` can be any type. :::note `length` must be a [compiled-time constant](./how-to-write-a-contract.md#compile-time-constant). @@ -105,7 +105,7 @@ byteString2Int(toByteString('010000')) // 1n byteString2Int(toByteString('810080')) // -129n ``` -- `len(a: ByteString): number` Returns the byte length of `a`. +- `len(a: ByteString): number` Returns the byte length of `a`. ```ts const s1 = toByteString('0011', false) // '0011', 2 bytes @@ -122,7 +122,7 @@ len(s2) // 5 ::: ```ts -const s1 = toByteString('793ff39de7e1dce2d853e24256099d25fa1b1598ee24069f24511d7a2deafe6c') +const s1 = toByteString('793ff39de7e1dce2d853e24256099d25fa1b1598ee24069f24511d7a2deafe6c') reverseByteString(s1, 32) // 6cfeea2d7a1d51249f0624ee98151bfa259d095642e253d8e2dce1e79df33f79 ``` @@ -220,7 +220,7 @@ TicTacToe.loadArtifact(artifact); ### `checkSig` -Function `checkSig(signature: Sig, publicKey: PubKey): boolean` verifies an ECDSA signature. It takes two inputs: an ECDSA signature and a public key. +Function `checkSig(signature: Sig, publicKey: PubKey): boolean` verifies an ECDSA signature. It takes two inputs: an ECDSA signature and a public key. It returns if the signature matches the public key. @@ -270,7 +270,7 @@ class MultiSigPayment extends SmartContract { @method() public unlock( - signatures: FixedArray, + signatures: FixedArray, publicKeys: FixedArray ) { // Check if the passed public keys belong to the specified addresses. @@ -294,8 +294,8 @@ class Counter extends SmartContract { @method(SigHash.ANYONECANPAY_SINGLE) public incOnChain() { // ... update state - - // construct the new state output + + // construct the new state output const output: ByteString = this.buildStateOutput(this.ctx.utxo.value) // ... verify outputs of current tx @@ -314,7 +314,7 @@ class Auction extends SmartContract { @method() public bid(bidder: Addr, bid: bigint) { - + // Addr // Auction continues with a higher bidder. @@ -370,7 +370,7 @@ If we assert the returned value to be `true`, we have effectively ensured that t ```ts class TimeLock extends SmartContract { - + @prop() locktime: bigint @@ -378,14 +378,14 @@ class TimeLock extends SmartContract { @method() public unlock() { - assert(this.timeLock(this.locktime), 'time lock not yet expired') + assert(this.timeLock(this.locktime), 'time lock not yet expired') } - + } ``` :::note -This mechanism can be employed solely to ensure that a method can be called **after** a specific point in time. In contrast, it cannot be employed to ensure that a method is called **before** a specific point in time. +This mechanism can be employed solely to ensure that a method can be called **after** a specific point in time. In contrast, it cannot be employed to ensure that a method is called **before** a specific point in time. ::: To learn more about time locks, see the [dedicated doc section](../advanced/timeLock.md). @@ -457,7 +457,7 @@ Function `async buildDeployTransaction(utxos: UTXO[], amount: number, changeAddr override async buildDeployTransaction(utxos: UTXO[], amount: number, changeAddress?: bsv.Address | string): Promise { const deployTx = new bsv.Transaction() // add p2pkh inputs for paying tx fees - .from(utxos) + .from(utxos) // add contract output .addOutput(new bsv.Transaction.Output({ script: this.lockingScript, @@ -483,7 +483,7 @@ Function `bindTxBuilder(methodName: string, txBuilder: MethodCallTxBuilder { @@ -608,7 +608,7 @@ These guidelines must be followed when using `HashedMap` in a contract `@method` - `clear(): void`: Remove all key and value pairs. - `size: number`: Returns the number of elements. -:::note +:::note `get()` is not listed, since the value itself is not stored and thus must be passed in and verified using `canGet()`. ::: diff --git a/docs/how-to-write-a-contract/how-to-write-a-contract.md b/docs/how-to-write-a-contract/how-to-write-a-contract.md index d74888dfd..b0d416b12 100644 --- a/docs/how-to-write-a-contract/how-to-write-a-contract.md +++ b/docs/how-to-write-a-contract/how-to-write-a-contract.md @@ -37,7 +37,7 @@ The smart contract above requires solving for two equations with unknown variabl Class members decorated with `@prop` and `@method` will end up on the blockchain and thus must be a strict subset of TypeScript. Everywhere decorated with them can be regarded in the on-chain context. Members decorated with neither are regular TypeScript and are kept off chain. The significant benefit of `sCrypt` is that both on-chain and off-chain code are written in the same language: TypeScript. :::note -You can use [the sCrypt template Repl](https://replit.com/@msinkec/sCrypt) and play with the code in your browser! +You can use the [sCrypt template on Repl.it](https://replit.com/@msinkec/sCrypt) and play with the code in your browser! ::: ## Properties @@ -84,10 +84,10 @@ static c: bigint @prop(true) static c: bigint = 1n -// good, `UINT_MAX` is a compile-time constant (CTC), and no need to typed explicitly +// good, `UINT_MAX` is a compile-time constant (CTC), and doesn't need to be typed explicitly static readonly UINT_MAX = 0xffffffffn -// valid, but not good enough, `@prop()` is not necessary for the CTC +// valid, but not good enough, `@prop()` is not necessary for a CTC @prop() static readonly UINT_MAX = 0xffffffffn @@ -98,7 +98,7 @@ static readonly UINT_MAX = 0xffffffffn ## Constructor -A smart contract must have an explicit constructor if it has at least one `@prop` that is not `static`. +A smart contract must have an explicit `constructor()` if it has at least one `@prop` that is not `static`. The `super` method **must** be called in the constructor and all the arguments of the constructor should be passed to `super` in the same order as they are passed into the constructor. For example, @@ -114,16 +114,16 @@ class A extends SmartContract { readonly p2: boolean constructor(p0: bigint, p1: bigint, p2: boolean) { - super(...arguments) // same as super(p0, p1, p2) + super(...arguments) // same as super(p0, p1, p2) this.p0 = p0 this.p1 = p1 this.p2 = p2 } } + ``` [`arguments`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments) is an array containing the values of the arguments passed to that function. `...` is the [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax). - ## Methods Like properties, a smart contract can also have two kinds of methods: @@ -252,7 +252,7 @@ static add(a: bigint, b: bigint): bigint { ``` :::note -**Recursion is disallowed**. A `@method`, public and not, cannot call itself, either directly in its own body or indirectly calls another method that transitively calls itself. +**Recursion is disallowed**. A `@method`, whether public or not, cannot call itself either directly in its own body, nor indirectly call another method that transitively calls itself. ::: A more detailed example is shown below. @@ -424,7 +424,7 @@ let abb: FixedArray, 3> = [[1n, 3n], [1n, 3n], [1n, 3n]] ``` :::caution -A `FixedArray` behaves differently in an on-chain and off-chain context, when passed as a function argument. It is *passed by reference* off chain, as a regular TypeScript/JavaScript array, while *passed by value* on chain. It is thus strongly recommended to NEVER mutate a `FixedArray` parameter's element inside a function. +A `FixedArray` behaves differently in an on-chain and off-chain context, when passed as a function argument. It is *passed by reference* off chain, as a regular TypeScript/JavaScript array, while *passed by value* on chain. Thus, it is strongly recommended to NEVER mutate a `FixedArray` parameter's element inside a function. ```ts class DemoContract extends SmartContract { @@ -664,7 +664,8 @@ const byte: ByteString = toByteString("ff") ### `for` -Bitcoin does not allow unbounded loops for security reasons, to prevent DoS attacks. All loops must be bounded at compile time. So if you want to loop inside `@method`, you must strictly use the following format: +Bitcoin does not allow unbounded loops for security reasons (eg: to prevent DoS attacks). +All loops must be bounded at compile time. So if you want to loop inside `@method`, you must strictly use the following format: ```ts for (let $i = 0; $i < $maxLoopCount; $i++) { diff --git a/docs/how-to-write-a-contract/scriptcontext.md b/docs/how-to-write-a-contract/scriptcontext.md index 2fa056ece..3ea07d002 100644 --- a/docs/how-to-write-a-contract/scriptcontext.md +++ b/docs/how-to-write-a-contract/scriptcontext.md @@ -140,13 +140,24 @@ But we can access the full prevouts via `this.prevouts`. [SigHash type](https://wiki.bitcoinsv.io/index.php/SIGHASH_flags) decides which part of the spending transaction is included in `ScriptContext`. ![](../../static/img/sighashtypes.png) -It defaults to `SigHash.ALL`, including all inputs and outputs. You can customize it by setting the argument of the `@method()` decorator, e.g., +It defaults to `SigHash.ALL`, including all inputs and outputs. You can customize it by setting the argument of the `@method()` decorator, like: ```ts +@method(SigHash.ANYONECANPAY_ALL) +public increment() { + ... +} + +@method(SigHash.ANYONECANPAY_NONE) +public increment() { + ... +} + @method(SigHash.ANYONECANPAY_SINGLE) public increment() { ... } + ``` There are a total of 6 sigHash types to choose from: @@ -160,7 +171,7 @@ There are a total of 6 sigHash types to choose from: | ANYONECANPAY_NONE | Sign its own input and no output | | ANYONECANPAY_SINGLE | Sign its own input and the output with the same index | -For more information, please refer to [this detailed guide](../advanced/sighash-type.md). +For more information, refer to the section on [Sighash Types](../advanced/sighash-type.md). ### Serialization diff --git a/docs/how-to-write-a-contract/stateful-contract.md b/docs/how-to-write-a-contract/stateful-contract.md index 83b79ed60..134d1b8db 100644 --- a/docs/how-to-write-a-contract/stateful-contract.md +++ b/docs/how-to-write-a-contract/stateful-contract.md @@ -5,14 +5,19 @@ sidebar_position: 3 # Stateful Contracts ## Overview -In Bitcoin's UTXO model, a smart contract is one-off and **stateless** by default, since the UTXO containing it is destroyed after being spent. Being stateless allows it to scale easily, the same as in [HTTP](https://stackoverflow.com/questions/5836881/stateless-protocol-and-stateful-protocol) and [REST APIs](https://www.geeksforgeeks.org/restful-statelessness/). -A smart contract can simulate state by requiring +In Bitcoin's UTXO model, a smart contract is one-off and **stateless** by default, since the UTXO containing the contract is destroyed after being spent. Being stateless allows it to scale, similar to [HTTP](https://stackoverflow.com/questions/5836881/stateless-protocol-and-stateful-protocol) and [REST APIs](https://www.geeksforgeeks.org/restful-statelessness/). +A smart contract can simulate state by requiring the output of the spending transaction containing the same contract but with the updated state, enabled by [ScriptContext](scriptcontext.md). This is similar to making HTTP seem stateful by using cookies. +### Managing state across bitcoin transactions + So far, all the contracts we’ve gone through have been stateless. But often, you may want a contract to have some concept of “memory” so that it may remember information about its previous interactions. That is, we need contracts that are **stateful**. -To achieve that, we divide a smart contract in the locking script of an output into two parts: code and state as shown below. The code part contains the business logic of a contract that encodes rules for state transition and must **NOT** change. State transition occurs when a transaction spends the output containing the old state and creates a new output containing the new state, while keeping the contract code intact. +To achieve that, we divide a smart contract in the locking script of an output into two parts: 1. code and 2. state, as shown below. + +The code part contains the business logic of a contract that encodes rules for state transition and **must not change**. +A state transition occurs when a transaction spends the output containing the old state and creates a new output containing the new state, while keeping the contract code intact. Since the new output contains the same contract code, its spending transaction must also retain the same code, otherwise it will fail. This chain of transactions can go on and on and thus a state is maintained along the chain, recursively. ![](../../static/img/state.jpg) @@ -26,9 +31,9 @@ npx scrypt-cli project --state counter Note the `state` option is turned on. -This will create a project containing a sample stateful contract named `Counter`. This contract maintains a single state: how many times it has been called since deployment. +This creates a project containing a sample stateful contract named `Counter`. This basic contract maintains a single state: how many times it has been called since deployment. -Let's take a look at the contract source file `src/contracts/counter.ts`. +Let's take a look at the contract source file `/src/contracts/counter.ts`. ### Stateful properties As shown [before](how-to-write-a-contract.md#properties), a `@prop(true)` decorator is used to make a property part of the contract stateful, meaning it can be mutated when the contract gets called. @@ -82,9 +87,12 @@ export class Counter extends SmartContract { @method() public incrementOnChain() { + // Increment counter. this.increment() - // make sure balance in the contract does not change + // Ensure next output will contain this contracts code with + // the updated count property. + // And make sure balance in the contract does not change const amount: bigint = this.ctx.utxo.value // outputs containing the latest state and an optional change output const outputs: ByteString = this.buildStateOutput(amount) + this.buildChangeOutput() @@ -101,7 +109,7 @@ export class Counter extends SmartContract { ## Stateless vs Stateful Contracts -The choice between stateless and stateful smart contracts hinges on the needs of your blockchain application. +The choice between stateless and stateful smart contracts depends on the needs of your blockchain application. If your app needs to store persistent data on chain, a stateful smart contract is appropriate. For example, with an [auction app](../tutorials/auction.md), you want to store the highest bidder so far and how much she bids, in case you need to return the fund to her when a higher bid arrives. diff --git a/docs/installation.md b/docs/installation.md index d54516b3a..8beed4b40 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -6,7 +6,7 @@ sidebar_position: 2 ## Prerequisite -1. Install `Node.js` (require version `>=16`) and `NPM` on your machine by following the instructions over [here](https://nodejs.org/en/download). +1. Install `Node.js` (require version `>=16`) and `NPM` on your machine by following the instructions [here](https://nodejs.org/en/download). 2. Install [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). @@ -23,20 +23,21 @@ softwareupdate --install-rosetta --agree-to-license ## The sCrypt CLI Tool -The [sCrypt CLI tool](https://github.com/sCrypt-Inc/scrypt-cli) is used to easily create, compile and publish `sCrypt` projects. The CLI provides best practice project scaffolding including dependencies such as sCrypt, a test framework ([Mocha](https://mochajs.org)), code auto-formatting ([Prettier](https://prettier.io)), linting ([ES Lint](https://eslint.org)), & more. +The [sCrypt CLI](https://github.com/sCrypt-Inc/scrypt-cli) tool is used to easily create, compile and publish `sCrypt` projects. +The CLI provides best practice project scaffolding including dependencies such as sCrypt, a test framework ([Mocha](https://mochajs.org)), code auto-formatting ([Prettier](https://prettier.io)), linting ([ES Lint](https://eslint.org)), and more. -We can run the CLI tool directly with `npx` and try it out by creating a demo project: +Run the CLI tool directly with `npx` and try it out by creating a demo project: ```sh npx scrypt-cli project demo ``` -Or you can install it globally to your machine. +Or install it globally on your machine. ```sh npm install -g scrypt-cli ``` :::tip -You can also simply fork [the demo contract Repl](https://replit.com/@msinkec/scryptTS-demo) and play with the code in your browser. +You can also fork [the demo contract on Repl.it](https://replit.com/@msinkec/scryptTS-demo) and play with the code in your browser. ::: diff --git a/docs/tutorials/hello-world.md b/docs/tutorials/hello-world.md index baa319cde..39578a77d 100644 --- a/docs/tutorials/hello-world.md +++ b/docs/tutorials/hello-world.md @@ -6,11 +6,11 @@ sidebar_position: 1 ## Overview -In this tutorial, we will go over how to quickly create a “Hello World” smart contract, deploy and call it. +In this tutorial, we will cover how to create a "Hello World" smart contract, deploy it, and call it. -## Create a new project +Before starting, ensure all [prerequisite tools](../../installation) are installed. -Make sure [all prerequisite tools](../../installation) are installed. +## Create a new project Run the following commands to create a new project: @@ -20,7 +20,9 @@ cd helloworld npm install ``` -The resulting project will contain a sample smart contract `src/contracts/helloworld.ts`, along with all the scaffolding. Let's modify it to the following code: +The resulting project will contain a sample smart contract `/src/contracts/helloworld.ts`, along with all the scaffolding. + +For this example, let's modify it to the following code: ```ts @@ -43,20 +45,15 @@ export class Helloworld extends SmartContract { } ``` -The `Helloworld` contract stores the sha256 hash of a message in the contract property `hash`. Only a message which hashes to the value set in `this.hash` will unlock the contract. +This `Helloworld` contract stores the sha256 hash of a message in the contract property `hash`. Only a message which hashes to the value set in `this.hash` will unlock the contract. Now let’s look at what is in the smart contract. - - `SmartContract`: all smart contracts must extend the `SmartContract` base class. - - `@prop`: the [`@prop` decorator](../how-to-write-a-contract/how-to-write-a-contract.md#properties) marks a contract property. - - `@method`: the [`@method` decorator](../how-to-write-a-contract/how-to-write-a-contract.md#method-decorator) marks a contract method. A [public method](../how-to-write-a-contract/#public-methods) is an entry point to a contract. - - `assert`: throws an error and makes the method call fail if its first argument is `false`. Here it ensures the passed message hashed to the expected digest. - ## Compile Contract 1. Run following command to compile the `Helloworld` contract: @@ -65,7 +62,7 @@ Now let’s look at what is in the smart contract. npx scrypt-cli compile ``` -This command will generate a contract artifact file at `artifacts\helloworld.json`. +This command will generate a contract artifact file at `/artifacts/helloworld.json`. 2. Then call the `loadArtifact()` function in the code: @@ -76,13 +73,14 @@ await Helloworld.loadArtifact() ## Compile using the `watch` option -Monitoring for Real-Time Error Detection +Monitoring for Real-time Error Detection ```sh npx scrypt-cli compile --watch ``` -The `watch` option in the provided command appears to facilitate continuous monitoring of sCrypt level errors during the compilation process. When invoked with the specified command, it utilizes the "npx scrypt-cli compile --watch" syntax to initiate a watch mode. This mode likely allows users to observe real-time updates and notifications regarding any errors specific to sCrypt, which are distinct from TypeScript errors. +The `watch` option in the provided command continuously monitors errors during the sCrypt compilation process. +Watch mode enables users to observe real-time updates and notifications regarding any errors specific to sCrypt, which are distinct from TypeScript errors. ![](../../static/img/watch.gif) @@ -94,15 +92,14 @@ Before we deploy the contract, you need to generate a Bitcoin key. npm run genprivkey ``` -then follow [the instruction](../../how-to-deploy-and-call-a-contract/faucet) to fund the key. +then follow the [faucet instructions](../../how-to-deploy-and-call-a-contract/faucet) to fund the key. Next, start deploying and calling the contract: 1. To [deploy a smart contract](../how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md#contract-deployment), simply call its `deploy` method. +1. To [call a smart contract](../how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md#contract-call), call one of its public methods. -2. To [call a smart contract](../how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md#contract-call), call one of its public method. - -Overwrite `deploy.ts` in the root of the project with the following code to deploy and call the `Helloworld` contract: +For this example, overwrite `deploy.ts` in the root of the project with the following code to deploy and call the `Helloworld` contract: ```ts import { Helloworld } from './src/contracts/helloworld' @@ -113,7 +110,8 @@ import { toByteString, sha256 } from 'scrypt-ts' // set network env process.env.NETWORK = 'testnet' - + // alternatively, set `NETWORK=testnet` in the .env file + const message = toByteString('hello world', true) await Helloworld.loadArtifact() @@ -126,18 +124,18 @@ import { toByteString, sha256 } from 'scrypt-ts' const deployTx = await instance.deploy(42) console.log('Helloworld contract deployed: ', deployTx.id) - // contract call + // call the contract const { tx: callTx } = await instance.methods.unlock(message) console.log('Helloworld contract `unlock` called: ', callTx.id) - })() ``` -Run the following command: +Run the following command to deploy AND call our example contract. ``` npx ts-node deploy.ts ``` + You will see some output like: ![](../../static/img/hello-world-deploy-and-call-output.png) @@ -153,12 +151,3 @@ You can also view [the calling transaction](https://test.whatsonchain.com/tx/f28 ![](../../static/img/hello-world-contract-call-tx.png) Congrats! You have deployed and called your first Bitcoin smart contract. - - - - - - - - -