SIP-328: Debt Distribution View Functions

Author
StatusImplemented
TypeGovernance
NetworkEthereum & Optimism
ImplementorTBD
ReleaseTBD
ProposalLoading status...
Created2023-06-16

Simple Summary

This proposal entails adding two functions to the Market Manager Module of the Synthetix Core System which will make it significantly easier for integrators to retrieve data pertaining to pools’ market exposure.

Abstract

A getMarketPoolDebtDistribution(uint128 marketId, uint128 poolId) function will return the shares of a market’s debt distribution for a given pool and the total number of shares. A getMarketPools(uint marketId) function will return the pools backing a giving market. (It is already possible to retrieve the markets that a pool is backing with the getPoolConfiguration function.)

Motivation

In the Synthetix V3 system, derivatives markets rely on pools of liquidity such that they can always fill orders for traders. When markets accrue debt (by paying out stablecoins to traders or having the value of their issued derivatives increase) or reduce debt (by depositing stablecoins from traders or having the value of their issued derivatives decrease), this change is distributed pro-rata across the vaults of collateral in all the pools backing it. The debt associated with liquidity providers’ positions are adjusted according to their share of the collateral they’ve provided to the vault.

Being able to more easily retrieve the relative exposure of pools to markets has a variety of use cases. If a market is consistently profitable, this value can signal what share of profits would be distributed to which pools. Also—although we anticipate spot markets which enable the skew fee and perpetual futures markets will be generally neutral to price action for liquidity providers—it could be used to create a product that automatically hedges liquidity provider exposure to any price action as necessary.

Specification

Overview

The two functions expose existing data structures in the Synthetix V3 core system. This should be a relatively low-risk system upgrade.

Rationale

The function implementations are not technically view functions, as we call distributeDebtToPools to ensure that the distribution has taken into account any pending updates to the debt distribution chain. This function can be called with callStatic by a client application if the information needs to be retrieved in a context where chain state is not being updated.

Also, the getMarketPools function returns two arrays of pools IDs: inRangePools are those which are currently subject to debt distributions from the specified market, while outRangePools are those temporarily exempt because of their maxDebtShareValue setting.

Technical Specification

The getMarketPoolDebtDistribution function could be implemented as follows:

function getMarketPoolDebtDistribution(
        uint128 marketId,
        uint128 poolId
    )
        external
        override
        returns (uint128 sharesD18, uint128 totalSharesD18, int128 valuePerShareD27)
    {
        Market.Data storage market = Market.load(marketId);
        market.distributeDebtToPools(999999999);

        DistributionActor.Data pool = market.poolsDebtDistribution.actorInfo(poolId);
        sharesD18 = pool.sharesD18;

        totalSharesD18 = market.poolsDebtDistribution.totalSharesD18;
        valuePerShareD27 = market.poolsDebtDistribution.valuePerShareD27;
    }

The getMarketPools function could be implemented as follows:

function getMarketPools(
        uint128 marketId
    ) external override returns (uint128[] inRangePoolIds, uint128[] outRangePoolIds) {
        Market.Data storage market = Market.load(marketId);
        market.distributeDebtToPools(999999999);

        HeapUtil.Data storage inRangePools = market.inRangePools;
        for (uint i = 1; i <= heapSize; i++) {
            HeapUtil.Node memory node = inRangePools.getByIndex(i);
            inRangePoolIds[i - 1] = node.id;
        }

        HeapUtil.Data storage outRangePools = market.outRangePools;
        for (uint i = 1; i <= heapSize; i++) {
            HeapUtil.Node memory node = outRangePools.getByIndex(i);
            outRangePoolIds[i - 1] = node.id;
        }
    }

Test Case

An application could take all of the market IDs for a given pool using the getPoolConfiguration function. For each of them, it could query getMarketPoolDebtDistribution and calculate sharesD18.divDecimal(totalSharesD18) from the return values. This would result in the percentage of the market's PnL adjustments that would be distributed to the given pool.

Take the following example scenario:

  • 75% of the liquidity provisioning for a spot synthetic ETH market, with a reportedDebt of $100 (i.e. $100 of issued synthetic ETH)
  • 25% of the liquidity provisioning for spot synthetic BTC market, with a reportedDebt of $200 (i.e. $200 of issued synthetic BTC)

To fully hedge the exposure to ETH and BTC price action for this pool, it would need $75 of ETH and $50 of BTC. Accordingly, a hedging product would allocate 60% (75/125) to ETH and 40% (50/125) to BTC.

Configurable Values (Via SCCP)

N/A

Copyright and related rights waived via CC0.