@venusprotocol/liquidity-hub 0.0.0

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 (76) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +422 -0
  3. package/contracts/Hub/Hub.sol +1614 -0
  4. package/contracts/Hub/HubStorage.sol +106 -0
  5. package/contracts/Hub/lib/HubAdminLib.sol +292 -0
  6. package/contracts/YieldGroup/YieldGroup.sol +128 -0
  7. package/contracts/YieldGroup/YieldGroupFRV.sol +107 -0
  8. package/contracts/YieldGroup/base/YieldGroupBase.sol +797 -0
  9. package/contracts/YieldGroup/base/YieldGroupBaseStorage.sol +73 -0
  10. package/contracts/adapters/AdapterCoreV1.sol +309 -0
  11. package/contracts/adapters/AdapterFRV.sol +276 -0
  12. package/contracts/adapters/AdapterFlux.sol +173 -0
  13. package/contracts/interfaces/IHub.sol +551 -0
  14. package/contracts/interfaces/IHubRegistry.sol +120 -0
  15. package/contracts/interfaces/IMigrator.sol +134 -0
  16. package/contracts/interfaces/IResourceAdapter.sol +154 -0
  17. package/contracts/interfaces/IYieldGroup.sol +119 -0
  18. package/contracts/interfaces/IYieldGroupBase.sol +507 -0
  19. package/contracts/interfaces/IYieldGroupFRV.sol +61 -0
  20. package/contracts/interfaces/external/IComptroller.sol +57 -0
  21. package/contracts/interfaces/external/IFRVVault.sol +113 -0
  22. package/contracts/interfaces/external/IFToken.sol +46 -0
  23. package/contracts/interfaces/external/IFluidLendingResolver.sol +63 -0
  24. package/contracts/interfaces/external/IVBep20.sol +39 -0
  25. package/contracts/interfaces/external/IVToken.sol +125 -0
  26. package/contracts/interfaces/external/IWBNB.sol +16 -0
  27. package/contracts/migrator/Migrator.sol +182 -0
  28. package/contracts/registry/HubRegistry.sol +179 -0
  29. package/deployments/bscmainnet/AdapterCoreV1.json +336 -0
  30. package/deployments/bscmainnet/AdapterFRV.json +225 -0
  31. package/deployments/bscmainnet/AdapterFlux.json +249 -0
  32. package/deployments/bscmainnet/CoreBeacon.json +110 -0
  33. package/deployments/bscmainnet/CoreSource_U.json +1083 -0
  34. package/deployments/bscmainnet/CoreSource_USDC.json +1083 -0
  35. package/deployments/bscmainnet/CoreSource_USDT.json +1083 -0
  36. package/deployments/bscmainnet/FRVBeacon.json +110 -0
  37. package/deployments/bscmainnet/FRVSource_U.json +978 -0
  38. package/deployments/bscmainnet/FRVSource_USDC.json +978 -0
  39. package/deployments/bscmainnet/FRVSource_USDT.json +978 -0
  40. package/deployments/bscmainnet/FluxBeacon.json +110 -0
  41. package/deployments/bscmainnet/FluxSource_U.json +1083 -0
  42. package/deployments/bscmainnet/FluxSource_USDC.json +1083 -0
  43. package/deployments/bscmainnet/FluxSource_USDT.json +1083 -0
  44. package/deployments/bscmainnet/HubBeacon.json +110 -0
  45. package/deployments/bscmainnet/HubImpl.json +2216 -0
  46. package/deployments/bscmainnet/HubRegistry.json +386 -0
  47. package/deployments/bscmainnet/HubRegistryImpl.json +386 -0
  48. package/deployments/bscmainnet/Hub_U.json +2216 -0
  49. package/deployments/bscmainnet/Hub_USDC.json +2216 -0
  50. package/deployments/bscmainnet/Hub_USDT.json +2216 -0
  51. package/deployments/bscmainnet/Migrator.json +375 -0
  52. package/deployments/bscmainnet/YieldGroupCoreImpl.json +1083 -0
  53. package/deployments/bscmainnet/YieldGroupFRVImpl.json +978 -0
  54. package/deployments/bscmainnet/YieldGroupFluxImpl.json +1083 -0
  55. package/deployments/bscmainnet_addresses.json +32 -0
  56. package/deployments/bsctestnet/AdapterCoreV1.json +336 -0
  57. package/deployments/bsctestnet/AdapterFRV.json +225 -0
  58. package/deployments/bsctestnet/AdapterFlux.json +249 -0
  59. package/deployments/bsctestnet/CoreBeacon.json +110 -0
  60. package/deployments/bsctestnet/CoreSource_USDT.json +1083 -0
  61. package/deployments/bsctestnet/FRVBeacon.json +110 -0
  62. package/deployments/bsctestnet/FRVSource_USDT.json +978 -0
  63. package/deployments/bsctestnet/FluxBeacon.json +110 -0
  64. package/deployments/bsctestnet/FluxSource_USDT.json +1083 -0
  65. package/deployments/bsctestnet/HubBeacon.json +110 -0
  66. package/deployments/bsctestnet/HubImpl.json +2216 -0
  67. package/deployments/bsctestnet/HubRegistry.json +386 -0
  68. package/deployments/bsctestnet/HubRegistryImpl.json +386 -0
  69. package/deployments/bsctestnet/HubRegistryProxyAdmin.json +157 -0
  70. package/deployments/bsctestnet/Hub_USDT.json +2216 -0
  71. package/deployments/bsctestnet/Migrator.json +375 -0
  72. package/deployments/bsctestnet/YieldGroupCoreImpl.json +1083 -0
  73. package/deployments/bsctestnet/YieldGroupFRVImpl.json +978 -0
  74. package/deployments/bsctestnet/YieldGroupFluxImpl.json +1083 -0
  75. package/deployments/bsctestnet_addresses.json +25 -0
  76. package/package.json +46 -0
