31. Implementing Token Standards
Token standards serve as the core foundational blueprint for asset issuance, financial primitives, and inter-contract interoperability across smart contract ecosystems. In the Kortana ecosystem, token standards are implemented using Quorlin—a high-level language designed to offer natural, English-like readable syntax while maintaining 100% ABI compatibility with the Ethereum Virtual Machine (EVM) ecosystem and executing natively on the Kortana Virtual Machine (KVM).
This chapter explores how token standards are constructed in Quorlin, how the Quorlin compiler processes standard interfaces, how types translate into canonical Ethereum ABIs, and how state changes are executed by the underlying KVM runtime.
31.1 Architecture of Token Standards in Quorlin
Quorlin handles smart contract interfaces and standard protocol compliance through built-in standard interface declarations and strict semantical type enforcement.
Standard Interfaces in the Compiler
In the compiler runtime (quorlin/standard.cpp), standard interfaces such as IERC20 are codified as static declarations. These declarations define the exact canonical signatures required for standard tokens.
The core IERC20 standard specification in the Quorlin compiler is declared as follows:
// Excerpt from quorlin/standard.cpp built.push_back(StandardInterface{ "IERC20", { {"totalSupply", "totalSupply()", {}, Type::U256, Mutability::View}, {"balanceOf", "balanceOf(address)", {Type::Address}, Type::U256, Mutability::View}, {"transfer", "transfer(address,uint256)", {Type::Address, Type::U256}, Type::Bool, Mutability::Mut}, {"allowance", "allowance(address,address)", {Type::Address, Type::Address}, Type::U256, Mutability::View}, } });
Notice that standard signatures use canonical Ethereum ABI syntax (transfer(address,uint256)), whereas the developer writes friendly English-like Quorlin source code.
Canonical Mapping: Display Types vs. ABI Types
Quorlin introduces readable keywords for primitive types:
numberrepresents an unsigned 256-bit integer (u256).truthrepresents a boolean logic value (yesorno).addressrepresents a 20-byte account or contract identity.textrepresents dynamic array text strings.
To bridge human readability with cross-chain ABI standards, the Quorlin compiler (quorlin/parser.cpp and quorlin/abi.cpp) maintains two distinct type representations:
- User/Diagnostic Type Names (
type_name): What developers read in diagnostic messages and write in source code (number,truth,address,text). - Canonical ABI Type Names (
abi_type_name): What is encoded into standard Keccak-256 function selectors and emitted in contract ABI JSON exports (uint256,bool,address,string).
Quorlin Type (type_name) | ABI Type (abi_type_name) | Description |
|---|---|---|
number | uint256 | Unsigned 256-bit integer |
truth | bool | Boolean value (yes / no) |
address | address | 20-byte cryptographic address |
text | string | UTF-8 encoded text string |
nothing | void | Empty or non-returning state |
Without this dual-mapping system, a function signature like transfer(address,number) would hash to an invalid 4-byte selector. The mapping guarantees that standard web3 wallets, external callers, and Solidity contracts can compute the selector keccak256("transfer(address,uint256)") and seamlessly interact with Quorlin tokens.
31.2 Step-by-Step Fungible Token Implementation
Below is a complete, standard-compliant implementation of a fungible token (similar to standard ERC-20 tokens) written in Quorlin.
contract DafoCoin { // --- State Storage Definitions --- number totalSupply; map<address, number> balances; map<address, map<address, number>> allowances; // --- Events --- event Transfer(address indexed from, address indexed to, number amount); event Approval(address indexed owner, address indexed spender, number amount); // --- Contract Initialization --- constructor { totalSupply = 1000000; balances[caller] = 1000000; emit Transfer(address(0), caller, 1000000); } // --- Read-Only View Methods --- reads number getTotalSupply() { return totalSupply; } reads number balanceOf(address owner) { return balances[owner]; } reads number allowance(address owner, address spender) { return allowances[owner][spender]; } // --- State-Mutating Transaction Methods --- writes truth approve(address spender, number amount) { allowances[caller][spender] = amount; emit Approval(caller, spender, amount); return yes; } writes truth transfer(address recipient, number amount) { number held = balances[caller]; require held >= amount, "not enough balance"; balances[caller] = held - amount; balances[recipient] = balances[recipient] + amount; emit Transfer(caller, recipient, amount); return yes; } writes truth transferFrom(address sender, address recipient, number amount) { number allowed = allowances[sender][caller]; number held = balances[sender]; require held >= amount, "not enough balance"; require allowed >= amount, "allowance exceeded"; allowances[sender][caller] = allowed - amount; balances[sender] = held - amount; balances[recipient] = balances[recipient] + amount; emit Transfer(sender, recipient, amount); return yes; } }
Deconstructing the Implementation
-
State Annotations (
readsvs.writes):- Functions annotated with
readsare read-only (equivalent toviewin Solidity). They can inspect contract storage or context variables without modifying state or consuming execution gas when called externally via static queries. - Functions annotated with
writesexecute state modifications. They require transaction signing, state lock validation, and gas processing.
- Functions annotated with
-
Context Built-ins:
- The
callerbuilt-in keyword dynamically evaluates to the 20-byte address of the execution caller (mapped tomsg.sender).
- The
-
Boolean Operations:
- Logical boolean return values use
yes(true) andno(false), matching Quorlin's objective of readable, standard syntax.
- Logical boolean return values use
-
Guards and Safe Execution:
- The
requirekeyword enforces execution invariants. If the condition evaluates tofalse, execution halts immediately, state modifications are reverted, and an error string is surfaced to the caller.
- The
31.3 ABI Generation and JSON Metadata
When compiling the token contract, the Quorlin compiler (quorlin/abi.cpp) processes all function and event declarations to produce standard JSON ABI metadata.
For example, given the function declaration:
writes truth transfer(address recipient, number amount)
The ABI emitter executes parameter_json() across all inputs and outputs:
// Excerpt from quorlin/abi.cpp [[nodiscard]] std::string parameter_json(std::string_view name, Type type, bool indexed, bool with_indexed) { std::string out = "{\"name\":" + quoted(name) + ",\"type\":" + quoted(abi_type_name(type)) + ",\"internalType\":" + quoted(type_name(type)); if (with_indexed) out += ",\"indexed\":" + std::string{indexed ? "true" : "false"}; return out + "}"; }
This transforms Quorlin internal definitions into standard ABI JSON formats:
{ "type": "function", "name": "transfer", "inputs": [ { "name": "recipient", "type": "address", "internalType": "address" }, { "name": "amount", "type": "uint256", "internalType": "number" } ], "outputs": [ { "name": "", "type": "bool", "internalType": "truth" } ], "stateMutability": "nonpayable" }
This metadata abstraction allows developers to author contracts cleanly while giving off-chain SDKs (like ethers.js, web3.js, or native Kortana tools) structural compatibility.
31.4 Under the Hood: KVM Compilation & State Storage Execution
When a token state modification occurs—such as updating balances via balances[caller] = held - amount;—the code generator (quorlin/codegen.cpp) translates high-level expressions into register instructions for the Kortana Virtual Machine (KVM).
Storage Slot Calculation
Storage mappings in Quorlin are backed by the Unified State Trie via the StateHost abstraction (kvm/state_host.cpp).
Reading or writing a mapping location involves calculating a 256-bit storage slot key: $$\text{SlotKey} = \text{Keccak256}(\text{Key} \mathbin{\Vert} \text{MappingSlot})$$
When the compiled binary executes on KVM, storage modifications interact directly with StateHost::set_storage:
// Excerpt from kvm/state_host.cpp Result<uint256_t> StateHost::set_storage(const Address& address, const uint256_t& key, const uint256_t& value) { KORTANA_TRY_ASSIGN(const uint256_t previous, world_.get_storage(address, key)); KORTANA_TRY(world_.put_storage(address, key, value)); return previous; }
Gas Pricing Dynamics for Tokens
KVM calculates gas dynamically during token calls using the rules in kvm/gas.cpp. Storage writes are priced based on the existing state slot content to incentivize efficient storage management:
- Cold/Hot Slot Access: Reading an address or storage slot fetches gas schedules determined by execution context.
- Zero to Non-Zero Writes: Initializing a balance (e.g. minting tokens or writing to a new account's balance) costs significantly more gas than updating an existing non-zero balance slot.
- Clearing Storage: Resetting a balance or allowance to zero grants gas refunds or lower net overhead depending on the active schedule.
31.5 Token Arithmetic and Security Guarantees
Token engineering requires arithmetic security to prevent integer overflow and underflow vulnerabilities.
Checked vs. Wrapped Arithmetic
Quorlin supports both strict, checked arithmetic and explicit wrapping arithmetic via dedicated AST binary operators (sema.cpp):
- Standard arithmetic operators (
+,-,*,/) emit safety checks during code generation. If an expression likeheld - amountyields a negative result or overflows 256 bits, execution halts and reverts. - Wrapping operators (
+wrap,-wrap,*wrap) explicitly opt into modulo $2^{256}$ wrapping behavior when low-level performance or intentional bitwise wrapping is needed.
Standard Compliance Verification
During compiler Phase 3 (quorlin/compiler.cpp), semantic analysis (Analyzer::analyze) validates token signatures against standard_interfaces(). If a contract purports to implement IERC20 but declares transfer with incorrect parameter types, the compiler produces diagnostic errors prior to emitting binary instructions:
error: interface 'IERC20' requires method 'transfer(address, uint256)', but found 'transfer(address, number)' with mismatched mutability or parameters.
This ensures that deployed token smart contracts always adhere strictly to protocol specifications, guaranteeing seamless interoperability across the Kortana ecosystem.