@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,173 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity 0.8.25;
3
+
4
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
+ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6
+
7
+ import { IResourceAdapter } from "../interfaces/IResourceAdapter.sol";
8
+ import { IFToken } from "../interfaces/external/IFToken.sol";
9
+ import { IFluidLendingResolver } from "../interfaces/external/IFluidLendingResolver.sol";
10
+
11
+ /**
12
+ * @title AdapterFlux
13
+ * @author Venus
14
+ * @notice Stateless adapter for Fluid lending fTokens (ERC-4626). ONE deployment serves every
15
+ * YieldGroupFlux that registers an fToken — there is no per-(YieldGroup, fToken) instance,
16
+ * no proxy, no clone factory.
17
+ * @dev Dispatch model: mutating functions (`deposit`, `withdraw`) MUST be invoked via DELEGATECALL
18
+ * from a YieldGroupFlux. They execute in the YieldGroup's storage context, so ERC-4626 share
19
+ * credits and debits land on the YieldGroup, not this adapter. View functions are invoked via
20
+ * normal CALL / STATICCALL with an explicit `holder` argument.
21
+ *
22
+ * Storage-safety invariants enforced by this contract:
23
+ * 1. Zero `internal`/`private`/`public` state variables are declared. Only `immutable` values
24
+ * are used (`_ADAPTER_SELF` and `LENDING_RESOLVER`). Immutables live in bytecode, not
25
+ * storage slots, so they're delegatecall-safe.
26
+ * 2. No inline assembly performs `sstore`.
27
+ * 3. External calls go to the supplied `resource` (fToken), its `asset()`, or the trusted,
28
+ * chain-fixed Fluid `LENDING_RESOLVER` (read-only). No arbitrary user-supplied target.
29
+ * 4. {onlyDelegateCall} reverts when a mutating function is invoked directly on the adapter,
30
+ * preventing accidents that would orphan shares here.
31
+ *
32
+ * No treasury-fee handling: a Fluid fToken delivers exactly the requested `assets` on
33
+ * withdraw (the Liquidity layer takes its revenue cut in the supply rate, not per-redeem).
34
+ * No gross-up needed.
35
+ *
36
+ * No lifecycle: fTokens are perpetual, so unlike `AdapterFRV` there is no state-machine
37
+ * pre-advance — the YieldGroup calls the adapter directly.
38
+ */
39
+ contract AdapterFlux is IResourceAdapter {
40
+ using SafeERC20 for IERC20;
41
+
42
+ // ============================== State (immutable only) ====================
43
+
44
+ /// @notice This adapter's own address, captured at deployment. Used by {onlyDelegateCall} to
45
+ /// detect direct invocation. Immutables live in bytecode, not storage.
46
+ address private immutable _ADAPTER_SELF;
47
+
48
+ /// @notice Fluid periphery `LendingResolver` address (chain-fixed). Read-only yieldGroup for spot
49
+ /// supply APR + reward rate in `spotAPYBps`.
50
+ address public immutable LENDING_RESOLVER;
51
+
52
+ // ============================== Errors ===================================
53
+
54
+ /// @notice A mutating function was invoked directly (not via delegatecall).
55
+ error NotDelegateCall();
56
+
57
+ /// @notice The supplied resolver address was the zero address at construction.
58
+ error ZeroResolver();
59
+
60
+ // ============================== Modifiers ================================
61
+
62
+ /// @notice Reverts {NotDelegateCall} when the call is not arriving via delegatecall.
63
+ /// @dev During a delegatecall, `address(this)` is the caller; during a direct call it equals
64
+ /// `_ADAPTER_SELF` (captured at construction).
65
+ modifier onlyDelegateCall() {
66
+ if (address(this) == _ADAPTER_SELF) revert NotDelegateCall();
67
+ _;
68
+ }
69
+
70
+ // ============================== Constructor ==============================
71
+
72
+ /// @param lendingResolver_ Fluid `LendingResolver` for this chain.
73
+ constructor(address lendingResolver_) {
74
+ if (lendingResolver_ == address(0)) revert ZeroResolver();
75
+ _ADAPTER_SELF = address(this);
76
+ LENDING_RESOLVER = lendingResolver_;
77
+ }
78
+
79
+ // ============================== External — mutating =======================
80
+
81
+ /// @inheritdoc IResourceAdapter
82
+ /// @dev `address(this)` here is the YieldGroup (delegatecall context). The YieldGroup already
83
+ /// holds `amount` of underlying; we approve the fToken, which then pulls `amount` from the
84
+ /// YieldGroup via its `liquidityCallback`. Shares are minted to the YieldGroup.
85
+ function deposit(address resource, uint256 amount) external override onlyDelegateCall returns (uint256 deposited) {
86
+ IERC20 assetToken = IERC20(IFToken(resource).asset());
87
+ assetToken.forceApprove(resource, amount);
88
+ IFToken(resource).deposit(amount, address(this));
89
+ return amount;
90
+ }
91
+
92
+ /// @inheritdoc IResourceAdapter
93
+ /// @dev `address(this)` here is the YieldGroup. `fToken.withdraw` burns its (YieldGroup's)
94
+ /// shares and delivers exactly `amount` of underlying directly to `to`.
95
+ function withdraw(address resource, uint256 amount, address to) external override onlyDelegateCall {
96
+ IFToken(resource).withdraw(amount, to, address(this));
97
+ }
98
+
99
+ /// @inheritdoc IResourceAdapter
100
+ /// @dev No-op: Fluid's live `previewRedeem` leaves no block-lazy stored rate to freshen.
101
+ // solhint-disable-next-line no-empty-blocks
102
+ function accrue(address resource) external override { }
103
+
104
+ // ============================== External — view ==========================
105
+
106
+ /// @inheritdoc IResourceAdapter
107
+ function asset(address resource) external view override returns (address) {
108
+ return IFToken(resource).asset();
109
+ }
110
+
111
+ /// @inheritdoc IResourceAdapter
112
+ /// @dev `previewRedeem(balanceOf(holder))` — the per-holder position value (shares × token
113
+ /// exchange price, including accrued rewards).
114
+ function totalAssets(address resource, address holder) external view override returns (uint256) {
115
+ uint256 shares = IFToken(resource).balanceOf(holder);
116
+ if (shares == 0) return 0;
117
+ return IFToken(resource).previewRedeem(shares);
118
+ }
119
+
120
+ /// @inheritdoc IResourceAdapter
121
+ /// @dev fToken headroom up to `int128.max` (effectively unbounded). The receiver param is
122
+ /// ignored by the fToken. The YieldGroup's per-resource cap is the binding control.
123
+ /// Pause-blind, like the fToken's own `maxDeposit`; a paused fToken is caught and skipped by
124
+ /// the YieldGroup deposit cascade, not gated here.
125
+ function maxDeposit(address resource) external view override returns (uint256) {
126
+ return IFToken(resource).maxDeposit(address(this));
127
+ }
128
+
129
+ /// @inheritdoc IResourceAdapter
130
+ /// @dev `min(Fluid withdrawal-limit, holder balance)`. No treasury-fee deduction — the fToken
131
+ /// delivers exactly the requested amount on redeem.
132
+ /// Intentionally pause-blind: the fToken's own `maxWithdraw` never reads Fluid's `IS_PAUSED`
133
+ /// bit (and no cheap getter exposes it), so this mirrors it. An underlying pause is caught
134
+ /// and skipped by the YieldGroup withdraw cascade's `try/catch` — where the real op reverts —
135
+ /// not gated here.
136
+ function maxWithdraw(address resource, address holder) external view override returns (uint256) {
137
+ return IFToken(resource).maxWithdraw(holder);
138
+ }
139
+
140
+ /// @inheritdoc IResourceAdapter
141
+ /// @dev Spot APY from the Fluid `LendingResolver`: base `supplyRate` (1e2 precision = already
142
+ /// basis points) + `rewardsRate` (1e12 precision, `/1e10` → bps). The `blocksPerYear`
143
+ /// parameter is unused — Fluid rates are pre-annualised APRs, not per-block.
144
+ function spotAPYBps(
145
+ address resource,
146
+ uint256 /* blocksPerYear */
147
+ )
148
+ external
149
+ view
150
+ override
151
+ returns (uint64)
152
+ {
153
+ IFluidLendingResolver.FTokenDetails memory details =
154
+ IFluidLendingResolver(LENDING_RESOLVER).getFTokenDetails(resource);
155
+ uint256 apyBps = details.supplyRate + (details.rewardsRate / 1e10);
156
+ // forge-lint: disable-next-line(unsafe-typecast)
157
+ return apyBps > type(uint64).max ? type(uint64).max : uint64(apyBps);
158
+ }
159
+
160
+ /// @inheritdoc IResourceAdapter
161
+ function receiptBalance(address resource, address holder) external view override returns (uint256) {
162
+ return IFToken(resource).balanceOf(holder);
163
+ }
164
+
165
+ /// @inheritdoc IResourceAdapter
166
+ /// @dev No-op: a Fluid fToken takes its revenue cut in the supply rate, not per-redeem, so
167
+ /// there is no unmodeled exit fee to guard against at registration.
168
+ function validateRegistration(address resource) external pure override {
169
+ // No Flux-specific registration precondition. `resource` validity is established by the
170
+ // YieldGroup's `asset()` probe and `code.length` check at registration time.
171
+ resource;
172
+ }
173
+ }