Skip to main content

Execution

The execution layer processes every transaction submitted to the Basalt network, transforming state transitions into deterministic, verifiable outcomes. It encompasses transaction validation, virtual machine execution (native C# AOT and WASM), sandbox isolation, a host interface for contract-to-chain interaction, and a granular gas metering model.

Transaction Types

Basalt defines four transaction types, each identified by a numeric type code:

Type CodeNameDescription
0TransferSimple value transfer between accounts
1ContractDeployDeploy new contract code to the chain
2ContractCallInvoke a function on a deployed contract
3SystemProtocol-level operations (staking, slashing, governance)

Transaction Structure

Every transaction, regardless of type, conforms to the following structure:

FieldTypeDescription
Typeuint8Transaction type code (0, 1, 2, or 3)
Nonceuint64Sender's transaction sequence number
Senderbyte[20]Address of the transaction originator
Tobyte[20]Recipient address (zero address for ContractDeploy)
ValueUInt256Amount of native token to transfer
GasLimituint64Maximum gas units the sender is willing to consume
GasPriceUInt256Price per gas unit in the smallest denomination
Databyte[]Payload (function selector + arguments for calls, bytecode for deploys)
Priorityuint8Transaction priority hint (0 = normal, 1 = high)
ChainIduint32Network identifier to prevent cross-chain replay
Signaturebyte[64]Ed25519 signature over the canonical serialization of all preceding fields

Type-Specific Behavior

Transfer (Type 0): The To field is the recipient address. The Data field is typically empty. The Value field specifies the amount to transfer. If the sender has insufficient balance (after accounting for gas costs), the transaction fails.

ContractDeploy (Type 1): The To field is set to the zero address (0x0000...0000). The Data field contains the compiled contract bytecode (native shared library or WASM module). The new contract's address is derived deterministically from the sender's address and nonce. The deployed code is stored under key 0xFF01 in the contract's storage trie.

ContractCall (Type 2): The To field is the target contract's address. The Data field contains the function selector (first 4 bytes) followed by ABI-encoded arguments. The Value field may be non-zero for payable functions.

System (Type 3): Reserved for protocol-level operations that modify consensus or governance state. System transactions are typically generated by the protocol itself (e.g., epoch transitions, reward distributions) or by validators performing staking operations.

Validation Pipeline

Every transaction passes through a seven-step validation pipeline before it is eligible for execution. Each step is a hard gate -- failure at any step causes the transaction to be rejected immediately.

Step 1: Signature Verification

The transaction's Ed25519 signature is verified against the canonical serialization of the transaction (all fields except the signature itself). The public key used for verification is derived from the Sender address. If the signature is invalid, the transaction is rejected.

During block validation, signature verification is performed using batch verification for efficiency, achieving a 2--3x speedup over individual verification when processing a full block of transactions.

Step 2: Nonce Check

The transaction's nonce must exactly equal the sender's current nonce in the world state. This prevents:

  • Replay attacks: A previously executed transaction cannot be resubmitted because the sender's nonce has already been incremented.
  • Out-of-order execution: Transactions from the same sender are processed in strict sequential order.

If the nonce is too low (already used), the transaction is rejected. If the nonce is too high (gap in the sequence), the transaction is held in the mempool pending the arrival of the missing intermediate transactions.

Step 3: Balance Check

The sender's account balance must be sufficient to cover the maximum possible cost of the transaction:

required_balance = Value + (GasLimit * GasPrice)

This check is performed before execution. The actual gas cost may be less than GasLimit * GasPrice, but the full amount must be available to guarantee that the sender can pay for the worst-case execution.

Step 4: Gas Limit Validation

The transaction's GasLimit must fall within the acceptable range:

  • Minimum: The intrinsic gas cost of the transaction (base cost plus per-byte data cost).
  • Maximum: The block gas limit (which constrains the total gas consumed by all transactions in a block).

Transactions with a gas limit below the intrinsic cost will always fail and are rejected upfront. Transactions exceeding the block gas limit cannot fit in any block and are also rejected.

Step 5: Data Size Validation

The Data field is subject to size limits based on the transaction type:

Transaction TypeMaximum Data Size
ContractCall (Type 2)128 KB
ContractDeploy (Type 1)2 MB
Transfer (Type 0)128 KB
System (Type 3)128 KB

These limits prevent excessively large transactions from consuming disproportionate network bandwidth and storage.

Step 6: Chain ID Validation

The transaction's ChainId must match the network's configured chain ID. This prevents cross-chain replay attacks where a transaction signed for one Basalt network (e.g., testnet) is submitted to a different network (e.g., mainnet).

Step 7: Compliance Check

The transaction is evaluated against the Basalt compliance layer:

  1. The sender's address is checked against the SanctionsList (IsSanctioned(byte[])). Sanctioned addresses cannot send transactions.
  2. The receiver's address is similarly checked.
  3. For high-value transfers or regulated token operations, the ComplianceEngine evaluates additional rules based on both parties' KYC status, jurisdiction, and transfer limits.

If any compliance check fails, the transaction is rejected with a descriptive error code.

BasaltVM

The Basalt Virtual Machine supports two execution modes, enabling contracts written in different languages and compiled to different targets.

Mode 1: Native C# AOT

Contracts written in C# are compiled to native shared libraries using the .NET ILC (IL Compiler) ahead-of-time compilation pipeline. This produces platform-specific native code (x64 or ARM64) that executes at near-native speed without JIT compilation overhead.

Advantages:

  • Maximum execution speed -- native machine code with no interpretation or JIT warmup.
  • Full access to the Basalt SDK type system and analyzers at compile time.
  • Deterministic compilation via the Basalt SDK build toolchain.

Compilation workflow:

  1. The developer writes a contract using the Basalt.Sdk.Contracts library.
  2. The Basalt.Sdk.Analyzers Roslyn analyzer validates the contract at compile time (no disallowed APIs, correct attribute usage, etc.).
  3. The contract is compiled via dotnet publish with Native AOT enabled, producing a .so (Linux) or .dylib (macOS) shared library.
  4. The shared library is submitted as the Data payload of a ContractDeploy transaction.

Mode 2: WASM (Wasmtime)

Contracts written in Rust, C, or any language that compiles to WebAssembly can be deployed as WASM modules and executed via the Wasmtime runtime.

Advantages:

  • Language agnostic -- any language with a WASM compilation target is supported.
  • Platform independent -- the same WASM module runs on any Basalt node regardless of host architecture.
  • Strong sandboxing guarantees inherited from the WASM execution model (linear memory, no direct host access).

Compilation workflow:

  1. The developer writes a contract in Rust (or another WASM-targeting language).
  2. The contract is compiled to a .wasm module using the appropriate toolchain (e.g., cargo build --target wasm32-unknown-unknown).
  3. The .wasm module is submitted as the Data payload of a ContractDeploy transaction.

Execution Mode Selection

The deployed code's format is detected automatically during contract execution. The first bytes of the Data payload are inspected:

  • If the payload begins with the WASM magic bytes (\0asm), it is executed via Wasmtime.
  • Otherwise, it is treated as a native shared library and loaded via the platform's dynamic linker.

Sandbox Isolation

All contract execution occurs within a strict sandbox that prevents contracts from accessing host system resources or interfering with the node's operation.

Resource Limits

ResourceLimit
Memory256 MB
CPU timeConfigurable per transaction (enforced via gas metering)
Stack size1 MB
ThreadsSingle-threaded only
Filesystem accessNone
Network accessNone

System Call Filtering

On Linux, the sandbox enforces a seccomp-bpf syscall allowlist. Only the following system calls are permitted:

Allowed SyscallPurpose
readReading from pre-opened file descriptors (WASM linear memory)
writeWriting to pre-opened file descriptors
mmapMemory allocation for the WASM linear memory
clock_gettimeAccessing monotonic clock for gas metering

Any attempt to invoke a system call not on the allowlist results in immediate termination of the contract execution with a SyscallViolation error. The transaction is marked as failed, gas is consumed up to the point of violation, and no state changes from the transaction are applied.

Determinism Guarantees

The sandbox ensures deterministic execution across all nodes:

  • No access to wall-clock time (only monotonic clock for internal metering).
  • No access to random number generators (contracts must use on-chain randomness sources).
  • No floating-point non-determinism (WASM floating-point semantics are fully deterministic; native AOT contracts are compiled with strict floating-point flags).
  • Single-threaded execution eliminates race conditions.

Host Interface

Contracts interact with the blockchain state through a set of host functions exposed by the BasaltVM runtime. These functions are the only mechanism by which a contract can read or modify on-chain state, communicate with other contracts, or emit observable side effects.

Storage Operations

FunctionSignatureGas Cost (Hot/Cold)Description
storage_read(key: bytes32) -> bytes50 / 200Read a value from the contract's storage trie
storage_write(key: bytes32, value: bytes) -> void500 (existing) / 5,000 (new)Write a value to the contract's storage trie
storage_delete(key: bytes32) -> void200 / 200Delete a key from the contract's storage trie

Hot vs. Cold: A storage slot is "hot" if it has been accessed earlier in the same transaction. The first access to a slot within a transaction is "cold" and incurs a higher gas cost due to the disk I/O required to load the trie node.

Context Functions

FunctionSignatureGas CostDescription
get_caller() -> address3Returns the address of the immediate caller
get_block_timestamp() -> uint643Returns the current block's Unix timestamp
get_block_height() -> uint643Returns the current block height
get_tx_value() -> uint2563Returns the value sent with the current call
get_gas_remaining() -> uint643Returns the remaining gas for this execution

Cross-Contract Calls

FunctionSignatureGas CostDescription
call_contract(address, function_selector, args, value, gas) -> bytes150 (hot) / 400 (cold)Invoke a function on another contract

Cross-contract calls create a new execution frame with its own gas allocation (capped by the caller's remaining gas and the explicit gas parameter). If the callee reverts, the caller can inspect the return data to determine the failure reason. State changes from reverted calls are rolled back.

Cryptographic Functions

FunctionSignatureGas CostDescription
verify_ed25519(message, signature, public_key) -> bool1,000 (hot) / 2,500 (cold)Verify an Ed25519 signature
verify_bls(message, signature, public_key) -> bool5,000 (hot) / 12,500 (cold)Verify a BLS12-381 signature
blake3_hash(data) -> bytes3210 (hot) / 25 (cold)Compute the BLAKE3 hash of arbitrary data

These precompiled cryptographic operations allow contracts to verify off-chain signatures and compute hashes without implementing the algorithms in contract code, which would be prohibitively expensive in gas.

Event Emission

FunctionSignatureGas CostDescription
emit_event(topics: bytes32[], data: bytes) -> void100 (hot) / 200 (cold)Emit an event log entry

Events are the primary mechanism for contracts to communicate observable outcomes to off-chain systems. Emitted events are included in the transaction receipt and indexed by the Bloom filter for efficient retrieval. Up to 4 topics can be indexed per event.

Gas Model

Every operation in the BasaltVM has an associated gas cost that reflects its computational and storage impact. Gas metering ensures that:

  1. Execution is bounded -- no transaction can consume unbounded resources.
  2. Resource usage is priced fairly -- more expensive operations (disk I/O, cryptographic verification) cost more gas.
  3. Denial-of-service attacks are economically prohibitive -- an attacker must pay for every unit of computation consumed.

Gas Cost Table

OperationHot Gas CostCold Gas CostNotes
Arithmetic (add, mul, etc.)13Basic ALU operations
Storage read50200First access to a slot is cold
Storage read (hot)50--Subsequent access within same tx
Storage write (existing key)500800Updating an existing value
Storage write (new key)5,0008,000Creating a new storage slot
Cross-contract call150400Per call frame
Ed25519 verify1,0002,500Per signature
BLS12-381 verify5,00012,500Per signature
Event emission100200Per event, plus per-byte data cost
BLAKE3 hash1025Per hash invocation

Hot vs. Cold Access

The dual-cost model distinguishes between hot and cold access:

  • Cold access: The first time a storage slot, contract, or resource is accessed within a transaction. Requires a full trie traversal and potentially a disk read. Charged at the higher "cold" rate.
  • Hot access: Any subsequent access to the same resource within the same transaction. The data is already cached in memory, so the lower "hot" rate applies.

This model incentivizes contracts to minimize the number of distinct storage slots accessed (reducing cold reads) and to batch operations on the same data (benefiting from hot pricing).

Gas Refunds

Certain operations that reduce state size receive partial gas refunds:

  • Storage deletion: Deleting a storage slot refunds a portion of the original write cost, incentivizing state cleanup.
  • Self-destruct: A contract that self-destructs receives a gas refund proportional to the storage freed.

Refunds are capped at 50% of the total gas consumed by the transaction to prevent refund manipulation.

Intrinsic Gas

Every transaction has a base intrinsic gas cost before any execution begins:

ComponentCost
Base transaction cost21,000 gas
Per non-zero data byte16 gas
Per zero data byte4 gas
Contract creation surcharge32,000 gas

Gas Sponsoring

Basalt supports a gas sponsoring mechanism that allows enterprises to prepay gas costs on behalf of their users, removing the requirement for end users to hold native tokens.

Enterprise Gas Quotas

Enterprises can purchase gas quotas in bulk through a protocol-level sponsoring contract. The sponsoring model works as follows:

  1. Quota purchase: An enterprise deposits native tokens into the gas sponsoring contract, receiving a gas quota denominated in gas units at a fixed price locked for a 30-day period.
  2. Per-address allocation: The enterprise configures daily gas caps for individual sponsored addresses. Each sponsored address can consume up to its daily cap without holding any native tokens.
  3. Transaction submission: When a sponsored user submits a transaction, the gas cost is deducted from the enterprise's quota rather than the user's balance. The transaction includes a sponsorship attestation that the validator verifies against the sponsoring contract.

Pricing Model

ParameterValue
Pricing period30 days
Price lockFixed at purchase time for the entire period
Minimum purchase10,000,000 gas units
Per-address daily capConfigurable by the enterprise (default: 1,000,000 gas/day)
RolloverUnused gas does not roll over to the next period

Benefits

  • User experience: End users interact with the blockchain without needing to acquire or manage native tokens.
  • Cost predictability: Enterprises lock in gas prices for 30-day periods, enabling budgeting and cost forecasting.
  • Compliance: Enterprises maintain control over which addresses can transact under their sponsorship, supporting regulatory requirements.

Gas sponsoring is entirely optional. Transactions without a sponsorship attestation are processed normally, with gas costs deducted from the sender's balance.