From 659899c3c1768b39c5ec385df9db3f97c1a60e3c Mon Sep 17 00:00:00 2001 From: Daniel Beal Date: Mon, 18 Nov 2024 22:16:21 -0800 Subject: [PATCH] add some new read-only functions to market and pool should make it much easier to understand the underlying computations that go behind capacity locked or similar issues in the future --- .../interfaces/IMarketManagerModule.sol | 30 +++++++++++++++ .../contracts/interfaces/IPoolModule.sol | 14 +++++++ .../modules/core/MarketManagerModule.sol | 37 +++++++++++++++++++ .../contracts/modules/core/PoolModule.sol | 16 ++++++++ .../synthetix/contracts/storage/Market.sol | 18 +++++++-- protocol/synthetix/contracts/storage/Pool.sol | 18 ++++++--- 6 files changed, 124 insertions(+), 9 deletions(-) diff --git a/protocol/synthetix/contracts/interfaces/IMarketManagerModule.sol b/protocol/synthetix/contracts/interfaces/IMarketManagerModule.sol index dad485e0f6..abf522a139 100644 --- a/protocol/synthetix/contracts/interfaces/IMarketManagerModule.sol +++ b/protocol/synthetix/contracts/interfaces/IMarketManagerModule.sol @@ -246,10 +246,40 @@ interface IMarketManagerModule { */ function getMinLiquidityRatio(uint128 marketId) external view returns (uint256 minRatioD18); + /** + * @notice Retrieves a list of pool ids supplying liquidity to the market. Additionally, returns a list of pool ids registered to the market, but not actively providing liquidity + * @param marketId the id of the market + */ function getMarketPools( uint128 marketId ) external returns (uint128[] memory inRangePoolIds, uint128[] memory outRangePoolIds); + /** + * @notice Retrieves the maximum value per share tolerated by a pool before it will bumped out of the pool + */ + function getMarketPoolMaxDebtPerShare( + uint128 marketId, + uint128 poolId + ) external view returns (int256); + + /** + * @notice Retrieves the amount of credit capacity added to the total provided by a single pool attached the market + * @param marketId the id of the market + * @param poolId the id of the specific pool to retrieve capacity contribution for + */ + function getMarketCapacityContributionFromPool( + uint128 marketId, + uint128 poolId + ) external view returns (uint256); + + /** + * @notice Retrieves internal data about the debt distribution on the market + * @param marketId the id of the market + * @param poolId the id of the specific pool to retrieve sharesD18 for + * @return sharesD18 the number of shares (USD denominated) supplied by the supplied pool in the market + * @return totalSharesD18 the number of shares (USD denominated) supplied by all pools attached to the market + * @return valuePerShareD27 the current value per share of the debt distribution + */ function getMarketPoolDebtDistribution( uint128 marketId, uint128 poolId diff --git a/protocol/synthetix/contracts/interfaces/IPoolModule.sol b/protocol/synthetix/contracts/interfaces/IPoolModule.sol index 1f6249f1eb..5c65e77331 100644 --- a/protocol/synthetix/contracts/interfaces/IPoolModule.sol +++ b/protocol/synthetix/contracts/interfaces/IPoolModule.sol @@ -217,6 +217,20 @@ interface IPoolModule { */ function getNominatedPoolOwner(uint128 poolId) external view returns (address nominatedOwner); + /** + * @notice Returns the current pool debt + * @param poolId The id of the pool whose total debt is being queried + * @return totalDebtD18 The total debt of all vaults put together + */ + function getPoolTotalDebt(uint128 poolId) external view returns (int256 totalDebtD18); + + /** + * @notice Returns the current pool debt divided by the computed value of the underlying vault liquidity + * @param poolId The id of the pool whose total debt is being queried + * @return debtPerShareD18 The total debt of all vaults put together divided by the computed collateral value of those vaults + */ + function getPoolDebtPerShare(uint128 poolId) external view returns (int256 debtPerShareD18); + /** * @notice Allows the system owner (not the pool owner) to set the system-wide minimum liquidity ratio. * @param minLiquidityRatio The new system-wide minimum liquidity ratio, denominated with 18 decimals of precision. (100% is represented by 1 followed by 18 zeros.) diff --git a/protocol/synthetix/contracts/modules/core/MarketManagerModule.sol b/protocol/synthetix/contracts/modules/core/MarketManagerModule.sol index a36df20fd2..af0c088039 100644 --- a/protocol/synthetix/contracts/modules/core/MarketManagerModule.sol +++ b/protocol/synthetix/contracts/modules/core/MarketManagerModule.sol @@ -172,6 +172,43 @@ contract MarketManagerModule is IMarketManagerModule { } } + /** + * @inheritdoc IMarketManagerModule + */ + function getMarketPoolMaxDebtPerShare( + uint128 marketId, + uint128 poolId + ) external view override returns (int256) { + Market.Data storage market = Market.load(marketId); + return market.getPoolMaxDebtPerShare(poolId); + } + + /** + * @inheritdoc IMarketManagerModule + */ + function getMarketCapacityContributionFromPool( + uint128 marketId, + uint128 poolId + ) external view override returns (uint256) { + Market.Data storage market = Market.load(marketId); + + int256 currentValuePerShare = market.poolsDebtDistribution.getValuePerShare(); + int256 poolMaxValuePerShare = market.getPoolMaxDebtPerShare(poolId); + + // the getCreditCapacityContribution function could throw a confusing error if the max value per share is less than value per share + if (currentValuePerShare > poolMaxValuePerShare) { + return 0; + } + + return + market + .getCreditCapacityContribution( + market.getPoolCreditCapacity(poolId), + poolMaxValuePerShare + ) + .toUint(); + } + /** * @inheritdoc IMarketManagerModule */ diff --git a/protocol/synthetix/contracts/modules/core/PoolModule.sol b/protocol/synthetix/contracts/modules/core/PoolModule.sol index 1cd2784b73..be1fd22c7d 100644 --- a/protocol/synthetix/contracts/modules/core/PoolModule.sol +++ b/protocol/synthetix/contracts/modules/core/PoolModule.sol @@ -305,6 +305,22 @@ contract PoolModule is IPoolModule { return Pool.load(poolId).name; } + /** + * @inheritdoc IPoolModule + */ + function getPoolTotalDebt(uint128 poolId) external view override returns (int256 totalDebtD18) { + return Pool.load(poolId).totalVaultDebtsD18; + } + + /** + * @inheritdoc IPoolModule + */ + function getPoolDebtPerShare( + uint128 poolId + ) external view override returns (int256 debtPerShareD18) { + (, debtPerShareD18) = Pool.load(poolId).getCurrentCreditCapacityAndDebtPerShare(); + } + /** * @inheritdoc IPoolModule */ diff --git a/protocol/synthetix/contracts/storage/Market.sol b/protocol/synthetix/contracts/storage/Market.sol index 06c1e19abb..fe1084275b 100644 --- a/protocol/synthetix/contracts/storage/Market.sol +++ b/protocol/synthetix/contracts/storage/Market.sol @@ -263,7 +263,7 @@ library Market { /** * @dev Returns the amount of credit capacity that a certain pool provides to the market. - + * * This credit capacity is obtained by reading the amount of shares that the pool has in the market's debt distribution, which represents the amount of USD denominated credit capacity that the pool has provided to the market. */ function getPoolCreditCapacity( @@ -343,6 +343,16 @@ library Market { return self.poolsDebtDistribution.getValuePerShare(); } + /** + * @dev Returns the debt per share at which a pool will be bumped from a market + */ + function getPoolMaxDebtPerShare( + Data storage self, + uint128 poolId + ) internal view returns (int256 maxShareValueD18) { + return -self.inRangePools.getById(poolId).priority; + } + /** * @dev Determine the amount of debt the pool would assume if its lastValue was updated * Needed for optimization. @@ -395,7 +405,7 @@ library Market { int256 newPoolMaxShareValueD18 ) internal returns (int256 debtChangeD18) { uint256 oldCreditCapacityD18 = getPoolCreditCapacity(self, poolId); - int256 oldPoolMaxShareValueD18 = -self.inRangePools.getById(poolId).priority; + int256 oldPoolMaxShareValueD18 = getPoolMaxDebtPerShare(self, poolId); // Sanity checks // require(oldPoolMaxShareValue == 0, "value is not 0"); @@ -537,10 +547,10 @@ library Market { // 2 cases where we want to break out of this loop if ( // If there is no pool in range, and we are going down - (maxDistributedD18 - actuallyDistributedD18 > 0 && - self.poolsDebtDistribution.totalSharesD18 == 0) || // If there is a pool in ragne, and the lowest max value per share does not hit the limit, exit // Note: `-edgePool.priority` is actually the max value per share limit of the pool + (maxDistributedD18 - actuallyDistributedD18 > 0 && + self.poolsDebtDistribution.totalSharesD18 == 0) || (self.poolsDebtDistribution.totalSharesD18 > 0 && -edgePool.priority >= k * getTargetValuePerShare(self, (maxDistributedD18 - actuallyDistributedD18))) diff --git a/protocol/synthetix/contracts/storage/Pool.sol b/protocol/synthetix/contracts/storage/Pool.sol index c67c384d02..9737c89d04 100644 --- a/protocol/synthetix/contracts/storage/Pool.sol +++ b/protocol/synthetix/contracts/storage/Pool.sol @@ -194,11 +194,10 @@ library Pool { // Read from storage once, before entering the loop below. // These values should not change while iterating through each market. - uint256 totalCreditCapacityD18 = self.vaultsDebtDistribution.totalSharesD18; - int128 debtPerShareD18 = totalCreditCapacityD18 > 0 // solhint-disable-next-line numcast/safe-cast - ? int256(self.totalVaultDebtsD18).divDecimal(totalCreditCapacityD18.toInt()).to128() // solhint-disable-next-line numcast/safe-cast - : int128(0); - + ( + uint256 totalCreditCapacityD18, + int128 debtPerShareD18 + ) = getCurrentCreditCapacityAndDebtPerShare(self); uint256 systemMinLiquidityRatioD18 = SystemPoolConfiguration.load().minLiquidityRatioD18; // Loop through the pool's markets, applying market weights, and tracking how this changes the amount of debt that this pool is responsible for. @@ -245,6 +244,15 @@ library Pool { } } + function getCurrentCreditCapacityAndDebtPerShare( + Data storage self + ) internal view returns (uint256 totalCreditCapacityD18, int128 debtPerShareD18) { + totalCreditCapacityD18 = self.vaultsDebtDistribution.totalSharesD18; + debtPerShareD18 = totalCreditCapacityD18 > 0 // solhint-disable-next-line numcast/safe-cast + ? int256(self.totalVaultDebtsD18).divDecimal(totalCreditCapacityD18.toInt()).to128() // solhint-disable-next-line numcast/safe-cast + : int128(0); + } + /** * @dev Determines the resulting maximum value per share for a market, according to a system-wide minimum liquidity ratio. This prevents markets from assigning more debt to pools than they have collateral to cover. *