Skip to content

MorphoERC4626SwapConnectors: gate redeem quotes on servicable liquidity (v0 production line) - #177

Merged
Kay-Zee merged 1 commit into
v0from
fix/morpho-4626-liquidity-aware-quotes-v0
Sep 1, 2026
Merged

MorphoERC4626SwapConnectors: gate redeem quotes on servicable liquidity (v0 production line)#177
Kay-Zee merged 1 commit into
v0from
fix/morpho-4626-liquidity-aware-quotes-v0

Conversation

@Kay-Zee

@Kay-Zee Kay-Zee commented Aug 31, 2026

Copy link
Copy Markdown
Member

What this is

The #175 changeset (liquidity-aware Morpho ERC4626 redeem quotes + VaultV2Lens support) cherry-picked onto v0, the branch production actually tracks. Clean cherry-pick of the squash commit b8d5cb6; no conflicts.

See #175 for the full problem statement, design, and verification. This PR exists because deployed mainnet contracts track v0, not main — deploying the fix requires the change to exist on the v0 lineage.

Verification (specific to this base)

  • The resulting MorphoERC4626SwapConnectors.cdc is strictly additive over the deployed mainnet contract: zero lines removed or changed relative to the code at 0x251032a66e9700ef (verified by diffing deployed code, import lines normalized). The rounding fix from fix morpho swap rounding #164 (deployed, previously missing from main) is preserved by construction.
  • ERC4626Utils.cdc delta vs deployed is exactly the three new view functions (idleAssets, liquidityAdapter, maxWithdrawViaLens).
  • flow test cadence/tests/MorphoERC4626SwapConnectors_test.cdc passes under flow-cli v2.13.4 (v0 CI pin), v2.15.3 (main CI pin), and v2.17.4 — each run performs the in-place contract update against the real forked mainnet state, which is the production update path.

Related

…ty (#175)

* gate Morpho ERC4626 redeem quotes on servicable liquidity

previewRedeem/previewWithdraw quote full NAV regardless of the vault's
available liquidity, so the redeem leg always won MultiSwapper quote
comparisons and then reverted at execution when the vault could not pay
out, instead of routing to an AMM leg.

Morpho Vault V2 hardcodes maxRedeem/maxWithdraw to 0, so servicability
is derived from VaultV2.exit semantics instead: when the vault provably
has no liquidity adapter (liquidityAdapter() == 0), redemptions are
served from idle assets only and quotes return 0.0 - the graceful
failure MultiSwapper routes around - unless the redemption fits in the
vault's idle balance. Vaults without liquidityAdapter() (MetaMorpho V1,
plain ERC4626) or with an adapter set keep legacy ungated behavior.

* support VaultV2Lens as authoritative liquidity oracle, non-breaking

A configured VaultV2Lens reports maxWithdraw = idle assets + liquidity
adapter availability for any Vault V2 vault, covering adapter-backed
vaults that the liquidityAdapter/idle heuristic cannot measure.

The lens address lives in the contract account's storage (not contract
state) because in-place contract updates reject new contract fields and
a new Swapper.init parameter would break existing callers. Set or clear
it with transactions/evm/morpho/set_vault_v2_lens.cdc signed by the
contract account; unset or failing lens calls fall back to the
heuristic. Off by default.

Fork test height bumped to 163054070 (the lens postdates the previously
pinned height); the new lens test uses the fork's local key for the
contract account to write config and includes a negative control
(vault-as-lens, whose hardcoded maxWithdraw = 0 zeroes the quote).

* keep fork height CI-compatible; make lens test height-independent

CI pins flow-cli v2.15.3, whose Cadence cannot type-check current
mainnet bridge contracts (Serialize uses StringBuilder), so any fork
height past that deployment fails CI at dependency load. Revert to the
previously pinned height and verify the lens branch with the
height-independent negative control (vault-as-lens: maxWithdraw is
hardcoded to 0). The real-lens integration is verified against live
mainnet state; verified locally with both v2.15.3 and v2.17.4.

* swap back actual share balance in testSwap instead of magic 0.99

Depositing 1.0 PYUSD0 yields a height-dependent share amount
(exchange rate, previewDeposit floor-rounding, and Cadence<->EVM
decimal truncation), so the hardcoded 0.99 swapBack and its
'investigate losses' TODO are replaced by reading the received share
balance and swapping it back in full.
@Kay-Zee
Kay-Zee marked this pull request as ready for review August 31, 2026 23:33
@Kay-Zee
Kay-Zee merged commit db25f87 into v0 Sep 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants