Skip to main content

Contract Runtime

The Savitri contract runtime executes smart contracts in a sandboxed environment with gas metering, storage isolation, and event emission.

Execution Flow

Contract Call TX arrives


BaseContract validation
│ check paused, owner, reserved slots

Gas Meter initialization
│ gas_limit from TX fee

ContractStorage setup
│ load contract state, init cache

Function dispatch
│ match function_selector → handler

Execution (with gas tracking)
│ SLOAD, SSTORE, events, calls

Result
├── Success → commit storage, emit events
└── Failure → revert all changes

BaseContract

All contracts extend BaseContract. Slots 0-99 are reserved:

SlotFieldSizeDescription
0owner32 bytesContract owner address
1versionu64Contract version
2governance_hookboolGovernance integration enabled
3fee_hookboolCustom fee logic enabled
4pausedboolContract paused state
5-99Reserved-Future use

BaseContract Functions

FunctionAccessDescription
owner()PublicGet contract owner
transfer_ownership(new_owner)Owner onlyTransfer ownership
version()PublicGet contract version
upgrade(new_version)Owner onlyUpgrade contract
pause()Owner onlyPause all operations
unpause()Owner onlyResume operations

Gas Metering

Gas Costs

OperationGasDescription
SLOAD100Storage read
SSTORE (new)20,000Storage write (empty slot)
SSTORE (update)5,000Storage write (existing slot)
CALL2,300Cross-contract call
CREATE32,000Contract deployment
TRANSFER300Token transfer
LOG375Event emission (base)
LOG_TOPIC375Per additional topic
LOG_DATA8Per byte of event data
CALLDATA16Per byte of call data

Batch Accounting

The gas meter uses batch accounting (100 operations per batch) to reduce overhead. Gas is committed in batches rather than per-operation.

Gas Limit

Gas limit is derived from the transaction fee:

gas_limit = tx.fee / gas_price

If gas runs out during execution, the entire transaction reverts.

Contract Storage

Slot-Based Model

Each contract has an independent key-value store where keys are u64 slot numbers and values are 32-byte arrays.

Slot Derivation

For mappings, slots are derived using Keccak256 to prevent collisions:

// Simple value
slot = FIXED_SLOT_NUMBER

// Mapping (address → value)
slot = keccak256(address || base_slot)[0..8] as u64

// Nested mapping (address → address → value)
inner = keccak256(outer_key || base_slot)
slot = keccak256(inner_key || inner)[0..8] as u64

Merkle Tree

Contract storage maintains a Merkle tree for state proof generation.

Event System

Contracts emit events for external consumers:

pub struct CustomEvent {
pub event_type: String, // e.g., "Transfer", "Approval"
pub topics: Vec<Vec<u8>>, // indexed fields
pub data: Vec<u8>, // non-indexed data
}

Standard events (from BaseContract):

  • OwnershipTransferred(old_owner, new_owner)
  • ContractUpgraded(old_version, new_version)
  • Paused(by)
  • Unpaused(by)

Contract Deployment

1. Create DeployTransaction (to = None, data = bytecode)
2. Assign contract address (derived from deployer + nonce)
3. Initialize BaseContract (set owner, version)
4. Run contract constructor (initialize function)
5. Store ContractInfo in CF_CONTRACTS
6. Emit ContractDeployed event

Contract Calls

1. Parse function_selector from TX data
2. Load contract from CF_CONTRACTS
3. Check BaseContract state (not paused, etc.)
4. Initialize GasMeter with gas_limit
5. Execute function with ContractStorage
6. If success: commit storage changes, deduct gas
7. If failure: revert all changes

EVM Interpreter

The evm_interpreter module provides basic EVM bytecode execution for compatibility with Ethereum contracts. This enables deploying Solidity-compiled contracts on Savitri.

Memory Monitoring

The memory_monitor tracks runtime memory usage to prevent DoS:

  • Per-contract memory limits
  • Allocation tracking
  • Automatic termination if limits exceeded

Parallel Execution

The parallel module enables parallel contract execution for independent transactions:

  • Dependency analysis between transactions
  • Conflict detection on storage slots
  • Parallel execution of non-conflicting TXs
  • Serial fallback for conflicting TXs

Contract Tracing

The tracing module provides execution traces for debugging:

  • Step-by-step execution log
  • Storage read/write tracking
  • Gas consumption per operation
  • Event emission log