Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions packages/contracts/blend-adapter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,15 +368,25 @@ impl MeridianBlendAdapter {
Ok(())
}

/// 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
pub fn total_shares(env: Env) -> i128 {
let pool: Address = adapter_common::get_or_not_initialized::<_, ContractError>(
&env,
env.storage().instance().get(&POOL_KEY),
);
let usdc = get_usdc(&env);
let client = BlendPoolClient::new(&env, &pool);
let index = client.get_reserve(&usdc).config.index;
client
.get_positions(&env.current_contract_address())
.collateral
.get(index)
.unwrap_or(0)
}

/// Refreshes the adapter's cached total_assets by attempting to accrue yield.
/// A failure to accrue (e.g., if the underlying pool reverts) must bubble up
/// rather than being swallowed, to prevent the vault from operating on stale
/// pricing. This adapter relies on the standard panic handler: bubbling by

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring reads as broken prose now: "This adapter relies on the standard panic handler: bubbling by discarding the error preserves this function's pre-existing fail-loud behaviour..." "bubbling" and "discarding" are opposites, and the sentence doesn't parse. Looks like the new first three lines were spliced onto the tail of the old docstring without removing the leftover fragment. Please rewrite this as one coherent comment.

/// 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
Expand Down
10 changes: 10 additions & 0 deletions packages/contracts/defindex-adapter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,16 @@ impl MeridianDefindexAdapter {
amounts.get(0).unwrap_or(0)
}

pub fn total_shares(env: Env) -> i128 {
let dfx: Address = adapter_common::get_or_not_initialized::<_, ContractError>(
&env,
env.storage().instance().get(&DFX_VAULT),
);
let adapter = env.current_contract_address();
let client = DefindexVaultClient::new(&env, &dfx);
client.balance(&adapter)
}

/// No-op: DeFindex's total_assets() already prices live on every call
/// via the vault's exchange rate, so there is no cache to refresh.
pub fn refresh(_env: Env) {}
Expand Down
24 changes: 22 additions & 2 deletions packages/contracts/vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub trait YieldAdapterInterface {
fn deposit(env: Env, amount: i128) -> i128;
fn withdraw(env: Env, shares: i128, recipient: Address) -> i128;
fn total_assets(env: Env) -> i128;
/// The adapter's current protocol-share balance, read from the underlying
/// protocol's own ledger rather than self-tracked. Lets the vault reconcile
/// ADPT_SH instead of estimating its decrements.
fn total_shares(env: Env) -> i128;
/// Refreshes the adapter's cached total_assets before it is read for
/// deposit/withdraw pricing. A no-op for adapters that already price
/// live on every call.
Expand Down Expand Up @@ -241,7 +245,7 @@ impl MeridianVault {
.set(&TOTAL_SH, &(total_shares + shares_to_mint));
env.storage()
.instance()
.set(&ADPT_SH, &(total_adapter_shares + adapter_shares));
.set(&ADPT_SH, &AdapterClient::new(&env, &adapter_addr).total_shares());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that ADPT_SH is set from AdapterClient::total_shares() here, total_adapter_shares (declared at line 197 from the old ADPT_SH storage read) and adapter_shares (declared at line 237 from AdapterClient::deposit()'s return value) are both unused. cargo clippy -- -D warnings fails on this, confirmed locally. deposit() still needs to call AdapterClient::deposit(&amount) for its side effect, but the return value can be discarded, and the storage read at line 197 can be removed outright.


// Stamp the entry time on the caller's first deposit; top-ups keep
// the original time. Keyed off whether an entry record exists rather
Expand Down Expand Up @@ -354,7 +358,7 @@ impl MeridianVault {
.set(&TOTAL_SH, &(total_shares - shares));
env.storage()
.instance()
.set(&ADPT_SH, &(total_adapter_shares - adapter_shares_to_burn));
.set(&ADPT_SH, &AdapterClient::new(&env, &adapter_addr).total_shares());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cargo fmt --all -- --check fails on this line and its counterpart in deposit() above; run cargo fmt before pushing.


let remaining = caller_shares - shares;

Expand Down Expand Up @@ -809,6 +813,10 @@ mod tests {
mock_total_assets(&env, &usdc)
}

pub fn total_shares(env: Env) -> i128 {
env.storage().instance().get(&MA_SH).unwrap_or(0)
}

pub fn refresh(_env: Env) {
// No-op: MockAdapter already prices total_assets() live.
}
Expand Down Expand Up @@ -868,6 +876,10 @@ mod tests {
mock_total_assets(&env, &usdc)
}

pub fn total_shares(env: Env) -> i128 {
env.storage().instance().get(&LA_SH).unwrap_or(0)
}

pub fn refresh(_env: Env) {
// No-op: LossyMockAdapter already prices total_assets() live.
}
Expand Down Expand Up @@ -919,6 +931,10 @@ mod tests {
mock_total_assets(&env, &usdc)
}

pub fn total_shares(env: Env) -> i128 {
env.storage().instance().get(&ZS_SH).unwrap_or(0)
}

pub fn refresh(_env: Env) {
// No-op: ZeroShareMockAdapter already prices total_assets() live.
}
Expand Down Expand Up @@ -994,6 +1010,10 @@ mod tests {
env.storage().instance().get(&CM_TOTAL).unwrap_or(0)
}

pub fn total_shares(env: Env) -> i128 {
env.storage().instance().get(&CM_SH).unwrap_or(0)
}

pub fn refresh(env: Env) {
// USDC address is always set in initialize(), so this is safe.
let usdc: Address =
Expand Down
Loading