Skip to main content

Contracts reference

Two Soroban contracts, in streamgive-contracts. Every mutating function here requires the auth of whichever address is named in its own doc comment below (checked via require_auth, not inferred).

ngo-registry​

Owns one fact: whether an address is a verified NGO.

Storage​

TypeFields
Ngoowner: Address, name: String, verified: bool
DataKey variantPoints to
AdminThe registry admin Address (instance storage)
Ngo(Address)An Ngo record, keyed by the NGO's own address (persistent storage)

Errors​

CodeNameMeaning
1AlreadyInitializedinit called more than once
2NotInitializedAdmin read before init
3AlreadyRegisteredregister called twice for the same address
4NotRegisteredLooked up (or tried to approve) an address with no entry

Functions​

fn init(env: Env, admin: Address) -> Result<(), Error>

Sets the admin. Once only.

fn admin(env: Env) -> Result<Address, Error>

Read-only.

fn register(env: Env, owner: Address, name: String) -> Result<(), Error>

Requires owner's auth. Self-service: an NGO registers itself. Creates an entry with verified: false. Emits a register event — topics (Symbol("register"), owner), data name.

fn get_ngo(env: Env, owner: Address) -> Result<Ngo, Error>

Read-only. Returns the entry whether verified or not.

fn approve_ngo(env: Env, ngo_owner: Address) -> Result<(), Error>

Requires the admin's auth. Flips verified to true on an existing entry. Emits an approved event — topics (Symbol("approved"), ngo_owner), no data.

donation-vault​

Holds every stream's escrowed balance. Notably, this contract doesn't check ngo-registry at all — create_stream accepts any address as the NGO, verified or not. Deploying the contracts covers why that's a deliberate choice and what it means operationally.

Storage​

TypeFields
Streamdonor: Address, ngo: Address, token: Address, rate: i128, balance: i128, withdrawn: i128, last_update: u64
DataKey variantPoints to
AdminThe vault admin Address (instance)
NextStreamIdThe u64 counter for the next stream's id (instance)
Stream(u64)A Stream record, keyed by its id (persistent)
Pausedbool — whether fund-moving actions are halted (instance)
TreasuryOptional Address the protocol fee is paid to (instance)
FeeBpsu32 protocol fee in basis points, capped at 1000 (10%) (instance)

Errors​

CodeNameMeaning
1AlreadyInitializedinit called more than once
2NotInitializedAdmin read before init
3StreamNotFoundNo stream with that id
4InvalidAmountA deposit, rate, or top-up amount was ≤ 0
5NothingToWithdrawNothing has accrued since the last withdrawal
6ContractPausedCalled a paused action while the vault is paused
7FeeTooHighset_fee_bps called with more than 1000 (10%)

Accrual​

Every function that pays out to the NGO uses the same rule:

accrued = min(rate * seconds_since_last_update, balance)

computed with saturating arithmetic — it can never overflow or panic, just cap out. withdraw, cancel_stream, top_up, and modify_rate all settle whatever has accrued before changing anything else, so donors and NGOs never lose or double-count funds across a top-up, a rate change, or a cancellation.

Functions​

fn init(env: Env, admin: Address) -> Result<(), Error>

Sets the admin and seeds the stream-id counter. Once only.

fn admin(env: Env) -> Result<Address, Error>
fn get_stream(env: Env, stream_id: u64) -> Result<Stream, Error>
fn paused(env: Env) -> bool
fn treasury(env: Env) -> Option<Address>
fn fee_bps(env: Env) -> u32

Read-only.

fn pause(env: Env) -> Result<(), Error>
fn unpause(env: Env) -> Result<(), Error>

Require the admin's auth. Halts (or restores) create_stream, withdraw, top_up, and modify_rate. cancel_stream is deliberately exempt — pausing stops new activity, it never traps donor funds already in the vault. Emit pause / unpause events (no data).

fn set_treasury(env: Env, treasury: Address) -> Result<(), Error>
fn set_fee_bps(env: Env, fee_bps: u32) -> Result<(), Error>

Require the admin's auth. Configure the optional protocol fee. With no treasury set, no fee is ever taken regardless of fee_bps.

fn create_stream(env: Env, donor: Address, ngo: Address, token: Address, deposit: i128, rate: i128) -> Result<u64, Error>

Requires donor's auth. Pulls deposit of token into the vault. Returns the new stream's id. Emits a created event — topics (Symbol("created"), stream_id), data (donor, ngo, token, deposit, rate).

fn withdraw(env: Env, stream_id: u64) -> Result<i128, Error>

Requires the stream's ngo's auth. Pays out everything accrued since the last checkpoint (minus the protocol fee, if any) and returns the gross accrued amount. Emits a withdraw event — topics (Symbol("withdraw"), stream_id), data accrued.

fn cancel_stream(env: Env, stream_id: u64) -> Result<(), Error>

Requires the stream's donor's auth. Settles accrued funds to the NGO, refunds the untouched remainder to the donor, and zeroes the stream's rate and balance — the record itself is kept, not deleted, so its history stays queryable. Emits a cancel event — topics (Symbol("cancel"), stream_id), data (accrued, refund).

fn top_up(env: Env, stream_id: u64, amount: i128) -> Result<(), Error>

Requires the stream's donor's auth. Adds amount to the stream's balance, after settling whatever had already accrued. Emits a topup event — topics (Symbol("topup"), stream_id), data amount.

fn modify_rate(env: Env, stream_id: u64, new_rate: i128) -> Result<(), Error>

Requires the stream's donor's auth. Changes the per-second rate, after settling whatever had accrued at the old rate — the new rate only ever applies going forward, never retroactively. Emits a ratemod event — topics (Symbol("ratemod"), stream_id), data new_rate.