Skip to content

Commit c5acff2

Browse files
authored
feat: aave v3 flash loan asset manager
1 parent e036b30 commit c5acff2

File tree

7 files changed

+503
-0
lines changed

7 files changed

+503
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
3+
/*
4+
This file is part of the Enzyme Protocol.
5+
6+
(c) Enzyme Foundation <security@enzyme.finance>
7+
8+
For the full license information, please view the LICENSE
9+
file that was distributed with this source code.
10+
*/
11+
12+
pragma solidity >=0.6.0 <0.9.0;
13+
14+
/// @title IAaveV3FlashLoanReceiver interface
15+
/// @author Enzyme Foundation <security@enzyme.finance>
16+
interface IAaveV3FlashLoanReceiver {
17+
function executeOperation(
18+
address[] calldata assets,
19+
uint256[] calldata amounts,
20+
uint256[] calldata premiums,
21+
address initiator,
22+
bytes calldata params
23+
) external returns (bool success_);
24+
25+
function ADDRESSES_PROVIDER() external view returns (address poolAddressProviderAddress_);
26+
27+
function POOL() external view returns (address poolAddress_);
28+
}

contracts/external-interfaces/IAaveV3Pool.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ interface IAaveV3Pool {
7979
address _onBehalfOf
8080
) external;
8181

82+
function flashLoan(
83+
address _receiverAddress,
84+
address[] calldata _assets,
85+
uint256[] calldata _amounts,
86+
uint256[] calldata _interestRateModes,
87+
address _onBehalfOf,
88+
bytes calldata _params,
89+
uint16 _referralCode
90+
) external;
91+
8292
function getReserveData(address _asset) external returns (ReserveData memory reserveData_);
8393

