Documentation Index
8 min readChapter 23

23. Gas Metering and Execution Costs

In the Kortana Virtual Machine (KVM), gas metering is the foundational mechanism that bounds execution runtime, prevents denial-of-service (DoS) attacks, and fairly prices computing resources across the network. Every low-level opcode executed by the KVM, and by extension every high-level statement in the Quorlin smart contract language, incurs a deterministic gas cost.

This chapter explores how the KVM computes execution costs, categorizes opcodes into distinct pricing tiers, handles state trie modifications, and provides optimization patterns for writing gas-efficient Quorlin contracts.


23.1 The KVM Gas Architecture

When a transaction triggers a smart contract execution, the node initializes the KVM interpreter with a specific gas limit provided by the transaction sender. Before executing any opcode, the interpreter checks whether sufficient gas remains in the current execution context. If the remaining gas is less than the required base cost of the instruction plus any dynamic memory or state modification expansion costs, the interpreter halts execution immediately and raises an out-of-gas error.

In KVM's design, gas costs are categorized into distinct tiers defined by the runtime's opcode specification and gas schedule.

Gas Pricing Tiers

The system partitions instruction base costs into discrete cost tiers:

TierCost IdentifierDescriptionOpcodes Included
FreekGasZeroZero base gas cost. Applied to execution halt/termination opcodes.Stop, Return, Revert, Invalid
Very LowkGasVeryLowStandard single-cycle 256-bit ALU, logical, register, and basic memory operations.Add, Sub, Lt, Gt, SLt, SGt, Eq, IsZero, And, Or, Xor, Not, Byte, Shl, Shr, Sar, Mov, LoadK, LoadI, MLoad, MStore, MStore8, CallDataLoad
LowkGasLowMulti-cycle or complex arithmetic operations.Mul, Div, SDiv, Mod, SMod, SignExtend

23.2 Halting and Free Opcodes

A critical design choice in the KVM gas model is how termination opcodes are priced. The opcodes Stop, Return, and Revert carry a base cost of kGasZero:

// Excerpt from kvm/gas.cpp uint64_t base_cost(Opcode opcode, const params::GasSchedule& schedule) noexcept { switch (opcode) { // --- Free: these halt, and charging for stopping would price the same work differently // depending on how a contract chose to end. --------------------------------------- // case Opcode::Stop: case Opcode::Return: case Opcode::Revert: return kGasZero; // `INVALID` consumes everything remaining rather than a fixed amount, so its base is zero // and the interpreter drains the counter. A fixed price would make deliberately aborting // cheaper than running out of gas, which is a refund by another name. case Opcode::Invalid: return kGasZero; ... } }

Why Termination Opcodes are Free

If terminating an execution charged extra gas, a function returning early via a Revert or explicit Return statement would pay a penalty compared to one executing to natural completion. Free termination guarantees that contracts are priced solely on the computational work performed prior to reaching the exit point.

The Invalid Opcode Exception

While Opcode::Invalid reports a base cost of kGasZero in base_cost(), its execution behavior is fundamentally different. When the interpreter encounters an Invalid instruction, it immediately drains all remaining gas in the call frame. Assigning a fixed gas cost to an invalid instruction would make deliberate contract abortions cheaper than running out of gas, effectively creating an unintended gas refund vector.


23.3 Base Arithmetic and Bitwise Instruction Costs

Operations that execute within standard register bounds without accessing external persistent storage are categorized into the kGasVeryLow or kGasLow tiers.

Very Low Cost Operations (kGasVeryLow)

Simple 256-bit integer operations consume standard minimal unit gas:

  • Arithmetic & Comparisons: Addition (Add), Subtraction (Sub), Less Than (Lt), Greater Than (Gt), Equality (Eq), Zero Test (IsZero).
  • Bitwise Operations: Bitwise AND (And), OR (Or), XOR (Xor), Logical/Arithmetic Shifts (Shl, Shr, Sar).
  • Register & Volatile Memory Operations: Transferring register values (Mov), loading constants (LoadK, LoadI), loading/storing volatile stack memory (MLoad, MStore, MStore8), and reading call payload (CallDataLoad).

Low Cost Operations (kGasLow)

Multiplication and division consume slightly more computational cycles on standard node hardware:

  • Multiplication and Division: Unsigned Multiplication (Mul), Unsigned/Signed Division (Div, SDiv), Modulo (Mod, SMod), and Sign Extension (SignExtend).

23.4 Storage Access and State Modification Costs

Persistent state on the Kortana blockchain is managed via the unified state trie. Storage operations (get_storage and set_storage) represent the most expensive execution paths in the KVM due to disk I/O, cryptographic hashing, and state tree overhead.

