Skip to content

Latest commit

 

History

History
1994 lines (1373 loc) · 60.8 KB

api-reference.md

File metadata and controls

1994 lines (1373 loc) · 60.8 KB

API Reference

Full documentation for every PayStream contract function: parameters, return values, errors, and CLI examples.

See also: Error Codes · Stream Status Lifecycle · Storage Layout (for off-chain indexers)


Stellar Explorer (Stellar Expert) links

Deep links to Stellar Expert for the deployed testnet contracts. Use these to verify transactions and inspect on-chain state without building custom tooling.

Contract Testnet address Stellar Expert link
PayStream Token CDZQHVQHQMHIGJGSQIJVXGPGNZJQDQV4BBKMG7MSIDTJTTBBQYAEUPB https://stellar.expert/explore/testnet/contract/CDZQHVQHQMHIGJGSQIJVXGPGNZJQDQV4BBKMG7MSIDTJTTBBQYAEUPB
PayStream Stream CBXKDJUQYQDQKSQT6AKZRQWXDXTQHVXHBQQJQ3WKDXJHPNMRGYAQMRS https://stellar.expert/explore/testnet/contract/CBXKDJUQYQDQKSQT6AKZRQWXDXTQHVXHBQQJQ3WKDXJHPNMRGYAQMRS

The contract addresses above mirror docs/testnet.md. When testnet.md is updated as part of a release, update this table to match so the explorer links stay current. A future improvement is to template these URLs from testnet.md so they auto-update on every release.