@@ -0,0 +1,179 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.25;
3
+
4
+ import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol";
5
+
6
+ import { IHub } from "../interfaces/IHub.sol";
7
+ import { IHubRegistry } from "../interfaces/IHubRegistry.sol";
8
+
9
+ /**
10
+ * @title HubRegistry
11
+ * @author Venus
12
+ * @notice Chain-level directory of Hubs, keyed by underlying asset — the single on-chain
13
+ * discovery root for the Liquidity Hub topology. Hubs are deployed outside the
14
+ * registry (the `deploy/06_DeployAssetVault` bootstrap script) and registered via
15
+ * `addHub`; the onboarding VIP orders `addHub` before the Hub's `addYieldGroup`
16
+ * calls in one atomic transaction, so {IHubRegistry-HubAdded} lands at a lower log
17
+ * index than every `YieldGroupAdded`. That ordering is a latency optimization only —
18
+ * indexers MUST seed each Hub's YieldGroup set from state on {IHubRegistry-HubAdded}
19
+ * (see the normative indexer contract in {IHubRegistry}). The registry holds no funds
20
+ * and never routes assets.
21
+ * @dev Upgradeable singleton behind a TransparentUpgradeableProxy (unlike the per-asset Hubs,
22
+ * which share a beacon). Mutating functions are ACM-gated with their literal
23
+ * function-signature strings; the gate is the first statement of every gated function.
24
+ * No reentrancy guard: the only external call (`IHub.asset()`) is a STATICCALL — a
25
+ * state-mutating callback reverts at the EVM level — and the registry moves no assets.
26
+ *
27
+ * Storage: the inherited chain (Initializable → Context → Ownable → Ownable2Step →
28
+ * AccessControlledV8) occupies slots 0..200, so the registry's own fields start at
29
+ * absolute slot 201 (verified via `forge inspect` and pinned by the storage-layout test):
30
+ *
31
+ * Slot Field
32
+ * ---- -----
33
+ * 201 _hubForAsset
34
+ * 202 _assetForHub
35
+ * 203 _hubs
36
+ * 204 _hubIndex
37
+ * 205..250 __gap (32 × 46)
38
+ *
39
+ * Order is append-only: new fields go after `_hubIndex`, shrinking `__gap` by the same
40
+ * number of slots; never reorder, never prepend.
41
+ */
42
+ contract HubRegistry is AccessControlledV8, IHubRegistry {
43
+ // ============================== State =====================================
44
+
45
+ /// @dev Active Hub for each asset (0 = none). Exactly one Hub per asset.
46
+ mapping(address asset => address hub) internal _hubForAsset;
47
+
48
+ /// @dev Reverse lookup: the asset a Hub is registered under (0 = not registered).
49
+ /// Doubles as the `isHub` flag and lets `removeHub` clean `_hubForAsset` without
50
+ /// an external call back into the Hub.
51
+ mapping(address hub => address asset) internal _assetForHub;
52
+
53
+ /// @dev Canonical enumeration of registered Hubs.
54
+ address[] internal _hubs;
55
+
56
+ /// @dev 1-indexed position in `_hubs` (0 = not registered). Enables O(1) swap-and-pop removal.
57
+ mapping(address hub => uint256 indexPlusOne) internal _hubIndex;
58
+
59
+ /// @dev Reserved slots for upgrades. Shrink by the number of slots a new field consumes.
60
+ uint256[46] private __gap;
61
+
62
+ // ============================== Constructor ==============================
63
+
64
+ /// @custom:oz-upgrades-unsafe-allow constructor
65
+ constructor() {
66
+ _disableInitializers();
67
+ }
68
+
69
+ /**
70
+ * @notice Initialise the registry
71
+ * @param accessControlManager_ AccessControlManagerV8 instance gating the mutating functions
72
+ * @custom:access Permissionless once (OZ `initializer` modifier prevents re-init)
73
+ * @custom:error Throws ZeroAddress if accessControlManager_ is the zero address
74
+ */
75
+ function initialize(address accessControlManager_) external initializer {
76
+ if (accessControlManager_ == address(0)) revert ZeroAddress();
77
+ __AccessControlled_init(accessControlManager_);
78
+ }
79
+
80
+ // ============================== External — mutating =======================
81
+
82
+ /**
83
+ * @notice Register an externally-deployed Hub under its underlying asset
84
+ * @dev ACM role `"addHub(address)"` (VIP). The asset key is read from `IHub(hub).asset()`;
85
+ * an address that does not implement `asset()` reverts naturally (governance-vetted
86
+ * input). In an onboarding VIP, order this call BEFORE the Hub's `addYieldGroup`
87
+ * calls so {HubAdded} precedes every `YieldGroupAdded` in the same transaction; for
88
+ * Hubs configured before registration, indexers seed the YieldGroup set from state
89
+ * (`registeredYieldGroups()`). Replacing an asset's Hub is an explicit {removeHub}
90
+ * then {addHub} — two events, so indexers never observe an implicit swap.
91
+ * @param hub Hub contract address
92
+ * @custom:event HubAdded Emitted with the derived asset and the hub
93
+ * @custom:error Throws ZeroAddress if hub is the zero address
94
+ * @custom:error Throws HubAlreadyRegistered if hub is already in the registry
95
+ * @custom:error Throws AssetAlreadyHasHub if the hub's asset already has a registered Hub
96
+ */
97
+ function addHub(address hub) external {
98
+ _checkAccessAllowed("addHub(address)");
99
+ if (hub == address(0)) revert ZeroAddress();
100
+ if (_assetForHub[hub] != address(0)) revert HubAlreadyRegistered(hub);
101
+
102
+ address asset = IHub(hub).asset();
103
+ // A real Hub can never report a zero asset (its initializer rejects it); the check keeps
104
+ // the `_assetForHub[hub] != 0 ⟺ registered` invariant safe against a broken input.
105
+ if (asset == address(0)) revert ZeroAddress();
106
+ address existingHub = _hubForAsset[asset];
107
+ if (existingHub != address(0)) revert AssetAlreadyHasHub(asset, existingHub);
108
+
109
+ _hubForAsset[asset] = hub;
110
+ _assetForHub[hub] = asset;
111
+ _hubs.push(hub);
112
+ _hubIndex[hub] = _hubs.length;
113
+
114
+ emit HubAdded(asset, hub);
115
+ }
116
+
117
+ /**
118
+ * @notice Remove a Hub from the registry
119
+ * @dev ACM role `"removeHub(address)"` (VIP). Swap-and-pop keeps enumeration O(1); the
120
+ * asset mapping is cleaned via the stored reverse lookup, so removal never calls
121
+ * back into the Hub (a bricked Hub can always be evicted).
122
+ * @param hub Hub to remove
123
+ * @custom:event HubRemoved Emitted with the asset the hub was registered under and the hub
124
+ * @custom:error Throws HubNotRegistered if hub is not in the registry
125
+ */
126
+ function removeHub(address hub) external {
127
+ _checkAccessAllowed("removeHub(address)");
128
+ address asset = _assetForHub[hub];
129
+ if (asset == address(0)) revert HubNotRegistered(hub);
130
+
131
+ uint256 idxPlusOne = _hubIndex[hub];
132
+ uint256 idx;
133
+ uint256 lastIdx;
134
+ unchecked {
135
+ // Safe: the `asset == 0` guard above proves `hub` is registered, so
136
+ // `idxPlusOne >= 1` and `_hubs.length >= 1`.
137
+ idx = idxPlusOne - 1;
138
+ lastIdx = _hubs.length - 1;
139
+ }
140
+ if (idx != lastIdx) {
141
+ address last = _hubs[lastIdx];
142
+ _hubs[idx] = last;
143
+ _hubIndex[last] = idxPlusOne;
144
+ }
145
+ _hubs.pop();
146
+ delete _hubIndex[hub];
147
+ delete _assetForHub[hub];
148
+ delete _hubForAsset[asset];
149
+
150
+ emit HubRemoved(asset, hub);
151
+ }
152
+
153
+ // ============================== External — views ==========================
154
+
155
+ /// @inheritdoc IHubRegistry
156
+ function hubForAsset(address asset) external view returns (address hub) {
157
+ hub = _hubForAsset[asset];
158
+ }
159
+
160
+ /// @inheritdoc IHubRegistry
161
+ function assetForHub(address hub) external view returns (address asset) {
162
+ asset = _assetForHub[hub];
163
+ }
164
+
165
+ /// @inheritdoc IHubRegistry
166
+ function getHubs() external view returns (address[] memory hubs) {
167
+ hubs = _hubs;
168
+ }
169
+
170
+ /// @inheritdoc IHubRegistry
171
+ function getHubsCount() external view returns (uint256 count) {
172
+ count = _hubs.length;
173
+ }
174
+
175
+ /// @inheritdoc IHubRegistry
176
+ function isHub(address hub) external view returns (bool registered) {
177
+ registered = _assetForHub[hub] != address(0);
178
+ }
179
+ }
@@ -0,0 +1,336 @@
1
+ {
2
+ "address": "0x4E514a0C7aB9d140eE204dfA0017574270D92944",
3
+ "block": 112779813,
4
+ "chainId": 56,
5
+ "contract": "AdapterCoreV1",
6
+ "abi": [
7
+ {
8
+ "type": "constructor",
9
+ "inputs": [],
10
+ "stateMutability": "nonpayable"
11
+ },
12
+ {
13
+ "type": "function",
14
+ "name": "EXP_SCALE",
15
+ "inputs": [],
16
+ "outputs": [
17
+ {
18
+ "name": "",
19
+ "type": "uint256",
20
+ "internalType": "uint256"
21
+ }
22
+ ],
23
+ "stateMutability": "view"
24
+ },
25
+ {
26
+ "type": "function",
27
+ "name": "MANTISSA_ONE",
28
+ "inputs": [],
29
+ "outputs": [
30
+ {
31
+ "name": "",
32
+ "type": "uint256",
33
+ "internalType": "uint256"
34
+ }
35
+ ],
36
+ "stateMutability": "view"
37
+ },
38
+ {
39
+ "type": "function",
40
+ "name": "accrue",
41
+ "inputs": [
42
+ {
43
+ "name": "resource",
44
+ "type": "address",
45
+ "internalType": "address"
46
+ }
47
+ ],
48
+ "outputs": [],
49
+ "stateMutability": "nonpayable"
50
+ },
51
+ {
52
+ "type": "function",
53
+ "name": "asset",
54
+ "inputs": [
55
+ {
56
+ "name": "resource",
57
+ "type": "address",
58
+ "internalType": "address"
59
+ }
60
+ ],
61
+ "outputs": [
62
+ {
63
+ "name": "",
64
+ "type": "address",
65
+ "internalType": "address"
66
+ }
67
+ ],
68
+ "stateMutability": "view"
69
+ },
70
+ {
71
+ "type": "function",
72
+ "name": "deposit",
73
+ "inputs": [
74
+ {
75
+ "name": "resource",
76
+ "type": "address",
77
+ "internalType": "address"
78
+ },
79
+ {
80
+ "name": "amount",
81
+ "type": "uint256",
82
+ "internalType": "uint256"
83
+ }
84
+ ],
85
+ "outputs": [
86
+ {
87
+ "name": "deposited",
88
+ "type": "uint256",
89
+ "internalType": "uint256"
90
+ }
91
+ ],
92
+ "stateMutability": "nonpayable"
93
+ },
94
+ {
95
+ "type": "function",
96
+ "name": "maxDeposit",
97
+ "inputs": [
98
+ {
99
+ "name": "resource",
100
+ "type": "address",
101
+ "internalType": "address"
102
+ }
103
+ ],
104
+ "outputs": [
105
+ {
106
+ "name": "",
107
+ "type": "uint256",
108
+ "internalType": "uint256"
109
+ }
110
+ ],
111
+ "stateMutability": "view"
112
+ },
113
+ {
114
+ "type": "function",
115
+ "name": "maxWithdraw",
116
+ "inputs": [
117
+ {
118
+ "name": "resource",
119
+ "type": "address",
120
+ "internalType": "address"
121
+ },
122
+ {
123
+ "name": "holder",
124
+ "type": "address",
125
+ "internalType": "address"
126
+ }
127
+ ],
128
+ "outputs": [
129
+ {
130
+ "name": "",
131
+ "type": "uint256",
132
+ "internalType": "uint256"
133
+ }
134
+ ],
135
+ "stateMutability": "view"
136
+ },
137
+ {
138
+ "type": "function",
139
+ "name": "receiptBalance",
140
+ "inputs": [
141
+ {
142
+ "name": "resource",
143
+ "type": "address",
144
+ "internalType": "address"
145
+ },
146
+ {
147
+ "name": "holder",
148
+ "type": "address",
149
+ "internalType": "address"
150
+ }
151
+ ],
152
+ "outputs": [
153
+ {
154
+ "name": "",
155
+ "type": "uint256",
156
+ "internalType": "uint256"
157
+ }
158
+ ],
159
+ "stateMutability": "view"
160
+ },
161
+ {
162
+ "type": "function",
163
+ "name": "spotAPYBps",
164
+ "inputs": [
165
+ {
166
+ "name": "resource",
167
+ "type": "address",
168
+ "internalType": "address"
169
+ },
170
+ {
171
+ "name": "blocksPerYear",
172
+ "type": "uint256",
173
+ "internalType": "uint256"
174
+ }
175
+ ],
176
+ "outputs": [
177
+ {
178
+ "name": "",
179
+ "type": "uint64",
180
+ "internalType": "uint64"
181
+ }
182
+ ],
183
+ "stateMutability": "view"
184
+ },
185
+ {
186
+ "type": "function",
187
+ "name": "totalAssets",
188
+ "inputs": [
189
+ {
190
+ "name": "resource",
191
+ "type": "address",
192
+ "internalType": "address"
193
+ },
194
+ {
195
+ "name": "holder",
196
+ "type": "address",
197
+ "internalType": "address"
198
+ }
199
+ ],
200
+ "outputs": [
201
+ {
202
+ "name": "",
203
+ "type": "uint256",
204
+ "internalType": "uint256"
205
+ }
206
+ ],
207
+ "stateMutability": "view"
208
+ },
209
+ {
210
+ "type": "function",
211
+ "name": "validateRegistration",
212
+ "inputs": [
213
+ {
214
+ "name": "resource",
215
+ "type": "address",
216
+ "internalType": "address"
217
+ }
218
+ ],
219
+ "outputs": [],
220
+ "stateMutability": "view"
221
+ },
222
+ {
223
+ "type": "function",
224
+ "name": "withdraw",
225
+ "inputs": [
226
+ {
227
+ "name": "resource",
228
+ "type": "address",
229
+ "internalType": "address"
230
+ },
231
+ {
232
+ "name": "amount",
233
+ "type": "uint256",
234
+ "internalType": "uint256"
235
+ },
236
+ {
237
+ "name": "to",
238
+ "type": "address",
239
+ "internalType": "address"
240
+ }
241
+ ],
242
+ "outputs": [],
243
+ "stateMutability": "nonpayable"
244
+ },
245
+ {
246
+ "type": "error",
247
+ "name": "NotDelegateCall",
248
+ "inputs": []
249
+ },
250
+ {
251
+ "type": "error",
252
+ "name": "TreasuryFeeUnsupported",
253
+ "inputs": [
254
+ {
255
+ "name": "resource",
256
+ "type": "address",
257
+ "internalType": "address"
258
+ },
259
+ {
260
+ "name": "treasuryPercent",
261
+ "type": "uint256",
262
+ "internalType": "uint256"
263
+ }
264
+ ]
265
+ },
266
+ {
267
+ "type": "error",
268
+ "name": "VTokenAccrueFailed",
269
+ "inputs": [
270
+ {
271
+ "name": "resource",
272
+ "type": "address",
273
+ "internalType": "address"
274
+ },
275
+ {
276
+ "name": "errorCode",
277
+ "type": "uint256",
278
+ "internalType": "uint256"
279
+ }
280
+ ]
281
+ },
282
+ {
283
+ "type": "error",
284
+ "name": "VTokenMintFailed",
285
+ "inputs": [
286
+ {
287
+ "name": "resource",
288
+ "type": "address",
289
+ "internalType": "address"
290
+ },
291
+ {
292
+ "name": "errorCode",
293
+ "type": "uint256",
294
+ "internalType": "uint256"
295
+ }
296
+ ]
297
+ },
298
+ {
299
+ "type": "error",
300
+ "name": "VTokenRedeemFailed",
301
+ "inputs": [
302
+ {
303
+ "name": "resource",
304
+ "type": "address",
305
+ "internalType": "address"
306
+ },
307
+ {
308
+ "name": "errorCode",
309
+ "type": "uint256",
310
+ "internalType": "uint256"
311
+ }
312
+ ]
313
+ },
314
+ {
315
+ "type": "error",
316
+ "name": "VTokenUnderfilled",
317
+ "inputs": [
318
+ {
319
+ "name": "resource",
320
+ "type": "address",
321
+ "internalType": "address"
322
+ },
323
+ {
324
+ "name": "requested",
325
+ "type": "uint256",
326
+ "internalType": "uint256"
327
+ },
328
+ {
329
+ "name": "actual",
330
+ "type": "uint256",
331
+ "internalType": "uint256"
332
+ }
333
+ ]
334
+ }
335
+ ]
336
+ }