Join the circle. r10: this takes NO money — there is no deposit. A
A member self-joins (member-authorised). The custodial equivalent is
enroll, where the system adds them without a member signature.
fn join(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
) -> 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. Clocks stop; resume shifts every
stored window forward by however long it was down.
This is also the gate upgrade stands behind — a circle must be
paused before its code can be swapped. With the vote gone there
would otherwise be no way into that state at all, and the contract
would be permanently unupgradable.
fn pause(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), soroban_sdk::Error>
Recruiting → Active: fix the roster's obligations, start the clock,
fn start(
env: soroban_sdk::Env,
creator: soroban_sdk::Address,
) -> Result
The system (creator) enrolls member on their behalf — the custodial
path. Same roster effect and admission gates as join, but authorised by
the creator, not the member: the member never signs, so no signature can
expire mid-async while a server-driven flow retries. This is how the
founding owner (and any member) is added when the flow is system-driven.
The platform vouches for the member's consent at the business layer.
fn enroll(
env: soroban_sdk::Env,
creator: soroban_sdk::Address,
member: soroban_sdk::Address,
) -> Result
Lift a pause, moving every deadline forward by its duration.
fn resume(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> 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 other entrypoint shut until this has run, which is why the
contract fails closed rather than half-working after a code swap.
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, and write the result back typed. The
only thing a future schema bump must touch is reference — it is
both the field list and the defaults for anything new.
fn migrate(
env: soroban_sdk::Env,
creator: soroban_sdk::Address,
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,
creator: soroban_sdk::Address,
new_wasm: soroban_sdk::BytesN<32>,
expect_schema: u32,
) -> Result<(), soroban_sdk::Error>
The circle owner (admin, r12) — the user, set at deploy and resettable
by creator for recovery. Read-only, so the server can display / verify it.
fn get_admin(env: soroban_sdk::Env) -> Result
fn get_cycle(env: soroban_sdk::Env) -> Result
fn get_round(env: soroban_sdk::Env, id: u64) -> Option
Reset the owner (admin) — the platform-side recovery door. Gated on
creator (the system), which sits ABOVE admin: if a user loses their
key, the system reassigns ownership to a fresh address. Distinct from the
owner's own two-step transfer_admin/accept_admin hand-off — this is a
direct set with no accept step, because a lost key can never complete an
accept. Clears any pending hand-off so state stays coherent.
NOTE (scope): this reassigns the owner ROLE only. It does not move the old owner's member record / funds — those stay bound to the old address (a member's payout requires that member's own auth). Member-fund recovery is a separate concern, deliberately out of scope here.
fn set_admin(
env: soroban_sdk::Env,
creator: soroban_sdk::Address,
new_admin: soroban_sdk::Address,
) -> Result<(), soroban_sdk::Error>
Pay into the currently Open round.
fn contribute(
env: soroban_sdk::Env,
member: soroban_sdk::Address,
) -> 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
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>
Creator-only, Recruiting-only policy replacement: before start()
fn set_policy(
env: soroban_sdk::Env,
creator: soroban_sdk::Address,
p: Policy,
) -> Result
Void an unresolved round. Remediation may do this at any time (with
fn void_round(
env: soroban_sdk::Env,
round_id: u64,
reason: VoidReason,
max_steps: u32,
) -> Result
The circle deployer (creator, r12) — the system's super-role.
fn get_creator(
env: soroban_sdk::Env,
) -> Result
fn accept_admin(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
The conservation ledger, one call. surplus < 0 = defect; alert.
fn get_solvency(env: soroban_sdk::Env) -> Result
Ask to leave. r10: 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. Admitted only
fn settle_round(
env: soroban_sdk::Env,
max_steps: u32,
next_opens_at: Option,
) -> Result
Deploy-time initialization. Runs exactly once, in the deploy
fn __constructor(
env: soroban_sdk::Env,
admin: soroban_sdk::Address,
remediation: soroban_sdk::Address,
creator: soroban_sdk::Address,
token: soroban_sdk::Address,
fee_account: soroban_sdk::Address,
policy: Policy,
min_members: u32,
) -> 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 the organiser decides to freeze them (this door) or anyone voids it. 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 releases the
member's slot and refunds any contribution they had staged into the
live round. Nobody is ever removed for not paying — the consequence for
falling behind is being frozen out of payouts, not thrown out — this
door is for the organiser's deliberate call.
fn remove_member(
env: soroban_sdk::Env,
creator: soroban_sdk::Address,
member: soroban_sdk::Address,
) -> Result
The incrementally-maintained aggregates, exposed so they can be
fn get_aggregates(env: soroban_sdk::Env) -> (u32, u32, u32, u32, u32)
The raw obligation bitmaps, so the counters above can be checked
fn get_obligation(env: soroban_sdk::Env) -> (soroban_sdk::Bytes, soroban_sdk::Bytes)
fn transfer_admin(
env: soroban_sdk::Env,
new_admin: soroban_sdk::Address,
) -> Result<(), soroban_sdk::Error>
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
Name a beneficiary out of turn for the round now awaiting settlement.
fn assign_beneficiary(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
member: soroban_sdk::Address,
) -> Result<(), soroban_sdk::Error>
fn get_schema_version(env: soroban_sdk::Env) -> u32