The Blend pool address (read-only view, handy for the frontend / dashboard).
fn pool(env: soroban_sdk::Env) -> soroban_sdk::Address
fn admin(env: soroban_sdk::Env) -> soroban_sdk::Address
Withdraw shares worth of underlying from Blend and send it to to. Returns underlying out.
fn redeem(env: soroban_sdk::Env, to: soroban_sdk::Address, shares: i128) -> i128
Pull amount USDC from from (the wrapper) and supply it to Blend. Returns shares minted.
fn deposit(env: soroban_sdk::Env, from: soroban_sdk::Address, amount: i128) -> i128
Human-readable semver of the source build (informational; for verifiable identity use
[Self::code_hash]).
fn version(env: soroban_sdk::Env) -> soroban_sdk::String
fn timelock(env: soroban_sdk::Env) -> u64
The live deployed WASM hash (32-byte SHA-256) of the running code β reflects the current build even across upgrades.
fn code_hash(env: soroban_sdk::Env) -> soroban_sdk::BytesN<32>
One-shot, admin-gated initialization.
admin β operational admin (pause/upgrade path; cannot move user funds).wrapper β the Spield wrapper; the sole authorized caller of deposit/redeem.pool β the Blend pool contract.underlying β the asset SAC (USDC) to supply.max_apr_bps β max allowed annual b_rate growth, in basis points (the sanity bound,
pro-rated by elapsed time on each read). Set generously above Blend's real max borrow APR
(e.g. 30_000 = 300%) so honest reads always pass while a wild read is still caught.fn initialize(
env: soroban_sdk::Env,
wrapper: soroban_sdk::Address,
pool: soroban_sdk::Address,
underlying: soroban_sdk::Address,
max_apr_bps: u32,
)
fn rate_bound(env: soroban_sdk::Env) -> (i128, u64, u32)
The underlying asset SAC.
fn underlying(env: soroban_sdk::Env) -> soroban_sdk::Address
Accept a pending admin proposal (step 2 of 2). Must be called by the proposed admin.
fn accept_admin(env: soroban_sdk::Env)
Live Blend b_rate (SCALAR_12), with the sanity bound applied and last_rate advanced.
fn current_rate(env: soroban_sdk::Env) -> i128
Where claimed BLND goes. Defaults to the admin until set.
fn emissions_to(env: soroban_sdk::Env) -> soroban_sdk::Address
fn set_timelock(env: soroban_sdk::Env, secs: u64)
Total shares this strategy holds in Blend (the wrapper's whole position).
fn total_shares(env: soroban_sdk::Env) -> i128
Atomic deploy-time constructor (no deployβinit front-run). Binds admin the moment the
strategy exists; the remaining [Self::initialize] is then gated to this admin.
fn __constructor(env: soroban_sdk::Env, admin: soroban_sdk::Address)
fn apply_upgrade(env: soroban_sdk::Env)
fn pending_admin(env: soroban_sdk::Env) -> Option
Propose a new admin (step 1 of 2). Current admin authorizes; the proposed admin must then
call accept_admin to take control.
fn propose_admin(env: soroban_sdk::Env, new_admin: soroban_sdk::Address)
fn cancel_upgrade(env: soroban_sdk::Env)
Underlying value of shares at the live rate.
fn position_value(env: soroban_sdk::Env, shares: i128) -> i128
Claim accrued BLND to [Self::emissions_to]. Permissionless.
Returns the amount claimed β 0 when there is nothing, which is not an error and must not
panic: a keeper calls this on a schedule and an empty claim is the normal case.
BLND is not re-supplied into Blend, and that is deliberate rather than lazy. SR is minted
one-for-one with the b_tokens Blend returns, and its exchange rate is Blend's own b_rate β
not position / supply. Re-supplying would therefore mint b_tokens that no SR is entitled
to: b_rate would not move, no redemption could reach the extra value, and Sr::realizable_rate
would start reporting above Sr::exchange_rate β a number users could see and never realise.
Routing BLND to the treasury keeps every SR invariant exactly as it is.
fn claim_emissions(env: soroban_sdk::Env) -> i128
fn pending_upgrade(env: soroban_sdk::Env) -> Option
Widen (or tighten) the max annual b_rate growth, in basis points (the bound is then
pro-rated by elapsed time on each read β see check_rate_bound_timed). Admin only.
This is the safety valve for the soft-brick risk: if Blend's real b_rate ever rises faster
than the configured annual ceiling, every current_rate read would otherwise panic and
freeze the whole protocol (no claims/redeems/mints/solvency reads). The admin can raise the
ceiling to unstick it without a redeploy. last_rate/last_ts are preserved.
Intentionally immediate (not timelocked): it is a liveness safety valve that can only widen
tolerance on an already-trusted, monotonic Blend rate β it can never mint value or move funds.
Raising it does not bypass any solvency check; the wrapper still asserts backing >= principal
against Blend's real position on every mutation.
fn set_max_apr_bps(env: soroban_sdk::Env, max_apr_bps: u32)
The recovery valve for a b_rate DECREASE. Lowers the stored last_rate high-water
mark to the pool's live b_rate, re-stamping last_ts to now. Admin only. Returns the
floor in effect after the call.
check_rate_bound_timed rejects current < last outright, because Blend documents b_rate
as monotonic non-decreasing. If Blend ever socialises bad debt and the rate dips by even one
stroop, that guard sits under current_rate, which sits under every wrapper entry point
β mint, claim_yield, redeem_pt (even at maturity), combine_and_redeem,
position_value and solvency all revert RateOutOfBounds. Only transfer_position
survives (it never reads the rate), so a position could be moved but never exited, and the
freeze lasts until Blend's rate climbs back above the high-water mark β with no admin
action, timelock, or upgrade able to shorten it.
[Self::set_max_apr_bps] is not a substitute: it widens the upper ceiling, and this
failure is the lower monotonicity guard. Wideni
fn reset_rate_floor(env: soroban_sdk::Env) -> i128
Schedule a contract upgrade to wasm_hash, applyable after the timelock. Returns the eta.
fn schedule_upgrade(env: soroban_sdk::Env, wasm_hash: soroban_sdk::BytesN<32>) -> u64
Admin-only. The destination is fixed precisely so that [Self::claim_emissions] can be open
to anyone without letting the caller choose where the money lands.
fn set_emissions_to(env: soroban_sdk::Env, to: soroban_sdk::Address)
Withdraw exactly amount underlying from Blend and send it to to. Returns shares burned.
fn redeem_underlying(
env: soroban_sdk::Env,
to: soroban_sdk::Address,
amount: i128,
) -> i128
The current b_rate sanity bound: (last_rate, last_ts, max_apr_bps). View for monitoring β
lets ops watch the last observed rate/time and the configured annual-growth ceiling.
Underlying the pool can actually pay out right now (tofix.md #20).
A withdrawal does not fail because the protocol is insolvent β it fails because borrowers have taken the supply and the pool has nothing on hand. Nothing on chain surfaced that in advance, so an exit either worked or reverted with no way to find out first, and no way to take a smaller amount that would have succeeded.
The bound is the pool's own token balance minus backstop_credit: a lending pool cannot
hand out what it does not hold, and part of what it holds is already owed to the backstop.
Still an upper bound, but a tight one. Touching the pool accrues a little more
backstop_credit, so by the time a withdrawal is evaluated the true limit has moved a hair
below what was read. Measured at 0.001% on a mainnet-parameter pool aged six month
fn available_liquidity(env: soroban_sdk::Env) -> i128
BLND accrued to this strategy and not yet claimed. Pure. 0 when the pool has no
emissions configured for our supply side, which is the case on mainnet today.
fn claimable_emissions(env: soroban_sdk::Env) -> i128
Cancel a pending admin proposal. Current admin only.
fn cancel_admin_transfer(env: soroban_sdk::Env)
Loss accounting. What the whole Blend position is really worth right now β live
b_rate, no monotonicity guard, no write.
[Self::current_rate] refuses a fallen rate, which freezes every deposit, sync and
redemption until [Self::reset_rate_floor]. That is the right safety behaviour and the
wrong behaviour for a question a user is entitled to an answer to during the freeze:
how much money actually exists? This reads the venue and reports it, unguarded.
It is therefore the only rate path here that can report a number LOWER than the stored
high-water mark. Never route a deposit or a redemption through it β the guard exists for a
reason. Sr::realizable_rate composes it into the per-share figure a UI should quote.
fn position_value_unguarded(env: soroban_sdk::Env) -> i128