Skip to content
Closed
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
39 changes: 39 additions & 0 deletions src/request/scripts/SyncMidasWithdrawal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IFacilityFunds} from "../../interfaces/facility/base/IFacilityFunds.sol";
import {IFacilityPositionManager} from "../../interfaces/facility/base/IFacilityPositionManager.sol";
import {WithdrawalStrategy} from "../../interfaces/manager/base/IPositionManagerAdmin.sol";

/// @title SyncMidasWithdrawal
/// @notice Stateless script designed to be delegatecalled from MorphoFlashLoanRequest.
/// Executes the remaining Midas redemption flow: withdrawManager → commit → unlock.
/// @dev Must NOT have any storage variables. The redeem order must already exist, its bond
/// leg must be complete, and instant redemption must be unlocked before execute().
/// @author 3F Protocol
contract SyncMidasWithdrawal {
/// @notice Settles the remaining Midas redemption for a facility intent.
/// @param facility The facility contract address.
/// @param intentId The intent ID on the facility.
/// @param withdrawAmount The remaining collateral required by the Midas redemption.
/// @param repayAmount The amount of debt to repay to the position manager.
/// @param useTarget Whether to use the target asset for the position manager withdrawal.
/// @param strategy The withdrawal strategy (SEQUENTIAL or PROPORTIONAL).
function run(
address facility,
uint256 intentId,
uint256 withdrawAmount,
uint256 repayAmount,
bool useTarget,
WithdrawalStrategy strategy
) external {
// 1. Withdraw collateral from position manager (repay debt, get collateral back)
IFacilityPositionManager(facility).withdrawManager(intentId, withdrawAmount, repayAmount, useTarget, strategy);

// 2. Commit the remaining redemption collateral
IFacilityFunds(facility).commit(intentId);

// 3. Unlock the redemption proceeds
IFacilityFunds(facility).unlock(intentId);
}
}
47 changes: 47 additions & 0 deletions test/request/MorphoFlashLoanRequest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.24;
import {FacilityBaseTest} from "test/facility/FacilityBase.t.sol";
import {MorphoFlashLoanRequest} from "src/request/MorphoFlashLoanRequest.sol";
import {MorphoFlashLoanRequestFactory} from "src/request/MorphoFlashLoanRequestFactory.sol";
import {SyncMidasWithdrawal} from "src/request/scripts/SyncMidasWithdrawal.sol";
import {SyncDeposit} from "src/request/scripts/SyncDeposit.sol";
import {SyncWithdrawal} from "src/request/scripts/SyncWithdrawal.sol";
import {SyncAllocatorDeposit} from "src/request/scripts/SyncAllocatorDeposit.sol";
Expand Down Expand Up @@ -105,6 +106,7 @@ contract MorphoFlashLoanRequestTest is FacilityBaseTest {
MorphoFlashLoanRequest public flashLoanRequest;
MockScript public mockScript;
SyncDeposit public syncDeposit;
SyncMidasWithdrawal public syncMidasWithdrawal;
SyncWithdrawal public syncWithdrawal;
SyncAllocatorDeposit public syncAllocatorDeposit;
MockMorphoAllocator public mockAllocator;
Expand Down Expand Up @@ -141,6 +143,12 @@ contract MorphoFlashLoanRequestTest is FacilityBaseTest {
vm.prank(owner);
flashLoanRequest.setScript(address(syncDeposit), true);

// Deploy and whitelist SyncMidasWithdrawal
syncMidasWithdrawal = new SyncMidasWithdrawal();
vm.label(address(syncMidasWithdrawal), "SyncMidasWithdrawal");
vm.prank(owner);
flashLoanRequest.setScript(address(syncMidasWithdrawal), true);

// Deploy and whitelist SyncWithdrawal
syncWithdrawal = new SyncWithdrawal();
vm.label(address(syncWithdrawal), "SyncWithdrawal");
Expand Down Expand Up @@ -937,6 +945,45 @@ contract MorphoFlashLoanRequestTest is FacilityBaseTest {
assertEq(collateralToken.balanceOf(address(facility)), 0);
}

function testFuzz_execute_syncMidasWithdrawalCommitUnlock(bool proportional) public {
uint256 morphoBalBefore = debtToken.balanceOf(address(morpho));
vm.prank(facilitator);
facility.depositManager(intentId, 0, 5_000e18, false);

uint256 withdrawAmount = 1_000e18;
uint256 repayAmount = 500e18;
WithdrawalStrategy strategy = proportional ? WithdrawalStrategy.PROPORTIONAL : WithdrawalStrategy.SEQUENTIAL;
vm.prank(facilitator);
_setFund(intentId, address(mockFund));
// MockFund represents the remaining redemption at the script's facility boundary.
mockFund.setNextCommitState(State.UNLOCKING);
mockFund.setUnlockAmount(withdrawAmount);
debtToken.setBalance(address(mockFund), withdrawAmount);
vm.prank(facilitator);
facility.create(intentId, withdrawAmount, withdrawAmount, Mode.REDEEM);

vm.prank(executor);
flashLoanRequest.execute(
repayAmount,
_buildSetRequestParams(intentId, block.timestamp + 1 hours),
address(syncMidasWithdrawal),
abi.encodeCall(
SyncMidasWithdrawal.run, (address(facility), intentId, withdrawAmount, repayAmount, false, strategy)
)
);

assertEq(positionManager.collateralAmount(), 9_000e18);
assertEq(positionManager.debtAmount(), 4_500e18);
assertEq(collateralToken.balanceOf(address(mockFund)), withdrawAmount);
assertEq(collateralToken.balanceOf(address(facility)), 0);
assertEq(debtToken.balanceOf(address(facility)), 5_500e18);
assertEq(debtToken.balanceOf(address(morpho)), morphoBalBefore - 4_500e18);
assertEq(debtToken.balanceOf(address(flashLoanRequest)), 0);
(, address fund, address request,) = facility.getIntent(intentId);
assertEq(fund, address(0));
assertEq(request, address(0));
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* I. pullFunds() TESTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand Down
Loading