@@ -8,8 +8,6 @@ import { ModeLib } from "@erc7579/lib/ModeLib.sol";
88import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol " ;
99import { CallType, ExecType, Delegation, ModeCode } from "../utils/Types.sol " ;
1010import { CALLTYPE_SINGLE, EXECTYPE_DEFAULT } from "../utils/Constants.sol " ;
11- import { ExecutionHelper } from "@erc7579/core/ExecutionHelper.sol " ;
12-
1311import { IDelegationManager } from "../interfaces/IDelegationManager.sol " ;
1412import { IAavePool } from "./interfaces/IAavePool.sol " ;
1513
@@ -43,7 +41,7 @@ import { IAavePool } from "./interfaces/IAavePool.sol";
4341 * - The contract is designed to never hold tokens during normal operation, making owner functions
4442 * purely for exceptional circumstances
4543 */
46- contract AaveAdapter is Ownable2Step , ExecutionHelper {
44+ contract AaveAdapter is Ownable2Step {
4745 using SafeERC20 for IERC20 ;
4846 using ExecutionLib for bytes ;
4947 using ModeLib for ModeCode;
@@ -93,6 +91,11 @@ contract AaveAdapter is Ownable2Step, ExecutionHelper {
9391 */
9492 error InvalidDelegationsLength ();
9593
94+ /**
95+ * @notice Thrown when the batch array is empty or lengths don't match
96+ */
97+ error InvalidBatchLength ();
98+
9699 /**
97100 * @notice Thrown when the caller is not the delegator for restricted functions
98101 */
@@ -118,24 +121,6 @@ contract AaveAdapter is Ownable2Step, ExecutionHelper {
118121 */
119122 error UnsupportedExecType (ExecType execType );
120123
121- ////////////////////// Modifiers //////////////////////
122-
123- /**
124- * @notice Require the function call to come from the DelegationManager.
125- */
126- modifier onlyDelegationManager () {
127- if (msg .sender != address (delegationManager)) revert NotDelegationManager ();
128- _;
129- }
130-
131- /**
132- * @notice Require the function call to come from this contract itself
133- */
134- modifier onlySelf () {
135- if (msg .sender != address (this )) revert NotSelf ();
136- _;
137- }
138-
139124 ////////////////////// State //////////////////////
140125
141126 /**
@@ -180,6 +165,15 @@ contract AaveAdapter is Ownable2Step, ExecutionHelper {
180165
181166 ////////////////////// Public Functions //////////////////////
182167
168+ /**
169+ * @notice Parameters for a single supply operation in a batch
170+ */
171+ struct SupplyParams {
172+ Delegation[] delegations;
173+ address token;
174+ uint256 amount;
175+ }
176+
183177 /**
184178 * @notice Supplies tokens to Aave using delegation-based token transfer
185179 * @dev Only the delegator can execute this function, ensuring full control over supply parameters.
@@ -189,33 +183,69 @@ contract AaveAdapter is Ownable2Step, ExecutionHelper {
189183 * @param _amount Amount of tokens to supply (use type(uint256).max for full balance)
190184 */
191185 function supplyByDelegation (Delegation[] memory _delegations , address _token , uint256 _amount ) external {
186+ _executeSupplyByDelegation (_delegations, _token, _amount, msg .sender );
187+ }
188+
189+ /**
190+ * @notice Supplies tokens to Aave using multiple delegation streams, executed sequentially
191+ * @dev Each element in _supplyStreams is executed one after the other. The caller must be the delegator
192+ * (first delegate in the chain) for each stream. Useful for batch operations across multiple users/tokens.
193+ * @param _supplyStreams Array of supply parameters, each containing delegations, token, and amount
194+ */
195+ function supplyByDelegationBatch (SupplyParams[] memory _supplyStreams ) external {
196+ uint256 streamsLength_ = _supplyStreams.length ;
197+ if (streamsLength_ == 0 ) revert InvalidBatchLength ();
198+
199+ address caller_ = msg .sender ;
200+ for (uint256 i = 0 ; i < streamsLength_;) {
201+ SupplyParams memory params_ = _supplyStreams[i];
202+ _executeSupplyByDelegation (params_.delegations, params_.token, params_.amount, caller_);
203+ unchecked {
204+ ++ i;
205+ }
206+ }
207+ }
208+
209+ /**
210+ * @notice Internal implementation of supply by delegation
211+ * @param _delegations Delegation chain for the redelegation pattern
212+ * @param _token Token to supply
213+ * @param _amount Amount to supply
214+ * @param _caller Authorized caller (must match first delegator in chain)
215+ */
216+ function _executeSupplyByDelegation (
217+ Delegation[] memory _delegations ,
218+ address _token ,
219+ uint256 _amount ,
220+ address _caller
221+ )
222+ internal
223+ {
192224 uint256 length_ = _delegations.length ;
193225 if (length_ < 2 ) revert InvalidDelegationsLength ();
194- if (_delegations[0 ].delegator != msg . sender ) revert UnauthorizedCaller ();
226+ if (_delegations[0 ].delegator != _caller ) revert UnauthorizedCaller ();
195227 if (_token == address (0 )) revert InvalidZeroAddress ();
196228
197229 // Root delegator is the original token owner (last in the delegation chain)
198230 address rootDelegator_ = _delegations[length_ - 1 ].delegator;
199231
200- bytes [] memory permissionContexts_ = new bytes [](2 );
232+ bytes [] memory permissionContexts_ = new bytes [](1 );
201233 permissionContexts_[0 ] = abi.encode (_delegations);
202- permissionContexts_[1 ] = abi.encode (new Delegation [](0 ));
203234
204- ModeCode[] memory encodedModes_ = new ModeCode [](2 );
235+ ModeCode[] memory encodedModes_ = new ModeCode [](1 );
205236 encodedModes_[0 ] = ModeLib.encodeSimpleSingle ();
206- encodedModes_[1 ] = ModeLib.encodeSimpleSingle ();
207237
208- bytes [] memory executionCallDatas_ = new bytes [](2 );
238+ bytes [] memory executionCallDatas_ = new bytes [](1 );
209239
210240 bytes memory encodedTransfer_ = abi.encodeCall (IERC20 .transfer, (address (this ), _amount));
211241 executionCallDatas_[0 ] = ExecutionLib.encodeSingle (address (_token), 0 , encodedTransfer_);
212- executionCallDatas_[1 ] = ExecutionLib.encodeSingle (
213- address (this ), 0 , abi.encodeWithSelector (this .supply.selector , _token, _amount, rootDelegator_)
214- );
215242
216243 delegationManager.redeemDelegations (permissionContexts_, encodedModes_, executionCallDatas_);
217244
218- emit SupplyExecuted (rootDelegator_, msg .sender , _token, _amount);
245+ _ensureAllowance (IERC20 (_token), _amount);
246+ aavePool.supply (_token, _amount, rootDelegator_, 0 );
247+
248+ emit SupplyExecuted (rootDelegator_, _caller, _token, _amount);
219249 }
220250
221251 /**
@@ -227,89 +257,32 @@ contract AaveAdapter is Ownable2Step, ExecutionHelper {
227257 * @param _amount Amount of tokens to withdraw (use type(uint256).max for full balance)
228258 */
229259 function withdrawByDelegation (Delegation[] memory _delegations , address _token , uint256 _amount ) external {
230- uint256 length_ = _delegations.length ;
231- if (length_ < 2 ) revert InvalidDelegationsLength ();
260+ if (_delegations.length < 2 ) revert InvalidDelegationsLength ();
232261 if (_delegations[0 ].delegator != msg .sender ) revert UnauthorizedCaller ();
233262 if (_token == address (0 )) revert InvalidZeroAddress ();
234263
235264 // Root delegator is the original token owner (last in the delegation chain)
236- address rootDelegator_ = _delegations[length_ - 1 ].delegator;
265+ address rootDelegator_ = _delegations[1 ].delegator;
237266
238- bytes [] memory permissionContexts_ = new bytes [](2 );
267+ bytes [] memory permissionContexts_ = new bytes [](1 );
239268 permissionContexts_[0 ] = abi.encode (_delegations);
240- permissionContexts_[1 ] = abi.encode (new Delegation [](0 ));
241269
242- ModeCode[] memory encodedModes_ = new ModeCode [](2 );
270+ ModeCode[] memory encodedModes_ = new ModeCode [](1 );
243271 encodedModes_[0 ] = ModeLib.encodeSimpleSingle ();
244- encodedModes_[1 ] = ModeLib.encodeSimpleSingle ();
245272
246- bytes [] memory executionCallDatas_ = new bytes [](2 );
273+ bytes [] memory executionCallDatas_ = new bytes [](1 );
247274
248275 // Get the aToken address for the underlying token
249276 IERC20 aToken_ = IERC20 (aavePool.getReserveAToken (_token));
250277
251278 bytes memory encodedTransfer_ = abi.encodeCall (IERC20 .transfer, (address (this ), _amount));
252279 executionCallDatas_[0 ] = ExecutionLib.encodeSingle (address (aToken_), 0 , encodedTransfer_);
253- executionCallDatas_[1 ] = ExecutionLib.encodeSingle (
254- address (this ), 0 , abi.encodeWithSelector (this .withdraw.selector , _token, _amount, rootDelegator_)
255- );
256280
257281 delegationManager.redeemDelegations (permissionContexts_, encodedModes_, executionCallDatas_);
258282
259- emit WithdrawExecuted (rootDelegator_, msg .sender , _token, _amount);
260- }
283+ aavePool.withdraw (_token, _amount, rootDelegator_);
261284
262- /**
263- * @notice Calls the actual supply function on the Aave pool
264- * @dev This function can only be called internally by this contract (`onlySelf`).
265- * @param _token Address of the token to supply
266- * @param _amount Amount of tokens to supply
267- * @param _onBehalfOf Address that will receive the aTokens
268- */
269- function supply (address _token , uint256 _amount , address _onBehalfOf ) external onlySelf {
270- _ensureAllowance (IERC20 (_token), _amount);
271- aavePool.supply (_token, _amount, _onBehalfOf, 0 );
272- }
273-
274- /**
275- * @notice Calls the actual withdraw function on the Aave pool
276- * @dev This function can only be called internally by this contract (`onlySelf`).
277- * @param _token Address of the underlying token to withdraw
278- * @param _amount Amount of tokens to withdraw
279- * @param _to Address that will receive the withdrawn tokens
280- */
281- function withdraw (address _token , uint256 _amount , address _to ) external onlySelf {
282- aavePool.withdraw (_token, _amount, _to);
283- }
284-
285- /**
286- * @notice Executes a call on behalf of this contract, authorized by the DelegationManager
287- * @dev Only callable by the DelegationManager. Supports single-call execution
288- * and handles the revert logic via ExecType.
289- * @dev Related: @erc7579/MSAAdvanced.sol
290- * @param _mode The encoded execution mode of the transaction (CallType, ExecType, etc.)
291- * @param _executionCalldata The encoded call data (single) to be executed
292- * @return returnData_ An array of returned data from the executed call
293- */
294- function executeFromExecutor (
295- ModeCode _mode ,
296- bytes calldata _executionCalldata
297- )
298- external
299- payable
300- onlyDelegationManager
301- returns (bytes [] memory returnData_ )
302- {
303- (CallType callType_ , ExecType execType_ ,,) = _mode.decode ();
304-
305- /* Only support single call type with default execution */
306- if (CallType.unwrap (CALLTYPE_SINGLE) != CallType.unwrap (callType_)) revert UnsupportedCallType (callType_);
307- if (ExecType.unwrap (EXECTYPE_DEFAULT) != ExecType.unwrap (execType_)) revert UnsupportedExecType (execType_);
308- /* Process single execution directly without additional checks */
309- (address target_ , uint256 value_ , bytes calldata callData_ ) = _executionCalldata.decodeSingle ();
310- returnData_ = new bytes [](1 );
311- returnData_[0 ] = _execute (target_, value_, callData_);
312- return returnData_;
285+ emit WithdrawExecuted (rootDelegator_, msg .sender , _token, _amount);
313286 }
314287
315288 /**
0 commit comments