@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.
- package/LICENSE +98 -0
- package/README.md +422 -0
- package/contracts/Hub/Hub.sol +1614 -0
- package/contracts/Hub/HubStorage.sol +106 -0
- package/contracts/Hub/lib/HubAdminLib.sol +292 -0
- package/contracts/YieldGroup/YieldGroup.sol +128 -0
- package/contracts/YieldGroup/YieldGroupFRV.sol +107 -0
- package/contracts/YieldGroup/base/YieldGroupBase.sol +797 -0
- package/contracts/YieldGroup/base/YieldGroupBaseStorage.sol +73 -0
- package/contracts/adapters/AdapterCoreV1.sol +309 -0
- package/contracts/adapters/AdapterFRV.sol +276 -0
- package/contracts/adapters/AdapterFlux.sol +173 -0
- package/contracts/interfaces/IHub.sol +551 -0
- package/contracts/interfaces/IHubRegistry.sol +120 -0
- package/contracts/interfaces/IMigrator.sol +134 -0
- package/contracts/interfaces/IResourceAdapter.sol +154 -0
- package/contracts/interfaces/IYieldGroup.sol +119 -0
- package/contracts/interfaces/IYieldGroupBase.sol +507 -0
- package/contracts/interfaces/IYieldGroupFRV.sol +61 -0
- package/contracts/interfaces/external/IComptroller.sol +57 -0
- package/contracts/interfaces/external/IFRVVault.sol +113 -0
- package/contracts/interfaces/external/IFToken.sol +46 -0
- package/contracts/interfaces/external/IFluidLendingResolver.sol +63 -0
- package/contracts/interfaces/external/IVBep20.sol +39 -0
- package/contracts/interfaces/external/IVToken.sol +125 -0
- package/contracts/interfaces/external/IWBNB.sol +16 -0
- package/contracts/migrator/Migrator.sol +182 -0
- package/contracts/registry/HubRegistry.sol +179 -0
- package/deployments/bscmainnet/AdapterCoreV1.json +336 -0
- package/deployments/bscmainnet/AdapterFRV.json +225 -0
- package/deployments/bscmainnet/AdapterFlux.json +249 -0
- package/deployments/bscmainnet/CoreBeacon.json +110 -0
- package/deployments/bscmainnet/CoreSource_U.json +1083 -0
- package/deployments/bscmainnet/CoreSource_USDC.json +1083 -0
- package/deployments/bscmainnet/CoreSource_USDT.json +1083 -0
- package/deployments/bscmainnet/FRVBeacon.json +110 -0
- package/deployments/bscmainnet/FRVSource_U.json +978 -0
- package/deployments/bscmainnet/FRVSource_USDC.json +978 -0
- package/deployments/bscmainnet/FRVSource_USDT.json +978 -0
- package/deployments/bscmainnet/FluxBeacon.json +110 -0
- package/deployments/bscmainnet/FluxSource_U.json +1083 -0
- package/deployments/bscmainnet/FluxSource_USDC.json +1083 -0
- package/deployments/bscmainnet/FluxSource_USDT.json +1083 -0
- package/deployments/bscmainnet/HubBeacon.json +110 -0
- package/deployments/bscmainnet/HubImpl.json +2216 -0
- package/deployments/bscmainnet/HubRegistry.json +386 -0
- package/deployments/bscmainnet/HubRegistryImpl.json +386 -0
- package/deployments/bscmainnet/Hub_U.json +2216 -0
- package/deployments/bscmainnet/Hub_USDC.json +2216 -0
- package/deployments/bscmainnet/Hub_USDT.json +2216 -0
- package/deployments/bscmainnet/Migrator.json +375 -0
- package/deployments/bscmainnet/YieldGroupCoreImpl.json +1083 -0
- package/deployments/bscmainnet/YieldGroupFRVImpl.json +978 -0
- package/deployments/bscmainnet/YieldGroupFluxImpl.json +1083 -0
- package/deployments/bscmainnet_addresses.json +32 -0
- package/deployments/bsctestnet/AdapterCoreV1.json +336 -0
- package/deployments/bsctestnet/AdapterFRV.json +225 -0
- package/deployments/bsctestnet/AdapterFlux.json +249 -0
- package/deployments/bsctestnet/CoreBeacon.json +110 -0
- package/deployments/bsctestnet/CoreSource_USDT.json +1083 -0
- package/deployments/bsctestnet/FRVBeacon.json +110 -0
- package/deployments/bsctestnet/FRVSource_USDT.json +978 -0
- package/deployments/bsctestnet/FluxBeacon.json +110 -0
- package/deployments/bsctestnet/FluxSource_USDT.json +1083 -0
- package/deployments/bsctestnet/HubBeacon.json +110 -0
- package/deployments/bsctestnet/HubImpl.json +2216 -0
- package/deployments/bsctestnet/HubRegistry.json +386 -0
- package/deployments/bsctestnet/HubRegistryImpl.json +386 -0
- package/deployments/bsctestnet/HubRegistryProxyAdmin.json +157 -0
- package/deployments/bsctestnet/Hub_USDT.json +2216 -0
- package/deployments/bsctestnet/Migrator.json +375 -0
- package/deployments/bsctestnet/YieldGroupCoreImpl.json +1083 -0
- package/deployments/bsctestnet/YieldGroupFRVImpl.json +978 -0
- package/deployments/bsctestnet/YieldGroupFluxImpl.json +1083 -0
- package/deployments/bsctestnet_addresses.json +25 -0
- package/package.json +46 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IYieldGroupBase
|
|
6
|
+
* @author Venus
|
|
7
|
+
* @notice Boundary between Hub and the concrete typed routers (the generic `YieldGroup` serving
|
|
8
|
+
* Core / Flux, `YieldGroupFRV`, ...). Hub depends only on this interface; it never reaches
|
|
9
|
+
* past a YieldGroup into the underlying market (vToken / Fluid / FRV).
|
|
10
|
+
* @dev This interface also declares the registry / queue / pause surface and the struct, events and
|
|
11
|
+
* errors shared by every concrete YieldGroup, so the shared `YieldGroupBase` implementation can
|
|
12
|
+
* live in one place. Family-specific surface (the per-resource cap + `blocksPerYear` for the
|
|
13
|
+
* generic; `forceRemoveResource` for FRV) lives in the leaf interfaces `IYieldGroup` and
|
|
14
|
+
* `IYieldGroupFRV`. Conventions:
|
|
15
|
+
* - All amounts are denominated in `asset()` units (no share conversion at this layer).
|
|
16
|
+
* - `deposit` / `withdraw` (and their `*Resource` variants) are Hub-only; implementers
|
|
17
|
+
* gate them with an `onlyHub` modifier. Hub pulls via safeTransferFrom, so Hub must
|
|
18
|
+
* approve this YieldGroup before calling `deposit`.
|
|
19
|
+
* - `withdraw` pushes the underlying directly to the supplied `to` address (rather than
|
|
20
|
+
* always to `msg.sender`) so future Hub optimisations can route to the end-receiver
|
|
21
|
+
* without an interface rev. Today Hub passes `address(this)`.
|
|
22
|
+
* - `deposit` returns the actually-placed amount, which may be LESS than the requested
|
|
23
|
+
* `amount` (partial fill). Implementers place what their inner queue can absorb, refund
|
|
24
|
+
* the unplaced remainder to the caller (the Hub) via `asset` transfer, and return the
|
|
25
|
+
* placed amount. The Hub's auto-routing cascade treats the return value as the placed
|
|
26
|
+
* amount and routes the refunded remainder to the next YieldGroup. `reallocate`, which
|
|
27
|
+
* moves an exact named amount, keeps its own `deposited == amount` exact-fill check and
|
|
28
|
+
* reverts {YieldGroupUnderfilled} on a short fill — so partial fills are only ever
|
|
29
|
+
* consumed by the auto-route, never by reallocation.
|
|
30
|
+
* Fee-on-transfer underlyings are unsupported and fail closed *inside* the deposit:
|
|
31
|
+
* the inner queue routes against the requested amount, so a token that skims the
|
|
32
|
+
* transfer leaves the YieldGroup short and the downstream mint reverts.
|
|
33
|
+
* - `withdraw` has no return value: it MUST deliver exactly `amount` to `to` or revert.
|
|
34
|
+
* Hub defensively measures the resulting balance delta rather than trusting a
|
|
35
|
+
* self-reported number; tighter than the prior return-value convention.
|
|
36
|
+
* - YieldGroups may hold assets across multiple internal vaults and route via their own
|
|
37
|
+
* inner queue; that detail is opaque to Hub.
|
|
38
|
+
*/
|
|
39
|
+
interface IYieldGroupBase {
|
|
40
|
+
// -------------------------------- Types ----------------------------------
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @notice Per-resource configuration.
|
|
44
|
+
* @dev The optional per-resource deposit cap lives in a separate mapping (see
|
|
45
|
+
* {IYieldGroup.resourceCap}), not in this struct, so adding it did not disturb the
|
|
46
|
+
* struct's single-slot packing. The `paused` flag enables a per-resource pause scope even
|
|
47
|
+
* when only one resource is registered. The `adapter` field is the `IResourceAdapter`
|
|
48
|
+
* implementation that translates between this YieldGroup and the resource's
|
|
49
|
+
* protocol-specific ABI; adapters are stateless and typically shared across every
|
|
50
|
+
* YieldGroup registering the same protocol family.
|
|
51
|
+
* @param registered True iff present in the resource registry.
|
|
52
|
+
* @param paused When true, routing skips this resource; existing balance still counts.
|
|
53
|
+
* @param adapter `IResourceAdapter` implementation handling this resource's ABI.
|
|
54
|
+
*/
|
|
55
|
+
struct ResourceConfig {
|
|
56
|
+
bool registered;
|
|
57
|
+
bool paused;
|
|
58
|
+
address adapter;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// -------------------------------- Events ----------------------------------
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @notice A new yield resource was registered alongside the adapter that handles it.
|
|
65
|
+
* @param resource Newly-registered resource address (e.g. a Venus vToken).
|
|
66
|
+
* @param adapter `IResourceAdapter` implementation translating this resource's ABI.
|
|
67
|
+
*/
|
|
68
|
+
event ResourceAdded(address indexed resource, address indexed adapter);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @notice A registered resource was removed.
|
|
72
|
+
* @param resource Removed resource address.
|
|
73
|
+
*/
|
|
74
|
+
event ResourceRemoved(address indexed resource);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @notice A registered resource's adapter was repointed without changing the resource itself.
|
|
78
|
+
* @param resource Resource whose adapter changed; its held balance and registry slot are kept.
|
|
79
|
+
* @param oldAdapter Previous `IResourceAdapter` implementation.
|
|
80
|
+
* @param newAdapter New `IResourceAdapter` implementation now handling this resource's ABI.
|
|
81
|
+
*/
|
|
82
|
+
event ResourceAdapterUpdated(address indexed resource, address indexed oldAdapter, address indexed newAdapter);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @notice A resource's pause flag flipped.
|
|
86
|
+
* @param resource Resource whose flag flipped.
|
|
87
|
+
* @param paused New pause state.
|
|
88
|
+
*/
|
|
89
|
+
event ResourcePauseToggled(address indexed resource, bool paused);
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @notice Inner deposit-queue order replaced.
|
|
93
|
+
* @param queue Full replacement queue in deposit-priority order.
|
|
94
|
+
*/
|
|
95
|
+
event InnerDepositQueueSet(address[] queue);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @notice Inner withdraw-queue order replaced.
|
|
99
|
+
* @param queue Full replacement queue in withdraw-priority order.
|
|
100
|
+
*/
|
|
101
|
+
event InnerWithdrawQueueSet(address[] queue);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @notice Underlying placed into a resource on a deposit / reallocate-push leg.
|
|
105
|
+
* @param resource Resource that received the deposit.
|
|
106
|
+
* @param amount Underlying units placed.
|
|
107
|
+
*/
|
|
108
|
+
event DepositRouted(address indexed resource, uint256 amount);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @notice Underlying pulled from a resource on a withdraw / reallocate-pull leg.
|
|
112
|
+
* @param resource Resource that was pulled from.
|
|
113
|
+
* @param amount Underlying units pulled.
|
|
114
|
+
*/
|
|
115
|
+
event WithdrawRouted(address indexed resource, uint256 amount);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @notice A cascade leg was skipped because its mutating dispatch reverted (e.g. the resource's
|
|
119
|
+
* external protocol paused it or rejected the leg). The operation routes around it
|
|
120
|
+
* instead of bubbling the revert; a healthy system emits this zero times.
|
|
121
|
+
* @param resource Resource whose deposit / withdraw leg was skipped.
|
|
122
|
+
* @param isDeposit True if the skipped leg was a deposit, false for a withdraw.
|
|
123
|
+
*/
|
|
124
|
+
event ResourceSkipped(address indexed resource, bool isDeposit);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @notice A resource's accrual poke reverted and was skipped so it does not brick the Hub's fee accrual; a healthy system emits this zero times.
|
|
128
|
+
* @param resource Resource whose accrual poke reverted.
|
|
129
|
+
* @param adapter Adapter registered for that resource.
|
|
130
|
+
*/
|
|
131
|
+
event ResourceAccrualFailed(address indexed resource, address indexed adapter);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @notice A stray non-asset, non-receipt ERC-20 balance was rescued via `sweep()`.
|
|
135
|
+
* @param token ERC-20 that was swept.
|
|
136
|
+
* @param to Recipient of the balance.
|
|
137
|
+
* @param amount Amount transferred.
|
|
138
|
+
*/
|
|
139
|
+
event Swept(address indexed token, address indexed to, uint256 amount);
|
|
140
|
+
|
|
141
|
+
// -------------------------------- Errors ----------------------------------
|
|
142
|
+
|
|
143
|
+
/// @notice Caller of an `onlyHub` function was not the registered Hub.
|
|
144
|
+
/// @param caller The non-Hub address that called the function.
|
|
145
|
+
error NotHub(address caller);
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @notice Caller lacked the ACM role required for the called function.
|
|
149
|
+
* @param caller Address whose ACM lookup failed.
|
|
150
|
+
* @param signature ACM role string that was checked.
|
|
151
|
+
*/
|
|
152
|
+
error Unauthorized(address caller, string signature);
|
|
153
|
+
|
|
154
|
+
/// @notice A `dispatch*External` self-call wrapper was invoked by anyone other than the
|
|
155
|
+
/// YieldGroup itself. These wrappers exist only so the inner cascade can `try/catch`
|
|
156
|
+
/// the adapter delegatecall; they are not part of the external API.
|
|
157
|
+
error OnlySelf();
|
|
158
|
+
|
|
159
|
+
/// @notice Required non-zero address parameter was zero.
|
|
160
|
+
error ZeroAddress();
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @notice The asset declared in `initialize` does not match the Hub's `asset()`.
|
|
164
|
+
* @param declared Asset address passed at initialisation.
|
|
165
|
+
* @param hubAsset Asset address reported by the Hub.
|
|
166
|
+
*/
|
|
167
|
+
error HubAssetMismatch(address declared, address hubAsset);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @notice A registered resource's asset (as reported by its adapter's `asset(resource)`) does
|
|
171
|
+
* not match this YieldGroup's asset.
|
|
172
|
+
* @param resource Resource under inspection.
|
|
173
|
+
* @param resourceAsset Asset the adapter reported for the resource.
|
|
174
|
+
* @param expected This YieldGroup's `assetToken`.
|
|
175
|
+
*/
|
|
176
|
+
error ResourceAssetMismatch(address resource, address resourceAsset, address expected);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @notice `addResource()` called with an already-registered resource.
|
|
180
|
+
* @param resource Address that is already registered.
|
|
181
|
+
*/
|
|
182
|
+
error ResourceAlreadyRegistered(address resource);
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @notice Operation targeted a resource that is not in the registry.
|
|
186
|
+
* @param resource Address that failed the registry lookup.
|
|
187
|
+
*/
|
|
188
|
+
error ResourceNotRegistered(address resource);
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @notice `removeResource()` called while the resource still holds a non-zero balance.
|
|
192
|
+
* @param resource Resource attempted to be removed.
|
|
193
|
+
* @param balance Current resource balance preventing removal.
|
|
194
|
+
*/
|
|
195
|
+
error ResourceHasBalance(address resource, uint256 balance);
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @notice A deposit (passive cascade or targeted `depositResource`) was routed to a paused
|
|
199
|
+
* resource. Pause / unpause toggles are idempotent no-ops, so this fires only on the
|
|
200
|
+
* deposit path — never for a redundant pause-state change.
|
|
201
|
+
* @param resource The paused resource the deposit targeted.
|
|
202
|
+
*/
|
|
203
|
+
error ResourceIsPaused(address resource);
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @notice Deposit exceeded aggregate spare capacity across the inner deposit queue.
|
|
207
|
+
* @param requested Underlying units requested.
|
|
208
|
+
* @param maxAvailable Aggregate spare capacity at revert time.
|
|
209
|
+
*/
|
|
210
|
+
error ResourceCapacityExceeded(uint256 requested, uint256 maxAvailable);
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @notice Withdraw exceeded aggregate liquid funds across the inner withdraw queue.
|
|
214
|
+
* @param requested Underlying units requested.
|
|
215
|
+
* @param available Aggregate liquid funds at revert time.
|
|
216
|
+
*/
|
|
217
|
+
error ResourceLiquidityInsufficient(uint256 requested, uint256 available);
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @notice The supplied resource address is not a contract (code length zero). Catches
|
|
221
|
+
* registering an EOA as a resource, which would make the adapter's `asset()` probe
|
|
222
|
+
* revert opaquely on the empty-returndata decode.
|
|
223
|
+
* @param resource Offending resource address.
|
|
224
|
+
*/
|
|
225
|
+
error ResourceNotContract(address resource);
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @notice The supplied adapter address is not a contract (code length zero). Catches
|
|
229
|
+
* registering an EOA, which would cause `delegatecall` to silently succeed
|
|
230
|
+
* without doing anything and orphan the deposit.
|
|
231
|
+
* @param adapter Offending adapter address.
|
|
232
|
+
*/
|
|
233
|
+
error AdapterNotContract(address adapter);
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* @notice The adapter completed a delegatecall but its observed effect did not match the
|
|
237
|
+
* requested amount (e.g. `withdraw` did not transfer the full amount). Indicates an
|
|
238
|
+
* adapter bug; the underlying protocol's own revert is bubbled up directly when it fires.
|
|
239
|
+
* @param resource Resource the adapter was acting on.
|
|
240
|
+
* @param requested Amount the YieldGroup asked the adapter to move.
|
|
241
|
+
* @param actual Amount the YieldGroup observed.
|
|
242
|
+
*/
|
|
243
|
+
error AdapterUnderfilled(address resource, uint256 requested, uint256 actual);
|
|
244
|
+
|
|
245
|
+
/// @notice Queue contains duplicates or references an unregistered resource.
|
|
246
|
+
error InvalidQueue();
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @notice Withdraw queue omits a resource still holding receipt tokens, which the cascade would
|
|
250
|
+
* then not visit.
|
|
251
|
+
* @param resource The funded resource missing from the queue.
|
|
252
|
+
*/
|
|
253
|
+
error WithdrawQueueOmitsFundedResource(address resource);
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @notice A targeted deposit fell below a resource's minimum deposit floor without being the
|
|
257
|
+
* exact residual tail. Only thrown by subclasses that impose a per-resource minimum
|
|
258
|
+
* (e.g. FRV's `minSupplierDeposit`); for families with no floor this never fires.
|
|
259
|
+
* @param resource Resource the deposit targeted.
|
|
260
|
+
* @param requested Underlying units requested.
|
|
261
|
+
* @param minimum The resource's minimum deposit floor.
|
|
262
|
+
*/
|
|
263
|
+
error ResourceBelowMinimumDeposit(address resource, uint256 requested, uint256 minimum);
|
|
264
|
+
|
|
265
|
+
/// @notice `sweep()` was called with `token == asset()`; the asset is counted as idle in
|
|
266
|
+
/// `totalAssets` and must never be sweepable.
|
|
267
|
+
error SweepProtectedAsset();
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @notice `sweep()` targeted a registered resource. In every family the resource address is the
|
|
271
|
+
* receipt token backing `totalAssets`, so sweeping it would drain the YieldGroup.
|
|
272
|
+
* @param resource The registered resource that may not be swept.
|
|
273
|
+
*/
|
|
274
|
+
error SweepProtectedResource(address resource);
|
|
275
|
+
|
|
276
|
+
// -------------------------------- Mutating -------------------------------
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @notice Move `amount` of `asset` from caller into this YieldGroup's vaults via the inner
|
|
280
|
+
* deposit queue. Pulls via safeTransferFrom; caller must have approved.
|
|
281
|
+
* @param amount Requested amount in asset units.
|
|
282
|
+
* @return deposited Amount actually placed across vaults; may be LESS than `amount` when the
|
|
283
|
+
* inner queue cannot absorb it all. The unplaced remainder is refunded to the caller
|
|
284
|
+
* and the Hub's auto-route places it elsewhere. `reallocate` instead asserts
|
|
285
|
+
* `deposited == amount` at its own call site and reverts on a short fill.
|
|
286
|
+
*/
|
|
287
|
+
function deposit(uint256 amount) external returns (uint256 deposited);
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @notice Pull `amount` of `asset` from this YieldGroup's vaults via the inner withdraw queue
|
|
291
|
+
* and send it to `to`.
|
|
292
|
+
* @dev Must deliver exactly `amount` to `to` or revert; no partial fill. Hub measures the
|
|
293
|
+
* balance delta on `to` to defensively verify delivery.
|
|
294
|
+
* @param amount Requested amount in asset units.
|
|
295
|
+
* @param to Recipient of the withdrawn assets.
|
|
296
|
+
*/
|
|
297
|
+
function withdraw(uint256 amount, address to) external;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* @notice Settle accrued interest across this YieldGroup's resources so a following
|
|
301
|
+
* {totalAssets} read values them at a fresh exchange rate.
|
|
302
|
+
* @dev `onlyHub`. The Hub calls this once per block before reading NAV for fee accrual, so
|
|
303
|
+
* the fee is charged on interest-current value rather than a one-cycle-stale one. The
|
|
304
|
+
* freshening is best-effort: a resource whose accrual reverts is left at its last-settled
|
|
305
|
+
* rate (still valid for `totalAssets`) instead of bricking the whole accrual. Block-lazy
|
|
306
|
+
* families (Core) do real work; Flux and FRV implement it as a no-op.
|
|
307
|
+
*/
|
|
308
|
+
function accrue() external;
|
|
309
|
+
|
|
310
|
+
// ------------------- Mutating (Operator reallocation) --------------------
|
|
311
|
+
// The two functions below address a SPECIFIC resource inside the YieldGroup, bypassing the
|
|
312
|
+
// inner queue. They exist solely for Hub-orchestrated `reallocate`. The Hub treats
|
|
313
|
+
// `resource` as an opaque address it does not interpret — the YieldGroup validates it against
|
|
314
|
+
// its own registry. This is the one place the Hub names something below the YieldGroup
|
|
315
|
+
// boundary; the rest of IYieldGroupBase stays resource-agnostic.
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* @notice Deposit `amount` of `asset` into a SPECIFIC resource within this YieldGroup,
|
|
319
|
+
* bypassing the inner deposit queue. Pulls via safeTransferFrom; caller must have
|
|
320
|
+
* approved.
|
|
321
|
+
* @dev `onlyHub`. Unlike {deposit} (which cascades through the inner queue), this routes
|
|
322
|
+
* the full amount into the single named `resource` and reverts if that resource
|
|
323
|
+
* cannot accept exactly `amount` — capacity, pause, or lifecycle state. Reverting on
|
|
324
|
+
* under-placement is required: the Hub's reallocate net-zero invariant relies on each
|
|
325
|
+
* leg moving its stated amount in full.
|
|
326
|
+
* @param resource Target resource (e.g. a specific vToken or FRV vault) to deposit into.
|
|
327
|
+
* @param amount Requested amount in asset units. Placed in full or the call reverts.
|
|
328
|
+
* @return deposited Amount placed; equals `amount` on success.
|
|
329
|
+
*/
|
|
330
|
+
function depositResource(address resource, uint256 amount) external returns (uint256 deposited);
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @notice Withdraw `amount` of `asset` from a SPECIFIC resource within this YieldGroup,
|
|
334
|
+
* bypassing the inner withdraw queue, and send it to `to`.
|
|
335
|
+
* @dev `onlyHub`. Unlike {withdraw}, this does NOT consume idle balance first and does NOT
|
|
336
|
+
* cascade — it redeems exactly `amount` from the single named `resource` or reverts
|
|
337
|
+
* (insufficient liquidity, or a locked lifecycle state for FRV). Must deliver exactly
|
|
338
|
+
* `amount` to `to`; pulling from a paused resource is permitted (wind-down).
|
|
339
|
+
* @param resource Target resource to pull from.
|
|
340
|
+
* @param amount Requested amount in asset units. Delivered in full or the call reverts.
|
|
341
|
+
* @param to Recipient of the withdrawn assets.
|
|
342
|
+
*/
|
|
343
|
+
function withdrawResource(address resource, uint256 amount, address to) external;
|
|
344
|
+
|
|
345
|
+
// ----------------------- Registry (governance) ---------------------------
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* @notice Register a new yield resource alongside the stateless adapter that handles its
|
|
349
|
+
* ABI. The same adapter address may be reused for any number of resources in the
|
|
350
|
+
* same protocol family (one deployment of `AdapterCoreV1` serves every Core V1
|
|
351
|
+
* vToken on every YieldGroup, for example).
|
|
352
|
+
* @dev ACM role `"addResource(address,address)"` (VIP). Validates:
|
|
353
|
+
* - `adapter` is a contract (not an EOA) — delegatecall to an EOA returns success
|
|
354
|
+
* with empty data and would silently no-op every routing call.
|
|
355
|
+
* - `adapter.asset(resource) == asset()` — the resource speaks the YieldGroup's
|
|
356
|
+
* underlying currency.
|
|
357
|
+
* Does not auto-append to either inner queue. Emits {ResourceAdded}.
|
|
358
|
+
* @param resource Resource address to register (e.g. a Venus vToken).
|
|
359
|
+
* @param adapter `IResourceAdapter` implementation handling this resource's ABI.
|
|
360
|
+
*/
|
|
361
|
+
function addResource(address resource, address adapter) external;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* @notice Remove a registered resource. Requires zero balance held in that resource.
|
|
365
|
+
* @dev ACM role `"removeResource(address)"` (VIP). Also removes the resource from both
|
|
366
|
+
* inner queues via swap-and-pop. Emits {ResourceRemoved}.
|
|
367
|
+
* @param resource Resource to remove.
|
|
368
|
+
*/
|
|
369
|
+
function removeResource(address resource) external;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* @notice Repoint a registered resource at a new adapter, keeping its balance and registry
|
|
373
|
+
* slot intact. Intended for the case where the underlying yield source is still
|
|
374
|
+
* functioning but the adapter needs a fix or must follow an underlying-protocol
|
|
375
|
+
* upgrade — `removeResource` is balance-gated and cannot serve that without first
|
|
376
|
+
* draining the position.
|
|
377
|
+
* @dev ACM role `"updateResourceAdapter(address,address)"` (VIP). Re-runs the same checks as
|
|
378
|
+
* `addResource` against `newAdapter` (contract-not-EOA, `asset(resource) == asset()`,
|
|
379
|
+
* `validateRegistration`); pause flag, cap, and queue position are unchanged. Emits
|
|
380
|
+
* {ResourceAdapterUpdated}.
|
|
381
|
+
* @param resource Registered resource to repoint.
|
|
382
|
+
* @param newAdapter New `IResourceAdapter` implementation handling this resource's ABI.
|
|
383
|
+
*/
|
|
384
|
+
function updateResourceAdapter(address resource, address newAdapter) external;
|
|
385
|
+
|
|
386
|
+
// ----------------------- Inner queues (Operator) -------------------------
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @notice Replace the inner deposit-routing order.
|
|
390
|
+
* @dev ACM role `"setInnerDepositQueue(address[])"` (Operator or VIP). Every entry must be
|
|
391
|
+
* a registered resource; duplicates revert. Emits {InnerDepositQueueSet}. Operator
|
|
392
|
+
* responsibility: keep every queue member in the inner withdraw queue. A member absent from
|
|
393
|
+
* the withdraw queue is funded by deposits but not reached by the withdraw cascade until it
|
|
394
|
+
* is (re)added there; recovery is one tx and lossless. Not enforced on-chain.
|
|
395
|
+
* @param queue Full replacement queue in deposit-priority order.
|
|
396
|
+
*/
|
|
397
|
+
function setInnerDepositQueue(address[] calldata queue) external;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* @notice Replace the inner withdraw-routing order.
|
|
401
|
+
* @dev ACM role `"setInnerWithdrawQueue(address[])"` (Operator or VIP). Independent of the
|
|
402
|
+
* deposit queue. Emits {InnerWithdrawQueueSet}.
|
|
403
|
+
* @param queue Full replacement queue in withdraw-priority order.
|
|
404
|
+
*/
|
|
405
|
+
function setInnerWithdrawQueue(address[] calldata queue) external;
|
|
406
|
+
|
|
407
|
+
// ----------------------- Pause (asymmetric) ------------------------------
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @notice Pause routing to a specific resource. Existing balance stays counted.
|
|
411
|
+
* @dev ACM role `"pauseResource(address)"` (Operator or VIP). Emits {ResourcePauseToggled}.
|
|
412
|
+
* @param resource Resource to pause.
|
|
413
|
+
*/
|
|
414
|
+
function pauseResource(address resource) external;
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* @notice Unpause routing to a specific resource.
|
|
418
|
+
* @dev ACM role `"unpauseResource(address)"` (VIP). Emits {ResourcePauseToggled}.
|
|
419
|
+
* @param resource Resource to unpause.
|
|
420
|
+
*/
|
|
421
|
+
function unpauseResource(address resource) external;
|
|
422
|
+
|
|
423
|
+
// ------------------------------- Sweep (governance) ----------------------
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* @notice Forward this YieldGroup's full balance of a stray ERC-20 to `to`, recovering rewards,
|
|
427
|
+
* airdrops, or mistransfers that would otherwise be locked. Mirrors the Hub's `sweep`.
|
|
428
|
+
* @dev ACM role `"sweep(address,address)"` (VIP). Bars `asset()` (counted as idle in `totalAssets`)
|
|
429
|
+
* and every registered resource (the receipt token backing NAV in all current families), so
|
|
430
|
+
* no value-bearing token is sweepable. Zero balance is a silent no-op; emits {Swept} otherwise.
|
|
431
|
+
* @param token Stray ERC-20 to sweep; neither `asset()` nor a registered resource.
|
|
432
|
+
* @param to Recipient of the balance.
|
|
433
|
+
*/
|
|
434
|
+
function sweep(address token, address to) external;
|
|
435
|
+
|
|
436
|
+
// ---------------------------------- Views --------------------------------
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* @notice The ERC-20 asset this YieldGroup accepts. Must match `Hub.asset()`.
|
|
440
|
+
* @return underlying Address of the asset token.
|
|
441
|
+
*/
|
|
442
|
+
function asset() external view returns (address underlying);
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* @notice Sum of `asset` held across all of this YieldGroup's underlying vaults.
|
|
446
|
+
* @dev Hub sums `totalAssets()` over its registered YieldGroups to compute its own totalAssets().
|
|
447
|
+
* @return total Asset units currently held by the YieldGroup.
|
|
448
|
+
*/
|
|
449
|
+
function totalAssets() external view returns (uint256 total);
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* @notice Aggregate remaining deposit capacity across this YieldGroup's underlying vaults.
|
|
453
|
+
* @dev Must satisfy: `deposit(maxDeposit())` does not revert (EIP-4626 honesty contract,
|
|
454
|
+
* transitively applied to YieldGroups). Caveat: a YieldGroup whose capacity depends on a
|
|
455
|
+
* lifecycle clock (FRV, whose vaults exit `Fundraising` by time) can report stale
|
|
456
|
+
* non-zero capacity from this view until a state-advancing call refreshes it; the
|
|
457
|
+
* honesty contract holds against current state, not a time-due future one.
|
|
458
|
+
* @return capacity Spare deposit capacity in asset units.
|
|
459
|
+
*/
|
|
460
|
+
function maxDeposit() external view returns (uint256 capacity);
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* @notice Aggregate currently-withdrawable amount across this YieldGroup's underlying vaults.
|
|
464
|
+
* @dev Must satisfy: `withdraw(maxWithdraw(), to)` does not revert (subject to upstream
|
|
465
|
+
* liquidity).
|
|
466
|
+
* @return liquid Withdrawable amount in asset units.
|
|
467
|
+
*/
|
|
468
|
+
function maxWithdraw() external view returns (uint256 liquid);
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @notice TVL-weighted spot (instantaneous) supply-side APY across this YieldGroup's vaults,
|
|
472
|
+
* in BPS (`10_000` = 100%). Informational only — not consumed by Hub accounting;
|
|
473
|
+
* intended for off-chain APY tracking. The YieldGroup layer has no notion of historical
|
|
474
|
+
* realisation.
|
|
475
|
+
* @return apyBps Spot APY in basis points.
|
|
476
|
+
*/
|
|
477
|
+
function spotAPYBps() external view returns (uint64 apyBps);
|
|
478
|
+
|
|
479
|
+
/// @notice The Hub authorised to call `deposit` / `withdraw`.
|
|
480
|
+
/// @return hubAddress Address of the registered Hub.
|
|
481
|
+
function hub() external view returns (address hubAddress);
|
|
482
|
+
|
|
483
|
+
/// @notice The Access Control Manager gating governance calls.
|
|
484
|
+
/// @return acmAddress Address of the ACM.
|
|
485
|
+
function accessControlManager() external view returns (address acmAddress);
|
|
486
|
+
|
|
487
|
+
/// @notice All registered resources, in insertion order.
|
|
488
|
+
/// @return list Storage copy of the registry array.
|
|
489
|
+
function resources() external view returns (address[] memory list);
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @notice Per-resource registration / pause state plus the adapter handling it.
|
|
493
|
+
* @param resource Address to look up.
|
|
494
|
+
* @return registered True iff present in `resources()`.
|
|
495
|
+
* @return paused True iff this resource is paused.
|
|
496
|
+
* @return adapter `IResourceAdapter` implementation bound to this resource.
|
|
497
|
+
*/
|
|
498
|
+
function resourceConfig(address resource) external view returns (bool registered, bool paused, address adapter);
|
|
499
|
+
|
|
500
|
+
/// @notice Current inner deposit-routing order.
|
|
501
|
+
/// @return queue Storage copy of the inner deposit queue.
|
|
502
|
+
function innerDepositQueue() external view returns (address[] memory queue);
|
|
503
|
+
|
|
504
|
+
/// @notice Current inner withdraw-routing order.
|
|
505
|
+
/// @return queue Storage copy of the inner withdraw queue.
|
|
506
|
+
function innerWithdrawQueue() external view returns (address[] memory queue);
|
|
507
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
import { IYieldGroupBase } from "./IYieldGroupBase.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title IYieldGroupFRV
|
|
8
|
+
* @author Venus
|
|
9
|
+
* @notice External interface for `YieldGroupFRV` — a per-asset YieldGroup that aggregates one or
|
|
10
|
+
* more Venus Fixed Rate Vaults behind the standard `IYieldGroupBase` Hub boundary.
|
|
11
|
+
* @dev Extends `IYieldGroupBase` (the Hub-facing surface + the shared registry / queue / pause
|
|
12
|
+
* machinery) with the FRV-only `forceRemoveResource` escape hatch. FRV has no per-resource
|
|
13
|
+
* deposit cap and no `blocksPerYear` annualiser, so it does not implement `IYieldGroup`. The
|
|
14
|
+
* per-vault lifecycle (Fundraising → Lock → Matured / Closed) is handled inside the
|
|
15
|
+
* implementation via `updateVaultState()` pre-advances; it is not part of this interface.
|
|
16
|
+
*/
|
|
17
|
+
interface IYieldGroupFRV is IYieldGroupBase {
|
|
18
|
+
// -------------------------------- Events ----------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @notice An un-drainable resource was force-removed by governance, orphaning its residual
|
|
22
|
+
* (non-withdrawable) shares as an accepted write-off.
|
|
23
|
+
* @param resource Force-removed FRV vault address.
|
|
24
|
+
* @param orphanedShares Receipt-token (share) balance written off at removal.
|
|
25
|
+
*/
|
|
26
|
+
event ResourceForceRemoved(address indexed resource, uint256 orphanedShares);
|
|
27
|
+
|
|
28
|
+
// -------------------------------- Errors ----------------------------------
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @notice `forceRemoveResource()` was called on a resource that still carries realizable value
|
|
32
|
+
* (NAV > 0). Force-removal may only orphan an already-valueless position; while value
|
|
33
|
+
* remains it must be exited via the normal `removeResource` path.
|
|
34
|
+
* @param resource Resource attempted to be force-removed.
|
|
35
|
+
* @param nav Realizable value still backing the position.
|
|
36
|
+
*/
|
|
37
|
+
error ResourceHasValue(address resource, uint256 nav);
|
|
38
|
+
|
|
39
|
+
// ----------------------- Initialisation ---------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @notice Initialise the YieldGroup. Called once via the proxy.
|
|
43
|
+
* @param hub_ Hub authorised to call `deposit` / `withdraw`.
|
|
44
|
+
* @param asset_ Underlying ERC-20 (must match `Hub.asset()`).
|
|
45
|
+
* @param acm_ AccessControlManagerV8 gating governance calls.
|
|
46
|
+
*/
|
|
47
|
+
function initialize(address hub_, address asset_, address acm_) external;
|
|
48
|
+
|
|
49
|
+
// ----------------------- Registry (governance) ---------------------------
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @notice Emergency-remove an un-drainable resource, orphaning its residual non-withdrawable
|
|
53
|
+
* shares as an accepted write-off (e.g. an FRV vault moved to `Closed`, whose shares can
|
|
54
|
+
* never be redeemed yet whose receipt balance permanently blocks `removeResource`).
|
|
55
|
+
* @dev ACM role `"forceRemoveResource(address)"` (VIP). Reverts {ResourceHasValue} if the
|
|
56
|
+
* resource still carries realizable value (NAV), so it can only ever orphan value that is
|
|
57
|
+
* already unrecoverable, never live funds. Also removes the resource from both inner queues.
|
|
58
|
+
* @param resource Resource to force-remove.
|
|
59
|
+
*/
|
|
60
|
+
function forceRemoveResource(address resource) external;
|
|
61
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IComptroller
|
|
6
|
+
* @author Venus
|
|
7
|
+
* @notice Minimal subset of the Venus Core Comptroller interface needed by the Core Yield
|
|
8
|
+
* Group adapter. Only the accessors used by cap math, pause checks, and treasury-fee
|
|
9
|
+
* gross-up are declared.
|
|
10
|
+
* @dev `actionPaused` uses `uint8` instead of importing Venus's `Action` enum to avoid
|
|
11
|
+
* pulling in the full ComptrollerInterface dependency tree. The deployed contract uses
|
|
12
|
+
* `enum Action { MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET,
|
|
13
|
+
* EXIT_MARKET }`, so `MINT = 0` and `REDEEM = 1`. The ABI is identical to the enum form.
|
|
14
|
+
*/
|
|
15
|
+
interface IComptroller {
|
|
16
|
+
/**
|
|
17
|
+
* @notice Per-market supply cap, in underlying units. `0` means mint is rejected outright
|
|
18
|
+
* (PolicyFacet:39 `require(supplyCap != 0)`).
|
|
19
|
+
* @param vToken Market to query.
|
|
20
|
+
* @return cap Supply cap in underlying units; `0` disables minting.
|
|
21
|
+
*/
|
|
22
|
+
function supplyCaps(address vToken) external view returns (uint256 cap);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @notice Whether a specific action is paused on a given market.
|
|
26
|
+
* @param market vToken market to query.
|
|
27
|
+
* @param action Action enum value: `0 = MINT`, `1 = REDEEM`, etc.
|
|
28
|
+
* @return paused True iff the action is paused.
|
|
29
|
+
*/
|
|
30
|
+
function actionPaused(address market, uint8 action) external view returns (bool paused);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @notice Fraction of every `redeemUnderlying` that is diverted to the treasury, scaled
|
|
34
|
+
* by `1e18`. Non-zero values mean the receiver gets less than the requested
|
|
35
|
+
* redeem amount; adapters must gross-up to deliver an exact net amount.
|
|
36
|
+
* @return percent Treasury cut, scaled by `1e18` (e.g. `5e16` = 5%).
|
|
37
|
+
*/
|
|
38
|
+
function treasuryPercent() external view returns (uint256 percent);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @notice Approve or revoke `delegate` as a redeem-on-behalf-of agent for `msg.sender`.
|
|
42
|
+
* After approval, `delegate` can call `vToken.redeemBehalf(msg.sender, ...)` and
|
|
43
|
+
* `vToken.redeemUnderlyingBehalf(msg.sender, ...)` to burn `msg.sender`'s vTokens
|
|
44
|
+
* and receive the underlying themselves (typically to forward).
|
|
45
|
+
* @param delegate Address to (un)authorise.
|
|
46
|
+
* @param approved True to grant, false to revoke.
|
|
47
|
+
*/
|
|
48
|
+
function updateDelegate(address delegate, bool approved) external;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @notice Whether `delegate` is currently approved as a redeem-on-behalf agent for `owner`.
|
|
52
|
+
* @param owner The vToken holder whose delegation set is queried.
|
|
53
|
+
* @param delegate The address to test.
|
|
54
|
+
* @return approved True iff `delegate` may redeem on `owner`'s behalf.
|
|
55
|
+
*/
|
|
56
|
+
function approvedDelegates(address owner, address delegate) external view returns (bool approved);
|
|
57
|
+
}
|