The next generation
language for Ethereum

Rust-inspired. EVM-native. Built for safety.

$ curl -fsSL https://raw.githubusercontent.com/argotorg/fe/master/feup/feup.sh | bash

Fe at a glance.

A vault contract with traits, pattern matching, and explicit effects.

Explicit Effects

The uses clause declares all capabilities a function requires: storage access, event emission, external calls, contract creation. Side effects become explicit.

Explicit Mutability

All bindings, storage fields, and effect parameters are immutable unless marked mut. The compiler rejects writes to immutable state.

Message Passing

Contracts interact via msg types and recv handlers, mirroring the EVM's transaction-based execution model.

Pattern Matching

match expressions destructure enums, tuples, and nested data. Extract variant payloads and branch on values. The compiler checks that all cases are covered.

Traits

Define shared interfaces with trait and implement them for any type. Used throughout the standard library for Encode, Decode, Default, and more.

Generics

Type parameters on struct, enum, trait, and fn with trait bounds. Monomorphized at compile time with zero runtime overhead.

Higher-Kinded Types

Supports higher-kinded types via * -> * bounds on traits. Enables abstractions like Functor, Applicative, and Monad, generic over Option<T>, Result<E, T>, or any type constructor.

Const Functions

Functions marked const fn are evaluated at compile time. The sol("deposit()") selector in the code computes its 4-byte hash during compilation, not at runtime.

Standard Library

Ships with Option<T>, Result<E, T>, StorageMap<K, V>, and traits like Default, Encode, Decode.

use std::abi::sol
use std::evm::{Address, Ctx, Log, StorageMap, revert}
use core::result::Result::{self, Ok, Err}
use core::abi::{Abi, AbiEncoder, AbiSize, Encode}

// Enums as error types
enum VaultError {
    InsufficientFunds,
    ZeroAmount,
}

// Traits define shared behavior
trait Validate {
    fn validate(self) -> Result<VaultError, u256>
}

struct Withdrawal {
    balance: u256,
    amount: u256,
}

impl Validate for Withdrawal {
    fn validate(self) -> Result<VaultError, u256> {
        if self.amount == 0 {
            return Err(VaultError::ZeroAmount)
        }
        if self.balance < self.amount {
            return Err(VaultError::InsufficientFunds)
        }
        Ok(self.balance - self.amount)
    }
}

// Events with indexed fields for efficient filtering
#[event]
struct Deposited {
    #[indexed]
    owner: Address,
    amount: u256,
}

struct VaultStore {
    balances: StorageMap<Address, u256>,
}

// Message interface defines the contract's public ABI
msg VaultMsg {
    #[selector = sol("deposit()")]
    Deposit {},
    #[selector = sol("withdraw(uint256)")]
    Withdraw { amount: u256 },
    #[selector = sol("balanceOf(address)")]
    BalanceOf { addr: Address } -> u256,
}

// Effects declared explicitly, no hidden state access
pub contract Vault uses (ctx: Ctx, log: mut Log) {
    mut store: VaultStore

    recv VaultMsg {
        Deposit {} uses (ctx, mut store, mut log) {
            let who = ctx.caller()
            store.balances.set(
                who,
                store.balances.get(who) + ctx.value()
            )
            log.emit(Deposited { owner: who, amount: ctx.value() })
        }

        // Pattern match on Result for control flow
        Withdraw { amount } uses (ctx, mut store) {
            let who = ctx.caller()
            let req = Withdrawal {
                balance: store.balances.get(who), 
                amount
            }
            match req.validate() {
                Ok(new_bal) => { store.balances.set(who, new_bal) }
                Err(e) => { revert(e) }
            }
        }

        BalanceOf { addr } -> u256 uses (store) {
            store.balances.get(addr)
        }
    }
}

Built-in Testing

Run fe test with integrated EVM simulation. No external frameworks needed.

Package Manager

Organize code into ingots. Add dependencies and share reusable contracts.

Code Formatter

Run fe fmt for consistent, opinionated formatting.

Language Server

LSP support with diagnostics, go-to-definition, and completions for many popular editors.

Docs Generator

Generate browsable HTML docs from your code with fe doc.

Dual Backend

Compiles through Sonatina, Fe's custom SSA-based backend, or via Yul for Solidity toolchain compatibility.

Explore more contracts written in Fe

See examples →