Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ suppress-none-returning = true
"tests/clients/test_algorand_client.py" = ["ERA001"]
"src/algokit_utils/_legacy_v2/**/*" = ["E501"]
"tests/**/*" = ["PLR2004"]
"packages/*/src/*/client.py" = ["C901", "PLR0912", "PLR0913"]
"packages/algokit-transact/src/algokit_transact/__init__.py" = ["RUF022"]
"src/algokit_transact/__init__.py" = ["RUF022"]
"src/algokit_utils/__init__.py" = [
"I001",
"RUF022",
Expand Down
39 changes: 39 additions & 0 deletions src/algokit_common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from algokit_common.address import address_from_public_key, public_key_from_address
from algokit_common.constants import * # noqa: F403
from algokit_common.hashing import base32_nopad_decode, base32_nopad_encode, sha512_256
from algokit_common.serde import (
DecodeError,
EncodeError,
addr,
addr_seq,
bytes_seq,
enum_value,
flatten,
from_wire,
int_seq,
nested,
to_wire,
to_wire_canonical,
wire,
)

__all__ = [
"DecodeError",
"EncodeError",
"addr",
"addr_seq",
"address_from_public_key",
"base32_nopad_decode",
"base32_nopad_encode",
"bytes_seq",
"enum_value",
"flatten",
"from_wire",
"int_seq",
"nested",
"public_key_from_address",
"sha512_256",
"to_wire",
"to_wire_canonical",
"wire",
]
23 changes: 23 additions & 0 deletions src/algokit_common/address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from algokit_common.constants import CHECKSUM_BYTE_LENGTH, PUBLIC_KEY_BYTE_LENGTH
from algokit_common.hashing import base32_nopad_decode, base32_nopad_encode, sha512_256


def public_key_from_address(address: str) -> bytes:
if not isinstance(address, str):
raise TypeError("address must be str")
raw = base32_nopad_decode(address)
if len(raw) != PUBLIC_KEY_BYTE_LENGTH + CHECKSUM_BYTE_LENGTH:
raise ValueError("invalid address length")
pk = raw[:PUBLIC_KEY_BYTE_LENGTH]
checksum = raw[PUBLIC_KEY_BYTE_LENGTH:]
expected = sha512_256(pk)[-CHECKSUM_BYTE_LENGTH:]
if checksum != expected:
raise ValueError("invalid address checksum")
return pk


def address_from_public_key(public_key: bytes) -> str:
if len(public_key) != PUBLIC_KEY_BYTE_LENGTH:
raise ValueError("invalid public key length")
checksum = sha512_256(public_key)[-CHECKSUM_BYTE_LENGTH:]
return base32_nopad_encode(public_key + checksum)
39 changes: 39 additions & 0 deletions src/algokit_common/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
TRANSACTION_DOMAIN_SEPARATOR = "TX"
TRANSACTION_GROUP_DOMAIN_SEPARATOR = "TG"
MULTISIG_DOMAIN_SEPARATOR = "MultisigAddr"
SIGNATURE_ENCODING_INCR = 75
HASH_BYTES_LENGTH = 32
PUBLIC_KEY_BYTE_LENGTH = 32
MAX_TX_GROUP_SIZE = 16
CHECKSUM_BYTE_LENGTH = 4
ADDRESS_LENGTH = 58
TRANSACTION_ID_LENGTH = 52
ZERO_ADDRESS = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
LENGTH_ENCODE_BYTE_SIZE = 2
BOOL_TRUE_BYTE = 0x80
BOOL_FALSE_BYTE = 0x00
SIGNATURE_BYTE_LENGTH = 64
EMPTY_SIGNATURE = bytes(SIGNATURE_BYTE_LENGTH)

# Application program size constraints
MAX_EXTRA_PROGRAM_PAGES = 3
PROGRAM_PAGE_SIZE = 2048 # In bytes

# Application reference limits
MAX_APP_ARGS = 16
MAX_ARGS_SIZE = 2048 # Maximum size in bytes of all args combined
MAX_OVERALL_REFERENCES = 8
MAX_ACCOUNT_REFERENCES = 8
MAX_APP_REFERENCES = 8
MAX_ASSET_REFERENCES = 8
MAX_BOX_REFERENCES = 8

# Application state schema limits
MAX_GLOBAL_STATE_KEYS = 64
MAX_LOCAL_STATE_KEYS = 16

# Asset configuration limits
MAX_ASSET_NAME_LENGTH = 32 # In bytes
MAX_ASSET_UNIT_NAME_LENGTH = 8 # In bytes
MAX_ASSET_URL_LENGTH = 96 # In bytes
MAX_ASSET_DECIMALS = 19
25 changes: 25 additions & 0 deletions src/algokit_common/hashing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Final

from Cryptodome.Hash import SHA512


def sha512_256(data: bytes) -> bytes:
ch = SHA512.new(truncate="256")
ch.update(data)
return ch.digest()


BASE32_ALPHABET_NO_PAD: Final[bytes] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"


def base32_nopad_encode(data: bytes) -> str:
import base64

return base64.b32encode(data).decode().rstrip("=")


def base32_nopad_decode(text: str) -> bytes:
import base64

pad_len = (-len(text)) % 8
return base64.b32decode(text + ("=" * pad_len))
40 changes: 40 additions & 0 deletions src/algokit_common/serde/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Shared serialization helpers for AlgoKit dataclasses.

The implementation mirrors the serde utilities that previously lived in
``algokit_transact`` so that auto-generated API clients can share the same
wire-format logic.
"""

from algokit_common.serde._core import (
DecodeError,
EncodeError,
addr,
addr_seq,
bytes_seq,
enum_value,
flatten,
from_wire,
int_seq,
nested,
sort_msgpack_value,
to_wire,
to_wire_canonical,
wire,
)

__all__ = [
"DecodeError",
"EncodeError",
"addr",
"addr_seq",
"bytes_seq",
"enum_value",
"flatten",
"from_wire",
"int_seq",
"nested",
"sort_msgpack_value",
"to_wire",
"to_wire_canonical",
"wire",
]
Loading
Loading