Skip to content

Commit

Permalink
improve docs (#216)
Browse files Browse the repository at this point in the history
- improved getting started
- added Cauldron DEX to examples, remove transferWithTimeout
- moved operators section to types page
- improved notice on pragma usage
- improved optimization guide
- added a guide page for walletconnect
- add SignatureAlgorithm  to the SignatureTemplate  docs
- separated the SignatureTemplate  docs to a separate page
- Improved the 'Contract Instantiation' page sub titles
  • Loading branch information
mr-zwets authored Oct 22, 2024
1 parent 0dd95d5 commit 7bd9ba5
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 141 deletions.
21 changes: 14 additions & 7 deletions website/docs/basics/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ title: Getting Started

To get started with writing CashScript smart contracts quickly, it is recommended to try out the [CashScript Playground](https://playground.cashscript.org/), a web application which lets you easily write and create contracts!

Here you will learn what a CashScript contract looks like through the `TransferWithTimeout` example contract. Then you can compile the contract and provide the contract arguments to create a smart contract address. To actually create the smart contract itself, you fund the address so that it has a UTXO on the contract address. After setting up the smart contract, you can try spending from the smart contract by invoking a contract function!
The Playground supports 'Mocknet', allowing you to create virtual UTXOs for development without having to get Testnet coins and set up a Testnet wallet.

:::tip
The [CashScript Playground](https://playground.cashscript.org/) is a great way to get started without doing any JavaScript coding to set up wallets, fetch balances and invoke contract functions. This way you can focus on learning one thing at a time!
The [CashScript Playground](https://playground.cashscript.org/) is a great way to get started without doing any JavaScript/TypeScript coding to set up wallets, fetch balances and invoke contract functions. This way you can focus on learning just CashScript!
:::

## Creating a CashScript Smart Contract
Here are the 5 simple steps for creating your first smart contract transaction with the Playground:
1. Compile a contract, for example the default `TransferWithTimeout` contract.
2. Create a new contract in the 'New Contract' tab by providing the contract arguments.
3. Add UTXOs to the smart contract address and the wallets used for testing.
4. Next, go to the TansactionBuilder select the contract and the function to invoke
5. Finally, specify the in- and outputs for the transaction and click 'Send'!

With the CashScript playground there's a nice integrated editor to get started, as well as an integrated compiler to change your CashScript contract into a Contract `Artifact` behind the scenes. Now, to get started we will have to set up a CashScript editor — outside of the Playground — and learn how to work with the `cashc` compiler to create CashScript contract artifacts.
## Creating a CashScript Contract

To get started coding locally we will use a code editor and learn how to work with the `cashc` compiler to create CashScript contract artifacts.

### Prerequisites

Expand All @@ -37,7 +44,7 @@ The command line CashScript compiler `cashc` can be installed from NPM.
npm install -g cashc
```

### Writing your first smart contract
### Writing your first contract

Open your code editor to start writing your first CashScript smart contract.
We can start from a basic `TransferWithTimeout` smart contract. Create a new file `TransferWithTimeout.cash`.
Expand Down Expand Up @@ -65,7 +72,7 @@ contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
There are some other examples available on the [Examples page](/docs/language/examples) that can be used to take inspiration from. Further examples of the TypeScript and JavaScript integration can be found on [GitHub](https://github.com/CashScript/cashscript/tree/master/examples).
:::

### Compiling your smart contract
### Compiling your contract

The next step after writing your smart contract is using the command line compiler to create a contract artifact, so that it can be imported into the CashScript SDK.

Expand All @@ -81,7 +88,7 @@ OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NI

## Creating a CashScript Transaction

After creating a contract artifact, we can now use the TypeScript SDK to initialise the smart contract and to invoke spending functions on the smart contract UTXOs. We'll continue with the `TransferWithTimeout` artifact generated in the previous steps.
After creating a contract artifact, we can now use the TypeScript SDK to initialise the smart contract and to invoke spending functions on the smart contract UTXOs. We'll continue with the `TransferWithTimeout` artifact generated earlier.

:::info
The CashScript SDK is written in TypeScript meaning that you can either use TypeScript or vanilla JavaScript to use the SDK.
Expand Down
76 changes: 34 additions & 42 deletions website/docs/guides/optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,26 @@ sidebar_label: Optimization

CashScript contracts are transpiled from a solidity syntax to [BCH Script](https://reference.cash/protocol/blockchain/script) by the `cashc` compiler. BCH Script is a lower level language (a list of stack-based operations) where each available operation is mapped to a single byte.

Depending on the complexity of the contract or system design, it may sometimes be useful to optimize the Bitcoin Script by tweaking the contract in CashScript before it is compiled. Below are some ideas to get started.
Depending on the complexity of the contract or system design, it may be useful to optimize the Bitcoin Script by tweaking the contract in CashScript before it is compiled because the minimum fees on the Bitcoin Cash network are based on the bytesize of a transaction (including your contract).

## Example Workflow

When optimizing your contract, you will need to compare the contract size to see if the changes have a positive impact.
With the compiler CLI, you can easily check the opcode count and bytesize directly from the generated contract artifact.

```bash
cashc ./contract.cash --opcount --size
```

The size outputs of the `cashc` compiler are based on the bytecode without constructor arguments. This means they will always be an underestimate, as the contract hasn't been initialized with contract arguments.

:::note
The compiler opcount and bytesize outputs are still helpful to compare the effect of changes to the smart contract code on the compiled output, given that the contract constructor arguments stay the same.
:::

:::tip
To get the exact contract opcount and bytesize including constructor parameters, initialise the contract with the TypScript SDK and check the values of `contract.opcount` and `contract.bytesize`.
:::

## Optimization Tips & Tricks

Expand Down Expand Up @@ -73,52 +92,25 @@ The concept of having NFT functions was first introduced by the [Jedex demo](htt
By using function NFTs you can use a modular contract design where the contract functions are offloaded to different UTXOs, each identifiable by the main contract by using the same tokenId.
:::

## Example Workflow

When trying to optimize your contract, you will need to compare the contract size to see if the changes have a positive impact.
With the compiler CLI, you can easily check the opcode count and bytesize directly from the contract (without creating a new artifact).
It's important to know the minimum fees on the Bitcoin Cash network are based on the bytesize of a transaction (including your contract).

```bash
cashc ./contract.cash --opcount --size
```

:::caution
The size output of the `cashc` compiler will always be an underestimate, as the contract hasn't been initialized with contract arguments.
:::

The `cashc` compiler only knows the opcount and bytesize of a contract before it is initialised with function arguments. Because of this, to get an accurate view of a contracts size, initialise the contract instance first, then get the size from there. This means you will have to re-compile the artifact before checking the contract size through the TypeScript SDK.
## Hand-optimizing Bytecode

```javascript
import { ElectrumNetworkProvider, Contract, SignatureTemplate } from 'cashscript';
import { alicePub, bobPriv, bobPub } from './keys.js';
import { compileFile } from 'cashc';
It's worth considering whether hand-optimizing the contract is necessary at all. If the contract works and there is no glaring inefficiency in the bytecode, perhaps the best optimization is to not to obsess prematurely about things like transaction size.

// compile contract code on the fly
const artifact = compileFile(new URL('contract.cash', import.meta.url));
>We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
// Initialise a network provider for network operations
const provider = new ElectrumNetworkProvider('chipnet');
### Overwriting the Artifact

// Instantiate a new TransferWithTimeout contract
const contractArguments = [alicePub, bobPub, 100000n]
const options = { provider }
const contract = new Contract(artifact, contractArguments, options);
To manually change the contract bytecode, you need to overwrite the `bytecode` key of your contract artifact.

console.log(contract.opcount);
console.log(contract.bytesize);
```typescript
interface Artifact {
bytecode: string // Compiled Script without constructor parameters added (in ASM format)
}
```

With this workflow, you can make changes to the contract and the run the JavaScript program to
get an accurate measure of how the bytesize of your contract changes with different optimizations.
## To optimize or not to optimize?
In the context of optimizing contract bytecode, there's an important remark to consider:
### OP_NOP
>We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
It's worth considering whether optimizing the redeem script is necessary at all. If the contract is accepted by the network, and there is no glaring inefficiency in the bytecode, perhaps the best optimization is to not to obsess prematurely about things like block size.
This way you can still use the CashScript TypeScript SDK while using a hand-optimized contract.

:::caution
If you manually overwite the `bytecode` in the artifact, this will make the auto generated 2-way-mapping to become obsolete.
This result of this is that the dubugging functionality will no longer work for the contract.
:::
93 changes: 93 additions & 0 deletions website/docs/guides/walletconnect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: WalletConnect
---

The BCH WalletConnect specification was created in July of 2023 and has become popular since. The standard integrates nicely with CashScript contract development as it was designed with CashScript contract usage in mind.

You can find a full list of Bitcoin Cash dapps supporting WalletConnect on [Tokenaut.cash](https://tokenaut.cash/dapps?filter=walletconnect).

:::tip
The specification is called ['wc2-bch-bcr'](https://github.com/mainnet-pat/wc2-bch-bcr) and has extra discussion and info on the [BCH research forum](https://bitcoincashresearch.org/t/wallet-connect-v2-support-for-bitcoincash/).
:::

## signTransaction Interface

Most relevant for smart contract usage is the BCH-WalletConnect `signTransaction` interface.

> This is a most generic interface to propose a bitcoincash transaction to a wallet which reconstructs it and signs it on behalf of the wallet user.
```typescript
signTransaction: (
options: {
transaction: string | TransactionBCH,
sourceOutputs: (Input | Output | ContractInfo)[],
broadcast?: boolean,
userPrompt?: string
}
) => Promise<{ signedTransaction: string, signedTransactionHash: string } | undefined>;
```

Important to note is how the wallet knows which inputs to sign:

>To signal that the wallet needs to sign an input, the app sets the corresponding input's `unlockingBytecode` to empty Uint8Array.
## signTransaction Example

### Create wcTransactionObj

Example code from the 'Cash-Ninjas' minting dapp repository, [link to source code](https://github.com/cashninjas/ninjas.cash/blob/main/js/mint.js).

```javascript
import { Contract } from "cashscript";
import { hexToBin, decodeTransaction } from "@bitauth/libauth";

async function transactionWalletConnect(){
// use the CashScript SDK to build a transaction
const rawTransactionHex = await transaction.build();
const decodedTransaction = decodeTransaction(hexToBin(rawTransactionHex));

// set input unlockingBytecode to empty Uint8Array for wallet signing
decodedTransaction.inputs[1].unlockingBytecode = Uint8Array.from([]);

// construct list SourceOutputs, see source code
const listSourceOutputs = constructSourceOuputs(decodedTransaction, utxoInfo, contract)

// wcTransactionObj to pass to signTransaction endpoint
const wcTransactionObj = {
transaction: decodedTransaction,
sourceOutputs: listSourceOutputs,
broadcast: true,
userPrompt: "Mint Cash-Ninja NFT"
};

// pass wcTransactionObj to WalletConnect client
const signResult = await signTransaction(wcTransactionObj);

// Handle signResult success / failure
}
```

### signTransaction Client interaction

Below is an implementation of `signTransaction` used earlier, this is where the communication with the client for 'signTransaction' takes place. See the source code for `signClient`, `connectedChain` and `session` details.

```javascript
import SignClient from '@walletconnect/sign-client';
import { stringify } from "@bitauth/libauth";

async function signTransaction(options){
try {
const result = await signClient.request({
chainId: connectedChain,
topic: session.topic,
request: {
method: "bch_signTransaction",
params: JSON.parse(stringify(options)),
},
});
return result;
} catch (error) {
return undefined;
}
}
```
8 changes: 4 additions & 4 deletions website/docs/language/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ title: Contract Structure
Contracts in CashScript are somewhat similar to classes in object-oriented languages. A notable difference is that there is no mutable state. So once a contract is instantiated with certain parameters, these values cannot change. Instead, functions can be called on the contract that act on the contract's values to spend money from the contract. The extension of CashScript source code files is `.cash`, and the structure of these source files is explained below.

## Pragma
A contract file may start with a pragma directive to indicate the CashScript version the contract was written for. This ensures that a contract is not compiled with an unsupported compiler version, which could cause unintended side effects.
A contract file may start with a pragma directive to indicate the CashScript version the contract was written for. This ensures that a contract is not compiled with an unsupported compiler version. The pragma directive follows regular [semantic versioning (SemVer)](https://semver.npmjs.com/) rules.

:::note
The pragma directive follows regular [semantic versioning rules](https://semver.npmjs.com/).
:::caution
Contract authors should be careful when allowing a range of versions to check that no breaking changes to the compiler were introduced in these versions which would result in different bytecode and smart contract address.
:::

#### Example
Expand Down Expand Up @@ -82,7 +82,7 @@ contract P2PKH(bytes20 pkh) {
### Variable declaration
Variables can be declared by specifying their type and name. All variables need to be initialised at the time of their declaration, but can be reassigned later on — unless specifying the `constant` keyword. Since CashScript is strongly typed and has no type inference, it is not possible to use keywords such as `var` or `let` to declare variables.

:::caution
:::note
CashScript disallows variable shadowing and unused variables.
:::

Expand Down
Loading

0 comments on commit 7bd9ba5

Please sign in to comment.