Skip to content

icellan/runar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

105 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rúnar

Old Norse plural for "runes" (rún = secret/script/mystery). Pronounced ROO-nar.

Write Bitcoin smart contracts in TypeScript, Go, Rust, Python, Solidity, or Move. Compile to Bitcoin Script.

Build Status License npm version


Write Once, Compile Anywhere

Rúnar lets you write Bitcoin SV smart contracts in the language you already know. All formats compile through the same pipeline and produce identical Bitcoin Script.

TypeScript

class P2PKH extends SmartContract {
  readonly pubKeyHash: Addr;

  constructor(pubKeyHash: Addr) {
    super(pubKeyHash);
    this.pubKeyHash = pubKeyHash;
  }

  public unlock(sig: Sig, pubKey: PubKey) {
    assert(hash160(pubKey) === this.pubKeyHash);
    assert(checkSig(sig, pubKey));
  }
}

Go

type P2PKH struct {
    runar.SmartContract
    PubKeyHash runar.Addr `runar:"readonly"`
}

func (c *P2PKH) Unlock(sig runar.Sig, pubKey runar.PubKey) {
    runar.Assert(runar.Hash160(pubKey) == c.PubKeyHash)
    runar.Assert(runar.CheckSig(sig, pubKey))
}

Rust

#[runar::contract]
pub struct P2PKH {
    #[readonly]
    pub pub_key_hash: Addr,
}

#[runar::methods(P2PKH)]
impl P2PKH {
    #[public]
    pub fn unlock(&self, sig: &Sig, pub_key: &PubKey) {
        assert!(hash160(pub_key) == self.pub_key_hash);
        assert!(check_sig(sig, pub_key));
    }
}

Solidity-like

pragma runar ^0.1.0;

contract P2PKH is SmartContract {
    Addr immutable pubKeyHash;

    constructor(Addr _pubKeyHash) {
        pubKeyHash = _pubKeyHash;
    }

    function unlock(Sig sig, PubKey pubKey) public {
        require(hash160(pubKey) == pubKeyHash);
        require(checkSig(sig, pubKey));
    }
}

Move-style

module P2PKH {
    use runar::types::{Addr, PubKey, Sig};
    use runar::crypto::{hash160, check_sig};

    resource struct P2PKH {
        pub_key_hash: Addr,
    }

    public fun unlock(contract: &P2PKH, sig: Sig, pub_key: PubKey) {
        assert!(hash160(pub_key) == contract.pub_key_hash, 0);
        assert!(check_sig(sig, pub_key), 0);
    }
}

Python

from runar import (SmartContract, Addr, PubKey,
    Sig, hash160, check_sig, assert_, public)

class P2PKH(SmartContract):
    pub_key_hash: Addr

    def __init__(self, pub_key_hash: Addr):
        super().__init__(pub_key_hash)
        self.pub_key_hash = pub_key_hash

    @public
    def unlock(self, sig: Sig, pub_key: PubKey):
        assert_(hash160(pub_key) == self.pub_key_hash)
        assert_(check_sig(sig, pub_key))

All six formats produce the same Bitcoin Script: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG


Why Rúnar?

