sol.bend source
sol.bend on the hub · documented module
import Base# Shared Solana view. The runtime maps the transaction onto this first-order# World. List<&2, U32> is the account-data word view: the checker sees a# linked list; the compiler emits O(1) slice ops (zero-copy).## PDA derivation and system-transfer CPI are trusted primitives. Prove# `pda_matches` / `Pubkey.eq` and the helpers below — not SHA256.type Pubkey is Data: Pk{w0: U32, w1: U32, w2: U32, w3: U32, w4: U32, w5: U32, w6: U32, w7: U32}# Book U64: two little-endian U32 words. The C backend implements# U64.* as a real machine u64; on-chain lamports and token amounts# are never truncated.type U64 is Data: U64{lo: U32, hi: U32}type Account is Data: Acc{ key: Pubkey, owner: Pubkey, lamports: U64, data: List<&2, U32>, is_signer: Bool, is_writable: Bool }# Stand-in for find_program_address([seed_le, owner], program_id).# On-chain the compiler replaces derive_pda / find_pda with the syscall.type Pda is Data: Pda{key: Pubkey, bump: U32}# Two CPI slots. queue_cpi fills cpi then cpi1. The runtime invokes# both after process returns Ok. Indices are World account slots.type Cpi is Data: CpiNone{} CpiTransfer{from: U32, to: U32, lamports: U64, sign: Bool, bump: U32} CpiTokenTransfer{from: U32, to: U32, mint: U32, auth: U32, amount: U64, decimals: U32, sign: Bool, bump: U32}type World is Data: World{ n: U32, ix0: U32, ix1: U64, ix2: U64, ix3: U64, pda_seed: U32, program_id: Pubkey, a0: Account, a1: Account, a2: Account, a3: Account, a4: Account, a5: Account, a6: Account, a7: Account, cpi: Cpi, cpi1: Cpi }type Outcome is Data: Ok{world: World} Err{code: U32}def ok(+w: World) -> Outcome: Ok{w}def fail(+code: U32) -> Outcome: Err{code}def E.ok() -> U32: 0def E.accounts() -> U32: 1def E.data() -> U32: 2def E.auth() -> U32: 3def E.init() -> U32: 4def E.missing() -> U32: 5def E.overflow() -> U32: 6def E.underflow() -> U32: 7def E.threshold() -> U32: 8def E.executed() -> U32: 9def E.owner() -> U32: 10def E.pda() -> U32: 11def E.cpi() -> U32: 12def E.token() -> U32: 13def U64.pack(lo: U32, hi: U32) -> U64: U64{lo, hi}def U64.from_u32(lo: U32) -> U64: U64{lo, 0}def U64.zero() -> U64: U64{0, 0}def U64.lo(x: U64) -> U32: match x: case U64{+lo, hi}: lodef U64.hi(x: U64) -> U32: match x: case U64{lo, +hi}: hidef U64.is_eq(+a: U64, +b: U64) -> Bool: match a b: case U64{+a0, +a1} U64{+b0, +b1}: Bool.and(U32.is_eq(a0, b0), U32.is_eq(a1, b1))def U64.is_ne(+a: U64, +b: U64) -> Bool: Bool.not(U64.is_eq(a, b))def U64.is_lt(+a: U64, +b: U64) -> Bool: match a b: case U64{+a0, +a1} U64{+b0, +b1}: Bool.or(U32.is_lt(a1, b1), Bool.and(U32.is_eq(a1, b1), U32.is_lt(a0, b0)))def U64.is_gt(+a: U64, +b: U64) -> Bool: U64.is_lt(b, a)def U64.is_le(+a: U64, +b: U64) -> Bool: Bool.not(U64.is_lt(b, a))def U64.is_ge(+a: U64, +b: U64) -> Bool: Bool.not(U64.is_lt(a, b))def U64.is_zero(+a: U64) -> Bool: match a: case U64{+lo, +hi}: Bool.and(U32.is_zero(lo), U32.is_zero(hi))def U64.carry(c: Bool, +hi: U32) -> U32: match c: case True{}: (hi + 1 : U32) case False{}: hidef U64.borrow(c: Bool, +hi: U32) -> U32: match c: case True{}: (hi - 1 : U32) case False{}: hidef U64.add(+a: U64, +b: U64) -> U64: match a b: case U64{+a0, +a1} U64{+b0, +b1}: +lo = (a0 + b0 : U32) +c = U32.is_lt(lo, a0) +hi = (a1 + b1 : U32) U64{lo, U64.carry(c, hi)}def U64.sub(+a: U64, +b: U64) -> U64: match a b: case U64{+a0, +a1} U64{+b0, +b1}: +lo = (a0 - b0 : U32) +br = U32.is_lt(a0, b0) +hi = (a1 - b1 : U32) U64{lo, U64.borrow(br, hi)}def U64.would_ovf(+a: U64, +b: U64) -> Bool: U64.is_lt(U64.add(a, b), a)def Pubkey.eq(+a: Pubkey, +b: Pubkey) -> Bool: match a b: case Pk{+a0, +a1, +a2, +a3, +a4, +a5, +a6, +a7} Pk{+b0, +b1, +b2, +b3, +b4, +b5, +b6, +b7}: Bool.and(U32.is_eq(a0, b0), Bool.and(U32.is_eq(a1, b1), Bool.and(U32.is_eq(a2, b2), Bool.and(U32.is_eq(a3, b3), Bool.and(U32.is_eq(a4, b4), Bool.and(U32.is_eq(a5, b5), Bool.and(U32.is_eq(a6, b6), U32.is_eq(a7, b7))))))))def Pubkey.zero() -> Pubkey: Pk{0, 0, 0, 0, 0, 0, 0, 0}def system_id() -> Pubkey: Pubkey.zero()def auth_ok(is_signer: Bool, same: Bool) -> Bool: Bool.and(is_signer, same)def word_at(xs: List<&2, U32>, i: Nat) -> U32: match xs i: case Nil{} _: 0 case h <> t 0n: h case h <> t 1n+p: word_at(t, p)def word_set(xs: List<&2, U32>, i: Nat, v: U32) -> List<&2, U32>: match xs i: case Nil{} _: Nil{} case h <> t 0n: v <> t case h <> t 1n+p: h <> word_set(t, p, v)def words_len(xs: List<&2, U32>) -> Nat: match xs: case Nil{}: 0n case h <> t: 1n+words_len(t)def has_words(xs: List<&2, U32>, n: Nat) -> Bool: Nat.is_ge(words_len(xs), n)def has_disc(xs: List<&2, U32>, d: U32) -> Bool: U32.is_eq(word_at(xs, 0n), d)def load_u32(xs: List<&2, U32>, i: Nat) -> U32: word_at(xs, i)def store_u32(xs: List<&2, U32>, i: Nat, v: U32) -> List<&2, U32>: word_set(xs, i, v)def acc_key(a: Account) -> Pubkey: match a: case Acc{+key, owner, lamports, data, is_signer, is_writable}: keydef acc_owner(a: Account) -> Pubkey: match a: case Acc{key, +owner, lamports, data, is_signer, is_writable}: ownerdef acc_signer(a: Account) -> Bool: match a: case Acc{key, owner, lamports, data, +is_signer, is_writable}: is_signerdef acc_writable(a: Account) -> Bool: match a: case Acc{key, owner, lamports, data, is_signer, +is_writable}: is_writabledef acc_lamports(a: Account) -> U64: match a: case Acc{key, owner, +lamports, data, is_signer, is_writable}: lamportsdef acc_data(a: Account) -> List<&2, U32>: match a: case Acc{key, owner, lamports, +data, is_signer, is_writable}: datadef acc_set_data(a: Account, data: List<&2, U32>) -> Account: match a: case Acc{key, owner, lamports, old, is_signer, is_writable}: Acc{key, owner, lamports, data, is_signer, is_writable}def acc_set_lamports(a: Account, lamports: U64) -> Account: match a: case Acc{key, owner, old, data, is_signer, is_writable}: Acc{key, owner, lamports, data, is_signer, is_writable}def acc_credit(+a: Account, amt: U64) -> Account: acc_set_lamports(a, U64.add(acc_lamports(a), amt))def acc_debit(+a: Account, amt: U64) -> Account: acc_set_lamports(a, U64.sub(acc_lamports(a), amt))def acc_owned(+a: Account, +program_id: Pubkey) -> Bool: Pubkey.eq(acc_owner(a), program_id)def acc_is_system(+a: Account) -> Bool: Pubkey.eq(acc_key(a), system_id())def pda_key(p: Pda) -> Pubkey: match p: case Pda{+key, bump}: keydef pda_bump(p: Pda) -> U32: match p: case Pda{key, +bump}: bumpdef pda_matches(+got: Pubkey, +want: Pubkey) -> Bool: Pubkey.eq(got, want)# Default seed bytes `bend`. Programs may pass another U32 seed.def PDA_SEED() -> U32: 1684956514# Checker / host XOR stand-in. Not SHA256. On-chain the compiler# replaces derive_pda_seeded with sol_try_find_program_address# ([seed_le_bytes, owner], program_id). Prove pda_matches / Pubkey.eq.def derive_pda_seeded(+program_id: Pubkey, +owner: Pubkey, +seed: U32) -> Pda: match program_id owner: case Pk{+p0, +p1, +p2, +p3, +p4, +p5, +p6, +p7} Pk{+o0, +o1, +o2, +o3, +o4, +o5, +o6, +o7}: Pda{Pk{ U32.xor(U32.xor(p0, o0), seed), U32.xor(p1, o1), U32.xor(p2, o2), U32.xor(p3, o3), U32.xor(p4, o4), U32.xor(p5, o5), U32.xor(p6, o6), U32.xor(p7, o7) }, 255}def derive_pda(+program_id: Pubkey, +owner: Pubkey) -> Pda: derive_pda_seeded(program_id, owner, PDA_SEED())def find_pda(+program_id: Pubkey, +owner: Pubkey, +bump: U32) -> Pubkey: pda_key(derive_pda(program_id, owner))def cpi_none() -> Cpi: CpiNone{}def cpi_is_none(c: Cpi) -> Bool: match c: case CpiNone{}: True{} case CpiTransfer{from, to, lamports, sign, bump}: False{} case CpiTokenTransfer{from, to, mint, auth, amount, decimals, sign, bump}: False{}def world_a0(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, +a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: a0def world_a1(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, +a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: a1def world_a2(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, +a2, a3, a4, a5, a6, a7, cpi, cpi1}: a2def world_a3(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, +a3, a4, a5, a6, a7, cpi, cpi1}: a3def world_a4(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, +a4, a5, a6, a7, cpi, cpi1}: a4def world_a5(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, +a5, a6, a7, cpi, cpi1}: a5def world_a6(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, +a6, a7, cpi, cpi1}: a6def world_a7(w: World) -> Account: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, +a7, cpi, cpi1}: a7def world_n(w: World) -> U32: match w: case World{+n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: ndef world_ix0(w: World) -> U32: match w: case World{n, +ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: ix0def world_ix1(w: World) -> U64: match w: case World{n, ix0, +ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: ix1def world_ix2(w: World) -> U64: match w: case World{n, ix0, ix1, +ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: ix2def world_ix3(w: World) -> U64: match w: case World{n, ix0, ix1, ix2, +ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: ix3def world_pda_seed(w: World) -> U32: match w: case World{n, ix0, ix1, ix2, ix3, +pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: pda_seeddef world_program_id(w: World) -> Pubkey: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, +program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: program_iddef world_cpi(w: World) -> Cpi: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, +cpi, cpi1}: cpidef world_cpi1(w: World) -> Cpi: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, +cpi1}: cpi1def world_set_a0(w: World, a0: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, old, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a1(w: World, a1: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, old, a2, a3, a4, a5, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a2(w: World, a2: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, old, a3, a4, a5, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a3(w: World, a3: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, old, a4, a5, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a4(w: World, a4: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, old, a5, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a5(w: World, a5: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, old, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a6(w: World, a6: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, old, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_a7(w: World, a7: Account) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, old, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_cpi(w: World, cpi: Cpi) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, old, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_cpi1(w: World, cpi1: Cpi) -> World: match w: case World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, old}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def world_set_pda_seed(w: World, pda_seed: U32) -> World: match w: case World{n, ix0, ix1, ix2, ix3, old, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}: World{n, ix0, ix1, ix2, ix3, pda_seed, program_id, a0, a1, a2, a3, a4, a5, a6, a7, cpi, cpi1}def queue_cpi(empty: Bool, +w: World, c: Cpi) -> World: match empty: case True{}: world_set_cpi(w, c) case False{}: world_set_cpi1(w, c)def cpi_transfer(+w: World, +from: U32, +to: U32, +lamports: U64) -> World: queue_cpi(cpi_is_none(world_cpi(w)), w, CpiTransfer{from, to, lamports, False{}, 0})def cpi_transfer_signed(+w: World, +from: U32, +to: U32, +lamports: U64, +bump: U32) -> World: queue_cpi(cpi_is_none(world_cpi(w)), w, CpiTransfer{from, to, lamports, True{}, bump})def cpi_token_transfer(+w: World, +from: U32, +to: U32, +mint: U32, +auth: U32, +amount: U64, +decimals: U32) -> World: queue_cpi(cpi_is_none(world_cpi(w)), w, CpiTokenTransfer{from, to, mint, auth, amount, decimals, False{}, 0})def cpi_token_transfer_signed(+w: World, +from: U32, +to: U32, +mint: U32, +auth: U32, +amount: U64, +decimals: U32, +bump: U32) -> World: queue_cpi(cpi_is_none(world_cpi(w)), w, CpiTokenTransfer{from, to, mint, auth, amount, decimals, True{}, bump})def world_amt(+w: World) -> U64: world_ix1(w)def need_accounts(w: World, k: U32) -> Bool: U32.is_ge(world_n(w), k)def counter_inc(+value: U32) -> U32: (value + 1 : U32)def dec_if(z: Bool, value: U32) -> Maybe<&2, U32>: match z: case True{}: None{} case False{}: Some{(value - 1 : U32)}def counter_dec(+value: U32) -> Maybe<&2, U32>: dec_if(U32.is_zero(value), value)def withdraw_if(lt: Bool, bal: U64, amt: U64) -> Maybe<&2, U64>: match lt: case True{}: None{} case False{}: Some{U64.sub(bal, amt)}def try_withdraw(+bal: U64, +amt: U64) -> Maybe<&2, U64>: withdraw_if(U64.is_lt(bal, amt), bal, amt)def deposit_if(ovf: Bool, bal: U64, amt: U64) -> Maybe<&2, U64>: match ovf: case True{}: None{} case False{}: Some{U64.add(bal, amt)}def try_deposit(+bal: U64, +amt: U64) -> Maybe<&2, U64>: deposit_if(U64.would_ovf(bal, amt), bal, amt)def load_u64(+xs: List<&2, U32>, +i: Nat) -> U64: U64.pack(word_at(xs, i), word_at(xs, 1n+i))def store_u64(+xs: List<&2, U32>, +i: Nat, v: U64) -> List<&2, U32>: match v: case U64{+lo, +hi}: word_set(word_set(xs, i, lo), 1n+i, hi)def popcount.go(fuel: Nat, +mask: U32, acc: U32) -> U32: match fuel: case 0n: acc case 1n+p: popcount.go(p, U32.shr(mask), (acc + U32.and(mask, 1) : U32))def popcount(mask: U32) -> U32: popcount.go(32n, mask, 0)def can_execute(approvals: U32, threshold: U32) -> Bool: U32.is_ge(popcount(approvals), threshold)def bit_set(mask: U32, i: U32) -> U32: U32.or(mask, U32.shln(1, U32.to_nat(i)))def pick_u32(b: Bool, t: U32, f: U32) -> U32: match b: case True{}: t case False{}: fdef maybe_u32(b: Bool, v: U32) -> Maybe<&2, U32>: match b: case True{}: Some{v} case False{}: None{}def zeros(n: Nat) -> List<&2, U32>: match n: case 0n: Nil{} case 1n+p: 0 <> zeros(p)def ensure_if(ok: Bool, xs: List<&2, U32>, n: Nat) -> List<&2, U32>: match ok: case True{}: xs case False{}: zeros(n)def ensure_words(+xs: List<&2, U32>, +n: Nat) -> List<&2, U32>: ensure_if(Nat.is_ge(words_len(xs), n), xs, n)def read_pk(+xs: List<&2, U32>, +off: Nat) -> Pubkey: Pk{ word_at(xs, off), word_at(xs, 1n+off), word_at(xs, 2n+off), word_at(xs, 3n+off), word_at(xs, 4n+off), word_at(xs, 5n+off), word_at(xs, 6n+off), word_at(xs, 7n+off) }def write_pk(xs: List<&2, U32>, +off: Nat, p: Pubkey) -> List<&2, U32>: match p: case Pk{+w0, +w1, +w2, +w3, +w4, +w5, +w6, +w7}: word_set(word_set(word_set(word_set(word_set(word_set(word_set(word_set( xs, off, w0), 1n+off, w1), 2n+off, w2), 3n+off, w3), 4n+off, w4), 5n+off, w5), 6n+off, w6), 7n+off, w7)def pk_eq_at(+xs: List<&2, U32>, +off: Nat, +k: Pubkey) -> Bool: Pubkey.eq(read_pk(xs, off), k)def pk_eq_words(+a: List<&2, U32>, +ao: Nat, +b: List<&2, U32>, +bo: Nat) -> Bool: Pubkey.eq(read_pk(a, ao), read_pk(b, bo))def owner_pick(is0: Bool, is1: Bool) -> Maybe<&2, U32>: match is0: case True{}: Some{0} case False{}: maybe_u32(is1, 1)def owner_index(+key: Pubkey, +o0: Pubkey, +o1: Pubkey, +n: U32) -> Maybe<&2, U32>: owner_pick( Bool.and(U32.is_gt(n, 0), Pubkey.eq(key, o0)), Bool.and(U32.is_gt(n, 1), Pubkey.eq(key, o1)))