Skip to content

Python API Reference

from ccl import CclLib, Network, CclError, CclClosedError

All functionality hangs off a CclLib instance. JSON-returning methods give you plain dicts / lists; key and hash methods return str.

CclLib(lib_path=None) # lib_path: directory containing libccl, overrides auto-resolution
lib.version() -> str
lib.close() -> None # idempotent
# context manager: with CclLib() as lib: ...

Constructing loads the native library (see resolution order), creates a GraalVM isolate, and verifies the library version matches the wrapper. The API groups are attributes: lib.account, lib.address, lib.crypto, lib.tx, lib.plutus, lib.script, lib.gov, lib.wallet, lib.quicktx.

Lifecycle. close() detaches all attached threads and tears down the isolate; it is idempotent and also runs on __exit__/__del__. Any call after close() raises CclClosedError — this is deliberate: passing a stale isolate handle to the native side would abort the whole process uncatchably, so the wrapper converts it into a catchable exception.

Threading. One CclLib may be shared across threads: each OS thread is attached to the isolate lazily on first use and gets its own native call state, so concurrent calls from a thread pool are safe.

class Network(IntEnum):
MAINNET = 0
TESTNET = 1
PREPROD = 2
PREVIEW = 3

Every method that derives keys (account, wallet, gov, signing) requires a network argument. Omitting it raises TypeError; an out-of-range value raises ValueError before any native call. Being an IntEnum, plain ints 0–3 are accepted too, but prefer the enum.

Gotcha: these values are CCL enum ordinals, not Cardano’s on-chain network id — the two are inverted for mainnet/testnet (Network.MAINNET == 0, but a mainnet address’s on-chain network_id is 1). address.info()["network_id"] is the genuine on-chain value; never feed it back into an API that takes a network.

ExceptionWhen
CclErrorA native call failed. Has .code (see table below) and .message. str(e) = "CCL Error <code>: <message>".
CclClosedError (a RuntimeError)Any API call after close().
TypeError / ValueErrorMissing / out-of-range network argument.
OSErrorNative library could not be loaded.
RuntimeErrorIsolate creation failure or wrapper/native version mismatch.

Error codes on CclError.code (also available as CclLib.CCL_ERROR_* constants):

ConstantCodeMeaning
CCL_ERROR_GENERAL-1Unspecified failure
CCL_ERROR_INVALID_ARGUMENT-2Bad argument
CCL_ERROR_SERIALIZATION-3(De)serialization failure
CCL_ERROR_CRYPTO-4Cryptographic failure
CCL_ERROR_INVALID_NETWORK-5Bad network value
CCL_ERROR_INVALID_MNEMONIC-6Bad mnemonic
CCL_ERROR_INVALID_ADDRESS-7Bad address
CCL_ERROR_INSUFFICIENT_FUNDS-8UTXOs can’t cover outputs + fee
CCL_ERROR_INVALID_TRANSACTION-9Bad transaction
CCL_ERROR_TX_BUILD-10TxPlan build failure (most common quicktx.build error — usually a malformed plan)

Predicate methods (address.validate, crypto.validate_mnemonic, crypto.verify) return False instead of raising.

create(network) -> dict
from_mnemonic(mnemonic, network, account_index=0, address_index=0) -> dict
get_private_key(mnemonic, network, account_index=0, address_index=0) -> str # extended key, 128 hex chars
get_public_key(mnemonic, network, account_index=0, address_index=0) -> str # 64 hex chars
get_drep_id(mnemonic, network, account_index=0) -> str # "drep1..."
sign_tx(mnemonic, tx_cbor_hex, network, account_index=0, address_index=0) -> str
sign_tx_with_keys(mnemonic, tx_cbor_hex, keys, network, account_index=0, address_index=0) -> str

create/from_mnemonic return {"mnemonic", "base_address", "enterprise_address", "stake_address"}.

  • create generates a fresh 24-word mnemonic; treat result["mnemonic"] as a secret.
  • get_private_key returns the 64-byte extended key as 128 hex chars. For raw Ed25519 signing (crypto.sign) use the first 64 hex chars (key[:64]).
  • sign_tx witnesses with the payment key only. When a transaction carries certificates that need other witnesses, use sign_tx_with_keyskeys is a list (or comma-separated string) of roles applied in order: "payment", "stake", "drep", "committee_cold", "committee_hot":
# A stake registration needs the payment key (fee) and the stake key (certificate):
signed = lib.account.sign_tx_with_keys(mnemonic, result["tx_cbor"], ["payment", "stake"], Network.TESTNET)

Note the argument order: unlike the other wrappers, sign_tx/sign_tx_with_keys take the transaction (and keys) before the network.

info(bech32_address) -> dict
validate(bech32_address) -> bool
to_bytes(bech32_address) -> str # hex
from_bytes(hex_bytes) -> str # bech32

