@ensuro/core 1.2.7 → 2.0.0-beta10

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 (109) hide show
  1. package/README.md +63 -27
  2. package/build/contracts/AccessManager.sol/AccessManager.json +559 -0
  3. package/build/contracts/ERC4626AssetManager.sol/ERC4626AssetManager.json +219 -0
  4. package/build/contracts/EToken.sol/EToken.json +217 -190
  5. package/build/contracts/LPManualWhitelist.sol/LPManualWhitelist.json +17 -4
  6. package/build/contracts/{extras/IPriceRiskModule.sol/IPriceRiskModule.json → LiquidityThresholdAssetManager.sol/LiquidityThresholdAssetManager.json} +81 -131
  7. package/build/contracts/Policy.sol/Policy.json +2 -2
  8. package/build/contracts/PolicyPool.sol/PolicyPool.json +425 -295
  9. package/build/contracts/PolicyPoolComponent.sol/PolicyPoolComponent.json +15 -2
  10. package/build/contracts/PremiumsAccount.sol/PremiumsAccount.json +868 -0
  11. package/build/contracts/{BaseAssetManager.sol/BaseAssetManager.json → Reserve.sol/Reserve.json} +37 -166
  12. package/build/contracts/RiskModule.sol/RiskModule.json +85 -312
  13. package/build/contracts/SignedQuoteRiskModule.sol/SignedQuoteRiskModule.json +960 -0
  14. package/build/contracts/{DataTypes.sol/DataTypes.json → TimeScaled.sol/TimeScaled.json} +4 -4
  15. package/build/contracts/TrustfulRiskModule.sol/TrustfulRiskModule.json +134 -336
  16. package/build/contracts/dependencies/ERC1967Proxy.sol/ERC1967Proxy.json +64 -0
  17. package/build/{interfaces/IExchange.sol/IExchange.json → contracts/dependencies/IERC20Metadata.sol/IERC20Metadata.json} +83 -79
  18. package/build/contracts/{WadRayMath.sol → dependencies/WadRayMath.sol}/WadRayMath.json +3 -3
  19. package/build/{interfaces/IPolicyPoolConfig.sol/IPolicyPoolConfig.json → contracts/interfaces/IAccessManager.sol/IAccessManager.json} +79 -77
  20. package/build/contracts/interfaces/IAssetManager.sol/IAssetManager.json +109 -0
  21. package/build/{interfaces → contracts/interfaces}/IEToken.sol/IEToken.json +39 -91
  22. package/build/{interfaces → contracts/interfaces}/ILPWhitelist.sol/ILPWhitelist.json +1 -1
  23. package/build/{interfaces → contracts/interfaces}/IPolicyHolder.sol/IPolicyHolder.json +1 -1
  24. package/build/{interfaces → contracts/interfaces}/IPolicyPool.sol/IPolicyPool.json +137 -100
  25. package/build/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol/IPolicyPoolComponent.json +1 -1
  26. package/build/contracts/interfaces/IPremiumsAccount.sol/IPremiumsAccount.json +315 -0
  27. package/build/{interfaces → contracts/interfaces}/IRiskModule.sol/IRiskModule.json +58 -34
  28. package/build/contracts/mocks/FixedRateVault.sol/FixedRateVault.json +736 -0
  29. package/build/contracts/mocks/ForwardProxy.sol/ForwardProxy.json +43 -0
  30. package/build/{interfaces/IInsolvencyHook.sol/IInsolvencyHook.json → contracts/mocks/IMintableERC20.sol/IMintableERC20.json} +13 -8
  31. package/build/contracts/mocks/PolicyHolderMock.sol/PolicyHolderMock.json +228 -0
  32. package/build/contracts/mocks/PolicyPoolComponentMock.sol/PolicyPoolComponentMock.json +35 -0
  33. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMock.json +702 -0
  34. package/build/contracts/mocks/PolicyPoolMock.sol/PolicyPoolMockForward.json +92 -0
  35. package/build/contracts/mocks/RiskModuleMock.sol/RiskModuleMock.json +764 -0
  36. package/build/contracts/mocks/TestCurrency.sol/TestCurrency.json +343 -0
  37. package/build/contracts/mocks/TestNFT.sol/TestNFT.json +388 -0
  38. package/contracts/AccessManager.sol +103 -0
  39. package/contracts/ERC4626AssetManager.sol +52 -0
  40. package/contracts/EToken.sol +254 -299
  41. package/contracts/LPManualWhitelist.sol +4 -4
  42. package/contracts/LiquidityThresholdAssetManager.sol +160 -0
  43. package/contracts/Policy.sol +49 -45
  44. package/contracts/PolicyPool.sol +379 -514
  45. package/contracts/PolicyPoolComponent.sol +41 -41
  46. package/contracts/PremiumsAccount.sol +480 -0
  47. package/contracts/Reserve.sol +213 -0
  48. package/contracts/RiskModule.sol +233 -174
  49. package/contracts/SignedQuoteRiskModule.sol +241 -0
  50. package/contracts/TimeScaled.sol +138 -0
  51. package/contracts/TrustfulRiskModule.sol +85 -23
  52. package/contracts/dependencies/ERC1967Proxy.sol +19 -0
  53. package/contracts/dependencies/IERC20Metadata.sol +19 -0
  54. package/contracts/dependencies/WadRayMath.sol +126 -0
  55. package/contracts/interfaces/IAccessManager.sol +148 -0
  56. package/contracts/interfaces/IAssetManager.sol +84 -0
  57. package/contracts/interfaces/IEToken.sol +195 -0
  58. package/contracts/interfaces/ILPWhitelist.sol +40 -0
  59. package/{interfaces → contracts/interfaces}/IPolicyHolder.sol +0 -0
  60. package/contracts/interfaces/IPolicyPool.sol +145 -0
  61. package/{interfaces → contracts/interfaces}/IPolicyPoolComponent.sol +3 -0
  62. package/contracts/interfaces/IPremiumsAccount.sol +76 -0
  63. package/contracts/interfaces/IRiskModule.sol +123 -0
  64. package/contracts/mocks/FixedRateVault.sol +85 -0
  65. package/contracts/mocks/ForwardProxy.sol +66 -0
  66. package/contracts/mocks/IMintableERC20.sol +13 -0
  67. package/contracts/mocks/PolicyHolderMock.sol +89 -0
  68. package/contracts/mocks/PolicyPoolComponentMock.sol +17 -0
  69. package/contracts/mocks/PolicyPoolMock.sol +148 -0
  70. package/contracts/mocks/RiskModuleMock.sol +75 -0
  71. package/contracts/mocks/TestCurrency.sol +35 -0
  72. package/contracts/mocks/TestNFT.sol +23 -0
  73. package/js/test-utils.js +356 -0
  74. package/package.json +2 -2
  75. package/build/contracts/AaveAssetManager.sol/AaveAssetManager.json +0 -742
  76. package/build/contracts/Exchange.sol/Exchange.json +0 -591
  77. package/build/contracts/FlightDelayRiskModule.sol/FlightDelayRiskModule.json +0 -1112
  78. package/build/contracts/PolicyNFT.sol/PolicyNFT.json +0 -570
  79. package/build/contracts/PolicyPoolConfig.sol/PolicyPoolConfig.json +0 -686
  80. package/build/contracts/dependencies/ChainlinkClientUpgradeable.sol/ChainlinkClientUpgradeable.json +0 -50
  81. package/build/contracts/extras/PriceRiskModule.sol/PriceRiskModule.json +0 -1055
  82. package/build/contracts/upgraded/IPolicyNFTv1.sol/IPolicyNFTv1.json +0 -322
  83. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1.json +0 -571
  84. package/build/contracts/upgraded/PolicyNFTv1.sol/PolicyNFTv1_Upgrade.json +0 -602
  85. package/build/interfaces/IAssetManager.sol/IAssetManager.json +0 -31
  86. package/build/interfaces/IExchange.sol/IPriceOracle.json +0 -30
  87. package/build/interfaces/IPolicyNFT.sol/IPolicyNFT.json +0 -321
  88. package/contracts/AaveAssetManager.sol +0 -298
  89. package/contracts/BaseAssetManager.sol +0 -301
  90. package/contracts/DataTypes.sol +0 -287
  91. package/contracts/Exchange.sol +0 -189
  92. package/contracts/FlightDelayRiskModule.sol +0 -208
  93. package/contracts/PolicyNFT.sol +0 -96
  94. package/contracts/PolicyPoolConfig.sol +0 -247
  95. package/contracts/WadRayMath.sol +0 -135
  96. package/contracts/dependencies/ChainlinkClientUpgradeable.sol +0 -371
  97. package/contracts/extras/IPriceRiskModule.sol +0 -41
  98. package/contracts/extras/PriceRiskModule.sol +0 -243
  99. package/contracts/upgraded/IPolicyNFTv1.sol +0 -15
  100. package/contracts/upgraded/PolicyNFTv1.sol +0 -126
  101. package/interfaces/IAssetManager.sol +0 -23
  102. package/interfaces/IEToken.sol +0 -48
  103. package/interfaces/IExchange.sol +0 -59
  104. package/interfaces/IInsolvencyHook.sol +0 -27
  105. package/interfaces/ILPWhitelist.sol +0 -23
  106. package/interfaces/IPolicyNFT.sol +0 -15
  107. package/interfaces/IPolicyPool.sol +0 -44
  108. package/interfaces/IPolicyPoolConfig.sol +0 -87
  109. package/interfaces/IRiskModule.sol +0 -29
