All skills

Guide for writing Python code with AlgoKit Utils (`algokit-utils`). Use this skill whenever the user is building on Algorand with Python — client setup, account management, payments, ASA operations, atomic transaction groups, smart contract deployment and interaction (AppFactory, AppClient, ARC-56/ARC-32 specs), raw app calls, TEAL compilation, key registration, network management, error handling, and the low-level crypto primitives in `algokit_crypto` (Ed25519 keygen/signing/verification, Peikert xHD BIP44 wallets, wrapped-secret patterns) and `algokit_common` (`sha512_256`). Trigger on imports from `algokit_utils` or `algokit_crypto`, references to `AlgorandClient`, `AppFactory`, `AppClient`, `AlgoAmount`, `ed25519_generator`, `peikert_hd_wallet_generator`, `sha512_256`, `WrappedEd25519Seed`, or `RawEd25519Signer`, or any Python code that builds on Algorand.

Use this Skill: https://skilld.dev/gh/algorand-devrel/algorand-agent-skills/algokit-utils-py

This session only. Nothing lands on disk.

referencesed25519.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Ed25519 keys, signing, and verification

All snippets below import from algokit_crypto, the low-level cryptographic package that ships alongside algokit-utils. This is the layer under AccountManager — reach for it when you are wiring up a custom signer, implementing protocol helpers, or verifying signatures produced by algokit-utils.

Generate a random Ed25519 keypair

Create a fresh Ed25519 keypair along with a raw signer function bound to it.

from algokit_crypto import ed25519_generator

keypair = ed25519_generator()
pubkey = keypair["ed25519_pubkey"]
secret_key = keypair["ed25519_secret_key"]
raw_signer = keypair["raw_ed25519_signer"]

print(len(pubkey))      # 32
print(len(secret_key))  # 32

What just happened: ed25519_generator() drew a cryptographically random 32-byte secret key via PyNaCl (libsodium), derived its 32-byte public key, and returned an Ed25519Keypair TypedDict alongside a raw_ed25519_signer closure that already captures the secret key. Any call to raw_ed25519_signer(bytes) will produce an Ed25519 signature over those bytes. The default export is currently an alias for pynacl_ed25519_generator — import pynacl_ed25519_generator directly if you want to pin the backend explicitly.

Generate a deterministic Ed25519 keypair from a seed

Pass a 32-byte seed to produce the same keypair every time.

from algokit_crypto import ed25519_generator

seed = bytes(32)  # 32 zero bytes — replace with real seed material

keypair = ed25519_generator(seed)
print(len(keypair["ed25519_pubkey"]))  # 32

What just happened: When you supply a 32-byte seed, PyNaCl uses it directly as the Ed25519 secret key — no extra hashing — so the resulting keypair is fully deterministic in the seed. A seed of any other length raises ValueError with the message Seed must be 32 bytes for ed25519 key generation. Useful for tests and for reproducing keys from a known source; never reuse a seed across unrelated identities.

Sign bytes with the generated signer

Call the returned raw_ed25519_signer to produce a signature over arbitrary bytes.

from algokit_crypto import ed25519_generator

keypair = ed25519_generator()
message = b"Hello, Algorand!"
signature = keypair["raw_ed25519_signer"](message)

print(len(signature))  # 64

What just happened: raw_ed25519_signer implements the RawEd25519Signer contract — a synchronous Callable[[bytes], bytes] that returns a 64-byte Ed25519 signature. It signs exactly the bytes you pass in, with no added domain separator. When signing Algorand transactions, logic sigs, or program data, let AccountManager or generate_address_with_signers handle the Algorand prefix (TX, Program, ProgData, MX) for you — the raw signer is deliberately agnostic to domain separation.

Build a custom RawEd25519Signer

Implement the RawEd25519Signer type to plug any external signing source into AlgoKit.

import httpx

from algokit_crypto import RawEd25519Signer

def custom_signer(bytes_to_sign: bytes) -> bytes:
    # Forward the bytes to a hardware wallet, KMS, HSM, or remote service
    response = httpx.post("https://my-signer.example.com/sign", content=bytes_to_sign)
    return response.content

signer: RawEd25519Signer = custom_signer

What just happened: RawEd25519Signer is simply Callable[[bytes], bytes]. Anything that satisfies that shape is accepted throughout the AlgoKit stack — AccountManager.set_signer and generate_address_with_signers combine it with a public key (via Ed25519SigningKey) to build full Algorand signers. The signer is expected to produce a raw 64-byte Ed25519 signature over the bytes it receives; any domain separation must be applied by the caller before invocation.