8494
function repay(address _asset, uint256 _amount, uint256 _interestRateMode, address _onBehalfOf) external;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
3+
/*
4+
This file is part of the Enzyme Protocol.
5+
6+
(c) Enzyme Foundation <security@enzyme.finance>
7+
8+
For the full license information, please view the LICENSE
9+
file that was distributed with this source code.
10+
*/
11+
12+
pragma solidity 0.8.19;
13+
14+
import {Address} from "openzeppelin-solc-0.8/utils/Address.sol";
15+
import {IAaveV3FlashLoanReceiver} from "../../../external-interfaces/IAaveV3FlashLoanReceiver.sol";
16+
import {IAaveV3Pool} from "../../../external-interfaces/IAaveV3Pool.sol";
17+
import {IAaveV3PoolAddressProvider} from "../../../external-interfaces/IAaveV3PoolAddressProvider.sol";
18+
import {IERC20} from "../../../external-interfaces/IERC20.sol";
19+
import {WrappedSafeERC20 as SafeERC20} from "../../../utils/0.8.19/open-zeppelin/WrappedSafeERC20.sol";
20+
import {IAaveV3FlashLoanAssetManager} from "./IAaveV3FlashLoanAssetManager.sol";
21+
22+
/// @title AaveV3FlashLoanAssetManagerLib Contract
23+
/// @author Enzyme Foundation <security@enzyme.finance>
24+
/// @notice An asset manager contract for executing flash loans on Aave V3
25+
/// @dev Intended as implementation contract for a proxy.
26+
/// Must add this contract instance as an asset manager on the intended Enzyme vault.
27+
contract AaveV3FlashLoanAssetManagerLib is IAaveV3FlashLoanAssetManager, IAaveV3FlashLoanReceiver {
28+
using SafeERC20 for IERC20;
29+
30+
// `REPAYMENT_BALANCE_BUFFER`: A small tolerance for repayment balance dust, e.g., in case of rebasing tokens
31+
uint256 internal constant REPAYMENT_BALANCE_BUFFER = 2;
32+
33+
uint16 public immutable AAVE_REFERRAL_CODE;
34+
address public immutable override ADDRESSES_PROVIDER;
35+
36+
// `owner`: The authorized caller of this contract instance
37+
address internal owner;
38+
// `borrowedAssetsRecipient`: The address where all borrowed assets are transferred. Generally the VaultProxy.
39+
address internal borrowedAssetsRecipient;
40+
41+
error AaveV3FlashLoanAssetManager__ExecuteOperation__BalanceExceedsRepayment(uint256 balance);
42+
error AaveV3FlashLoanAssetManager__ExecuteOperation__UnauthorizedCaller();
43+
error AaveV3FlashLoanAssetManager__ExecuteOperation__UnauthorizedInitiator();
44+
error AaveV3FlashLoanAssetManager__FlashLoan__Unauthorized();
45+
error AaveV3FlashLoanAssetManager__Init__AlreadyInitialized();
46+
47+
event BorrowedAssetsRecipientSet(address borrowedAssetsRecipient);
48+
event OwnerSet(address owner);
49+
50+
constructor(address _aavePoolAddressProviderAddress, uint16 _aaveReferralCode) {
51+
AAVE_REFERRAL_CODE = _aaveReferralCode;
52+
ADDRESSES_PROVIDER = _aavePoolAddressProviderAddress;
53+
}
54+
55+
/// @notice Initializes the contract
56+
/// @param _owner The owner (authorized caller) of the contract
57+
/// @param _borrowedAssetsRecipient The recipient of the flash loan borrowed assets
58+
function init(address _owner, address _borrowedAssetsRecipient) external {
59+
if (getOwner() != address(0)) revert AaveV3FlashLoanAssetManager__Init__AlreadyInitialized();
60+
61+
__setOwner(_owner);
62+
__setBorrowedAssetsRecipient(_borrowedAssetsRecipient);
63+
}
64+
65+
/// @notice Executes a flash loan on Aave V3
66+
/// @param _assets The assets to borrow
67+
/// @param _amounts The amounts to borrow
68+
/// @param _encodedCalls Encoded Call[] items to execute during the flash loan
69+
function flashLoan(address[] calldata _assets, uint256[] calldata _amounts, bytes calldata _encodedCalls)
70+
external
71+
override
72+
{
73+
if (msg.sender != getOwner()) revert AaveV3FlashLoanAssetManager__FlashLoan__Unauthorized();
74+
75+
IAaveV3Pool(POOL()).flashLoan({
76+
_receiverAddress: address(this),
77+
_assets: _assets,
78+
_amounts: _amounts,
79+
_interestRateModes: new uint256[](_assets.length), // 0 is "no open debt"
80+
_onBehalfOf: address(0), // unused when interest mode = 0
81+
_params: _encodedCalls,
82+
_referralCode: AAVE_REFERRAL_CODE
83+
});
84+
}
85+
86+
/// @dev Helper to set `borrowedAssetsRecipient`
87+
function __setBorrowedAssetsRecipient(address _borrowedAssetsRecipient) internal {
88+
borrowedAssetsRecipient = _borrowedAssetsRecipient;
89+
90+
emit BorrowedAssetsRecipientSet(_borrowedAssetsRecipient);
91+
}
92+
93+
/// @dev Helper to set `owner`
94+
function __setOwner(address _owner) internal {
95+
owner = _owner;
96+
97+
emit OwnerSet(_owner);
98+
}
99+
100+
//==================================================================================================================
101+
// IAaveV3FlashLoanReceiver
102+
//==================================================================================================================
103+
104+
/// @notice Required callback function for Aave V3 flash loans
105+
function executeOperation(
106+
address[] calldata _assets,
107+
uint256[] calldata _amounts,
108+
uint256[] calldata _premiums,
109+
address _initiator,
110+
bytes calldata _params
111+
) external override returns (bool success_) {
112+
if (_initiator != address(this)) revert AaveV3FlashLoanAssetManager__ExecuteOperation__UnauthorizedInitiator();
113+
address poolAddress = POOL();
114+
if (msg.sender != poolAddress) revert AaveV3FlashLoanAssetManager__ExecuteOperation__UnauthorizedCaller();
115+
116+
// Send full balances of all borrowed assets to vault.
117+
// Leaving 0-balance for all assets makes calculating repayment amounts to transfer simpler,
118+
// and prevents griefing by sending surplus assets here.
119+
{
120+
address recipient = getBorrowedAssetsRecipient();
121+
for (uint256 i; i < _assets.length; i++) {
122+
IERC20 asset = IERC20(_assets[i]);
123+
asset.safeTransfer(recipient, asset.balanceOf(address(this)));
124+
}
125+
}
126+
127+
// Execute calls.
128+
// The final `Call[]` items should transfer exact "asset + premium" amounts to this contract to repay the loan.
129+
{
130+
Call[] memory calls = abi.decode(_params, (Call[]));
131+
for (uint256 i; i < calls.length; i++) {
132+
Call memory call = calls[i];
133+
134+
Address.functionCall({target: call.target, data: call.data});
135+
}
136+
}
137+
138+
// Validate that this contract has no more than the exact expected amounts to repay loan + interest,
139+
// and grant allowances to Aave Pool to reclaim those amounts.
140+
// Protects against unexpected lowering of premiums.
141+
for (uint256 i; i < _assets.length; i++) {
142+
IERC20 asset = IERC20(_assets[i]);
143+
uint256 repaymentAmount = _amounts[i] + _premiums[i];
144+
145+
uint256 balance = asset.balanceOf(address(this));
146+
if (balance > repaymentAmount + REPAYMENT_BALANCE_BUFFER) {
147+
revert AaveV3FlashLoanAssetManager__ExecuteOperation__BalanceExceedsRepayment(balance);
148+
}
149+
150+
asset.safeApprove(poolAddress, repaymentAmount);
151+
}
152+
153+
return true;
154+
}
155+
156+
/// @notice Returns the Aave V3 pool
157+
function POOL() public view override returns (address poolAddress_) {
158+
return IAaveV3PoolAddressProvider(ADDRESSES_PROVIDER).getPool();
159+
}
160+
161+
//==================================================================================================================
162+
// Storage getters
163+
//==================================================================================================================
164+
165+
/// @notice Gets the recipient of the flash loan borrowed assets
166+
/// @return borrowedAssetsRecipient_ The recipient
167+
function getBorrowedAssetsRecipient() public view returns (address borrowedAssetsRecipient_) {
168+
return borrowedAssetsRecipient;
169+
}
170+
171+
/// @notice Gets the owner (authorized caller) of the contract
172+
/// @return owner_ The owner
173+
function getOwner() public view returns (address owner_) {
174+
return owner;
175+
}
176+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
3+
/*
4+
This file is part of the Enzyme Protocol.
5+
6+
(c) Enzyme Foundation <security@enzyme.finance>
7+
8+
For the full license information, please view the LICENSE
9+
file that was distributed with this source code.
10+
*/
11+
12+
pragma solidity >=0.6.0 <0.9.0;
13+
14+
/// @title IAaveV3FlashLoanAssetManager Interface
15+
/// @author Enzyme Foundation <security@enzyme.finance>
16+
interface IAaveV3FlashLoanAssetManager {
17+
struct Call {
18+
address target;
19+
bytes data;
20+
}
21+
22+
function flashLoan(address[] calldata _assets, uint256[] calldata _amounts, bytes calldata _encodedCalls)
23+
external;
24+
25+
function init(address _owner, address _vaultProxyAddress) external;
26+
}

tests/interfaces/external/IAaveV3Pool.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ interface IAaveV3Pool {
8080
uint256 data;
8181
}
8282

83+
function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint256 premiumInBps_);
84+
8385
function getReserveData(address _asset) external view returns (ReserveData memory reserveData_);
8486

8587
function getUserConfiguration(address _user)

tests/interfaces/interfaces.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ ISingleAssetRedemptionQueueFactory.sol: SingleAssetRedemptionQueueFactory.abi.js
5353
ISingleAssetRedemptionQueueLib.sol: SingleAssetRedemptionQueueLib.abi.json
5454

5555
# Smart accounts
56+
IAaveV3FlashLoanAssetManager.sol: AaveV3FlashLoanAssetManagerLib.abi.json
5657
IMultiCallAccountMixin.sol: MultiCallAccountMixin.abi.json
5758
IMultiCallAccountMixinHarness.sol: MultiCallAccountMixinHarness.abi.json
5859
ISharePriceThrottledAssetManagerLib.sol: SharePriceThrottledAssetManagerLib.abi.json

0 commit comments

Comments
 (0)