Skip to content

Commit bbe7341

Browse files
yan-manKogaroshiDhairyaSethi
authored
feat: update liquidity transfer flow and prevent asset re-listing in Hub (#955)
Co-authored-by: Kogaroshi <[email protected]> Co-authored-by: DhairyaSethi <[email protected]>
1 parent 5cb4226 commit bbe7341

File tree

64 files changed

+1580
-535
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1580
-535
lines changed

snapshots/Hub.Operations.json

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
{
2-
"add": "118313",
3-
"draw": "120582",
4-
"eliminateDeficit: full": "59772",
5-
"eliminateDeficit: partial": "69348",
6-
"mintFeeShares": "82887",
7-
"payFee": "69103",
8-
"refreshPremium": "70027",
9-
"remove: full": "76677",
10-
"remove: partial": "81324",
11-
"reportDeficit": "112761",
12-
"restore: full": "91129",
13-
"restore: partial": "99743",
14-
"transferShares": "67904"
2+
"add": "87764",
3+
"add: with transfer": "109371",
4+
"draw": "120584",
5+
"eliminateDeficit: full": "59756",
6+
"eliminateDeficit: partial": "69332",
7+
"mintFeeShares": "82871",
8+
"payFee": "69032",
9+
"refreshPremium": "70045",
10+
"remove: full": "76661",
11+
"remove: partial": "81308",
12+
"reportDeficit": "112658",
13+
"restore: full": "77662",
14+
"restore: full - with transfer": "145600",
15+
"restore: partial": "86292",
16+
"restore: partial - with transfer": "135925",
17+
"transferShares": "67922"
1518
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"borrowNative": "227075",
3-
"repayNative": "240687",
4-
"supplyAsCollateralNative": "184855",
5-
"supplyNative": "137821",
6-
"withdrawNative: full": "125868",
7-
"withdrawNative: partial": "137134"
2+
"borrowNative": "227034",
3+
"repayNative": "241524",
4+
"supplyAsCollateralNative": "185514",
5+
"supplyNative": "138361",
6+
"withdrawNative: full": "125806",
7+
"withdrawNative: partial": "137057"
88
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"borrowWithSig": "215255",
3-
"repayWithSig": "241721",
2+
"borrowWithSig": "215230",
3+
"repayWithSig": "242529",
44
"setSelfAsUserPositionManagerWithSig": "75532",
5-
"setUsingAsCollateralWithSig": "124430",
6-
"supplyWithSig": "154428",
5+
"setUsingAsCollateralWithSig": "124426",
6+
"supplyWithSig": "154944",
77
"updateUserDynamicConfigWithSig": "62593",
88
"updateUserRiskPremiumWithSig": "61448",
9-
"withdrawWithSig": "131892"
9+
"withdrawWithSig": "131824"
1010
}

snapshots/Spoke.Getters.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"getUserAccountData: supplies: 0, borrows: 0": "11703",
3-
"getUserAccountData: supplies: 1, borrows: 0": "48475",
4-
"getUserAccountData: supplies: 2, borrows: 0": "79975",
5-
"getUserAccountData: supplies: 2, borrows: 1": "100248",
6-
"getUserAccountData: supplies: 2, borrows: 2": "118472"
3+
"getUserAccountData: supplies: 1, borrows: 0": "48459",
4+
"getUserAccountData: supplies: 2, borrows: 0": "79943",
5+
"getUserAccountData: supplies: 2, borrows: 1": "100216",
6+
"getUserAccountData: supplies: 2, borrows: 2": "118440"
77
}
Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
{
2-
"borrow: first": "187980",
3-
"borrow: second action, same reserve": "167952",
4-
"liquidationCall (receiveShares): full": "292113",
5-
"liquidationCall (receiveShares): partial": "290435",
6-
"liquidationCall: full": "299442",
7-
"liquidationCall: partial": "297764",
8-
"permitReserve + repay (multicall)": "195257",
9-
"permitReserve + supply (multicall)": "172877",
10-
"permitReserve + supply + enable collateral (multicall)": "216210",
11-
"repay: full": "153291",
12-
"repay: partial": "176899",
2+
"borrow: first": "187966",
3+
"borrow: second action, same reserve": "167938",
4+
"liquidationCall (receiveShares): full": "294802",
5+
"liquidationCall (receiveShares): partial": "293124",
6+
"liquidationCall: full": "302109",
7+
"liquidationCall: partial": "300431",
8+
"permitReserve + repay (multicall)": "196047",
9+
"permitReserve + supply (multicall)": "173494",
10+
"permitReserve + supply + enable collateral (multicall)": "216808",
11+
"repay: full": "156200",
12+
"repay: partial": "179808",
1313
"setUserPositionManagerWithSig: disable": "44936",
1414
"setUserPositionManagerWithSig: enable": "68965",
15-
"supply + enable collateral (multicall)": "162665",
16-
"supply: 0 borrows, collateral disabled": "122988",
17-
"supply: 0 borrows, collateral enabled": "130607",
18-
"supply: second action, same reserve": "105888",
15+
"supply + enable collateral (multicall)": "165398",
16+
"supply: 0 borrows, collateral disabled": "125737",
17+
"supply: 0 borrows, collateral enabled": "133321",
18+
"supply: second action, same reserve": "108637",
1919
"updateUserDynamicConfig: 1 collateral": "73418",
2020
"updateUserDynamicConfig: 2 collaterals": "87911",
21-
"updateUserRiskPremium: 1 borrow": "94787",
22-
"updateUserRiskPremium: 2 borrows": "103315",
21+
"updateUserRiskPremium: 1 borrow": "94771",
22+
"updateUserRiskPremium: 2 borrows": "103299",
2323
"usingAsCollateral: 0 borrows, enable": "78218",
24-
"usingAsCollateral: 1 borrow, disable": "105004",
25-
"usingAsCollateral: 1 borrow, enable": "118399",
26-
"usingAsCollateral: 2 borrows, disable": "125769",
27-
"usingAsCollateral: 2 borrows, enable": "139164",
28-
"withdraw: 0 borrows, full": "128457",
29-
"withdraw: 0 borrows, partial": "133148",
30-
"withdraw: 1 borrow, partial": "160797",
31-
"withdraw: 2 borrows, partial": "173769",
32-
"withdraw: non collateral": "106367"
24+
"usingAsCollateral: 1 borrow, disable": "104988",
25+
"usingAsCollateral: 1 borrow, enable": "118383",
26+
"usingAsCollateral: 2 borrows, disable": "125753",
27+
"usingAsCollateral: 2 borrows, enable": "139148",
28+
"withdraw: 0 borrows, full": "128425",
29+
"withdraw: 0 borrows, partial": "133100",
30+
"withdraw: 1 borrow, partial": "160749",
31+
"withdraw: 2 borrows, partial": "173721",
32+
"withdraw: non collateral": "106335"
3333
}

