diff --git a/markets/perps-market/contracts/interfaces/IGlobalPerpsMarketModule.sol b/markets/perps-market/contracts/interfaces/IGlobalPerpsMarketModule.sol index b9ef04a9a0..88709999ee 100644 --- a/markets/perps-market/contracts/interfaces/IGlobalPerpsMarketModule.sol +++ b/markets/perps-market/contracts/interfaces/IGlobalPerpsMarketModule.sol @@ -236,4 +236,17 @@ interface IGlobalPerpsMarketModule { * @dev InterestRateUpdated event is emitted */ function updateInterestRate() external; + + /** + * @notice Checks if a market is solvent within its capacity when considering a position change. + * @param marketId The ID of the market to check. + * @param accountId The ID of the account involved in the market. + * @param sizeDelta The change in position size to be considered. + * @return isMarketSolvent A boolean indicating whether the market is solvent after the position change. + */ + function isWithinMarketCapacity( + uint128 marketId, + uint128 accountId, + int256 sizeDelta + ) external view returns (bool isMarketSolvent); } diff --git a/markets/perps-market/contracts/modules/GlobalPerpsMarketModule.sol b/markets/perps-market/contracts/modules/GlobalPerpsMarketModule.sol index 68d3f30e81..994631238f 100644 --- a/markets/perps-market/contracts/modules/GlobalPerpsMarketModule.sol +++ b/markets/perps-market/contracts/modules/GlobalPerpsMarketModule.sol @@ -15,6 +15,8 @@ import {OwnableStorage} from "@synthetixio/core-contracts/contracts/ownership/Ow import {AddressError} from "@synthetixio/core-contracts/contracts/errors/AddressError.sol"; import {ParameterError} from "@synthetixio/core-contracts/contracts/errors/ParameterError.sol"; import {KeeperCosts} from "../storage/KeeperCosts.sol"; +import {PerpsMarket} from "../storage/PerpsMarket.sol"; +import {MathUtil} from "../utils/MathUtil.sol"; /** * @title Module for global Perps Market settings. @@ -25,6 +27,7 @@ contract GlobalPerpsMarketModule is IGlobalPerpsMarketModule { using GlobalPerpsMarket for GlobalPerpsMarket.Data; using SetUtil for SetUtil.UintSet; using KeeperCosts for KeeperCosts.Data; + using PerpsMarket for PerpsMarket.Data; /** * @inheritdoc IGlobalPerpsMarketModule @@ -271,4 +274,25 @@ contract GlobalPerpsMarketModule is IGlobalPerpsMarketModule { emit InterestRateUpdated(PerpsMarketFactory.load().perpsMarketId, interestRate); } + + /** + * @inheritdoc IGlobalPerpsMarketModule + */ + function isWithinMarketCapacity(uint128 marketId, uint128 accountId, int256 sizeDelta) external view override returns (bool isMarketSolvent) { + int128 oldPositionSize = PerpsMarket.accountPosition(marketId, accountId); + int128 newPositionSize = oldPositionSize + sizeDelta; + + uint256 newMagnitude = MathUtil.abs(runtime.newPositionSize); + uint256 oldMagnitude = MathUtil.abs(oldPositionSize); + + bool sameSide = newPositionSize > 0 == oldPositionSize > 0; + + if (newMagnitude > oldMagnitude || !sameSide) { + int256 lockedCreditDelta = PerpsMarket.requiredCreditForSize(sizeDelta, PerpsPrice.Tolerance.DEFAULT); + (bool isMarketSolvent, , ) = GlobalPerpsMarket.isMarketSolventForCreditDelta(sizeDelta); + return isMarketSolvent; + } + + return true; + } }