@@ -0,0 +1,241 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
5
+ import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
6
+ import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
7
+ import {RiskModule} from "./RiskModule.sol";
8
+ import {Policy} from "./Policy.sol";
9
+
10
+ /**
11
+ * @title SignedQuote Risk Module
12
+ * @dev Risk Module that for policy creation verifies the different components of the price have been signed by a
13
+ trusted account (PRICER_ROLE). For the resolution (resolvePolicy), it has to be called by an authorized user
14
+ * @custom:security-contact security@ensuro.co
15
+ * @author Ensuro
16
+ */
17
+ contract SignedQuoteRiskModule is RiskModule {
18
+ bytes32 public constant POLICY_CREATOR_ROLE = keccak256("POLICY_CREATOR_ROLE");
19
+ bytes32 public constant PRICER_ROLE = keccak256("PRICER_ROLE");
20
+ bytes32 public constant RESOLVER_ROLE = keccak256("RESOLVER_ROLE");
21
+
22
+ /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
23
+ bool internal immutable _creationIsOpen;
24
+
25
+ event NewSignedPolicy(uint256 indexed policyId, bytes32 policyData);
26
+
27
+ /// @custom:oz-upgrades-unsafe-allow constructor
28
+ constructor(
29
+ IPolicyPool policyPool_,
30
+ IPremiumsAccount premiumsAccount_,
31
+ bool creationIsOpen_
32
+ ) RiskModule(policyPool_, premiumsAccount_) {
33
+ _creationIsOpen = creationIsOpen_;
34
+ }
35
+
36
+ /**
37
+ * @dev Initializes the RiskModule
38
+ * @param name_ Name of the Risk Module
39
+ * @param collRatio_ Collateralization ratio to compute solvency requirement as % of payout (in ray)
40
+ * @param ensuroPpFee_ % of pure premium that will go for Ensuro treasury (in ray)
41
+ * @param srRoc_ return on capital paid to Senior LPs (annualized percentage - in ray)
42
+ * @param maxPayoutPerPolicy_ Maximum payout per policy (in wad)
43
+ * @param exposureLimit_ Max exposure (sum of payouts) to be allocated to this module (in wad)
44
+ * @param wallet_ Address of the RiskModule provider
45
+ */
46
+ function initialize(
47
+ string memory name_,
48
+ uint256 collRatio_,
49
+ uint256 ensuroPpFee_,
50
+ uint256 srRoc_,
51
+ uint256 maxPayoutPerPolicy_,
52
+ uint256 exposureLimit_,
53
+ address wallet_
54
+ ) public initializer {
55
+ __RiskModule_init(
56
+ name_,
57
+ collRatio_,
58
+ ensuroPpFee_,
59
+ srRoc_,
60
+ maxPayoutPerPolicy_,
61
+ exposureLimit_,
62
+ wallet_
63
+ );
64
+ }
65
+
66
+ function _newPolicySigned(
67
+ uint256 payout,
68
+ uint256 premium,
69
+ uint256 lossProb,
70
+ uint40 expiration,
71
+ bytes32 policyData,
72
+ bytes32 quoteSignatureR,
73
+ bytes32 quoteSignatureVS,
74
+ uint40 quoteValidUntil,
75
+ address payer,
76
+ address onBehalfOf
77
+ ) internal returns (uint256 policyId) {
78
+ if (!_creationIsOpen)
79
+ _policyPool.access().checkComponentRole(
80
+ address(this),
81
+ POLICY_CREATOR_ROLE,
82
+ _msgSender(),
83
+ false
84
+ );
85
+ require(quoteValidUntil >= block.timestamp, "Quote expired");
86
+
87
+ /**
88
+ * Checks the quote has been signed by an authorized user
89
+ * The "quote" is computed as hash of the following fields (encodePacked):
90
+ * - address(this): the address of this RiskModule
91
+ * - payout, premium, lossProb, expiration: the base parameters of the policy
92
+ * - policyData: a hash of the private details of the policy. The calculation should include some
93
+ * unique id (quoteId), so each policyData identifies a policy.
94
+ * - quoteValidUntil: the maximum validity of the quote
95
+ */
96
+ bytes32 quoteHash = ECDSA.toEthSignedMessageHash(
97
+ abi.encodePacked(
98
+ address(this),
99
+ payout,
100
+ premium,
101
+ lossProb,
102
+ expiration,
103
+ policyData,
104
+ quoteValidUntil
105
+ )
106
+ );
107
+ address signer = ECDSA.recover(quoteHash, quoteSignatureR, quoteSignatureVS);
108
+ _policyPool.access().checkComponentRole(address(this), PRICER_ROLE, signer, false);
109
+ uint96 internalId = uint96(uint256(policyData) % 2**96);
110
+
111
+ policyId = _newPolicy(payout, premium, lossProb, expiration, payer, onBehalfOf, internalId).id;
112
+ emit NewSignedPolicy(policyId, policyData);
113
+ return policyId;
114
+ }
115
+
116
+ /**
117
+  * @dev Creates a new Policy using a signed quote. The caller is the payer of the policy.
118
+  *
119
+  * Requirements:
120
+  * - The caller approved the spending of the premium to the PolicyPool
121
+  * - The quote has been signed by an address with the component role PRICER_ROLE
122
+  *
123
+  * Emits:
124
+  * - {PolicyPool.NewPolicy}
125
+  * - {NewSignedPolicy}
126
+  *
127
+  * @param payout The exposure (maximum payout) of the policy
128
+  * @param premium The premium that will be paid by the payer
129
+  * @param lossProb The probability of having to pay the maximum payout (wad)
130
+  * @param expiration The expiration of the policy (timestamp)
131
+  * @param onBehalfOf The policy holder
132
+  * @param policyData A hash of the private details of the policy. The last 96 bits will be used as internalId
133
+  * @param quoteSignatureR The signature of the "quote". R component (EIP-2098 signature)
134
+  * @param quoteSignatureVS The signature of the "quote". VS component (EIP-2098 signature)
135
+  * @param quoteValidUntil The expiration of the quote
136
+  * @return Returns the id of the created policy
137
+  */
138
+ function newPolicy(
139
+ uint256 payout,
140
+ uint256 premium,
141
+ uint256 lossProb,
142
+ uint40 expiration,
143
+ address onBehalfOf,
144
+ bytes32 policyData,
145
+ bytes32 quoteSignatureR,
146
+ bytes32 quoteSignatureVS,
147
+ uint40 quoteValidUntil
148
+ ) external returns (uint256) {
149
+ return
150
+ _newPolicySigned(
151
+ payout,
152
+ premium,
153
+ lossProb,
154
+ expiration,
155
+ policyData,
156
+ quoteSignatureR,
157
+ quoteSignatureVS,
158
+ quoteValidUntil,
159
+ _msgSender(),
160
+ onBehalfOf
161
+ );
162
+ }
163
+
164
+ /**
165
+  * @dev Creates a new Policy using a signed quote. The payer is the policy holder
166
+  *
167
+  * Requirements:
168
+  * - currency().allowance(onBehalfOf, _msgSender()) > 0
169
+  * - The quote has been signed by an address with the component role PRICER_ROLE
170
+  *
171
+  * Emits:
172
+  * - {PolicyPool.NewPolicy}
173
+  * - {NewSignedPolicy}
174
+  *
175
+  * @param payout The exposure (maximum payout) of the policy
176
+  * @param premium The premium that will be paid by the payer
177
+  * @param lossProb The probability of having to pay the maximum payout (wad)
178
+  * @param expiration The expiration of the policy (timestamp)
179
+  * @param onBehalfOf The policy holder
180
+  * @param policyData A hash of the private details of the policy. The last 96 bits will be used as internalId
181
+  * @param quoteSignatureR The signature of the "quote". R component (EIP-2098 signature)
182
+  * @param quoteSignatureVS The signature of the "quote". VS component (EIP-2098 signature)
183
+  * @param quoteValidUntil The expiration of the quote
184
+  * @return Returns the id of the created policy
185
+  */
186
+ function newPolicyPaidByHolder(
187
+ uint256 payout,
188
+ uint256 premium,
189
+ uint256 lossProb,
190
+ uint40 expiration,
191
+ address onBehalfOf,
192
+ bytes32 policyData,
193
+ bytes32 quoteSignatureR,
194
+ bytes32 quoteSignatureVS,
195
+ uint40 quoteValidUntil
196
+ ) external returns (uint256) {
197
+ require(
198
+ onBehalfOf == _msgSender() || currency().allowance(onBehalfOf, _msgSender()) > 0,
199
+ "Sender is not authorized to create policies onBehalfOf"
200
+ );
201
+ /**
202
+ * The standard is the payer should be the _msgSender() but usually, in this type of module,
203
+ * the sender is an operative account managed by software, where the onBehalfOf is a more
204
+ * secure account (hardware wallet) that does the cash movements.
205
+ * This non standard behaviour allows for a more secure setup, where the sender never manages
206
+ * cash.
207
+ * We leverage the currency's allowance mechanism to allow the sender access to the payer's
208
+ * funds.
209
+ * Note that this allowance won't be spent, so anything above 0 is accepted.
210
+ */
211
+ return
212
+ _newPolicySigned(
213
+ payout,
214
+ premium,
215
+ lossProb,
216
+ expiration,
217
+ policyData,
218
+ quoteSignatureR,
219
+ quoteSignatureVS,
220
+ quoteValidUntil,
221
+ onBehalfOf,
222
+ onBehalfOf
223
+ );
224
+ }
225
+
226
+ function resolvePolicy(Policy.PolicyData calldata policy, uint256 payout)
227
+ external
228
+ onlyComponentRole(RESOLVER_ROLE)
229
+ whenNotPaused
230
+ {
231
+ _policyPool.resolvePolicy(policy, payout);
232
+ }
233
+
234
+ function resolvePolicyFullPayout(Policy.PolicyData calldata policy, bool customerWon)
235
+ external
236
+ onlyComponentRole(RESOLVER_ROLE)
237
+ whenNotPaused
238
+ {
239
+ _policyPool.resolvePolicyFullPayout(policy, customerWon);
240
+ }
241
+ }
@@ -0,0 +1,138 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ pragma solidity ^0.8.0;
4
+
5
+ import {WadRayMath} from "./dependencies/WadRayMath.sol";
6
+ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
7
+
8
+ /**
9
+ * @title TimeScaled
10
+ * @dev Library for packed amounts that increase continuoulsly with a scale factor
11
+ * @custom:security-contact security@ensuro.co
12
+ * @author Ensuro
13
+ */
14
+ library TimeScaled {
15
+ using WadRayMath for uint256;
16
+ using SafeCast for uint256;
17
+
18
+ uint256 private constant SECONDS_PER_YEAR = 365 days;
19
+ uint112 private constant MIN_SCALE = 1e17; // 0.0000000001 == 1e-10 in ray
20
+ uint112 private constant RAY112 = 1e27;
21
+
22
+ struct ScaledAmount {
23
+ uint112 scale;
24
+ uint112 amount;
25
+ uint32 lastUpdate;
26
+ }
27
+
28
+ function updateScale(ScaledAmount storage scaledAmount, uint256 interestRate) internal {
29
+ if (scaledAmount.lastUpdate >= uint32(block.timestamp)) return;
30
+ if (scaledAmount.amount == 0) {
31
+ scaledAmount.lastUpdate = uint32(block.timestamp);
32
+ } else {
33
+ scaledAmount.scale = getScale(scaledAmount, interestRate).toUint112();
34
+ scaledAmount.lastUpdate = uint32(block.timestamp);
35
+ }
36
+ }
37
+
38
+ function getScale(ScaledAmount storage scaledAmount, uint256 interestRate)
39
+ internal
40
+ view
41
+ returns (uint256)
42
+ {
43
+ uint32 now_ = uint32(block.timestamp);
44
+ if (scaledAmount.lastUpdate >= now_) {
45
+ return scaledAmount.scale;
46
+ }
47
+ uint256 timeDifference = uint256(now_ - scaledAmount.lastUpdate);
48
+ return
49
+ uint256(scaledAmount.scale).rayMul(
50
+ ((interestRate.wadToRay() * timeDifference) / SECONDS_PER_YEAR) + WadRayMath.RAY
51
+ );
52
+ }
53
+
54
+ function getScaledAmount(ScaledAmount storage scaledAmount, uint256 interestRate)
55
+ internal
56
+ view
57
+ returns (uint256)
58
+ {
59
+ return
60
+ uint256(scaledAmount.amount)
61
+ .wadToRay()
62
+ .rayMul(getScale(scaledAmount, interestRate))
63
+ .rayToWad();
64
+ }
65
+
66
+ function init(ScaledAmount storage scaledAmount) internal {
67
+ scaledAmount.scale = RAY112;
68
+ scaledAmount.amount = 0;
69
+ scaledAmount.lastUpdate = uint32(block.timestamp);
70
+ }
71
+
72
+ function scaleAmount(ScaledAmount storage scaledAmount, uint256 toScale)
73
+ internal
74
+ view
75
+ returns (uint256)
76
+ {
77
+ return toScale.wadToRay().rayDiv(uint256(scaledAmount.scale)).rayToWad();
78
+ }
79
+
80
+ function scaleAmountNow(
81
+ ScaledAmount storage scaledAmount,
82
+ uint256 interestRate,
83
+ uint256 toScale
84
+ ) internal view returns (uint256) {
85
+ return toScale.wadToRay().rayDiv(getScale(scaledAmount, interestRate)).rayToWad();
86
+ }
87
+
88
+ function add(
89
+ ScaledAmount storage scaledAmount,
90
+ uint256 amount,
91
+ uint256 interestRate
92
+ ) internal returns (uint256) {
93
+ updateScale(scaledAmount, interestRate);
94
+ uint256 scaledAdd = scaleAmount(scaledAmount, amount);
95
+ scaledAmount.amount += scaledAdd.toUint96();
96
+ return scaledAdd;
97
+ }
98
+
99
+ function sub(
100
+ ScaledAmount storage scaledAmount,
101
+ uint256 amount,
102
+ uint256 interestRate
103
+ ) internal returns (uint256) {
104
+ updateScale(scaledAmount, interestRate);
105
+ uint256 scaledSub = scaleAmount(scaledAmount, amount);
106
+ scaledAmount.amount -= scaledSub.toUint96();
107
+ if (scaledAmount.amount == 0) {
108
+ // Reset scale if amount == 0
109
+ scaledAmount.scale = RAY112;
110
+ }
111
+ return scaledSub;
112
+ }
113
+
114
+ function discreteChange(
115
+ ScaledAmount storage scaledAmount,
116
+ int256 amount,
117
+ uint256 interestRate
118
+ ) internal {
119
+ updateScale(scaledAmount, interestRate);
120
+ uint256 newScaledAmount = uint256(int256(getScaledAmount(scaledAmount, interestRate)) + amount);
121
+ scaledAmount.scale = newScaledAmount
122
+ .wadToRay()
123
+ .rayDiv(uint256(scaledAmount.amount).wadToRay())
124
+ .toUint112();
125
+ require(scaledAmount.scale >= MIN_SCALE, "Scale too small, can lead to rounding errors");
126
+ }
127
+
128
+ function maxNegativeAdjustment(ScaledAmount storage scaledAmount, uint256 interestRate)
129
+ internal
130
+ view
131
+ returns (uint256)
132
+ {
133
+ uint256 ts = getScaledAmount(scaledAmount, interestRate);
134
+ uint256 minTs = uint256(scaledAmount.amount).wadToRay().rayMul(MIN_SCALE).rayToWad();
135
+ if (ts > minTs) return ts - minTs;
136
+ else return 0;
137
+ }
138
+ }
@@ -1,7 +1,8 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  pragma solidity ^0.8.0;
3
3
 