snapshots/Spoke.Operations.json

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
{
2-
"borrow: first": "257925",
3-
"borrow: second action, same reserve": "200897",
4-
"liquidationCall (receiveShares): full": "325294",
5-
"liquidationCall (receiveShares): partial": "312136",
6-
"liquidationCall: full": "332623",
7-
"liquidationCall: partial": "319465",
8-
"permitReserve + repay (multicall)": "194732",
9-
"permitReserve + supply (multicall)": "172877",
10-
"permitReserve + supply + enable collateral (multicall)": "216210",
11-
"repay: full": "152748",
12-
"repay: partial": "210067",
2+
"borrow: first": "257929",
3+
"borrow: second action, same reserve": "200901",
4+
"liquidationCall (receiveShares): full": "327985",
5+
"liquidationCall (receiveShares): partial": "314827",
6+
"liquidationCall: full": "335292",
7+
"liquidationCall: partial": "322134",
8+
"permitReserve + repay (multicall)": "195488",
9+
"permitReserve + supply (multicall)": "173494",
10+
"permitReserve + supply + enable collateral (multicall)": "216808",
11+
"repay: full": "155641",
12+
"repay: partial": "212978",
1313
"setUserPositionManagerWithSig: disable": "44936",
1414
"setUserPositionManagerWithSig: enable": "68965",
15-
"supply + enable collateral (multicall)": "162665",
16-
"supply: 0 borrows, collateral disabled": "122988",
17-
"supply: 0 borrows, collateral enabled": "130607",
18-
"supply: second action, same reserve": "105888",
15+
"supply + enable collateral (multicall)": "165398",
16+
"supply: 0 borrows, collateral disabled": "125737",
17+
"supply: 0 borrows, collateral enabled": "133321",
18+
"supply: second action, same reserve": "108637",
1919
"updateUserDynamicConfig: 1 collateral": "73418",
2020
"updateUserDynamicConfig: 2 collaterals": "87911",
21-
"updateUserRiskPremium: 1 borrow": "161531",
22-
"updateUserRiskPremium: 2 borrows": "225986",
21+
"updateUserRiskPremium: 1 borrow": "161533",
22+
"updateUserRiskPremium: 2 borrows": "226006",
2323
"usingAsCollateral: 0 borrows, enable": "78218",
24-
"usingAsCollateral: 1 borrow, disable": "155923",
25-
"usingAsCollateral: 1 borrow, enable": "185143",
26-
"usingAsCollateral: 2 borrows, disable": "222790",
27-
"usingAsCollateral: 2 borrows, enable": "267835",
28-
"withdraw: 0 borrows, full": "128457",
29-
"withdraw: 0 borrows, partial": "133148",
30-
"withdraw: 1 borrow, partial": "225038",
31-
"withdraw: 2 borrows, partial": "262415",
32-
"withdraw: non collateral": "106367"
24+
"usingAsCollateral: 1 borrow, disable": "155925",
25+
"usingAsCollateral: 1 borrow, enable": "185145",
26+
"usingAsCollateral: 2 borrows, disable": "222810",
27+
"usingAsCollateral: 2 borrows, enable": "267855",
28+
"withdraw: 0 borrows, full": "128425",
29+
"withdraw: 0 borrows, partial": "133100",
30+
"withdraw: 1 borrow, partial": "225008",
31+
"withdraw: 2 borrows, partial": "262403",
32+
"withdraw: non collateral": "106335"
3333
}

