Refreshes the cached USDC value of the adapter's Blend position to
include yield accrued since the last call. Permissionless: anyone can
call it, and it should be called (by the vault or a keeper) before any
total_assets() read that will inform a deposit or withdrawal price,
since total_assets() itself only returns the cached value rather than
querying Blend live on every call.
Reads the adapter's current bToken balance from Blend's own ledger
(get_positions) rather than self-tracking it, so there is no risk of
drift between the stored total and Blend's actual accounting.
fn accrue(env: soroban_sdk::Env) -> Result<(), ContractError>
Called by the vault after transferring amount USDC to this adapter.
Supplies the USDC to the Blend lending pool as collateral and returns
the real bTokens credited, measured from Blend's own ledger rather
than assumed 1:1, so the vault's adapter-share accounting (ADPT_SH)
tracks genuine, appreciating shares instead of raw principal (#486).
fn deposit(env: soroban_sdk::Env, amount: i128) -> i128
Refreshes the cached total_assets to include yield accrued since the last call, satisfying the shared YieldAdapterInterface contract. Currently just calls accrue(), which remains a public, permissionless entry point in its own right.
Panics on failure rather than returning a Result: the shared adapter interface's refresh() has no error return, so propagating accrue()'s Result would mean changing that interface's ABI across both adapters and the vault's calls into them. Panicking here instead of silently discarding the error preserves this function's pre-existing fail-loud behaviour (accrue()'s storage read used to be a bare unwrap(), which panicked directly) rather than downgrading a real failure into a silent no-op success.
fn refresh(env: soroban_sdk::Env)
Returns the Blend pool this adapter supplies to.
fn get_pool(env: soroban_sdk::Env) -> soroban_sdk::Address
Called by the vault to redeem shares bTokens from the Blend pool.
Blend's own withdraw request is denominated in underlying USDC, not
bTokens, so shares is converted using the current b_rate before
submitting. Returns the USDC amount actually delivered to recipient,
measured directly rather than assumed to equal the request (#489).
fn withdraw(
env: soroban_sdk::Env,
shares: i128,
recipient: soroban_sdk::Address,
) -> i128
Retained so the ABI of adapters already deployed from earlier WASM is unchanged, and so an old adapter can still be initialized by hand.
On any adapter deployed from this WASM it is unreachable:
__constructor has already set VAULT_KEY, so every call returns
AlreadyInitialized. That is the intended behaviour, not a leftover.
An attacker calling this against a freshly deployed adapter is
rejected instead of served.
fn initialize(
env: soroban_sdk::Env,
vault: soroban_sdk::Address,
pool: soroban_sdk::Address,
usdc: soroban_sdk::Address,
) -> Result<(), ContractError>
Returns "blend", identifying which protocol this adapter wraps.
fn get_protocol(env: soroban_sdk::Env) -> soroban_sdk::Symbol
Returns the cached USDC value of the adapter's Blend position. Reflects
yield only as of the last accrue() call; call accrue() first for a
value that includes interest accrued since then.
fn total_assets(env: soroban_sdk::Env) -> i128
Returns the adapter's current bToken share balance, read directly from Blend's ledger rather than self-tracked.
fn total_shares(env: soroban_sdk::Env) -> i128
Links the adapter to its vault, Blend pool, and USDC token.
Runs inside the CreateContract host operation that deploys this
adapter, in the same transaction, so the adapter is never observable
on-ledger in an uninitialized state.
This is what closes the front-running window in #505. initialize()
below has no authorization check by design (there is no deployer
identity in storage yet to check against), so for as long as deploy
and initialize were two separate transactions, anyone watching the
ledger could land initialize() first with their own address as
vault, becoming the only party able to move funds through the
adapter. Adding an auth check to initialize() would not have helped:
it would only prove the racer controls the address they chose to pass
in. Removing the intervening ledger is the fix.
fn __constructor(
env: soroban_sdk::Env,
vault: soroban_sdk::Address,
pool: soroban_sdk::Address,
usdc: soroban_sdk::Address,
)