Numbers in a bytes-only store
Storage holds bytes, but contracts count things. The conversion pattern below is used by every Animica contract, including the token standard.
Storage holds bytes. Contracts count balances, supplies and votes. So every Animica contract contains some version of these two helpers:
def _uget(key: bytes) -> int:
raw = storage.get(key, b"")
if raw == b"":
return 0
return int.from_bytes(raw, "big")
def _uset(key: bytes, value: int) -> None:
if value == 0:
storage.delete(key)
return
storage.set(key, value.to_bytes(max(1, (value.bit_length() + 7) // 8), "big"))
Read it once and it stops being mysterious:
int.from_bytes(raw, "big")— big-endian, the network byte order used everywhere in Animica.(bit_length() + 7) // 8— the minimum number of bytes that can hold the value.max(1, ...)because a zero-bit number still needs one byte.- Zero deletes the key. Storing
b"\x00"and storing nothing would be two different byte strings meaning the same number, and two encodings of one value is how consensus bugs start. Deleting also refunds storage.
_uget returning 0 for a missing key is what lets a token contract say balance_of(anyone) without pre-creating an entry for every address on the chain. It is not an optimisation; it is the design.
Your turn
Implement a counter using the integer pattern. add(n) adds n and returns the new total; total() reads it. Storing zero must delete the key — check with is_empty().
Hints
One way to do it
from stdlib import storage
K = b"demo:total"
def _uget(key: bytes) -> int:
raw = storage.get(key, b"")
if raw == b"":
return 0
return int.from_bytes(raw, "big")
def _uset(key: bytes, value: int) -> None:
v = int(value)
if v == 0:
storage.delete(key)
return
storage.set(key, v.to_bytes(max(1, (v.bit_length() + 7) // 8), "big"))
def add(n: int) -> int:
_uset(K, _uget(K) + int(n))
return _uget(K)
def total() -> int:
return _uget(K)
def is_empty() -> bool:
return storage.get(K, b"") == b""
Claim your 10 ANM
Finish this lesson and claim 10 ANM, once per address. Paid from the Animica treasury in batches — allow a few minutes.
Stuck? Ask
Answered by Animica's own free inference network. It is donated GPU capacity, so give it 20-30 seconds.