Construct an Ed25519SigningKey manually

Assemble an Ed25519SigningKey TypedDict from an existing public key and signer.

from algokit_crypto import Ed25519SigningKey, RawEd25519Signer

pubkey: bytes = ...          # 32 bytes from your key store
raw_signer: RawEd25519Signer = ...  # e.g. a hardware wallet wrapper

signing_key: Ed25519SigningKey = {
    "ed25519_pubkey": pubkey,
    "raw_ed25519_signer": raw_signer,
}

What just happened: Ed25519SigningKey is a minimal TypedDict — just the 32-byte public key and a RawEd25519Signer. Every AlgoKit helper that accepts a "signing key" takes this shape, which is why third-party key sources (hardware wallets, KMS, browser extensions) can be integrated without any extra adapter layer: expose a public key and a sign function, bundle them in a dict, and pass it on.

Verify an Ed25519 signature

Check whether a 64-byte signature is valid for the given message and public key.

from algokit_crypto import ed25519_generator, ed25519_verifier

keypair = ed25519_generator()
message = b"Hello, Algorand!"
signature = keypair["raw_ed25519_signer"](message)

is_valid = ed25519_verifier(signature, message, keypair["ed25519_pubkey"])
print(is_valid)  # True

What just happened: ed25519_verifier(signature, message, pubkey) returns a bool. Internally it delegates to PyNaCl's VerifyKey.verify and returns True only when the signature is a valid Ed25519 signature for exactly the message bytes you pass in under the given public key. A bad signature is caught as nacl.exceptions.BadSignatureError and converted into False — flipping a single bit in any argument causes it to return False rather than raise.

Verify a signature produced by the Algorand signing path

When the bytes you want to check came from a higher-level Algorand signer, verify against the same domain-prefixed bytes the signer saw.

from algokit_crypto import ed25519_generator, ed25519_verifier
from algokit_transact import generate_address_with_signers

keypair = ed25519_generator()
signers = generate_address_with_signers(
    keypair["ed25519_pubkey"],
    keypair["raw_ed25519_signer"],
)

message = b"Hello, Algorand!"
signature = signers.mx_bytes_signer(message)

# The signer prefixed `MX` before signing — feed the verifier the same bytes
signed_bytes = b"MX" + message
is_valid = ed25519_verifier(signature, signed_bytes, keypair["ed25519_pubkey"])
# The "raw" message would not verify, because mx_bytes_signer adds a domain prefix
is_raw_valid = ed25519_verifier(signature, message, keypair["ed25519_pubkey"])

print(is_valid, is_raw_valid)  # True False

What just happened: Every Algorand signer returned from generate_address_with_signers — transaction signer, delegated lsig signer, program-data signer, mx-bytes signer — applies a domain-separation prefix before signing. When verifying a signature that came out of one of those higher-level signers, reproduce the exact bytes that were signed (for mx_bytes_signer that is b"MX" + data). Verifying against the raw user message will return False.

Use the pinned PyNaCl backend explicitly

Import the pynacl_-prefixed variants when you want a guaranteed implementation.

from algokit_crypto import (
    RawEd25519Signer,
    RawEd25519Verifier,
    pynacl_ed25519_generator,
    pynacl_ed25519_verifier,
)

keypair = pynacl_ed25519_generator()
signer: RawEd25519Signer = keypair["raw_ed25519_signer"]
verify: RawEd25519Verifier = pynacl_ed25519_verifier

What just happened: pynacl_ed25519_generator and pynacl_ed25519_verifier are the current concrete backings for ed25519_generator and ed25519_verifier. The package documents that the default exports may change in future versions — if your code depends on specific behaviour of the PyNaCl (libsodium) implementation (constant-time guarantees, exact error messages, etc.) import the pynacl_* symbol directly so future default-repointing cannot break you.

Source: SKILL.md on GitHub

1 warning1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    The skill is a technical reference guide for the Algorand Python SDK (AlgoKit Utils). It promotes secure coding practices, including environment-based secret management and memory-safe cryptographic patterns for handling private keys. No malicious patterns or security risks were identified.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: MEDIUM · 2 issues

Signed by skilld at 4235fb8. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 6 months ago

README badge

README badge for algorand-devrel/algorand-agent-skills/algokit-utils-py