Skip to content

POC Borrow and lend callbacks#552

Open
peyha wants to merge 37 commits into
mainfrom
feat/callbacks
Open

POC Borrow and lend callbacks#552
peyha wants to merge 37 commits into
mainfrom
feat/callbacks

Conversation

@peyha
Copy link
Copy Markdown
Contributor

@peyha peyha commented Mar 23, 2026

The borrow callback supply collaterals on behalf of the user (maker or taker) while the lend callback withdraws the liquidity (either from another obligation or from a morpho vault) on behalf of the user.

Additional questions

  • Should the lend callback be separated into 2 contracts ? One for morpho vault and one for midnight

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f224c90130

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/periphery/LenderCallback.sol Outdated
Comment on lines +37 to +41
IERC4626(vault).withdraw(buyerAssets, buyer, buyer);
} else if (withdrawType == WithdrawType.Midnight) {
address obligationDataAddress = address(uint160(uint256(withdrawData)));
Obligation memory otherObligation = abi.decode(obligationDataAddress.code, (Obligation));
Midnight(midnight).withdraw(otherObligation, buyerAssets, buyer, buyer);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject callback sources that use a different loan token

onBuy never checks that the selected source actually matches obligation.loanToken. If callbackData points to a vault or another Midnight obligation for a different asset and the buyer already has the traded loan token approved to Midnight, the callback will withdraw the wrong asset first and Midnight.take will still settle using the buyer's wallet balance. That turns a bad source configuration into an unintended extra withdrawal instead of a clean revert.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

not sure it should be required, could be a comment

Comment thread src/periphery/LenderCallback.sol Outdated
Comment on lines +23 to +25
function withdraw(uint256 assets, address receiver, address) external returns (uint256) {
ERC20(asset).transfer(receiver, assets);
return assets;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the vault mock enforce ERC4626 ownership rules

MockVault.withdraw ignores the owner argument and has no share or allowance accounting, so the new testOnBuyVaultV2* cases can pass even when LenderCallback.onBuy would revert against a compliant ERC4626. In particular, the callback currently calls withdraw(buyerAssets, buyer, buyer) as a third party, and this mock never exercises the approval check that a real vault would perform.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will update the test with arbitrary vault v2 when the adapter is live

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b9f0c7a2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/periphery/VaultLenderCallback.sol Outdated
{
require(msg.sender == MIDNIGHT, "unauthorized");
address vault = abi.decode(data, (address));
IERC4626(vault).withdraw(buyerAssets, buyer, buyer);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate vault asset before withdrawing

onBuy withdraws from any vault address in data but never checks that IERC4626(vault).asset() matches the offer’s obligation.loanToken. When the source vault holds a different asset and the buyer already has/approved the obligation loan token, take still succeeds by pulling the loan token from the wallet while also draining the unrelated vault asset, turning a bad callback config into unintended extra withdrawal. Fresh evidence after the callback split: this new VaultLenderCallback path ignores the Obligation token fields entirely.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

same as before, it could be a comment

require(msg.sender == MIDNIGHT, "unauthorized");
bytes32 otherObligationId = abi.decode(data, (bytes32));
Obligation memory otherObligation = abi.decode(address(uint160(uint256(otherObligationId))).code, (Obligation));
Midnight(MIDNIGHT).withdraw(otherObligation, buyerAssets, buyer, buyer);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate source obligation token before withdrawing

onBuy decodes an arbitrary source obligation from data and immediately calls Midnight.withdraw without verifying that otherObligation.loanToken equals the traded obligation.loanToken. If data points to a different-token obligation and the buyer has approved the traded token, settlement can still complete from wallet balance while withdrawing unrelated funds from the source obligation, so a misconfigured callback does not fail closed. Fresh evidence after the split: the new ObligationLenderCallback still ignores the destination obligation’s token in this path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

same as before, it could be a comment

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee5c8fdad9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread test/LenderCallbackTest.sol Outdated
MathisGD and others added 17 commits April 21, 2026 18:27
Co-authored-by: prd-carapulse[bot] <264278285+prd-carapulse[bot]@users.noreply.github.com>
Co-authored-by: Quentin Garchery <garchery.quentin@gmail.com>
Co-authored-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: prd-carapulse[bot] <264278285+prd-carapulse[bot]@users.noreply.github.com>
Co-authored-by: MathisGD <mathis@morpho.xyz>
Co-authored-by: Quentin Garchery <garchery.quentin@gmail.com>
Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: Quentin Garchery <garchery.quentin@gmail.com>
Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: MathisGD <mathis@morpho.xyz>
Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: MathisGD <mathis@morpho.xyz>
Co-authored-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: Adrien Husson <adhusson@gmail.com>
Co-authored-by: peyha <peyha.nicole@outlook.fr>
MathisGD and others added 4 commits April 21, 2026 18:28
Signed-off-by: MathisGD <74971347+MathisGD@users.noreply.github.com>
Co-authored-by: prd-carapulse[bot] <264278285+prd-carapulse[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 864df15f18

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

require(msg.sender == MIDNIGHT, "unauthorized");
CollateralData[] memory collateralData = abi.decode(data, (CollateralData[]));
for (uint256 i = 0; i < collateralData.length; i++) {
Midnight(MIDNIGHT)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add allowance setup for collateral transfers

BorrowerCallback.onSell calls Midnight.supplyCollateral from the callback address, but this contract never grants MIDNIGHT an allowance for collateral tokens and exposes no function to do so. On-chain, a regular ERC20 collateral therefore reverts on first use when supplyCollateral reaches safeTransferFrom with allowance(callback, MIDNIGHT) == 0. The new tests pass only because they impersonate the callback address with vm.prank(...) to call approve, which is not possible in production.

Useful? React with 👍 / 👎.

@MathisGD MathisGD changed the title Borrow and lend callbacks POC Borrow and lend callbacks May 13, 2026
@peyha peyha self-assigned this May 13, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: adb8a02744

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/periphery/BorrowerCallback.sol Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bdf8858f0f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

require(msg.sender == MIDNIGHT, "unauthorized");
bytes32 otherMarketId = abi.decode(data, (bytes32));
Market memory otherMarket = abi.decode(address(uint160(uint256(otherMarketId))).code, (Market));
Midnight(MIDNIGHT).withdraw(otherMarket, buyerAssets, buyer, address(this));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject withdrawing from the market being filled

When data points to the same market as the active take, this withdraw can burn the buyer's just-booked credit before the callback payment is collected, because Midnight.take updates credit/debt and then invokes the buy callback before the loan-token transfers. If the market already has withdrawable liquidity, a lender can fund a new borrow from existing repaid funds without bringing external assets, leaving the new debt unmatched by lender credit. Keep the id callback parameter and reject a source market whose decoded/recomputed id equals it, e.g. require(Midnight(MIDNIGHT).toId(otherMarket) != id, "same market");.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 486aebc53e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread test/LenderCallbackTest.sol Outdated
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.

4 participants