Take a seat. TWO signatures: the member's, because a seat is a
fn join(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
expected_amount: i128,
) -> Result
Withdraw the caller's hand, less an optional business-set platform fee
fn claim(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
fee: i128,
) -> Result
The organiser pauses the circle — one status field: settle
and open_round stop; contribute, claim and exits carry on (pause
stops the machine, not the recording or anyone's money).
This is also the gate upgrade and a void's INITIATION stand
behind — disruptive operations happen with the machine stopped. It is
the only door into Paused, so without it the contract would be
permanently unupgradable.
fn pause(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Recruiting → Active: fix the roster's obligations and start the
fn start(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Lift a pause: one status field, nothing else — it opens no round,
guards nothing and moves no deadline; open_round is the one opener
and the displayed deadline is the business layer's concern.
fn resume(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Bring storage written by an older build into this one's shape, so a
code swap lands as upgrade() → migrate(from) with no bespoke
ceremony per schema bump.
#[contracttype] encodes a struct as a map keyed by field name, so
old bytes decode only if the field SET matches exactly — a gained
field makes the host trap, a removed one too. ensure_schema keeps
every WRITE entrypoint shut until this has run (plus
get_active_policy, the one view that decodes a stored struct),
which is why the contract fails closed rather than half-working
after a code swap. The remaining views and keep_alive carry no
gate: they read scalars or bump TTLs, and a genuinely undecodable
entry traps on its own rather than returning #109.
The reshaping is generic: read the stored value as a raw
Map<Symbol, Val>, drop keys this build no longer has, fill keys it
has gained from reference (Policy is FLAT — there is no nested map
to recurse into) — and write the result back typed. NOTE: this
reshapes the POLICY entry only. T
fn migrate(
env: soroban_sdk::Env,
from_version: u32,
reference: Policy,
) -> Result<(), soroban_sdk::Error>
Swap the executable. Requires the circle Paused (no live window can
fn upgrade(
env: soroban_sdk::Env,
new_wasm: soroban_sdk::BytesN<32>,
expect_schema: u32,
) -> Result<(), soroban_sdk::Error>
fn get_cycle(env: soroban_sdk::Env) -> Result
fn get_round(env: soroban_sdk::Env, id: u64) -> Option
Contribute one share to a NAMED round — any time, any circle
fn contribute(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
round_id: u64,
) -> Result
fn get_member(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
) -> Result
fn get_roster(
env: soroban_sdk::Env,
page: u32,
) -> soroban_sdk::Vec
The circle deployer (system) — the system, the one on-chain role.
fn get_system(
env: soroban_sdk::Env,
) -> Result
Permissionless, batched TTL rescue: a member can keep their own
fn keep_alive(
env: soroban_sdk::Env,
members: soroban_sdk::Vec,
from_round: u64,
to_round: u64,
) -> Result<(), soroban_sdk::Error>
Open the next round — the ONE opener in the contract.
fn open_round(
env: soroban_sdk::Env,
opens_at: u64,
) -> Result
Void (reset) an unresolved round: every payment refunded in full
fn void_round(
env: soroban_sdk::Env,
round_id: u64,
max_steps: u32,
) -> Result
Which seats have taken a hand in the CURRENT cycle, one bit per slot — the one place that answers "have I had my turn this cycle". Cleared at the turnover.
fn get_received(env: soroban_sdk::Env) -> soroban_sdk::Bytes
The conservation ledger, one call. surplus < 0 = defect; alert.
fn get_solvency(env: soroban_sdk::Env) -> Result
Ask to leave. This FREEZES the member immediately — there is no
fn request_exit(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
) -> Result
Settle the oldest unresolved round. Permissionless, and admitted by
fn settle_round(
env: soroban_sdk::Env,
max_steps: u32,
) -> Result
Deploy-time initialization. Runs exactly once, in the deploy
fn __constructor(
env: soroban_sdk::Env,
system: soroban_sdk::Address,
first_member: soroban_sdk::Address,
token: soroban_sdk::Address,
fee_account: soroban_sdk::Address,
policy: Policy,
) -> Result<(), soroban_sdk::Error>
fn get_claimable(env: soroban_sdk::Env, member: soroban_sdk::Address) -> i128
The organiser removes a member. No vote, no automatic trigger.
Nobody is removed AUTOMATICALLY for not paying — there is no violation
counter and no auto-kick. A member who does not pay simply blocks the
round until they pay after all (a late share cures it), the
organiser freezes them (this door), or the organiser resets the round
(pause + void_round, system-only). But a circle still needs that
door: someone unreachable,
someone who asked to leave off-chain, someone who should not have been
let in — and, yes, someone stalling a round. In an acquaintance circle
that call belongs to the person who assembled it, not to a 60% vote
among people who all know each other anyway.
Removal is not confiscation. It goes through the SAME door as a
voluntary exit (begin_exit): an IMMEDIATE freeze that drops them
from every round's obligations and refunds any contribution they had
staged into a live round. The record and its slot are KEPT — slots
are never reused, they are the circle's seats for life. Nobody is
fn remove_member(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
) -> Result
The raw obligation bitmap, so the counters above can be checked
fn get_obligation(env: soroban_sdk::Env) -> soroban_sdk::Bytes
fn get_join_amount(env: soroban_sdk::Env) -> Result
The authoritative clock. A round that closes early moves its own
fn get_round_window(env: soroban_sdk::Env) -> Result
fn get_active_policy(env: soroban_sdk::Env) -> Result
fn get_schema_version(env: soroban_sdk::Env) -> u32
fn get_claimable_balance(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
) -> Result