@@ -6,12 +6,16 @@ import {MorphoFlashLoanRequest} from "src/request/MorphoFlashLoanRequest.sol";
66import {MorphoFlashLoanRequestFactory} from "src/request/MorphoFlashLoanRequestFactory.sol " ;
77import {SyncDeposit} from "src/request/scripts/SyncDeposit.sol " ;
88import {SyncWithdrawal} from "src/request/scripts/SyncWithdrawal.sol " ;
9+ import {SyncAllocatorDeposit} from "src/request/scripts/SyncAllocatorDeposit.sol " ;
10+ import {IMorphoAllocator} from "src/interfaces/request/IMorphoAllocator.sol " ;
11+ import {MockMorphoAllocator} from "test/mock/request/MockMorphoAllocator.sol " ;
912import {Offer} from "src/interfaces/request/IOfferReceiver.sol " ;
1013import {LibCommonErrors} from "src/libs/common/LibCommonErrors.sol " ;
1114import {MockERC20} from "test/mock/MockERC20.sol " ;
1215import {Mode, State} from "src/libs/funds/Order.sol " ;
1316import {IntentProperties, Asset} from "src/libs/facility/LibIntent.sol " ;
1417import {WithdrawalStrategy} from "src/interfaces/manager/base/IPositionManagerAdmin.sol " ;
18+ import {MarketParams} from "lib/morpho-blue/src/interfaces/IMorpho.sol " ;
1519
1620/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
1721/* HELPER CONTRACTS */
@@ -102,6 +106,8 @@ contract MorphoFlashLoanRequestTest is FacilityBaseTest {
102106 MockScript public mockScript;
103107 SyncDeposit public syncDeposit;
104108 SyncWithdrawal public syncWithdrawal;
109+ SyncAllocatorDeposit public syncAllocatorDeposit;
110+ MockMorphoAllocator public mockAllocator;
105111
106112 address public executor = makeAddr ("executor " );
107113
@@ -141,6 +147,22 @@ contract MorphoFlashLoanRequestTest is FacilityBaseTest {
141147 vm.prank (owner);
142148 flashLoanRequest.setScript (address (syncWithdrawal), true );
143149
150+ // Deploy and whitelist SyncAllocatorDeposit
151+ syncAllocatorDeposit = new SyncAllocatorDeposit ();
152+ vm.label (address (syncAllocatorDeposit), "SyncAllocatorDeposit " );
153+ vm.prank (owner);
154+ flashLoanRequest.setScript (address (syncAllocatorDeposit), true );
155+
156+ // Deploy a MorphoAllocator test double and wire its roles:
157+ // - it holds FACILITATOR_ROLE on the facility (to unlock + depositManager)
158+ // - the flash loan request proxy holds EXECUTOR_ROLE on it (msg.sender under delegatecall)
159+ mockAllocator = new MockMorphoAllocator (owner, address (facility));
160+ vm.label (address (mockAllocator), "MockMorphoAllocator " );
161+ vm.prank (owner);
162+ facility.grantRoles (address (mockAllocator), FACILITATOR_ROLE);
163+ vm.prank (owner);
164+ mockAllocator.grantRoles (address (flashLoanRequest), 1 ); // EXECUTOR_ROLE == _ROLE_0
165+
144166 // Create an intent with deposits and move to resolving phase
145167 intentId = _createIntentWithDeposits (DEFAULT_AMOUNT);
146168 }
@@ -605,6 +627,174 @@ contract MorphoFlashLoanRequestTest is FacilityBaseTest {
605627 assertEq (collateralToken.balanceOf (address (facility)), 0 );
606628 }
607629
630+ /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
631+ /* H1b. SYNC ALLOCATOR DEPOSIT INTEGRATION TEST */
632+ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
633+
634+ /// @dev Sets up a fresh targetPM intent in the resolving phase with the mock fund configured for a
635+ /// synchronous deposit (create → ACCEPTED, commit → UNLOCKING, unlock → ENDED). Returns the
636+ /// intent ID and the amount used for the fund deposit / shares / borrow.
637+ function _setupSyncAllocatorIntent () internal returns (uint256 syncIntentId , uint256 amount ) {
638+ vm.prank (owner);
639+ syncIntentId = facility.createIntent (_intentParamsWithTargetPM ());
640+
641+ amount = 1_000e18 ;
642+ _mintDebt (user, amount);
643+ vm.prank (user);
644+ facility.deposit (syncIntentId, amount);
645+
646+ // Move to resolving phase
647+ vm.warp (block .timestamp + 1 days + 1 );
648+
649+ // Set the mock fund on the intent (requires facilitator + guardian sig)
650+ vm.prank (facilitator);
651+ _setFund (syncIntentId, address (mockFund));
652+
653+ mockFund.setNextCreateState (State.ACCEPTED);
654+ mockFund.setNextCommitState (State.UNLOCKING);
655+ mockFund.setNextUnlockState (State.ENDED);
656+ mockFund.setCommitAmount (amount);
657+
658+ // sharesFromFund == amount so the PM deposit nets to zero shares on the facility.
659+ mockFund.setUnlockAmount (amount);
660+ collateralToken.setBalance (address (mockFund), amount);
661+ }
662+
663+ function _emptyMarket () internal pure returns (MarketParams memory ) {
664+ return MarketParams (address (0 ), address (0 ), address (0 ), address (0 ), 0 );
665+ }
666+
667+ /// @dev Default empty-rebalance SyncAllocatorDeposit payload (deposit == borrow == amount, targetPM).
668+ function _syncAllocatorDepositPayload (uint256 _intentId , uint256 amount ) internal view returns (bytes memory ) {
669+ SyncAllocatorDeposit.AllocatorParams memory allocatorParams = SyncAllocatorDeposit.AllocatorParams ({
670+ deallocations: new IMorphoAllocator.Deallocation [](0 ),
671+ allocateAdapter: address (0 ),
672+ allocateMarket: _emptyMarket (),
673+ depositAmount: amount,
674+ borrowAmount: amount,
675+ useTarget: true ,
676+ minSharesUnlocked: 0
677+ });
678+ return abi.encodeCall (
679+ SyncAllocatorDeposit.run, (address (facility), address (mockAllocator), _intentId, amount, amount, allocatorParams)
680+ );
681+ }
682+
683+ function test_execute_syncAllocatorDepositFullCycle () public {
684+ (uint256 syncIntentId , uint256 amount ) = _setupSyncAllocatorIntent ();
685+
686+ MorphoFlashLoanRequest.SetRequestParams memory params =
687+ _buildSetRequestParams (syncIntentId, block .timestamp + 1 hours);
688+
689+ // Empty rebalance: no deallocations, allocation skipped (allocateAdapter == address(0)).
690+ // pmDepositAmount = borrowAmount = amount.
691+ SyncAllocatorDeposit.AllocatorParams memory allocatorParams = SyncAllocatorDeposit.AllocatorParams ({
692+ deallocations: new IMorphoAllocator.Deallocation [](0 ),
693+ allocateAdapter: address (0 ),
694+ allocateMarket: _emptyMarket (),
695+ depositAmount: amount,
696+ borrowAmount: amount,
697+ useTarget: true , // targetPM
698+ minSharesUnlocked: 0
699+ });
700+
701+ bytes memory scriptPayload = abi.encodeCall (
702+ SyncAllocatorDeposit.run,
703+ (address (facility), address (mockAllocator), syncIntentId, amount, amount, allocatorParams)
704+ );
705+
706+ uint256 morphoBalBefore = debtToken.balanceOf (address (morpho));
707+
708+ vm.prank (executor);
709+ flashLoanRequest.execute (amount, params, address (syncAllocatorDeposit), scriptPayload);
710+
711+ // Verify: request removed from intent
712+ (,, address request ,) = facility.getIntent (syncIntentId);
713+ assertEq (request, address (0 ));
714+
715+ // Verify: flash loan fully repaid (Morpho balance only decreased by borrow amount, not flash loan)
716+ assertEq (debtToken.balanceOf (address (morpho)), morphoBalBefore - amount);
717+
718+ // Verify: flash loan request has no leftover balance
719+ assertEq (debtToken.balanceOf (address (flashLoanRequest)), 0 );
720+
721+ // Verify: collateral was deposited into PM (facility no longer holds it)
722+ assertEq (collateralToken.balanceOf (address (facility)), 0 );
723+
724+ // Verify: the allocator received the params routed by the script
725+ assertEq (mockAllocator.lastDepositAmount (), amount);
726+ assertEq (mockAllocator.lastBorrowAmount (), amount);
727+ assertEq (mockAllocator.lastDeallocationsLength (), 0 );
728+ assertEq (mockAllocator.lastAllocateAdapter (), address (0 ));
729+ assertTrue (mockAllocator.lastUseTarget ());
730+ }
731+
732+ function test_execute_syncAllocatorDeposit_forwardsDeallocations () public {
733+ (uint256 syncIntentId , uint256 amount ) = _setupSyncAllocatorIntent ();
734+
735+ MorphoFlashLoanRequest.SetRequestParams memory params =
736+ _buildSetRequestParams (syncIntentId, block .timestamp + 1 hours);
737+
738+ // Non-empty rebalance: proves the calldata array survives delegatecall + encode/decode round-trip.
739+ IMorphoAllocator.Deallocation[] memory deallocations = new IMorphoAllocator.Deallocation [](1 );
740+ deallocations[0 ] = IMorphoAllocator.Deallocation ({
741+ adapter: address (0xADA9 ), marketParams: _emptyMarket (), amount: 123 , maxUtilisation: 1e18
742+ });
743+
744+ SyncAllocatorDeposit.AllocatorParams memory allocatorParams = SyncAllocatorDeposit.AllocatorParams ({
745+ deallocations: deallocations,
746+ allocateAdapter: address (0xBEEF ),
747+ allocateMarket: _emptyMarket (),
748+ depositAmount: amount,
749+ borrowAmount: amount,
750+ useTarget: true ,
751+ minSharesUnlocked: 0
752+ });
753+
754+ bytes memory scriptPayload = abi.encodeCall (
755+ SyncAllocatorDeposit.run,
756+ (address (facility), address (mockAllocator), syncIntentId, amount, amount, allocatorParams)
757+ );
758+
759+ vm.prank (executor);
760+ flashLoanRequest.execute (amount, params, address (syncAllocatorDeposit), scriptPayload);
761+
762+ assertEq (mockAllocator.lastDeallocationsLength (), 1 );
763+ assertEq (mockAllocator.lastAllocateAdapter (), address (0xBEEF ));
764+ }
765+
766+ function test_execute_syncAllocatorDeposit_revertsWhenRequestLacksExecutorRole () public {
767+ // Remove EXECUTOR_ROLE from the request proxy on the allocator.
768+ vm.prank (owner);
769+ mockAllocator.revokeRoles (address (flashLoanRequest), 1 );
770+
771+ (uint256 syncIntentId , uint256 amount ) = _setupSyncAllocatorIntent ();
772+
773+ MorphoFlashLoanRequest.SetRequestParams memory params =
774+ _buildSetRequestParams (syncIntentId, block .timestamp + 1 hours);
775+ bytes memory scriptPayload = _syncAllocatorDepositPayload (syncIntentId, amount);
776+
777+ vm.prank (executor);
778+ vm.expectRevert ();
779+ flashLoanRequest.execute (amount, params, address (syncAllocatorDeposit), scriptPayload);
780+ }
781+
782+ function test_execute_syncAllocatorDeposit_revertsWhenAllocatorLacksFacilitatorRole () public {
783+ // Remove FACILITATOR_ROLE from the allocator on the facility (so unlock reverts).
784+ vm.prank (owner);
785+ facility.revokeRoles (address (mockAllocator), FACILITATOR_ROLE);
786+
787+ (uint256 syncIntentId , uint256 amount ) = _setupSyncAllocatorIntent ();
788+
789+ MorphoFlashLoanRequest.SetRequestParams memory params =
790+ _buildSetRequestParams (syncIntentId, block .timestamp + 1 hours);
791+ bytes memory scriptPayload = _syncAllocatorDepositPayload (syncIntentId, amount);
792+
793+ vm.prank (executor);
794+ vm.expectRevert ();
795+ flashLoanRequest.execute (amount, params, address (syncAllocatorDeposit), scriptPayload);
796+ }
797+
608798 /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
609799 /* H2. SYNC WITHDRAWAL INTEGRATION TEST */
610800 /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
0 commit comments