Skip to main content

Getting Started

Try CashScript Online

To get started with writing CashScript smart contracts quickly, it is recommended to try out the CashScript Playground, a web application which lets you easily write and create contracts!

The Playground has a code-editor and CashScript compiler easily accesible, without any prerequisites. Further, the playground allows to easily create contracts, wallets and build transactions.

tip

The CashScript Playground is a great way to get started without doing any JavaScript/TypeScript coding, this way you can focus on learning just CashScript!

The Playground supports 'Mocknet', this is the recommended way to get started developing. This way you can create virtual UTXOs for testing without having to get Testnet coins and set up a Testnet wallet.

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 mock UTXOs to the smart contract address and the wallets used for testing.
  4. Next, go to the TransactionBuilder select the contract UTXO and the function to invoke on it
  5. Finally, specify the in- and outputs for the transaction and click 'Evaluate'!

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

To write CashScript smart contracts locally you use a code editor. For the best developer experience, we recommend to use VS Code with the CashScript extension. This way it will automatically recognize .cash files and offer highlighting and autocompletion.

prerequisites
  • Basic familiarity with the command line
  • Node.js installed (v20 or newer)
  • A code editor (VS Code recommended)
tip

To set up your CashScript developer environment, see the Syntax Highlighting guide.

Installing the CashScript compiler

The command line CashScript compiler cashc can be installed from NPM.

npm install -g cashc

Writing your first contract

We can start from a basic TransferWithTimeout smart contract, a simple contract for tips which allows the recipient to claim their gift at any time, but if they don't claim within some time, the sender can reclaim it.

Open your code editor to start writing your first CashScript smart contract. Then create a new file TransferWithTimeout.cash and copy over the smart contracts code from below.

pragma cashscript ^0.11.0;

contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
// Allow the recipient to claim their received money
function transfer(sig recipientSig) {
require(checkSig(recipientSig, recipient));
}

// Allow the sender to reclaim their sent money after the timeout is reached
function timeout(sig senderSig) {
require(checkSig(senderSig, sender));
require(tx.time >= timeout);
}
}

Let's take some time to understand the contract structure. At the top, the smart contract declares the CashScript language version it's using with pragma. Then a TransferWithTimeout is declare which takes in 3 contract arguments and has 2 contract functions: transfer and timeout. These contract functions both have require statements necessary to be met to be able to spend BCH from the contract.

tip

There are some other examples available on the Examples page that can be used to take inspiration from. Further examples of the TypeScript and JavaScript integration can be found on GitHub.

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.

cashc ./TransferWithTimeout.cash --output ./TransferWithTimeout.json

This will create an artifact file TransferWithTimeout.json from your CashScript file. If you look at the bytecode property on the artifact you will see the raw BCH script generated by the cashc compiler:

OP_3 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_4 OP_ROLL OP_ROT OP_CHECKSIG OP_NIP OP_NIP OP_NIP OP_ELSE OP_3 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_3 OP_ROLL OP_SWAP OP_CHECKSIGVERIFY OP_SWAP OP_CHECKLOCKTIMEVERIFY OP_2DROP OP_1 OP_ENDIF

Creating a CashScript Transaction

After creating a contract artifact, we can now use the TypeScript SDK to initialise the smart contract and to create a transaction spending from the smart contract UTXO. 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. It's recommended to use TypeScript for full type-safety of all you contract logic.

Installing the TypeScript SDK

The TypeScript SDK can be installed into your project with NPM. Note that CashScript is a pure ESM package.

npm install cashscript

Initialising a Contract

To initialise a contract with the SDK we will need 3 things: the contract artifact, the contract constructor arguments and a NetworkProvider.

To do this, we import the ElectrumNetworkProvider and Contract classes from the CashScript SDK. We also need to import the contract artifact. Lastly, we need public keys from a generated key-pair to use as contract constructor arguments.

tip

For a code example of how to generate key-pairs with Libauth, see the CashScript Examples' common.ts file where Alice and Bob's key-pairs are created.

With the instantiated contract, we can now get the contract address and get the contract balance and UTXOs in the following way:

import { ElectrumNetworkProvider, Contract } from 'cashscript';
import artifact from './TransferWithTimeout.json' with { type: 'json' };
import { alicePub, bobPub } from './keys.js';

// Initialise a network provider for network operations
const provider = new ElectrumNetworkProvider('chipnet');

// Instantiate a new TransferWithTimeout contract
const contractArguments = [alicePub, bobPub, 100000n];
const contract = new Contract(artifact, contractArguments, {provider});

// Get the contract address and info about its balance
console.log("Contract address: " + contract.address);
console.log("Contract balance: " + await contract.getBalance());
console.log("Contract UTXOs: " + await contract.getUtxos());

Next, to spend from the smart contract we've initialised, you would need to make sure there is an actual contract balance on the smart contract address.

To make development easier we'll use the MockNetworkProvider instead so you can simulate transactions in a 'mock' network environment.

Creating a Transaction

Finally to create a transaction spending from the smart contract UTXO we use the TransactionBuilder to add in- and outputs to the transaction. The difference between the total BCH amount in the in- and outputs is the transaction fee.

To spend from an input you specify the UTXO together with an Unlocker to actually provide the 'unlock' script matching the input's locking bytecode. For the initialized smart contract the Unlockers are available as methods on the Contract instance. Below we will invoke the transfer function on the contract utxo through contract.unlock.transfer(...).

import { MockNetworkProvider, Contract, SignatureTemplate, TransactionBuilder, randomUtxo } from 'cashscript';
import { alicePub, bobPriv, bobPub } from './keys.js';
import artifact from './TransferWithTimeout.json' with { type: 'json' };

// Initialise a mocknetwork provider for easy testing
const provider = new MockNetworkProvider();

// Instantiate a new TransferWithTimeout contract
const contractArguments = [alicePub, bobPub, 100000n];
const contract = new Contract(artifact, contractArguments, {provider});

// Create a mocknet UTXO for testing
const contractMockUtxo = randomUtxo()
provider.addUtxo(contract.address, contractMockUtxo);

// Create the signatureTemplate for bob to sign the contract input
const bobSignatureTemplate = new SignatureTemplate(bobPriv)

// Start building the transaction
const transferDetails = await new TransactionBuilder({ provider })
.addInput(contractMockUtxo, contract.unlock.transfer(bobSignatureTemplate))
.addOutput({
to: 'bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx',
amount: 10000n
})
.send();

console.log(transferDetails);

Congrats 🎉! You've successfully created a transaction spending from a Bitcoin Cash smart contract!

To use the timeout function you need to use Alice as signer for the spending condition. Secondly you also need get to use .setLocktime() with a valid argument during the transaction building to pass the tx.time check of the timeout function.