Halts split — the only way new PT/YT enters existence. Guardian or
admin, so stopping a live exploit does not wait on a multisig.
recombine, redeem_at_maturity, claim_yield and observe_rate are
never blocked: pausing must not strip a holder of their principal or
their accrued yield.
fn pause(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
Pulls sy_amount SY from from into escrow and mints equal PT and YT,
denominated in asset units: face = sy_amount * rate / WAD. At rate 1.00
this equals sy_amount. PT is the fixed principal claim; YT is the yield
claim. The escrow holds the SY shares; their asset value at the current
rate equals the PT face exactly at mint, which is the coverage invariant.
fn split(
env: soroban_sdk::Env,
from: soroban_sdk::Address,
sy_amount: i128,
) -> Result<(i128, i128), soroban_sdk::Error>
Moves a non-protocol token out of escrow. SY, PT and YT are all refused: SY is the escrow, and PT/YT arriving here are mid-flight in a flash route.
fn sweep(
env: soroban_sdk::Env,
token_id: soroban_sdk::Address,
to: soroban_sdk::Address,
) -> Result
fn config(env: soroban_sdk::Env) -> Result
Admin only — the deliberate half of the asymmetric pair.
fn unpause(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
fn guardian(env: soroban_sdk::Env) -> Result
fn maturity(env: soroban_sdk::Env) -> Result
PT and YT balances the holder currently owns, read from the token contracts.
fn position(
env: soroban_sdk::Env,
holder: soroban_sdk::Address,
) -> Result
fn is_paused(env: soroban_sdk::Env) -> bool
Burns equal PT and YT (asset units) from from and returns principal in SY
shares: pt_amount * WAD / rate, capped to the holder's pro-rata share of
escrow under a shortfall (identical cap to redeem_at_maturity). Burning the
YT settles the holder's accrued yield first (the YT burn hook banks it into
the holder's claim ledger), so recombine returns only principal and the
banked yield stays owed and covered by the remaining escrow. Never reverts on
collateralization: a shortfall is priced as a haircut, matching Pendle.
fn recombine(
env: soroban_sdk::Env,
from: soroban_sdk::Address,
pt_amount: i128,
yt_amount: i128,
) -> Result
fn initialize(
env: soroban_sdk::Env,
admin: soroban_sdk::Address,
sy_token: soroban_sdk::Address,
pt_token: soroban_sdk::Address,
yt_token: soroban_sdk::Address,
maturity: u64,
) -> Result<(), soroban_sdk::Error>
fn is_matured(env: soroban_sdk::Env) -> Result
Pays holder their accrued YT yield in SY out of escrow, capped so PT
principal is always senior to banked YT yield, and returns the SY amount
paid. Allowed any time, including after maturity, so a holder can always
collect yield earned over the term.
PT-senior surplus cap. The YT contract settles the holder and reports the
banked total owed WITHOUT zeroing it (settle). The tokenizer then pays
only min(owed, surplus), where
surplus = max(0, escrow_shares - pt_face_reservation)
and pt_face_reservation = ceil(pt_supply * WAD / rate) is the SY escrow
needed to redeem every outstanding PT at its face at rate. The
reservation is rounded UP, so PT is never shorted by a rounding notch and
the surplus is the conservative (smaller) amount. It then consumes
exactly pay from the YT ledger and pushes pay SY. Anything owed beyond
pay stays banked in the YT ledger, claimable later once the rate
recovers (a transient sub-stroop dip) or, under a permanent slash, capped
there forever by the short escrow:
fn claim_yield(
env: soroban_sdk::Env,
holder: soroban_sdk::Address,
) -> Result
SY the tokenizer custodies, equal to the outstanding PT (and YT) supply.
fn escrowed_sy(env: soroban_sdk::Env) -> Result
fn accept_admin(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
fn is_renounced(env: soroban_sdk::Env) -> bool
Permissionless: before maturity, read the live SY rate and record it as the latest observation the maturity freeze may use. Every mutating operation records one as a side effect; this poke exists so anyone (a keeper, or a YT holder who wants the freeze to credit yield accrued right up to maturity) can refresh the observation on an otherwise idle market without moving tokens. Returns the observed rate.
After maturity, delegates to freeze_maturity_rate instead of erroring,
so a keeper polling this single entrypoint never dead-ends on a bare
Error::Matured with no indication of what to call instead.
fn observe_rate(env: soroban_sdk::Env) -> Result
fn set_guardian(
env: soroban_sdk::Env,
guardian: soroban_sdk::Address,
) -> Result<(), soroban_sdk::Error>
The frozen maturity rate, or 0 if not yet snapshotted.
fn maturity_rate(env: soroban_sdk::Env) -> Result
fn pending_admin(env: soroban_sdk::Env) -> Option
PT and YT minted for sy_amount SY at the current rate, in asset units.
fn preview_split(
env: soroban_sdk::Env,
sy_amount: i128,
) -> Result<(i128, i128), soroban_sdk::Error>
fn propose_admin(
env: soroban_sdk::Env,
new_admin: soroban_sdk::Address,
) -> Result<(), soroban_sdk::Error>
fn cancel_upgrade(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
fn renounce_admin(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
fn execute_upgrade(env: soroban_sdk::Env) -> Result<(), soroban_sdk::Error>
fn pending_upgrade(env: soroban_sdk::Env) -> Option<(soroban_sdk::BytesN<32>, u64)>
fn propose_upgrade(
env: soroban_sdk::Env,
wasm_hash: soroban_sdk::BytesN<32>,
) -> Result
SY shares returned for recombining equal PT and YT (asset units) at the
current rate. This is the principal only; any accrued YT yield is settled
separately into the holder's claim ledger. Mirrors recombine exactly,
including the pro-rata escrow cap, so the preview never overquotes
during a rate-regression shortfall.
Point-in-time read of the live Blend SY rate: if the rate moves between
this quote and submission, the executed recombine share count can
differ. The underlying value redeemed does not — recombine always
returns pt_face worth of principal regardless of rate, so a moved rate
changes the SY share count, not what it's worth. recombine has no
on-chain min_sy_out floor by design; a caller needing an exact share
count should compare this preview to its bound client-side before
submitting.
fn preview_recombine(
env: soroban_sdk::Env,
pt_amount: i128,
yt_amount: i128,
) -> Result
After maturity, burns pt_amount PT (asset units) from from and returns
principal in SY shares: pt_amount * WAD / rate, capped to the holder's
pro-rata share of escrow.
Insolvency guard: if a rate regression (negative yield, a slash) has left
the escrow unable to cover all PT principal, the payout is capped to
escrow_shares * pt_amount / pt_supply, so PT holders share the shortfall
pro-rata rather than letting the first redeemers drain the escrow at the
expense of the last. When solvent, the ideal payout is the smaller of the
two, so this pays principal in full. Capping preserves the escrow/PT ratio,
keeping every later redeemer's share fair.
The rate read here is the current SY rate; Phase 3 step 9 snapshots a maturity rate so post-maturity rate moves do not change redemption.
fn redeem_at_maturity(
env: soroban_sdk::Env,
from: soroban_sdk::Address,
pt_amount: i128,
) -> Result
Permissionless: after maturity, snapshot and return the SY rate used for
all redemption. Any caller may poke this so the maturity rate is captured
promptly; redemption also snapshots it lazily on first use. Idempotent
once set. The snapshot is the last rate observed at or before maturity,
never a live post-maturity read (see effective_rate), so the timing of
this call cannot move value between PT and YT.
fn freeze_maturity_rate(env: soroban_sdk::Env) -> Result