Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion snapshots/Hub.Operations.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"refreshPremium": "71999",
"remove: full": "76993",
"remove: partial": "81640",
"reportDeficit": "115225",
"reportDeficit": "115076",
"restore: full": "80471",
"restore: full - with transfer": "173377",
"restore: partial": "89137",
Expand Down
1 change: 0 additions & 1 deletion src/hub/Hub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,6 @@ contract Hub is IHub, AccessManaged {
uint256 premiumAmountRay
) internal view {
require(spoke.active, SpokeNotActive());
require(!spoke.paused, SpokePaused());
require(drawnAmount > 0 || premiumAmountRay > 0, InvalidAmount());
uint256 drawn = _getSpokeDrawn(asset, spoke);
uint256 premiumRay = _getSpokePremiumRay(asset, spoke);
Expand Down
26 changes: 25 additions & 1 deletion tests/unit/Hub/Hub.ReportDeficit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,37 @@ contract HubReportDeficitTest is HubBase {
hub1.reportDeficit(usdxAssetId, 0, ZERO_PREMIUM_DELTA);
}

/// @dev paused spoke can still report deficit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to move it down, alongside the rest of positive cases. Also, do we have tests for all other combinations? (active and paused, active and !paused, !active and paused, !active and !paused)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved down and added the combo tests. I think also worthwhile adding these for all the actions on spoke/hub in a dedicated test file, wdyt. I will add that to this PR as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function test_reportDeficit_paused() public {
// draw usdx liquidity to be restored
_drawLiquidity({
assetId: usdxAssetId,
amount: 1,
withPremium: true,
skipTime: true,
spoke: address(spoke1)
});

_updateSpokePaused(hub1, usdxAssetId, address(spoke1), true);

// even if spoke is paused, it can report deficit
vm.prank(address(spoke1));
hub1.reportDeficit(usdxAssetId, 1, ZERO_PREMIUM_DELTA);
}

function test_reportDeficit_fuzz_revertsWith_SurplusDrawnDeficitReported(
uint256 drawnAmount
) public {
drawnAmount = bound(drawnAmount, 1, MAX_SUPPLY_AMOUNT);

// draw usdx liquidity to be restored
_drawLiquidity(usdxAssetId, drawnAmount, true, true, address(spoke1));
_drawLiquidity({
assetId: usdxAssetId,
amount: drawnAmount,
withPremium: true,
skipTime: true,
spoke: address(spoke1)
});

(uint256 drawn, uint256 premium) = hub1.getSpokeOwed(usdxAssetId, address(spoke1));
assertGt(drawn, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,57 @@ contract SpokeLiquidationCallScenariosTest is SpokeLiquidationCallBaseTest {
})
);
}

/// @dev a paused peripheral asset won't block a liquidation
function test_scenario_paused_asset() public {
uint256 collateralReserveId = _wethReserveId(spoke);
uint256 debtReserveId = _daiReserveId(spoke);

_increaseCollateralSupply(spoke, collateralReserveId, 10e18, user);
// borrow usdx as peripheral debt asset not directly involved in liquidation
_openSupplyPosition(spoke, _usdxReserveId(spoke), 100e6);
Utils.borrow(spoke, _usdxReserveId(spoke), user, 100e6, user);
_makeUserLiquidatable(spoke, user, debtReserveId, 0.95e18);

// set spoke paused
IHub hub = _hub(spoke, _usdxReserveId(spoke));
_updateSpokePaused(hub, usdxAssetId, address(spoke), true);

_openSupplyPosition(spoke, collateralReserveId, MAX_SUPPLY_AMOUNT);

vm.expectCall(
address(hub),
abi.encodeWithSelector(IHubBase.refreshPremium.selector, usdxAssetId)
);

vm.prank(liquidator);
spoke.liquidationCall(collateralReserveId, debtReserveId, user, type(uint256).max, false);
}

/// @dev a paused peripheral asset won't block a liquidation with deficit
function test_scenario_paused_asset_with_deficit() public {
uint256 collateralReserveId = _wethReserveId(spoke);
uint256 debtReserveId = _daiReserveId(spoke);

_increaseCollateralSupply(spoke, collateralReserveId, 10e18, user);
// borrow usdx as peripheral debt asset not directly involved in liquidation
_openSupplyPosition(spoke, _usdxReserveId(spoke), 100e6);
Utils.borrow(spoke, _usdxReserveId(spoke), user, 100e6, user);
// make user unhealthy to result in deficit
_makeUserLiquidatable(spoke, user, debtReserveId, 0.5e18);

// set spoke paused
IHub hub = _hub(spoke, _usdxReserveId(spoke));
_updateSpokePaused(hub, usdxAssetId, address(spoke), true);

_openSupplyPosition(spoke, collateralReserveId, MAX_SUPPLY_AMOUNT);

vm.expectCall(
address(hub),
abi.encodeWithSelector(IHubBase.reportDeficit.selector, usdxAssetId)
);

vm.prank(liquidator);
spoke.liquidationCall(collateralReserveId, debtReserveId, user, type(uint256).max, false);
}
}
Loading