Skip to main content

Axiomatic ProofKit (Python) is the client SDK for building and publishing p1 Proof-of-Valuation (PoVal) attestations on Algorand.

The goal is a simple, trust-minimized flow:

  1. Build a canonical p1 payload (JCS/ACJ-style JSON).
  2. Publish it on-chain as the note of a 0-ALGO self-transaction.
  3. Keep signing and key management on the client side.
  4. Allow anyone to independently recompute and verify what was published.

This SDK does not depend on any Axiomatic backend.


Who is this for?

Use axiomatic_proofkit if you:

  • Build Python back-ends that need to issue PoVal attestations on Algorand.
  • Integrate model-based valuations into RWA / tokenization / lending flows.
  • Want deterministic, auditable p1 payloads aligned with the Node SDKs.

If you only need to verify on-chain notes, see axiomatic_verifier.


Typical use cases

  1. Real estate tokenization

    • Compute a fair value v and interval u for a property.
    • Build a p1 attestation with axiomatic_proofkit.
    • Anchor the PoVal proof on Algorand TestNet/MainNet as a 0-ALGO self-tx.
  2. On-chain collateral / lending

    • Issue PoVal attestations for assets posted as collateral.
    • Use the on-chain note + txid as input for your credit/risk policies.
  3. Back-office & audit tooling

    • Periodically issue updated valuations.
    • Keep a verifiable trail of model versions, hashes and timestamps.

Installation

Requires Python 3.10+.

pip install axiomatic_proofkit
pip install py-algorand-sdk

Exposed API

From axiomatic_proofkit:

  • build_p1(...)
  • canonical_note_bytes_p1(p1)
  • assert_note_size_ok(p1, max_bytes=...)
  • build_canonical_input(raw_input, allowed_keys=...)
  • compute_input_hash(canonical_input, allowed_keys=...)
  • publish_p1(p1, from_addr=..., sign=..., network=..., algod=..., wait_rounds=...)
  • PublishError

These are the only functions you need for typical integrations.


p1 structure

build_p1 returns a dict of the form:

p1 = {
    "s": "p1",          # schema marker
    "a": "re:EUR",      # asset tag (e.g. "re:EUR")
    "mv": "v2",         # model version
    "mh": "",           # model hash (hex, optional)
    "ih": "...",        # input hash (hex, required)
    "v": 550000.0,      # point estimate (e.g. value)
    "u": [520000.0, 580000.0],  # uncertainty range [low, high]
    "ts": 1762609210,   # unix epoch seconds
}

The object is normalized and serialized using a JCS/ACJ-style canonical JSON encoder for stable hashing and cross-language parity.


Quickstart: publish a p1 to Algorand TestNet

The following example mirrors the internal smoke test used to validate the SDK.

1. Environment

Create a .env file next to your script:

ALGORAND_MNEMONIC=your 25-word mnemonic for a TestNet account
ALGORAND_NETWORK=testnet
# Optional: override default node
# ALGOD_URL=https://testnet-api.algonode.cloud

For production, use a secure signing setup (wallet, KMS, HSM, etc.). This example uses a mnemonic only for demonstration.

2. Example script (publish_p1_example.py)

import os
import sys
import json
import base64
from pathlib import Path

from algosdk import account, mnemonic, encoding, transaction
from algosdk.v2client.algod import AlgodClient

from axiomatic_proofkit import (
    build_p1,
    canonical_note_bytes_p1,
    assert_note_size_ok,
    build_canonical_input,
    compute_input_hash,
    publish_p1,
)

ROOT = Path(__file__).resolve().parent


def load_env_from_file(env_path: Path) -> None:
    try:
        for line in env_path.read_text(encoding="utf-8").splitlines():
            if not line or line.strip().startswith("#") or "=" not in line:
                continue
            k, v = line.split("=", 1)
            k, v = k.strip(), v.strip()
            if k and (k not in os.environ):
                os.environ[k] = v
    except FileNotFoundError:
        pass


load_env_from_file(ROOT / ".env")

MNEMONIC = os.getenv("ALGORAND_MNEMONIC")
NETWORK = (os.getenv("ALGORAND_NETWORK") or "testnet").strip()
ALGOD_URL = (
    os.getenv("ALGOD_URL")
    or (
        "https://mainnet-api.algonode.cloud"
        if NETWORK == "mainnet"
        else "https://testnet-api.algonode.cloud"
    )
).strip()

if not MNEMONIC:
    print("❌ ALGORAND_MNEMONIC missing.", file=sys.stderr)
    sys.exit(1)


