@usdu-core/usdu-core 0.0.1

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 (107) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +6 -0
  3. package/.prettierrc.json +7 -0
  4. package/LICENSE +674 -0
  5. package/README.md +244 -0
  6. package/contracts/curve/CurveAdapterV1.sol +310 -0
  7. package/contracts/curve/CurveAdapterV1_1.sol +198 -0
  8. package/contracts/curve/helpers/ICurveStableSwapNG.json +566 -0
  9. package/contracts/curve/helpers/ICurveStableSwapNG.sol +177 -0
  10. package/contracts/deploy/VaultDeployer.sol +110 -0
  11. package/contracts/morpho/MorphoAdapterV1.sol +255 -0
  12. package/contracts/morpho/MorphoAdapterV1_1.sol +137 -0
  13. package/contracts/morpho/MorphoAdapterV1_2.sol +126 -0
  14. package/contracts/morpho/helpers/AggregatorV3Interface.sol +24 -0
  15. package/contracts/morpho/helpers/ChainlinkDataFeedLib.sol +36 -0
  16. package/contracts/morpho/helpers/ConstantsLib.sol +20 -0
  17. package/contracts/morpho/helpers/ErrorsLib.sol +17 -0
  18. package/contracts/morpho/helpers/IERC4626.sol +6 -0
  19. package/contracts/morpho/helpers/IMetaMorphoV1_1.sol +229 -0
  20. package/contracts/morpho/helpers/IMetaMorphoV1_1Factory.sol +32 -0
  21. package/contracts/morpho/helpers/IMorpho.sol +361 -0
  22. package/contracts/morpho/helpers/IMorphoCallbacks.sol +52 -0
  23. package/contracts/morpho/helpers/IMorphoChainlinkOracleV2.sol +39 -0
  24. package/contracts/morpho/helpers/IMorphoChainlinkOracleV2Factory.sol +56 -0
  25. package/contracts/morpho/helpers/IOracle.sol +15 -0
  26. package/contracts/morpho/helpers/MarketParamsLib.sol +21 -0
  27. package/contracts/morpho/helpers/MathLib.sol +45 -0
  28. package/contracts/morpho/helpers/Morpho.sol.bak +517 -0
  29. package/contracts/morpho/helpers/MorphoChainlinkOracleV2.sol +157 -0
  30. package/contracts/morpho/helpers/PendingLib.sol +47 -0
  31. package/contracts/morpho/helpers/SharesMathLib.sol +45 -0
  32. package/contracts/morpho/helpers/VaultLib.sol +18 -0
  33. package/contracts/reward/RewardDistributionV1.sol +110 -0
  34. package/contracts/reward/RewardRouterV0.sol +63 -0
  35. package/contracts/reward/Rewards.example.json +72 -0
  36. package/contracts/stablecoin/IStablecoin.sol +211 -0
  37. package/contracts/stablecoin/IStablecoinMetadata.sol +27 -0
  38. package/contracts/stablecoin/IStablecoinModifier.sol +52 -0
  39. package/contracts/stablecoin/Stablecoin.sol +376 -0
  40. package/contracts/stablecoin/libraries/ConstantsLib.sol +13 -0
  41. package/contracts/stablecoin/libraries/ErrorsLib.sol +45 -0
  42. package/contracts/stablecoin/libraries/EventsLib.sol +74 -0
  43. package/contracts/stablecoin/libraries/PendingLib.sol +38 -0
  44. package/contracts/vault/VaultAdapterRecoverV1.sol +29 -0
  45. package/contracts/vault/VaultAdapterV1.sol +126 -0
  46. package/dist/index.d.mts +16154 -0
  47. package/dist/index.d.ts +16154 -0
  48. package/dist/index.js +21134 -0
  49. package/dist/index.mjs +21061 -0
  50. package/docs/CoreVault: Integration of new Markets.md +197 -0
  51. package/docs/CoreVault: SupplyQueue.md +11 -0
  52. package/docs/Markets USDC Vault.md +35 -0
  53. package/docs/Markets USDU Vault.md +89 -0
  54. package/docs/Markets WETH Vault.md +35 -0
  55. package/docs/Overview.drawio +117 -0
  56. package/exports/abis/curve/CurveAdapterV1.ts +599 -0
  57. package/exports/abis/curve/CurveAdapterV1_1.ts +609 -0
  58. package/exports/abis/curve/CurveAdapterV1_2.ts +721 -0
  59. package/exports/abis/curve/helper/ICurveStableSwapNG.ts +1589 -0
  60. package/exports/abis/morpho/MorphoAdapterV1.ts +516 -0
  61. package/exports/abis/morpho/MorphoAdapterV1_1.ts +489 -0
  62. package/exports/abis/morpho/MorphoAdapterV1_2.ts +459 -0
  63. package/exports/abis/morpho/helper/AggregatorV3Interface.ts +113 -0
  64. package/exports/abis/morpho/helper/IMetaMorphoV1_1.ts +1483 -0
  65. package/exports/abis/morpho/helper/IMetaMorphoV1_1Base.ts +607 -0
  66. package/exports/abis/morpho/helper/IMetaMorphoV1_1StaticTyping.ts +696 -0
  67. package/exports/abis/morpho/helper/IMorpho.ts +1024 -0
  68. package/exports/abis/morpho/helper/IMorphoBase.ts +886 -0
  69. package/exports/abis/morpho/helper/IMorphoChainlinkOracleV2.ts +132 -0
  70. package/exports/abis/morpho/helper/IMorphoChainlinkOracleV2Factory.ts +109 -0
  71. package/exports/abis/morpho/helper/IMorphoFlashLoanCallback.ts +20 -0
  72. package/exports/abis/morpho/helper/IMorphoLiquidateCallback.ts +20 -0
  73. package/exports/abis/morpho/helper/IMorphoRepayCallback.ts +20 -0
  74. package/exports/abis/morpho/helper/IMorphoStaticTyping.ts +1003 -0
  75. package/exports/abis/morpho/helper/IMorphoSupplyCallback.ts +20 -0
  76. package/exports/abis/morpho/helper/IMorphoSupplyCollateralCallback.ts +20 -0
  77. package/exports/abis/morpho/helper/IMulticall.ts +21 -0
  78. package/exports/abis/morpho/helper/IOracle.ts +15 -0
  79. package/exports/abis/morpho/helper/IOwnable.ts +55 -0
  80. package/exports/abis/morpho/helper/MorphoChainlinkOracleV2.ts +188 -0
  81. package/exports/abis/openzeppelin/ERC20.ts +310 -0
  82. package/exports/abis/openzeppelin/ERC20Permit.ts +520 -0
  83. package/exports/abis/openzeppelin/IERC20.ts +185 -0
  84. package/exports/abis/openzeppelin/IERC20Metadata.ts +224 -0
  85. package/exports/abis/openzeppelin/IERC20Permit.ts +77 -0
  86. package/exports/abis/openzeppelin/IERC4626.ts +614 -0
  87. package/exports/abis/reward/RewardDistributionV1.ts +246 -0
  88. package/exports/abis/stablecoin/ErrorsLib.ts +114 -0
  89. package/exports/abis/stablecoin/EventsLib.ts +372 -0
  90. package/exports/abis/stablecoin/IStablecoin.ts +642 -0
  91. package/exports/abis/stablecoin/IStablecoinMetadata.ts +856 -0
  92. package/exports/abis/stablecoin/IStablecoinModifier.ts +15 -0
  93. package/exports/abis/stablecoin/Stablecoin.ts +1922 -0
  94. package/exports/abis/termmax/ITermMaxVault.ts +2335 -0
  95. package/exports/abis/vault/VaultAdapterRecoverV1.ts +490 -0
  96. package/exports/abis/vault/VaultAdapterV1.ts +459 -0
  97. package/exports/address.config.ts +113 -0
  98. package/exports/address.types.ts +130 -0
  99. package/exports/index.ts +61 -0
  100. package/hardhat.config.ts +231 -0
  101. package/helper/store.args.ts +17 -0
  102. package/helper/wallet.info.ts +3 -0
  103. package/helper/wallet.ts +41 -0
  104. package/install-macos.sh +46 -0
  105. package/package.json +73 -0
  106. package/tsconfig.json +15 -0
  107. package/tsup.config.ts +10 -0
@@ -0,0 +1,376 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';
5
+
6
+ import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
7
+ import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
8
+ import {ERC20Permit} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol';
9
+ import {ERC1363} from '@openzeppelin/contracts/token/ERC20/extensions/ERC1363.sol';
10
+
11
+ import {ConstantsLib} from './libraries/ConstantsLib.sol';
12
+ import {ErrorsLib} from './libraries/ErrorsLib.sol';
13
+ import {EventsLib} from './libraries/EventsLib.sol';
14
+ import {PendingLib, PendingUint192, PendingAddress} from './libraries/PendingLib.sol';
15
+
16
+ import {IStablecoin, IERC20} from './IStablecoin.sol';
17
+
18
+ /// @title Stablecoin
19
+ /// @author @samclassix <samclassix@proton.me>, @wrytlabs <wrytlabs@proton.me>
20
+ /// @notice A stablecoin implementation built on top of Morpho, utilizing a role-based access control system
21
+ /// with curator and guardian roles for secure management and governance.
22
+ contract Stablecoin is IStablecoin, ERC20, ERC20Permit, ERC1363 {
23
+ using Math for uint256;
24
+ using SafeERC20 for ERC20;
25
+ using PendingLib for PendingUint192;
26
+ using PendingLib for PendingAddress;
27
+
28
+ address public curator;
29
+ PendingAddress public pendingCurator;
30
+
31
+ address public guardian;
32
+ PendingAddress public pendingGuardian;
33
+
34
+ uint256 public timelock;
35
+ PendingUint192 public pendingTimelock;
36
+
37
+ mapping(address module => uint256) public modules;
38
+ mapping(address module => PendingUint192) public pendingModules;
39
+
40
+ mapping(address account => uint256) public unfreeze;
41
+ mapping(address account => PendingUint192) public pendingUnfreeze;
42
+
43
+ // ---------------------------------------------------------------------------------------
44
+
45
+ modifier onlyCurator() {
46
+ verifyCurator(_msgSender());
47
+ _;
48
+ }
49
+
50
+ modifier onlyGuardian() {
51
+ verifyGuardian(_msgSender());
52
+ _;
53
+ }
54
+
55
+ modifier onlyCuratorOrGuardian() {
56
+ verifyCuratorOrGuardian(_msgSender());
57
+ _;
58
+ }
59
+
60
+ modifier onlyModule() {
61
+ verifyModule(_msgSender());
62
+ _;
63
+ }
64
+
65
+ modifier validModule() {
66
+ verifyValidModule(_msgSender());
67
+ _;
68
+ }
69
+
70
+ modifier afterTimelock(uint256 validAt) {
71
+ if (validAt == 0) revert ErrorsLib.NoPendingValue();
72
+ if (block.timestamp < validAt) revert ErrorsLib.TimelockNotElapsed();
73
+ _;
74
+ }
75
+
76
+ modifier claimPublicFee(uint256 fee, uint256 min) {
77
+ if (fee < min) revert ErrorsLib.ProposalFeeToLow(min);
78
+ _transfer(_msgSender(), curator, fee);
79
+ _;
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------------------
83
+
84
+ constructor(string memory _name, string memory _symbol, address _curator) ERC20(_name, _symbol) ERC20Permit(_name) {
85
+ curator = _curator;
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------------------
89
+
90
+ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
91
+ return
92
+ interfaceId == type(IERC20).interfaceId ||
93
+ interfaceId == type(ERC20Permit).interfaceId ||
94
+ interfaceId == type(IStablecoin).interfaceId ||
95
+ super.supportsInterface(interfaceId);
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------------------
99
+ // check role functions with public visibility
100
+
101
+ /// @inheritdoc IStablecoin
102
+ function checkCurator(address account) public view returns (bool) {
103
+ return account == curator;
104
+ }
105
+
106
+ /// @inheritdoc IStablecoin
107
+ function checkGuardian(address account) public view returns (bool) {
108
+ return account == guardian;
109
+ }
110
+
111
+ /// @inheritdoc IStablecoin
112
+ function checkCuratorOrGuardian(address account) public view returns (bool) {
113
+ return (account == curator || account == guardian);
114
+ }
115
+
116
+ /// @inheritdoc IStablecoin
117
+ function checkModule(address account) public view returns (bool) {
118
+ return modules[account] != 0;
119
+ }
120
+
121
+ /// @inheritdoc IStablecoin
122
+ function checkValidModule(address account) public view returns (bool) {
123
+ return modules[account] > block.timestamp;
124
+ }
125
+
126
+ // ---------------------------------------------------------------------------------------
127
+ // verify role functions with public visibility
128
+
129
+ /// @inheritdoc IStablecoin
130
+ function verifyCurator(address account) public view {
131
+ if (checkCurator(account) == false) revert ErrorsLib.NotCuratorRole(account);
132
+ }
133
+
134
+ /// @inheritdoc IStablecoin
135
+ function verifyGuardian(address account) public view {
136
+ if (checkGuardian(account) == false) revert ErrorsLib.NotGuardianRole(account);
137
+ }
138
+
139
+ /// @inheritdoc IStablecoin
140
+ function verifyCuratorOrGuardian(address account) public view {
141
+ if (checkCuratorOrGuardian(account) == false) revert ErrorsLib.NotCuratorNorGuardianRole(account);
142
+ }
143
+
144
+ /// @inheritdoc IStablecoin
145
+ function verifyModule(address account) public view {
146
+ if (checkModule(account) == false) revert ErrorsLib.NotModuleRole(account);
147
+ }
148
+
149
+ /// @inheritdoc IStablecoin
150
+ function verifyValidModule(address account) public view {
151
+ if (checkValidModule(account) == false) revert ErrorsLib.NotValidModuleRole(account);
152
+ }
153
+
154
+ // ---------------------------------------------------------------------------------------
155
+ // allowance and update ERC20 modifications
156
+
157
+ /// @inheritdoc ERC20
158
+ function allowance(address owner, address spender) public view virtual override(ERC20, IERC20) returns (uint256) {
159
+ if (checkValidModule(_msgSender())) return type(uint256).max;
160
+ return super.allowance(owner, spender);
161
+ }
162
+
163
+ /// @inheritdoc ERC20
164
+ function _update(address from, address to, uint256 value) internal virtual override {
165
+ uint256 since = unfreeze[from];
166
+ if (since != 0 && since <= block.timestamp) revert ErrorsLib.AccountFreezed(from, since);
167
+ super._update(from, to, value);
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------------------
171
+ // allow minting modules to mint and burnFrom. allow anyone to burn tokens
172
+
173
+ /// @inheritdoc IStablecoin
174
+ function mintModule(address to, uint256 value) external validModule {
175
+ _mint(to, value);
176
+ }
177
+
178
+ /// @inheritdoc IStablecoin
179
+ function burnModule(address from, uint256 amount) external onlyModule {
180
+ _burn(from, amount);
181
+ }
182
+
183
+ /// @inheritdoc IStablecoin
184
+ function burn(uint256 amount) external {
185
+ _burn(_msgSender(), amount);
186
+ }
187
+
188
+ // ---------------------------------------------------------------------------------------
189
+ // curator management
190
+
191
+ /// @inheritdoc IStablecoin
192
+ function setCurator(address newCurator) external onlyCurator {
193
+ if (curator == newCurator) revert ErrorsLib.AlreadySet();
194
+ if (pendingCurator.validAt != 0) revert ErrorsLib.AlreadyPending();
195
+ pendingCurator.update(newCurator, timelock);
196
+ emit EventsLib.SubmitCurator(_msgSender(), newCurator, timelock);
197
+ }
198
+
199
+ /// @inheritdoc IStablecoin
200
+ function setCuratorPublic(address newCurator, uint256 fee) external claimPublicFee(fee, ConstantsLib.PUBLIC_FEE * 10) {
201
+ if (curator == newCurator) revert ErrorsLib.AlreadySet();
202
+ if (pendingCurator.validAt != 0) revert ErrorsLib.AlreadyPending();
203
+ pendingCurator.update(newCurator, timelock * 2);
204
+ emit EventsLib.SubmitCurator(_msgSender(), newCurator, timelock * 2);
205
+ }
206
+
207
+ /// @inheritdoc IStablecoin
208
+ function revokePendingCurator() external onlyCuratorOrGuardian {
209
+ if (pendingCurator.validAt == 0) revert ErrorsLib.NoPendingValue();
210
+ emit EventsLib.RevokePendingCurator(_msgSender(), pendingCurator.value);
211
+ delete pendingCurator;
212
+ }
213
+
214
+ /// @inheritdoc IStablecoin
215
+ function acceptCurator() external afterTimelock(pendingCurator.validAt) {
216
+ if (pendingCurator.value != _msgSender()) revert ErrorsLib.NotCuratorRole(_msgSender());
217
+ curator = pendingCurator.value;
218
+ emit EventsLib.SetCurator(_msgSender(), curator);
219
+ delete pendingCurator;
220
+ }
221
+
222
+ // ---------------------------------------------------------------------------------------
223
+ // guardian management
224
+
225
+ /// @inheritdoc IStablecoin
226
+ function setGuardian(address newGuardian) external onlyCurator {
227
+ if (guardian == newGuardian) revert ErrorsLib.AlreadySet();
228
+ if (pendingGuardian.validAt != 0) revert ErrorsLib.AlreadyPending();
229
+
230
+ if (guardian == address(0)) {
231
+ _setGuardian(newGuardian);
232
+ } else {
233
+ pendingGuardian.update(newGuardian, timelock);
234
+ emit EventsLib.SubmitGuardian(_msgSender(), newGuardian, timelock);
235
+ }
236
+ }
237
+
238
+ /// @inheritdoc IStablecoin
239
+ function revokePendingGuardian() external onlyCuratorOrGuardian {
240
+ if (pendingGuardian.validAt == 0) revert ErrorsLib.NoPendingValue();
241
+ emit EventsLib.RevokePendingGuardian(_msgSender(), pendingGuardian.value);
242
+ delete pendingGuardian;
243
+ }
244
+
245
+ /// @inheritdoc IStablecoin
246
+ function acceptGuardian() external afterTimelock(pendingGuardian.validAt) {
247
+ _setGuardian(pendingGuardian.value);
248
+ }
249
+
250
+ function _setGuardian(address newGuardian) internal {
251
+ guardian = newGuardian;
252
+ emit EventsLib.SetGuardian(_msgSender(), guardian);
253
+ delete pendingGuardian;
254
+ }
255
+
256
+ // ---------------------------------------------------------------------------------------
257
+ // timelock management
258
+
259
+ /// @inheritdoc IStablecoin
260
+ function setTimelock(uint256 newTimelock) external onlyCurator {
261
+ if (timelock == newTimelock) revert ErrorsLib.AlreadySet();
262
+ if (pendingTimelock.validAt != 0) revert ErrorsLib.AlreadyPending();
263
+ _checkTimelockBounds(newTimelock);
264
+
265
+ if (newTimelock > timelock) {
266
+ _setTimelock(newTimelock);
267
+ } else {
268
+ // Safe "unchecked" cast because newTimelock <= MAX_TIMELOCK.
269
+ pendingTimelock.update(uint184(newTimelock), timelock);
270
+ emit EventsLib.SubmitTimelock(_msgSender(), newTimelock, timelock);
271
+ }
272
+ }
273
+
274
+ /// @inheritdoc IStablecoin
275
+ function revokePendingTimelock() external onlyCuratorOrGuardian {
276
+ if (pendingTimelock.validAt == 0) revert ErrorsLib.NoPendingValue();
277
+ emit EventsLib.RevokePendingTimelock(_msgSender(), pendingTimelock.value);
278
+ delete pendingTimelock;
279
+ }
280
+
281
+ /// @inheritdoc IStablecoin
282
+ function acceptTimelock() external afterTimelock(pendingTimelock.validAt) {
283
+ _setTimelock(pendingTimelock.value);
284
+ }
285
+
286
+ /// @dev Reverts if `newTimelock` is not within the bounds.
287
+ function _checkTimelockBounds(uint256 newTimelock) internal pure {
288
+ if (newTimelock > ConstantsLib.MAX_TIMELOCK) revert ErrorsLib.AboveMaxTimelock();
289
+ if (newTimelock < ConstantsLib.MIN_TIMELOCK) revert ErrorsLib.BelowMinTimelock();
290
+ }
291
+
292
+ /// @dev Sets `timelock` to `newTimelock`.
293
+ function _setTimelock(uint256 newTimelock) internal {
294
+ timelock = newTimelock;
295
+ emit EventsLib.SetTimelock(_msgSender(), newTimelock);
296
+ delete pendingTimelock;
297
+ }
298
+
299
+ // ---------------------------------------------------------------------------------------
300
+ // module managment
301
+
302
+ /// @inheritdoc IStablecoin
303
+ function setModule(address module, uint256 expiredAt, string calldata message) external onlyCurator {
304
+ if (modules[module] == expiredAt) revert ErrorsLib.AlreadySet();
305
+ if (pendingModules[module].validAt != 0) revert ErrorsLib.AlreadyPending();
306
+
307
+ if (totalSupply() == 0) {
308
+ _setModule(module, expiredAt);
309
+ } else {
310
+ pendingModules[module].update(uint184(expiredAt), timelock);
311
+ emit EventsLib.SubmitModule(_msgSender(), module, expiredAt, message, timelock);
312
+ }
313
+ }
314
+
315
+ /// @inheritdoc IStablecoin
316
+ function setModulePublic(address module, uint256 expiredAt, string calldata message, uint256 fee) external claimPublicFee(fee, ConstantsLib.PUBLIC_FEE) {
317
+ if (modules[module] == expiredAt) revert ErrorsLib.AlreadySet();
318
+ if (pendingModules[module].validAt != 0) revert ErrorsLib.AlreadyPending();
319
+
320
+ pendingModules[module].update(uint184(expiredAt), timelock * 2);
321
+ emit EventsLib.SubmitModule(_msgSender(), module, expiredAt, message, timelock * 2);
322
+ }
323
+
324
+ /// @inheritdoc IStablecoin
325
+ function revokePendingModule(address module, string calldata message) external onlyCuratorOrGuardian {
326
+ if (pendingModules[module].validAt == 0) revert ErrorsLib.NoPendingValue();
327
+ emit EventsLib.RevokePendingModule(_msgSender(), module, message);
328
+ delete pendingModules[module];
329
+ }
330
+
331
+ /// @inheritdoc IStablecoin
332
+ function acceptModule(address module) external afterTimelock(pendingModules[module].validAt) {
333
+ _setModule(module, pendingModules[module].value);
334
+ }
335
+
336
+ function _setModule(address module, uint256 expiredAt) internal {
337
+ modules[module] = expiredAt;
338
+ emit EventsLib.SetModule(_msgSender(), module);
339
+ delete pendingModules[module];
340
+ }
341
+
342
+ // ---------------------------------------------------------------------------------------
343
+ // account freeze and unfreeze management
344
+
345
+ /// @inheritdoc IStablecoin
346
+ function setFreeze(address account, string calldata message) external onlyCurator {
347
+ if (unfreeze[account] != 0) revert ErrorsLib.AlreadySet();
348
+ unfreeze[account] = block.timestamp;
349
+ emit EventsLib.SetFreeze(_msgSender(), account, message);
350
+ }
351
+
352
+ // ---------------------------------------------------------------------------------------
353
+
354
+ /// @inheritdoc IStablecoin
355
+ function setUnfreeze(address account, string calldata message) external onlyCuratorOrGuardian {
356
+ if (unfreeze[account] == 0) revert ErrorsLib.AlreadySet();
357
+ if (pendingUnfreeze[account].validAt != 0) revert ErrorsLib.AlreadyPending();
358
+
359
+ pendingUnfreeze[account].update(uint184(0), timelock * 2);
360
+ emit EventsLib.SubmitUnfreeze(_msgSender(), account, message, timelock * 2);
361
+ }
362
+
363
+ /// @inheritdoc IStablecoin
364
+ function revokePendingUnfreeze(address account, string calldata message) external onlyCuratorOrGuardian {
365
+ if (pendingUnfreeze[account].validAt == 0) revert ErrorsLib.NoPendingValue();
366
+ emit EventsLib.RevokeUnfreeze(_msgSender(), account, message);
367
+ delete pendingUnfreeze[account];
368
+ }
369
+
370
+ /// @inheritdoc IStablecoin
371
+ function acceptUnfreeze(address account) external afterTimelock(pendingUnfreeze[account].validAt) {
372
+ unfreeze[account] = 0;
373
+ emit EventsLib.SetUnfreeze(_msgSender(), account);
374
+ delete pendingUnfreeze[account];
375
+ }
376
+ }
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ library ConstantsLib {
5
+ /// @dev The minimum delay of a timelock.
6
+ uint256 internal constant MIN_TIMELOCK = 7 days;
7
+
8
+ /// @dev The maximum delay of a timelock.
9
+ uint256 internal constant MAX_TIMELOCK = 4 weeks;
10
+
11
+ /// @dev The fee to pay for public proposals
12
+ uint256 internal constant PUBLIC_FEE = 10000 ether;
13
+ }
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ library ErrorsLib {
5
+ /// @notice Thrown when the caller doesn't have the curator role.
6
+ error NotCuratorRole(address account);
7
+
8
+ /// @notice Thrown when the caller doesn't have the guardian role.
9
+ error NotGuardianRole(address account);
10
+
11
+ /// @notice Thrown when the caller doesn't have the curator nor the guardian role.
12
+ error NotCuratorNorGuardianRole(address account);
13
+
14
+ error NotModuleRole(address account);
15
+
16
+ error NotValidModuleRole(address account);
17
+
18
+ // ---------------------------------------------------------------------------------------
19
+
20
+ /// @notice Thrown when there's no pending value to set.
21
+ error NoPendingValue();
22
+
23
+ /// @notice Thrown when the value is already set.
24
+ error AlreadySet();
25
+
26
+ /// @notice Thrown when a value is already pending.
27
+ error AlreadyPending();
28
+
29
+ // ---------------------------------------------------------------------------------------
30
+
31
+ /// @notice Thrown when the submitted timelock is above the max timelock.
32
+ error AboveMaxTimelock();
33
+
34
+ /// @notice Thrown when the submitted timelock is below the min timelock.
35
+ error BelowMinTimelock();
36
+
37
+ /// @notice Thrown when the timelock is not elapsed.
38
+ error TimelockNotElapsed();
39
+
40
+ // ---------------------------------------------------------------------------------------
41
+
42
+ error ProposalFeeToLow(uint256 minimum);
43
+
44
+ error AccountFreezed(address account, uint256 since);
45
+ }
@@ -0,0 +1,74 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ library EventsLib {
5
+ /// @notice Emitted when a pending `newCurator` is submitted.
6
+ event SubmitCurator(address indexed caller, address indexed newCurator, uint256 timelock);
7
+
8
+ /// @notice Emitted when a `pendingGuardian` is revoked.
9
+ event RevokePendingCurator(address indexed caller, address indexed pendingGuardian);
10
+
11
+ /// @notice Emitted when `guardian` is set to `newCurator`.
12
+ event SetCurator(address indexed caller, address indexed guardian);
13
+
14
+ // ---------------------------------------------------------------------------------------
15
+
16
+ /// @notice Emitted when a pending `newGuardian` is submitted.
17
+ event SubmitGuardian(address indexed caller, address indexed newGuardian, uint256 timelock);
18
+
19
+ /// @notice Emitted when a `pendingGuardian` is revoked.
20
+ event RevokePendingGuardian(address indexed caller, address indexed pendingGuardian);
21
+
22
+ /// @notice Emitted when `guardian` is set to `newGuardian`.
23
+ event SetGuardian(address indexed caller, address indexed guardian);
24
+
25
+ // ---------------------------------------------------------------------------------------
26
+
27
+ /// @notice Emitted when a pending `newTimelock` is submitted.
28
+ event SubmitTimelock(address indexed caller, uint256 newTimelock, uint256 timelock);
29
+
30
+ /// @notice Emitted when a `pendingTimelock` is revoked.
31
+ event RevokePendingTimelock(address indexed caller, uint256 pendingTimelock);
32
+
33
+ /// @notice Emitted when `timelock` is set to `newTimelock`.
34
+ event SetTimelock(address indexed caller, uint256 newTimelock);
35
+
36
+ // ---------------------------------------------------------------------------------------
37
+
38
+ /// @notice Emitted when a new `newModule` is submitted.
39
+ event SubmitModule(address indexed caller, address indexed newModule, uint256 expiredAt, string message, uint256 timelock);
40
+
41
+ /// @notice Emitted when a `pendingModule` is revoked.
42
+ event RevokePendingModule(address indexed caller, address indexed module, string message);
43
+
44
+ /// @notice Emitted when `Module` is set to `newModule`.
45
+ event SetModule(address indexed caller, address indexed newModule);
46
+
47
+ // ---------------------------------------------------------------------------------------
48
+
49
+ /// @notice Emitted when an account is frozen by the curator.
50
+ /// @param caller The address that triggered the freeze.
51
+ /// @param account The address being frozen.
52
+ /// @param message A message explaining the reason for freezing.
53
+ event SetFreeze(address indexed caller, address indexed account, string message);
54
+
55
+ // ---------------------------------------------------------------------------------------
56
+
57
+ /// @notice Emitted when an unfreeze request is submitted.
58
+ /// @param caller The address that submitted the unfreeze request.
59
+ /// @param account The address requested to be unfrozen.
60
+ /// @param message A message explaining the reason for unfreezing.
61
+ /// @param timelock The timestamp after which the unfreeze can be accepted.
62
+ event SubmitUnfreeze(address indexed caller, address indexed account, string message, uint256 timelock);
63
+
64
+ /// @notice Emitted when a pending unfreeze request is revoked.
65
+ /// @param caller The address that revoked the unfreeze request.
66
+ /// @param account The address whose unfreeze was revoked.
67
+ /// @param message A message explaining the reason for revocation.
68
+ event RevokeUnfreeze(address indexed caller, address indexed account, string message);
69
+
70
+ /// @notice Emitted when an unfreeze request is accepted and finalized.
71
+ /// @param caller The address that finalized the unfreeze.
72
+ /// @param account The address that was unfrozen.
73
+ event SetUnfreeze(address indexed caller, address indexed account);
74
+ }
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ struct PendingUint192 {
5
+ /// @notice The pending value to set.
6
+ uint192 value;
7
+ /// @notice The timestamp at which the pending value becomes valid.
8
+ uint64 validAt;
9
+ }
10
+
11
+ struct PendingAddress {
12
+ /// @notice The pending value to set.
13
+ address value;
14
+ /// @notice The timestamp at which the pending value becomes valid.
15
+ uint64 validAt;
16
+ }
17
+
18
+ /// @title PendingLib
19
+ /// @author Morpho Labs
20
+ /// @custom:contact security@morpho.org
21
+ /// @notice Library to manage pending values and their validity timestamp.
22
+ library PendingLib {
23
+ /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
24
+ /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
25
+ function update(PendingUint192 storage pending, uint184 newValue, uint256 timelock) internal {
26
+ pending.value = newValue;
27
+ // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
28
+ pending.validAt = uint64(block.timestamp + timelock);
29
+ }
30
+
31
+ /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
32
+ /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
33
+ function update(PendingAddress storage pending, address newValue, uint256 timelock) internal {
34
+ pending.value = newValue;
35
+ // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
36
+ pending.validAt = uint64(block.timestamp + timelock);
37
+ }
38
+ }
@@ -0,0 +1,29 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Stablecoin, IERC20, IERC4626, VaultAdapterV1, SafeERC20} from './VaultAdapterV1.sol';
5
+
6
+ /**
7
+ * @title VaultAdapterRecoverV1
8
+ * @author @samclassix <samclassix@proton.me>, @wrytlabs <wrytlabs@proton.me>
9
+ * @notice This contract serves as an adapter for interacting with vaults, facilitating the direct minting of liquidity into them.
10
+ * @notice It includes a recovery mechanism designed to handle edge cases for collateral as Real-World Assets (RWA), specifically for KYC-approved users during liquidation events.
11
+ */
12
+ contract VaultAdapterRecoverV1 is VaultAdapterV1 {
13
+ using SafeERC20 for IERC20;
14
+
15
+ constructor(
16
+ Stablecoin _stable,
17
+ IERC4626 _vault,
18
+ address[5] memory _receivers,
19
+ uint32[5] memory _weights
20
+ ) VaultAdapterV1(_stable, _vault, _receivers, _weights) {}
21
+
22
+ function recoverAll(address token) external onlyCurator {
23
+ IERC20(token).safeTransfer(stable.curator(), IERC20(token).balanceOf(address(this)));
24
+ }
25
+
26
+ function recover(address token, uint256 amount) external onlyCurator {
27
+ IERC20(token).safeTransfer(stable.curator(), amount);
28
+ }
29
+ }