Documentation Index
7 min readChapter 10

10. Anatomy of a Smart Contract

A Quorlin smart contract is the foundational building block of decentralized logic on the Kortana blockchain. Designed to feel familiar to developers coming from Java, C#, or JavaScript while reading with the natural clarity of English, Quorlin source code (.ql files) compiles directly into Kortana Virtual Machine (KVM) bytecode and exports Ethereum-compatible JSON ABIs.

This chapter breaks down every structural component of a Quorlin smart contract—from top-level contract declarations down to type mappings, state management, state mutability annotations, events, and compilation mechanics.


10.1 High-Level Architecture of a Quorlin Contract

Every Quorlin program consists of a contract block containing state variables, structured types, event definitions, a deployment constructor, and operational functions.

Here is a complete, illustrative token contract demonstrating every primary language construct:

contract DafoCoin { // 1. State Variables number totalSupply; map<address, number> balances; map<address, map<address, number>> allowances; // 2. Events event Transfer(address indexed from, address indexed to, number amount); event Approval(address indexed owner, address indexed spender, number amount); // 3. Constructor constructor { totalSupply = 1000000; balances[caller] = 1000000; } // 4. Read-Only Functions (Views) reads number getTotalSupply() { return totalSupply; } reads number balanceOf(address owner) { return balances[owner]; } reads number allowance(address owner, address spender) { return allowances[owner][spender]; } // 5. State-Writing Functions writes truth transfer(address recipient, number amount) { number senderBalance = balances[caller]; require senderBalance >= amount, "insufficient balance"; balances[caller] = senderBalance - amount; balances[recipient] = balances[recipient] + amount; emit Transfer(caller, recipient, amount); return yes; } writes truth approve(address spender, number amount) { allowances[caller][spender] = amount; emit Approval(caller, spender, amount); return yes; } }

10.2 Type System & Representation Mapping

Quorlin employs natural language names for its fundamental primitives. During lexical analysis (lexer.cpp), parsing (parser.cpp), semantic verification (sema.cpp), and ABI emission (abi.cpp), these types undergo specific mappings between Quorlin syntax, compiler diagnostics, and external Ethereum-compatible ABI specs.

Quorlin KeywordInternal AST Type (Type)Diagnostic Name (type_name)Ethereum/ABI Type (abi_type_name)Description
numberType::U256numberuint256256-bit unsigned integer for arithmetic and balances.
truthType::BooltruthboolBoolean value represented by literals yes and no.
addressType::Addressaddressaddress20-byte Kortana / Ethereum account address.
textType::TexttextstringUTF-8 dynamic byte sequence bounded by kMaxTextBytes.
nothingType::VoidnothingvoidRepresents the absence of a return value.

Literals and Booleans

Unlike languages that use true and false, Quorlin enforces clear English boolean literals:

  • yes evaluates to boolean true.
  • no evaluates to boolean false.
writes truth setStatus(truth active) { if active == yes { // ... } else { // ... } return yes; }

10.3 Persistent State Storage and Maps

State variables represent permanent data written directly to Kortana's unified state trie via KVM storage instructions (set_storage / get_storage).

Simple State Variables

Declared directly within the contract body outside of any function scope:

number totalSupply; address contractOwner; truth isPaused;

Key-Value Mappings (map)

Quorlin supports single and nested key-value mappings using the map<KeyType, ValueType> syntax.

  • Keys can be primitive types such as address or number.
  • Values can be primitives or nested mappings.
map<address, number> balances; map<address, map<address, number>> allowances;

State variables are initialized either implicitly to their zero-equivalent value or explicitly inside the constructor.


10.4 Composite Data Types: Records

Quorlin allows contracts to define custom struct-like data containers using the record keyword.

record UserProfile { address account; number reputationScore; truth isActive; }

When semantic analysis checks member access on a record (sema.cpp), field lookup is validated against the record's symbol table. If an invalid field is referenced, the semantic analyzer constructs diagnostic suggestions using the exact declared field order (e.g., reporting that a record has fields account, reputationScore and isActive).


10.5 Contract Initialization: The Constructor

The constructor block is executed once during contract deployment. It initializes contract storage and sets up initial balances or ownership parameters.

constructor { totalSupply = 1000000; balances[caller] = 1000000; }

ABI Emission Behavior

Even if a constructor takes no arguments, the Quorlin ABI emitter (abi.cpp) explicitly emits a constructor object in the standard contract JSON ABI:

{ "type": "constructor", "inputs": [], "stateMutability": "nonpayable" }

This guarantees full compatibility with external deployment tools, wallets, and client SDKs expecting an explicit constructor entry in the artifact ABI.


10.6 Functions and State Mutability

Functions define the executable logic of a smart contract. Quorlin enforces strict functional mutability rules at the grammar and semantic levels.

Function Declaration Syntax

Every function signature strictly declares its mutability modifier, return type, name, and parameter list:

<mutability> <return_type> <function_name>(<parameters>)

State Mutability Keywords: reads vs writes

  1. reads (State View / Query):
    • Declares that the function reads contract storage or execution context but cannot mutate state, emit events, or modify underlying storage.
    • Corresponds to view / pure functions in standard ABI terms.
    • Enables zero-cost off-chain execution calls.
reads number balanceOf(address owner) { return balances[owner]; }
  1. writes (State Mutation / Transaction):
    • Declares that the function alters state storage, emits events, or transfers underlying assets.
    • Requires gas and execution via signed transactions on the Kortana network.
writes truth transfer(address recipient, number amount) { // State mutations allowed balances[caller] = balances[caller] - amount; balances[recipient] = balances[recipient] + amount; return yes; }

10.7 Context Variables and Execution Environment

Quorlin provides intrinsic keywords to inspect execution context:

caller

The caller built-in keyword resolves to Builtin::Caller during analysis (parser.cpp, sema.cpp). It returns the 20-byte address of the account or contract currently invoking the function execution context.

address currentSender = caller;

10.8 Assertions and Error Handling: require

Contract invariants and precondition validations are enforced using the require statement.

Syntax

require <condition_expression>, "<error_message_string>";

If the condition evaluates to no (false), execution aborts immediately, reverting all uncommitted state changes and refunding remaining gas according to KVM revert semantics.

writes truth withdraw(number amount) { number currentBalance = balances[caller]; require currentBalance >= amount, "insufficient balance to withdraw"; balances[caller] = currentBalance - amount; return yes; }

10.9 Events and Logging

Events provide an interface for smart contracts to log activity onto the blockchain, allowing off-chain applications, indexers, and user interfaces to monitor contract state transitions.

Event Declaration

Events are declared at contract scope using the event keyword. Parameters can optionally be flagged with indexed to make them filterable topics in log queries.

event Transfer(address indexed from, address indexed to, number amount);

Emitting Events

Events are triggered inside writes functions using the emit keyword:

emit Transfer(caller, recipient, amount);

ABI Representation

When compiled, the Quorlin compiler processes parameter types to generate standard Ethereum topic signatures (e.g., converting number to uint256 and truth to bool):

{ "type": "event", "name": "Transfer", "inputs": [ { "name": "from", "type": "address", "internalType": "address", "indexed": true }, { "name": "to", "type": "address", "internalType": "address", "indexed": true }, { "name": "amount", "type": "uint256", "internalType": "number", "indexed": false } ] }

10.10 The Compilation Pipeline: Source to Bytecode

Understanding the lifecycle of a Quorlin smart contract requires examining how the four-stage compiler (compiler.cpp) translates human-readable .ql code into executable KVM module binaries.

+------------------+     Lexer      +-------------------+
|  Quorlin Source  | -------------> |   Token Stream    |
|     (*.ql)       |                +-------------------+
+------------------+                          |
                                              | Parser
                                              v
+------------------+    Analyzer    +-------------------+
| Analysis Result  | <------------- | Abstract Syntax   |
| (Symbol Table)   |                |   Tree (AST)      |
+------------------+                +-------------------+
         |
         | CodeGenerator
         v
+------------------+     Module     +-------------------+
|  KVM Bytecode    | -------------> | KVM Binary Module |
|  Instructions    |    Emitter     |   (.kvm / ABI)    |
+------------------+                +-------------------+
  1. Lexical Analysis (lexer.cpp): Converts characters into typed tokens (TokenKind::Contract, TokenKind::Reads, TokenKind::Writes, TokenKind::Number, etc.).
  2. Parsing (parser.cpp): Constructs the Abstract Syntax Tree (SourceUnit and ContractDeclaration).
  3. Semantic Analysis (sema.cpp): Verifies type agreement, checks symbol declarations, confirms state mutability compliance, and establishes variable scopes.
  4. Code Generation (codegen.cpp): Generates 32-bit fixed-width KVM instructions in R-form, I-form, or J-form formats, resolves label jump targets, and outputs the final execution module alongside the ABI JSON artifact.