Skip to content

[midnight adapter] Adapter - #786

Draft
adhusson wants to merge 92 commits into
mainfrom
feat/adapter-v2
Draft

[midnight adapter] Adapter#786
adhusson wants to merge 92 commits into
mainfrom
feat/adapter-v2

Conversation

@adhusson

@adhusson adhusson commented Nov 7, 2025

Copy link
Copy Markdown
Contributor

TODO

  • events
  • tests
  • specs

Things to decide

  • linked list vs unordered array
  • forceDeallocate by selling vs forceDeallocate by minting shares
  • On sell, what determines the minimum sell price?
    • Current answer: check the sell does not create a vault-level loss.
    • Alternative: cap loss rate.
  • On buy, where can the adapter source liquidity?
    • Current answer: from idle.
    • Alternative: also from a liquidity adapter. Can be useful to increase available liquidity on take.
  • Sscale growth by WAD? Otherwise for instance with 1 year ttm at 5%/yr, any WBTC take below ~6.3BTC is added to assets instead of being spread to growth. In general growth = interest / ttm so any take where interest < ttm the remainder goes to current assets. In the WBTC example 6.3e8 * 5/100 is ~ 1 year in seconds. But for others it's not as bad (for usdc 1 year 5%, everything goes to assets below $630).

State creation costs increases in glamsterdam that can bias more than before towards creating fewer slots.

Note:

  • When removing credit I decided to underevaluate growth, and put the remainder in current assets; that means current assets can increase when losing credit. It is better than overvaluing growth because in that case you may have to decrease current assets by more than the currently accounted amount, and that might underflow.

@peyha peyha left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice 🔥 🔥 using a linked list to manage maturities is smart

If I understand correctly, the allocation workflow should be

An allocator creates a make offer for the vault
A taker takes this offer onchain
| morphoV2 calls onBuy on the adapter
| | the adapter updates its state
| | the adapter calls allocate on its parent vault
| | | the vault V2 calls allocate on the market V2 adapter
| | | | the adapter returns the corresponding ids and allocated assets

Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated
Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated
Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated
Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated

// Do not cleanup the linked list if we end up at 0 growth
function withdraw(Obligation memory obligation, uint256 obligationUnits, uint256 shares) external {
require(IVaultV2(parentVault).isAllocator(msg.sender), NotAuthorized());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shouldn't lenders also be able to trigger a withdraw on morpho V2 ?

Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated
Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated
Comment thread src/adapters/MorphoMarketV2Adapter.sol Outdated
@adhusson adhusson self-assigned this Jun 12, 2026
maturityData.netCredit += boughtNetCredit.toUint128();
marketData.netCredit += boughtNetCredit.toUint128();

// Insert the maturity in the list if needed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

would a dedicated lib for this kind of logic make sense ?

uint256 netCreditLoss = uint256(marketData.netCredit) + boughtNetCredit - currentNetCredit(marketId);
decreaseNetCredit(marketId, market.maturity, netCreditLoss);

IVaultV2(parentVault)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the vault v2 doc specifies here that "[adapters] must not re-enter (directly or indirectly) the vault" and this line is breaking this property, a few possibilities to move forward

  1. Adapt the vault v2 doc to take this specific case into account
  2. Use a new intermediary contract "Relayer" to handle the callback onBuy/onSell before calling the vault (but it adds operational complexity)
  3. Refactor this function to make it clear that onBuy is not interacting dangerously with the vault

Comment on lines +142 to +144
IVaultV2(parentVault)
.deallocate(address(this), abi.encode(zeroedDurationsIds, -int256(uint256(maturityData.netCredit))), 0);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

another way to update these caps would be to return these ids/change in allocate/deallocate to let the vault update it automatically

/// @dev Returns the number of durations in packedDurations that are at most the time to maturity.
function durationCount(uint256 maturity) internal view returns (uint256 count) {
uint256 timeToMaturity = maturity.zeroFloorSub(block.timestamp);
while (count < durationsLength && timeToMaturity >= packedDurations.get(count)) count++;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could speed this up with fenwick trees or segment tree + lazy propagation. not worth it for small duration array though

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.

2 participants