fn pause(env: soroban_sdk::Env)
fn stake(
env: soroban_sdk::Env,
staker: soroban_sdk::Address,
amount: i128,
beneficiary: soroban_sdk::Address,
) -> Result<(), PoolError>
fn unpause(env: soroban_sdk::Env)
fn withdraw(
env: soroban_sdk::Env,
staker: soroban_sdk::Address,
beneficiary: soroban_sdk::Address,
) -> Result<(), PoolError>
fn get_claim(env: soroban_sdk::Env, claim_id: soroban_sdk::BytesN<32>) -> Option
fn get_stake(
env: soroban_sdk::Env,
wallet: soroban_sdk::Address,
) -> Option
fn get_vault(env: soroban_sdk::Env) -> Option
fn is_paused(env: soroban_sdk::Env) -> bool
Sets the DeFindex vault address. Refuses while shares are held.
Deploy SAFU's own vault via the DeFindex factory with
upgradable = false and vault_fee = 0 — both are constructor
arguments on the real vault (verified on-chain 2026-08-14). With
upgradable = false the vault's upgrade(new_wasm_hash) path is
permanently dead, which removes the Manager-can-upgrade custody risk
structurally rather than mitigating it procedurally.
fn set_vault(
env: soroban_sdk::Env,
vault_address: soroban_sdk::Address,
) -> Result<(), PoolError>
fn set_oracle(
env: soroban_sdk::Env,
new_oracle: soroban_sdk::Address,
) -> Result<(), PoolError>
V8 isEligible: has an active, non-withdrawn, non-suspended
stake. Does NOT check the time gate — see is_claim_eligible.
fn is_eligible(env: soroban_sdk::Env, wallet: soroban_sdk::Address) -> bool
fn cancel_claim(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
fn claim_stream(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
beneficiary: soroban_sdk::Address,
) -> Result
fn set_pool_cap(env: soroban_sdk::Env, new_cap: i128) -> Result<(), PoolError>
fn set_treasury(
env: soroban_sdk::Env,
treasury: soroban_sdk::Address,
) -> Result<(), PoolError>
CHANGED T2/D1: deadline + signature are now required arguments.
When caller is the oracle the contract verifies an Ed25519
signature over the verdict on-chain; when caller is the admin
(manual fallback) both are ignored, exactly as in V8. See
claim::submit_claim for the full auth-model rationale.
fn submit_claim(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
wallet: soroban_sdk::Address,
tx_hash: soroban_sdk::BytesN<32>,
entitlement: i128,
tier: u32,
hack_timestamp: u64,
deadline: u64,
signature: soroban_sdk::BytesN<64>,
) -> Result, PoolError>
Deploy-time initialization.
CHANGED 2026-08-17 (7a audit, Finding 3): was a separate initialize
entrypoint. Renamed to __constructor, the SDK-recognised constructor
hook, which runs as part of the deploy invocation itself.
Why this mattered: initialize authorized the admin argument
passed to it, and the only thing stopping a second call was the
AlreadyInitialized guard. That guard protects against
re-initialization but not against being first — between the deploy
transaction and the legitimate init transaction, anyone observing the
chain could call initialize with themselves as admin and satisfy
require_auth trivially. The T1 testnet deployment used two separate
transactions (README), so the window was real rather than theoretical.
Stellar's own guidance is explicit: prefer __constructor because it
"removes the front-running window between deploy and init", and note
that a contract deployed without one can never gain it afterwards —
which is why this had to change before D4's d
fn __constructor(
env: soroban_sdk::Env,
admin: soroban_sdk::Address,
oracle: soroban_sdk::Address,
oracle_pubkey: soroban_sdk::BytesN<32>,
co_signer: soroban_sdk::Address,
xlm_token: soroban_sdk::Address,
pool_cap: i128,
) -> Result<(), PoolError>
NEW 2026-07-22 (Rule A) — staker-authorized. Burns the wallet's
entire lifetime points balance, forfeits the stake, starts
cooldown/vesting. Must be called within APPROVE_WINDOW_LEDGERS of
the claim entering AwaitingApproval.
fn approve_claim(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
Redeem a tranche and send only the excess above proportional principal to treasury. Returns the yield amount (0 on a loss).
fn extract_yield(
env: soroban_sdk::Env,
shares: i128,
min_xlm_out: i128,
) -> Result
fn set_co_signer(
env: soroban_sdk::Env,
new_co_signer: soroban_sdk::Address,
) -> Result<(), PoolError>
fn suspend_stake(
env: soroban_sdk::Env,
wallet: soroban_sdk::Address,
) -> Result<(), PoolError>
fn emergency_exit(
env: soroban_sdk::Env,
staker: soroban_sdk::Address,
) -> Result<(), PoolError>
fn get_deploy_bps(env: soroban_sdk::Env) -> i128
Max fraction of total_staked deployable, in bps. Starts at 0, so
the yield layer is inert until admin opts in. Hard-capped at
MAX_DEPLOY_BPS (80%); 5000 (50%) is the T2 recommendation.
fn set_deploy_bps(env: soroban_sdk::Env, bps: i128) -> Result<(), PoolError>
fn transfer_admin(
env: soroban_sdk::Env,
new_admin: soroban_sdk::Address,
) -> Result<(), PoolError>
Send already-liquid excess above staker principal to treasury.
fn withdraw_yield(env: soroban_sdk::Env, amount: i128) -> Result<(), PoolError>
Supply liquid XLM into the vault. Returns shares gained.
min_shares_out is the caller's floor against an adverse share
price — quote it off-chain via the vault's
get_asset_amounts_per_shares first.
fn deploy_to_vault(
env: soroban_sdk::Env,
amount: i128,
min_shares_out: i128,
) -> Result
T2/D1 — admin-only. Cancels a signed-but-not-yet-submitted oracle
approval. Takes the full approval parameters rather than a
precomputed hash (V8's shape); see claim::revoke_approval for why.
fn revoke_approval(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
wallet: soroban_sdk::Address,
tx_hash: soroban_sdk::BytesN<32>,
entitlement: i128,
tier: u32,
hack_timestamp: u64,
deadline: u64,
) -> Result<(), PoolError>
fn set_beneficiary(
env: soroban_sdk::Env,
staker: soroban_sdk::Address,
new_beneficiary: soroban_sdk::Address,
) -> Result<(), PoolError>
claim_id optional — pass the wallet's in-flight claim (if any) so
its Rule A/B deadline clock resets on unsuspend (eng review
blocker #1). None if the wallet has no claim needing a reset.
fn unsuspend_stake(
env: soroban_sdk::Env,
wallet: soroban_sdk::Address,
claim_id: Option>,
) -> Result<(), PoolError>
fn approve_override(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
wallet: soroban_sdk::Address,
tx_hash: soroban_sdk::BytesN<32>,
entitlement: i128,
tier: u32,
) -> Result<(), PoolError>
T3 (2026-08-24) — permissionless sibling of provide_liquidity.
Pulls back only enough to cover total_allocated if liquid balance
is short of it. No caller input. Works while paused, same reasoning
as provide_liquidity. Returns 0 if nothing's short.
fn ensure_liquidity(env: soroban_sdk::Env) -> Result
fn get_total_staked(env: soroban_sdk::Env) -> i128
fn get_total_stakers(env: soroban_sdk::Env) -> u32
V8 yieldBalance(): (liquid + deployed) − total_staked, floored at 0.
fn get_yield_balance(env: soroban_sdk::Env) -> i128
V8 isClaimEligible: is_eligible AND the 90-day time gate has
passed.
fn is_claim_eligible(env: soroban_sdk::Env, wallet: soroban_sdk::Address) -> bool
Redeem shares so the XLM sits liquid in the contract, ready to fund
payouts and withdrawals. Nothing leaves the pool. Works while
paused, deliberately — emergency_exit depends on it.
fn provide_liquidity(
env: soroban_sdk::Env,
shares: i128,
min_xlm_out: i128,
) -> Result
T2/D1 — rotates the oracle's Ed25519 attestation key. Distinct from
set_oracle, which rotates the policy Address; see admin.rs for why
the two are separate identities and what rotation costs.
fn set_oracle_pubkey(
env: soroban_sdk::Env,
new_pubkey: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
NEW 2026-07-22 (Rule B sweep) — permissionless. Releases whatever's
left uncollected if the staker goes COLLECTION_INACTIVITY_LEDGERS
with zero claim_stream activity.
fn expire_stale_claim(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
Real liquid XLM held by the contract — distinct from
get_total_staked, which counts XLM sitting in the vault too.
fn get_liquid_balance(env: soroban_sdk::Env) -> i128
V8 pointsOf: live-computed if still staked and not withdrawn,
else the banked balance. NOT a raw storage read.
fn get_points_balance(env: soroban_sdk::Env, wallet: soroban_sdk::Address) -> i128
T3 (2026-08-24) — permissionless. Sweeps a queued claim whose claim window ran out before capacity ever freed.
fn expire_queued_claim(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
fn get_total_allocated(env: soroban_sdk::Env) -> i128
T2/D1 — rotates BOTH oracle identities in one call. Prefer this over
set_oracle + set_oracle_pubkey in sequence whenever the whole
oracle is being replaced; see admin.rs for the drift window the
two-step leaves open and why it is a port-introduced hazard rather
than a V8 parity cost.
fn set_oracle_identity(
env: soroban_sdk::Env,
new_oracle: soroban_sdk::Address,
new_pubkey: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
fn unlock_pending_claim(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
T3 (2026-08-24) — permissionless sibling of deploy_to_vault. Puts
idle liquidity (above what's reserved for live claims) to work
automatically, up to the existing deploy_bps ceiling. No caller
input — the contract computes the amount and its own slippage
floor. Requires at least one prior manual deploy_to_vault call
(needs a reference rate); returns 0 if nothing's idle or no room
remains under the ceiling.
fn auto_deploy_liquidity(env: soroban_sdk::Env) -> Result
XLM in the vault at ORIGINAL deposit value, never marked to market.
fn get_total_deployed_xlm(env: soroban_sdk::Env) -> i128
fn cancel_pending_override(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
wallet: soroban_sdk::Address,
tx_hash: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
NEW 2026-07-22 (Rule A sweep) — permissionless, mirrors
unlock_pending_claim. Releases the reservation back to the pool
if the staker never approved within the window.
fn expire_pending_approval(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
Current deployed fraction in bps. May read above get_deploy_bps
after a large withdrawal — that is drift, not a breach; see vault.rs.
fn get_deployment_ratio_bps(env: soroban_sdk::Env) -> i128
T3 (2026-08-24) — permissionless. Re-checks a queued claim's original
blocker (DailyStressCapExceeded/Insolvent) against current state;
admits it for real if that now passes, otherwise returns
QueueReleaseNotYetEligible and it stays Reserved.
fn try_release_queued_claim(
env: soroban_sdk::Env,
claim_id: soroban_sdk::BytesN<32>,
) -> Result<(), PoolError>
fn get_total_deployed_shares(env: soroban_sdk::Env) -> i128
fn get_total_extracted_yield(env: soroban_sdk::Env) -> i128