Key Principle
A token is an ownable abstraction representing assets, currency, or access rights — but on Ethereum a token is not an asset the recipient holds; it is a row in someone else's contract. "Sending ether is an intrinsic action of the Ethereum platform, but sending or even owning tokens is not." Tokens live at the contract level, not the protocol level — which is the entire reason standards exist, and why reusing mature implementations is the security imperative.
Why This Matters
The protocol "does not know anything about" tokens, so every token reimplements its own logic and interoperability depends on shared conventions. The abstraction gap (token vs. ether, message vs. transaction, EOA vs. contract) is what makes tokens lose-able, and no protocol change fully closes it — only defensive UI and later standards paper over it.
Good Examples
Token definition and the 10 uses
Currency is "just the first app." Taxonomy of uses: currency, resource, asset, access, equity, voting, collectible, identity, attestation, utility. Functions overlap and one token may bundle several — physical tokens commingled functions inseparably (a driver's license is attestation and identity); digital tokens can decouple previously linked functions and develop them independently. That decoupling is the design opportunity.
Fungibility vs. non-fungibility (provenance erodes fungibility)
Fungible = units interchangeable without difference in value/function. It is not binary: "Strictly speaking, if a token's historical provenance can be tracked, then it is not entirely fungible." On a transparent ledger, traceability silently enables blacklisting/whitelisting, eroding fungibility you may have assumed. Non-fungible = each token is a unique item with a unique identifier (a Van Gogh token ≠ a Picasso token; one CryptoKitty ≠ another). Note: "fungible" here means interchangeable, NOT directly cashable. [DATED 2018: NFTs were nascent — ERC-721 finalized after this writing; the NFT boom postdates the book.]
Counterparty risk / intrinsic vs. extrinsic
- Counterparty risk: the risk the other party fails to meet their obligation. When a token represents an off-chain asset, the on-chain transfer is only as good as the custodian's willingness and ability to honor it (e.g. a precious-metal certificate of deposit = seller + buyer + custodian, ≥3 parties).
- Intrinsic token is the asset — consensus rules + your keys enforce ownership with no intermediary, so it "does not carry additional counterparty risk." "If you hold the keys for a CryptoKitty, there is no other party holding that CryptoKitty for you — you own it directly."
- Extrinsic token is a claim enforced by "law, custom, and policy, separate from the consensus rules." Tokenizing an extrinsic asset does not erase counterparty risk — the failure mode is treating an extrinsic token as if it were intrinsic. The one real upside: converting genuinely extrinsic value to intrinsic removes the intermediary (corporate equity → DAO voting token).
Utility vs. security ("it's a duck" regulatory trap)
Utility token = "use of the token is required to gain access to a service, application, or resource." Equity/security token = "shares in the control or ownership of something," a regulated activity. Startups relabel equity offerings as "pre-sale of service access vouchers" to dodge regulation, but "If it walks like a duck and quacks like a duck, it's a duck" — regulators judge substance over label, and the relabeling reads as deliberate deception. Adoption test: adopt a token only if the application cannot work without it or it lifts a fundamental market barrier — never to "raise money fast." A utility token multiplies risk (platform + economy + regulators + technology), and single-platform tokens recreate worthless company scrip. [DATED 2018: securities/Howey framing has since evolved through extensive SEC enforcement and case law; treat specific regulatory posture as superseded.]
ERC-20 interface and workflows
ERC-20 (Fabian Vogelsteller, Nov 2015, GitHub issue #20 → EIP-20) standardizes fungible tokens. Interface:
contract ERC20 {
function totalSupply() constant returns (uint theTotalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}Optional metadata: name, symbol, decimals. Two mappings are the root cause of everything: mapping(address => uint256) balances; and mapping(address => mapping(address => uint256)) allowed;. "Owning" a token is just being a key in that map.
transfer(to, value)— direct wallet-to-wallet; the common case.approve+transferFrom— delegated spending so a contract can pull the owner's tokens (the ICO/exchange pattern). It exists precisely because tokens can't be "sent to" a contract safely.
decimals UX trap and flaws
decimalschanges only display, not storage. METoken has 2 decimals, so transferring 1,000 MET means passing100000— a mismatch silently moves 100× the intended amount.- Tokens to non-aware contracts are lost forever: send ERC-20 via
transferto a contract with no token-receiving logic and "There is no solution to this problem. The MET sent to Faucet is stuck, forever." Estimated >$2.5M USD lost this way (exchanges publishing ether-only addresses as token "deposit" addresses). Fix in the book: pull via delegation — METFaucet rejects ether (function () public payable { revert(); }) and the ownerapproves it, thenwithdrawcallstransferFrom. - Tokens need ether for gas: the token contract can't pay for you, so "owning" a token without ether means you can't move it. "the burden of managing ERC20 tokens correctly is pushed to the user interface."
ERC-20 successors
- ERC-223 — receiving contracts must implement
tokenFallback;transferto a contract lacking it fails. Detects contract-ness via assemblyextcodesize(_addr) > 0. "Not widely implemented." - ERC-777 — ERC20-compatible;
sendlike ether;tokensToSend/tokensReceivedhooks (registered via ERC-820); recipient contracts must implementtokensReceivedor the transfer reverts (prevents locking); operators, explicit mint/burn events. Cost: bundles two new standards at once.[DATED 2018: ERC-777's reentrancy hooks proved a security liability and adoption stalled; ERC-20-with-better-UX stayed dominant; later standards ERC-1363 (transfer-and-call), ERC-4626 (tokenized vaults).]
ERC-721 (deeds → NFT)
One inverted mapping defines the standard: ERC-20 maps owner → balance (interchangeable), ERC-721 maps deedID → owner (mapping (uint256 => address) private deedOwner;), making the 256-bit ID the primary key. "From this basic difference flow all the properties of a non-fungible token." Represents specific unique things (a house, artwork, in-game item, or negative-value obligations like loans/liens). supportsInterface / is ERC165 provides runtime interface detection; optional ERC721Metadata and ERC721Enumerable. [DATED 2018: finalized as the NFT standard, universally called "NFT" not "deed"; standard names are tokenURI, tokenByIndex, etc.; the NFT boom (2021+) postdates the book.]
Standards are descriptive, and security-by-maturity
A standard is the minimum spec for compliance — the external behavior you must implement — and is descriptive, not prescriptive (governs observable behavior, never internal implementation). Deploy an ERC-20-compliant token and every existing wallet trades it "without any wallet upgrade or effort on your part." Token contracts are immutable and hold real value, so reuse battle-tested implementations: "there are many subtle ways that a contract can be compromised." The book uses OpenZeppelin's ERC20, "security-focused from the ground up." Extending a standard "represents a trade-off between innovation/risk and interoperability/security" — owner control, burning, minting, crowdfunding, caps, recovery backdoors, whitelisting, blacklisting each add attack surface.
Counterpoints
- "Complexity is the enemy of security. Every single line of code you add expands the attack surface of your contract and could represent a vulnerability lying in wait." Every extension is a power and therefore a liability.
- Many live tokens "are barely disguised scams, pyramid schemes, and money grabs"; separate durable tech from the bubble.
[DATED 2018: ICO bubble burst post-2018.] [DATED 2018: pragma ^0.4.21 + manual SafeMath via OpenZeppelin — Solidity 0.8+ has built-in overflow checks; named-function constructors removed (use constructor()); constant on functions → view; Ropsten/Kovan retired, use Sepolia/Holesky.]
Key Quotes
"Strictly speaking, if a token's historical provenance can be tracked, then it is not entirely fungible." — Antonopoulos & Wood, Chapter 10
"If it walks like a duck and quacks like a duck, it's a duck." — Antonopoulos & Wood, Chapter 10
"There is no solution to this problem. The MET sent to Faucet is stuck, forever." — Antonopoulos & Wood, Chapter 10
"Complexity is the enemy of security. Every single line of code you add expands the attack surface of your contract and could represent a vulnerability lying in wait." — Antonopoulos & Wood, Chapter 10
Rules of Thumb
- Tokens live in contract state, not the protocol — follow a standard so every wallet interoperates.
- Tokenizing an extrinsic asset does not remove counterparty risk; know whether your token is intrinsic.
- Adopt a token only if the app can't work without it or it lifts a real market barrier — never to raise money fast.
decimalsis display-only — always pass amounts in smallest units.- Never
transferERC-20 to a contract with no token-receiving logic; use approve + transferFrom (pull). - Recipients need ether for gas to move tokens.
- Reuse OpenZeppelin; every extension you bolt on adds attack surface.
Related References
- Smart Contract Vulnerability Catalog - approve race, stuck tokens, arithmetic flaws in token contracts
- Secure Design Patterns - security-by-maturity, pull-over-push, code reuse
- Solidity Essentials — Writing Immutable, Self-Owning Contracts - mappings, events, interfaces, decimals
- Ethereum Core Framework — The World Computer & Power-as-Liability - intrinsic vs imported trust, contract vs protocol level
- Rules of Thumb — Heuristics Across All Chapters - consolidated heuristics