src/hub/Hub.sol

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ contract Hub is IHub, AccessManaged {
4343
mapping(uint256 assetId => Asset) internal _assets;
4444
mapping(uint256 assetId => mapping(address spoke => SpokeData)) internal _spokes;
4545
mapping(uint256 assetId => EnumerableSet.AddressSet) internal _assetToSpokes;
46+
EnumerableSet.AddressSet internal _underlyingAssets;
4647

4748
/// @dev Constructor.
4849
/// @dev The authority contract must implement the `AccessManaged` interface for access control.
@@ -67,6 +68,7 @@ contract Hub is IHub, AccessManaged {
6768
MIN_ALLOWED_UNDERLYING_DECIMALS <= decimals && decimals <= MAX_ALLOWED_UNDERLYING_DECIMALS,
6869
InvalidAssetDecimals()
6970
);
71+
require(!_underlyingAssets.contains(underlying), UnderlyingAlreadyListed());
7072

7173
uint256 assetId = _assetCount++;
7274
IBasicInterestRateStrategy(irStrategy).setInterestRateData(assetId, irData);
@@ -100,6 +102,7 @@ contract Hub is IHub, AccessManaged {
100102
feeReceiver: feeReceiver,
101103
liquidityFee: 0
102104
});
105+
_underlyingAssets.add(underlying);
103106
_addFeeReceiver(assetId, feeReceiver);
104107

105108
emit AddAsset(assetId, underlying, decimals);
@@ -202,23 +205,26 @@ contract Hub is IHub, AccessManaged {
202205
}
203206