Storage Read (get_storage)

When a Quorlin contract accesses a state variable or reads a key from a map, the KVM calls StateHost::get_storage:

Result<uint256_t> StateHost::get_storage(const Address& address, const uint256_t& key) const { // Straight to the shared trie. This one line is where §21's "unified state trie" is actually // satisfied — the KEVM reads the same slot through the same call. return world_.get_storage(address, key); }

Reading a storage slot requires traversing the underlying state trie for the specified contract address and 256-bit slot key.

Storage Write (set_storage) and Differential Pricing

Modifying persistent state is substantially more expensive than reading state. Crucially, writing to state incurs differential pricing based on whether the operation is filling an empty slot or overwriting an existing slot:

Result<uint256_t> StateHost::set_storage(const Address& address, const uint256_t& key, const uint256_t& value) { // The previous value is returned so the interpreter can price the write without a second read: // filling an empty slot costs several times an overwrite, and it needs to know which this is. KORTANA_TRY_ASSIGN(const uint256_t previous, world_.get_storage(address, key)); KORTANA_TRY(world_.put_storage(address, key, value)); return previous; }

Why State Creation Costs More Than State Overwriting

  1. Filling an Empty Slot (previous == 0 and value != 0): Writing a value to a previously unallocated storage key expands the total size of the global state trie. This increases structural overhead across all full nodes on the network and costs significantly more gas.
  2. Overwriting an Existing Slot (previous != 0 and value != 0): Updating a value in a slot that already exists modifies a key in-place. Because it does not grow the structural footprint of the unified state trie, it is charged a lower gas fee.
  3. Clearing a Slot (previous != 0 and value == 0): Setting a non-zero storage key to zero prunes a branch from the trie, yielding a gas refund upon transaction finalization.

23.5 Gas Dynamics in Quorlin Contracts

The Quorlin smart contract language exposes low-level KVM mechanics through clean, readable constructs. Understanding how high-level Quorlin statements compile down to KVM opcodes allows developers to write gas-efficient code.

Gas Profile Comparison: State Caching

Consider a storage balance update pattern in Quorlin. In the naive implementation below, state variables are accessed repeatedly:

contract StorageNaive { map<address, number> balances; writes truth addReward(address user, number bonus) { // Uncached: Performs three separate storage reads for balances[user] balances[user] = balances[user] + bonus; balances[user] = balances[user] + 5; return yes; } }

In StorageNaive, each evaluation of balances[user] triggers a KVM storage read opcode, invoking StateHost::get_storage three times.

By introducing a local working variable, we eliminate redundant state lookups:

contract StorageOptimized { map<address, number> balances; writes truth addReward(address user, number bonus) { // Cached: Single storage read, local register/memory manipulations, single storage write number currentBalance = balances[user]; currentBalance = currentBalance + bonus + 5; balances[user] = currentBalance; return yes; } }

Comparative Analysis of StorageNaive vs StorageOptimized

Execution StepStorageNaiveStorageOptimized
Storage Reads (get_storage)3 trie lookups1 trie lookup
Arithmetic (Add)2 kGasVeryLow operations2 kGasVeryLow operations
Storage Writes (set_storage)2 trie updates1 trie update
Total Gas ImpactVery HighMinimum possible for state change

23.6 Gas Awareness with Control Flow and Guards

Validation logic in Quorlin relies on the require statement. When a require check fails, the contract emits a Revert opcode and halts immediately.

contract TokenVault { number totalSupply; map<address, number> balances; writes truth withdraw(number amount) { // Guard clause executed BEFORE performing expensive operations number userBalance = balances[caller]; require userBalance >= amount, "Insufficient funds"; balances[caller] = userBalance - amount; totalSupply = totalSupply - amount; return yes; } }

Early Guard Optimization Rule

Always place checks and validations (such as require expressions) at the top of functions before modifying state or performing nested calls. If the condition evaluates to no (false), execution halts via Revert. Because termination opcodes are free (kGasZero), the user pays only for the minimal comparison opcodes executed prior to the failure, saving gas on unexecuted state updates.


23.7 Summary of Gas Optimization Guidelines

  1. Minimize Trie Access: Store intermediate arithmetic results in local stack variables rather than repeatedly reading or writing state fields and map entries.
  2. Order Statements Efficiently: Run cheap validation checks (require) at the beginning of function execution to abort early before performing expensive state reads or modifications.
  3. Understand State Lifecycle: Overwriting existing state slots costs significantly less gas than instantiating new non-zero state entries.
  4. Use reads for Non-State-Modifying Operations: Mark functions that only query data as reads. View calls executed off-chain do not generate a transaction on-chain, eliminating gas costs for external clients.