@@ -6,40 +6,56 @@ import {
66 IERC20Metadata ,
77 ERC20Upgradeable ,
88 ERC4626Upgradeable ,
9+ SafeERC20,
910 Math
1011} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol " ;
12+ import {IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol " ;
1113import {AccessControlUpgradeable} from '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol ' ;
1214import {ERC7201Helper } from './utils/ERC7201Helper.sol ' ;
1315import {IManagedToken} from './interfaces/IManagedToken.sol ' ;
16+ import {ILiquidityPool} from './interfaces/ILiquidityPool.sol ' ;
1417
1518contract LiquidityHub is ERC4626Upgradeable , AccessControlUpgradeable {
1619 using Math for uint256 ;
1720
1821 IManagedToken immutable public SHARES;
19- bytes32 public constant ASSETS_UPDATE_ROLE = "ASSETS_UPDATE_ROLE " ;
22+ ILiquidityPool immutable public LIQUIDITY_POOL;
23+ bytes32 public constant ASSETS_ADJUST_ROLE = "ASSETS_ADJUST_ROLE " ;
24+
25+ event TotalAssetsAdjustment (uint256 oldAssets , uint256 newAssets );
26+ event AssetsLimitSet (uint256 oldLimit , uint256 newLimit );
2027
2128 error ZeroAddress ();
2229 error NotImplemented ();
2330 error IncompatibleAssetsAndShares ();
31+ error AssetsLimitIsTooBig ();
2432
2533 /// @custom:storage-location erc7201:sprinter.storage.LiquidityHub
2634 struct LiquidityHubStorage {
2735 uint256 totalAssets;
36+ uint256 assetsLimit;
2837 }
2938
3039 bytes32 private constant StorageLocation = 0xb877bfaae1674461dd1960c90f24075e3de3265a91f6906fe128ab8da6ba1700 ;
3140
32- constructor (address shares ) {
41+ constructor (address shares , address liquidityPool ) {
3342 ERC7201Helper .validateStorageLocation (
3443 StorageLocation,
3544 'sprinter.storage.LiquidityHub '
3645 );
3746 if (shares == address (0 )) revert ZeroAddress ();
47+ if (liquidityPool == address (0 )) revert ZeroAddress ();
3848 SHARES = IManagedToken (shares);
49+ LIQUIDITY_POOL = ILiquidityPool (liquidityPool);
3950 _disableInitializers ();
4051 }
4152
42- function initialize (IERC20 asset_ , address admin ) external initializer () {
53+ function initialize (
54+ IERC20 asset_ ,
55+ address admin ,
56+ address adjuster ,
57+ uint256 newAssetsLimit
58+ ) external initializer () {
4359 ERC4626Upgradeable .__ERC4626_init (asset_);
4460 require (
4561 IERC20Metadata (address (asset_)).decimals () <= IERC20Metadata (address (SHARES)).decimals (),
@@ -48,6 +64,28 @@ contract LiquidityHub is ERC4626Upgradeable, AccessControlUpgradeable {
4864 // Deliberately not initializing ERC20Upgradable because its
4965 // functionality is delegated to SHARES.
5066 _grantRole (DEFAULT_ADMIN_ROLE, admin);
67+ _grantRole (ASSETS_ADJUST_ROLE, adjuster);
68+ _setAssetsLimit (newAssetsLimit);
69+ }
70+
71+ function adjustTotalAssets (uint256 amount , bool isIncrease ) external onlyRole (ASSETS_ADJUST_ROLE) {
72+ LiquidityHubStorage storage $ = _getStorage ();
73+ uint256 assets = $.totalAssets;
74+ uint256 newAssets = isIncrease ? assets + amount : assets - amount;
75+ $.totalAssets = newAssets;
76+ emit TotalAssetsAdjustment (assets, newAssets);
77+ }
78+
79+ function setAssetsLimit (uint256 newAssetsLimit ) external onlyRole (DEFAULT_ADMIN_ROLE) {
80+ _setAssetsLimit (newAssetsLimit);
81+ }
82+
83+ function _setAssetsLimit (uint256 newAssetsLimit ) internal {
84+ require (newAssetsLimit <= type (uint256 ).max / 10 ** _decimalsOffset (), AssetsLimitIsTooBig ());
85+ LiquidityHubStorage storage $ = _getStorage ();
86+ uint256 oldLimit = $.assetsLimit;
87+ $.assetsLimit = newAssetsLimit;
88+ emit AssetsLimitSet (oldLimit, newAssetsLimit);
5189 }
5290
5391 function name () public pure override (IERC20Metadata , ERC20Upgradeable ) returns (string memory ) {
@@ -91,26 +129,79 @@ contract LiquidityHub is ERC4626Upgradeable, AccessControlUpgradeable {
91129 return _getStorage ().totalAssets;
92130 }
93131
94- function _convertToShares (uint256 assets , Math.Rounding rounding ) internal view virtual override returns (uint256 ) {
95- (uint256 supplyShares , uint256 supplyAssets ) = _getTotals ();
132+ function assetsLimit () public view returns (uint256 ) {
133+ return _getStorage ().assetsLimit;
134+ }
135+
136+ function maxDeposit (address ) public view virtual override returns (uint256 ) {
137+ uint256 total = totalAssets ();
138+ uint256 limit = assetsLimit ();
139+ if (total >= limit) {
140+ return 0 ;
141+ }
142+ return limit - total;
143+ }
144+
145+ function maxMint (address ) public view virtual override returns (uint256 ) {
146+ return _convertToShares (maxDeposit (address (0 )), Math.Rounding.Floor);
147+ }
148+
149+ function depositWithPermit (
150+ uint256 assets ,
151+ address receiver ,
152+ uint256 deadline ,
153+ uint8 v ,
154+ bytes32 r ,
155+ bytes32 s
156+ ) external {
157+ IERC20Permit (asset ()).permit (
158+ _msgSender (),
159+ address (this ),
160+ assets,
161+ deadline,
162+ v,
163+ r,
164+ s
165+ );
166+ deposit (assets, receiver);
167+ }
168+
169+ function _toShares (
170+ uint256 assets ,
171+ uint256 supplyShares ,
172+ uint256 supplyAssets ,
173+ Math.Rounding rounding
174+ ) internal view returns (uint256 ) {
175+ (supplyShares, supplyAssets) = _getTotals (supplyShares, supplyAssets);
96176 return assets.mulDiv (supplyShares, supplyAssets, rounding);
97177 }
98178
99- function _convertToAssets (uint256 shares , Math.Rounding rounding ) internal view virtual override returns (uint256 ) {
100- (uint256 supplyShares , uint256 supplyAssets ) = _getTotals ();
179+ function _toAssets (
180+ uint256 shares ,
181+ uint256 supplyShares ,
182+ uint256 supplyAssets ,
183+ Math.Rounding rounding
184+ ) internal view returns (uint256 ) {
185+ (supplyShares, supplyAssets) = _getTotals (supplyShares, supplyAssets);
101186 return shares.mulDiv (supplyAssets, supplyShares, rounding);
102187 }
103188
104- function _getTotals () internal view returns (uint256 supply , uint256 assets ) {
105- supply = totalSupply ();
106- if (supply == 0 ) {
107- supply = 10 ** _decimalsOffset ();
189+ function _convertToShares (uint256 assets , Math.Rounding rounding ) internal view virtual override returns (uint256 ) {
190+ return _toShares (assets, totalSupply (), totalAssets (), rounding);
191+ }
192+
193+ function _convertToAssets (uint256 shares , Math.Rounding rounding ) internal view virtual override returns (uint256 ) {
194+ return _toAssets (shares, totalSupply (), totalAssets (), rounding);
195+ }
196+
197+ function _getTotals (uint256 supplyShares , uint256 supplyAssets ) internal view returns (uint256 , uint256 ) {
198+ if (supplyShares == 0 ) {
199+ supplyShares = 10 ** _decimalsOffset ();
108200 }
109- assets = totalAssets ();
110- if (assets == 0 ) {
111- assets = 1 ;
201+ if (supplyAssets == 0 ) {
202+ supplyAssets = 1 ;
112203 }
113- return (supply, assets );
204+ return (supplyShares, supplyAssets );
114205 }
115206
116207 function _update (address from , address to , uint256 value ) internal virtual override {
@@ -128,8 +219,12 @@ contract LiquidityHub is ERC4626Upgradeable, AccessControlUpgradeable {
128219 }
129220
130221 function _deposit (address caller , address receiver , uint256 assets , uint256 shares ) internal virtual override {
131- super ._deposit (caller, receiver, assets, shares);
132- _getStorage ().totalAssets += assets;
222+ LiquidityHubStorage storage $ = _getStorage ();
223+ SafeERC20.safeTransferFrom (IERC20 (asset ()), caller, address (LIQUIDITY_POOL), assets);
224+ _mint (receiver, shares);
225+ $.totalAssets += assets;
226+ LIQUIDITY_POOL.deposit ();
227+ emit Deposit (caller, receiver, assets, shares);
133228 }
134229
135230 function _withdraw (
@@ -139,8 +234,14 @@ contract LiquidityHub is ERC4626Upgradeable, AccessControlUpgradeable {
139234 uint256 assets ,
140235 uint256 shares
141236 ) internal virtual override {
142- _getStorage ().totalAssets -= assets;
143- super ._withdraw (caller, receiver, owner, assets, shares);
237+ LiquidityHubStorage storage $ = _getStorage ();
238+ if (caller != owner) {
239+ _spendAllowance (owner, caller, shares);
240+ }
241+ $.totalAssets -= assets;
242+ _burn (owner, shares);
243+ LIQUIDITY_POOL.withdraw (receiver, assets);
244+ emit Withdraw (caller, receiver, owner, assets, shares);
144245 }
145246
146247 function _decimalsOffset () internal view virtual override returns (uint8 ) {
0 commit comments