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:
Creating the keypairs
Generating a Contract
Building the Oracle
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
SignatureTemplatefor anysigargumentsTransactionBuilderto 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)