@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.
- package/.claude/settings.local.json +12 -0
- package/.env.example +6 -0
- package/.prettierrc.json +7 -0
- package/LICENSE +674 -0
- package/README.md +244 -0
- package/contracts/curve/CurveAdapterV1.sol +310 -0
- package/contracts/curve/CurveAdapterV1_1.sol +198 -0
- package/contracts/curve/helpers/ICurveStableSwapNG.json +566 -0
- package/contracts/curve/helpers/ICurveStableSwapNG.sol +177 -0
- package/contracts/deploy/VaultDeployer.sol +110 -0
- package/contracts/morpho/MorphoAdapterV1.sol +255 -0
- package/contracts/morpho/MorphoAdapterV1_1.sol +137 -0
- package/contracts/morpho/MorphoAdapterV1_2.sol +126 -0
- package/contracts/morpho/helpers/AggregatorV3Interface.sol +24 -0
- package/contracts/morpho/helpers/ChainlinkDataFeedLib.sol +36 -0
- package/contracts/morpho/helpers/ConstantsLib.sol +20 -0
- package/contracts/morpho/helpers/ErrorsLib.sol +17 -0
- package/contracts/morpho/helpers/IERC4626.sol +6 -0
- package/contracts/morpho/helpers/IMetaMorphoV1_1.sol +229 -0
- package/contracts/morpho/helpers/IMetaMorphoV1_1Factory.sol +32 -0
- package/contracts/morpho/helpers/IMorpho.sol +361 -0
- package/contracts/morpho/helpers/IMorphoCallbacks.sol +52 -0
- package/contracts/morpho/helpers/IMorphoChainlinkOracleV2.sol +39 -0
- package/contracts/morpho/helpers/IMorphoChainlinkOracleV2Factory.sol +56 -0
- package/contracts/morpho/helpers/IOracle.sol +15 -0
- package/contracts/morpho/helpers/MarketParamsLib.sol +21 -0
- package/contracts/morpho/helpers/MathLib.sol +45 -0
- package/contracts/morpho/helpers/Morpho.sol.bak +517 -0
- package/contracts/morpho/helpers/MorphoChainlinkOracleV2.sol +157 -0
- package/contracts/morpho/helpers/PendingLib.sol +47 -0
- package/contracts/morpho/helpers/SharesMathLib.sol +45 -0
- package/contracts/morpho/helpers/VaultLib.sol +18 -0
- package/contracts/reward/RewardDistributionV1.sol +110 -0
- package/contracts/reward/RewardRouterV0.sol +63 -0
- package/contracts/reward/Rewards.example.json +72 -0
- package/contracts/stablecoin/IStablecoin.sol +211 -0
- package/contracts/stablecoin/IStablecoinMetadata.sol +27 -0
- package/contracts/stablecoin/IStablecoinModifier.sol +52 -0
- package/contracts/stablecoin/Stablecoin.sol +376 -0
- package/contracts/stablecoin/libraries/ConstantsLib.sol +13 -0
- package/contracts/stablecoin/libraries/ErrorsLib.sol +45 -0
- package/contracts/stablecoin/libraries/EventsLib.sol +74 -0
- package/contracts/stablecoin/libraries/PendingLib.sol +38 -0
- package/contracts/vault/VaultAdapterRecoverV1.sol +29 -0
- package/contracts/vault/VaultAdapterV1.sol +126 -0
- package/dist/index.d.mts +16154 -0
- package/dist/index.d.ts +16154 -0
- package/dist/index.js +21134 -0
- package/dist/index.mjs +21061 -0
- package/docs/CoreVault: Integration of new Markets.md +197 -0
- package/docs/CoreVault: SupplyQueue.md +11 -0
- package/docs/Markets USDC Vault.md +35 -0
- package/docs/Markets USDU Vault.md +89 -0
- package/docs/Markets WETH Vault.md +35 -0
- package/docs/Overview.drawio +117 -0
- package/exports/abis/curve/CurveAdapterV1.ts +599 -0
- package/exports/abis/curve/CurveAdapterV1_1.ts +609 -0
- package/exports/abis/curve/CurveAdapterV1_2.ts +721 -0
- package/exports/abis/curve/helper/ICurveStableSwapNG.ts +1589 -0
- package/exports/abis/morpho/MorphoAdapterV1.ts +516 -0
- package/exports/abis/morpho/MorphoAdapterV1_1.ts +489 -0
- package/exports/abis/morpho/MorphoAdapterV1_2.ts +459 -0
- package/exports/abis/morpho/helper/AggregatorV3Interface.ts +113 -0
- package/exports/abis/morpho/helper/IMetaMorphoV1_1.ts +1483 -0
- package/exports/abis/morpho/helper/IMetaMorphoV1_1Base.ts +607 -0
- package/exports/abis/morpho/helper/IMetaMorphoV1_1StaticTyping.ts +696 -0
- package/exports/abis/morpho/helper/IMorpho.ts +1024 -0
- package/exports/abis/morpho/helper/IMorphoBase.ts +886 -0
- package/exports/abis/morpho/helper/IMorphoChainlinkOracleV2.ts +132 -0
- package/exports/abis/morpho/helper/IMorphoChainlinkOracleV2Factory.ts +109 -0
- package/exports/abis/morpho/helper/IMorphoFlashLoanCallback.ts +20 -0
- package/exports/abis/morpho/helper/IMorphoLiquidateCallback.ts +20 -0
- package/exports/abis/morpho/helper/IMorphoRepayCallback.ts +20 -0
- package/exports/abis/morpho/helper/IMorphoStaticTyping.ts +1003 -0
- package/exports/abis/morpho/helper/IMorphoSupplyCallback.ts +20 -0
- package/exports/abis/morpho/helper/IMorphoSupplyCollateralCallback.ts +20 -0
- package/exports/abis/morpho/helper/IMulticall.ts +21 -0
- package/exports/abis/morpho/helper/IOracle.ts +15 -0
- package/exports/abis/morpho/helper/IOwnable.ts +55 -0
- package/exports/abis/morpho/helper/MorphoChainlinkOracleV2.ts +188 -0
- package/exports/abis/openzeppelin/ERC20.ts +310 -0
- package/exports/abis/openzeppelin/ERC20Permit.ts +520 -0
- package/exports/abis/openzeppelin/IERC20.ts +185 -0
- package/exports/abis/openzeppelin/IERC20Metadata.ts +224 -0
- package/exports/abis/openzeppelin/IERC20Permit.ts +77 -0
- package/exports/abis/openzeppelin/IERC4626.ts +614 -0
- package/exports/abis/reward/RewardDistributionV1.ts +246 -0
- package/exports/abis/stablecoin/ErrorsLib.ts +114 -0
- package/exports/abis/stablecoin/EventsLib.ts +372 -0
- package/exports/abis/stablecoin/IStablecoin.ts +642 -0
- package/exports/abis/stablecoin/IStablecoinMetadata.ts +856 -0
- package/exports/abis/stablecoin/IStablecoinModifier.ts +15 -0
- package/exports/abis/stablecoin/Stablecoin.ts +1922 -0
- package/exports/abis/termmax/ITermMaxVault.ts +2335 -0
- package/exports/abis/vault/VaultAdapterRecoverV1.ts +490 -0
- package/exports/abis/vault/VaultAdapterV1.ts +459 -0
- package/exports/address.config.ts +113 -0
- package/exports/address.types.ts +130 -0
- package/exports/index.ts +61 -0
- package/hardhat.config.ts +231 -0
- package/helper/store.args.ts +17 -0
- package/helper/wallet.info.ts +3 -0
- package/helper/wallet.ts +41 -0
- package/install-macos.sh +46 -0
- package/package.json +73 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +10 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
interface ICurveStableSwapNG {
|
|
5
|
+
event Transfer(address indexed sender, address indexed receiver, uint256 value);
|
|
6
|
+
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
7
|
+
|
|
8
|
+
event TokenExchange(address indexed buyer, int128 sold_id, uint256 tokens_sold, int128 bought_id, uint256 tokens_bought);
|
|
9
|
+
|
|
10
|
+
event TokenExchangeUnderlying(address indexed buyer, int128 sold_id, uint256 tokens_sold, int128 bought_id, uint256 tokens_bought);
|
|
11
|
+
|
|
12
|
+
event AddLiquidity(address indexed provider, uint256[] token_amounts, uint256[] fees, uint256 invariant, uint256 token_supply);
|
|
13
|
+
|
|
14
|
+
event RemoveLiquidity(address indexed provider, uint256[] token_amounts, uint256[] fees, uint256 token_supply);
|
|
15
|
+
|
|
16
|
+
event RemoveLiquidityOne(address indexed provider, int128 token_id, uint256 token_amount, uint256 coin_amount, uint256 token_supply);
|
|
17
|
+
|
|
18
|
+
event RemoveLiquidityImbalance(
|
|
19
|
+
address indexed provider,
|
|
20
|
+
uint256[] token_amounts,
|
|
21
|
+
uint256[] fees,
|
|
22
|
+
uint256 invariant,
|
|
23
|
+
uint256 token_supply
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
event RampA(uint256 old_A, uint256 new_A, uint256 initial_time, uint256 future_time);
|
|
27
|
+
|
|
28
|
+
event StopRampA(uint256 A, uint256 t);
|
|
29
|
+
|
|
30
|
+
event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);
|
|
31
|
+
|
|
32
|
+
event SetNewMATime(uint256 ma_exp_time, uint256 D_ma_time);
|
|
33
|
+
|
|
34
|
+
// ----- Core Functions -----
|
|
35
|
+
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
|
|
36
|
+
|
|
37
|
+
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy, address _receiver) external returns (uint256);
|
|
38
|
+
|
|
39
|
+
function exchange_received(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
|
|
40
|
+
|
|
41
|
+
function exchange_received(int128 i, int128 j, uint256 _dx, uint256 _min_dy, address _receiver) external returns (uint256);
|
|
42
|
+
|
|
43
|
+
function add_liquidity(uint256[] calldata _amounts, uint256 _min_mint_amount) external returns (uint256);
|
|
44
|
+
|
|
45
|
+
function add_liquidity(uint256[] calldata _amounts, uint256 _min_mint_amount, address _receiver) external returns (uint256);
|
|
46
|
+
|
|
47
|
+
function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 _min_received) external returns (uint256);
|
|
48
|
+
|
|
49
|
+
function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 _min_received, address _receiver) external returns (uint256);
|
|
50
|
+
|
|
51
|
+
function remove_liquidity_imbalance(uint256[] calldata _amounts, uint256 _max_burn_amount) external returns (uint256);
|
|
52
|
+
|
|
53
|
+
function remove_liquidity_imbalance(
|
|
54
|
+
uint256[] calldata _amounts,
|
|
55
|
+
uint256 _max_burn_amount,
|
|
56
|
+
address _receiver
|
|
57
|
+
) external returns (uint256);
|
|
58
|
+
|
|
59
|
+
function remove_liquidity(uint256 _burn_amount, uint256[] calldata _min_amounts) external returns (uint256[] memory);
|
|
60
|
+
|
|
61
|
+
function remove_liquidity(uint256 _burn_amount, uint256[] calldata _min_amounts, address _receiver) external returns (uint256[] memory);
|
|
62
|
+
|
|
63
|
+
function remove_liquidity(
|
|
64
|
+
uint256 _burn_amount,
|
|
65
|
+
uint256[] calldata _min_amounts,
|
|
66
|
+
address _receiver,
|
|
67
|
+
bool _claim_admin_fees
|
|
68
|
+
) external returns (uint256[] memory);
|
|
69
|
+
|
|
70
|
+
// ----- Admin & Fee Management -----
|
|
71
|
+
function withdraw_admin_fees() external;
|
|
72
|
+
|
|
73
|
+
function ramp_A(uint256 _future_A, uint256 _future_time) external;
|
|
74
|
+
|
|
75
|
+
function stop_ramp_A() external;
|
|
76
|
+
|
|
77
|
+
function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier) external;
|
|
78
|
+
|
|
79
|
+
// ----- ERC20-like -----
|
|
80
|
+
function transfer(address _to, uint256 _value) external returns (bool);
|
|
81
|
+
|
|
82
|
+
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
|
|
83
|
+
|
|
84
|
+
function approve(address _spender, uint256 _value) external returns (bool);
|
|
85
|
+
|
|
86
|
+
function permit(
|
|
87
|
+
address _owner,
|
|
88
|
+
address _spender,
|
|
89
|
+
uint256 _value,
|
|
90
|
+
uint256 _deadline,
|
|
91
|
+
uint8 _v,
|
|
92
|
+
bytes32 _r,
|
|
93
|
+
bytes32 _s
|
|
94
|
+
) external returns (bool);
|
|
95
|
+
|
|
96
|
+
// ----- Views -----
|
|
97
|
+
function last_price(uint256 i) external view returns (uint256);
|
|
98
|
+
|
|
99
|
+
function ema_price(uint256 i) external view returns (uint256);
|
|
100
|
+
|
|
101
|
+
function get_p(uint256 i) external view returns (uint256);
|
|
102
|
+
|
|
103
|
+
function price_oracle(uint256 i) external view returns (uint256);
|
|
104
|
+
|
|
105
|
+
function D_oracle() external view returns (uint256);
|
|
106
|
+
|
|
107
|
+
function DOMAIN_SEPARATOR() external view returns (bytes32);
|
|
108
|
+
|
|
109
|
+
function get_dx(int128 i, int128 j, uint256 dy) external view returns (uint256);
|
|
110
|
+
|
|
111
|
+
function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
|
|
112
|
+
|
|
113
|
+
function calc_withdraw_one_coin(uint256 _burn_amount, int128 i) external view returns (uint256);
|
|
114
|
+
|
|
115
|
+
function totalSupply() external view returns (uint256);
|
|
116
|
+
|
|
117
|
+
function get_virtual_price() external view returns (uint256);
|
|
118
|
+
|
|
119
|
+
function calc_token_amount(uint256[] calldata _amounts, bool _is_deposit) external view returns (uint256);
|
|
120
|
+
|
|
121
|
+
function A() external view returns (uint256);
|
|
122
|
+
|
|
123
|
+
function A_precise() external view returns (uint256);
|
|
124
|
+
|
|
125
|
+
function balances(uint256 i) external view returns (uint256);
|
|
126
|
+
|
|
127
|
+
function get_balances() external view returns (uint256[] memory);
|
|
128
|
+
|
|
129
|
+
function stored_rates() external view returns (uint256[] memory);
|
|
130
|
+
|
|
131
|
+
function dynamic_fee(int128 i, int128 j) external view returns (uint256);
|
|
132
|
+
|
|
133
|
+
function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;
|
|
134
|
+
|
|
135
|
+
// --- View functions ---
|
|
136
|
+
function N_COINS() external view returns (uint256);
|
|
137
|
+
|
|
138
|
+
function coins(uint256 arg0) external view returns (address);
|
|
139
|
+
|
|
140
|
+
function fee() external view returns (uint256);
|
|
141
|
+
|
|
142
|
+
function offpeg_fee_multiplier() external view returns (uint256);
|
|
143
|
+
|
|
144
|
+
function admin_fee() external view returns (uint256);
|
|
145
|
+
|
|
146
|
+
function initial_A() external view returns (uint256);
|
|
147
|
+
|
|
148
|
+
function future_A() external view returns (uint256);
|
|
149
|
+
|
|
150
|
+
function initial_A_time() external view returns (uint256);
|
|
151
|
+
|
|
152
|
+
function future_A_time() external view returns (uint256);
|
|
153
|
+
|
|
154
|
+
function admin_balances(uint256 arg0) external view returns (uint256);
|
|
155
|
+
|
|
156
|
+
function ma_exp_time() external view returns (uint256);
|
|
157
|
+
|
|
158
|
+
function D_ma_time() external view returns (uint256);
|
|
159
|
+
|
|
160
|
+
function ma_last_time() external view returns (uint256);
|
|
161
|
+
|
|
162
|
+
function name() external view returns (string memory);
|
|
163
|
+
|
|
164
|
+
function symbol() external view returns (string memory);
|
|
165
|
+
|
|
166
|
+
function decimals() external view returns (uint8);
|
|
167
|
+
|
|
168
|
+
function version() external view returns (string memory);
|
|
169
|
+
|
|
170
|
+
function balanceOf(address arg0) external view returns (uint256);
|
|
171
|
+
|
|
172
|
+
function allowance(address arg0, address arg1) external view returns (uint256);
|
|
173
|
+
|
|
174
|
+
function nonces(address arg0) external view returns (uint256);
|
|
175
|
+
|
|
176
|
+
function salt() external view returns (bytes32);
|
|
177
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Stablecoin} from '../stablecoin/Stablecoin.sol';
|
|
5
|
+
|
|
6
|
+
import {IMorpho, MarketParams, Id} from '../morpho/helpers/IMorpho.sol';
|
|
7
|
+
import {ConstantsLib} from '../morpho/helpers/ConstantsLib.sol';
|
|
8
|
+
|
|
9
|
+
import {IMetaMorphoV1_1} from '../morpho/helpers/IMetaMorphoV1_1.sol';
|
|
10
|
+
import {IMetaMorphoV1_1Factory} from '../morpho/helpers/IMetaMorphoV1_1Factory.sol';
|
|
11
|
+
|
|
12
|
+
import {MorphoAdapterV1} from '../morpho/MorphoAdapterV1.sol';
|
|
13
|
+
import {RewardRouterV0} from '../reward/RewardRouterV0.sol';
|
|
14
|
+
|
|
15
|
+
contract VaultDeployer {
|
|
16
|
+
IMetaMorphoV1_1Factory public immutable vaultFactory;
|
|
17
|
+
IMorpho public immutable morpho;
|
|
18
|
+
address public immutable alloc;
|
|
19
|
+
address public immutable urd;
|
|
20
|
+
|
|
21
|
+
Stablecoin public immutable stable;
|
|
22
|
+
|
|
23
|
+
IMetaMorphoV1_1 public immutable core;
|
|
24
|
+
IMetaMorphoV1_1 public immutable staked;
|
|
25
|
+
|
|
26
|
+
MorphoAdapterV1 public immutable adapter;
|
|
27
|
+
RewardRouterV0 public immutable reward;
|
|
28
|
+
|
|
29
|
+
constructor(IMorpho _morpho, IMetaMorphoV1_1Factory _factory, address _alloc, address _urd, address _curator) {
|
|
30
|
+
// deploy stablecoin
|
|
31
|
+
stable = new Stablecoin('USDU', 'USDU', address(this));
|
|
32
|
+
|
|
33
|
+
// morpho contracts
|
|
34
|
+
vaultFactory = _factory;
|
|
35
|
+
morpho = _morpho;
|
|
36
|
+
alloc = _alloc;
|
|
37
|
+
urd = _urd;
|
|
38
|
+
|
|
39
|
+
// set up morpho vaults
|
|
40
|
+
core = vaultFactory.createMetaMorpho(address(this), 0, address(stable), 'USDU Core', 'sUSDU', 0);
|
|
41
|
+
core.setCurator(_curator);
|
|
42
|
+
core.setFeeRecipient(_curator);
|
|
43
|
+
core.setFee(ConstantsLib.MAX_FEE);
|
|
44
|
+
core.setSkimRecipient(_curator);
|
|
45
|
+
core.setIsAllocator(alloc, true);
|
|
46
|
+
|
|
47
|
+
staked = vaultFactory.createMetaMorpho(address(this), 0, address(core), 'USDU Staked', 'ssUSDU', 0);
|
|
48
|
+
staked.setCurator(_curator);
|
|
49
|
+
staked.setFeeRecipient(_curator);
|
|
50
|
+
staked.setFee(ConstantsLib.MAX_FEE);
|
|
51
|
+
staked.setSkimRecipient(_curator);
|
|
52
|
+
staked.setIsAllocator(alloc, true);
|
|
53
|
+
|
|
54
|
+
// prepare markets
|
|
55
|
+
MarketParams memory coreIdleMarket = MarketParams(address(stable), address(0), address(0), address(0), 0);
|
|
56
|
+
MarketParams memory stakedIdleMarket = MarketParams(address(core), address(0), address(0), address(0), 0);
|
|
57
|
+
Id[] memory idCore = new Id[](1);
|
|
58
|
+
idCore[0] = getMarketId(coreIdleMarket);
|
|
59
|
+
Id[] memory idStaked = new Id[](1);
|
|
60
|
+
idStaked[0] = getMarketId(stakedIdleMarket);
|
|
61
|
+
|
|
62
|
+
// create markets
|
|
63
|
+
morpho.createMarket(coreIdleMarket);
|
|
64
|
+
morpho.createMarket(stakedIdleMarket);
|
|
65
|
+
|
|
66
|
+
// attach stable idle market to core vault
|
|
67
|
+
core.submitCap(coreIdleMarket, 100_000_000 ether);
|
|
68
|
+
core.acceptCap(coreIdleMarket);
|
|
69
|
+
core.setSupplyQueue(idCore);
|
|
70
|
+
|
|
71
|
+
// attach staked idle market to staked vault
|
|
72
|
+
staked.submitCap(stakedIdleMarket, 100_000_000 ether);
|
|
73
|
+
staked.acceptCap(stakedIdleMarket);
|
|
74
|
+
staked.setSupplyQueue(idStaked);
|
|
75
|
+
|
|
76
|
+
// set up reward helper
|
|
77
|
+
reward = new RewardRouterV0(stable, _urd);
|
|
78
|
+
|
|
79
|
+
// set up morpho adapter and reward router
|
|
80
|
+
address[5] memory receivers = [address(reward), address(0), address(0), address(0), address(0)];
|
|
81
|
+
uint32[5] memory weights = [uint32(1000), uint32(0), uint32(0), uint32(0), uint32(0)];
|
|
82
|
+
|
|
83
|
+
// set up modules
|
|
84
|
+
adapter = new MorphoAdapterV1(stable, core, staked, receivers, weights);
|
|
85
|
+
stable.setModule(address(adapter), type(uint256).max, 'MorphoAdapterV1');
|
|
86
|
+
|
|
87
|
+
// prepare stable for curator
|
|
88
|
+
stable.setCurator(_curator); // no timelock, new curator needs to accept role
|
|
89
|
+
stable.setTimelock(7 days); // will apply now for further steps
|
|
90
|
+
|
|
91
|
+
// prepare vaults for curator, needs 2nd step to accept new role
|
|
92
|
+
core.transferOwnership(_curator);
|
|
93
|
+
staked.transferOwnership(_curator);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getMarketId(MarketParams memory marketParams) public pure returns (Id) {
|
|
97
|
+
return
|
|
98
|
+
Id.wrap(
|
|
99
|
+
keccak256(
|
|
100
|
+
abi.encode(
|
|
101
|
+
marketParams.loanToken,
|
|
102
|
+
marketParams.collateralToken,
|
|
103
|
+
marketParams.oracle,
|
|
104
|
+
marketParams.irm,
|
|
105
|
+
marketParams.lltv
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
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 {Context} from '@openzeppelin/contracts/utils/Context.sol';
|
|
7
|
+
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
|
8
|
+
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
|
|
9
|
+
|
|
10
|
+
import {Stablecoin} from '../stablecoin/Stablecoin.sol';
|
|
11
|
+
import {ErrorsLib} from '../stablecoin/libraries/ErrorsLib.sol';
|
|
12
|
+
|
|
13
|
+
import {IMetaMorphoV1_1} from './helpers/IMetaMorphoV1_1.sol';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @title MorphoAdapterV1
|
|
17
|
+
* @author @samclassix <samclassix@proton.me>, @wrytlabs <wrytlabs@proton.me>
|
|
18
|
+
* @notice This is an adapter for interacting with Morpho to mint liquidity straight into the market.
|
|
19
|
+
*/
|
|
20
|
+
contract MorphoAdapterV1 is Context {
|
|
21
|
+
using Math for uint256;
|
|
22
|
+
using SafeERC20 for Stablecoin;
|
|
23
|
+
using SafeERC20 for IMetaMorphoV1_1;
|
|
24
|
+
|
|
25
|
+
Stablecoin public immutable stable;
|
|
26
|
+
IMetaMorphoV1_1 public immutable core;
|
|
27
|
+
IMetaMorphoV1_1 public immutable staked;
|
|
28
|
+
|
|
29
|
+
uint256 public totalMinted;
|
|
30
|
+
uint256 public totalRevenue;
|
|
31
|
+
|
|
32
|
+
address[5] public receivers;
|
|
33
|
+
uint32[5] public weights;
|
|
34
|
+
uint256 public totalWeights;
|
|
35
|
+
|
|
36
|
+
address[5] public pendingReceivers;
|
|
37
|
+
uint32[5] public pendingWeights;
|
|
38
|
+
uint256 public pendingValidAt;
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
event SubmitDistribution(address indexed caller, address[5] receivers, uint32[5] weights, uint256 timelock);
|
|
43
|
+
event RevokeDistribution(address indexed caller);
|
|
44
|
+
event SetDistribution(address indexed caller);
|
|
45
|
+
|
|
46
|
+
event Deposit(uint256 amount, uint256 sharesCore, uint256 sharesStaked, uint256 totalMinted);
|
|
47
|
+
event Redeem(uint256 amount, uint256 sharesCore, uint256 sharesStaked, uint256 totalMinted);
|
|
48
|
+
event Revenue(uint256 amount, uint256 totalRevenue, uint256 totalMinted);
|
|
49
|
+
event Distribution(address indexed receiver, uint256 amount, uint256 ratio);
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
error ForwardCallFailed(address forwardedTo);
|
|
54
|
+
error MismatchLength(uint256 receivers, uint256 weights);
|
|
55
|
+
error NothingToReconcile(uint256 assets, uint256 minted);
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
modifier onlyCurator() {
|
|
60
|
+
stable.verifyCurator(_msgSender());
|
|
61
|
+
_;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
modifier onlyCuratorOrGuardian() {
|
|
65
|
+
stable.verifyCuratorOrGuardian(_msgSender());
|
|
66
|
+
_;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
modifier afterTimelock(uint256 validAt) {
|
|
70
|
+
if (validAt == 0) revert ErrorsLib.NoPendingValue();
|
|
71
|
+
if (block.timestamp < validAt) revert ErrorsLib.TimelockNotElapsed();
|
|
72
|
+
_;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
constructor(
|
|
78
|
+
Stablecoin _stable,
|
|
79
|
+
IMetaMorphoV1_1 _core,
|
|
80
|
+
IMetaMorphoV1_1 _staked,
|
|
81
|
+
address[5] memory _receivers,
|
|
82
|
+
uint32[5] memory _weights
|
|
83
|
+
) {
|
|
84
|
+
stable = _stable;
|
|
85
|
+
core = _core;
|
|
86
|
+
staked = _staked;
|
|
87
|
+
_setDistribution(_receivers, _weights);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
function totalAssets() public view returns (uint256) {
|
|
93
|
+
// this will use `_accruedFeeAndAssets`
|
|
94
|
+
uint256 assetsFromStaked = staked.convertToAssets(staked.balanceOf(address(this)));
|
|
95
|
+
return core.convertToAssets(assetsFromStaked);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
function setDistribution(address[5] calldata _receivers, uint32[5] calldata _weights) external onlyCurator {
|
|
101
|
+
if (_receivers.length != _weights.length) revert MismatchLength(_receivers.length, _weights.length);
|
|
102
|
+
if (pendingValidAt != 0) revert ErrorsLib.AlreadyPending();
|
|
103
|
+
|
|
104
|
+
if (receivers[0] == address(0)) {
|
|
105
|
+
_setDistribution(_receivers, _weights);
|
|
106
|
+
} else {
|
|
107
|
+
pendingReceivers = _receivers;
|
|
108
|
+
pendingWeights = _weights;
|
|
109
|
+
pendingValidAt = block.timestamp + stable.timelock();
|
|
110
|
+
emit SubmitDistribution(_msgSender(), _receivers, _weights, pendingValidAt);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function revokePendingDistribution() external onlyCuratorOrGuardian {
|
|
115
|
+
if (pendingValidAt == 0) revert ErrorsLib.NoPendingValue();
|
|
116
|
+
emit RevokeDistribution(_msgSender());
|
|
117
|
+
_cleanUpPending();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function applyDistribution() external afterTimelock(pendingValidAt) {
|
|
121
|
+
_setDistribution(pendingReceivers, pendingWeights);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function _setDistribution(address[5] memory _receivers, uint32[5] memory _weights) internal {
|
|
125
|
+
// reset totalWeights
|
|
126
|
+
totalWeights = 0;
|
|
127
|
+
|
|
128
|
+
// update total weight
|
|
129
|
+
for (uint32 i = 0; i < _receivers.length; i++) {
|
|
130
|
+
totalWeights += _weights[i];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// update distribution
|
|
134
|
+
receivers = _receivers;
|
|
135
|
+
weights = _weights;
|
|
136
|
+
|
|
137
|
+
// emit event
|
|
138
|
+
emit SetDistribution(_msgSender());
|
|
139
|
+
|
|
140
|
+
_cleanUpPending();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function _cleanUpPending() internal {
|
|
144
|
+
delete pendingReceivers;
|
|
145
|
+
delete pendingWeights;
|
|
146
|
+
delete pendingValidAt;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
function deposit(uint256 amount) external onlyCurator {
|
|
152
|
+
// mint stables
|
|
153
|
+
stable.mintModule(address(this), amount);
|
|
154
|
+
totalMinted += amount;
|
|
155
|
+
|
|
156
|
+
// approve stable for deposit into core vault
|
|
157
|
+
stable.forceApprove(address(core), amount);
|
|
158
|
+
uint256 sharesCore = core.deposit(amount, address(this));
|
|
159
|
+
|
|
160
|
+
// approve core shares for deposit into staked vault
|
|
161
|
+
core.forceApprove(address(staked), sharesCore);
|
|
162
|
+
uint256 sharesStaked = staked.deposit(sharesCore, address(this));
|
|
163
|
+
|
|
164
|
+
emit Deposit(amount, sharesCore, sharesStaked, totalMinted);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ---------------------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
function redeem(uint256 sharesStaked) external onlyCurator {
|
|
170
|
+
// reconcile, triggers `_accruedFeeAndAssets` in vault
|
|
171
|
+
_reconcile(totalAssets(), true);
|
|
172
|
+
|
|
173
|
+
// approve staked shares for redeem from staked vault
|
|
174
|
+
staked.forceApprove(address(staked), sharesStaked);
|
|
175
|
+
uint256 sharesCore = staked.redeem(sharesStaked, address(this), address(this));
|
|
176
|
+
|
|
177
|
+
// approve core shares for redeem from core vault
|
|
178
|
+
core.forceApprove(address(core), sharesCore);
|
|
179
|
+
uint256 amount = core.redeem(sharesCore, address(this), address(this));
|
|
180
|
+
|
|
181
|
+
// reduce minted amount
|
|
182
|
+
if (totalMinted >= amount) {
|
|
183
|
+
stable.burn(amount);
|
|
184
|
+
totalMinted -= amount;
|
|
185
|
+
} else {
|
|
186
|
+
// fallback, burn existing totalMinted if available
|
|
187
|
+
if (totalMinted > 0) {
|
|
188
|
+
stable.burn(totalMinted);
|
|
189
|
+
totalMinted = 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// fallback, distribute remaining balance
|
|
193
|
+
uint256 bal = stable.balanceOf(address(this));
|
|
194
|
+
if (bal > 0) {
|
|
195
|
+
_distribute(bal);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
emit Redeem(amount, sharesCore, sharesStaked, totalMinted);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
function reconcile() external {
|
|
205
|
+
_reconcile(totalAssets(), false);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function _reconcile(uint256 assets, bool allowPassing) internal returns (uint256) {
|
|
209
|
+
if (assets > totalMinted) {
|
|
210
|
+
uint256 mintToReconcile = assets - totalMinted;
|
|
211
|
+
totalRevenue += mintToReconcile;
|
|
212
|
+
|
|
213
|
+
stable.mintModule(address(this), mintToReconcile);
|
|
214
|
+
totalMinted += mintToReconcile;
|
|
215
|
+
emit Revenue(mintToReconcile, totalRevenue, totalMinted);
|
|
216
|
+
|
|
217
|
+
_distribute(mintToReconcile);
|
|
218
|
+
return mintToReconcile;
|
|
219
|
+
} else {
|
|
220
|
+
if (allowPassing) {
|
|
221
|
+
return 0;
|
|
222
|
+
} else {
|
|
223
|
+
revert NothingToReconcile(assets, totalMinted);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ---------------------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
function _distribute(uint256 amount) internal {
|
|
231
|
+
for (uint256 i = 0; i < 5; i++) {
|
|
232
|
+
address receiver = receivers[i];
|
|
233
|
+
uint256 weight = weights[i];
|
|
234
|
+
uint256 split;
|
|
235
|
+
|
|
236
|
+
// end distribution
|
|
237
|
+
if (receiver == address(0)) return;
|
|
238
|
+
|
|
239
|
+
// last item reached (index: 5 - 1 = 4) OR next receiver is zeroAddress
|
|
240
|
+
if (i == 4 || (i < 4 && receivers[i + 1] == address(0))) {
|
|
241
|
+
// distribute remainings, eliminating rounding or deposit issues
|
|
242
|
+
split = stable.balanceOf(address(this));
|
|
243
|
+
} else {
|
|
244
|
+
// distribute weighted split
|
|
245
|
+
split = (weight * amount) / totalWeights;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// distribute revenue split
|
|
249
|
+
stable.transfer(receiver, split);
|
|
250
|
+
|
|
251
|
+
// last weighted ratio might be inconsistant, due to remaining assets distribution
|
|
252
|
+
emit Distribution(receiver, split, (weight * 1 ether) / totalWeights);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
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 {RewardDistributionV1, Stablecoin} from '../reward/RewardDistributionV1.sol';
|
|
10
|
+
|
|
11
|
+
import {IMetaMorphoV1_1} from './helpers/IMetaMorphoV1_1.sol';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @title MorphoAdapterV1_1
|
|
15
|
+
* @author @samclassix <samclassix@proton.me>, @wrytlabs <wrytlabs@proton.me>
|
|
16
|
+
* @notice This is an adapter for interacting with Morpho to mint liquidity straight into the market.
|
|
17
|
+
*/
|
|
18
|
+
contract MorphoAdapterV1_1 is RewardDistributionV1 {
|
|
19
|
+
using Math for uint256;
|
|
20
|
+
using SafeERC20 for Stablecoin;
|
|
21
|
+
using SafeERC20 for IMetaMorphoV1_1;
|
|
22
|
+
|
|
23
|
+
IMetaMorphoV1_1 public immutable core;
|
|
24
|
+
IMetaMorphoV1_1 public immutable staked;
|
|
25
|
+
|
|
26
|
+
uint256 public totalMinted;
|
|
27
|
+
uint256 public totalRevenue;
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
event Deposit(uint256 amount, uint256 sharesCore, uint256 sharesStaked, uint256 totalMinted);
|
|
32
|
+
event Redeem(uint256 amount, uint256 sharesCore, uint256 sharesStaked, uint256 totalMinted);
|
|
33
|
+
event Revenue(uint256 amount, uint256 totalRevenue, uint256 totalMinted);
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
error NothingToReconcile(uint256 assets, uint256 minted);
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
Stablecoin _stable,
|
|
43
|
+
IMetaMorphoV1_1 _core,
|
|
44
|
+
IMetaMorphoV1_1 _staked,
|
|
45
|
+
address[5] memory _receivers,
|
|
46
|
+
uint32[5] memory _weights
|
|
47
|
+
) RewardDistributionV1(_stable, _receivers, _weights) {
|
|
48
|
+
core = _core;
|
|
49
|
+
staked = _staked;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
function totalAssets() public view returns (uint256) {
|
|
55
|
+
// this will use `_accruedFeeAndAssets`
|
|
56
|
+
uint256 assetsFromStaked = staked.convertToAssets(staked.balanceOf(address(this)));
|
|
57
|
+
return core.convertToAssets(assetsFromStaked);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
function deposit(uint256 amount) external onlyCurator {
|
|
63
|
+
// mint stables
|
|
64
|
+
stable.mintModule(address(this), amount);
|
|
65
|
+
totalMinted += amount;
|
|
66
|
+
|
|
67
|
+
// approve stable for deposit into core vault
|
|
68
|
+
stable.forceApprove(address(core), amount);
|
|
69
|
+
uint256 sharesCore = core.deposit(amount, address(this));
|
|
70
|
+
|
|
71
|
+
// approve core shares for deposit into staked vault
|
|
72
|
+
core.forceApprove(address(staked), sharesCore);
|
|
73
|
+
uint256 sharesStaked = staked.deposit(sharesCore, address(this));
|
|
74
|
+
|
|
75
|
+
emit Deposit(amount, sharesCore, sharesStaked, totalMinted);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
function redeem(uint256 sharesStaked) external onlyCurator {
|
|
81
|
+
// reconcile, triggers `_accruedFeeAndAssets` in vault
|
|
82
|
+
_reconcile(totalAssets(), true);
|
|
83
|
+
|
|
84
|
+
// redeem staked shares from staked vault
|
|
85
|
+
uint256 sharesCore = staked.redeem(sharesStaked, address(this), address(this));
|
|
86
|
+
|
|
87
|
+
// redeem core shares from core vault
|
|
88
|
+
uint256 amount = core.redeem(sharesCore, address(this), address(this));
|
|
89
|
+
|
|
90
|
+
// reduce minted amount
|
|
91
|
+
if (totalMinted >= amount) {
|
|
92
|
+
stable.burn(amount);
|
|
93
|
+
totalMinted -= amount;
|
|
94
|
+
} else {
|
|
95
|
+
// fallback, burn existing totalMinted if available
|
|
96
|
+
if (totalMinted > 0) {
|
|
97
|
+
stable.burn(totalMinted);
|
|
98
|
+
totalMinted = 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// fallback, distribute remaining balance
|
|
102
|
+
_distribute();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
emit Redeem(amount, sharesCore, sharesStaked, totalMinted);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
function reconcile() external {
|
|
111
|
+
_reconcile(totalAssets(), false);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function _reconcile(uint256 assets, bool allowPassing) internal returns (uint256) {
|
|
115
|
+
if (assets > totalMinted) {
|
|
116
|
+
// calc revenue
|
|
117
|
+
uint256 mintToReconcile = assets - totalMinted;
|
|
118
|
+
totalRevenue += mintToReconcile;
|
|
119
|
+
|
|
120
|
+
// mint revenue to reconcile
|
|
121
|
+
stable.mintModule(address(this), mintToReconcile);
|
|
122
|
+
totalMinted += mintToReconcile;
|
|
123
|
+
emit Revenue(mintToReconcile, totalRevenue, totalMinted);
|
|
124
|
+
|
|
125
|
+
// distribute balance
|
|
126
|
+
_distribute();
|
|
127
|
+
|
|
128
|
+
return mintToReconcile;
|
|
129
|
+
} else {
|
|
130
|
+
if (allowPassing) {
|
|
131
|
+
return 0;
|
|
132
|
+
} else {
|
|
133
|
+
revert NothingToReconcile(assets, totalMinted);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|