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:

kindinference method
paidpredict
tokenizedinference
freepredict

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.staking is 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:

TypeFields
ValidatorInfoaddress, moniker, commission_bps, total_delegated, self_delegation, status, uptime_bps, registered_at_block
Delegationdelegator, validator, amount, pending_rewards, last_claim_block
UnbondingRecordid, delegator, validator, amount, complete_after_block, completed
StakingParamsunbonding_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