@ensuro/core 1.2.0 → 1.2.3

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.
Files changed (33) hide show
  1. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +2 -51
  2. package/build/contracts/DataTypes.sol/DataTypes.json +2 -2
  3. package/build/contracts/EToken.sol/EToken.json +2 -2
  4. package/build/contracts/Exchange.sol/Exchange.json +483 -0
  5. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +2 -2
  6. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +2 -2
  7. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +2 -90
  9. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +28 -2
  10. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +2 -2
  11. package/build/contracts/extras/IPriceRiskModule.sol/IPriceRiskModule.json +253 -0
  12. package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +1036 -0
  13. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +2 -2
  14. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +2 -2
  15. package/build/interfaces/IEToken.sol/IEToken.json +26 -0
  16. package/build/interfaces/IExchange.sol/IExchange.json +153 -0
  17. package/build/interfaces/IExchange.sol/IPriceOracle.json +30 -0
  18. package/build/interfaces/IPolicyHolder.sol/IPolicyHolder.json +108 -0
  19. package/build/interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json +13 -0
  20. package/contracts/AaveAssetManager.sol +35 -63
  21. package/contracts/BaseAssetManager.sol +55 -37
  22. package/contracts/EToken.sol +3 -19
  23. package/contracts/Exchange.sol +142 -0
  24. package/contracts/PolicyPool.sol +41 -35
  25. package/contracts/PolicyPoolComponent.sol +4 -0
  26. package/contracts/PolicyPoolConfig.sol +16 -0
  27. package/contracts/RiskModule.sol +5 -26
  28. package/contracts/extras/IPriceRiskModule.sol +41 -0
  29. package/contracts/extras/PriceRiskModule.sol +243 -0
  30. package/interfaces/IExchange.sol +47 -0
  31. package/interfaces/IPolicyHolder.sol +42 -0
  32. package/interfaces/IPolicyPoolConfig.sol +4 -0
  33. package/package.json +1 -1
@@ -0,0 +1,108 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "IPolicyHolder",
4
+ "sourceName": "interfaces/IPolicyHolder.sol",
5
+ "abi": [
6
+ {
7
+ "inputs": [
8
+ {
9
+ "internalType": "address",
10
+ "name": "operator",
11
+ "type": "address"
12
+ },
13
+ {
14
+ "internalType": "address",
15
+ "name": "from",
16
+ "type": "address"
17
+ },
18
+ {
19
+ "internalType": "uint256",
20
+ "name": "tokenId",
21
+ "type": "uint256"
22
+ },
23
+ {
24
+ "internalType": "bytes",
25
+ "name": "data",
26
+ "type": "bytes"
27
+ }
28
+ ],
29
+ "name": "onERC721Received",
30
+ "outputs": [
31
+ {
32
+ "internalType": "bytes4",
33
+ "name": "",
34
+ "type": "bytes4"
35
+ }
36
+ ],
37
+ "stateMutability": "nonpayable",
38
+ "type": "function"
39
+ },
40
+ {
41
+ "inputs": [
42
+ {
43
+ "internalType": "address",
44
+ "name": "operator",
45
+ "type": "address"
46
+ },
47
+ {
48
+ "internalType": "address",
49
+ "name": "from",
50
+ "type": "address"
51
+ },
52
+ {
53
+ "internalType": "uint256",
54
+ "name": "policyId",
55
+ "type": "uint256"
56
+ },
57
+ {
58
+ "internalType": "uint256",
59
+ "name": "amount",
60
+ "type": "uint256"
61
+ }
62
+ ],
63
+ "name": "onPayoutReceived",
64
+ "outputs": [
65
+ {
66
+ "internalType": "bytes4",
67
+ "name": "",
68
+ "type": "bytes4"
69
+ }
70
+ ],
71
+ "stateMutability": "nonpayable",
72
+ "type": "function"
73
+ },
74
+ {
75
+ "inputs": [
76
+ {
77
+ "internalType": "address",
78
+ "name": "operator",
79
+ "type": "address"
80
+ },
81
+ {
82
+ "internalType": "address",
83
+ "name": "from",
84
+ "type": "address"
85
+ },
86
+ {
87
+ "internalType": "uint256",
88
+ "name": "policyId",
89
+ "type": "uint256"
90
+ }
91
+ ],
92
+ "name": "onPolicyExpired",
93
+ "outputs": [
94
+ {
95
+ "internalType": "bytes4",
96
+ "name": "",
97
+ "type": "bytes4"
98
+ }
99
+ ],
100
+ "stateMutability": "nonpayable",
101
+ "type": "function"
102
+ }
103
+ ],
104
+ "bytecode": "0x",
105
+ "deployedBytecode": "0x",
106
+ "linkReferences": {},
107
+ "deployedLinkReferences": {}
108
+ }
@@ -184,6 +184,19 @@
184
184
  "stateMutability": "nonpayable",
