Contract Instantiation
Before interacting with a smart contract on the BCH network, the CashScript SDK needs to instantiate a Contract
object. This is done by providing the contract's information and constructor arguments. After this instantiation, the CashScript SDK can interact with the BCH contract.
CashScript offers a TypeScript SDK, which can also be used easily in vanilla JavaScript codebases. Because of the separation of the compiler and the SDK, CashScript contracts can be integrated into other languages in the future.
Contract class
The Contract
class is used to represent a CashScript contract in a JavaScript object. These objects can be used to retrieve information such as the contract's address and balance. They can also be used to interact with the contract by calling the contract's functions.
Constructor
new Contract(
artifact: Artifact,
constructorArgs: ConstructorArgument[],
options? : {
provider: NetworkProvider,
addressType: 'p2sh20' | 'p2sh32',
}
)
A CashScript contract can be instantiated by providing an Artifact
object, a list of constructor arguments, and optionally an options object configuring NetworkProvider
and addressType
.
An Artifact
object is the result of compiling a CashScript contract. See the Language Documentation for more information on Artifacts. Compilation can be done using the standalone cashc
CLI or programmatically with the cashc
NPM package (see CashScript Compiler).
The NetworkProvider
option is used to manage network operations for the CashScript contract. By default, a mainnet ElectrumNetworkProvider
is used, but the network providers can be configured. See the docs on NetworkProvider.
The addressType
option is used to choose between a p2sh20
and p2sh32
address type for the CashScript contract. By default p2sh32
is used because it has increased cryptographic security — but it is not yet supported by all wallets.
p2sh32 was introduced because p2sh20 is cryptographically insecure for a large subset of smart contracts. For contracts holding large sums of BCH this provides an incentive to find a hash collision and hack the contract.
Example
import { Contract, ElectrumNetworkProvider } from 'cashscript';
import { compileFile } from 'cashc';
// Import the artifact JSON
import P2PKH from './p2pkh.json';
// Or compile a contract file
const P2PKH = compileFile(new URL('p2pkh.cash', import.meta.url));
const provider = new ElectrumNetworkProvider('chipnet');
const addressType = 'p2sh20';
const contractArguments = [alicePkh]
const options = { provider, addressType}
const contract = new Contract(P2PKH, contractArguments, options);
address
contract.address: string
A contract's regular address can be retrieved through the address
member field.
Example
console.log(contract.address)
opcount
contract.opcount: number
The number of opcodes in the contract's bytecode can be retrieved through the opcount
member field. This is useful to ensure that the contract is not too big, since Bitcoin Cash smart contracts can contain a maximum of 201 opcodes.
Example
assert(contract.opcount <= 201)
bytesize
contract.bytesize: number
The size of the contract's bytecode in bytes can be retrieved through the bytesize
member field. This is useful to ensure that the contract is not too big, since Bitcoin Cash smart contracts can be 520 bytes at most.
Example
console.log(contract.bytesize)
bytecode
contract.bytecode: string
Returns the contract's redeem script encoded as a hex string.
Example
console.log(contract.bytecode)
getBalance()
async contract.getBalance(): Promise<bigint>
Returns the total balance of the contract in satoshis. Both confirmed and unconfirmed balance is included in this figure.
Example
const contractBalance = await contract.getBalance()
getUtxos()
async contract.getUtxos(): Promise<Utxo[]>
Returns all UTXOs that can be spent by the contract. Both confirmed and unconfirmed UTXOs are included.
interface Utxo {
txid: string;
vout: number;
satoshis: bigint;
token?: TokenDetails;
}
Example
const utxos = await contract.getUtxos()
Contract functions
contract.functions.<functionName>(...args: FunctionArgument[]): Transaction
Once a smart contract has been instantiated, you can invoke a contract function to spend from the contract with the 'Simple transaction-builder' by calling the function name under the functions
member field of a contract object.
To call these functions successfully, the provided parameters must match the function signature defined in the CashScript code.
These contract functions return an incomplete Transaction
object, which needs to be completed by providing outputs of the transaction. For more information see the Simple transaction-builder page.
Example
import { alice } from './somewhere';
const tx = await contract.functions
.transfer(new SignatureTemplate(alice))
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000n)
.send()
Contract unlockers
contract.unlock.<functionName>(...args: FunctionArgument[]): Unlocker
Once a smart contract has been instantiated, you can invoke a contract function on a smart contract UTXO to use the 'Advanced transaction-builder' by calling the function name under the unlock
member field of a contract object.
To call these functions successfully, the provided parameters must match the function signature defined in the CashScript code.
These contract functions return an incomplete transactionBuilder
object, which needs to be completed by providing outputs of the transaction. For more information see the Advanced transaction-builder page.
import { contract, transactionBuilder } from './somewhere.js';
const contractUtxos = await contract.getUtxos();
transactionBuilder.addInput(contractUtxos[0], contract.unlock.spend());
SignatureTemplate
new SignatureTemplate(signer: Keypair | Uint8Array | string, hashtype?: HashType)
You may notice the SignatureTemplate
object in the example above. When a contract function has a sig
parameter, it requires a cryptographic signature over the spending transaction. But to generate this signature, the transaction needs to be built first, which is not yet the case when a contract function is first called.
So in the place of a signature, a SignatureTemplate
can be passed, which will automatically generate the correct signature using the signer
parameter. This signer can be any representation of a private key, including BCHJS' ECPair
, bitcore-lib-cash' PrivateKey
, WIF strings, or raw private key buffers. This ensures that any BCH library can be used.
The default hashtype
is HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS
because this is the most secure option for smart contract use cases.
export enum HashType {
SIGHASH_ALL = 0x01,
SIGHASH_NONE = 0x02,
SIGHASH_SINGLE = 0x03,
SIGHASH_UTXOS = 0x20,
SIGHASH_ANYONECANPAY = 0x80,
}
If you're using "old-style" covenants (using CashScript v0.6.0 or lower), you need to configure HashType.SIGHASH_ALL
as the hashtype
parameter for the SignatureTemplate.
Example
const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
const sig = new SignatureTemplate(wif, HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS );