Examples

An extensive collection of examples is available in the upstream CashScript repository. In CashScript-Py, examples are currently split across:

  • Getting started (recommended starting point)

  • examples/ (runnable scripts)

Below we discuss the HodlVault example flow, focusing on SDK usage (contract instantiation, signatures, providers, and transaction building).

Runnable script: examples/hodl_vault.py

HodlVault

We will break up the development of the smart contract application in 4 manageable steps:

  1. Creating the keypairs

  2. Generating a Contract

  3. Building the Oracle

  4. Sending a Transaction

Creating the keypairs

cashscript_py_support.utils provides a Keypair helper which produces chipnet keys by default:

from cashscript_py_support.utils import Keypair

alice = Keypair()
oracle_keypair = Keypair()

Caution: Never hardcode private keys in production. Prefer environment variables or a secure wallet/seed flow.

Generating a Contract

import json
from typing import Any

from cashscript_py import Contract, ElectrumNetworkProvider

with open("./examples/hodl_vault.json", "r", encoding="utf-8") as f:
    artifact: dict[str, Any] = json.load(f)

provider = ElectrumNetworkProvider("chipnet")

# Instantiate a new contract using the compiled artifact and network provider, providing constructor parameters.
parameters = [alice.public_key, oracle_keypair.public_key, 100_000, 30_000]
contract = Contract(artifact, parameters, provider)

print("contract address:", contract.address)
print("contract balance:", await contract.get_balance())

Building the Oracle

The upstream example signs a message containing two 4-byte little-endian VM numbers (block height and price). In CashScript-Py, a more thorough implementation lives at tests/fixture/price_oracle.py, but for documentation purposes we inline a minimal version here.

This oracle:

  • creates an 8-byte message: blockHeight(4) || price(4) (both minimally-encoded VM numbers, padded to width 4)

  • signs sha256(message) using Schnorr (no hashtype byte), matching OP_CHECKDATASIG semantics

import hashlib

from cashscript_py import SignatureAlgorithm, SignatureTemplate
from cashscript_py.helpers.script import encode_int as encode_vm_int


def _pad_vm_number_le(b: bytes, width: int) -> bytes:
    b = bytes(b)
    if len(b) > width:
        raise ValueError("Number too large for requested width")
    return b + (b"\x00" * (width - len(b)))


class PriceOracle:
    def __init__(self, private_key_bytes: bytes) -> None:
        self._tmpl = SignatureTemplate(private_key_bytes, signature_algorithm=SignatureAlgorithm.SCHNORR)

    def create_message(self, block_height: int, bch_usd_price: int) -> bytes:
        bh_le = _pad_vm_number_le(bytes(encode_vm_int(block_height)), 4)
        price_le = _pad_vm_number_le(bytes(encode_vm_int(bch_usd_price)), 4)
        return bh_le + price_le

    def sign_message(self, message: bytes) -> bytes:
        digest = hashlib.sha256(message).digest()
        return self._tmpl.sign_message_hash(digest)

Sending a Transaction

Finally, combine:

  • Contract.get_utxos()

  • provider.get_block_height()

  • the oracle message/signature

  • SignatureTemplate for any sig arguments

  • TransactionBuilder to spend and broadcast

from cashscript_py import Output, SignatureTemplate, TransactionBuilder

contract_utxos = await contract.get_utxos()

selected_contract_utxo = contract_utxos[0]

current_block_height = await provider.get_block_height()

oracle = PriceOracle(oracle_keypair.private_key)
oracle_message = oracle.create_message(current_block_height, bch_usd_price=30_000)
oracle_signature = oracle.sign_message(oracle_message)

alice_signature_template = SignatureTemplate(alice.private_key)

transfer_details: dict[str, str] = await (
    TransactionBuilder(provider)
    .add_input(
        selected_contract_utxo,
        contract.unlock["spend"](alice_signature_template, oracle_signature, oracle_message),
    )
    .add_output(Output(to=alice.address, amount=1000))
    .set_locktime(current_block_height)
    .send()
)

print(transfer_details)