Documentation Index
7 min readChapter 31

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:

  • number represents an unsigned 256-bit integer (u256).
  • truth represents a boolean logic value (yes or no).
  • address represents a 20-byte account or contract identity.
  • text represents 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:

  1. User/Diagnostic Type Names (type_name): What developers read in diagnostic messages and write in source code (number, truth, address, text).
  2. 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
numberuint256Unsigned 256-bit integer
truthboolBoolean value (yes / no)
addressaddress20-byte cryptographic address
textstringUTF-8 encoded text string
nothingvoidEmpty 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

  1. State Annotations (reads vs. writes):

    • Functions annotated with reads are read-only (equivalent to view in 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 writes execute state modifications. They require transaction signing, state lock validation, and gas processing.
  2. Context Built-ins:

    • The caller built-in keyword dynamically evaluates to the 20-byte address of the execution caller (mapped to msg.sender).
  3. Boolean Operations:

    • Logical boolean return values use yes (true) and no (false), matching Quorlin's objective of readable, standard syntax.
  4. Guards and Safe Execution:

    • The require keyword enforces execution invariants. If the condition evaluates to false, execution halts immediately, state modifications are reverted, and an error string is surfaced to the caller.

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:

  1. Cold/Hot Slot Access: Reading an address or storage slot fetches gas schedules determined by execution context.
  2. 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.
  3. 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 like held - amount yields 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.