@venusprotocol/liquidity-hub 2.1.0-dev.4 → 2.1.0-dev.6
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/README.md +111 -30
- package/contracts/Hub/Hub.sol +71 -63
- package/contracts/adapters/AdapterCentrifuge.sol +5 -0
- package/contracts/adapters/AdapterCoreV1.sol +6 -0
- package/contracts/adapters/AdapterFRV.sol +27 -7
- package/contracts/adapters/AdapterFlux.sol +6 -0
- package/contracts/adapters/AdapterSpokeV1.sol +428 -0
- package/contracts/interfaces/IHub.sol +6 -0
- package/contracts/interfaces/IHubLens.sol +71 -0
- package/contracts/interfaces/IResourceAdapter.sol +23 -7
- package/contracts/interfaces/IYieldGroupBase.sol +6 -1
- package/contracts/interfaces/external/ISpokeComptroller.sol +69 -0
- package/contracts/interfaces/external/IVTokenIsolated.sol +153 -0
- package/contracts/lens/HubCapacityMath.sol +610 -0
- package/contracts/lens/HubLens.sol +311 -0
- package/deployments/bscmainnet/AdapterCentrifuge.json +21 -2
- package/deployments/bscmainnet/AdapterCoreV1.json +21 -2
- package/deployments/bscmainnet/AdapterFRV.json +21 -2
- package/deployments/bscmainnet/AdapterFlux.json +21 -2
- package/deployments/bscmainnet/AdapterSpokeV1.json +339 -0
- package/deployments/bscmainnet/HubImpl.json +16 -3
- package/deployments/bscmainnet/HubLens.json +89 -0
- package/deployments/bscmainnet/SpokeBeacon.json +110 -0
- package/deployments/bscmainnet/SpokeSource_U.json +1083 -0
- package/deployments/bscmainnet/SpokeSource_USDC.json +1083 -0
- package/deployments/bscmainnet/SpokeSource_USDT.json +1083 -0
- package/deployments/bscmainnet/YieldGroupSpokeImpl.json +1083 -0
- package/deployments/bscmainnet_addresses.json +13 -6
- package/deployments/bsctestnet/AdapterCentrifuge.json +21 -2
- package/deployments/bsctestnet/AdapterCoreV1.json +21 -2
- package/deployments/bsctestnet/AdapterFRV.json +21 -2
- package/deployments/bsctestnet/AdapterFlux.json +21 -2
- package/deployments/bsctestnet/AdapterSpokeV1.json +339 -0
- package/deployments/bsctestnet/HubImpl.json +16 -3
- package/deployments/bsctestnet/HubLens.json +89 -0
- package/deployments/bsctestnet/SpokeBeacon.json +110 -0
- package/deployments/bsctestnet/SpokeSource_USDT.json +1083 -0
- package/deployments/bsctestnet/YieldGroupSpokeImpl.json +1083 -0
- package/deployments/bsctestnet_addresses.json +11 -6
- package/package.json +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title ISpokeComptroller
|
|
6
|
+
* @author Venus
|
|
7
|
+
* @notice Minimal subset of the `SpokeComptroller` (isolated-pools) interface needed by the Spoke
|
|
8
|
+
* Yield Group adapter: the supply allowlist that gates who may fund a hub-funded market,
|
|
9
|
+
* plus the cap and pause accessors the deposit-capacity math reads.
|
|
10
|
+
* @dev Deliberately NOT the shared isolated-pools `Comptroller`. {isSupplyAllowlistEnabled} and
|
|
11
|
+
* {isAllowedSupplier} exist only on the spoke fork, so a call to either reverts against any
|
|
12
|
+
* other Comptroller — which is what makes `AdapterSpokeV1.validateRegistration` reject a
|
|
13
|
+
* non-spoke market without needing an explicit type probe. {isMarketListed} carries no such
|
|
14
|
+
* signal, since the shared `Comptroller` declares it too; it is read to tie a market to the
|
|
15
|
+
* Comptroller it names.
|
|
16
|
+
*
|
|
17
|
+
* `actionPaused` takes a `uint8` rather than importing the isolated-pools `Action` enum, whose
|
|
18
|
+
* ABI encoding is identical. The deployed enum is
|
|
19
|
+
* `{ MINT, REDEEM, BORROW, REPAY, SEIZE, LIQUIDATE, TRANSFER, ENTER_MARKET, EXIT_MARKET }`, so
|
|
20
|
+
* `MINT = 0` and `REDEEM = 1` — the same ordering as the Core Comptroller's.
|
|
21
|
+
*/
|
|
22
|
+
interface ISpokeComptroller {
|
|
23
|
+
/**
|
|
24
|
+
* @notice Per-market supply cap, in underlying units.
|
|
25
|
+
* @dev The uncapped sentinel is one-sided. Here `type(uint256).max` skips the check entirely,
|
|
26
|
+
* while `0` is a real cap of zero that rejects every mint (`preMintHook` compares
|
|
27
|
+
* `nextTotalSupply > supplyCap`). The Core Comptroller has no uncapped sentinel; there `0`
|
|
28
|
+
* is the value that disables minting.
|
|
29
|
+
* @param vToken Market to query.
|
|
30
|
+
* @return cap Supply cap in underlying units; `type(uint256).max` disables the check.
|
|
31
|
+
*/
|
|
32
|
+
function supplyCaps(address vToken) external view returns (uint256 cap);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @notice Whether a specific action is paused on a given market.
|
|
36
|
+
* @param market vToken market to query.
|
|
37
|
+
* @param action Action enum value: `0 = MINT`, `1 = REDEEM`, etc.
|
|
38
|
+
* @return paused True iff the action is paused.
|
|
39
|
+
*/
|
|
40
|
+
function actionPaused(address market, uint8 action) external view returns (bool paused);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @notice Whether the Comptroller lists `vToken` as one of its markets.
|
|
44
|
+
* @dev `preMintHook` reverts `MarketNotListed` for anything else, and `unlistMarket` can flip a
|
|
45
|
+
* live market back to unlisted once every action is paused and its caps are zero.
|
|
46
|
+
* @param vToken Market to query.
|
|
47
|
+
* @return listed True iff `vToken` is a listed market of this Comptroller.
|
|
48
|
+
*/
|
|
49
|
+
function isMarketListed(address vToken) external view returns (bool listed);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @notice Whether `vToken` accepts supply only from the accounts on its supply allowlist.
|
|
53
|
+
* @dev Disabled by default, so a market can be listed before the allowlist is armed.
|
|
54
|
+
* @param vToken Market to query.
|
|
55
|
+
* @return enabled True iff the allowlist gates minting on this market.
|
|
56
|
+
*/
|
|
57
|
+
function isSupplyAllowlistEnabled(address vToken) external view returns (bool enabled);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @notice Whether `supplier` may mint `vToken` while its supply allowlist is enabled.
|
|
61
|
+
* @dev `preMintHook` checks the account CREDITED with the vTokens, not the account paying. The
|
|
62
|
+
* adapter is delegatecalled, so the credited account is the YieldGroup, and the YieldGroup
|
|
63
|
+
* address is what has to be allowlisted.
|
|
64
|
+
* @param vToken Market whose allowlist to read.
|
|
65
|
+
* @param supplier Account to test.
|
|
66
|
+
* @return allowed True iff `supplier` is on `vToken`'s supply allowlist.
|
|
67
|
+
*/
|
|
68
|
+
function isAllowedSupplier(address vToken, address supplier) external view returns (bool allowed);
|
|
69
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity 0.8.25;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title IVTokenIsolated
|
|
6
|
+
* @author Venus
|
|
7
|
+
* @notice Minimal subset of the Venus isolated-pools `VToken` interface needed by the Spoke Yield
|
|
8
|
+
* Group adapter. Kept narrow on purpose, and kept SEPARATE from the Core `IVToken`: the two
|
|
9
|
+
* share most selectors but differ in semantics on every line that matters to an adapter, so
|
|
10
|
+
* one interface serving both would attach the wrong contract to the wrong reader.
|
|
11
|
+
* @dev The four semantic differences from Core's `VBep20`, each verified against
|
|
12
|
+
* `isolated-pools/contracts/VToken.sol`:
|
|
13
|
+
*
|
|
14
|
+
* 1. **`badDebt` is inside the exchange rate.** `_exchangeRateStored` computes
|
|
15
|
+
* `(totalCash + totalBorrows + badDebt - totalReserves) / totalSupply`. `healAccount` moves
|
|
16
|
+
* an unrecoverable shortfall out of `totalBorrows` and into `badDebt`, leaving that
|
|
17
|
+
* numerator unchanged — so the rate does NOT fall when a loss is recorded and no supplier is
|
|
18
|
+
* marked down. The written-off amount is missing cash instead, until it is recovered
|
|
19
|
+
* through `badDebtRecovered`.
|
|
20
|
+
* 2. **Redeem is bounded by cash NET of reserves.** `_redeemFresh` reverts
|
|
21
|
+
* `RedeemTransferOutNotPossible` when `getCash() - totalReserves < redeemAmount`, not when
|
|
22
|
+
* `getCash() < redeemAmount`. Reserves are not payable liquidity.
|
|
23
|
+
* 3. **Both redeems OVER-deliver.** `redeem(t)` transfers `truncate(exchangeRate x t)`, and
|
|
24
|
+
* `redeemUnderlying(a)` burns `ceil(a / exchangeRate)` tokens and transfers what those are
|
|
25
|
+
* worth — `>= a` by up to one vToken unit of underlying. Callers must transfer the exact
|
|
26
|
+
* requested amount onward and retain the surplus; they must NOT assume an exact delivery.
|
|
27
|
+
* 4. **No per-redeem pool fee.** Isolated pools take their cut through `reserveFactorMantissa`
|
|
28
|
+
* (already netted out of the exchange rate) and route it to the ProtocolShareReserve. There
|
|
29
|
+
* is no `treasuryPercent` equivalent, so there is nothing to gross up on redeem.
|
|
30
|
+
*
|
|
31
|
+
* Shared with Core: `mint` pulls `mintAmount` from `msg.sender` and credits vTokens to
|
|
32
|
+
* `msg.sender`; both mutating calls return a Compound-style error code that isolated pools
|
|
33
|
+
* always sets to `NO_ERROR` (failures revert instead); `mintBehalf` /
|
|
34
|
+
* `redeemUnderlyingBehalf` exist but are on no path here, because the adapter is delegatecalled
|
|
35
|
+
* and so is already `msg.sender` from the vToken's perspective.
|
|
36
|
+
*/
|
|
37
|
+
interface IVTokenIsolated {
|
|
38
|
+
// -------------------------------- Mutating -------------------------------
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @notice Supply `mintAmount` of underlying and receive vTokens.
|
|
42
|
+
* @dev Accrues interest first, so the supply-cap check in `preMintHook` runs against a
|
|
43
|
+
* POST-accrual exchange rate — one notch tighter than the rate a `maxDeposit` view reads.
|
|
44
|
+
* @param mintAmount Underlying units to deposit.
|
|
45
|
+
* @return errorCode Compound-style error code; always `0` (failures revert).
|
|
46
|
+
*/
|
|
47
|
+
function mint(uint256 mintAmount) external returns (uint256 errorCode);
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @notice Burn exactly `redeemTokens` vTokens and receive `truncate(exchangeRate x redeemTokens)`
|
|
51
|
+
* of underlying.
|
|
52
|
+
* @dev The token-denominated redeem, and the one an adapter should reach for: the burn is the
|
|
53
|
+
* caller's own number, so it can be held inside a known balance by construction.
|
|
54
|
+
* {redeemUnderlying} instead DERIVES the burn from the request and rounds it up, and
|
|
55
|
+
* `_redeemFresh` subtracts that derived count from the caller's balance in checked
|
|
56
|
+
* arithmetic — a request one unit above `balanceOfUnderlying` panics rather than failing
|
|
57
|
+
* gracefully.
|
|
58
|
+
*
|
|
59
|
+
* Reverts `"redeemAmount is zero"` when the payout truncates away, which cannot happen for a
|
|
60
|
+
* count sized to cover a non-zero amount of underlying.
|
|
61
|
+
* @param redeemTokens vToken units to burn.
|
|
62
|
+
* @return errorCode Compound-style error code; always `0` (failures revert).
|
|
63
|
+
*/
|
|
64
|
+
function redeem(uint256 redeemTokens) external returns (uint256 errorCode);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @notice Burn vTokens and receive AT LEAST `redeemAmount` of underlying.
|
|
68
|
+
* @dev Over-delivers by up to one vToken unit of underlying (see note 3 in the contract NatSpec).
|
|
69
|
+
* Reverts `"redeemAmount is zero"` when the payout truncates away. Declared for completeness
|
|
70
|
+
* of the market's redeem surface; see {redeem} for why the burn it derives makes it the
|
|
71
|
+
* wrong primitive for a caller that has to stay inside a known balance.
|
|
72
|
+
* @param redeemAmount Underlying units to withdraw.
|
|
73
|
+
* @return errorCode Compound-style error code; always `0` (failures revert).
|
|
74
|
+
*/
|
|
75
|
+
function redeemUnderlying(uint256 redeemAmount) external returns (uint256 errorCode);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @notice Settle accrued interest into the stored exchange rate up to the current block or
|
|
79
|
+
* timestamp.
|
|
80
|
+
* @dev Also sweeps accrued reserves to the ProtocolShareReserve once
|
|
81
|
+
* `reduceReservesBlockDelta` has elapsed. That lowers `getCash()` and `totalReserves()` by
|
|
82
|
+
* the same amount, so any quantity derived from `getCash() - totalReserves()` is invariant
|
|
83
|
+
* across the sweep.
|
|
84
|
+
* @return errorCode Compound-style error code; always `0` (failures revert).
|
|
85
|
+
*/
|
|
86
|
+
function accrueInterest() external returns (uint256 errorCode);
|
|
87
|
+
|
|
88
|
+
// ---------------------------------- Views --------------------------------
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @notice vToken balance of `account`, in vToken units (NOT underlying).
|
|
92
|
+
* @param account Holder to query.
|
|
93
|
+
* @return balance vToken units held by `account`.
|
|
94
|
+
*/
|
|
95
|
+
function balanceOf(address account) external view returns (uint256 balance);
|
|
96
|
+
|
|
97
|
+
/// @notice Total vToken supply, in vToken units.
|
|
98
|
+
/// @return supply Total outstanding vTokens.
|
|
99
|
+
function totalSupply() external view returns (uint256 supply);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @notice Last-settled underlying-per-vToken exchange rate, scaled by `1e18`.
|
|
103
|
+
* @dev INCLUDES `badDebt` in its numerator. It is the rate the market mints, redeems and prices
|
|
104
|
+
* caps at, and the rate `AdapterSpokeV1` values a position at.
|
|
105
|
+
* @return rate Exchange rate scaled by `1e18`.
|
|
106
|
+
*/
|
|
107
|
+
function exchangeRateStored() external view returns (uint256 rate);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @notice Underlying cash the market considers itself to hold.
|
|
111
|
+
* @dev Reserves sit inside this figure but are not redeemable; subtract `totalReserves()` before
|
|
112
|
+
* treating it as payable liquidity.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately not pinned to a definition. Implementations differ on whether this is the
|
|
115
|
+
* market's raw underlying balance or an internally tracked figure that ignores unsolicited
|
|
116
|
+
* transfers, and the two disagree by exactly any donation the market has received. Reading
|
|
117
|
+
* it rather than reconstructing it keeps valuation and liquidity consistent under either:
|
|
118
|
+
* `_redeemFresh` gates on this same quantity, so whatever it counts is also what it will pay
|
|
119
|
+
* out, and whatever it ignores is value no caller could have redeemed anyway.
|
|
120
|
+
* @return cash Underlying units the market counts as cash.
|
|
121
|
+
*/
|
|
122
|
+
function getCash() external view returns (uint256 cash);
|
|
123
|
+
|
|
124
|
+
/// @notice Accrued protocol reserves, in underlying units. Not payable to suppliers.
|
|
125
|
+
/// @return reserves Underlying units reserved for the protocol.
|
|
126
|
+
function totalReserves() external view returns (uint256 reserves);
|
|
127
|
+
|
|
128
|
+
/// @notice The ERC-20 underlying this market is backed by.
|
|
129
|
+
/// @return token Address of the underlying ERC-20.
|
|
130
|
+
function underlying() external view returns (address token);
|
|
131
|
+
|
|
132
|
+
/// @notice Comptroller of the pool this market belongs to.
|
|
133
|
+
/// @return comptrollerAddress Address of the pool's Comptroller.
|
|
134
|
+
function comptroller() external view returns (address comptrollerAddress);
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @notice Current supply interest rate per slot, scaled by `1e18`.
|
|
138
|
+
* @dev A "slot" is a block on a block-based market and a second on a time-based one; annualise
|
|
139
|
+
* with {blocksOrSecondsPerYear}, never with a chain-level constant.
|
|
140
|
+
* @return rate Per-slot supply rate, scaled by `1e18`.
|
|
141
|
+
*/
|
|
142
|
+
function supplyRatePerBlock() external view returns (uint256 rate);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @notice Slots per year for this market: blocks per year on a block-based market, seconds per
|
|
146
|
+
* year on a time-based one.
|
|
147
|
+
* @dev An immutable set at construction (`TimeManagerV8`), so it is the only annualiser that
|
|
148
|
+
* matches this market's rate unit. A YieldGroup-level `blocksPerYear` cannot, because one
|
|
149
|
+
* YieldGroup can hold markets of both kinds.
|
|
150
|
+
* @return periods Slots per year.
|
|
151
|
+
*/
|
|
152
|
+
function blocksOrSecondsPerYear() external view returns (uint256 periods);
|
|
153
|
+
}
|