def compute_input_hash_from_golden() -> str:
    """
    If golden/p1_input.json exists, compute its canonical ACJ/JCS hash.
    Otherwise return a deterministic placeholder for the smoke test.
    """
    golden_dir = ROOT / "golden"
    inp_path = golden_dir / "p1_input.json"

    if not inp_path.is_file():
        return "0" * 64

    try:
        raw = json.loads(inp_path.read_text(encoding="utf-8"))
    except Exception as e:
        print(f"⚠️ Unable to read golden/p1_input.json: {e}")
        return "0" * 64

    allowed_keys = list(raw.keys())
    cin = build_canonical_input(raw, allowed_keys=allowed_keys)
    ih = compute_input_hash(cin, allowed_keys=allowed_keys)
    print(f"✅ Golden input hash: {ih}")
    return ih


def main() -> None:
    # 1) Derive private key and address from mnemonic (demo only)
    sk = mnemonic.to_private_key(MNEMONIC)
    addr = account.address_from_private_key(sk)

    # 2) Compute input hash (from golden file if available)
    ih = compute_input_hash_from_golden()

    # 3) Build canonical p1
    p1 = build_p1(
        asset_tag="re:EUR",
        model_version="v2",
        model_hash_hex="",
        input_hash_hex=ih,
        value_eur=550_000,
        uncertainty_low_eur=520_000,
        uncertainty_high_eur=580_000,
        timestamp_epoch=None,  # use "now"
    )

    # 4) Inspect note size and hash
    note_bytes, note_sha, note_len = canonical_note_bytes_p1(p1)
    assert note_bytes is not None  # just to show it's a bytes-like object
    assert_note_size_ok(p1)
    print(f"P1 note size: {note_len} bytes | sha256: {note_sha}")

    # 5) Client-side signer: keep your keys local
    def sign(unsigned_bytes: bytes) -> bytes:
        """
        unsigned_bytes: msgpack-encoded UnsignedTransaction.
        Return: msgpack bytes of the SignedTransaction.
        """
        tx_dict = encoding.msgpack.unpackb(unsigned_bytes)
        txn = transaction.Transaction.undictify(tx_dict)
        stx = txn.sign(sk)
        stx_b64 = encoding.msgpack_encode(stx)   # base64(msgpack signed)
        return base64.b64decode(stx_b64)         # raw msgpack bytes

    # 6) Algod client (Algonode by default)
    algod = AlgodClient("", ALGOD_URL)

    # 7) Publish p1 as a 0-ALGO self-transaction note
    res = publish_p1(
        p1,
        network=NETWORK,
        algod=algod,
        from_addr=addr,
        sign=sign,
        wait_rounds=4,
    )

    print("PUBLISHED:")
    print(json.dumps(res, indent=2))


if __name__ == "__main__":
    main()

Run:

python publish_p1_example.py

You should see a JSON result with:

  • txid
  • explorer_url
  • note_sha256
  • note_size
  • network

You can open the explorer URL and inspect the on-chain note.


Publish → verify (end-to-end)

To verify a published p1 attestation from Python, use axiomatic_verifier:

from axiomatic_verifier import verify_tx

res = verify_tx(
    txid="YOUR_TXID",
    network="testnet",
    # or your own indexer:
    # indexer_url="https://testnet-idx.algonode.cloud",
    max_skew_past_sec=3600,
    max_skew_future_sec=300,
)

print(res["verified"], res.get("reason"))

This checks:

  • that the note is structurally valid,
  • that canonicalization and sha256 match,
  • and that the timestamp ts is within your allowed time window.

Security notes

  • The sign function is fully controlled by you.

  • For real integrations, replace inline mnemonic signing with:

    • wallet connectors,
    • custodial services,
    • HSM/KMS or cloud KMS.
  • The SDK never sends your secrets anywhere and does not rely on Axiomatic servers.

This is an early-access SDK: feedback and issues are very welcome.

Metadata

Release files for axiomatic-proofkit 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for axiomatic-proofkit 0.1.4
File Size Uploaded
axiomatic_proofkit-0.1.4.tar.gz 7.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for axiomatic-proofkit 0.1.4
File Interpreter ABI Platform
axiomatic_proofkit-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 16.3 kB

Release files / axiomatic_proofkit-0.1.4.tar.gz

Download URL axiomatic_proofkit-0.1.4.tar.gz
Size 7.7 kB
Tags Source
SHA-256 checksum
How to use checksums
e33c48a552c7796552fee7cb9f550efa89c4fb6ad7ec24ba9b6be3378a9a52dd
BLAKE2b-256 checksum
How to use checksums
a667b1a62bf00f37cfb0a16a7a482aca3054dd3c7938d690e7e7628d567e9762
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / axiomatic_proofkit-0.1.4-py3-none-any.whl

Download URL axiomatic_proofkit-0.1.4-py3-none-any.whl
Size 8.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
919f1cc149dafa0f2a609e2719ca0f400f1b2d19f47119eda4cd978236e20275
BLAKE2b-256 checksum
How to use checksums
4f21c345cefa1527f1bb0fc01d06016f979d7728f70cf11188df477db3c43f68
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page