@shift-defi/strategies 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/contracts/curve-gauge/CurveGauge.sol +296 -0
- package/contracts/dependencies/aave-v3/DataTypes.sol +333 -0
- package/contracts/dependencies/aave-v3/IAtoken.sol +141 -0
- package/contracts/dependencies/aave-v3/IInitializableAToken.sol +51 -0
- package/contracts/dependencies/aave-v3/IPool.sol +822 -0
- package/contracts/dependencies/aave-v3/IPoolAddressesProvider.sol +223 -0
- package/contracts/dependencies/aave-v3/IScaledBalanceToken.sol +66 -0
- package/contracts/dependencies/angle/IAngleMerkleDistributor.sol +11 -0
- package/contracts/dependencies/compound-v3/ICompoundV3.sol +17 -0
- package/contracts/dependencies/curve/ICurveStableSwapNG.sol +16 -0
- package/contracts/dependencies/curve/ILiquidityGaugeV6.sol +16 -0
- package/contracts/dependencies/fluid/IFluidLendingResolver.sol +6 -0
- package/contracts/dependencies/fluid/IFluidMerkleDistributor.sol +26 -0
- package/contracts/dependencies/fluid/IFluidToken.sol +14 -0
- package/contracts/fluid/FluidSupply.sol +255 -0
- package/contracts/interfaces/ICompoundV3Supply.sol +32 -0
- package/contracts/interfaces/ICurveGauge.sol +35 -0
- package/contracts/interfaces/IFluidSupply.sol +26 -0
- package/contracts/interfaces/IMorphoVault.sol +63 -0
- package/contracts/morpho/MorphoVault.sol +340 -0
- package/package.json +37 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IPoolAddressesProvider
|
|
6
|
+
* @author Aave
|
|
7
|
+
* @notice Defines the basic interface for a Pool Addresses Provider.
|
|
8
|
+
*/
|
|
9
|
+
interface IPoolAddressesProvider {
|
|
10
|
+
/**
|
|
11
|
+
* @dev Emitted when the market identifier is updated.
|
|
12
|
+
* @param oldMarketId The old id of the market
|
|
13
|
+
* @param newMarketId The new id of the market
|
|
14
|
+
*/
|
|
15
|
+
event MarketIdSet(string indexed oldMarketId, string indexed newMarketId);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @dev Emitted when the pool is updated.
|
|
19
|
+
* @param oldAddress The old address of the Pool
|
|
20
|
+
* @param newAddress The new address of the Pool
|
|
21
|
+
*/
|
|
22
|
+
event PoolUpdated(address indexed oldAddress, address indexed newAddress);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @dev Emitted when the pool configurator is updated.
|
|
26
|
+
* @param oldAddress The old address of the PoolConfigurator
|
|
27
|
+
* @param newAddress The new address of the PoolConfigurator
|
|
28
|
+
*/
|
|
29
|
+
event PoolConfiguratorUpdated(address indexed oldAddress, address indexed newAddress);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @dev Emitted when the price oracle is updated.
|
|
33
|
+
* @param oldAddress The old address of the PriceOracle
|
|
34
|
+
* @param newAddress The new address of the PriceOracle
|
|
35
|
+
*/
|
|
36
|
+
event PriceOracleUpdated(address indexed oldAddress, address indexed newAddress);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @dev Emitted when the ACL manager is updated.
|
|
40
|
+
* @param oldAddress The old address of the ACLManager
|
|
41
|
+
* @param newAddress The new address of the ACLManager
|
|
42
|
+
*/
|
|
43
|
+
event ACLManagerUpdated(address indexed oldAddress, address indexed newAddress);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @dev Emitted when the ACL admin is updated.
|
|
47
|
+
* @param oldAddress The old address of the ACLAdmin
|
|
48
|
+
* @param newAddress The new address of the ACLAdmin
|
|
49
|
+
*/
|
|
50
|
+
event ACLAdminUpdated(address indexed oldAddress, address indexed newAddress);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @dev Emitted when the price oracle sentinel is updated.
|
|
54
|
+
* @param oldAddress The old address of the PriceOracleSentinel
|
|
55
|
+
* @param newAddress The new address of the PriceOracleSentinel
|
|
56
|
+
*/
|
|
57
|
+
event PriceOracleSentinelUpdated(address indexed oldAddress, address indexed newAddress);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @dev Emitted when the pool data provider is updated.
|
|
61
|
+
* @param oldAddress The old address of the PoolDataProvider
|
|
62
|
+
* @param newAddress The new address of the PoolDataProvider
|
|
63
|
+
*/
|
|
64
|
+
event PoolDataProviderUpdated(address indexed oldAddress, address indexed newAddress);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @dev Emitted when a new proxy is created.
|
|
68
|
+
* @param id The identifier of the proxy
|
|
69
|
+
* @param proxyAddress The address of the created proxy contract
|
|
70
|
+
* @param implementationAddress The address of the implementation contract
|
|
71
|
+
*/
|
|
72
|
+
event ProxyCreated(bytes32 indexed id, address indexed proxyAddress, address indexed implementationAddress);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @dev Emitted when a new non-proxied contract address is registered.
|
|
76
|
+
* @param id The identifier of the contract
|
|
77
|
+
* @param oldAddress The address of the old contract
|
|
78
|
+
* @param newAddress The address of the new contract
|
|
79
|
+
*/
|
|
80
|
+
event AddressSet(bytes32 indexed id, address indexed oldAddress, address indexed newAddress);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @dev Emitted when the implementation of the proxy registered with id is updated
|
|
84
|
+
* @param id The identifier of the contract
|
|
85
|
+
* @param proxyAddress The address of the proxy contract
|
|
86
|
+
* @param oldImplementationAddress The address of the old implementation contract
|
|
87
|
+
* @param newImplementationAddress The address of the new implementation contract
|
|
88
|
+
*/
|
|
89
|
+
event AddressSetAsProxy(
|
|
90
|
+
bytes32 indexed id,
|
|
91
|
+
address indexed proxyAddress,
|
|
92
|
+
address oldImplementationAddress,
|
|
93
|
+
address indexed newImplementationAddress
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @notice Returns the id of the Aave market to which this contract points to.
|
|
98
|
+
* @return The market id
|
|
99
|
+
*/
|
|
100
|
+
function getMarketId() external view returns (string memory);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @notice Associates an id with a specific PoolAddressesProvider.
|
|
104
|
+
* @dev This can be used to create an onchain registry of PoolAddressesProviders to
|
|
105
|
+
* identify and validate multiple Aave markets.
|
|
106
|
+
* @param newMarketId The market id
|
|
107
|
+
*/
|
|
108
|
+
function setMarketId(string calldata newMarketId) external;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @notice Returns an address by its identifier.
|
|
112
|
+
* @dev The returned address might be an EOA or a contract, potentially proxied
|
|
113
|
+
* @dev It returns ZERO if there is no registered address with the given id
|
|
114
|
+
* @param id The id
|
|
115
|
+
* @return The address of the registered for the specified id
|
|
116
|
+
*/
|
|
117
|
+
function getAddress(bytes32 id) external view returns (address);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @notice General function to update the implementation of a proxy registered with
|
|
121
|
+
* certain `id`. If there is no proxy registered, it will instantiate one and
|
|
122
|
+
* set as implementation the `newImplementationAddress`.
|
|
123
|
+
* @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit
|
|
124
|
+
* setter function, in order to avoid unexpected consequences
|
|
125
|
+
* @param id The id
|
|
126
|
+
* @param newImplementationAddress The address of the new implementation
|
|
127
|
+
*/
|
|
128
|
+
function setAddressAsProxy(bytes32 id, address newImplementationAddress) external;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @notice Sets an address for an id replacing the address saved in the addresses map.
|
|
132
|
+
* @dev IMPORTANT Use this function carefully, as it will do a hard replacement
|
|
133
|
+
* @param id The id
|
|
134
|
+
* @param newAddress The address to set
|
|
135
|
+
*/
|
|
136
|
+
function setAddress(bytes32 id, address newAddress) external;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @notice Returns the address of the Pool proxy.
|
|
140
|
+
* @return The Pool proxy address
|
|
141
|
+
*/
|
|
142
|
+
function getPool() external view returns (address);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @notice Updates the implementation of the Pool, or creates a proxy
|
|
146
|
+
* setting the new `pool` implementation when the function is called for the first time.
|
|
147
|
+
* @param newPoolImpl The new Pool implementation
|
|
148
|
+
*/
|
|
149
|
+
function setPoolImpl(address newPoolImpl) external;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @notice Returns the address of the PoolConfigurator proxy.
|
|
153
|
+
* @return The PoolConfigurator proxy address
|
|
154
|
+
*/
|
|
155
|
+
function getPoolConfigurator() external view returns (address);
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @notice Updates the implementation of the PoolConfigurator, or creates a proxy
|
|
159
|
+
* setting the new `PoolConfigurator` implementation when the function is called for the first time.
|
|
160
|
+
* @param newPoolConfiguratorImpl The new PoolConfigurator implementation
|
|
161
|
+
*/
|
|
162
|
+
function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @notice Returns the address of the price oracle.
|
|
166
|
+
* @return The address of the PriceOracle
|
|
167
|
+
*/
|
|
168
|
+
function getPriceOracle() external view returns (address);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @notice Updates the address of the price oracle.
|
|
172
|
+
* @param newPriceOracle The address of the new PriceOracle
|
|
173
|
+
*/
|
|
174
|
+
function setPriceOracle(address newPriceOracle) external;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @notice Returns the address of the ACL manager.
|
|
178
|
+
* @return The address of the ACLManager
|
|
179
|
+
*/
|
|
180
|
+
function getACLManager() external view returns (address);
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @notice Updates the address of the ACL manager.
|
|
184
|
+
* @param newAclManager The address of the new ACLManager
|
|
185
|
+
*/
|
|
186
|
+
function setACLManager(address newAclManager) external;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @notice Returns the address of the ACL admin.
|
|
190
|
+
* @return The address of the ACL admin
|
|
191
|
+
*/
|
|
192
|
+
function getACLAdmin() external view returns (address);
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @notice Updates the address of the ACL admin.
|
|
196
|
+
* @param newAclAdmin The address of the new ACL admin
|
|
197
|
+
*/
|
|
198
|
+
function setACLAdmin(address newAclAdmin) external;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @notice Returns the address of the price oracle sentinel.
|
|
202
|
+
* @return The address of the PriceOracleSentinel
|
|
203
|
+
*/
|
|
204
|
+
function getPriceOracleSentinel() external view returns (address);
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @notice Updates the address of the price oracle sentinel.
|
|
208
|
+
* @param newPriceOracleSentinel The address of the new PriceOracleSentinel
|
|
209
|
+
*/
|
|
210
|
+
function setPriceOracleSentinel(address newPriceOracleSentinel) external;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @notice Returns the address of the data provider.
|
|
214
|
+
* @return The address of the DataProvider
|
|
215
|
+
*/
|
|
216
|
+
function getPoolDataProvider() external view returns (address);
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @notice Updates the address of the data provider.
|
|
220
|
+
* @param newDataProvider The address of the new DataProvider
|
|
221
|
+
*/
|
|
222
|
+
function setPoolDataProvider(address newDataProvider) external;
|
|
223
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IScaledBalanceToken
|
|
6
|
+
* @author Aave
|
|
7
|
+
* @notice Defines the basic interface for a scaled-balance token.
|
|
8
|
+
*/
|
|
9
|
+
interface IScaledBalanceToken {
|
|
10
|
+
/**
|
|
11
|
+
* @dev Emitted after the mint action
|
|
12
|
+
* @param caller The address performing the mint
|
|
13
|
+
* @param onBehalfOf The address of the user that will receive the minted tokens
|
|
14
|
+
* @param value The scaled-up amount being minted (based on user entered amount and balance increase from interest)
|
|
15
|
+
* @param balanceIncrease The increase in scaled-up balance since the last action of 'onBehalfOf'
|
|
16
|
+
* @param index The next liquidity index of the reserve
|
|
17
|
+
*/
|
|
18
|
+
event Mint(
|
|
19
|
+
address indexed caller,
|
|
20
|
+
address indexed onBehalfOf,
|
|
21
|
+
uint256 value,
|
|
22
|
+
uint256 balanceIncrease,
|
|
23
|
+
uint256 index
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @dev Emitted after the burn action
|
|
28
|
+
* @dev If the burn function does not involve a transfer of the underlying asset, the target defaults to zero address
|
|
29
|
+
* @param from The address from which the tokens will be burned
|
|
30
|
+
* @param target The address that will receive the underlying, if any
|
|
31
|
+
* @param value The scaled-up amount being burned (user entered amount - balance increase from interest)
|
|
32
|
+
* @param balanceIncrease The increase in scaled-up balance since the last action of 'from'
|
|
33
|
+
* @param index The next liquidity index of the reserve
|
|
34
|
+
*/
|
|
35
|
+
event Burn(address indexed from, address indexed target, uint256 value, uint256 balanceIncrease, uint256 index);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @notice Returns the scaled balance of the user.
|
|
39
|
+
* @dev The scaled balance is the sum of all the updated stored balance divided by the reserve's liquidity index
|
|
40
|
+
* at the moment of the update
|
|
41
|
+
* @param user The user whose balance is calculated
|
|
42
|
+
* @return The scaled balance of the user
|
|
43
|
+
*/
|
|
44
|
+
function scaledBalanceOf(address user) external view returns (uint256);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @notice Returns the scaled balance of the user and the scaled total supply.
|
|
48
|
+
* @param user The address of the user
|
|
49
|
+
* @return The scaled balance of the user
|
|
50
|
+
* @return The scaled total supply
|
|
51
|
+
*/
|
|
52
|
+
function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @notice Returns the scaled total supply of the scaled balance token. Represents sum(debt/index)
|
|
56
|
+
* @return The scaled total supply
|
|
57
|
+
*/
|
|
58
|
+
function scaledTotalSupply() external view returns (uint256);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @notice Returns last index interest was accrued to the user's balance
|
|
62
|
+
* @param user The address of the user
|
|
63
|
+
* @return The last index interest was accrued to the user's balance, expressed in ray
|
|
64
|
+
*/
|
|
65
|
+
function getPreviousIndex(address user) external view returns (uint256);
|
|
66
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
|
|
4
|
+
|
|
5
|
+
interface IcASSETv3 is IERC20 {
|
|
6
|
+
function baseToken() external view returns (address);
|
|
7
|
+
|
|
8
|
+
function supply(address, uint) external;
|
|
9
|
+
|
|
10
|
+
function withdraw(address, uint) external;
|
|
11
|
+
|
|
12
|
+
function accrueAccount(address) external;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface IcRewards {
|
|
16
|
+
function claim(address, address, bool) external;
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
interface ICurveStableSwapNG {
|
|
5
|
+
function coins(uint256 index) external view returns (address);
|
|
6
|
+
|
|
7
|
+
function get_balances() external view returns (uint256[] memory);
|
|
8
|
+
|
|
9
|
+
function totalSupply() external view returns (uint256);
|
|
10
|
+
|
|
11
|
+
function add_liquidity(uint256[] memory, uint256) external;
|
|
12
|
+
|
|
13
|
+
function remove_liquidity(uint256, uint256[] memory) external;
|
|
14
|
+
|
|
15
|
+
function get_virtual_price() external view returns (uint256);
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
interface ILiquidityGaugeV6 {
|
|
5
|
+
function lp_token() external view returns (address);
|
|
6
|
+
|
|
7
|
+
function balanceOf(address) external view returns (uint256);
|
|
8
|
+
|
|
9
|
+
function deposit(uint256) external;
|
|
10
|
+
|
|
11
|
+
function withdraw(uint256) external;
|
|
12
|
+
|
|
13
|
+
function claim_rewards() external;
|
|
14
|
+
|
|
15
|
+
function reward_count() external view returns (uint256);
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
interface IFluidMerkleDistributor {
|
|
5
|
+
/// @notice Claims rewards for a given recipient
|
|
6
|
+
/// @param recipient_ - address of the recipient
|
|
7
|
+
/// @param cumulativeAmount_ - cumulative amount of rewards to claim
|
|
8
|
+
/// @param positionType_ - type of position, 1 for lending, 2 for vaults, 3 for smart lending, etc
|
|
9
|
+
/// @param positionId_ - id of the position, fToken address for lending and vaultId for vaults
|
|
10
|
+
/// @param cycle_ - cycle of the rewards
|
|
11
|
+
/// @param merkleProof_ - merkle proof of the rewards
|
|
12
|
+
/// @param metadata_ - metadata of the rewards
|
|
13
|
+
function claim(
|
|
14
|
+
address recipient_,
|
|
15
|
+
uint256 cumulativeAmount_,
|
|
16
|
+
uint8 positionType_,
|
|
17
|
+
bytes32 positionId_,
|
|
18
|
+
uint256 cycle_,
|
|
19
|
+
bytes32[] calldata merkleProof_,
|
|
20
|
+
bytes calldata metadata_
|
|
21
|
+
) external;
|
|
22
|
+
|
|
23
|
+
/// @notice Gets the address of the reward token
|
|
24
|
+
/// @return The address of the reward token
|
|
25
|
+
function TOKEN() external view returns (address);
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
interface IFluidToken {
|
|
5
|
+
function asset() external view returns (address);
|
|
6
|
+
|
|
7
|
+
function deposit(uint256 assets_, address receiver_) external returns (uint256 shares);
|
|
8
|
+
|
|
9
|
+
function redeem(uint256 shares_, address receiver_, address owner_) external returns (uint256 assets_);
|
|
10
|
+
|
|
11
|
+
function convertToAssets(uint256 shares_) external view returns (uint256);
|
|
12
|
+
|
|
13
|
+
function convertToShares(uint256 assets_) external view returns (uint256);
|
|
14
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.28;
|
|
3
|
+
|
|
4
|
+
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
|
|
5
|
+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
|
7
|
+
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
|
|
8
|
+
|
|
9
|
+
import {StrategyTemplate} from "@shift-defi/core/StrategyTemplate.sol";
|
|
10
|
+
import {IStrategyTemplate} from "@shift-defi/core/interfaces/IStrategyTemplate.sol";
|
|
11
|
+
import {Errors} from "@shift-defi/core/libraries/Errors.sol";
|
|
12
|
+
|
|
13
|
+
import {IFluidSupply} from "../interfaces/IFluidSupply.sol";
|
|
14
|
+
import {IFluidMerkleDistributor} from "../dependencies/fluid/IFluidMerkleDistributor.sol";
|
|
15
|
+
import {IFluidToken} from "../dependencies/fluid/IFluidToken.sol";
|
|
16
|
+
|
|
17
|
+
contract FluidSupply is AccessControlUpgradeable, IFluidSupply, StrategyTemplate {
|
|
18
|
+
using Math for uint256;
|
|
19
|
+
using SafeERC20 for IERC20;
|
|
20
|
+
|
|
21
|
+
/// @notice State ID for the Fluid supply state (asset supplied to Fluid)
|
|
22
|
+
bytes32 private constant FLUID_SUPPLY_STATE_ID = keccak256("FLUID_SUPPLY_STATE_ID");
|
|
23
|
+
|
|
24
|
+
/// @notice State ID for the token only state (holding the asset directly)
|
|
25
|
+
bytes32 private constant UNDERLYING_ASSET_STATE_ID = keccak256("UNDERLYING_ASSET_STATE_ID");
|
|
26
|
+
|
|
27
|
+
/// @notice Role for the Merkle claimer
|
|
28
|
+
bytes32 private constant MERKLE_CLAIMER_ROLE = keccak256("MERKLE_CLAIMER_ROLE");
|
|
29
|
+
|
|
30
|
+
/// @notice Position type for lending positions of Fluid merkle rewards
|
|
31
|
+
uint8 private constant POSITION_TYPE_LENDING = 1;
|
|
32
|
+
|
|
33
|
+
/// @notice The Fluid fToken address
|
|
34
|
+
address public fToken;
|
|
35
|
+
|
|
36
|
+
/// @notice The underlying asset address
|
|
37
|
+
address public underlyingAsset;
|
|
38
|
+
|
|
39
|
+
/// @notice The last recorded value of vault token amount * share price
|
|
40
|
+
uint256 public lastAssetsValue;
|
|
41
|
+
|
|
42
|
+
/// @notice The address of the Fluid Merkle distributor contract
|
|
43
|
+
address public merkleDistributor;
|
|
44
|
+
|
|
45
|
+
/// @notice The address of the Merkle reward token
|
|
46
|
+
address public merkleRewardToken;
|
|
47
|
+
|
|
48
|
+
struct SlippageParams {
|
|
49
|
+
uint256 enterMaxSlippage;
|
|
50
|
+
uint256 exitMaxSlippage;
|
|
51
|
+
uint256 emergencyExitMaxSlippage;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
constructor() {
|
|
55
|
+
_disableInitializers();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @notice Initializes the FluidSupply strategy contract
|
|
59
|
+
/// @dev Sets up the Fluid fToken and asset addresses, and configures the strategy states
|
|
60
|
+
/// @param strategyContainer The address of the strategy container contract
|
|
61
|
+
/// @param defaultAdmin The address of the default admin
|
|
62
|
+
/// @param merkleClaimer The address of the Merkle claimer
|
|
63
|
+
/// @param _fToken The address of the strategy's fToken
|
|
64
|
+
/// @param _merkleDistributor The address of the Merkle distributor
|
|
65
|
+
function initialize(
|
|
66
|
+
address strategyContainer,
|
|
67
|
+
address defaultAdmin,
|
|
68
|
+
address merkleClaimer,
|
|
69
|
+
address _fToken,
|
|
70
|
+
address _merkleDistributor,
|
|
71
|
+
SlippageParams memory slippageParams
|
|
72
|
+
) external initializer {
|
|
73
|
+
__AccessControl_init();
|
|
74
|
+
|
|
75
|
+
__StrategyTemplate_init(
|
|
76
|
+
strategyContainer,
|
|
77
|
+
slippageParams.enterMaxSlippage,
|
|
78
|
+
slippageParams.exitMaxSlippage,
|
|
79
|
+
slippageParams.emergencyExitMaxSlippage
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
require(defaultAdmin != address(0), Errors.ZeroAddress());
|
|
83
|
+
_grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
|
|
84
|
+
require(merkleClaimer != address(0), Errors.ZeroAddress());
|
|
85
|
+
_grantRole(MERKLE_CLAIMER_ROLE, merkleClaimer);
|
|
86
|
+
|
|
87
|
+
require(_fToken != address(0), Errors.ZeroAddress());
|
|
88
|
+
fToken = _fToken;
|
|
89
|
+
underlyingAsset = IFluidToken(_fToken).asset();
|
|
90
|
+
|
|
91
|
+
require(_merkleDistributor != address(0), Errors.ZeroAddress());
|
|
92
|
+
merkleDistributor = _merkleDistributor;
|
|
93
|
+
|
|
94
|
+
merkleRewardToken = IFluidMerkleDistributor(_merkleDistributor).TOKEN();
|
|
95
|
+
|
|
96
|
+
_setState(UNDERLYING_ASSET_STATE_ID, false, false, true, 0);
|
|
97
|
+
_setState(FLUID_SUPPLY_STATE_ID, true, true, false, 1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// @inheritdoc IStrategyTemplate
|
|
101
|
+
function stateNav(bytes32 stateId) public view override returns (uint256) {
|
|
102
|
+
if (stateId == FLUID_SUPPLY_STATE_ID) {
|
|
103
|
+
return fluidNav();
|
|
104
|
+
} else if (stateId == UNDERLYING_ASSET_STATE_ID) {
|
|
105
|
+
return underlyingAssetNav();
|
|
106
|
+
} else if (stateId == NO_ALLOCATION_STATE_ID) {
|
|
107
|
+
return 0;
|
|
108
|
+
} else {
|
|
109
|
+
revert StateNotFound(stateId);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// @notice Calculates the NAV for the Fluid supply state
|
|
114
|
+
/// @dev Returns the value of fTokens held by the contract in notion
|
|
115
|
+
/// @return The NAV of fluid supply state in notion
|
|
116
|
+
function fluidNav() public view returns (uint256) {
|
|
117
|
+
address fTokenCached = fToken;
|
|
118
|
+
uint256 fTokenBalance = IERC20(fTokenCached).balanceOf(address(this));
|
|
119
|
+
return getTokenAmountInNotion(underlyingAsset, IFluidToken(fTokenCached).convertToAssets(fTokenBalance));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// @notice Calculates the NAV for the asset state
|
|
123
|
+
/// @dev Returns the value of assets held by the contract in notion
|
|
124
|
+
/// @return The NAV of underlying assets state in notion
|
|
125
|
+
function underlyingAssetNav() public view returns (uint256) {
|
|
126
|
+
address underlyingAssetCached = underlyingAsset;
|
|
127
|
+
return getTokenAmountInNotion(underlyingAssetCached, IERC20(underlyingAssetCached).balanceOf(address(this)));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// @inheritdoc IFluidSupply
|
|
131
|
+
function manualClaim(ClaimParams calldata claimParams) external nonReentrant onlyRole(MERKLE_CLAIMER_ROLE) {
|
|
132
|
+
IFluidMerkleDistributor(merkleDistributor).claim(
|
|
133
|
+
address(this),
|
|
134
|
+
claimParams.cumulativeAmount,
|
|
135
|
+
POSITION_TYPE_LENDING,
|
|
136
|
+
bytes32(uint256(uint160(fToken))),
|
|
137
|
+
claimParams.cycle,
|
|
138
|
+
claimParams.merkleProof,
|
|
139
|
+
claimParams.metadata
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function _emergencyExit(bytes32 toStateId, uint256 share) internal override {
|
|
144
|
+
if (toStateId == UNDERLYING_ASSET_STATE_ID) {
|
|
145
|
+
_exitFluid(share);
|
|
146
|
+
} else {
|
|
147
|
+
revert StateNotFound(toStateId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function _enterState(bytes32 stateId) internal override {
|
|
152
|
+
if (stateId == FLUID_SUPPLY_STATE_ID) {
|
|
153
|
+
_enterFluid();
|
|
154
|
+
} else {
|
|
155
|
+
revert StateNotFound(stateId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function _exitFromState(bytes32 stateId, uint256 share) internal virtual override {
|
|
160
|
+
if (stateId == FLUID_SUPPLY_STATE_ID) {
|
|
161
|
+
_exitFluid(share);
|
|
162
|
+
} else {
|
|
163
|
+
revert StateNotFound(stateId);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function _enterTarget() internal override {
|
|
168
|
+
_enterFluid();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function _exitTarget(uint256 share) internal override {
|
|
172
|
+
_exitFluid(share);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function _enterFluid() internal {
|
|
176
|
+
address underlyingAssetCached = underlyingAsset;
|
|
177
|
+
address fTokenCached = fToken;
|
|
178
|
+
uint256 underlyingAssetBalance = IERC20(underlyingAssetCached).balanceOf(address(this));
|
|
179
|
+
|
|
180
|
+
if (underlyingAssetBalance == 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
IERC20(underlyingAssetCached).safeIncreaseAllowance(fTokenCached, underlyingAssetBalance);
|
|
185
|
+
IFluidToken(fTokenCached).deposit(underlyingAssetBalance, address(this));
|
|
186
|
+
lastAssetsValue = IFluidToken(fTokenCached).convertToAssets(IERC20(fTokenCached).balanceOf(address(this)));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function _exitFluid(uint256 share) internal {
|
|
190
|
+
require(share > 0, Errors.ZeroAmount());
|
|
191
|
+
require(share <= MAX_BPS, Errors.IncorrectAmount());
|
|
192
|
+
|
|
193
|
+
address fTokenCached = fToken;
|
|
194
|
+
uint256 fTokenAmountToRedeem = IERC20(fTokenCached).balanceOf(address(this)).mulDiv(share, MAX_BPS);
|
|
195
|
+
|
|
196
|
+
if (fTokenAmountToRedeem == 0) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
IFluidToken(fTokenCached).redeem(fTokenAmountToRedeem, address(this), address(this));
|
|
201
|
+
lastAssetsValue = IFluidToken(fTokenCached).convertToAssets(IERC20(fTokenCached).balanceOf(address(this)));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function _calculateAccruedAssetsValue() private view returns (uint256) {
|
|
205
|
+
address fTokenCached = fToken;
|
|
206
|
+
uint256 lastAssetsValueCached = lastAssetsValue;
|
|
207
|
+
uint256 currentAssetsValue = IFluidToken(fTokenCached).convertToAssets(
|
|
208
|
+
IERC20(fTokenCached).balanceOf(address(this))
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (currentAssetsValue > lastAssetsValueCached) {
|
|
212
|
+
return currentAssetsValue - lastAssetsValueCached;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function _harvest(bytes32 _stateId, address _treasury, uint256 _feePct) internal override {
|
|
219
|
+
AutomaticHarvestLocalVars memory vars;
|
|
220
|
+
vars.fTokenCached = fToken;
|
|
221
|
+
vars.merkleRewardToken = merkleRewardToken;
|
|
222
|
+
|
|
223
|
+
vars.accruedAssetsValue = _calculateAccruedAssetsValue();
|
|
224
|
+
|
|
225
|
+
if (vars.accruedAssetsValue > 0) {
|
|
226
|
+
vars.accruedFTokenAmount = IFluidToken(vars.fTokenCached).convertToShares(vars.accruedAssetsValue);
|
|
227
|
+
vars.fTokensToTreasury = vars.accruedFTokenAmount.mulDiv(_feePct, MAX_BPS);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (_stateId == FLUID_SUPPLY_STATE_ID) {
|
|
231
|
+
if (IERC20(vars.merkleRewardToken).balanceOf(address(this)) > 0) {
|
|
232
|
+
vars.fTokensAmountBefore = IERC20(vars.fTokenCached).balanceOf(address(this));
|
|
233
|
+
_swapToInputTokens(vars.merkleRewardToken, underlyingAsset, 0, false);
|
|
234
|
+
_enterFluid();
|
|
235
|
+
vars.reinvestFTokenDelta =
|
|
236
|
+
IERC20(vars.fTokenCached).balanceOf(address(this)) - vars.fTokensAmountBefore;
|
|
237
|
+
|
|
238
|
+
if (vars.reinvestFTokenDelta > 0) {
|
|
239
|
+
vars.feeFromReinvest = vars.reinvestFTokenDelta.mulDiv(_feePct, MAX_BPS);
|
|
240
|
+
if (vars.feeFromReinvest > 0) {
|
|
241
|
+
vars.fTokensToTreasury += vars.feeFromReinvest;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (vars.fTokensToTreasury > 0) {
|
|
248
|
+
IERC20(vars.fTokenCached).safeTransfer(_treasury, vars.fTokensToTreasury);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
lastAssetsValue = IFluidToken(vars.fTokenCached).convertToAssets(
|
|
252
|
+
IERC20(vars.fTokenCached).balanceOf(address(this))
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|