204207
/// @inheritdoc IHubBase
205-
function add(uint256 assetId, uint256 amount, address from) external returns (uint256) {
208+
function add(uint256 assetId, uint256 amount) external returns (uint256) {
206209
Asset storage asset = _assets[assetId];
207210
SpokeData storage spoke = _spokes[assetId][msg.sender];
208211

209212
asset.accrue();
210-
_validateAdd(asset, spoke, amount, from);
213+
_validateAdd(asset, spoke, amount);
211214

215+
uint256 liquidity = asset.liquidity + amount;
216+
require(
217+
asset.underlying.balanceOf(address(this)) >= liquidity,
218+
InsufficientLiquidity(liquidity)
219+
);
212220
uint120 shares = asset.toAddedSharesDown(amount).toUint120();
213221
require(shares > 0, InvalidShares());
214222
asset.addedShares += shares;
215223
spoke.addedShares += shares;
216-
asset.liquidity += amount.toUint120();
224+
asset.liquidity = liquidity.toUint120();
217225

218226
asset.updateDrawnRate(assetId);
219227

220-
asset.underlying.safeTransferFrom(from, address(this), amount);
221-
222228
emit Add(assetId, msg.sender, shares, amount);
223229

224230
return shares;
@@ -279,25 +285,27 @@ contract Hub is IHub, AccessManaged {
279285
uint256 assetId,
280286
uint256 drawnAmount,
281287
uint256 premiumAmount,
282-
PremiumDelta calldata premiumDelta,
283-
address from
288+
PremiumDelta calldata premiumDelta
284289
) external returns (uint256) {
285290
Asset storage asset = _assets[assetId];
286291
SpokeData storage spoke = _spokes[assetId][msg.sender];
287292

288293
asset.accrue();
289-
_validateRestore(asset, spoke, drawnAmount, premiumAmount, from);
294+
_validateRestore(asset, spoke, drawnAmount, premiumAmount);
290295

291296
uint120 drawnShares = asset.toDrawnSharesDown(drawnAmount).toUint120();
292297
asset.drawnShares -= drawnShares;
293298
spoke.drawnShares -= drawnShares;
294299
_applyPremiumDelta(asset, spoke, premiumDelta, premiumAmount);
295-
uint256 totalAmount = drawnAmount + premiumAmount;
296-
asset.liquidity += totalAmount.toUint120();
297300

298-
asset.updateDrawnRate(assetId);
301+
uint256 liquidity = asset.liquidity + drawnAmount + premiumAmount;
302+
require(
303+
asset.underlying.balanceOf(address(this)) >= liquidity,
304+
InsufficientLiquidity(liquidity)
305+
);
306+
asset.liquidity = liquidity.toUint120();
299307

300-
asset.underlying.safeTransferFrom(from, address(this), totalAmount);
308+
asset.updateDrawnRate(assetId);
301309

302310
emit Restore(assetId, msg.sender, drawnShares, premiumDelta, drawnAmount, premiumAmount);
303311

@@ -441,6 +449,11 @@ contract Hub is IHub, AccessManaged {
441449
emit Reclaim(assetId, msg.sender, amount);
442450
}
443451

452+
/// @inheritdoc IHub
453+
function isUnderlyingListed(address underlying) external view returns (bool) {
454+
return _underlyingAssets.contains(underlying);
455+
}
456+
444457
/// @inheritdoc IHub
445458
function getAssetCount() external view returns (uint256) {
446459
return _assetCount;
@@ -776,10 +789,8 @@ contract Hub is IHub, AccessManaged {
776789
function _validateAdd(
777790
Asset storage asset,
778791
SpokeData storage spoke,
779-
uint256 amount,
780-
address from
792+
uint256 amount
781793
) internal view {
782-
require(from != address(this), InvalidAddress());
783794
require(amount > 0, InvalidAmount());
784795
require(spoke.active, SpokeNotActive());
785796
require(!spoke.paused, SpokePaused());
@@ -823,10 +834,8 @@ contract Hub is IHub, AccessManaged {
823834
Asset storage asset,
824835
SpokeData storage spoke,
825836
uint256 drawnAmount,
826-
uint256 premiumAmount,
827-
address from
837+
uint256 premiumAmount
828838
) internal view {
829-
require(from != address(this), InvalidAddress());
830839
require(drawnAmount + premiumAmount > 0, InvalidAmount());
831840
require(spoke.active, SpokeNotActive());
832841
require(!spoke.paused, SpokePaused());

src/hub/interfaces/IHub.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ interface IHub is IHubBase, IAccessManaged {
176176
uint256 amount
177177
);
178178

179+
/// @notice Thrown when an underlying asset is already listed.
180+
error UnderlyingAlreadyListed();
181+
179182
/// @notice Thrown when an asset is not listed.
180183
error AssetNotListed();
181184

@@ -240,7 +243,7 @@ interface IHub is IHubBase, IAccessManaged {
240243
error InvalidInterestRateStrategy();
241244

242245
/// @notice Adds a new asset to the Hub.
243-
/// @dev The same underlying asset address can be added as an asset multiple times.
246+
/// @dev The same underlying asset address cannot be added as an asset multiple times.
244247
/// @dev The fee receiver is added as a new spoke with maximum add cap and zero draw cap.
245248
/// @param underlying The address of the underlying asset.
246249
/// @param decimals The number of decimals of `underlying`.
@@ -324,6 +327,11 @@ interface IHub is IHubBase, IAccessManaged {
324327
/// @param amount The amount to reclaim.
325328
function reclaim(uint256 assetId, uint256 amount) external;
326329

330+
/// @notice Returns whether the underlying is listed as an asset.
331+
/// @param underlying The address of the underlying asset.
332+
/// @return True if the underlying asset is listed.
333+
function isUnderlyingListed(address underlying) external view returns (bool);
334+
327335
/// @notice Returns the number of listed assets.
328336
/// @return The number of listed assets.
329337
function getAssetCount() external view returns (uint256);

src/hub/interfaces/IHubBase.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,12 @@ interface IHubBase {
9191

9292
/// @notice Add asset on behalf of user.
9393
/// @dev Only callable by active spokes.
94+
/// @dev Underlying assets must be transferred to the Hub before invocation.
95+
/// @dev Extra underlying liquidity retained in the Hub can be skimmed by any Spoke through this action.
9496
/// @param assetId The identifier of the asset.
9597
/// @param amount The amount of asset liquidity to add.
96-
/// @param from The address from which to pull assets.
9798
/// @return The amount of shares added.
98-
function add(uint256 assetId, uint256 amount, address from) external returns (uint256);
99+
function add(uint256 assetId, uint256 amount) external returns (uint256);
99100

100101
/// @notice Remove added asset on behalf of user.
101102
/// @dev Only callable by active spokes.
@@ -116,18 +117,18 @@ interface IHubBase {
116117
/// @notice Restore assets on behalf of user.
117118
/// @dev Only callable by active spokes.
118119
/// @dev Interest is always paid off first from premium, then from drawn.
120+
/// @dev Underlying assets must be transferred to the Hub before invocation.
121+
/// @dev Extra underlying liquidity retained in the Hub can be skimmed by any Spoke through this action.
119122
/// @param assetId The identifier of the asset.
120123
/// @param drawnAmount The drawn amount to restore.
121124
/// @param premiumAmount The premium amount to repay.
122125
/// @param premiumDelta The premium delta to apply which signal premium repayment.
123-
/// @param from The address from which to pull assets.
124126
/// @return The amount of drawn shares restored.
125127
function restore(
126128
uint256 assetId,
127129
uint256 drawnAmount,
128130
uint256 premiumAmount,
129-
PremiumDelta calldata premiumDelta,
130-
address from
131+
PremiumDelta calldata premiumDelta
131132
) external returns (uint256);
132133

133134
/// @notice Reports deficit.

0 commit comments

Comments
 (0)