Refinement Types
Refinement types are a core Ora feature: types carry value-level constraints, and the compiler enforces them through a proof-first, runtime-fallback model. This is a major step forward for smart contract safety, verification, and auditor-friendly codebases.
Why refinements matter
- Encode safety invariants directly in types.
- Reduce boilerplate checks by moving constraints into the compiler.
- Allow proofs to remove runtime guards when constraints are satisfied.
Implemented today
Refinements currently supported in the front end:
MinValue<T, N>MaxValue<T, N>InRange<T, Min, Max>Scaled<T, D>Exact<T>NonZeroAddress
Guards are inserted at parameters, assignments, returns, and storage writes.
Guards are skipped when the type resolver can prove the refinement or when a
trusted source is used (for example, std.msg.sender() for NonZeroAddress).
Example
pub fn transfer(to: NonZeroAddress, amount: MinValue<u256, 1>) -> bool
requires balances[std.msg.sender()] >= amount
{
balances[to] += amount;
return true;
}
More examples
Range constraints
fn set_bps(bps: InRange<u256, 0, 10000>) {
// 0..10000 inclusive
}
Refinements proven by logic (no runtime guard)
fn accrue(balance: MinValue<u256, 1>, rate_bps: InRange<u256, 0, 10000>) -> MinValue<u256, 1> {
var interest: u256 = (balance * rate_bps) / 10000;
var next: MinValue<u256, 1> = balance + interest;
return next; // SMT can discharge the MinValue guard
}
Control-flow proof with refined locals
fn safe_withdraw(balance: MinValue<u256, 1>, amount: MinValue<u256, 1>) -> MinValue<u256, 1> {
if (balance < amount) return balance;
// Inside this branch: amount <= balance
var new_balance: MinValue<u256, 1> = balance - amount;
return new_balance; // SMT proves guard
}
Zero-cost refinement with SMT proof
fn charge_fee(
balance: InRange<u256, 0, 1_000_000>,
amount: InRange<u256, 1, 10_000>,
fee_bps: InRange<u256, 0, 1000>
) -> InRange<u256, 0, 1_000_000> {
if (balance < amount) return balance;
var fee: u256 = (amount * fee_bps) / 10_000;
var next: InRange<u256, 0, 1_000_000> = balance - amount + fee;
return next; // SMT proves bounds, guard eliminated
}
Exact division
fn split_evenly(total: Exact<u256>, parts: MinValue<u256, 1>) -> u256 {
return total / parts; // guard preserves exactness
}
Scaled values
fn add_scaled(x: Scaled<u256, 18>, y: Scaled<u256, 18>) -> Scaled<u256, 18> {
return x + y;
}
Refinements on storage values
contract Treasury {
storage var reserve: MinValue<u256, 1>;
pub fn deposit(amount: MinValue<u256, 1>) {
reserve = reserve + amount;
}
}
Non-zero address guard
fn set_owner(owner: NonZeroAddress) {
// cannot be zero address
}
How it works in the pipeline
- Type resolver validates the refinement and checks subtyping.
- Lowering emits refinement guards as runtime checks when needed.
- SMT pass can discharge guards when proof succeeds.
Refinement constraints that cannot refine types are surfaced as SMT-only assumptions instead of being dropped.
Policy: proof-first, runtime-fallback
- Prove statically when possible.
- Emit a runtime guard only when proof is not available or too costly.
- Keep guard identity stable to allow SMT-based removal.
When the proof succeeds, the guard is removed, making the refinement cost-free at runtime.
If function logic already establishes a constraint (for example, if (x > 0)),
the SMT pass can prove the refinement and skip any extra runtime guard.
fn ensure_positive(x: u256) -> MinValue<u256, 1> {
if (x == 0) return 1;
var y: MinValue<u256, 1> = x;
return y; // SMT proves the guard from the branch condition
}
Switch-based proof
fn classify(amount: u256) -> MinValue<u256, 1> {
switch (amount) {
0 => return 1,
else => {
var out: MinValue<u256, 1> = amount;
return out; // SMT proves amount > 0 in else
}
}
}
Implementation details
- Refinement validation:
src/ast/type_resolver/refinements/**. - Guard skip rules:
src/ast/type_resolver/core/statement.zig. - Guard emission:
src/mlir/statements/helpers.zigandsrc/mlir/declarations/refinements.zig. - SMT verification:
src/z3/verification.zigwith encoding insrc/z3/encoder.zig.
Evidence
docs/compiler/refinement-types-strategy.mdTYPE_SYSTEM_STATE.mddocs/formal-specs/ora-2.md