Skip to content

Add Credit Market AMO Strategy#2927

Open
shahthepro wants to merge 4 commits into
masterfrom
shah/credit-market-amo
Open

Add Credit Market AMO Strategy#2927
shahthepro wants to merge 4 commits into
masterfrom
shah/credit-market-amo

Conversation

@shahthepro

@shahthepro shahthepro commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Code Changes

  • Adds CreditMarketAMOStrategy, an AMO that mints an OToken and supplies it as the sole lender into a deposit-gated Morpho Vault V2 ("credit vault"). Borrowers post their own collateral and borrow the
    OToken; the interest they pay grows the strategy's position and reaches OToken holders as rebase yield. The strategy only ever mints OToken in and burns OToken out — it never touches the Vault's backing
    asset.

  • The contract is generic: the OToken, hard asset, and credit vault are set at deploy time, so one implementation serves OUSD or OETH. Wired up for OUSD first.

Key design decisions

  • Phantom backing. checkBalance reports the full live position value (keeps total value and rebase correct), but the strategy provides zero redemption capacity — it can only burn OToken. mintCap is the
    only on-chain bound and must be sized below the protocol's real redeemable liquidity.
  • mintCap-gated minting. Minting is off until governance raises the cap; mintAndSupply / redeemAndBurn are Governor-or-Strategist; withdrawAll tolerates illiquidity so cleanup never reverts.
  • Yield-first accounting. netMinted tracks principal still deployed, drawing accrued interest down before principal, so netMinted ≤ positionValue() always.
  • Shared merkle claim. Extracted merkleClaim into AbstractMerkleClaimStrategy, now shared with Generalized4626Strategy.

@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.00000% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.37%. Comparing base (5acff6a) to head (f5d87df).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
...ntracts/strategies/AbstractMerkleClaimStrategy.sol 8.33% 11 Missing ⚠️
...s/contracts/strategies/CreditMarketAMOStrategy.sol 98.52% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2927      +/-   ##
==========================================
+ Coverage   44.63%   45.37%   +0.74%     
==========================================
  Files         110      112       +2     
  Lines        4920     4989      +69     
  Branches     1362     1381      +19     
==========================================
+ Hits         2196     2264      +68     
- Misses       2721     2722       +1     
  Partials        3        3              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@naddison36 naddison36 left a comment

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.

A simple and clean implementation. Well done.

I think we need to decide if principal and accruedYield should be the true values or we just document that these aren't accurate as the netMinted amount will be less than the true principal amount once a burn has been done.

Comment thread contracts/contracts/strategies/AbstractMerkleClaimStrategy.sol
Comment thread contracts/contracts/strategies/CreditMarketAMOStrategy.sol
}

/// @notice The principal, ie net OToken minted into the position.
function principal() external view returns (uint256) {

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.

nit: is this view function adding value over the already public netMinted storage variable?

Also, netMinted may drift below the true principal once accrued interest is burned (redeemAndBurn lowers it by the amount withdrawn, floored at 0).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I removed principal and another duplicate method. Re: the drift, I changed how the strategy behaves: it now burns through the yield/interest tokens first before it eats into the printed tokens.

So, if there's 100 OUSD minted and an interest of 10 OUSD is earned on top of it, when the strategist burns 50 OUSD, netMinted will be 60 OUSD (instead of 50 OUSD with the original implementation): f5d87df

}

/// @notice Interest accrued on the position above the principal, in OToken units.
function accruedYield() external view returns (uint256) {

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 won't be the true accrued yield after yielded oTokens are burned leaving netMinted not reflecting the true principal amount.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed by changing the behaviour to burn through yield first: f5d87df

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