SDK Types
Beyond decorators and the base panda module, the SDK ships a few first-class types that make contracts read cleanly. This page covers three: the Model handle, the panda.staking primitive, and the panda.crypto module. For the full decorator/API reference, see the SDK Reference.
panda.model.Model
A first-class model handle. Instead of passing raw address strings around, hold a typed Model in contract state and call polymorphic methods on it. It serializes to its address, so it costs the same one hop as a raw cross-contract call.
from panda.model import Model
m = Model("0xABC...")
m.address # the on-chain address
m.kind # "paid" | "tokenized" | "free" | "empty" | "unknown"
m.infer(inputs) # primary inference call (polymorphic)
m.predict(inputs)# alias of infer
Polymorphic inference
infer() dispatches to the right underlying method based on the model's declared kind:
| kind | inference method |
|---|---|
paid | predict |
tokenized | inference |
free | predict |
The polymorphism lives in the SDK — there is no wrapper contract, so the call cost is identical to panda.call(...).
Sub-types
Helpers are grouped under dot-notation sub-types so the surface stays discoverable:
# Metadata
m.meta.accuracy # float, 0.0 - 1.0 (from accuracy_bps)
m.meta.version # int
m.meta.is_ready # bool
m.meta.raw() # full metadata dict (fresh fetch)
# Rent (for models that meter access over time)
m.rent.pay(amount) # top up the rent reserve -> calls pay_rent
m.rent.status() # { blocks_remaining, ... } -> calls rent_status
m.rent.due # bool -- True if rent is past due
# Funds (prepaid inference credits)
m.funds.fund(amount) # add credits -> "fund" (paid) or "buy" (tokenized)
m.funds.balance # int -> "get_balance" / "get_holdings"
funds dispatches per kind: paid models use fund/get_balance, tokenized models use buy/get_holdings.
Discovery
Class-level helpers enumerate models the chain knows about (today the union of ModelExchange listings and the paid-model registry):
Model.find.all() # list[Model]
Model.find.by_kind("paid") # list[Model] filtered by kind
Persistence
PandaVM recognizes __panda_serialize__ / __panda_deserialize__, so a Model stored in contract state persists as its address string and rebuilds lazily on load. Use Model.empty() for the zero-address placeholder.
The model marketplace contracts these handles wrap are documented in Model Marketplace Contracts.
panda.staking
The first-class staking primitive. Contracts import it to delegate, undelegate, claim, and read validator state. All operations route through the on-chain StakingRegistry system contract (see Staking Contracts).
panda.stakingis for in-VM contract code only — it uses the cross-contract call transport, which only works inside PandaVM or the test harness. Off-chain callers (wallets, scripts) use the RPC client instead.
Mutating helpers
Each forwards the caller ctx:
from panda import staking
delegation_id = staking.delegate(validator="0xVal", amount=1000, ctx=ctx) # -> str
record_id = staking.undelegate(validator="0xVal", amount=500, ctx=ctx) # -> str
released = staking.complete_unbonding(record_id=record_id, ctx=ctx) # -> int
claimed = staking.claim_rewards(validator="0xVal", ctx=ctx) # -> int
staking.register_validator(commission_bps=1000, moniker="v", self_delegation=5, ctx=ctx)
Read helpers
info = staking.validator("0xVal") # ValidatorInfo
mine = staking.my_delegations(ctx=ctx) # list[Delegation]
allv = staking.list_validators(active_only=True) # list[ValidatorInfo]
params = staking.get_staking_params() # StakingParams
pool = staking.get_rewards_pool() # int
rec = staking.get_unbonding_record(record_id)# UnbondingRecord
Data types
All are JSON-safe named tuples that mirror what the registry returns:
| Type | Fields |
|---|---|
ValidatorInfo | address, moniker, commission_bps, total_delegated, self_delegation, status, uptime_bps, registered_at_block |
Delegation | delegator, validator, amount, pending_rewards, last_claim_block |
UnbondingRecord | id, delegator, validator, amount, complete_after_block, completed |
StakingParams | unbonding_period_blocks, commission_max_bps, min_self_delegation, slashing_bps_dishonesty |
The registry address defaults to the well-known sentinel staking-registry; tests and custom deployments can override it with set_staking_registry_address(addr).
See Staking Contracts for the contracts these helpers drive, and Staking for the off-chain SDK flow.
panda.crypto
Deterministic cryptographic primitives. Every function is pure and deterministic, built only on Python's hashlib/hmac — safe to call inside metered contract execution.
Hashing
from panda import crypto
crypto.sha256(data) # hex string
crypto.sha256_bytes(data) # raw 32 bytes
crypto.sha3_256(data) # hex
crypto.keccak256(data) # EVM-compatible Keccak-256 (hex)
crypto.blake2b(data, digest_size=32)
crypto.double_sha256(data) # Bitcoin-style
crypto.hash160(data) # RIPEMD-160(SHA-256(data))
HMAC
crypto.hmac_sha256(key, msg) # hex
crypto.hmac_verify(key, msg, expected_mac) # bool, constant-time
Merkle trees
root = crypto.merkle_root(leaves)
proof = crypto.merkle_proof(leaves, index) # [{"hash", "position"}]
ok = crypto.merkle_verify(leaf, proof, root) # bool
Nodes are combined with a canonical smaller-hash-first ordering.
Commitments and key derivation
c = crypto.pedersen_commit(value, blinding) # H(value || 0x00 || blinding)
ok = crypto.pedersen_verify(c, value, blinding) # bool
dk = crypto.derive_key(master_key, path, rounds=100000) # PBKDF2-HMAC-SHA256
Address utilities
crypto.address_from_pubkey(pubkey_hex) # 0x + last 20 bytes of keccak256
crypto.checksum_address(address) # EIP-55 mixed-case checksum
Signature recovery (secp256k1 / ECDSA)
Deterministic, pure-Python secp256k1 signature recovery — verified against the
canonical go-ethereum recover vector, with EIP-2 high-s rejection for
malleability safety. Lets a contract authorize actions from an off-chain signer
(the same primitive Ethereum's ecrecover precompile provides).
# Recover the signer's address from an ECDSA signature.
# msg_hash : 32-byte message hash, hex ("0x…"); the caller pre-hashes.
# signature : 65-byte signature, hex — r || s || v (v may be 27/28 or 0/1).
# Returns the lowercase 0x address, or "" on any failure (bad input,
# non-canonical high-s, invalid point).
signer = crypto.ecrecover(msg_hash, signature)
# True iff the signature recovers to expected_address (case-insensitive).
ok = crypto.verify_signature(msg_hash, signature, expected_address)
Use it for meta-transactions, gasless approvals, or verifying an off-chain
oracle/attestation inside a @contract. Non-canonical (s > n/2) signatures are
rejected, and recovery is fully deterministic so every validator agrees.
See also
- SDK Reference — decorators,
Context, events, and the full base API - Staking Contracts and Model Marketplace Contracts
- Crypto & Security — patterns using
panda.crypto