Failing correctly: require and revert
A contract that accepts bad input corrupts its own state forever. abi.require is how you refuse.
abi.require(condition, message) aborts the whole call when the condition is false. Nothing is written, no events survive, and the caller gets the message.
from stdlib import abi
def withdraw(amount: int) -> None:
abi.require(amount > 0, b"amount_must_be_positive")
The message is bytes, and by convention it is a short machine-readable snake_case code rather than a sentence. Callers branch on it; humans read your docs.
abi.revert(message) aborts unconditionally — useful in an else branch that should be unreachable.
Reverting is normal, not exceptional. On-chain, a reverted call still costs the caller gas but changes no state. Your job is to revert *early*, before you have written anything — the VM will unwind either way, but a contract that validates first is far easier to reason about.
Your turn
Write a vault with a deposit limit. deposit(n) must reject non-positive amounts with b"must_be_positive", reject anything above 1000 with b"over_limit", and otherwise add to the balance.
Hints
One way to do it
from stdlib import abi, storage
K = b"vault:balance"
LIMIT = 1000
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)
if v == 0:
storage.delete(key)
return
storage.set(key, v.to_bytes(max(1, (v.bit_length() + 7) // 8), "big"))
def deposit(n: int) -> int:
amount = int(n)
abi.require(amount > 0, b"must_be_positive")
abi.require(amount <= LIMIT, b"over_limit")
_uset(K, _uget(K) + amount)
return _uget(K)
def balance() -> int:
return _uget(K)
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.