SIP-165: Debt Pool Unification


Simple Summary

This SIP seeks to merge the debt pools between L1 and L2, tracking the total synth debt with a common oracle.


Once SIP 156 has been implemented, each chain will have its own isolated debt pool. In order to actually allow synths to be teleported between these two chains, we must ensure that debt is tracked properly across them.

Merging the debt pool across chains is relatively straightforward provided issuance only happens on one chain; the debt oracle will simply need to add together the debt from each chain. On-chain, it will be necessary to update the target c-ratios to be identical, and then perform a synchronised cut-over to the new oracle along with debt ledger updates to account for the new supply each chain is now aware of.


Merging the debt pools is necessary in order to allow synths to be transported freely and fungibly between multiple chains.



The Chainlink oracle must monitor the debt composition across multiple chains, and simultaneously push updates to the aggregated debt number to all of those chains.

To transition to this shared debt pool model, the current L1 and L2 chains must be brought in sync by ensuring the target c-ratio is identical between chains, staking are migrated entirely to L2, and each debt ledger is updated to ensure that staker debt balances properly account for the newly-merged debt value.


Although it would have been possible to transmit debt updates whenever synths are transferred between layers, this is a substantial increment in complexity relative to using the oracle directly. Given the establishment of a debt oracle as of SIP 156, it is straightforward to leverage this new structure to support a unified debt pool.

Technical Specification

SIP 156 only aimed at converting the isolated debt pools on L1 and L2 to use an oracle. However, before synths can transit freely between the chains, their debt pools must be combined.

Cross-Chain Debt Calculation

In order to do this, the Chainlink oracle must aggregate all debt across chains into a single number for staking balances and c-ratios to be computed against. For example:

// Implemented off-chain
function systemDebt(chain, allChains) {
    totalDebt = 0;
    anyInvalid = false;
    for (_chain of allChains) {
        chainDebt, chainInvalid = _chain.systemDebt();
        totalDebt += chainDebt;
        anyInvalid |= chainInvalid;
    return (totalDebt, anyInvalid, chain.systemDebt());

The _chain.systemDebt() function corresponds to the sums over all debt components on a particular chain, as described in SIP-156. The logic here does not differ between chains, but simply queries the relevant debt pool contract on each layer. This function is described so that it is straightforward to add additional chains to the system.

Be aware that, as synths will eventually move between chains, the aggregate debt contribution of a single chain could potentially be negative. Note also that this function returns both the overall system debt, along with the individual system debt for a particular chain. That is, each chain should be aware of its own debt and validity status. The interface of the DebtPool contract must be updated to reflect this.

It will also be important that the debt number is updated simultaneously across all chains. That is, the very same oracle must be responsible for sending the exact same debt number to all chains at the same time, to minimise front minting style risks associated with distinct debt numbers existing on chains between which synths can communicate.

Cross-Chain Debt Shares Calculation

SIP 185 introduces a new debt shares method of tracking the debt percentage ownership of stakers on L1 and L2. In order to combine the debt shares supply between the chains, the Chainlink oracle must update / aggregate the total supply of debt shares across all chains into a single number and reported.

Similar to tracking all debt across chains, the combined system debt shares can be calculated as:

// Implemented off-chain in oracle nodes
function systemDebtShares(chain, allChains) {
    totalDebtShares = 0;
    anyInvalid = false;
    for (_chain of allChains) {
        chainDebtShares= _chain.debtShares();
        totalDebtShares += chainDebtShares;
    return (totalDebtShares, chain.debtShares());

The _chain.debtShares() function corresponds to the total supply of the debt shares on each chain as described in SIP-185. The debt shares supply are 'fungible' in the sense that they will be issued based on the sUSD supply / units on each chain on genesis of the debt shares contracts.

As debt is migrated from different chains across to each other, this would allow the synths to be kept on the chain but the debt ownership / shares to be move across chains (L1 -> L2 for example).

The cut-off from using the total supply of each individual chain's Debt Shares contract to using the System Debt Shares to calculate the debt percentage each staker owes would allow migration of debt ownership across chains.

Coalescence Procedure

In considering how to merge two pre-existing debt pools, the first point to note is that it will be necessary to make sure that issuance can only occur on exactly one chain. This is because, although the SIP's proposal is able to update the aggregated value of the debt pool, it does not make any accomodation for migrating debt ledger entries across. Therefore, if debt is created or destroyed on one chain, the debt ownership percentage of stakers on the other chain will not be properly updated to reflect the change in supply.

So before the debt pools can be merged, issuance and burning on one chain must be disabled. To avoid a liquidity crisis, at this stage, all staked debt on one chain must given a method of seamlessly migrating to the other without unstaking, and incentives to do so. This procedure is out of the scope of the current SIP, but the simplest way of accomplishing this is likely through a bridge contract that can teleport debt between chains, properly reconciling the debt ledger entries as it does so. The debt-migration process, then:

  1. Ensure the target and liquidation issuance ratio settings on each chain are identical.
  2. Enable debt migration.
  3. Launch the new debt oracle to run concurrently with the old one on both chains.
  4. Disable issuance / burning on L1.
  5. Pause issuance / burning on L2.
  6. Add new debt ledger entries on each chain, to record the new incoming debt from the other chain.
  7. Cut over to the new debt oracle aggregators.
  8. Unpause issuance and burning on L2.

On each chain, the value of the new debt ledger entry will be as if the total debt on the opposing chain was suddenly issued from a new wallet, as per the logic within Issuer._addToDebtRegister(). Its value should be computed as follows:

state.lastDebtLedgerEntry() * (1 - (otherChainDebt / (thisChainDebt + otherChainDebt))

Adding new debt ledger entries will require taking control of SynthetixState and calling the appendDebtLedgerValue function directly.

Once these steps have been completed, both chains will share in a common debt pool, and teleporting synths between the individual debt pools can be enabled. Once all debt has been migrated, then the L1 debt ledger can be removed.

Knock-on effects

The main area this will affect is issuance, which must now be restricted to a single chain. Additionally, the following areas will require investigation, as they will be affected by the debt pool merger:

  • Governance: Although users must claim rewards independently on each layer, even if those rewards are accruing to the same wallet address, it will be important to ensure that governance processes respect the newly-combined debt pool, and that vote weights respect the combined weight of a user's staked collateral across all chains.
  • Loans: The interest rates charged on loans are determined by examining an aggregate skew in the system. It's not clear that this can be computed accurately across two chains. It should be verified that the behaviour of the loans contracts only examine the skew on their own chain, not involving the full cross-chain debt if it is impossible to properly account for it.
  • Fees & Inflationary Supply: The inflationary supply is at present issued only on L1. Some accomodation needs to be made to ensure these are being fairly distributed between the chains as debt migrates across, and in such a way that it still incentivises wallets to migrate to L2.

Test Cases


Configurable Values (Via SCCP)


Copyright and related rights waived via CC0.