A token, part 2: allowances

Letting a third party spend on your behalf — the mechanism every exchange and marketplace depends on, and its famous footgun.

transfer moves your own tokens. But a marketplace contract needs to move tokens *for* you, at a moment you are not the caller. That is what an allowance is: a standing permission, from an owner to a spender, for an amount.

def _k_allow(owner: bytes, spender: bytes) -> bytes:
    return b"tok:allow:" + owner + b":" + spender

Three functions:

  • approve(spender, amount) — the caller grants a spender an allowance
  • allowance(owner, spender) — read it
  • transfer_from(owner, to, amount) — the spender moves the owner's tokens, spending down their allowance

The approve race. Suppose you approved a spender for 100 and want to reduce it to 50. You send approve(spender, 50). A spender watching the mempool can try to spend the original 100 before your change lands, and then spend 50 more afterwards — 150 total against an allowance you never intended.

The mitigation is social and procedural, not clever code: set an allowance to zero first, confirm it, then set the new value. Some tokens add increase_allowance/decrease_allowance for this reason. This is a real, long-standing hazard in fungible-token design and it is worth knowing that the simple approve you are about to write has it.

transfer_from where caller == owner should still work without an allowance — you never need permission to move your own tokens. The Animica token standard handles exactly this case with a branch before the allowance check.

Your turn

Add approve, allowance and transfer_from. A spender must have enough allowance AND the owner enough balance; the allowance is spent down. An owner moving their own tokens needs no allowance.

Hints

Stuck? Ask

Answered by Animica's own free inference network. It is donated GPU capacity, so give it 20-30 seconds.