Skip to main content

Overview

The Fenines execution layer implements a fully EVM-compatible state machine with modifications to support consensus-driven system transactions and native staking primitives. The architecture maintains Ethereum compatibility while extending functionality through precompiled system contracts at deterministic addresses.
EVM Version: London (EIP-1559 compatible)
Gas Model: Dynamic base fee with burn mechanism
State Model: Modified Patricia Merkle Trie
System Contracts: 4 precompiled addresses

State Machine Architecture

State Transition Function

The state transition function extends Ethereum’s model: Υ(σ,T)=σTXsystem\Upsilon(\sigma, T) = \sigma' \cup \\{\mathcal{TX}_{\text{system}}\\} where:
  • σ\sigma = World state at block n1n-1
  • TT = Set of user transactions
  • TXsystem\mathcal{TX}_{\text{system}} = System transactions (at epoch boundaries)
  • σ\sigma' = Resulting world state at block nn

Epoch-Aware Transitions

State evolution branches based on block position: σn={ProcessBlock(σn1,Tn)if nmod2000ProcessEpoch(σn1,Tn,TXsys)if nmod200=0\sigma_{n} = \begin{cases} \text{ProcessBlock}(\sigma_{n-1}, T_n) & \text{if } n \bmod 200 \neq 0 \\ \text{ProcessEpoch}(\sigma_{n-1}, T_n, \mathcal{TX}_{\text{sys}}) & \text{if } n \bmod 200 = 0 \end{cases}

System Contract Framework

Contract Deployment Strategy

System contracts are genesis-embedded with immutable addresses:
AddressContractTypePurpose
0x0000...1000FenineSystemLogicValidator & delegator management
0x0000...1001NFTPassportLogicWhitelist & referral system
0x0000...1002TaxManagerLogicTax calculation & burn mechanism
0x0000...1003RewardManagerStorageDynamic reward configuration
0xFFFF...FFFFFeeRecorderEOAGas fee accumulation
These addresses are consensus-critical. Changing them requires a hard fork.

FenineSystem Contract (0x1000)

Core Functions

// Create validator
function createValidator(
    uint256 commissionRate,
    bytes calldata blsPubKey
) external payable returns (uint256 validatorId)

// Stake as validator
function stakeValidator(uint256 validatorId) 
    external payable

// Unstake validator
function unstakeValidator() external

// Exit validator (withdraw)
function exitValidator() external
State Machine:NOT_EXISTcreateCREATEDstakeSTAKEDepochVALIDATED\text{NOT\_EXIST} \xrightarrow{\text{create}} \text{CREATED} \xrightarrow{\text{stake}} \text{STAKED} \xrightarrow{\text{epoch}} \text{VALIDATED}VALIDATEDunstakeUNSTAKEDepochCREATED\text{VALIDATED} \xrightarrow{\text{unstake}} \text{UNSTAKED} \xrightarrow{\text{epoch}} \text{CREATED}

Storage Layout

Critical Storage Slots

These slots are consensus-critical and must match exactly across all nodes.
// Slot 11 (0x0B): activeValidatorSet.length
// Used by consensus engine for reward routing
bytes32 constant ACTIVE_VALIDATOR_SET_SLOT = bytes32(uint256(11));

// Dynamic array elements at:
// keccak256(slot) + index
Invariant Verification:
forge inspect src/FenineSystem.sol:FenineSystem storage-layout

State Variables

SlotVariableTypePurpose
0initializedboolInitialization guard
1currentEpochuint256Epoch counter
2totalValidatorsuint256VA count
3totalDelegatorsuint256DC count
4totalStakeduint256Network-wide stake
11activeValidatorSet.lengthuint256Consensus-read

NFTPassport Contract (0x1001)

Implements a whitelist + referral system:
contract NFTPassport {
    // Mint passport with referral
    function mintPassport(bytes32 referralKey) 
        external payable returns (uint256 tokenId)
    
    // Check passport status
    function hasPassport(address user) 
        external view returns (bool)
    
    // Get referrer
    function getReferrer(address user) 
        external view returns (address)
    
    // Generate referral key (validator only)
    function generateReferralKey() 
        external returns (bytes32)
}
Referral Tree: Tree(u)=parent(u),children(u)\text{Tree}(u) = \\{\text{parent}(u), \text{children}(u)\\} where each user has at most 1 parent and unlimited children.

TaxManager Contract (0x1002)

Computes tax and distributes burn/dev shares:
contract TaxManager {
    // Compute tax
    function computeTax(uint256 amount) 
        external view returns (
            uint256 taxAmount,
            uint256 burnShare,
            uint256 devShare
        )
    
    // Update tax rate (governance)
    function setTaxRate(uint256 newRate) external
    
    // Update distribution (governance)
    function setBurnDevSplit(uint256 burnPct, uint256 devPct) 
        external
}
Tax Distribution: Tax=R×τBurn=Tax×βDev=Tax×(1β)\begin{align*} \text{Tax} &= R \times \tau \\ \text{Burn} &= \text{Tax} \times \beta \\ \text{Dev} &= \text{Tax} \times (1 - \beta) \end{align*} Default: τ=0.10\tau = 0.10, β=0.50\beta = 0.50

RewardManager Contract (0x1003)

Stores dynamic reward configuration:
contract RewardManager {
    uint256 public blockReward;         // Slot 0
    uint256 public pendingReward;       // Slot 1
    uint256 public pendingActivationEpoch; // Slot 2
    
    // Update reward (governance)
    function updateBlockReward(uint256 newReward, uint256 activationEpoch) 
        external
}
Reward Transition: Ractive(E)={Rcurrentif E<EactivationRpendingif EEactivationR_{\text{active}}(E) = \begin{cases} R_{\text{current}} & \text{if } E < E_{\text{activation}} \\ R_{\text{pending}} & \text{if } E \geq E_{\text{activation}} \end{cases}

Gas Model

EIP-1559 Base Fee

Fenines implements London’s dynamic fee market: BaseFeen+1=BaseFeen×(1+18×GusedGtargetGtarget)\text{BaseFee}_{n+1} = \text{BaseFee}_n \times \left(1 + \frac{1}{8} \times \frac{G_{\text{used}} - G_{\text{target}}}{G_{\text{target}}}\right) Parameters:
ParameterValueDescription
Gas Target15MTarget gas per block
Gas Limit30MMaximum gas per block
Elasticity2xMax/target ratio
Adjustment12.5%Max change per block

Base Fee Burn

Portion of gas fees are burned: Burnedn=BaseFeen×Gused\text{Burned}_n = \text{BaseFee}_n \times G_{\text{used}} This creates deflationary pressure: Supplyn+1=Supplyn+RblockBurnedn\text{Supply}_{n+1} = \text{Supply}_n + R_{\text{block}} - \text{Burned}_n Network becomes deflationary when: Burned>Rblock    BaseFee×Gused>1 FEN\text{Burned} > R_{\text{block}} \iff \text{BaseFee} \times G_{\text{used}} > 1 \text{ FEN} Break-even Analysis: BaseFeebreak-even=1018Gused\text{BaseFee}_{\text{break-even}} = \frac{10^{18}}{G_{\text{used}}} For Gused=15MG_{\text{used}} = 15M (target): BaseFeebreak-even66.67 gwei\text{BaseFee}_{\text{break-even}} \approx 66.67 \text{ gwei}

Gas Price Priority

Transaction ordering within a block follows: Priority(tx)=min(maxFeePerGas,maxPriorityFeePerGas+baseFee)\text{Priority}(tx) = \min(\text{maxFeePerGas}, \text{maxPriorityFeePerGas} + \text{baseFee}) Transactions sorted by priority descending.

System Transaction Execution

Exemption Rules

System transactions bypass standard checks:
CheckUser TXSystem TX
Balance ≥ gasLimit × maxFee✅ Required❌ Exempt
Nonce sequential✅ Required✅ Required
Signature valid✅ Required✅ Required
Gas limit ≤ block limit✅ Required✅ Required
Implementation:
func (st *StateTransition) preCheck() error {
    // Skip balance check for system transactions
    if st.isSystemTx() {
        return nil
    }
    
    // Standard balance check
    if st.state.GetBalance(st.from) < requiredBalance {
        return ErrInsufficientFunds
    }
    return nil
}

Execution Order

At epoch blocks, transactions execute in strict order: Oexec=[TXsys,1,TXsys,2,TXsys,3,TXuser,1,,TXuser,n]\mathcal{O}_{\text{exec}} = [\mathcal{TX}_{\text{sys,1}}, \mathcal{TX}_{\text{sys,2}}, \mathcal{TX}_{\text{sys,3}}, \mathcal{TX}_{\text{user,1}}, \ldots, \mathcal{TX}_{\text{user,n}}] Ordering Guarantee: i<j:nonce(TXi)<nonce(TXj)\forall i < j: \quad \text{nonce}(\mathcal{TX}_i) < \text{nonce}(\mathcal{TX}_j)

State Commitment

After all transactions execute, state root computed: StateRoot=Keccak256(MPT(σn))\text{StateRoot} = \text{Keccak256}(\text{MPT}(\sigma_n)) where MPT = Modified Patricia Merkle Trie.

State Storage Model

Account State

Each account stores:
type Account struct {
    Nonce    uint64       // Transaction counter
    Balance  *big.Int     // FEN balance (wei)
    Root     common.Hash  // Storage trie root
    CodeHash common.Hash  // Contract code hash
}
Account Hash: Haccount=Keccak256(RLP(nonce,balance,storageRoot,codeHash))H_{\text{account}} = \text{Keccak256}(\text{RLP}(\text{nonce}, \text{balance}, \text{storageRoot}, \text{codeHash}))

Storage Trie

Contract storage organized as: Storage(addr,key)=MPTaccount[Keccak256(key)]\text{Storage}(addr, key) = \text{MPT}_{\text{account}}\left[\text{Keccak256}(key)\right] Optimization: Leaf nodes compressed via RLP encoding.

World State Trie

Global state organized as: StateRoot=MPT[Keccak256(addr)Haccount]\text{StateRoot} = \text{MPT}\left[\text{Keccak256}(addr) \mapsto H_{\text{account}}\right] Depth: Maximum depth 64\approx 64 nibbles (32 bytes × 2).

Transaction Lifecycle

1

Submission

User submits signed transaction:
const tx = {
  to: "0x...",
  value: parseEther("1.0"),
  data: "0x...",
  maxFeePerGas: parseUnits("100", "gwei"),
  maxPriorityFeePerGas: parseUnits("2", "gwei"),
  gasLimit: 21000,
  nonce: await signer.getTransactionCount()
}
const signedTx = await signer.signTransaction(tx)
2

Validation

Node validates:Signaturevalid?Nonce=?σ(from).nonceBalance?value+gasLimit×maxFeeGasLimit?BlockGasLimit\begin{align*} \text{Signature} &\stackrel{?}{\text{valid}} \\ \text{Nonce} &\stackrel{?}{=} \sigma(\text{from}).\text{nonce} \\ \text{Balance} &\stackrel{?}{\geq} \text{value} + \text{gasLimit} \times \text{maxFee} \\ \text{GasLimit} &\stackrel{?}{\leq} \text{BlockGasLimit} \end{align*}
3

Mempool

Transaction enters pending pool:PpendingPpendingtx\mathcal{P}_{\text{pending}} \leftarrow \mathcal{P}_{\text{pending}} \cup \\{tx\\}Sorted by:
  1. Nonce (ascending)
  2. Priority fee (descending)
4

Inclusion

Validator selects transactions for next block:Tn=argmaxTPlefttxTPriorityFee(tx)rightT_n = \arg\max_{T \subset \mathcal{P}} \\left\\{ \sum_{tx \in T} \text{PriorityFee}(tx) \\right\\}subject to:txTGasLimit(tx)30M\sum_{tx \in T} \text{GasLimit}(tx) \leq 30M
5

Execution

EVM executes transaction:
1. Deduct gas: balance -= gasLimit × maxFee
2. Execute bytecode
3. Refund unused: balance += gasRefund × maxFee
4. Pay validator: validator += gasUsed × priorityFee
5. Burn base fee: supply -= gasUsed × baseFee
6

Receipt

Transaction receipt generated:
type Receipt struct {
    Status          uint64  // 1 = success, 0 = revert
    CumulativeGas   uint64  // Total gas used in block
    Logs            []Log   // Event logs
    Bloom           Bloom   // Log bloom filter
    TransactionHash Hash    // Transaction hash
}

Precompiled Contracts

Fenines inherits Ethereum’s precompiles:
AddressFunctionGas Cost
0x01ECRecover3000
0x02SHA25660 + 12/word
0x03RIPEMD160600 + 120/word
0x04Identity15 + 3/word
0x05ModExpDynamic
0x06BN256Add150 (Berlin)
0x07BN256Mul6000 (Berlin)
0x08BN256Pairing45000 + 34000/pair
0x09Blake2FDynamic
Gas costs reflect Berlin hardfork pricing (active at genesis).

EVM Opcode Set

London-Active Opcodes

New opcodes enabled:
OpcodeNameGasDescription
0x46BASEFEE2Get current base fee
0x3FEXTCODEHASH100Get code hash of account

Gas Costs (Berlin)

Cold vs warm storage access: GasSLOAD={2100if cold access100if warm access\text{Gas}_{\text{SLOAD}} = \begin{cases} 2100 & \text{if cold access} \\ 100 & \text{if warm access} \end{cases} GasSSTORE={20000if cold, 0→nonzero2900if warm, 0→nonzero5000if cold, nonzero→nonzero100if warm, nonzero→nonzero\text{Gas}_{\text{SSTORE}} = \begin{cases} 20000 & \text{if cold, 0→nonzero} \\ 2900 & \text{if warm, 0→nonzero} \\ 5000 & \text{if cold, nonzero→nonzero} \\ 100 & \text{if warm, nonzero→nonzero} \end{cases}

State Synchronization

Fast Sync

Nodes can sync via state snapshots: Syncfast=Headers+StateSnapBrecent+Receiptsrecent\text{Sync}_{\text{fast}} = \text{Headers} + \text{StateSnap}_{B_{\text{recent}}} + \text{Receipts}_{\text{recent}} Time Complexity: Tsync=O(nlogn)T_{\text{sync}} = \mathcal{O}(n \log n) where nn = number of accounts.

Snap Sync

Optimized sync protocol:
  1. Download block headers
  2. Download state snapshot (parallel)
  3. Heal missing trie nodes
  4. Verify state root
Bandwidth Reduction: Datasnap20%×Datafull\text{Data}_{\text{snap}} \approx 20\% \times \text{Data}_{\text{full}}

Security Considerations

Reentrancy Protection

System contracts use OpenZeppelin’s ReentrancyGuard:
modifier nonReentrant() {
    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
    _status = _ENTERED;
    _;
    _status = _NOT_ENTERED;
}

Integer Overflow

Solidity 0.8+ has built-in overflow checks: a,bZ+:a+b<2256\forall a, b \in \mathbb{Z}^+: \quad a + b < 2^{256} Otherwise reverts with Panic(0x11).

Access Control

Critical functions protected:
modifier onlyValidator() {
    require(msg.sender == block.coinbase, "Not validator");
    _;
}

modifier onlyGovernance() {
    require(msg.sender == governanceAddress, "Not governance");
    _;
}

Performance Optimizations

State Caching

Hot paths cache frequently accessed storage:
CacheCapacityPurpose
Account Cache1000Recent account states
Storage Cache10000Recent storage slots
Code Cache1000Contract bytecode
Hit Rate: HitRate85-95%\text{HitRate} \approx 85\text{-}95\% for typical workloads.

Parallel Execution

Independent transactions execute in parallel: Speedup=TsequentialTparalleln1+α(n1)\text{Speedup} = \frac{T_{\text{sequential}}}{T_{\text{parallel}}} \leq \frac{n}{1 + \alpha(n-1)} where α\alpha = serialization factor (Amdahl’s Law).

FenineSystem ABI

Full contract specification

System Transactions

Epoch boundary protocol

Gas Optimization

Best practices for efficiency

Smart Contract Security

Audit checklist and patterns