Bitcoin Script development today forces a choice between hand-writing opcodes (error-prone, unauditable) or adopting a framework with heavy decorator-based DSLs that obscure what happens on-chain. Rúnar takes a different path:

  • No decorators — uses native language keywords (readonly, public, immutable, #[readonly])
  • Write in your language — TypeScript, Go, Rust, Python, Solidity-like, or Move-style
  • Test nativelyvitest for TS, go test for Go, cargo test for Rust, pytest for Python
  • Four compilers — TypeScript (reference), Go, Rust, Python — all produce byte-identical output
  • Post-quantum ready — WOTS+ and SLH-DSA (FIPS 205) signature verification in Bitcoin Script
  • Nanopass architecture — 6 small passes, each auditable in a single sitting
  • Full IDE support — type checking, autocompletion, go-to-definition in every language

Quick Start

TypeScript

pnpm add runar-lang runar-compiler runar-cli
runar compile MyContract.runar.ts    # => artifacts/MyContract.runar.json

Go

# In your go.mod, add:
#   require github.com/icellan/runar/packages/runar-go v0.1.0
# Contracts are real Go — test with go test, compile with the Rúnar Go compiler
go test ./...

Rust

# In Cargo.toml: runar = { package = "runar-lang", version = "0.1.0" }
# Contracts are real Rust — test with cargo test, compile with the Rúnar Rust compiler
cargo test

Python

# pip install runar-lang
# Contracts are real Python — test with pytest, compile with any Rúnar compiler
PYTHONPATH=packages/runar-py python3 -m pytest

Test Your Contracts

Every contract format has native testing support. Business logic tests run the contract as real code in the host language. Rúnar compile checks verify the contract will produce valid Bitcoin Script.

TypeScript (vitest):

import { TestContract } from 'runar-testing';

const counter = TestContract.fromSource(source, { count: 0n });
counter.call('increment');
expect(counter.state.count).toBe(1n);

Go (go test):

func TestCounter_Increment(t *testing.T) {
    c := &Counter{Count: 0}
    c.Increment()
    if c.Count != 1 { t.Errorf("expected 1, got %d", c.Count) }
}

func TestCounter_Compile(t *testing.T) {
    if err := runar.CompileCheck("Counter.runar.go"); err != nil {
        t.Fatalf("Rúnar compile check failed: %v", err)
    }
}

Rust (cargo test):

#[path = "Counter.runar.rs"]
mod contract;
use contract::*;

#[test]
fn test_increment() {
    let mut c = Counter { count: 0 };
    c.increment();
    assert_eq!(c.count, 1);
}

#[test]
fn test_compile() {
    runar::compile_check(include_str!("Counter.runar.rs"), "Counter.runar.rs").unwrap();
}

Python (pytest):

from conftest import load_contract
from runar import hash160, mock_sig, mock_pub_key

contract_mod = load_contract("P2PKH.runar.py")
P2PKH = contract_mod.P2PKH

def test_unlock():
    pk = mock_pub_key()
    c = P2PKH(pub_key_hash=hash160(pk))
    c.unlock(mock_sig(), pk)

Supported Formats

Format Extension Compilers IDE Support Status
TypeScript .runar.ts TS, Go, Rust, Python Full (tsc) Stable
Go .runar.go Go, Python Full (gopls) Experimental
Rust DSL .runar.rs Rust, Python Full (rust-analyzer) Experimental
Python .runar.py TS, Go, Rust, Python Full (pyright) Experimental
Solidity-like .runar.sol TS, Go, Rust, Python Syntax highlighting Experimental
Move-style .runar.move TS, Go, Rust, Python Syntax highlighting Experimental

All formats parse into the same ContractNode AST. From there, the pipeline is identical:

  .runar.ts ──┐
  .runar.sol ──┤
  .runar.move ─┤
  .runar.py ───┼──► ContractNode AST ──► Validate ──► TypeCheck ──► ANF ──► Stack ──► Bitcoin Script
  .runar.go ───┤
  .runar.rs ───┘

Example Contracts

16 example contracts demonstrate all major patterns:

Contract Pattern Stateful Multi-method
P2PKH Pay-to-Public-Key-Hash No No
Escrow Multi-party authorization No Yes (4 paths)
Counter Stateful state machine Yes Yes
Auction Bidding with deadline Yes Yes
CovenantVault Spending constraints No No
OraclePriceFeed Rabin signature oracle No No
FungibleToken Token with split/merge Yes Yes (3 paths)
SimpleNFT NFT with transfer/burn Yes Yes
PostQuantumWallet WOTS+ signature verification No No
SPHINCSWallet SLH-DSA (FIPS 205) verification No No
SchnorrZKP Schnorr zero-knowledge proof (EC ops) No No
FunctionPatterns Public/private methods, built-ins Yes Yes
MathDemo Math built-in functions Yes Yes
ConvergenceProof Convergence proof pattern No No
ECDemo EC point operations No No
BoundedCounter Property initializers with defaults Yes Yes

11 contracts are available in all 6 formats (TypeScript, Go, Rust, Solidity, Move, Python). FunctionPatterns, PostQuantumWallet, SPHINCSWallet, SchnorrZKP, and ConvergenceProof are available in TypeScript, Go, Rust, and Python.

examples/
  ts/p2pkh/          P2PKH.runar.ts + P2PKH.test.ts
  go/p2pkh/          P2PKH.runar.go + P2PKH_test.go
  rust/p2pkh/        P2PKH.runar.rs + P2PKH_test.rs
  sol/p2pkh/         P2PKH.runar.sol + P2PKH.test.ts
  move/p2pkh/        P2PKH.runar.move + P2PKH.test.ts
  python/p2pkh/      P2PKH.runar.py + test_p2pkh.py

Architecture

Compilation Pipeline

The compiler is structured as six small, composable nanopass transforms. Each pass does one thing, transforms one IR into the next, and is small enough to audit in a single sitting.

Pass Name Input Output
1 Parse Source (any format) Rúnar AST
2 Validate Rúnar AST Validated AST
3 Type-check Validated AST Typed AST
4 ANF Lower Typed AST ANF IR
5 Stack Lower ANF IR Stack IR
6 Emit Stack IR Bitcoin Script

The constant folding optimizer (+ dead binding elimination) is available between passes 4 and 5 but is disabled by default to preserve ANF conformance. The peephole optimizer runs between passes 5 and 6 (always enabled).

Multi-Compiler Strategy

Rúnar defines a canonical IR conformance boundary at the ANF level. Any compiler that produces byte-identical ANF IR for a given source file is conformant:

  • The TypeScript compiler is the reference implementation
  • The Go compiler produces identical output for all example contracts including post-quantum
  • The Rust compiler produces identical output for all example contracts including post-quantum
  • The Python compiler produces identical output for all example contracts including post-quantum

The conformance suite in conformance/ contains 25 golden-file tests (including WOTS+, SLH-DSA, and EC primitives). All four compilers must pass the same suite.

Contract Model

  • SmartContract — stateless, all properties readonly
  • StatefulSmartContract — mutable state carried across transactions via OP_PUSH_TX
  • this.addOutput(satoshis, ...values) — multi-output intrinsic for token splitting/merging
  • Only Rúnar built-in functions are allowed — the compiler rejects arbitrary function calls

Language Subset

Only a strict subset of each language is valid Rúnar. The compiler enforces this at parse, validate, and typecheck time:

Allowed: Class/struct declarations, readonly/mutable properties, public/private methods, const/let variables, if/else, bounded for loops, arithmetic/comparison/logical/bitwise operators, ternary expressions, Rúnar built-in function calls.

Disallowed: Unbounded loops, recursion, async/await, closures, exceptions, dynamic arrays, arbitrary function calls (Math.floor, console.log, etc.).


Project Structure

packages/
  runar-lang/          # Language types and builtins (developer imports)
  runar-compiler/      # TypeScript compiler (6 nanopass passes)
  runar-ir-schema/     # Shared IR type definitions and JSON schemas
  runar-testing/       # TestContract API, Script VM, interpreter, fuzzer
  runar-sdk/           # Deployment SDK (providers, signers)
  runar-cli/           # CLI tool
  runar-go/            # Go package: types, mock crypto, real hashes, CompileCheck(), deployment SDK
  runar-rs/            # Rust crate: prelude types, mock crypto, real hashes, compile_check(), deployment SDK
  runar-rs-macros/     # Rust proc-macros (#[runar::contract], #[public], etc.)
  runar-py/            # Python package: types, mock crypto, real hashes, deployment SDK
compilers/
  go/                 # Go compiler (tree-sitter + native Go frontend)
  rust/               # Rust compiler (SWC + native Rust frontend)
  python/             # Python compiler (native Python frontend)
conformance/          # Cross-compiler conformance test suite
examples/
  ts/                 # TypeScript contracts + tests
  go/                 # Go contracts + tests
  rust/               # Rust contracts + tests
  sol/                # Solidity-like contracts + tests
  move/               # Move-style contracts + tests
  python/             # Python contracts + tests
  sdk-usage/          # SDK usage reference docs (not runnable)
end2end-example/      # End-to-end example (ts, go, rust, sol, move, webapp, webapp-blackjack)
spec/                 # Language specification
docs/                 # Documentation + format guides

Development

Prerequisites

  • Node.js >= 20, pnpm 9.15+
  • Go 1.26+ (for Go compiler and Go contract tests)
  • Rust 1.75+ (for Rust compiler and Rust contract tests)
  • Python 3.10+ (for Python compiler and Python contract tests)

Build & Test

git clone https://github.com/icellan/runar.git && cd runar
pnpm install && pnpm build

# TypeScript (packages + all format examples)
npx vitest run

# Go compiler + Go contract tests
cd compilers/go && go test ./...
cd examples/go && go test ./...

# Rust compiler + Rust contract tests
cd compilers/rust && cargo test
cd examples/rust && cargo test

# Python package + Python contract tests
cd packages/runar-py && python3 -m pytest
cd examples/python && PYTHONPATH=../../packages/runar-py python3 -m pytest

Academic Foundations

Technique Reference Used In
Nanopass compilation Sarkar, Waddell & Dybvig (ICFP 2004) 6-pass pipeline architecture
Administrative Normal Form Flanagan, Sabry, Duba & Felleisen (PLDI 1993) IR between typed AST and stack machine
Affine types Walker (2005), Move language (2019) Compile-time resource safety
Definitional interpreter Reynolds (1972), Amin & Rompf (POPL 2017) Reference interpreter oracle
Differential testing Yang, Chen, Eide & Regehr (PLDI 2011, CSmith) Cross-compiler fuzzing
Stack scheduling Koopman (1989) ANF → Bitcoin Script stack mapping

License

MIT

About

Write Bitcoin smart contracts in TypeScript, Go, Rust, Solidity, or Move. Compile to Bitcoin Script

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors