@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,47 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.0;
3
+
4
+ struct MarketConfig {
5
+ /// @notice The maximum amount of assets that can be allocated to the market.
6
+ uint184 cap;
7
+ /// @notice Whether the market is in the withdraw queue.
8
+ bool enabled;
9
+ /// @notice The timestamp at which the market can be instantly removed from the withdraw queue.
10
+ uint64 removableAt;
11
+ }
12
+
13
+ struct PendingUint192 {
14
+ /// @notice The pending value to set.
15
+ uint192 value;
16
+ /// @notice The timestamp at which the pending value becomes valid.
17
+ uint64 validAt;
18
+ }
19
+
20
+ struct PendingAddress {
21
+ /// @notice The pending value to set.
22
+ address value;
23
+ /// @notice The timestamp at which the pending value becomes valid.
24
+ uint64 validAt;
25
+ }
26
+
27
+ /// @title PendingLib
28
+ /// @author Morpho Labs
29
+ /// @custom:contact security@morpho.org
30
+ /// @notice Library to manage pending values and their validity timestamp.
31
+ library PendingLib {
32
+ /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
33
+ /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
34
+ function update(PendingUint192 storage pending, uint184 newValue, uint256 timelock) internal {
35
+ pending.value = newValue;
36
+ // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
37
+ pending.validAt = uint64(block.timestamp + timelock);
38
+ }
39
+
40
+ /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
41
+ /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
42
+ function update(PendingAddress storage pending, address newValue, uint256 timelock) internal {
43
+ pending.value = newValue;
44
+ // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
45
+ pending.validAt = uint64(block.timestamp + timelock);
46
+ }
47
+ }
@@ -0,0 +1,45 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {MathLib} from './MathLib.sol';
5
+
6
+ /// @title SharesMathLib
7
+ /// @author Morpho Labs
8
+ /// @custom:contact security@morpho.org
9
+ /// @notice Shares management library.
10
+ /// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
11
+ /// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
12
+ library SharesMathLib {
13
+ using MathLib for uint256;
14
+
15
+ /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
16
+ /// high precision computations.
17
+ /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
18
+ /// stays low enough not to inflate these assets to a significant value.
19
+ /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
20
+ uint256 internal constant VIRTUAL_SHARES = 1e6;
21
+
22
+ /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
23
+ /// empty.
24
+ uint256 internal constant VIRTUAL_ASSETS = 1;
25
+
26
+ /// @dev Calculates the value of `assets` quoted in shares, rounding down.
27
+ function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
28
+ return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
29
+ }
30
+
31
+ /// @dev Calculates the value of `shares` quoted in assets, rounding down.
32
+ function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
33
+ return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
34
+ }
35
+
36
+ /// @dev Calculates the value of `assets` quoted in shares, rounding up.
37
+ function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
38
+ return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
39
+ }
40
+
41
+ /// @dev Calculates the value of `shares` quoted in assets, rounding up.
42
+ function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
43
+ return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
44
+ }
45
+ }
@@ -0,0 +1,18 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {IERC4626} from './IERC4626.sol';
5
+
6
+ /// @title VaultLib
7
+ /// @author Morpho Labs
8
+ /// @custom:contact security@morpho.org
9
+ /// @notice Library exposing functions to price shares of an ERC4626 vault.
10
+ library VaultLib {
11
+ /// @dev Converts `shares` into the corresponding assets on the `vault`.
12
+ /// @dev When `vault` is the address zero, returns 1.
13
+ function getAssets(IERC4626 vault, uint256 shares) internal view returns (uint256) {
14
+ if (address(vault) == address(0)) return 1;
15
+
16
+ return vault.convertToAssets(shares);
17
+ }
18
+ }
@@ -0,0 +1,110 @@
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 {IStablecoinModifier, Stablecoin} from '../stablecoin/IStablecoinModifier.sol';
7
+ import {ErrorsLib} from '../stablecoin/libraries/ErrorsLib.sol';
8
+
9
+ abstract contract RewardDistributionV1 is IStablecoinModifier {
10
+ using Math for uint256;
11
+
12
+ address[5] public receivers;
13
+ uint32[5] public weights;
14
+ uint256 public totalWeights;
15
+
16
+ address[5] public pendingReceivers;
17
+ uint32[5] public pendingWeights;
18
+ uint256 public pendingValidAt;
19
+
20
+ // ---------------------------------------------------------------------------------------
21
+
22
+ event SubmitDistribution(address indexed caller, address[5] receivers, uint32[5] weights, uint256 timelock);
23
+ event RevokeDistribution(address indexed caller);
24
+ event SetDistribution(address indexed caller);
25
+ event Distribution(address indexed receiver, uint256 amount, uint256 ratio);
26
+
27
+ // ---------------------------------------------------------------------------------------
28
+
29
+ constructor(Stablecoin _stable, address[5] memory _receivers, uint32[5] memory _weights) IStablecoinModifier(_stable) {
30
+ _setDistribution(_receivers, _weights);
31
+ }
32
+
33
+ // ---------------------------------------------------------------------------------------
34
+
35
+ function setDistribution(address[5] calldata _receivers, uint32[5] calldata _weights) external onlyCurator {
36
+ if (pendingValidAt != 0) revert ErrorsLib.AlreadyPending();
37
+
38
+ if (receivers[0] == address(0)) {
39
+ _setDistribution(_receivers, _weights);
40
+ } else {
41
+ pendingReceivers = _receivers;
42
+ pendingWeights = _weights;
43
+ pendingValidAt = block.timestamp + stable.timelock();
44
+ emit SubmitDistribution(_msgSender(), _receivers, _weights, pendingValidAt);
45
+ }
46
+ }
47
+
48
+ function revokePendingDistribution() external onlyCuratorOrGuardian {
49
+ if (pendingValidAt == 0) revert ErrorsLib.NoPendingValue();
50
+ emit RevokeDistribution(_msgSender());
51
+ _cleanUpPending();
52
+ }
53
+
54
+ function applyDistribution() external afterTimelock(pendingValidAt) {
55
+ _setDistribution(pendingReceivers, pendingWeights);
56
+ }
57
+
58
+ function _setDistribution(address[5] memory _receivers, uint32[5] memory _weights) internal {
59
+ // reset totalWeights
60
+ totalWeights = 0;
61
+
62
+ // update total weight
63
+ for (uint32 i = 0; i < 5; i++) {
64
+ if (_receivers[i] == address(0)) continue;
65
+ totalWeights += _weights[i];
66
+ }
67
+
68
+ // update distribution
69
+ receivers = _receivers;
70
+ weights = _weights;
71
+
72
+ // emit event
73
+ emit SetDistribution(_msgSender());
74
+
75
+ _cleanUpPending();
76
+ }
77
+
78
+ function _cleanUpPending() internal {
79
+ delete pendingReceivers;
80
+ delete pendingWeights;
81
+ delete pendingValidAt;
82
+ }
83
+
84
+ // ---------------------------------------------------------------------------------------
85
+
86
+ function _distribute() internal {
87
+ // distribute all stables
88
+ uint256 amount = stable.balanceOf(address(this));
89
+
90
+ // distribution is not available
91
+ if (totalWeights == 0 || amount == 0) return;
92
+
93
+ for (uint256 i = 0; i < 5; i++) {
94
+ address receiver = receivers[i];
95
+ uint256 weight = weights[i];
96
+
97
+ // end distribution
98
+ if (receiver == address(0)) return;
99
+
100
+ // distribute weighted split
101
+ uint256 split = (weight * amount) / totalWeights;
102
+
103
+ // distribute revenue split
104
+ stable.transfer(receiver, split);
105
+
106
+ // last weighted ratio might be inconsistant, due to remaining assets distribution
107
+ emit Distribution(receiver, split, (weight * 1 ether) / totalWeights);
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,63 @@
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 {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
7
+ import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
8
+
9
+ import {IStablecoin} from '../stablecoin/IStablecoin.sol';
10
+
11
+ /// @title RewardRouterV0 - Non-custodial Router
12
+ /// @author @samclassix <samclassix@proton.me>, @wrytlabs <wrytlabs@proton.me>
13
+ /// @notice This contract acts purely as a non-custodial router, helping connect on-chain deposits with the off-chain reward program.
14
+ contract RewardRouterV0 {
15
+ using Math for uint256;
16
+ using SafeERC20 for IERC20;
17
+
18
+ // stable
19
+ IStablecoin public immutable stable;
20
+
21
+ // UniversalRewardsDistributor
22
+ address public immutable urd;
23
+
24
+ // signer -> token -> approved
25
+ mapping(address => mapping(address => uint256)) public approved;
26
+
27
+ // events
28
+ event SetApprove(address indexed signer, address indexed token, uint256 amount);
29
+ event Rewards(address indexed signer, address indexed token, uint256 amount);
30
+
31
+ // errors
32
+
33
+ error AllowanceTooLow();
34
+
35
+ // ---------------------------------------------------------------------------------------
36
+
37
+ constructor(IStablecoin _stable, address _urd) {
38
+ stable = _stable;
39
+ urd = _urd;
40
+ }
41
+
42
+ // ---------------------------------------------------------------------------------------
43
+
44
+ function setApprove(address signer, address token, uint256 amount) external {
45
+ stable.verifyCurator(msg.sender);
46
+ approved[signer][token] = amount;
47
+ emit SetApprove(signer, token, amount);
48
+ }
49
+
50
+ // ---------------------------------------------------------------------------------------
51
+
52
+ /// @dev This is a work around, since a signature is needed for the program.
53
+ // The contract immediately forwards the tokens to urd, avoiding custody.
54
+ /// @param token The token contract address
55
+ /// @param amount The amount to deposit
56
+ function deposit(address signer, address token, uint256 amount) external {
57
+ if (approved[signer][token] < amount) revert AllowanceTooLow();
58
+ approved[signer][token] -= amount;
59
+ IERC20(token).transfer(signer, amount);
60
+ IERC20(token).transferFrom(signer, urd, amount);
61
+ emit Rewards(signer, token, amount);
62
+ }
63
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "domain": {
3
+ "name": "Morpho Rewards Hub",
4
+ "version": "1",
5
+ "chainId": 1
6
+ },
7
+ "types": {
8
+ "BaseProgramPayload": [
9
+ {
10
+ "name": "programType",
11
+ "type": "string"
12
+ },
13
+ {
14
+ "name": "fundsSender",
15
+ "type": "address"
16
+ },
17
+ {
18
+ "name": "start",
19
+ "type": "string"
20
+ },
21
+ {
22
+ "name": "end",
23
+ "type": "string"
24
+ },
25
+ {
26
+ "name": "targetChainId",
27
+ "type": "uint256"
28
+ },
29
+ {
30
+ "name": "distributorAddress",
31
+ "type": "address"
32
+ },
33
+ {
34
+ "name": "distributorChainId",
35
+ "type": "uint256"
36
+ },
37
+ {
38
+ "name": "assetAddress",
39
+ "type": "address"
40
+ }
41
+ ],
42
+ "VaultProgramPayload": [
43
+ {
44
+ "name": "base",
45
+ "type": "BaseProgramPayload"
46
+ },
47
+ {
48
+ "name": "vault",
49
+ "type": "address"
50
+ },
51
+ {
52
+ "name": "amount",
53
+ "type": "string"
54
+ }
55
+ ]
56
+ },
57
+ "primaryType": "VaultProgramPayload",
58
+ "message": {
59
+ "base": {
60
+ "programType": "vault-reward",
61
+ "fundsSender": "0x0170F42f224b99CcbbeE673093589c5f9691dd06",
62
+ "start": "1749945600",
63
+ "end": "1751241600",
64
+ "targetChainId": 1,
65
+ "distributorAddress": "0x330eefa8a787552dc5cad3c3ca644844b1e61ddb",
66
+ "distributorChainId": 1,
67
+ "assetAddress": "0xB58E61C3098d85632Df34EecfB899A1Ed80921cB"
68
+ },
69
+ "vault": "0xFa7ED49Eb24A6117D8a3168EEE69D26b45C40C63",
70
+ "amount": "10000000000000000"
71
+ }
72
+ }
@@ -0,0 +1,211 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
5
+
6
+ interface IStablecoin is IERC20 {
7
+ // ---------------------------------------------------------------------------------------
8
+
9
+ /// @notice Checks if the given account has the curator role.
10
+ /// @param account The address to check.
11
+ /// @return True if the account is a curator, false otherwise.
12
+ function checkCurator(address account) external view returns (bool);
13
+
14
+ /// @notice Checks if the given account has the guardian role.
15
+ /// @param account The address to check.
16
+ /// @return True if the account is a guardian, false otherwise.
17
+ function checkGuardian(address account) external view returns (bool);
18
+
19
+ /// @notice Checks if the given account is either a curator or a guardian.
20
+ /// @param account The address to check.
21
+ /// @return True if the account is a curator or a guardian, false otherwise.
22
+ function checkCuratorOrGuardian(address account) external view returns (bool);
23
+
24
+ /// @notice Checks if the given account has any module role (active or expired).
25
+ /// @param account The address to check.
26
+ /// @return True if the account is a module, false otherwise.
27
+ function checkModule(address account) external view returns (bool);
28
+
29
+ /// @notice Checks if the given account is a valid (non-expired) module.
30
+ /// @param account The address to check.
31
+ /// @return True if the account is a valid module, false otherwise.
32
+ function checkValidModule(address account) external view returns (bool);
33
+
34
+ // ---------------------------------------------------------------------------------------
35
+
36
+ /// @notice Verifies that the given account is a curator.
37
+ /// @dev Reverts if the account is not a curator.
38
+ /// @param account The address to verify.
39
+ function verifyCurator(address account) external view;
40
+
41
+ /// @notice Verifies that the given account is a guardian.
42
+ /// @dev Reverts if the account is not a guardian.
43
+ /// @param account The address to verify.
44
+ function verifyGuardian(address account) external view;
45
+
46
+ /// @notice Verifies that the given account is either a curator or a guardian.
47
+ /// @dev Reverts if the account is neither a curator nor a guardian.
48
+ /// @param account The address to verify.
49
+ function verifyCuratorOrGuardian(address account) external view;
50
+
51
+ /// @notice Verifies that the given account has a module role (active or expired).
52
+ /// @dev Reverts if the account is not a module.
53
+ /// @param account The address to verify.
54
+ function verifyModule(address account) external view;
55
+
56
+ /// @notice Verifies that the given account is a valid (non-expired) module.
57
+ /// @dev Reverts if the account is not a valid module.
58
+ /// @param account The address to verify.
59
+ function verifyValidModule(address account) external view;
60
+
61
+ // ---------------------------------------------------------------------------------------
62
+
63
+ /// @notice Mints `value` tokens to address `to`.
64
+ /// @dev Callable only by addresses with the `validModule` role.
65
+ /// @param to The address receiving the minted tokens.
66
+ /// @param value The amount of tokens to mint.
67
+ /// @custom:role validModule
68
+ function mintModule(address to, uint256 value) external;
69
+
70
+ /// @notice Burns `amount` tokens from address `from`.
71
+ /// @dev Callable only by addresses with the `onlyModule` role.
72
+ /// @param from The address whose tokens will be burned.
73
+ /// @param amount The amount of tokens to burn.
74
+ /// @custom:role onlyModule
75
+ function burnModule(address from, uint256 amount) external;
76
+
77
+ /// @notice Burns `amount` tokens from the caller's balance.
78
+ /// @dev Publicly accessible by any user with sufficient balance.
79
+ /// @param amount The amount of tokens to burn.
80
+ /// @custom:role public
81
+ function burn(uint256 amount) external;
82
+
83
+ // ---------------------------------------------------------------------------------------
84
+
85
+ /// @notice Sets a new curator for the contract.
86
+ /// @dev Callable only by the current curator.
87
+ /// @param newCurator The address to assign as the new curator.
88
+ /// @custom:role onlyCurator
89
+ function setCurator(address newCurator) external;
90
+
91
+ /// @notice Publicly initiates a curator change by paying a fee.
92
+ /// @dev Caller must pay `fee`, which must be at least `ConstantsLib.PUBLIC_FEE * 10`.
93
+ /// @dev Applies a timelock that is twice as long
94
+ /// @param newCurator The address to be proposed as the new curator.
95
+ /// @param fee The fee amount submitted with the transaction.
96
+ /// @custom:rule claimPublicFee(fee, ConstantsLib.PUBLIC_FEE * 10)
97
+ function setCuratorPublic(address newCurator, uint256 fee) external;
98
+
99
+ /// @notice Cancels a pending curator proposal.
100
+ /// @dev Callable by either the current curator or a guardian.
101
+ /// @custom:role onlyCuratorOrGuardian
102
+ function revokePendingCurator() external;
103
+
104
+ /// @notice Accepts the curator role after the timelock period.
105
+ /// @dev Only new curator can accept the new role.
106
+ /// @dev Callable only after `pendingCurator.validAt` timestamp has passed.
107
+ /// @custom:rule afterTimelock(pendingCurator.validAt)
108
+ function acceptCurator() external;
109
+
110
+ // ---------------------------------------------------------------------------------------
111
+
112
+ /// @notice Sets a new guardian for the contract.
113
+ /// @dev Callable only by the current curator.
114
+ /// @param newGuardian The address to assign as the new guardian.
115
+ /// @custom:role onlyCurator
116
+ function setGuardian(address newGuardian) external;
117
+
118
+ /// @notice Cancels a pending guardian proposal.
119
+ /// @dev Callable by either the current curator or the current guardian.
120
+ /// @custom:role onlyCuratorOrGuardian
121
+ function revokePendingGuardian() external;
122
+
123
+ /// @notice Accepts the guardian role after the timelock period.
124
+ /// @dev Callable only after `pendingGuardian.validAt` timestamp has passed.
125
+ /// @custom:rule afterTimelock(pendingGuardian.validAt)
126
+ function acceptGuardian() external;
127
+
128
+ // ---------------------------------------------------------------------------------------
129
+
130
+ /// @notice Proposes a new timelock duration for sensitive actions.
131
+ /// @dev Callable only by the current curator.
132
+ /// @param newTimelock The new timelock duration (typically in seconds).
133
+ /// @custom:role onlyCurator
134
+ function setTimelock(uint256 newTimelock) external;
135
+
136
+ /// @notice Cancels a pending timelock change.
137
+ /// @dev Callable by either the curator or the guardian.
138
+ /// @custom:role onlyCuratorOrGuardian
139
+ function revokePendingTimelock() external;
140
+
141
+ /// @notice Finalizes the timelock change after the delay period.
142
+ /// @dev Callable only after `pendingTimelock.validAt` has passed.
143
+ /// @custom:rule afterTimelock(pendingTimelock.validAt)
144
+ function acceptTimelock() external;
145
+
146
+ // ---------------------------------------------------------------------------------------
147
+
148
+ /// @notice Proposes a new module with an expiration time and message.
149
+ /// @dev Callable only by the curator.
150
+ /// @param module The address of the module to config.
151
+ /// @param expiredAt The timestamp when the module should expire.
152
+ /// @param message A message describing the module purpose or context.
153
+ /// @custom:role onlyCurator
154
+ function setModule(address module, uint256 expiredAt, string calldata message) external;
155
+
156
+ /// @notice Publicly proposes a new module by paying a fee.
157
+ /// @dev Requires a fee at least equal to `ConstantsLib.PUBLIC_FEE`.
158
+ /// @dev Applies a timelock that is twice as long
159
+ /// @param module The address of the module to config.
160
+ /// @param expiredAt The timestamp when the module should expire.
161
+ /// @param message A message describing the module purpose or context.
162
+ /// @param fee The fee amount submitted with the transaction.
163
+ /// @custom:rule claimPublicFee(fee, ConstantsLib.PUBLIC_FEE)
164
+ function setModulePublic(address module, uint256 expiredAt, string calldata message, uint256 fee) external;
165
+
166
+ /// @notice Cancels a pending module proposal.
167
+ /// @dev Callable by either the curator or the guardian.
168
+ /// @param module The address of the module to revoke.
169
+ /// @param message A message explaining the revocation.
170
+ /// @custom:role onlyCuratorOrGuardian
171
+ function revokePendingModule(address module, string calldata message) external;
172
+
173
+ /// @notice Finalizes the addition of a module after the timelock period.
174
+ /// @dev Callable only after `pendingModules[module].validAt` has passed.
175
+ /// @param module The address of the module to accept.
176
+ /// @custom:rule afterTimelock(pendingModules[module].validAt)
177
+ function acceptModule(address module) external;
178
+
179
+ // ---------------------------------------------------------------------------------------
180
+
181
+ /// @notice Freezes the specified account, typically to restrict actions like transfers or module usage.
182
+ /// @dev Callable only by the curator.
183
+ /// @param account The address to be frozen.
184
+ /// @param message A message explaining the reason for freezing the account.
185
+ /// @custom:role onlyCurator
186
+ function setFreeze(address account, string calldata message) external;
187
+
188
+ // ---------------------------------------------------------------------------------------
189
+
190
+ /// @notice Initiates the unfreezing of a frozen account.
191
+ /// @dev Callable by either the curator or the guardian.
192
+ /// @param account The address to be unfrozen.
193
+ /// @param message A message explaining the reason for unfreezing.
194
+ /// @custom:role onlyCuratorOrGuardian
195
+ function setUnfreeze(address account, string calldata message) external;
196
+
197
+ /// @notice Cancels a pending unfreeze request.
198
+ /// @dev Callable by either the curator or the guardian.
199
+ /// @param account The address whose unfreeze request is being revoked.
200
+ /// @param message A message explaining the revocation.
201
+ /// @custom:role onlyCuratorOrGuardian
202
+ function revokePendingUnfreeze(address account, string calldata message) external;
203
+
204
+ /// @notice Finalizes the unfreezing of an account after the timelock period.
205
+ /// @dev Callable only after `pendingUnfreeze[account].validAt` has passed.
206
+ /// @param account The address to unfreeze.
207
+ /// @custom:rule afterTimelock(pendingUnfreeze[account].validAt)
208
+ function acceptUnfreeze(address account) external;
209
+
210
+ // ---------------------------------------------------------------------------------------
211
+ }
@@ -0,0 +1,27 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {IStablecoin} from './IStablecoin.sol';
5
+ import {PendingLib, PendingUint192, PendingAddress} from './libraries/PendingLib.sol';
6
+
7
+ interface IStablecoinMetadata is IStablecoin {
8
+ function curator() external view returns (address);
9
+
10
+ function pendingCurator() external view returns (PendingAddress memory);
11
+
12
+ function guardian() external view returns (address);
13
+
14
+ function pendingGuardian() external view returns (PendingAddress memory);
15
+
16
+ function timelock() external view returns (uint256);
17
+
18
+ function pendingTimelock() external view returns (PendingUint192 memory);
19
+
20
+ function modules(address module) external view returns (uint256);
21
+
22
+ function pendingModules(address module) external view returns (PendingUint192 memory);
23
+
24
+ function unfreeze(address account) external view returns (uint256);
25
+
26
+ function pendingUnfreeze(address account) external view returns (PendingUint192 memory);
27
+ }
@@ -0,0 +1,52 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Context} from '@openzeppelin/contracts/utils/Context.sol';
5
+
6
+ import {Stablecoin} from './Stablecoin.sol';
7
+ import {ErrorsLib} from './libraries/ErrorsLib.sol';
8
+
9
+ abstract contract IStablecoinModifier is Context {
10
+ Stablecoin public immutable stable;
11
+
12
+ modifier onlyCurator() {
13
+ stable.verifyCurator(_msgSender());
14
+ _;
15
+ }
16
+
17
+ modifier onlyGuardian() {
18
+ stable.verifyGuardian(_msgSender());
19
+ _;
20
+ }
21
+
22
+ modifier onlyCuratorOrGuardian() {
23
+ stable.verifyCuratorOrGuardian(_msgSender());
24
+ _;
25
+ }
26
+
27
+ modifier onlyModule() {
28
+ stable.verifyModule(_msgSender());
29
+ _;
30
+ }
31
+
32
+ modifier validModule() {
33
+ stable.verifyValidModule(_msgSender());
34
+ _;
35
+ }
36
+
37
+ modifier afterTimelock(uint256 validAt) {
38
+ if (validAt == 0) revert ErrorsLib.NoPendingValue();
39
+ if (block.timestamp < validAt) revert ErrorsLib.TimelockNotElapsed();
40
+ _;
41
+ }
42
+
43
+ modifier claimPublicFee(uint256 fee, uint256 min) {
44
+ if (fee < min) revert ErrorsLib.ProposalFeeToLow(min);
45
+ stable.transferFrom(_msgSender(), stable.curator(), fee);
46
+ _;
47
+ }
48
+
49
+ constructor(Stablecoin _stable) {
50
+ stable = _stable;
51
+ }
52
+ }