info returns {"type", "network_id", "payment_credential_hash", ...}. type is e.g. "Base", "Enterprise", "Pointer", "Reward"; network_id is the genuine on-chain id (mainnet = 1).

blake2b_256(data_hex) -> str # 64 hex chars
blake2b_224(data_hex) -> str # 56 hex chars
generate_mnemonic(word_count=24) -> str
validate_mnemonic(mnemonic) -> bool
sign(message_hex, sk_hex) -> str # Ed25519; 32-byte key (64 hex chars)
verify(signature_hex, message_hex, pk_hex) -> bool
digest = lib.crypto.blake2b_256("48656c6c6f") # "Hello"
sk = lib.account.get_private_key(mnemonic, Network.TESTNET)[:64]
sig = lib.crypto.sign("68656c6c6f", sk)
hash(tx_cbor_hex) -> str # 64 hex chars
sign_with_secret_key(tx_cbor_hex, sk_cbor_hex) -> str
to_json(tx_cbor_hex) -> dict
from_json(tx_json) -> str # accepts dict or JSON string; returns CBOR hex
deserialize(tx_cbor_hex) -> dict

to_json/deserialize return a dict with a body key (inputs/outputs/fee).

Known limitations (current release): tx.from_json and tx.sign_with_secret_key hit GraalVM reflection gaps in the native library and are not usable yet. For signing, use account.sign_tx (mnemonic-based), which covers the common path.

data_hash(datum_cbor_hex) -> str # 64 hex chars
data_to_json(cbor_hex) -> str
data_from_json(json_str) -> str # accepts dict or JSON string; returns CBOR hex
h = lib.plutus.data_hash("182a") # hash of PlutusData int 42

Known limitation (current release): data_to_json/data_from_json hit a GraalVM reflection gap and are not usable yet; data_hash works.

native_from_json(json_str) -> str # JSON string: {"policy_id", "script_hash", "cbor_hex"}
hash(script_cbor_hex, script_type=0) -> str # 56 hex chars

script_type: 0 native, 1 PlutusV1, 2 PlutusV2, 3 PlutusV3.

import json
script = json.loads(lib.script.native_from_json(json.dumps({"type": "sig", "keyHash": key_hash})))
# script["policy_id"], script["script_hash"], script["cbor_hex"]
drep_key_from_mnemonic(mnemonic, network, account_index=0) -> dict
committee_cold_key_from_mnemonic(mnemonic, network, account_index=0) -> dict
committee_hot_key_from_mnemonic(mnemonic, network, account_index=0) -> dict

The DRep method returns {"drep_id": "drep1...", "verification_key", "verification_key_hash"}; committee methods return {"id": "cc_cold1..." / "cc_hot1...", ...}.

HD wallet: one mnemonic, many sequential addresses.

create(network) -> dict # {"mnemonic", "stake_address", "addresses"}
from_mnemonic(mnemonic, network) -> dict
get_address(mnemonic, network, index=0) -> str # bech32
build(txplan_yaml, utxos, protocol_params, exec_units=None, additional_signers=0) -> dict
build_with(txplan_yaml, provider, sender, evaluator=None, additional_signers=0) -> dict

Both return {"tx_cbor": str, "tx_hash": str, "fee": str}.

  • build is fully offline: you describe the transaction as TxPlan YAML and supply the chain data yourself. UTXO selection, fee calculation, and change handling happen inside the native library. It never submits — sign the returned tx_cbor and submit with any HTTP client.
  • utxos is a list of CCL Utxo dicts: {"tx_hash", "output_index", "address", "amount": [{"unit", "quantity"}]}. unit is "lovelace" or policyId + assetNameHex. Pass quantities as strings ("quantity": "100000000"); Python ints are arbitrary precision, so reading values back (e.g. int(result["fee"])) is always exact.
  • protocol_params is the CCL ProtocolParams dict; unknown fields are ignored.
  • exec_units — for Plutus transactions, [{"mem": ..., "steps": ...}], one entry per redeemer in transaction order. When omitted, the native library computes them offline with the embedded Scalus evaluator.
  • additional_signers budgets vkey witnesses for fee estimation, beyond those the input UTXOs imply (one per sender). You know how many keys will sign: 0 for a plain payment, 1 for a stake or DRep certificate (payment+stake signing), 2 for both in one tx, the number of sig keys for a native-script spend, plus one per plan-level required signer. Undercounting yields a fee the node rejects with FeeTooSmallUTxO; overcounting only overpays (~4,400 lovelace per extra witness).
  • build_with fetches UTXOs and protocol parameters from a provider, then builds. With an evaluator it runs two passes: draft build → remote evaluation → rebuild with the returned units.
result = lib.quicktx.build(yaml, utxos, params) # plain payment: 0 extra signers
stake_result = lib.quicktx.build(yaml, utxos, params,
additional_signers=1) # payment+stake signing
plutus_result = lib.quicktx.build(yaml, utxos, params,
exec_units=[{"mem": 2000000, "steps": 500000000}])