Contract 2cec7e749d46b96a392be85dd284b8a988261ab7716b2218c7a5f39bbe2162db

← Back to Index 📥 Download WASM

Meta

cliver 28.0.0#300aaf69ab100536678bdb641428b06f06b318ea
rssdkver 27.0.6#60926a20d1f9f0a669d5fe551636f42a1302f0c0
rsver 1.96.0

Instances

  • CB3LZVWKGGWSYHHIE7ILK5CJH2MLUB6SWAU7UK6PMQEP3AESD3DAUBRC

Interface

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

Imports

WebAssembly Text (WAT) ▶