185
185
  "type": "function"
186
186
  },
187
+ {
188
+ "inputs": [],
189
+ "name": "exchange",
190
+ "outputs": [
191
+ {
192
+ "internalType": "contract IExchange",
193
+ "name": "",
194
+ "type": "address"
195
+ }
196
+ ],
197
+ "stateMutability": "view",
198
+ "type": "function"
199
+ },
187
200
  {
188
201
  "inputs": [
189
202
  {
@@ -1,18 +1,18 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  pragma solidity ^0.8.0;
3
3
 
4
+ import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
4
5
  import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
6
  import {BaseAssetManager} from "./BaseAssetManager.sol";
6
7
  import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
8
  import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
8
9
  import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
10
+ import {IExchange} from "../interfaces/IExchange.sol";
9
11
  import {WadRayMath} from "./WadRayMath.sol";
10
12
  import {ILendingPoolAddressesProvider} from "@aave/protocol-v2/contracts/interfaces/ILendingPoolAddressesProvider.sol";
11
13
  import {ILendingPool} from "@aave/protocol-v2/contracts/interfaces/ILendingPool.sol";
12
14
  import {IAToken} from "@aave/protocol-v2/contracts/interfaces/IAToken.sol";
13
- import {IPriceOracle} from "@aave/protocol-v2/contracts/interfaces/IPriceOracle.sol";
14
15
  import {AaveProtocolDataProvider} from "@aave/protocol-v2/contracts/misc/AaveProtocolDataProvider.sol";
15
- import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
16
16
 
17
17
  /**
18
18
  * @title AssetManager that reinvests the capital in AAVE
@@ -29,6 +29,7 @@ import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUn
29
29
  contract AaveAssetManager is BaseAssetManager {
30
30
  using SafeERC20 for IERC20Metadata;
31
31
  using WadRayMath for uint256;
32
+ using AddressUpgradeable for address;
32
33
 
33
34
  bytes32 public constant SWAP_REWARDS_ROLE = keccak256("SWAP_REWARDS_ROLE");
34
35
 
@@ -41,10 +42,8 @@ contract AaveAssetManager is BaseAssetManager {
41
42
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
42
43
  IERC20Metadata internal immutable _rewardToken;
43
44
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
44
- IUniswapV2Router02 internal immutable _swapRouter; // We will use SushiSwap in Polygon
45
45
  uint256 internal _claimRewardsMin; // Minimum amount of rewards accumulated to claim
46
46
  uint256 internal _reinvestRewardsMin; // Minimum amount of rewards to reinvest into AAVE
47
- uint256 internal _maxSlippage; // Maximum slippage in WAD
48
47
 
49
48
  bytes32 internal constant DATA_PROVIDER_ID =
50
49
  0x0100000000000000000000000000000000000000000000000000000000000000;
@@ -54,13 +53,10 @@ contract AaveAssetManager is BaseAssetManager {
54
53
  event RewardSwapped(uint256 rewardIn, uint256 currencyOut);
55
54
 
56
55
  /// @custom:oz-upgrades-unsafe-allow constructor
57
- constructor(
58
- IPolicyPool policyPool_,
59
- ILendingPoolAddressesProvider aaveAddrProv_,
60
- IUniswapV2Router02 swapRouter_
61
- ) BaseAssetManager(policyPool_) {
56
+ constructor(IPolicyPool policyPool_, ILendingPoolAddressesProvider aaveAddrProv_)
57
+ BaseAssetManager(policyPool_)
58
+ {
62
59
  _aaveAddrProv = aaveAddrProv_;
63
- _swapRouter = swapRouter_;
64
60
  AaveProtocolDataProvider dataProvider = AaveProtocolDataProvider(
65
61
  aaveAddrProv_.getAddress(DATA_PROVIDER_ID)
66
62
  );
@@ -77,23 +73,19 @@ contract AaveAssetManager is BaseAssetManager {
77
73
  uint256 liquidityMiddle_,
78
74
  uint256 liquidityMax_,
79
75
  uint256 claimRewardsMin_,
80
- uint256 reinvestRewardsMin_,
81
- uint256 maxSlippage_
76
+ uint256 reinvestRewardsMin_
82
77
  ) public initializer {
83
78
  __BaseAssetManager_init(liquidityMin_, liquidityMiddle_, liquidityMax_);
84
- __AaveAssetManager_init(claimRewardsMin_, reinvestRewardsMin_, maxSlippage_);
79
+ __AaveAssetManager_init(claimRewardsMin_, reinvestRewardsMin_);
85
80
  }
86
81
 
87
82
  // solhint-disable-next-line func-name-mixedcase
88
- function __AaveAssetManager_init(
89
- uint256 claimRewardsMin_,
90
- uint256 reinvestRewardsMin_,
91
- uint256 maxSlippage_
92
- ) internal initializer {
83
+ function __AaveAssetManager_init(uint256 claimRewardsMin_, uint256 reinvestRewardsMin_)
84
+ internal
85
+ initializer
86
+ {
93
87
  _claimRewardsMin = claimRewardsMin_;
94
88
  _reinvestRewardsMin = reinvestRewardsMin_;
95
- require(maxSlippage_ <= 1e17, "maxSlippage can't be more than 10%");
96
- _maxSlippage = maxSlippage_;
97
89
  }
98
90
 
99
91
  function getInvestmentValue() public view override returns (uint256) {
@@ -130,14 +122,14 @@ contract AaveAssetManager is BaseAssetManager {
130
122
  return ILendingPool(_aaveAddrProv.getLendingPool());
131
123
  }
132
124
 
133
- function priceOracle() public view returns (IPriceOracle) {
134
- return IPriceOracle(_aaveAddrProv.getPriceOracle());
135
- }
136
-
137
125
  function _aaveDataProvider() internal view returns (AaveProtocolDataProvider) {
138
126
  return AaveProtocolDataProvider(_aaveAddrProv.getAddress(DATA_PROVIDER_ID));
139
127
  }
140
128
 
129
+ function _exchange() internal view returns (IExchange) {
130
+ return policyPool().config().exchange();
131
+ }
132
+
141
133
  function aToken() public view returns (IAToken) {
142
134
  return _aToken;
143
135
  }
@@ -158,19 +150,7 @@ contract AaveAssetManager is BaseAssetManager {
158
150
  }
159
151
 
160
152
  function _rewardToCurrency(uint256 amount) internal view returns (uint256) {
161
- // TODO Check: this is safe? Or I should use IPriceOracle instead
162
- IPriceOracle oracle = priceOracle();
163
- IERC20Metadata from_ = rewardToken();
164
- IERC20Metadata to_ = currency();
165
- uint256 exchangeRate = oracle.getAssetPrice(address(from_)).wadDiv(
166
- oracle.getAssetPrice(address(to_))
167
- );
168
- if (from_.decimals() > to_.decimals()) {
169
- exchangeRate /= 10**(from_.decimals() - to_.decimals());
170
- } else {
171
- exchangeRate *= 10**(from_.decimals() - to_.decimals());
172
- }
173
- return amount.wadMul(exchangeRate);
153
+ return _exchange().convert(address(rewardToken()), address(currency()), amount);
174
154
  }
175
155
 
176
156
  function reinvestRewardToken() public {
@@ -183,28 +163,31 @@ contract AaveAssetManager is BaseAssetManager {
183
163
  }
184
164
 
185
165
  function _swapRewards(uint256 amount, address outAddr) internal returns (uint256, uint256) {
186
- address[] memory path = _exchangePath();
187
- uint256 swapIn = IERC20Metadata(path[0]).balanceOf(address(this));
166
+ IERC20Metadata rw = rewardToken();
167
+ uint256 swapIn = rw.balanceOf(address(this));
188
168
  if (swapIn < amount) {
189
169
  uint256 toWithdraw = amount - swapIn;
190
170
  if (rewardAToken().balanceOf(address(this)) < toWithdraw) {
191
171
  toWithdraw = type(uint256).max; // if not enought withdraw all
192
172
  }
193
- swapIn += lendingPool().withdraw(path[0], toWithdraw, address(this));
173
+ swapIn += lendingPool().withdraw(address(rw), toWithdraw, address(this));
194
174
  } else {
195
175
  swapIn = amount;
196
176
  }
197
- uint256 swapOutMin = _rewardToCurrency(swapIn).wadMul(1e18 - _maxSlippage);
198
- IERC20Metadata(path[0]).approve(address(_swapRouter), swapIn);
199
- uint256[] memory amounts = _swapRouter.swapExactTokensForTokens(
177
+ address swapRouter = _exchange().getSwapRouter();
178
+ rw.approve(swapRouter, swapIn);
179
+ bytes memory swapCall = _exchange().sell(
180
+ address(rw),
181
+ address(currency()),
200
182
  swapIn,
201
- swapOutMin,
202
- path,
203
183
  outAddr,
204
184
  block.timestamp
205
185
  );
206
- emit RewardSwapped(swapIn, amounts[1]);
207
- return (swapIn, amounts[1]);
186
+
187
+ bytes memory response = swapRouter.functionCall(swapCall, "Swap operation failed");
188
+ uint256 swapOut = _exchange().decodeSwapOut(response);
189
+ emit RewardSwapped(swapIn, swapOut);
190
+ return (swapIn, swapOut);
208
191
  }
209
192
 
210
193
  function swapRewards(uint256 amount)
@@ -250,7 +233,11 @@ contract AaveAssetManager is BaseAssetManager {
250
233
  // In this case, it's safe using getAmountsIn to compute how many rewards are needed to swap
251
234
  // but then, when I do the swap I validate the slippage with the market price (given by AAVE's Oracle)
252
235
  // is acceptable
253
- uint256 requiredRewards = _swapRouter.getAmountsIn(remainingAmount, _exchangePath())[0];
236
+ uint256 requiredRewards = _exchange().getAmountIn(
237
+ address(rewardToken()),
238
+ address(currency()),
239
+ remainingAmount
240
+ );
254
241
  (, uint256 currencyOut) = _swapRewards(requiredRewards, address(_policyPool));
255
242
  if (currencyOut < remainingAmount) {
256
243
  remainingAmount -= currencyOut;
@@ -286,10 +273,6 @@ contract AaveAssetManager is BaseAssetManager {
286
273
  return _reinvestRewardsMin;
287
274
  }
288
275
 
289
- function maxSlippage() external view returns (uint256) {
290
- return _maxSlippage;
291
- }
292
-
293
276
  function setClaimRewardsMin(uint256 newValue) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
294
277
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
295
278
  require(
@@ -312,15 +295,4 @@ contract AaveAssetManager is BaseAssetManager {
312
295
  _reinvestRewardsMin = newValue;
313
296
  _parameterChanged(IPolicyPoolConfig.GovernanceActions.setReinvestRewardsMin, newValue, tweak);
314
297
  }
315
-
316
- function setMaxSlippage(uint256 newValue) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
317
- require(newValue <= 1e17, "maxSlippage can't be more than 10%");
318
- bool tweak = !hasPoolRole(LEVEL2_ROLE);
319
- require(
320
- !tweak || _isTweakWad(_maxSlippage, newValue, 3e26),
321
- "Tweak exceeded: maxSlippage tweaks only up to 30%"
322
- );
323
- _maxSlippage = newValue;
324
- _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxSlippage, newValue, tweak);
325
- }
326
298
  }
@@ -34,11 +34,6 @@ abstract contract BaseAssetManager is IAssetManager, PolicyPoolComponent {
34
34
  event MoneyDeinvested(uint256 amount);
35
35
  event EarningsDistributed(bool positive, uint256 amount);
36
36
 
37
- modifier validateParamsAfterChange() {
38
- _;
39
- _validateParameters();
40
- }
41
-
42
37
  /// @custom:oz-upgrades-unsafe-allow constructor
43
38
  // solhint-disable-next-line no-empty-blocks
44
39
  constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
@@ -74,7 +69,7 @@ abstract contract BaseAssetManager is IAssetManager, PolicyPoolComponent {
74
69
  _validateParameters();
75
70
  }
76
71
 
77
- function _validateParameters() internal view {
72
+ function _validateParameters() internal view override {
78
73
  require(
79
74
  _liquidityMin <= _liquidityMiddle && _liquidityMiddle <= _liquidityMax,
80
75
  "Validation: Liquidity limits are invalid"
@@ -217,31 +212,7 @@ abstract contract BaseAssetManager is IAssetManager, PolicyPoolComponent {
217
212
  return _liquidityMax;
218
213
  }
219
214
 
220
- function setLiquidityMin(uint256 newValue)
221
- external
222
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
223
- validateParamsAfterChange
224
- {
225
- _setLiquidityMin(newValue);
226
- }
227
-
228
- function setLiquidityMiddle(uint256 newValue)
229
- external
230
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
231
- validateParamsAfterChange
232
- {
233
- _setLiquidityMiddle(newValue);
234
- }
235
-
236
- function setLiquidityMax(uint256 newValue)
237
- external
238
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
239
- validateParamsAfterChange
240
- {
241
- _setLiquidityMax(newValue);
242
- }
243
-
244
- function _setLiquidityMin(uint256 newValue) internal {
215
+ function setLiquidityMin(uint256 newValue) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
245
216
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
246
217
  require(
247
218
  !tweak || _isTweakRay(_liquidityMin, newValue, 3e26),
@@ -251,7 +222,7 @@ abstract contract BaseAssetManager is IAssetManager, PolicyPoolComponent {
251
222
  _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMin, newValue, tweak);
252
223
  }
253
224
 
254
- function _setLiquidityMiddle(uint256 newValue) internal {
225
+ function setLiquidityMiddle(uint256 newValue) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
255
226
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
256
227
  require(
257
228
  !tweak || _isTweakRay(_liquidityMiddle, newValue, 3e26),
@@ -261,7 +232,7 @@ abstract contract BaseAssetManager is IAssetManager, PolicyPoolComponent {
261
232
  _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMiddle, newValue, tweak);
262
233
  }
263
234
 
264
- function _setLiquidityMax(uint256 newValue) internal {
235
+ function setLiquidityMax(uint256 newValue) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
265
236
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
266
237
  require(
267
238
  !tweak || _isTweakRay(_liquidityMax, newValue, 3e26),
@@ -275,9 +246,56 @@ abstract contract BaseAssetManager is IAssetManager, PolicyPoolComponent {
275
246
  uint256 min,
276
247
  uint256 middle,
277
248
  uint256 max
278
- ) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) validateParamsAfterChange {
279
- if (min != type(uint256).max) _setLiquidityMin(min);
280
- if (middle != type(uint256).max) _setLiquidityMiddle(middle);
281
- if (max != type(uint256).max) _setLiquidityMax(max);
249
+ ) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
250
+ bool setMin = min != type(uint256).max;
251
+ bool setMiddle = middle != type(uint256).max;
252
+ bool setMax = max != type(uint256).max;
253
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
254
+ // First set all - Then call _parameterChanged
255
+ if (setMin) {
256
+ require(
257
+ !tweak || _isTweakRay(_liquidityMin, min, 3e26),
258
+ "Tweak exceeded: liquidityMin tweaks only up to 30%"
259
+ );
260
+ _liquidityMin = min;
261
+ }
262
+ if (setMiddle) {
263
+ require(
264
+ !tweak || _isTweakRay(_liquidityMiddle, middle, 3e26),
265
+ "Tweak exceeded: liquidityMiddle tweaks only up to 30%"
266
+ );
267
+ _liquidityMiddle = middle;
268
+ }
269
+ if (setMax) {
270
+ require(
271
+ !tweak || _isTweakRay(_liquidityMax, max, 3e26),
272
+ "Tweak exceeded: liquidityMax tweaks only up to 30%"
273
+ );
274
+ _liquidityMax = max;
275
+ }
276
+ if (setMin) _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMin, min, tweak);
277
+ if (setMiddle)
278
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMiddle, middle, tweak);
279
+ if (setMax) _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMax, max, tweak);
280
+ }
281
+
282
+ function _setLiquidityMiddle(uint256 newValue) internal {
283
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
284
+ require(
285
+ !tweak || _isTweakRay(_liquidityMiddle, newValue, 3e26),
286
+ "Tweak exceeded: liquidityMiddle tweaks only up to 30%"
287
+ );
288
+ _liquidityMiddle = newValue;
289
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMiddle, newValue, tweak);
290
+ }
291
+
292
+ function _setLiquidityMax(uint256 newValue) internal {
293
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
294
+ require(
295
+ !tweak || _isTweakRay(_liquidityMax, newValue, 3e26),
296
+ "Tweak exceeded: liquidityMax tweaks only up to 30%"
297
+ );
298
+ _liquidityMax = newValue;
299
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityMax, newValue, tweak);
282
300
  }
283
301
  }
@@ -64,11 +64,6 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
64
64
  _;
65
65
  }
66
66
 
67
- modifier validateParamsAfterChange() {
68
- _;
69
- _validateParameters();
70
- }
71
-
72
67
  /// @custom:oz-upgrades-unsafe-allow constructor
73
68
  // solhint-disable-next-line no-empty-blocks
74
69
  constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
@@ -130,7 +125,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
130
125
  }
131
126
 
132
127
  // runs validation on EToken parameters
133
- function _validateParameters() internal view {
128
+ function _validateParameters() internal view override {
134
129
  require(
135
130
  _liquidityRequirement >= 8e26 && _liquidityRequirement <= 13e26,
136
131
  "Validation: liquidityRequirement must be [0.8, 1.3]"
@@ -703,7 +698,6 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
703
698
  function setPoolLoanInterestRate(uint256 newRate)
704
699
  external
705
700
  onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
706
- validateParamsAfterChange
707
701
  {
708
702
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
709
703
  require(
@@ -718,7 +712,6 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
718
712
  function setLiquidityRequirement(uint256 newRate)
719
713
  external
720
714
  onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
721
- validateParamsAfterChange
722
715
  {
723
716
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
724
717
  require(
@@ -729,11 +722,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
729
722
  _parameterChanged(IPolicyPoolConfig.GovernanceActions.setLiquidityRequirement, newRate, tweak);
730
723
  }
731
724
 
732
- function setMaxUtilizationRate(uint256 newRate)
733
- external
734
- onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE)
735
- validateParamsAfterChange
736
- {
725
+ function setMaxUtilizationRate(uint256 newRate) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
737
726
  bool tweak = !hasPoolRole(LEVEL2_ROLE);
738
727
  require(
739
728
  !tweak || _isTweakRay(_maxUtilizationRate, newRate, 3e26),
@@ -743,11 +732,7 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
743
732
  _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxUtilizationRate, newRate, tweak);
744
733
  }
745
734
 
746
- function setAcceptAllRMs(bool acceptAllRMs_)
747
- external
748
- onlyPoolRole(LEVEL2_ROLE)
749
- validateParamsAfterChange
750
- {
735
+ function setAcceptAllRMs(bool acceptAllRMs_) external onlyPoolRole(LEVEL2_ROLE) {
751
736
  _acceptAllRMs = acceptAllRMs_;
752
737
  _parameterChanged(
753
738
  IPolicyPoolConfig.GovernanceActions.setAcceptAllRMs,
@@ -759,7 +744,6 @@ contract EToken is PolicyPoolComponent, IERC20Metadata, IEToken {
759
744
  function setAcceptException(address riskModule, bool isException)
760
745
  external
761
746
  onlyPoolRole(LEVEL2_ROLE)
762
- validateParamsAfterChange
763
747
  {
764
748
  _acceptExceptions[riskModule] = isException;
765
749
  uint256 value = uint160(riskModule);
@@ -0,0 +1,142 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
5
+ import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
6
+ import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
7
+ import {IPolicyPoolConfig} from "../interfaces/IPolicyPoolConfig.sol";
8
+ import {PolicyPoolComponent} from "./PolicyPoolComponent.sol";
9
+ import {IExchange, IPriceOracle} from "../interfaces/IExchange.sol";
10
+ import {WadRayMath} from "./WadRayMath.sol";
11
+
12
+ /**
13
+ * @title Exchange contract
14
+ * @dev Helper contract that handles exchange operations between assets
15
+ * @custom:security-contact security@ensuro.co
16
+ * @author Ensuro
17
+ */
18
+ contract Exchange is IExchange, PolicyPoolComponent {
19
+ using WadRayMath for uint256;
20
+
21
+ IPriceOracle internal _oracle;
22
+ IUniswapV2Router02 internal _swapRouter; // We will use SushiSwap in Polygon
23
+ uint256 internal _maxSlippage; // Maximum slippage in WAD
24
+
25
+ /// @custom:oz-upgrades-unsafe-allow constructor
26
+ // solhint-disable-next-line no-empty-blocks
27
+ constructor(IPolicyPool policyPool_) PolicyPoolComponent(policyPool_) {}
28
+
29
+ /**
30
+ * @dev Initializes the RiskModule
31
+ * @param oracle_ Oracle that provides the price of the assets
32
+ * @param swapRouter_ Uniswap compatible exchange
33
+ * @param maxSlippage_ Maximum slippage admitted for exchange operations
34
+ */
35
+ function initialize(
36
+ IPriceOracle oracle_,
37
+ IUniswapV2Router02 swapRouter_,
38
+ uint256 maxSlippage_
39
+ ) public initializer {
40
+ __PolicyPoolComponent_init();
41
+ __Exchange_init(oracle_, swapRouter_, maxSlippage_);
42
+ }
43
+
44
+ // solhint-disable-next-line func-name-mixedcase
45
+ function __Exchange_init(
46
+ IPriceOracle oracle_,
47
+ IUniswapV2Router02 swapRouter_,
48
+ uint256 maxSlippage_
49
+ ) internal initializer {
50
+ _oracle = oracle_;
51
+ _swapRouter = swapRouter_;
52
+ _maxSlippage = maxSlippage_;
53
+ _validateParameters();
54
+ }
55
+
56
+ function _validateParameters() internal view override {
57
+ require(_maxSlippage <= 1e17, "maxSlippage can't be more than 10%");
58
+ require(address(_oracle) != address(0), "I need a price oracle");
59
+ }
60
+
61
+ function convert(
62
+ address assetFrom,
63
+ address assetTo,
64
+ uint256 amount
65
+ ) public view override returns (uint256) {
66
+ uint256 exchangeRate = _oracle.getAssetPrice(assetFrom).wadDiv(_oracle.getAssetPrice(assetTo));
67
+ uint8 decFrom = IERC20Metadata(assetFrom).decimals();
68
+ uint8 decTo = IERC20Metadata(assetTo).decimals();
69
+ if (decFrom > decTo) {
70
+ exchangeRate /= 10**(decFrom - decTo);
71
+ } else {
72
+ exchangeRate *= 10**(decTo - decFrom);
73
+ }
74
+ return amount.wadMul(exchangeRate);
75
+ }
76
+
77
+ function getAmountIn(
78
+ address assetIn,
79
+ address assetOut,
80
+ uint256 amountOut
81
+ ) external view override returns (uint256) {
82
+ return _swapRouter.getAmountsIn(amountOut, _exchangePath(assetIn, assetOut))[0];
83
+ }
84
+
85
+ function getSwapRouter() external view override returns (address) {
86
+ return address(_swapRouter);
87
+ }
88
+
89
+ function getPriceOracle() external view override returns (IPriceOracle) {
90
+ return IPriceOracle(_oracle);
91
+ }
92
+
93
+ function _exchangePath(address assetIn, address assetOut)
94
+ internal
95
+ pure
96
+ returns (address[] memory)
97
+ {
98
+ address[] memory path = new address[](2);
99
+ path[0] = assetIn;
100
+ path[1] = assetOut;
101
+ return path;
102
+ }
103
+
104
+ function sell(
105
+ address assetIn,
106
+ address assetOut,
107
+ uint256 amountInExact,
108
+ address outAddr,
109
+ uint256 deadline
110
+ ) external view override returns (bytes memory) {
111
+ uint256 amountOutMin = convert(assetIn, assetOut, amountInExact).wadMul(1e18 - _maxSlippage);
112
+
113
+ return
114
+ abi.encodeWithSignature(
115
+ "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)",
116
+ amountInExact,
117
+ amountOutMin,
118
+ _exchangePath(assetIn, assetOut),
119
+ outAddr,
120
+ deadline
121
+ );
122
+ }
123
+
124
+ function decodeSwapOut(bytes memory responseData) external pure override returns (uint256) {
125
+ return abi.decode(responseData, (uint256[]))[1];
126
+ }
127
+
128
+ function maxSlippage() external view returns (uint256) {
129
+ return _maxSlippage;
130
+ }
131
+
132
+ function setMaxSlippage(uint256 newValue) external onlyPoolRole2(LEVEL2_ROLE, LEVEL3_ROLE) {
133
+ require(newValue <= 1e17, "maxSlippage can't be more than 10%");
134
+ bool tweak = !hasPoolRole(LEVEL2_ROLE);
135
+ require(
136
+ !tweak || _isTweakWad(_maxSlippage, newValue, 3e26),
137
+ "Tweak exceeded: maxSlippage tweaks only up to 30%"
138
+ );
139
+ _maxSlippage = newValue;
140
+ _parameterChanged(IPolicyPoolConfig.GovernanceActions.setMaxSlippage, newValue, tweak);
141
+ }
142
+ }