@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,134 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IMigrator
|
|
6
|
+
* @author Venus
|
|
7
|
+
* @notice One-click migration from an existing Venus Core position into a Liquidity Hub. In a
|
|
8
|
+
* single atomic transaction the Migrator pulls a user's Core vToken, redeems it to the
|
|
9
|
+
* underlying, and deposits that into the matching Hub, minting share tokens to `receiver`.
|
|
10
|
+
* @dev A single immutable, permissionless deployment serves every registered Hub — the target is
|
|
11
|
+
* chosen per call via the `hub` argument and must be present in the HubRegistry, so a migration
|
|
12
|
+
* can only ever land in a governance-registered Hub. Two paths exist because Venus splits its
|
|
13
|
+
* markets by asset type:
|
|
14
|
+
* ERC-20-backed markets (vUSDT, vUSDC, ...) go through {migrateFromCore}; the native market
|
|
15
|
+
* (vBNB) redeems raw BNB and so has the dedicated {migrateFromCoreBNB} path that wraps to WBNB
|
|
16
|
+
* before depositing into the WBNB Hub.
|
|
17
|
+
*/
|
|
18
|
+
interface IMigrator {
|
|
19
|
+
/**
|
|
20
|
+
* @notice Emitted on a completed migration.
|
|
21
|
+
* @param user The caller whose Core position was migrated.
|
|
22
|
+
* @param vToken The Core market the position was redeemed from (the WBNB path reports vBNB).
|
|
23
|
+
* @param hub The Hub the underlying was deposited into.
|
|
24
|
+
* @param vTokenAmount vToken receipts pulled from `user` and redeemed.
|
|
25
|
+
* @param underlying Underlying (or wrapped-native) amount deposited into the Hub.
|
|
26
|
+
* @param shares Hub shares minted to the receiver.
|
|
27
|
+
*/
|
|
28
|
+
event Migrated(
|
|
29
|
+
address indexed user,
|
|
30
|
+
address indexed vToken,
|
|
31
|
+
address indexed hub,
|
|
32
|
+
uint256 vTokenAmount,
|
|
33
|
+
uint256 underlying,
|
|
34
|
+
uint256 shares
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/// @notice A required address argument was the zero address.
|
|
38
|
+
error ZeroAddress();
|
|
39
|
+
/// @notice A required amount argument was zero.
|
|
40
|
+
error ZeroAmount();
|
|
41
|
+
/// @notice The target `hub` is not a governance-registered Hub in the HubRegistry.
|
|
42
|
+
error HubNotRegistered(address hub);
|
|
43
|
+
/// @notice The vToken's underlying does not match the Hub's asset.
|
|
44
|
+
error AssetMismatch(address vTokenUnderlying, address hubAsset);
|
|
45
|
+
/// @notice The Core market returned a non-zero (failure) error code on redeem.
|
|
46
|
+
error VTokenRedeemFailed(address vToken, uint256 errorCode);
|
|
47
|
+
/// @notice The redeem produced no underlying for the Migrator to deposit.
|
|
48
|
+
error NothingReceived();
|
|
49
|
+
/// @notice The minted shares were below the caller's `minShares` floor.
|
|
50
|
+
error InsufficientShares(uint256 shares, uint256 minShares);
|
|
51
|
+
/// @notice Native value was received from an address other than the configured vBNB market.
|
|
52
|
+
error OnlyVBNB();
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @notice Migrate an ERC-20-backed Core position into its Hub.
|
|
56
|
+
* @dev Caller must first `approve` this Migrator to transfer `vTokenAmount` of `vToken`.
|
|
57
|
+
* Reverts unless `IVBep20(vToken).underlying() == IHub(hub).asset()` and the resulting
|
|
58
|
+
* `shares >= minShares`. Use the WBNB path for vBNB. This is the no-consent path — use
|
|
59
|
+
* {migrateFromCoreWithConsent} to record the user's disclaimer consent.
|
|
60
|
+
* @param vToken The Core market to redeem from (e.g. vUSDT).
|
|
61
|
+
* @param vTokenAmount vToken receipts to pull and redeem.
|
|
62
|
+
* @param hub The Hub to deposit the redeemed underlying into.
|
|
63
|
+
* @param receiver Recipient of the minted Hub shares.
|
|
64
|
+
* @param minShares Minimum acceptable minted shares (slippage guard).
|
|
65
|
+
* @return shares Hub shares minted to `receiver`.
|
|
66
|
+
*/
|
|
67
|
+
function migrateFromCore(address vToken, uint256 vTokenAmount, address hub, address receiver, uint256 minShares)
|
|
68
|
+
external
|
|
69
|
+
returns (uint256 shares);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @notice Migrate an ERC-20-backed Core position into its Hub and optionally record the migrating
|
|
73
|
+
* user's disclaimer consent on-chain in the same transaction.
|
|
74
|
+
* @dev Identical to {migrateFromCore} plus a consent record. The record is attributed to
|
|
75
|
+
* `msg.sender` (the migrating user, who calls this Migrator directly), so it is correct even
|
|
76
|
+
* though the Hub deposit is intermediated. Emitted before any state change, so it is rolled
|
|
77
|
+
* back with the transaction if a later leg reverts. The hash is optional — passing
|
|
78
|
+
* `bytes32(0)` skips the emit and migrates without recording.
|
|
79
|
+
* @param vToken The Core market to redeem from (e.g. vUSDT).
|
|
80
|
+
* @param vTokenAmount vToken receipts to pull and redeem.
|
|
81
|
+
* @param hub The Hub to deposit the redeemed underlying into.
|
|
82
|
+
* @param receiver Recipient of the minted Hub shares.
|
|
83
|
+
* @param minShares Minimum acceptable minted shares (slippage guard).
|
|
84
|
+
* @param consentHash keccak256 hash of the disclaimer content the user consented to;
|
|
85
|
+
* `bytes32(0)` skips recording (no event emitted).
|
|
86
|
+
* @return shares Hub shares minted to `receiver`.
|
|
87
|
+
*/
|
|
88
|
+
function migrateFromCoreWithConsent(
|
|
89
|
+
address vToken,
|
|
90
|
+
uint256 vTokenAmount,
|
|
91
|
+
address hub,
|
|
92
|
+
address receiver,
|
|
93
|
+
uint256 minShares,
|
|
94
|
+
bytes32 consentHash
|
|
95
|
+
) external returns (uint256 shares);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @notice Migrate a native vBNB position into the WBNB Hub.
|
|
99
|
+
* @dev Caller must first `approve` this Migrator to transfer `vBNBAmount` of the configured
|
|
100
|
+
* vBNB market. The redeemed BNB is wrapped to WBNB before deposit. Reverts unless `hub` is a
|
|
101
|
+
* registered WBNB Hub (`IHub(hub).asset() == WBNB`) and `shares >= minShares`.
|
|
102
|
+
* This is the no-consent path — use {migrateFromCoreBNBWithConsent} to record consent.
|
|
103
|
+
* @param vBNBAmount vBNB receipts to pull and redeem.
|
|
104
|
+
* @param hub The WBNB Hub to deposit into.
|
|
105
|
+
* @param receiver Recipient of the minted Hub shares.
|
|
106
|
+
* @param minShares Minimum acceptable minted shares (slippage guard).
|
|
107
|
+
* @return shares Hub shares minted to `receiver`.
|
|
108
|
+
*/
|
|
109
|
+
function migrateFromCoreBNB(uint256 vBNBAmount, address hub, address receiver, uint256 minShares)
|
|
110
|
+
external
|
|
111
|
+
returns (uint256 shares);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @notice Migrate a native vBNB position into the WBNB Hub and optionally record the migrating
|
|
115
|
+
* user's disclaimer consent on-chain in the same transaction.
|
|
116
|
+
* @dev Identical to {migrateFromCoreBNB} plus a consent record attributed to `msg.sender` (the
|
|
117
|
+
* migrating user), emitted before any state change. The hash is optional — passing
|
|
118
|
+
* `bytes32(0)` skips the emit and migrates without recording.
|
|
119
|
+
* @param vBNBAmount vBNB receipts to pull and redeem.
|
|
120
|
+
* @param hub The WBNB Hub to deposit into.
|
|
121
|
+
* @param receiver Recipient of the minted Hub shares.
|
|
122
|
+
* @param minShares Minimum acceptable minted shares (slippage guard).
|
|
123
|
+
* @param consentHash keccak256 hash of the disclaimer content the user consented to;
|
|
124
|
+
* `bytes32(0)` skips recording (no event emitted).
|
|
125
|
+
* @return shares Hub shares minted to `receiver`.
|
|
126
|
+
*/
|
|
127
|
+
function migrateFromCoreBNBWithConsent(
|
|
128
|
+
uint256 vBNBAmount,
|
|
129
|
+
address hub,
|
|
130
|
+
address receiver,
|
|
131
|
+
uint256 minShares,
|
|
132
|
+
bytes32 consentHash
|
|
133
|
+
) external returns (uint256 shares);
|
|
134
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IResourceAdapter
|
|
6
|
+
* @author Venus
|
|
7
|
+
* @notice Boundary between a `YieldGroup` and a specific yield-protocol ABI (e.g. Compound-
|
|
8
|
+
* style vTokens for Core V1, a different shape for Core V2, Fluid for Flux, etc.).
|
|
9
|
+
* A single deployment of each implementation is shared across every YieldGroup that
|
|
10
|
+
* registers a resource of the matching protocol family.
|
|
11
|
+
* @dev Dispatch model — load-bearing for security review:
|
|
12
|
+
* - **Mutating functions (`deposit`, `withdraw`) MUST be invoked via DELEGATECALL** from
|
|
13
|
+
* the YieldGroup. They execute in the YieldGroup's storage context so that
|
|
14
|
+
* receipt-token credits and debits land on the YieldGroup, not the adapter. Adapter
|
|
15
|
+
* implementations enforce this with an `address(this) != _ADAPTER_SELF` guard.
|
|
16
|
+
* - **View functions are invoked via normal CALL / STATICCALL.** They take an explicit
|
|
17
|
+
* `holder` parameter (the YieldGroup) where their answer depends on whose position is
|
|
18
|
+
* being queried.
|
|
19
|
+
* - **`accrue` is invoked via normal CALL** (not delegatecall). It settles the resource's
|
|
20
|
+
* own global interest state and credits nothing to the holder, so it needs neither the
|
|
21
|
+
* YieldGroup's storage context nor an `onlyDelegateCall` guard.
|
|
22
|
+
*
|
|
23
|
+
* Adapter implementation invariants (required of every adapter; enforced in-code):
|
|
24
|
+
* 1. The contract declares zero storage variables; only `immutable` values are
|
|
25
|
+
* permitted (immutables live in bytecode, not storage slots, so they're safe across
|
|
26
|
+
* delegatecall).
|
|
27
|
+
* 2. No inline assembly performs `sstore`.
|
|
28
|
+
* 3. External calls go only to (a) the supplied `resource`, (b) addresses the resource
|
|
29
|
+
* itself reports — its asset (`underlying()` on a vToken, `asset()` on an ERC-4626
|
|
30
|
+
* resource) and, where applicable, its `comptroller()` — and (c) trusted, chain-fixed
|
|
31
|
+
* addresses captured as immutables at construction (e.g. AdapterFlux's `LENDING_RESOLVER`,
|
|
32
|
+
* read-only). Never an arbitrary user-supplied address. The exact target set is
|
|
33
|
+
* per-adapter; see each implementation's NatSpec for its list.
|
|
34
|
+
* 4. Mutating functions revert when called outside a delegatecall context, preventing
|
|
35
|
+
* accidental direct invocation that would orphan receipt tokens on the adapter.
|
|
36
|
+
*/
|
|
37
|
+
interface IResourceAdapter {
|
|
38
|
+
// -------------------------------- Mutating -------------------------------
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @notice Deposit `amount` of the resource's underlying into `resource`.
|
|
42
|
+
* @dev MUST be delegatecalled from the YieldGroup. Pre-conditions:
|
|
43
|
+
* - The YieldGroup already holds `amount` of `underlying` (transferred in by the
|
|
44
|
+
* caller of `YieldGroup.deposit`).
|
|
45
|
+
* Post-conditions:
|
|
46
|
+
* - Receipt tokens (e.g. vTokens) are credited to the YieldGroup.
|
|
47
|
+
* - The full `amount` of `underlying` leaves the YieldGroup into `resource` (under
|
|
48
|
+
* delegatecall the code runs in the YieldGroup's context, so the adapter contract
|
|
49
|
+
* itself never custodies funds).
|
|
50
|
+
* @param resource Underlying yield protocol address (e.g. a Venus vToken).
|
|
51
|
+
* @param amount Underlying units to deposit.
|
|
52
|
+
* @return deposited Actual amount placed; equals `amount` on success.
|
|
53
|
+
*/
|
|
54
|
+
function deposit(address resource, uint256 amount) external returns (uint256 deposited);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @notice Redeem exactly `amount` of underlying from `resource` and deliver it to `to`.
|
|
58
|
+
* @dev MUST be delegatecalled from the YieldGroup. Pre-conditions:
|
|
59
|
+
* - The YieldGroup holds enough receipt tokens to back the redeem.
|
|
60
|
+
* Post-conditions:
|
|
61
|
+
* - `to` receives exactly `amount` of underlying.
|
|
62
|
+
* - Some adapters redeem slightly more than `amount` and leave the excess as idle on
|
|
63
|
+
* the YieldGroup (e.g. AdapterCoreV1's treasury-fee gross-up and its sub-one-vToken
|
|
64
|
+
* dust bump). Such surplus is counted in `totalAssets` and consumed idle-first on the
|
|
65
|
+
* next call. Fee-free adapters (Flux, FRV) leave no surplus.
|
|
66
|
+
* @param resource Underlying yield protocol address.
|
|
67
|
+
* @param amount Net underlying units to deliver to `to`.
|
|
68
|
+
* @param to Recipient of the underlying.
|
|
69
|
+
*/
|
|
70
|
+
function withdraw(address resource, uint256 amount, address to) external;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @notice Settle `resource`'s accrued interest up to the current block, so a subsequent
|
|
74
|
+
* {totalAssets} read values the position at a fresh exchange rate rather than a
|
|
75
|
+
* one-cycle-stale one.
|
|
76
|
+
* @dev Invoked via normal CALL (not delegatecall): it mutates the resource's own global
|
|
77
|
+
* interest state, not the holder's position, so receipt tokens never move. Only
|
|
78
|
+
* block-lazy adapters do real work here — Core pokes the vToken's `accrueInterest`;
|
|
79
|
+
* Flux and FRV implement it as a no-op (their NAV is not a block-lazy exchange rate).
|
|
80
|
+
* @param resource Underlying yield protocol address to settle interest on.
|
|
81
|
+
*/
|
|
82
|
+
function accrue(address resource) external;
|
|
83
|
+
|
|
84
|
+
// ---------------------------------- Views --------------------------------
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @notice The ERC-20 underlying accepted by `resource`. YieldGroup uses this to validate
|
|
88
|
+
* that a resource's asset matches its own at registration time.
|
|
89
|
+
* @param resource Underlying yield protocol address.
|
|
90
|
+
* @return underlying Address of the asset token.
|
|
91
|
+
*/
|
|
92
|
+
function asset(address resource) external view returns (address underlying);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @notice Underlying value held by `holder` via `resource`, using stale (non-accruing)
|
|
96
|
+
* exchange-rate state. Cheap; safe to call from Hub-level totalAssets aggregation.
|
|
97
|
+
* @param resource Underlying yield protocol address.
|
|
98
|
+
* @param holder Address whose position to value (the YieldGroup).
|
|
99
|
+
* @return value Underlying units `holder` holds via this resource.
|
|
100
|
+
*/
|
|
101
|
+
function totalAssets(address resource, address holder) external view returns (uint256 value);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @notice Spare deposit headroom on `resource` right now (capacity remaining before the
|
|
105
|
+
* resource rejects mint via its own caps or pause).
|
|
106
|
+
* @param resource Underlying yield protocol address.
|
|
107
|
+
* @return capacity Underlying units a holder can still place into this resource.
|
|
108
|
+
*/
|
|
109
|
+
function maxDeposit(address resource) external view returns (uint256 capacity);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @notice Underlying that `holder` can withdraw from `resource` right now, NET of any
|
|
113
|
+
* protocol fees that will be taken on redeem. Bounded by the lesser of `holder`'s
|
|
114
|
+
* position value and the resource's available cash.
|
|
115
|
+
* @param resource Underlying yield protocol address.
|
|
116
|
+
* @param holder Address whose position to query (the YieldGroup).
|
|
117
|
+
* @return liquid Underlying units deliverable to a recipient on a redeem call.
|
|
118
|
+
*/
|
|
119
|
+
function maxWithdraw(address resource, address holder) external view returns (uint256 liquid);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @notice Spot supply-side APY for `resource` in BPS.
|
|
123
|
+
* @dev `blocksPerYear` is consumed only by block-rate adapters (e.g. Core annualises a
|
|
124
|
+
* per-block supply rate with it). Adapters whose yieldGroup is already annualised — Flux
|
|
125
|
+
* (Fluid APR) and FRV (time-based fixed APY) — ignore the parameter.
|
|
126
|
+
* @param resource Underlying yield protocol address.
|
|
127
|
+
* @param blocksPerYear Per-chain block cadence (chain-dependent; ~10_512_000 on BNB Chain);
|
|
128
|
+
* used only by block-rate adapters, ignored by the rest.
|
|
129
|
+
* @return apyBps Spot APY in basis points (capped at `uint64.max`).
|
|
130
|
+
*/
|
|
131
|
+
function spotAPYBps(address resource, uint256 blocksPerYear) external view returns (uint64 apyBps);
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @notice Raw receipt-token balance `holder` owns via `resource` (vToken / fToken / FRV
|
|
135
|
+
* shares), in receipt-token units — NOT underlying value.
|
|
136
|
+
* @dev Used by `YieldGroup.removeResource` as a share-based emptiness gate. A value-based
|
|
137
|
+
* check (`totalAssets`) rounds a small-but-non-zero receipt balance down to 0, which
|
|
138
|
+
* would let removal orphan those receipt tokens; reading the raw balance closes that gap.
|
|
139
|
+
* @param resource Underlying yield protocol address.
|
|
140
|
+
* @param holder Address whose receipt balance to read (the YieldGroup).
|
|
141
|
+
* @return shares Receipt-token units `holder` holds in this resource.
|
|
142
|
+
*/
|
|
143
|
+
function receiptBalance(address resource, address holder) external view returns (uint256 shares);
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @notice Revert if `resource` fails a protocol-specific precondition for registration.
|
|
147
|
+
* @dev Called by `YieldGroup.addResource`. AdapterCoreV1 rejects a vToken whose Comptroller
|
|
148
|
+
* charges a non-zero `treasuryPercent` — that exit fee leaves the pool on every redeem
|
|
149
|
+
* without burning shares, so it is unmodeled in the Hub's gross NAV and must not be
|
|
150
|
+
* registered. FRV and Flux have no per-redeem pool fee and implement this as a no-op.
|
|
151
|
+
* @param resource Underlying yield protocol address being registered.
|
|
152
|
+
*/
|
|
153
|
+
function validateRegistration(address resource) external view;
|
|
154
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
import { IYieldGroupBase } from "./IYieldGroupBase.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title IYieldGroup
|
|
8
|
+
* @author Venus
|
|
9
|
+
* @notice External interface for the generic `YieldGroup` — the concrete router shared by the
|
|
10
|
+
* synchronous families (Core vTokens, Fluid fTokens). "Core" vs "Flux" is a deployment
|
|
11
|
+
* identity (which adapter + resources + cap + `blocksPerYear` are wired into the proxy),
|
|
12
|
+
* not a separate contract.
|
|
13
|
+
* @dev Extends `IYieldGroupBase` (the Hub-facing surface + the shared registry / queue / pause
|
|
14
|
+
* machinery) with the per-resource deposit cap and the `blocksPerYear` annualiser. Lifecycle
|
|
15
|
+
* families with no cap (FRV) implement `IYieldGroupFRV` instead. Conventions:
|
|
16
|
+
* - Mutating admin calls are ACM-gated. The intended split is asymmetric: governance can both
|
|
17
|
+
* loosen and tighten; the Operator can only tighten (pause, lower a cap, reorder a queue) —
|
|
18
|
+
* never loosen (unpause, raise, register). This asymmetry is enforced purely by which
|
|
19
|
+
* addresses governance grants each ACM role to, not by a hard-coded branch in the contract.
|
|
20
|
+
* - `deposit` / `withdraw` are `onlyHub`-gated; nothing else can move funds.
|
|
21
|
+
*/
|
|
22
|
+
interface IYieldGroup is IYieldGroupBase {
|
|
23
|
+
// -------------------------------- Events ----------------------------------
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @notice A resource's optional per-resource deposit cap was raised (loosened).
|
|
27
|
+
* @param resource Resource whose cap changed.
|
|
28
|
+
* @param newCap New cap in asset units (`0` = unbounded; protocol cap is the only limit).
|
|
29
|
+
*/
|
|
30
|
+
event ResourceCapRaised(address indexed resource, uint256 newCap);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @notice A resource's optional per-resource deposit cap was lowered (tightened).
|
|
34
|
+
* @param resource Resource whose cap changed.
|
|
35
|
+
* @param newCap New cap in asset units (`0` = unbounded; protocol cap is the only limit).
|
|
36
|
+
*/
|
|
37
|
+
event ResourceCapLowered(address indexed resource, uint256 newCap);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @notice The `blocksPerYear` annualiser was updated by governance.
|
|
41
|
+
* @param oldBlocksPerYear Previous value.
|
|
42
|
+
* @param newBlocksPerYear New value.
|
|
43
|
+
*/
|
|
44
|
+
event BlocksPerYearSet(uint256 oldBlocksPerYear, uint256 newBlocksPerYear);
|
|
45
|
+
|
|
46
|
+
// -------------------------------- Errors ----------------------------------
|
|
47
|
+
|
|
48
|
+
/// @notice `raiseResourceCap` called with a value not strictly looser than the current cap
|
|
49
|
+
/// (recall `0` = unbounded, so raising to `0` removes the cap entirely).
|
|
50
|
+
error NotIncreasing();
|
|
51
|
+
|
|
52
|
+
/// @notice `lowerResourceCap` called with a value not strictly tighter than the current cap.
|
|
53
|
+
error NotDecreasing();
|
|
54
|
+
|
|
55
|
+
// ----------------------- Initialisation -----------------------------------
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @notice Initialise the YieldGroup. Called once via the proxy.
|
|
59
|
+
* @param hub_ Hub authorised to call `deposit` / `withdraw`.
|
|
60
|
+
* @param asset_ Underlying ERC-20 (must match `Hub.asset()`).
|
|
61
|
+
* @param blocksPerYear_ Per-chain block cadence used only to annualise spot APY. Pass the
|
|
62
|
+
* chain's value for block-rate families (Core, ~10_512_000 on BNB Chain at 3s/block) and
|
|
63
|
+
* `0` for families whose adapter pre-annualises and ignores it (Flux).
|
|
64
|
+
* @param acm_ AccessControlManagerV8 gating governance calls.
|
|
65
|
+
*/
|
|
66
|
+
function initialize(address hub_, address asset_, uint256 blocksPerYear_, address acm_) external;
|
|
67
|
+
|
|
68
|
+
// ----------------------- Resource caps (asymmetric) ----------------------
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @notice Raise (or remove) the optional per-resource deposit cap. This caps how much
|
|
72
|
+
* underlying THIS YieldGroup may hold in `resource`, independent of the resource's
|
|
73
|
+
* own protocol-level supply cap. The effective deposit room is always
|
|
74
|
+
* `min(protocol headroom, this cap − our balance)`.
|
|
75
|
+
* @dev ACM role `"raiseResourceCap(address,uint256)"` (Guardian or VIP) — raising a cap is loosening.
|
|
76
|
+
* `newCap` must be strictly looser than the current cap, where `0` means unbounded
|
|
77
|
+
* (so raising to `0` removes the cap). Emits {ResourceCapRaised}.
|
|
78
|
+
* @param resource Registered resource whose cap is being raised.
|
|
79
|
+
* @param newCap New cap in asset units (`0` = unbounded).
|
|
80
|
+
*/
|
|
81
|
+
function raiseResourceCap(address resource, uint256 newCap) external;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @notice Lower the optional per-resource deposit cap (tighten). Lowering below the
|
|
85
|
+
* current balance simply stops new deposits into `resource`; it never forces a
|
|
86
|
+
* withdrawal, mirroring `lowerYieldGroupCap` at the Hub.
|
|
87
|
+
* @dev ACM role `"lowerResourceCap(address,uint256)"` (Operator or VIP) — lowering is
|
|
88
|
+
* tightening. `newCap` must be strictly tighter than the current cap (recall `0` =
|
|
89
|
+
* unbounded), and cannot be `0`. Emits {ResourceCapLowered}.
|
|
90
|
+
* @param resource Registered resource whose cap is being lowered.
|
|
91
|
+
* @param newCap New cap in asset units (must be non-zero and below the current effective cap).
|
|
92
|
+
*/
|
|
93
|
+
function lowerResourceCap(address resource, uint256 newCap) external;
|
|
94
|
+
|
|
95
|
+
// ----------------------- Blocks per year (governance) --------------------
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @notice Update the per-chain `blocksPerYear` annualiser read by `spotAPYBps`. Purely cosmetic:
|
|
99
|
+
* it scales only the reported spot APY and never share pricing, routing, caps, or
|
|
100
|
+
* fees. Intended for the rare chain block-time change, applied per YieldGroup.
|
|
101
|
+
* @dev ACM role `"setBlocksPerYear(uint256)"` (VIP). Accepts any value, including `0` (the Flux
|
|
102
|
+
* sentinel, whose adapter pre-annualises and ignores it). Emits {BlocksPerYearSet}.
|
|
103
|
+
* @param newBlocksPerYear New blocks-per-year value for the deployment chain.
|
|
104
|
+
*/
|
|
105
|
+
function setBlocksPerYear(uint256 newBlocksPerYear) external;
|
|
106
|
+
|
|
107
|
+
// --------------------------------- Views ---------------------------------
|
|
108
|
+
|
|
109
|
+
/// @notice The constant used to annualise per-block supply rates.
|
|
110
|
+
/// @return blocks Number of blocks per year on the deployment chain (`0` if unused).
|
|
111
|
+
function blocksPerYear() external view returns (uint256 blocks);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @notice The optional per-resource deposit cap set by governance / Operator.
|
|
115
|
+
* @param resource Address to look up.
|
|
116
|
+
* @return cap Cap in asset units; `0` means unbounded (only the protocol cap binds).
|
|
117
|
+
*/
|
|
118
|
+
function resourceCap(address resource) external view returns (uint256 cap);
|
|
119
|
+
}
|