Skip to content
Fe 26.2 is not production-ready. This is an initial release of a new compiler. Learn more

Intrinsics Reference

Intrinsics are built-in functions that provide direct access to EVM operations and low-level functionality. This appendix documents available intrinsics in Fe.

Execution-context information is exposed as methods on the Ctx effect. Any function that declares uses (ctx: Ctx) can call them:

MethodReturnsDescription
ctx.caller()AddressAddress that called this contract
ctx.block_number()u256Current block number
ctx.timestamp()u256Current block timestamp (seconds since epoch)
ctx.coinbase()AddressCurrent block miner/validator address
ctx.prevrandao()u256Block randomness (post-Merge; replaces difficulty)
ctx.gaslimit()u256Current block gas limit
ctx.chainid()u256Current chain ID
ctx.origin()AddressOriginal transaction sender
ctx.gasprice()u256Gas price of the transaction
ctx.gas()u256Remaining gas for execution
fn only_owner(owner: Address) uses (ctx: Ctx) {
assert!(ctx.caller() == owner, "not owner")
}
fn get_timestamp() -> u256 uses (ctx: Ctx) {
ctx.timestamp()
}

Contract state and identity are also Ctx methods:

MethodReturnsDescription
ctx.address()AddressThis contract’s address
ctx.balance(addr)u256ETH balance of address (in wei)
ctx.selfbalance()u256This contract’s ETH balance
ctx.extcodesize(addr)u256Size of code at address
ctx.extcodehash(addr)u256Keccak256 hash of code at address
fn get_contract_balance() -> u256 uses (ctx: Ctx) {
ctx.selfbalance()
}
fn is_contract(addr: Address) -> bool uses (ctx: Ctx) {
ctx.extcodesize(addr) > 0
}

Hash functions and signature recovery live in std::evm::crypto. The hash functions operate on a memory region (offset and length), and the precompile-backed ones return a Result:

FunctionReturnsDescription
crypto::keccak256(offset, len)u256Keccak-256 of a memory region
crypto::sha256(offset, len)Result<PrecompileError, u256>SHA-256 (precompile 0x02)
crypto::ripemd160(offset, len)Result<PrecompileError, u256>RIPEMD-160 (precompile 0x03)
crypto::ecrecover(hash, v, r, s)Result<PrecompileError, Option<u256>>Recover signer from signature (precompile 0x01)

Signature recovery takes plain u256 scalars and returns a Result; the inner Option is None for a non-canonical signature:

use std::evm::crypto
fn verify_signature(
message_hash: u256,
v: u256,
r: u256,
s: u256,
expected_signer: u256,
) -> bool {
match crypto::ecrecover(hash: message_hash, v: v, r: r, s: s) {
Ok(maybe_signer) => {
match maybe_signer {
Some(signer) => signer == expected_signer
None => false
}
}
Err(_) => false
}
}

The hash functions take a memory region rather than a value, so you first write bytes to memory (through the RawMem effect) and then hash them:

use std::evm::crypto
// `offset`/`len` describe bytes already written to EVM memory.
fn hash_region(offset: u256, len: u256) -> u256 {
crypto::keccak256(offset, len)
}

Control flow for error handling:

IntrinsicDescription
assert!(condition[, message])Revert if condition is false
revertUnconditionally revert execution
todo()Placeholder that always reverts
fn transfer(from: Address, to: Address, amount: u256, balance: u256) {
assert!(from.inner != 0)
assert!(to.inner != 0)
assert!(balance >= amount)
}
fn not_implemented() {
todo()
}

For calling other contracts:

IntrinsicDescription
call(addr, value, data)Call another contract
staticcall(addr, data)Read-only call (no state changes)
delegatecall(addr, data)Call using this contract’s storage
fn send_eth(to: Address, amount: u256) -> bool {
let (success, _) = call(to, amount, [])
success
}

Low-level memory operations:

IntrinsicDescription
mload(offset)Load 32 bytes from memory
mstore(offset, value)Store 32 bytes to memory
msize()Current memory size

These are rarely needed in Fe as the compiler manages memory automatically.

Direct storage access:

IntrinsicDescription
sload(slot)Load from storage slot
sstore(slot, value)Store to storage slot

Fe’s storage system abstracts over these. Use storage structs and effects instead.

Event emission:

IntrinsicDescription
log.emit(event)Emit an event to the transaction log
#[event]
struct TransferEvent {
#[indexed]
from: Address,
#[indexed]
to: Address,
value: u256,
}
fn emit_transfer(from: own Address, to: own Address, value: u256) uses (log: mut Log) {
log.emit(event: TransferEvent { from, to, value })
}
IntrinsicDescription
msg_value()ETH sent with the current call
fn deposit() uses (ctx: Ctx) {
let amount = ctx.value()
// Process deposit...
}
MethodReturnsDescription
ctx.blockhash(number)u256Hash of a recent block (last 256 blocks)
fn get_recent_block_hash(block_num: u256) -> u256 uses (ctx: Ctx) {
ctx.blockhash(block_num)
}
CategoryIntrinsics
Contextctx.caller, ctx.block_number, ctx.timestamp, ctx.chainid, etc.
Contractctx.address, ctx.balance, ctx.extcodesize, ctx.extcodehash
Cryptocrypto::keccak256, crypto::sha256, crypto::ecrecover
Controlassert!, revert, todo
Callscall, staticcall, delegatecall
Eventslog.emit
  1. Use effects instead of raw intrinsics - Fe’s effect system (uses Ctx, uses Log) provides safer access to intrinsics

  2. Avoid low-level storage/memory - Let Fe manage these automatically

  3. Check ecrecover results - Always verify the recovered address is not zero

  4. Be careful with block_hash - Only works for the last 256 blocks

  5. Handle call failures - External calls can fail; always check return values