SIP-246: Upgrade Collateral Short

Author
MEB, Matt, Ethernaut
StatusImplemented
TypeGovernance
NetworkOptimism
ImplementorTBD
ReleaseKochab
ProposalLoading status...
Created2022-05-31

Simple Summary

This SIP proposes an upgrade for the Collateral Short contract to fix a few accounting bugs recently discovered in the contract. It also introduces an owner function to be able to purge debt from the old contract and removes the collapseFeeRate (currently unused).

Abstract

An accounting bug in the Synthetix shorting contract was causing excess debt to accumulate in the shorting contract without being automatically burnt. While the issue was solved by burning excess debt, the opening of new shorts was paused while fixes were conducted.

Motivation

To make sure sUSD is burnt when closing with collateral and debt does not accumulate.

Furthermore, the ability to move users to an upgraded contract ensures that partners do not need to complete any actions when we must upgrade our contracts. Partners like Lyra rely heavily on the collateral shorts contract.

Technical Specification

This SIP requires a general refactoring of the _repayWithCollateral function to account for the proper fees to be paid and to update balances accordingly.

Most importantly, it must implement the burning of sUSD synths in the contract when repaying a loan with collateral:

_synthsUSD().burn(address(this), collateralToRemove);

Additionally, this SIP requires the introduction of an onlyOwner function, called upgradeCollateralShort, that allows for the burning of excess sUSD in the legacy CollateralShort contract as needed:

   /**
    * Function used to migrate balances from the CollateralShort contract
    * @param short The address of the CollateralShort contract to be upgraded
    * @param amount The amount of sUSD collateral to be burnt
    */
   function upgradeCollateralShort(address short, uint amount) external onlyOwner {
       require(short == resolver.getAddress("CollateralShortLegacy"), "Issuer: wrong short address");
       require(address(synths[sUSD]) != address(0), "Issuer: synth doesn't exist");
       require(amount > 0, "Issuer: cannot burn 0 synths");
 
       exchanger().settle(short, sUSD);
 
       synths[sUSD].burn(short, amount);
   }

Note: the legacy CollateralShort contract must be added to the AddressResolver as ā€¯CollateralShortLegacyā€¯ in order for this owner function to work as intended. This is to distinguish the older contract from the newly deployed contract which will be known in the resolver as CollateralShort.

Test Cases

// For `_repayWithCollateral`, it should pay the relevant fees and balances should update accordingly

// The fee pool should have received fees (exchange + accrued interest)
assert.deepEqual(
  await sUSDSynth.balanceOf(FEE_ADDRESS),
  beforeFeePoolBalance.add(sUSDAccruedInterest).add(exchangeFee),
  'The fee pool did not receive enough fees'
);

// The loan amount should have been reduced by the expected amount
// amountRepaid might be less than ethAmountToRepay if there's accrued interest
assert.deepEqual(
  loan.amount,
  ethAmountToShort.sub(amountRepaid),
  'The loan amount was not reduced correctly'
);

// The loan collateral should have been reduced by the expected amount
assert.deepEqual(
  loan.collateral,
  beforeLoanCollateral.sub(collateralToUse),
  'The loan collateral was not reduced correctly'
);

// The contract sUSD balance should have been reduced by the expected amount
assert.deepEqual(
  await sUSDSynth.balanceOf(short.address),
  beforeShortBalance.sub(collateralToUse),
  'The short contracts holds excess sUSD'
);

// The user sUSD balance should remain unchanged
assert.deepEqual(
  await sUSDSynth.balanceOf(account1),
  beforeUserBalance,
  'The user sUSD balance is unexpected'
);

Configurable Values (Via SCCP)

Removed collapseFeeRate for the sake of reducing complexity and since it was not being utilized.

Copyright and related rights waived via CC0.