A token, part 1: balances and transfer
Mint, read a balance, move value between accounts. This is the core every fungible token shares.
A fungible token is a mapping from address to amount, plus rules about who may change it. You already have every piece: composite keys from the mappings lesson, the integer pattern, abi.caller() and abi.require.
Three functions make a usable token:
mint(to, amount)— bring new units into existence and add to total supplybalance_of(addr)— read anyone's balance, freetransfer(to, amount)— move the caller's own units to someone else
Total supply is stored, not computed. There is no way to iterate every key in storage and sum balances — storage has no such operation, and a contract that needed one would cost unbounded gas. Keep a running total and update it in the same place you change balances.
Deduct before you credit, and check before you deduct. If transfer credits the recipient before verifying the sender can afford it, a single bad input mints tokens out of nothing. Order: require, deduct, credit, emit.
Your turn
Implement mint, balance_of, transfer and total_supply. Transfer must move the caller's tokens, reject overdrafts with b"insufficient", and emit Transfer.
Hints
have >= amt and revert b"insufficient" BEFORE any _uset call.One way to do it
from stdlib import abi, events, storage
def _k_bal(addr: bytes) -> bytes:
return b"tok:bal:" + addr
def _uget(key: bytes) -> int:
raw = storage.get(key, b"")
return 0 if raw == b"" else int.from_bytes(raw, "big")
def _uset(key: bytes, value: int) -> None:
v = int(value)
abi.require(v >= 0, b"negative")
if v == 0:
storage.delete(key)
return
storage.set(key, v.to_bytes(max(1, (v.bit_length() + 7) // 8), "big"))
K_TOTAL = b"tok:total"
def mint(to: bytes, amount: int) -> int:
amt = int(amount)
abi.require(amt > 0, b"must_be_positive")
dest = bytes(to)
_uset(_k_bal(dest), _uget(_k_bal(dest)) + amt)
_uset(K_TOTAL, _uget(K_TOTAL) + amt)
events.emit(b"Mint", {"to": dest, "value": amt})
return _uget(K_TOTAL)
def balance_of(addr: bytes) -> int:
return _uget(_k_bal(bytes(addr)))
def total_supply() -> int:
return _uget(K_TOTAL)
def transfer(to: bytes, amount: int) -> int:
amt = int(amount)
abi.require(amt > 0, b"must_be_positive")
src = abi.caller()
dest = bytes(to)
have = _uget(_k_bal(src))
abi.require(have >= amt, b"insufficient")
_uset(_k_bal(src), have - amt)
_uset(_k_bal(dest), _uget(_k_bal(dest)) + amt)
events.emit(b"Transfer", {"from": src, "to": dest, "value": amt})
return _uget(_k_bal(src))
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.