3 unstable releases
| new 0.2.1 | Feb 7, 2026 |
|---|---|
| 0.2.0 | Feb 6, 2026 |
| 0.1.0 | Jan 7, 2026 |
#824 in Magic Beans
155KB
4K
SLoC
Pyra: a smart contract language for the EVM
A compiled, statically typed language with Python-like syntax. It compiles directly to EVM bytecode. The focus is clarity, safety, and predictable gas use.
Philosophy
- Python-like, indentation-based syntax
- Static typing
- Ahead-of-time compilation
- Predictable gas usage
- Security-focused: reentrancy checks and arithmetic checks
- Minimal runtime
- EVM-first: compiles directly to EVM bytecode (no Yul dependency)
Language design
Syntax
Inspired by Python, but statically typed and compiled:
let total_supply: uint256 = 10000
def transfer(to: address, amount: uint256):
if amount > 0:
call_contract(to, amount)
Features
- Indentation-based blocks
- Immutable by default; use
mutfor mutable state - Static typing with built-in types
- Abstractions compile with no runtime overhead
- Generics/templates are resolved at compile time
- Reentrancy protection is enabled by default
- Compile-time gas estimation
- Built-in hooks for formal verification
- No inheritance; prefer modular composition
- No runtime garbage collection
- Compile-time constants using
const
Built-in types
uint256,int256,bool,address,bytes- Fixed-size arrays
struct(custom types)- Generic containers such as
Vec<T>andMap<K, V>(compile-time optimized) - No dynamic arrays or mappings in v1
Compiler architecture
Source Code (.pyra)
↓
Lexer (logos) → Token stream with Indent/Dedent
↓
Parser (chumsky) → AST
↓
Type Checker → scoped symbol table + storage-aware globals
↓
Storage Layout → auto-discovered slot allocation
↓
IR Lowering → intermediate representation with 40+ opcodes
↓
Safety Hardening → checked add/sub/mul + reentrancy guards
↓
Bytecode Verification → jump safety + label validation
↓
Code Generation → EVM bytecode with ABI function dispatch
↓
Deploy Bytecode (.bin) + ABI JSON (.abi)
Compiler notes
- Implemented in Rust
- Single-pass compilation: parse, type-check, and verify in one pass
- Direct bytecode generation: no Yul or solc dependency
- Parallel and incremental compilation
- Zero-copy parsing to reduce allocations
Compiler stack
| Stage | Tooling/Tech | Notes |
|---|---|---|
| Lexer/Parser | logos + chumsky (Rust) | Zero-copy parsing with indentation tracking |
| AST + Type Checker | Custom Rust structs | Scoped symbol table with storage-aware globals |
| Storage Layout | Auto-discovery engine | Sequential slot allocation from AST analysis |
| IR Lowering | Custom IR with 40+ opcodes | AST → IR with selector computation |
| Safety Hardening | Overflow/underflow/reentrancy | Automatic checked math and mutex guards |
| Gas Estimator | Static analysis engine | Per-function gas cost prediction |
| Code Generator | IR → EVM bytecode (Rust) | ABI dispatch + label resolution |
| Bytecode Verifier | Jump/label validation | Orphan jump and duplicate label detection |
| CLI | clap (Rust) | pyra build contracts/MyToken.pyra --gas-report |
How it differs from Vyper
1. Abstractions without runtime cost
Write high-level code that compiles to the same bytecode as hand-written versions:
# This generic function...
def safe_add<T: Numeric>(a: T, b: T) -> T:
return a + b
# ...generates identical bytecode to:
def add_uint256(a: uint256, b: uint256) -> uint256:
return a + b
2. Compile-time gas estimation
# Compiler provides gas costs:
def transfer(to: address, amount: uint256): # Gas: 21,000 + 5,000 SSTORE
balances[msg.sender] -= amount # Gas: 5,000 SSTORE
balances[to] += amount # Gas: 20,000 SSTORE
3. Built-in formal verification
def withdraw(amount: uint256):
require amount <= balances[msg.sender]
# @verify: balance_sum_invariant
# @verify: no_overflow_underflow
# @verify: reentrancy_safe
balances[msg.sender] -= amount
msg.sender.transfer(amount)
4. Generics and templates
Type-safe code reuse without runtime cost:
# Generic data structures
struct Vault<T: Token> {
token: T,
balance: uint256
}
# Generic functions with constraints
def swap<A: ERC20, B: ERC20>(token_a: A, token_b: B, amount: uint256)
5. Automatic reentrancy protection
Built into the language:
def withdraw(amount: uint256):
# Automatically generates a reentrancy guard
balances[msg.sender] -= amount
msg.sender.transfer(amount) # Guarded by default
Security features
- Automatic reentrancy protection on external calls
- Overflow and underflow checks unless explicitly marked
unchecked - Formal verification integration
- Compile-time bounds checking for array access
- Immutable by default to prevent accidental state changes
- Gas limit analysis
- Compile-time integer overflow detection
v1 goals
- Rust-based lexer and parser with logos and chumsky
- Single-pass AST and type checker
- Direct EVM bytecode generator
- Compile-time gas estimation
- Basic formal verification (Z3 integration)
- Automatic reentrancy protection
- Generics with no runtime overhead
- CLI tool (
pyra build ...) - Example contracts (ERC20, vault)
Project structure
pyra/
├── compiler/ # Rust compiler codebase
│ ├── src/
│ │ ├── lexer.rs # logos-based lexer with indentation tracking
│ │ ├── parser.rs # chumsky grammar (functions, structs, events, control flow)
│ │ ├── ast.rs # AST definitions
│ │ ├── typer.rs # Static type checker with scoped symbol table
│ │ ├── storage.rs # Storage layout engine with auto-discovery
│ │ ├── ir.rs # Intermediate representation (AST → IR lowering)
│ │ ├── codegen.rs # IR → EVM bytecode with ABI dispatch
│ │ ├── gas.rs # Per-function gas estimation engine
│ │ ├── security.rs # Overflow/underflow checks + reentrancy guards
│ │ ├── verifier.rs # Bytecode verification (jump safety, label checks)
│ │ ├── abi.rs # ABI JSON generation (functions, constructors, events)
│ │ ├── evm.rs # Low-level EVM helpers
│ │ ├── compiler.rs # Compilation driver
│ │ └── bin/pyra.rs # CLI entry point
│ ├── benches/ # Criterion benchmarks
│ ├── tests/ # Integration tests
│ └── Cargo.toml
├── contracts/ # Example .pyra contracts
├── stdlib/ # Standard library (Pyra code)
├── docs/ # Architecture and language reference
└── README.md
Roadmap
| Week | Milestone |
|---|---|
| 1-2 | Rust lexer and parser plus basic AST |
| 3 | Type checker and generics |
| 4 | Direct EVM code generation and gas estimation |
| 5 | Formal verification and security analysis |
| 6 | CLI tool and working contracts |
Future features
- ZKVM-compatible backend (Cairo, Risc0)
- Gas profiler CLI with optimization suggestions
- WASM output for off-chain simulation
- Contract interface generator (ABIs)
- Multi-chain support (EVM, Solana, Move)
- Built-in DeFi primitives (AMM, lending, governance)
- IDE integration (VS Code, Neovim)
Quick start
# Install (crates.io)
cargo install --locked pyra-compiler
# Compile example contracts
pyra build contracts/ERC20.pyra
pyra build contracts/Vault.pyra
# Compile with gas report
pyra build contracts/ERC20.pyra --gas-report
Fallback (GitHub):
cargo install --locked --git https://github.com/DavidIfebueme/pyra pyra-compiler
Outputs:
<Contract>.abi<Contract>.bin
Performance
The compiler includes Criterion benchmarks under compiler/benches/ to track lexer/parse/codegen performance.
Author
Built by DavidIfebueme
Disclaimer
This is an experimental project. Use at your own risk. Not production-ready until v1 is officially tagged and audited.
Dependencies
~4.5MB
~74K SLoC