Mainnet: Placeholder section added until mainnet launch. Mainnet contract IDs and Stellar Explorer links (mainnet network) will be published here after the mainnet prototype (PROD-34 / issue #323) is deployed. See docs/mainnet.md.


Stream Contract

initialize

Set the contract admin. Must be called once after deployment before any other function.

Emits a contract_initialized event on success so off-chain indexers can determine when and by whom the contract was initialised.

Caller: Admin

Parameter Type Description
admin Address Address that will become the contract admin

Returns: nothing

Errors:

  • Panics if admin auth fails
  • Panics with "already initialized" if called more than once (SC-01 / LOW-02)

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- initialize --admin <ADMIN_ADDRESS>

propose_admin

Step 1 of a two-step admin transfer. Current admin nominates a new admin.

Caller: Current admin

Parameter Type Description
new_admin Address Address being nominated as the next admin

Returns: nothing

Errors:

  • Panics if caller is not the current admin

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- propose_admin --new_admin <NEW_ADMIN_ADDRESS>

accept_admin

Step 2 of a two-step admin transfer. Nominated admin accepts and becomes the new admin.

Caller: Nominated (pending) admin

Parameter Type Description
new_admin Address Must match the address set by propose_admin

Returns: nothing

Errors:

  • Panics if there is no pending admin
  • Panics if new_admin does not match the pending admin

Example:

stellar contract invoke --id <STREAM_ID> --source <NEW_ADMIN_KEY> --network testnet \
  -- accept_admin --new_admin <NEW_ADMIN_ADDRESS>

pause_contract

Admin pauses the entire contract. Blocks create_stream, create_streams_batch, and withdraw.

Caller: Admin

Parameter Type Description
nonce u64 Current admin nonce (replay protection)

Returns: nothing

Errors:

  • Panics if caller is not the admin
  • Panics if nonce does not match the stored nonce (E009)

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- pause_contract --nonce 0

unpause_contract

Admin unpauses the contract, restoring normal operation.

Caller: Admin

Parameter Type Description
nonce u64 Current admin nonce (replay protection)

Returns: nothing

Errors:

  • Panics if caller is not the admin
  • Panics if nonce does not match the stored nonce (E009)

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- unpause_contract --nonce 1

set_min_deposit

Admin sets the minimum deposit enforced on create_stream.

Caller: Admin

Parameter Type Description
admin Address Must equal the stored admin
nonce u64 Current admin nonce (replay protection)
amount i128 New minimum deposit (must be > 0)

Returns: nothing

Errors:

  • Panics if admin auth fails or does not match stored admin
  • Panics if nonce is wrong (E009)
  • Panics if amount ≤ 0 (E002)

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- set_min_deposit --admin <ADMIN_ADDRESS> --nonce 2 --amount 100000

expire_streams

Admin batch-expires streams that crossed stop_time + GRACE_PERIOD and have no remaining claimable tokens. Ineligible entries are skipped without reverting. The admin nonce is consumed even when every stream is skipped.

set_protocol_fee

Admin configures the fee charged on stream creation. The fee is deducted from the gross deposit before the net amount is locked in escrow. fee_bps is capped at 100 (1%); setting it to 0 disables fees.

Caller: Admin

Parameter Type Description
admin Address Must equal the stored admin
nonce u64 Current admin nonce (replay protection)
stream_ids Vec<u64> Streams to evaluate; each must be Active, have stop_time > 0, now > stop_time + GRACE_PERIOD, and claimable == 0 to expire

Returns: nothing

Errors:

  • Panics if admin auth fails or does not match the stored admin
  • Panics if nonce does not match the stored admin nonce (E009) | fee_bps | u16 | Fee in basis points, from 0 to 100 | | treasury | Address | Address receiving collected fees |

Errors: Panics if the caller is not the admin, the nonce is wrong, or fee_bps exceeds 100.

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- expire_streams --admin <ADMIN_ADDRESS> --nonce 3 --stream_ids '[1,2,3]'
  -- set_protocol_fee --admin <ADMIN_ADDRESS> --nonce 2 --fee_bps 10 --treasury <TREASURY_ADDRESS>

get_protocol_fee

Return the configured protocol fee and treasury. Before configuration, returns (0, None).

Caller: Anyone

Returns: (u16, Option<Address>) — fee in basis points and optional treasury address

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- get_protocol_fee

create_stream

Employer creates a salary stream and deposits funds into the contract escrow.

Caller: Employer

Parameter Type Description
employer Address Employer address; funds are pulled from here
employee Address Employee address; receives streamed tokens
token_address Address SEP-41 token contract address
deposit i128 Gross amount; configured fee is deducted before the net amount is locked in escrow
rate_per_second i128 Tokens streamed per second
stop_time u64 Hard stop timestamp (0 = indefinite)
low_balance_threshold Option<i128> Emit a low_bal event after any withdraw that leaves less than this remaining (None/0 = disabled)
cliff_time Option<u64> Cliff timestamp (PROD-17). Nothing accrues before it; once passed, accrual runs from cliff_time (not from the start). None, or a cliff at/before now, means no cliff

Returns: u64 — the new stream ID

Cliff behaviour: claimable is 0 while now < cliff_time. Withdrawing, pausing/resuming or changing the rate before the cliff never moves accrual earlier than the cliff. Cancelling before the cliff refunds the full deposit to the employer. The first withdraw / withdraw_all / cancel_stream / settle_stream call at or after the cliff emits cliff_reached once. Read the cliff with get_cliff_time(stream_id) -> Option<u64>.

Errors:

  • Panics if contract is paused
  • E035 if cliff_time > stop_time (when stop_time is non-zero)
  • E002 if deposit ≤ 0
  • E007 if deposit < minimum deposit
  • E001 if rate_per_second ≤ 0
  • E008 if rate_per_second > 1,000,000,000
  • Panics if stop_time is in the past (when non-zero)
  • Panics if employer == employee
  • Panics if token transfer fails (insufficient balance or allowance)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- create_stream \
    --employer <EMPLOYER_ADDRESS> \
    --employee <EMPLOYEE_ADDRESS> \
    --token_address <TOKEN_ID> \
    --deposit 1000000 \
    --rate_per_second 100 \
    --stop_time 0 \
    --low_balance_threshold 100000

create_linear_vest

Employer creates a fixed grant that vests linearly over vest_period. The grant is the gross deposit; any configured protocol fee is deducted before the net grant is escrowed. An optional cliff delays all claimability until the cliff duration has elapsed, while vesting accrues from the start time. Paused time is excluded from the vesting period.

Caller: Employer

Parameter Type Description
employer Address Employer funding the grant
employee Address Grant recipient
token_address Address SEP-41 token contract address
total_grant i128 Gross grant amount (must meet minimum deposit)
vest_period u64 Vesting duration in seconds; must be greater than zero
cliff Option<u64> Optional cliff duration in seconds; must not exceed vest_period

Returns: u64 — the new stream ID

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- create_linear_vest --employer <EMPLOYER_ADDRESS> \
  --employee <EMPLOYEE_ADDRESS> --token_address <TOKEN_ID> \
  --total_grant 1000000 --vest_period 31536000 --cliff 7776000

get_stream_type

Return whether a stream is Payroll or LinearVest. Existing streams without type metadata are treated as Payroll.

Caller: Anyone

Parameter Type Description
stream_id u64 Stream to query

Returns: StreamType — Payroll or LinearVest


get_linear_vest_schedule

Return the grant amount, vest duration, and optional cliff for a linear vest. Returns None for payroll streams.

Caller: Anyone

Parameter Type Description
stream_id u64 Stream to query

Returns: Option<LinearVestSchedule> — schedule containing total_grant, vest_period, and cliff


create_streams_batch

Employer creates multiple salary streams atomically. All streams succeed or all revert.

Caller: Employer

Parameter Type Description
employer Address Employer address
params Vec<StreamParams> List of stream parameters (see below)

StreamParams fields:

Field Type Description
employee Address Employee address
token Address SEP-41 token contract address
deposit i128 Gross deposit; any configured protocol fee is deducted before escrow
rate_per_second i128 Tokens per second for this stream
stop_time u64 Hard stop timestamp (0 = indefinite)
cliff_time Option<u64> Optional cliff (PROD-17); same rules as create_stream. Also stored in templates

Returns: Vec<u64> — list of new stream IDs in the same order as params

Limit: 50 streams per call

Errors: Same per-stream validations as create_stream, including protocol fee deduction; panics if params is empty or contains more than 50 entries (E029)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- create_streams_batch \
    --employer <EMPLOYER_ADDRESS> \
    --params '[{"employee":"<ADDR1>","token":"<TOKEN_ID>","deposit":500000,"rate_per_second":50,"stop_time":0},{"employee":"<ADDR2>","token":"<TOKEN_ID>","deposit":500000,"rate_per_second":50,"stop_time":0}]'

withdraw

Employee withdraws all claimable tokens earned so far.

Caller: Employee

Parameter Type Description
employee Address Must match the stream's employee
stream_id u64 ID of the stream to withdraw from

Returns: i128 — amount transferred (0 if nothing claimable)

Errors:

  • Panics if contract is paused
  • Panics if stream not found
  • Panics if caller is not the stream's employee
  • Panics if stream is not Active or Exhausted
  • E003 if a reentrant withdraw is detected

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYEE_KEY> --network testnet \
  -- withdraw --employee <EMPLOYEE_ADDRESS> --stream_id 1

settle_stream

Anyone may finalize an Active stream after its stop_time is reached. Any remaining claimable tokens are transferred to the current stream receipt holder, then the stream transitions to Exhausted and emits a status event. No caller authorization is required.

Parameter Type Description
stream_id u64 ID of the stream to settle

Errors:

  • Panics if the stream is not found
  • Panics if the stream is not Active
  • Panics if stop_time is unset or has not been reached

Example:

stellar contract invoke --id <STREAM_ID> --source <KEEPER_KEY> --network testnet \
  -- settle_stream --stream_id 1

withdraw_all

Employee withdraws all claimable tokens from every stream they receive in a single transaction. Auth is checked once. Cancelled and Paused streams are silently skipped; streams with nothing claimable are also skipped.

Caller: Employee

Parameter Type Description
employee Address Employee address (requires auth, checked once)

Returns: Vec<i128> — amounts transferred in employee stream-index order, with one entry per stream that paid out; empty if nothing was claimable

Errors:

  • Panics if contract is paused
  • E003 if a reentrant withdraw is detected on any stream

Gas note: Each stream in the employee's index incurs one token::transfer cross-contract call. For a 10-stream portfolio this is roughly 10× the cost of a single withdraw call; plan accordingly for large indexes.

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYEE_KEY> --network testnet \
  -- withdraw_all --employee <EMPLOYEE_ADDRESS>

top_up

Employer adds more funds to an active stream.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream to top up
amount i128 Additional tokens to deposit

Returns: nothing

Errors:

  • Panics if stream not found
  • Panics if caller is not the stream's employer
  • E005 if stream is Cancelled
  • E006 if stream is Exhausted
  • Panics if amount ≤ 0
  • Panics if token transfer fails

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- top_up --employer <EMPLOYER_ADDRESS> --stream_id 1 --amount 500000

set_low_balance_threshold

Employer updates the low-balance alert threshold for a stream at any time without topping up. Setting the threshold to 0 disables the alert.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer (requires auth)
stream_id u64 ID of the stream to update
threshold i128 New alert threshold in deposit tokens (≥ 0; 0 disables alert)

Returns: nothing

Errors:

  • Panics if stream not found (E015)
  • Panics if caller is not the stream's employer (E018)
  • E005 if stream is Cancelled
  • E006 if stream is Exhausted
  • Panics if threshold < 0

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- set_low_balance_threshold --employer <EMPLOYER_ADDRESS> --stream_id 1 --threshold 500000

set_auto_topup

Employer pre-authorizes recurring top-ups. After each withdraw / withdraw_all, if deposit - withdrawn < trigger_threshold, the contract pulls topup_amount from the employer via transfer_from (capped so the cumulative total never exceeds max_total). The employer must first approve the stream contract as spender for at least max_total. If the allowance or balance is insufficient, the top-up is skipped and the withdraw still succeeds. Calling again replaces the config and resets the running total.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream
trigger_threshold i128 Remaining balance below which a top-up fires (> 0)
topup_amount i128 Amount per top-up (> 0, ≤ max_total)
max_total i128 Cap on cumulative auto top-ups

Auth: employer.require_auth()

Returns: nothing

Errors:

  • Panics if stream not found or caller is not the employer
  • E005 if stream is Cancelled
  • E025 if parameters are invalid (trigger_threshold ≤ 0, topup_amount ≤ 0, or topup_amount > max_total)

Events: at_set with enabled = true (see events schema)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- set_auto_topup --employer <EMPLOYER_ADDRESS> --stream_id 1 \
  --trigger_threshold 100000 --topup_amount 500000 --max_total 5000000

cancel_auto_topup

Employer revokes a stream's recurring top-up authorization.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream

Auth: employer.require_auth()

Returns: nothing

Errors:

  • Panics if stream not found or caller is not the employer
  • E026 if no auto top-up is configured

Events: at_set with enabled = false (see events schema)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- cancel_auto_topup --employer <EMPLOYER_ADDRESS> --stream_id 1

get_auto_topup_config

Query the recurring auto top-up configuration for a stream. Used by off-chain dashboards to display auto top-up status.

Caller: Anyone (no auth required)

Parameter Type Description
stream_id u64 ID of the stream

Returns: Option<AutoTopupConfig> — Some(AutoTopupConfig) if configured, or None if no auto top-up is set for the stream.

AutoTopupConfig fields:

Field Type Description
trigger_threshold i128 Remaining deposit threshold below which an auto top-up is triggered
topup_amount i128 Amount pulled from the employer per top-up
max_total i128 Lifetime cap on cumulative auto top-up transfers
total_topped_up i128 Cumulative tokens deposited via auto top-up so far

Errors: None (returns None if stream has no config or does not exist)

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- get_auto_topup_config --stream_id 1

(Note: get_auto_topup is also supported as an alias.)


maybe_auto_topup (internal)

Not a public entry point. Runs automatically at the end of withdraw and, per stream, inside withdraw_all. Integrators cannot call it directly; it is documented here so its behaviour is predictable.

Parameter Type Description
stream &mut Stream Stream being withdrawn from
now u64 Current ledger timestamp

Behaviour: if an AutoTopupConfig exists, the stream has not reached stop_time, and deposit - withdrawn < trigger_threshold, it pulls min(topup_amount, max_total - total_topped_up) from the employer via transfer_from, adds it to deposit, reactivates an Exhausted stream, increments total_topped_up, and emits at_up.

Auth: none of its own — it relies on the employer's prior token approve of the stream contract.

Returns: nothing

Errors: never panics. The top-up is silently skipped (the withdraw still succeeds) when no config exists, the stream has ended, the cap is reached, or the allowance/balance is insufficient.


Auto top-up worked example

# 1. Employer approves the stream contract to spend up to max_total
stellar contract invoke --id <TOKEN_ID> --source <EMPLOYER_KEY> --network testnet \
  -- approve --from <EMPLOYER_ADDRESS> --spender <STREAM_ID> \
  --amount 5000000 --expiration_ledger <LEDGER>

# 2. Configure auto top-up: refill 500000 whenever remaining < 100000, up to 5000000 total
stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- set_auto_topup --employer <EMPLOYER_ADDRESS> --stream_id 1 \
  --trigger_threshold 100000 --topup_amount 500000 --max_total 5000000

# 3. Once remaining balance would drop below 100000, the employee withdraws
stellar contract invoke --id <STREAM_ID> --source <EMPLOYEE_KEY> --network testnet \
  -- withdraw --employee <EMPLOYEE_ADDRESS> --stream_id 1

# 4. Verify the top-up fired: total_topped_up should now be 500000
stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- get_auto_topup_config --stream_id 1

The withdraw transaction also emits an at_up event (["v1", "at_up", 1] → [500000, 500000]), and get_stream shows deposit increased by 500000.


pause_stream

Employer pauses an active stream. Accrual stops until resume_stream is called.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream to pause

Returns: nothing

Errors:

  • Panics if stream not found
  • Panics if caller is not the stream's employer
  • Panics if stream is not Active

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- pause_stream --employer <EMPLOYER_ADDRESS> --stream_id 1

pause_stream_batch

Employer pauses multiple Active streams atomically. The call reverts entirely if any stream is missing, belongs to another employer, or is not Active. A status event is emitted for each stream on success.

Caller: Employer

Parameter Type Description
employer Address Must own every stream (requires auth once)
stream_ids Vec<u64> IDs of 1–50 streams to pause

Returns: nothing

Errors:

  • Panics if stream_ids is empty or contains more than 50 entries (E029)
  • Panics if any stream is not found, does not belong to employer, or is not Active

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- pause_stream_batch --employer <EMPLOYER_ADDRESS> --stream_ids '[1,2,3]'

resume_stream

Employer resumes a paused stream. The last_withdraw_time is reset to now so paused time is excluded from accrual.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream to resume

Returns: nothing

Errors:

  • Panics if stream not found
  • Panics if caller is not the stream's employer
  • Panics if stream is not Paused

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- resume_stream --employer <EMPLOYER_ADDRESS> --stream_id 1

resume_stream_batch

Employer resumes multiple Paused streams atomically. The call reverts entirely if any stream is missing, belongs to another employer, or is not Paused. Each stream's accrual clock restarts at the current ledger timestamp, and a status event is emitted for each stream.

Caller: Employer

Parameter Type Description
employer Address Must own every stream (requires auth once)
stream_ids Vec<u64> IDs of 1–50 streams to resume

Returns: nothing

Errors:

  • Panics if stream_ids is empty or contains more than 50 entries (E029)
  • Panics if any stream is not found, does not belong to employer, or is not Paused

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- resume_stream_batch --employer <EMPLOYER_ADDRESS> --stream_ids '[1,2,3]'

update_rate

Employer changes the rate_per_second of an Active or Paused stream without cancelling and recreating it.

Before applying the new rate, any tokens accrued since the last withdrawal are settled by resetting last_withdraw_time to the current ledger timestamp. The employee is credited at the old rate for all elapsed time; future accrual uses the new rate.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream to update
new_rate i128 New tokens-per-second rate (1 – 1,000,000,000)

Returns: nothing

Errors:

  • Panics if stream not found
  • Panics if caller is not the stream's employer
  • Panics if stream is not Active or Paused
  • E001 if new_rate ≤ 0
  • E008 if new_rate > 1,000,000,000

Emitted event: rate_updated(stream_id, old_rate, new_rate)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- update_rate --employer <EMPLOYER_ADDRESS> --stream_id 1 --new_rate 20

Worked Example (numeric):

Initial state:

  • Stream created at time T=0 with deposit=10,000, rate=10 tokens/sec, no stop time
  • At T=0: withdrawn=0, pending_accrual=0

Period 1 (rate A = 10/s):

  • Advance to T=100: 100 seconds elapsed at old rate
  • claimable() = 100 × 10 = 1,000 tokens

Rate change:

  • Call update_rate at T=100 to set rate B = 20/s
  • Before applying new rate, accrued 1,000 tokens are banked into pending_accrual
  • After update: pending_accrual = 1,000, rate_per_second = 20, last_withdraw_time = 100

Period 2 (rate B = 20/s):

  • Advance to T=150: 50 more seconds at new rate (20/s)
  • New accrual = 50 × 20 = 1,000 tokens
  • Total claimable() = pending_accrual + new accrual = 1,000 + 1,000 = 2,000

Withdraw:

  • withdraw() at T=150 pays out 2,000 tokens total (covering both periods)
  • After withdraw: withdrawn = 2,000, pending_accrual = 0
  • Future accrual resumes from T=150 at 20/s

This matches test_update_rate_withdraw_clears_pending_accrual.


update_stop_time

Employer changes the hard stop timestamp of an Active stream without cancelling and recreating it. Set new_stop_time to 0 to make the stream indefinite. A nonzero timestamp cannot precede last_withdraw_time, so this operation cannot move the hard stop behind the stream's current accrual baseline.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream to update
new_stop_time u64 Unix timestamp in seconds, or 0 for no hard stop

Returns: nothing

Errors:

  • Panics if stream not found
  • E017 if the stream is not Active
  • E018 if caller is not the stream's employer
  • E030 if nonzero new_stop_time is before last_withdraw_time

Emitted event: stop_time_updated(stream_id, old_stop_time, new_stop_time)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- update_stop_time --employer <EMPLOYER_ADDRESS> --stream_id 1 --new_stop_time 1893456000

cancel_stream

Employer cancels a stream. The employee receives all earned tokens; the employer is refunded the remainder.

Caller: Employer

Parameter Type Description
employer Address Must match the stream's employer
stream_id u64 ID of the stream to cancel

Returns: nothing

Errors:

  • Panics if stream not found
  • Panics if caller is not the stream's employer
  • Panics if stream is already Cancelled or Exhausted

Emitted event: stream_cancelled (enriched — see Events section)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- cancel_stream --employer <EMPLOYER_ADDRESS> --stream_id 1

cancel_streams_batch

Employer cancels multiple streams atomically in a single transaction. All cancellations succeed or all revert. Cheaper than N individual cancel_stream calls for N ≥ 2.

Caller: Employer

Parameter Type Description
employer Address Must own every stream in the list (requires auth once)
stream_ids Vec<u64> IDs of 1–50 streams to cancel

Returns: nothing

Errors:

  • Panics if stream_ids is empty
  • Panics with E029 if stream_ids contains more than 50 entries
  • Panics if any stream is not found
  • Panics if any stream does not belong to employer
  • Panics if any stream is already Cancelled or Exhausted (reverts entire batch)

Example:

stellar contract invoke --id <STREAM_ID> --source <EMPLOYER_KEY> --network testnet \
  -- cancel_streams_batch --employer <EMPLOYER_ADDRESS> --stream_ids '[1,2,3]'

get_stream

Read the full state of a stream by ID.

Caller: Anyone

Parameter Type Description
stream_id u64 ID of the stream to read

Returns: Stream — the stream struct

Stream fields:

Field Type Description
id u64 Stream ID
employer Address Employer address
employee Address Employee address
token Address Token contract address
deposit i128 Total deposited
withdrawn i128 Total already withdrawn
rate_per_second i128 Tokens streamed per second
start_time u64 Ledger timestamp when stream started
stop_time u64 Hard stop timestamp (0 = none)
last_withdraw_time u64 Timestamp of last withdrawal or resume
status StreamStatus Active / Paused / Cancelled / Exhausted
locked bool Reentrancy guard (always false at rest)
low_balance_threshold i128 Remaining-deposit threshold for the low_bal event (0 = disabled)

Errors:

  • Panics if stream not found

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- get_stream --stream_id 1

get_streams_batch

Read the full state of up to 50 streams in a single call. Lets off-chain dashboards replace N get_stream calls with one.

Caller: Anyone

Parameter Type Description
stream_ids Vec<u64> IDs of the streams to read (max 50)

Returns: Vec<Stream> — streams in the same order as stream_ids

Errors:

  • Panics if stream_ids has more than 50 entries
  • Panics if any stream ID is not found (consistent with get_stream)

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- get_streams_batch --stream_ids '[1, 2, 3]'

stream_status

Query only the status of a stream by ID. Lighter than get_stream for read-heavy off-chain indexers that only need to know whether a stream is Active, Paused, Cancelled, or Exhausted (SC-06).

Caller: Anyone

Parameter Type Description
stream_id u64 ID of the stream to query

Returns: StreamStatus — one of Active, Paused, Cancelled, Exhausted

Errors:

  • Panics with "stream not found" if no stream exists for stream_id

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_status --stream_id 1

claimable

Query how many tokens the employee can withdraw right now.

Caller: Anyone

Parameter Type Description
stream_id u64 ID of the stream to query

Returns: i128 — claimable token amount

Formula:

claimable = min(
    (now - last_withdraw_time) * rate_per_second,
    deposit - withdrawn
)

Time is capped at stop_time when set. Returns 0 for Cancelled or Exhausted streams.

Errors:

  • Panics if stream not found

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- claimable --stream_id 1

Worked examples

Each example mirrors a scenario covered in contracts/stream/src/test.rs.

1. Normal accrual — deposit = 1_000, rate_per_second = 10, stop_time = 0, last_withdraw_time = 0, withdrawn = 0, now = 50

elapsed   = 50 - 0            = 50
earned    = 50 * 10           = 500
remaining = 1_000 - 0         = 1_000
claimable = min(500, 1_000)   = 500

2. With stop_time — deposit = 1_000, rate_per_second = 10, stop_time = 60, last_withdraw_time = 0, withdrawn = 0, now = 100

effective_end = min(now, stop_time) = 60
elapsed       = 60 - 0              = 60
earned        = 60 * 10             = 600
remaining     = 1_000 - 0           = 1_000
claimable     = min(600, 1_000)     = 600   # time after stop_time never accrues

3. After pause/resume — deposit = 10_000, rate_per_second = 10; paused at T=100, resumed at T=200 (resume_stream sets last_withdraw_time = 200), withdrawn = 0, now = 250

elapsed   = 250 - 200          = 50    # the paused interval 100..200 is excluded
earned    = 50 * 10            = 500
remaining = 10_000 - 0         = 10_000
claimable = min(500, 10_000)   = 500

Explorer link (example): <https://stellar.expert/explore/testnet/tx/<TX_HASH>> Replace <TX_HASH> with the actual transaction hash from your resume_stream invocation to inspect it on Stellar Expert.

Resuming resets last_withdraw_time, so tokens accrued before the pause but not yet withdrawn are not carried over. Employees should withdraw before a stream is paused.

4. Exhausted stream — deposit = 1_000, rate_per_second = 10, fully withdrawn at T=100 (withdrawn = 1_000, status = Exhausted), now = 500

status == Exhausted  → claimable = 0
(formula would also give min(400 * 10, 1_000 - 1_000) = min(4_000, 0) = 0)

claimable_batch

Query claimable amounts for up to 50 streams in one contract call. Missing stream IDs return 0; all other results use the same calculation as claimable and preserve input order.

Caller: Anyone

Parameter Type Description
stream_ids Vec<u64> Stream IDs to query (maximum 50)

Returns: Vec<i128> — claimable amounts in the same order as stream_ids

Errors:

  • Panics if stream_ids has more than 50 entries
  • E004 if claimable amount arithmetic overflows

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- claimable_batch --stream_ids '[1, 2, 3]'

claimable_at

Query how many tokens would be claimable at an arbitrary timestamp.

Caller: Anyone

Parameter Type Description
stream_id u64 ID of the stream to query
timestamp u64 Hypothetical ledger timestamp

Returns: i128 — claimable amount at the given timestamp

Errors:

  • Panics if stream not found

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- claimable_at --stream_id 1 --timestamp 1800000000

stream_count

Total number of streams ever created.

Caller: Anyone

Returns: u64

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_count

stream_ids_by_employer

Return all stream IDs owned by an employer.

Caller: Anyone

Parameter Type Description
employer Address Employer address to query

Returns: Vec<u64>

Migration: Use this canonical name in new clients. streams_by_employer remains available as a deprecated compatibility alias.

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_ids_by_employer --employer <EMPLOYER_ADDRESS>

stream_ids_by_employee

Return all stream IDs paying an employee.

Caller: Anyone

Parameter Type Description
employee Address Employee address to query

Returns: Vec<u64>

Migration: Use this canonical name in new clients. streams_by_employee remains available as a deprecated compatibility alias.

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_ids_by_employee --employee <EMPLOYEE_ADDRESS>

streams_by_employee_paginated

Return a page of stream IDs paying an employee, in creation order. The requested limit is capped at 50; an offset at or beyond the total stream count returns an empty vector.

Caller: Anyone

Parameter Type Description
employee Address Employee address to query
offset u32 Number of stream IDs to skip
limit u32 Maximum number of IDs to return (capped at 50)

Returns: Vec<u64> — stream ID page

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- streams_by_employee_paginated \
  --employee <EMPLOYEE_ADDRESS> --offset 0 --limit 20

streams_in_time_range

Return all stream IDs created within a time window, using the day-bucket timestamp index. Results are unique, deduplicated, and sorted ascending by stream_id. The effective range is capped at 30 days.

Caller: Anyone

Parameter Type Description
from_ts u64 Inclusive lower bound (Unix timestamp in seconds)
to_ts u64 Exclusive upper bound (Unix timestamp in seconds)

Returns: Vec<u64> — unique stream IDs in ascending order for the effective range

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- streams_in_time_range --from_ts 1714569600 --to_ts 1714656000

Migration: streams_in_range remains available as a deprecated alias for compatibility.


stream_count_by_employer

Return the number of streams owned by an employer. Equivalent to stream_ids_by_employer(employer).len() but avoids loading the full ID vector — useful for pagination and dashboards.

Caller: Anyone

Parameter Type Description
employer Address Employer address to query

Returns: u64 — stream count; 0 if the address has no streams

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_count_by_employer --employer <EMPLOYER_ADDRESS>

stream_count_by_employee

Return the number of streams paying an employee. Equivalent to stream_ids_by_employee(employee).len() but avoids loading the full ID vector — useful for pagination and dashboards.

Caller: Anyone

Parameter Type Description
employee Address Employee address to query

Returns: u64 — stream count; 0 if the address has no streams

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_count_by_employee --employee <EMPLOYEE_ADDRESS>

stream_count_by_token

Return the number of streams funded with a token. The count is maintained in an on-chain token index, so callers do not need to scan all streams.

Caller: Anyone

Parameter Type Description
token Address Token contract address to query

Returns: u64 — stream count; 0 if no streams use the token

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_count_by_token --token <TOKEN_ADDRESS>

stream_ids_by_token

Return a page of stream IDs funded with a token, in creation order. The requested limit is capped at 50; an offset at or beyond the token's stream count returns an empty vector.

Caller: Anyone

Parameter Type Description
token Address Token contract address to query
offset u64 Number of stream IDs to skip
limit u64 Maximum number of IDs to return (capped at 50)

Returns: Vec<u64> — stream ID page

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- stream_ids_by_token --token <TOKEN_ADDRESS> --offset 0 --limit 20

admin_nonce

Return the current admin nonce. Use this to build the next admin transaction.

Caller: Anyone

Returns: u64

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- admin_nonce

admin

Return the current contract admin address.

Off-chain tools use this to discover the admin without decoding raw ledger state.

Caller: Anyone

Returns: Address

Errors:

  • Panics with "admin not set" (E021) if the contract has not been initialised.

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- admin

upgrade

Admin upgrades the contract WASM in-place.

Caller: Admin

Parameter Type Description
new_wasm_hash BytesN<32> Hash of the new WASM blob (must be uploaded first)
nonce u64 Current admin nonce (replay protection)

Returns: nothing

Errors:

  • Panics if caller is not the admin
  • Panics if nonce is wrong (E009)

migrate

Migration hook called by the admin after an upgrade to confirm the new WASM is operational. Also writes the compile-time CONTRACT_VERSION constant (1 as of the current release) into instance storage so that version() can be queried off-chain.

Caller: Admin

Parameter Type Description
admin Address Must match the stored admin

Returns: nothing

Side effects:

  • Writes CONTRACT_VERSION to DataKey::Version in instance storage.

Example:

stellar contract invoke --id <STREAM_ID> --source <ADMIN_KEY> --network testnet \
  -- migrate --admin <ADMIN_ADDRESS>

version

Return the contract version stored by the last migrate call. Returns 0 if migrate has never been called (pre-upgrade / initial deployment state).

Caller: Anyone

Returns: u32 — current contract version (1 after the first migrate)

Example:

stellar contract invoke --id <STREAM_ID> --source <ANY_KEY> --network testnet \
  -- version

Stream Templates (PROD-20)

Templates are saved per employer in persistent storage (TTL extended on every save and read). One employer can never read, use or delete another employer's template: lookups are keyed by (employer, template_id).

save_template

Parameter Type Description
employer Address Template owner (requires auth)
template_id u32 Employer-chosen identifier; saving again overwrites
params StreamParams Token, deposit, rate, stop time and optional cliff. employee is ignored when creating from the template

create_stream_from_template

Parameter Type Description
employer Address Template owner (requires auth via create_stream)
employee Address Recipient of the new stream
template_id u32 Template to use

Returns: u64 — the new stream ID. Same validations as create_stream.

Errors: E031 if employer has no template with this ID.

delete_template

Parameter Type Description
employer Address Template owner (requires auth)
template_id u32 Template to delete

Removes the template and drops it from list_templates. Streams already created from it are unaffected. Errors: E031 if no such template exists for this employer.

get_template / list_templates

get_template(employer, template_id) -> Option<StreamParams> and list_templates(employer) -> Vec<(u32, StreamParams)>. Read-only.


Token Contract

The token implements the SEP-41 interface; see sep41-compliance.md for the full checklist, events and deviations. SEP-41 additions in this release: decimals, name, symbol, the admin-only write-once set_metadata(admin, decimal, name, symbol), the transfer / approve / burn / mint events, and transfer taking a MuxedAddress recipient.

initialize

Initialise the token with an admin and an initial supply minted to the admin.

Caller: Admin

Parameter Type Description
admin Address Token admin address
initial_supply i128 Tokens minted to admin on init

Returns: nothing

Example:

stellar contract invoke --id <TOKEN_ID> --source <ADMIN_KEY> --network testnet \
  -- initialize --admin <ADMIN_ADDRESS> --initial_supply 1000000000

total_supply

Return the total token supply.

Caller: Anyone

Returns: i128

Example:

stellar contract invoke --id <TOKEN_ID> --source <ANY_KEY> --network testnet \
  -- total_supply

balance

Return the token balance of an address.

Caller: Anyone

Parameter Type Description
owner Address Address to query

Returns: i128

Example:

stellar contract invoke --id <TOKEN_ID> --source <ANY_KEY> --network testnet \
  -- balance --owner <ADDRESS>

transfer

Transfer tokens from one address to another.

Caller: from (requires auth)

Parameter Type Description
from Address Sender
to Address Recipient
amount i128 Amount to transfer (must be > 0)

Returns: nothing

Errors:

  • Panics if amount ≤ 0
  • Panics if from has insufficient balance

Example:

stellar contract invoke --id <TOKEN_ID> --source <FROM_KEY> --network testnet \
  -- transfer --from <FROM_ADDRESS> --to <TO_ADDRESS> --amount 1000

approve

Approve a spender to transfer tokens on behalf of the owner.

Caller: owner (requires auth)

Parameter Type Description
owner Address Token owner
spender Address Address being approved
amount i128 Allowance amount
expiration_ledger u32 Last ledger sequence at which the allowance is valid

Returns: nothing

Errors:

  • Panics if amount is negative
  • Panics if amount > 0 and expiration_ledger is before the current ledger

Example:

stellar contract invoke --id <TOKEN_ID> --source <OWNER_KEY> --network testnet \
  -- approve --owner <OWNER_ADDRESS> --spender <SPENDER_ADDRESS> --amount 5000 --expiration_ledger 1000000

allowance

Return the allowance granted by owner to spender.

Caller: Anyone

Parameter Type Description
owner Address Token owner
spender Address Approved spender

Returns: i128 — the remaining allowance; 0 if none or expired (SEP-41). Use allowance_with_expiration(owner, spender) -> (i128, u32) to also read the expiration ledger.

Example:

stellar contract invoke --id <TOKEN_ID> --source <ANY_KEY> --network testnet \
  -- allowance --owner <OWNER_ADDRESS> --spender <SPENDER_ADDRESS>

transfer_from

Transfer tokens on behalf of from using an existing allowance.

Caller: spender (requires auth)

Parameter Type Description
spender Address Address with an existing allowance
from Address Token owner
to Address Recipient
amount i128 Amount to transfer

Returns: nothing

Errors:

  • Panics if the allowance has expired
  • Panics if allowance is insufficient
  • Panics if from has insufficient balance
  • T001 if the recipient balance would overflow

Example:

stellar contract invoke --id <TOKEN_ID> --source <SPENDER_KEY> --network testnet \
  -- transfer_from --spender <SPENDER_ADDRESS> --from <FROM_ADDRESS> --to <TO_ADDRESS> --amount 1000

mint

Admin mints new tokens to an address, increasing total supply.

Caller: Admin

Parameter Type Description
admin Address Must match the stored admin
to Address Recipient of minted tokens
amount i128 Amount to mint (must be > 0)
nonce u64 Current admin nonce (see admin_nonce); consumed on success

Returns: nothing

Errors:

  • Panics if caller is not the admin
  • Panics if nonce does not match the stored admin nonce
  • Panics if amount ≤ 0
  • T001 if the recipient balance or total supply would overflow

Example:

stellar contract invoke --id <TOKEN_ID> --source <ADMIN_KEY> --network testnet \
  -- mint --admin <ADMIN_ADDRESS> --to <RECIPIENT_ADDRESS> --amount 1000000 --nonce 0

admin_nonce

Return the admin nonce that the next admin-gated call (mint, mint_to_batch, propose_admin, pause_token, unpause_token, upgrade) must supply. get_admin_nonce is an alias with identical behaviour.

Caller: Anyone

Returns: u64

Example:

stellar contract invoke --id <TOKEN_ID> --source <ANY_KEY> --network testnet \
  -- get_admin_nonce

mint_to_batch

Admin mints tokens to up to 50 recipients in one call. A single admin nonce is consumed for the whole batch, and the batch is all-or-nothing. Emits one mint event per recipient.

Caller: Admin

Parameter Type Description
admin Address Must match the stored admin
nonce u64 Current admin nonce; consumed once
recipients Vec<(Address, i128)> Recipient / amount pairs (each amount > 0)

Returns: nothing

Errors:

  • Panics if caller is not the admin
  • Panics if nonce does not match the stored admin nonce
  • T003 if more than 50 recipients are supplied
  • Panics if any amount ≤ 0
  • T001 if any balance or the total supply would overflow (whole batch reverts)

pause_token / unpause_token

Admin pauses or resumes all token movement. While paused, transfer, transfer_from, burn and burn_from panic with T002. Consumes the admin nonce and emits token_paused with data true (pause) or false (unpause).

Caller: Admin

Parameter Type Description
admin Address Must match the stored admin
nonce u64 Current admin nonce

Returns: nothing

Errors:

  • Panics if caller is not the admin
  • Panics if nonce does not match the stored admin nonce

Example:

stellar contract invoke --id <TOKEN_ID> --source <ADMIN_KEY> --network testnet \
  -- pause_token --admin <ADMIN_ADDRESS> --nonce 0

is_paused

Return whether the token is paused. Caller: Anyone. Returns: bool


propose_admin

Step 1 of two-step admin transfer: the current admin nominates a new admin.

Caller: Admin

Parameter Type Description
new_admin Address Address nominated as the next admin
nonce u64 Current admin nonce; consumed on success

Returns: nothing

Errors:

  • Panics if nonce does not match the stored admin nonce

Example:

stellar contract invoke --id <TOKEN_ID> --source <ADMIN_KEY> --network testnet \
  -- propose_admin --new_admin <NEW_ADMIN_ADDRESS> --nonce 0

accept_admin

Step 2 of two-step admin transfer: the nominated address accepts and becomes admin.

Caller: new_admin (requires auth)

Parameter Type Description
new_admin Address Must match the pending admin

Returns: nothing

Errors:

  • Panics if there is no pending admin
  • Panics if new_admin does not match the pending admin

Example:

stellar contract invoke --id <TOKEN_ID> --source <NEW_ADMIN_KEY> --network testnet \
  -- accept_admin --new_admin <NEW_ADMIN_ADDRESS>

upgrade (token)

fn upgrade(env: Env, new_wasm_hash: BytesN<32>, nonce: u64)

Admin replaces the token WASM in-place; balances and total supply are preserved. Requires admin auth; panics with invalid nonce if nonce ≠ admin_nonce().

migrate (token)

fn migrate(env: Env, admin: Address)

Post-upgrade no-op hook. Requires admin auth and that it matches the stored admin.

burn

Burn tokens from the caller's own balance, reducing total supply.

Caller: from (requires auth)

Parameter Type Description
from Address Address whose tokens are burned
amount i128 Amount to burn (must be > 0)

Returns: nothing

Errors:

  • Panics if amount ≤ 0
  • Panics if from has insufficient balance
  • T002 if the token is paused

Emits burn with topics ("burn", from) and data amount.

Example:

stellar contract invoke --id <TOKEN_ID> --source <FROM_KEY> --network testnet \
  -- burn --from <FROM_ADDRESS> --amount 500

burn_from

Burn tokens on behalf of from using an existing allowance.

Caller: spender (requires auth)

Parameter Type Description
spender Address Address with an existing allowance
from Address Token owner whose tokens are burned
amount i128 Amount to burn (must be > 0)

Returns: nothing

Errors:

  • Panics if amount ≤ 0
  • Panics if the allowance has expired
  • Panics if allowance is insufficient
  • Panics if from has insufficient balance
  • T002 if the token is paused

Deducts amount from both from's balance and spender's allowance. Emits burn with topics ("burn", from) and data amount.

Example:

stellar contract invoke --id <TOKEN_ID> --source <SPENDER_KEY> --network testnet \
  -- burn_from --spender <SPENDER_ADDRESS> --from <FROM_ADDRESS> --amount 500

Events

All events are emitted via env.events().publish(). The topic tuple is (symbol, stream_id) and the data tuple carries the payload described below.

cliff_reached

Emitted once per stream, by the first withdraw, withdraw_all, cancel_stream or settle_stream call at or after the stream's cliff (PROD-17).

Topics: ("v1", "cliff", stream_id) Data: cliff_time: u64


stream_cancelled

Emitted by cancel_stream instead of the generic status event. Carries the exact cash-flow amounts so off-chain indexers can track fund movements without re-simulating the transaction.

Field Type Description
stream_id u64 ID of the cancelled stream (in topics)
employer Address Employer address (refund recipient)
employee Address Employee address (claimable recipient)
claimable_paid i128 Tokens transferred to the employee at cancellation
refund_paid i128 Tokens returned to the employer

Topics: ("cancelled", stream_id) Data: (employer, employee, claimable_paid, refund_paid)


rate_updated

Emitted by update_rate when the employer changes the stream's rate_per_second.

Field Type Description
stream_id u64 ID of the updated stream (in topics)
old_rate i128 Previous rate_per_second value
new_rate i128 New rate_per_second value

Topics: ("rate_upd", stream_id) Data: (old_rate, new_rate)


stop_time_updated

Emitted by update_stop_time when the employer changes a stream's hard stop time.

Field Type Description
stream_id u64 ID of the updated stream (in topics)
old_stop_time u64 Previous hard stop timestamp (0 means indefinite)
new_stop_time u64 New hard stop timestamp (0 means indefinite)

Topics: ("v1", "stop_upd", stream_id) Data: (old_stop_time, new_stop_time)


low_balance

Emitted by withdraw when the stream's remaining deposit (deposit - withdrawn) falls below its non-zero low_balance_threshold. Off-chain systems can subscribe to this event to alert the employer to top up. The threshold is set via create_stream and can be changed via top_up or set_low_balance_threshold.

Field Type Description
stream_id u64 ID of the stream (in topics)
employer Address Employer to notify
remaining i128 Deposit remaining after the withdraw
threshold i128 The stream's low_balance_threshold

Topics: ("low_bal", stream_id) Data: (employer, remaining, threshold)


stream_expired

Emitted by expire_streams when an eligible stream is transitioned from Active to Exhausted.

Field Type Description
stream_id u64 ID of the expired stream (in topics)

Topics: ("expired", stream_id) Data: empty tuple ()


threshold_updated

Emitted by set_low_balance_threshold when the employer updates the low-balance alert threshold for a stream.

Field Type Description
stream_id u64 ID of the updated stream (in topics)
old_threshold i128 Previous low_balance_threshold value
new_threshold i128 New low_balance_threshold value

Topics: ("thresh_up", stream_id) Data: (old_threshold, new_threshold)


Error Codes

Stream contract panics are prefixed with a stable code defined in contracts/stream/src/types.rs. Keep this table in sync with that file (see CONTRIBUTING.md).

Code Constant Meaning Triggered By Recommended Fix
E001 ERR_ZERO_RATE rate_per_second must be > 0 create_stream, create_streams_batch, update_rate Pass a rate_per_second / new_rate ≥ 1
E002 ERR_ZERO_DEPOSIT deposit / amount must be > 0 create_stream, create_streams_batch, set_min_deposit Pass a deposit / amount > 0
E003 ERR_REENTRANT Reentrant withdraw detected (stream locked flag set) withdraw, withdraw_all Do not re-enter withdraw from a token callback; retry in a separate transaction
E004 ERR_OVERFLOW Arithmetic overflow in claimable / balance calculation claimable, claimable_at, withdraw, withdraw_all, top_up Use smaller deposit, rate_per_second, or top-up amount values
E005 ERR_STREAM_CANCELLED Cannot top up a cancelled stream top_up, set_auto_topup, set_low_balance_threshold Create a new stream instead
E006 ERR_STREAM_EXHAUSTED Cannot top up an exhausted stream top_up, set_low_balance_threshold Create a new stream instead
E007 ERR_BELOW_MIN_DEPOSIT Deposit below minimum create_stream, create_streams_batch Deposit at least the minimum (default 10_000, changed via set_min_deposit)
E008 ERR_INVALID_RATE rate_per_second exceeds maximum (1,000,000,000) create_stream, create_streams_batch, update_rate Pass a rate ≤ 1,000,000,000
E009 ERR_BAD_NONCE Invalid admin nonce propose_admin, pause_contract, unpause_contract, set_min_deposit, expire_streams, upgrade, propose_upgrade, execute_upgrade, cancel_upgrade Read the current value with admin_nonce and pass it as nonce
E009 ERR_BAD_NONCE Invalid admin nonce propose_admin, pause_contract, unpause_contract, set_min_deposit, set_protocol_fee, upgrade, propose_upgrade, execute_upgrade, cancel_upgrade Read the current value with admin_nonce and pass it as nonce
E029 ERR_BATCH_TOO_LARGE Batch contains more than 50 streams create_streams_batch, cancel_streams_batch Split the operation into batches of at most 50 streams
E030 ERR_INVALID_STOP_TIME Nonzero stop time precedes last withdrawal update_stop_time Use 0 or a timestamp at/after last_withdraw_time
T001 ERR_OVERFLOW (token) Token arithmetic overflow Token mint, mint_to_batch, transfer, transfer_from Use smaller amounts
T002 ERR_PAUSED (token) Token is paused Token transfer, transfer_from, burn, burn_from Wait for the admin to call unpause_token
T003 ERR_BATCH_TOO_LARGE (token) More than 50 recipients Token mint_to_batch Split into batches of at most 50

Stream Status Lifecycle

Cancelled and Exhausted are terminal states: no function moves a stream out of them.

stateDiagram-v2
    [*] --> Active: create_stream / create_streams_batch
    Active --> Paused: pause_stream / pause_stream_batch
    Paused --> Active: resume_stream / resume_stream_batch
    Active --> Cancelled: cancel_stream / cancel_streams_batch
    Paused --> Cancelled: cancel_stream / cancel_streams_batch
    Active --> Exhausted: withdraw / withdraw_all / settle_stream
    Cancelled --> [*]
    Exhausted --> [*]
Loading

update_rate, top_up, and set_low_balance_threshold do not change a stream's status.

settle_stream (callable by anyone) triggers the Active → Exhausted transition for streams whose stop_time has passed and whose deposit is fully streamed. withdraw performs the same transition automatically when called after stop_time with nothing left to claim.