4
- import {IPolicyPool} from "../interfaces/IPolicyPool.sol";
4
+ import {IPolicyPool} from "./interfaces/IPolicyPool.sol";
5
+ import {IPremiumsAccount} from "./interfaces/IPremiumsAccount.sol";
5
6
  import {RiskModule} from "./RiskModule.sol";
6
7
  import {Policy} from "./Policy.sol";
7
8
 
@@ -19,61 +20,122 @@ contract TrustfulRiskModule is RiskModule {
19
20
 
20
21
  /// @custom:oz-upgrades-unsafe-allow constructor
21
22
  // solhint-disable-next-line no-empty-blocks
22
- constructor(IPolicyPool policyPool_) RiskModule(policyPool_) {}
23
+ constructor(IPolicyPool policyPool_, IPremiumsAccount premiumsAccount_)
24
+ RiskModule(policyPool_, premiumsAccount_)
25
+ {} // solhint-disable-line no-empty-blocks
23
26
 
24
27
  /**
25
28
  * @dev Initializes the RiskModule
26
29
  * @param name_ Name of the Risk Module
27
- * @param scrPercentage_ Solvency Capital Requirement percentage, to calculate
28
- capital requirement as % of (payout - premium) (in ray)
29
- * @param ensuroFee_ % of premium that will go for Ensuro treasury (in ray)
30
- * @param scrInterestRate_ cost of capital (in ray)
31
- * @param maxScrPerPolicy_ Max SCR to be allocated to this module (in wad)
32
- * @param scrLimit_ Max SCR to be allocated to this module (in wad)
30
+ * @param collRatio_ Collateralization ratio to compute solvency requirement as % of payout (in ray)
31
+ * @param ensuroPpFee_ % of pure premium that will go for Ensuro treasury (in ray)
32
+ * @param srRoc_ return on capital paid to Senior LPs (annualized percentage - in ray)
33
+ * @param maxPayoutPerPolicy_ Maximum payout per policy (in wad)
34
+ * @param exposureLimit_ Max exposure (sum of payouts) to be allocated to this module (in wad)
33
35
  * @param wallet_ Address of the RiskModule provider
34
36
  */
35
37
  function initialize(
36
38
  string memory name_,
37
- uint256 scrPercentage_,
38
- uint256 ensuroFee_,
39
- uint256 scrInterestRate_,
40
- uint256 maxScrPerPolicy_,
41
- uint256 scrLimit_,
39
+ uint256 collRatio_,
40
+ uint256 ensuroPpFee_,
41
+ uint256 srRoc_,
42
+ uint256 maxPayoutPerPolicy_,
43
+ uint256 exposureLimit_,
42
44
  address wallet_
43
45
  ) public initializer {
44
46
  __RiskModule_init(
45
47
  name_,
46
- scrPercentage_,
47
- ensuroFee_,
48
- scrInterestRate_,
49
- maxScrPerPolicy_,
50
- scrLimit_,
48
+ collRatio_,
49
+ ensuroPpFee_,
50
+ srRoc_,
51
+ maxPayoutPerPolicy_,
52
+ exposureLimit_,
51
53
  wallet_
52
54
  );
53
55
  }
54
56
 
57
+ /**
58
+ * @dev Creates a new Policy
59
+ *
60
+ * Requirements:
61
+ * - The caller has been granted componentRole(PRICER_ROLE)
62
+ *
63
+ * Emits:
64
+ * - {PolicyPool.NewPolicy}
65
+ *
66
+ * @param payout The exposure (maximum payout) of the policy
67
+ * @param premium The premium that will be paid by the policyHolder
68
+ * @param lossProb The probability of having to pay the maximum payout (wad)
69
+ * @param expiration The expiration of the policy (timestamp)
70
+ * @param onBehalfOf The policy holder
71
+ * @param internalId An id that's unique within this module and it will be used to identify the policy
72
+ * @return Returns the id of the created policy
73
+ */
55
74
  function newPolicy(
56
75
  uint256 payout,
57
76
  uint256 premium,
58
77
  uint256 lossProb,
59
78
  uint40 expiration,
60
- address customer,
79
+ address onBehalfOf,
61
80
  uint96 internalId
62
- ) external onlyRole(PRICER_ROLE) returns (uint256) {
63
- return _newPolicy(payout, premium, lossProb, expiration, customer, internalId).id;
81
+ ) external onlyComponentRole(PRICER_ROLE) returns (uint256) {
82
+ address payer = onBehalfOf;
83
+ if (payer != _msgSender() && _policyPool.currency().allowance(payer, _msgSender()) < premium)
84
+ /**
85
+ * The standard is the payer should be the _msgSender() but usually, in this type of module,
86
+ * the sender is an operative account managed by software, where the onBehalfOf is a more
87
+ * secure account (hardware wallet) that does the cash movements.
88
+ * This non standard behaviour allows for a more secure setup, where the sender never manages
89
+ * cash.
90
+ * We leverage the currency's allowance mechanism to allow the sender access to the payer's
91
+ * funds.
92
+ * Note that this allowance won't be spent, so it can be set as the maximum amount of a single
93
+ * premium even for multiple policies.
94
+ */
95
+ payer = _msgSender();
96
+
97
+ return _newPolicy(payout, premium, lossProb, expiration, payer, onBehalfOf, internalId).id;
64
98
  }
65
99
 
100
+ /**
101
+ * @dev Resolves a policy, if payout > 0, it pays to the policy holder.
102
+ *
103
+ * Requirements:
104
+ * - The caller has been granted componentRole(RESOLVER_ROLE)
105
+ * - payout <= policy.payout
106
+ * - block.timestamp >= policy.expiration
107
+ *
108
+ * Emits:
109
+ * - {PolicyPool.PolicyResolved}
110
+ *
111
+ * @param policy The policy previously created (from {NewPolicy} event)
112
+ * @param payout The payout to transfer to the policy holder
113
+ */
66
114
  function resolvePolicy(Policy.PolicyData calldata policy, uint256 payout)
67
115
  external
68
- onlyRole(RESOLVER_ROLE)
116
+ onlyComponentRole(RESOLVER_ROLE)
69
117
  whenNotPaused
70
118
  {
71
119
  _policyPool.resolvePolicy(policy, payout);
72
120
  }
73
121
 
122
+ /**
123
+ * @dev Resolves a policy with full payout (policy.payout) if customerWon == true
124
+ *
125
+ * Requirements:
126
+ * - The caller has been granted componentRole(RESOLVER_ROLE)
127
+ * - block.timestamp >= policy.expiration
128
+ *
129
+ * Emits:
130
+ * - {PolicyPool.PolicyResolved}
131
+ *
132
+ * @param policy The policy previously created (from {NewPolicy} event)
133
+ * @param customerWon If true, policy.payout is transferred to the policy holder. If false, the policy is resolved
134
+ * without payout and can't be longer claimed.
135
+ */
74
136
  function resolvePolicyFullPayout(Policy.PolicyData calldata policy, bool customerWon)
75
137
  external
76
- onlyRole(RESOLVER_ROLE)
138
+ onlyComponentRole(RESOLVER_ROLE)
77
139
  whenNotPaused
78
140
  {
79
141
  _policyPool.resolvePolicyFullPayout(policy, customerWon);
@@ -0,0 +1,19 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ /*
5
+ This is a workaround for:
6
+
7
+ https://github.com/eth-brownie/brownie/issues/1581
8
+
9
+ Which affects ethproto. The contract is just here for ethproto's
10
+ sake and is not actually used by any contract.
11
+
12
+ Once that bug is fixed (either in brownie or in ethproto) this
13
+ file can be safely removed.
14
+
15
+ */
16
+
17
+ import {ERC1967Proxy as ERC1967ProxyOriginal} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
18
+
19
+ abstract contract ERC1967Proxy is ERC1967ProxyOriginal {}
@@ -0,0 +1,19 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ /*
5
+ This is a workaround for:
6
+
7
+ https://github.com/eth-brownie/brownie/issues/1581
8
+
9
+ Which affects ethproto. The contract is just here for ethproto's
10
+ sake and is not actually used by any contract.
11
+
12
+ Once that bug is fixed (either in brownie or in ethproto) this
13
+ file can be safely removed.
14
+
15
+ */
16
+
17
+ import {IERC20Metadata as IERC20MetadataOriginal} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
18
+
19
+ interface IERC20Metadata is IERC20MetadataOriginal {}
@@ -0,0 +1,126 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity ^0.8.0;
3
+
4
+ /**
5
+ * @title WadRayMath library
6
+ * @author Aave
7
+ * @notice Provides functions to perform calculations with Wad and Ray units
8
+ * @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
9
+ * with 27 digits of precision)
10
+ * @dev Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
11
+ **/
12
+ library WadRayMath {
13
+ // HALF_WAD and HALF_RAY expressed with extended notation as constant with operations are not supported in Yul assembly
14
+ uint256 internal constant WAD = 1e18;
15
+ uint256 internal constant HALF_WAD = 0.5e18;
16
+
17
+ uint256 internal constant RAY = 1e27;
18
+ uint256 internal constant HALF_RAY = 0.5e27;
19
+
20
+ uint256 internal constant WAD_RAY_RATIO = 1e9;
21
+
22
+ /**
23
+ * @dev Multiplies two wad, rounding half up to the nearest wad
24
+ * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
25
+ * @param a Wad
26
+ * @param b Wad
27
+ * @return c = a*b, in wad
28
+ **/
29
+ function wadMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
30
+ // to avoid overflow, a <= (type(uint256).max - HALF_WAD) / b
31
+ assembly {
32
+ if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_WAD), b))))) {
33
+ revert(0, 0)
34
+ }
35
+
36
+ c := div(add(mul(a, b), HALF_WAD), WAD)
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @dev Divides two wad, rounding half up to the nearest wad
42
+ * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
43
+ * @param a Wad
44
+ * @param b Wad
45
+ * @return c = a/b, in wad
46
+ **/
47
+ function wadDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
48
+ // to avoid overflow, a <= (type(uint256).max - halfB) / WAD
49
+ assembly {
50
+ if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), WAD))))) {
51
+ revert(0, 0)
52
+ }
53
+
54
+ c := div(add(mul(a, WAD), div(b, 2)), b)
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @notice Multiplies two ray, rounding half up to the nearest ray
60
+ * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
61
+ * @param a Ray
62
+ * @param b Ray
63
+ * @return c = a raymul b
64
+ **/
65
+ function rayMul(uint256 a, uint256 b) internal pure returns (uint256 c) {
66
+ // to avoid overflow, a <= (type(uint256).max - HALF_RAY) / b
67
+ assembly {
68
+ if iszero(or(iszero(b), iszero(gt(a, div(sub(not(0), HALF_RAY), b))))) {
69
+ revert(0, 0)
70
+ }
71
+
72
+ c := div(add(mul(a, b), HALF_RAY), RAY)
73
+ }
74
+ }
75
+
76
+ /**
77
+ * @notice Divides two ray, rounding half up to the nearest ray
78
+ * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
79
+ * @param a Ray
80
+ * @param b Ray
81
+ * @return c = a raydiv b
82
+ **/
83
+ function rayDiv(uint256 a, uint256 b) internal pure returns (uint256 c) {
84
+ // to avoid overflow, a <= (type(uint256).max - halfB) / RAY
85
+ assembly {
86
+ if or(iszero(b), iszero(iszero(gt(a, div(sub(not(0), div(b, 2)), RAY))))) {
87
+ revert(0, 0)
88
+ }
89
+
90
+ c := div(add(mul(a, RAY), div(b, 2)), b)
91
+ }
92
+ }
93
+
94
+ /**
95
+ * @dev Casts ray down to wad
96
+ * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
97
+ * @param a Ray
98
+ * @return b = a converted to wad, rounded half up to the nearest wad
99
+ **/
100
+ function rayToWad(uint256 a) internal pure returns (uint256 b) {
101
+ assembly {
102
+ b := div(a, WAD_RAY_RATIO)
103
+ let remainder := mod(a, WAD_RAY_RATIO)
104
+ if iszero(lt(remainder, div(WAD_RAY_RATIO, 2))) {
105
+ b := add(b, 1)
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @dev Converts wad up to ray
112
+ * @dev assembly optimized for improved gas savings, see https://twitter.com/transmissions11/status/1451131036377571328
113
+ * @param a Wad
114
+ * @return b = a converted in ray
115
+ **/
116
+ function wadToRay(uint256 a) internal pure returns (uint256 b) {
117
+ // to avoid overflow, b/WAD_RAY_RATIO == a
118
+ assembly {
119
+ b := mul(a, WAD_RAY_RATIO)
120
+
121
+ if iszero(eq(div(b, WAD_RAY_RATIO), a)) {
122
+ revert(0, 0)
123
+ }
124
+ }
125
+ }
126
+ }