Visual lifecycle documentation for stateful StellarForge contracts.
Tokens vest linearly after a cliff period. The admin can cancel at any time to reclaim unvested tokens.
stateDiagram-v2
[*] --> Active : initialize()
Active --> Active : claim()\n[cliff reached, tokens available]
Active --> Cancelled : cancel()\n[admin only]
Active --> CliffReached : time >= start + cliff_seconds
CliffReached --> CliffReached : claim()\n[partial tokens]
CliffReached --> FullyVested : time >= start + duration_seconds
CliffReached --> Cancelled : cancel()\n[admin only]
FullyVested --> FullyVested : claim()\n[remaining tokens]
FullyVested --> Cancelled : cancel()\n[admin only, no unvested tokens]
Cancelled --> [*]
Notes:
CliffReachedandFullyVestedare logical sub-states ofActive(thecancelledflag is the only on-chain state bit).claim()reverts withCliffNotReachedbefore the cliff,NothingToClaimif all vested tokens are already withdrawn, andCancelledafter cancellation.cancel()auto-transfers any unvested tokens back to the admin.
Tokens stream per-second from sender to recipient. The sender can cancel early; the recipient can withdraw at any time.
stateDiagram-v2
[*] --> Active : create_stream()\n[tokens locked in contract]
Active --> Active : withdraw()\n[recipient pulls accrued tokens]
Active --> Finished : time >= end_time
Active --> Cancelled : cancel_stream()\n[sender only]
Finished --> Finished : withdraw()\n[recipient pulls remaining tokens]
Finished --> Cancelled : cancel_stream()\n[no-op: stream already done]
Cancelled --> [*]
Notes:
Finishedmeansnow >= end_time; the stream is no longer accruing but unclaimed tokens are still withdrawable.cancel_stream()atomically pays out accrued tokens to the recipient and refunds unstreamed tokens to the sender.withdraw()reverts withNothingToWithdrawif no tokens have accrued since the last withdrawal, andAlreadyCancelledon a cancelled stream.
Token-weighted proposals go through voting, optional failure/pass finalization, a timelock delay, and then execution.
stateDiagram-v2
[*] --> Active : propose()\n[voting opens immediately]
Active --> Active : vote()\n[accumulate for/against weight]
Active --> Passed : finalize()\n[votes_for > votes_against\nAND total >= quorum]
Active --> Failed : finalize()\n[quorum not met\nOR votes_against >= votes_for]
Passed --> Executed : execute()\n[after timelock_delay elapsed]
Failed --> [*]
Executed --> [*]
Notes:
finalize()can only be called aftervote_end(i.e.now > vote_start + voting_period).execute()reverts withTimelockNotElapsedif called beforepassed_at + timelock_delay.- There is no on-chain
Cancelledstate for proposals in the current implementation. - Voting weight is caller-supplied; integrators should pass the voter's token balance as
weight.