This corrects a potentially-exploitable problem whereby issuer’s debt balances immediately after issuing synths are less than the sUSD they have minted.
Since the EtherWrapper cap was raised, calculation of the excluded debt component of partial debt updates has been interfering with incrementing the cached debt number upon issuance of synths. This SIP resolves the issue by not computing the excluded debt component during sUSD issuance.
The attack surface is limited by the size of a user’s wallet, since the available profit is at most the user’s issued fraction of the debt pool. The burn lock (SIP 40) also makes this difficult to take advantage of in practice. However, if a debt snapshot is not performed quickly enough, an issuer could make free profit by burning their newly-issued sUSD, freeing up all their staked SNX while leaving some sUSD left over.
DebtCache._updateCachedSynthDebtsWithRates function is called during issuance to update the cached debt with the
new synth supply. Due to the way non-SNX-backed debt was being excluded from the cached debt in this function,
once the value of non-SNX-backed debt exceeded the circulating sUSD supply the delta being applied truncated to zero.
Consequently, the newly-issued synths were effectively invisible to the debt cache, and everyone’s debt balance
will be underreported until a fresh snapshot is taken.
The existing logic is incorrect, and this SIP proposes to correct it. It should be noted that as it stands, the offending code will now be completely unused. This is intentional until a more robust pass is made on the debt pool when the L1 and L2 debt pools are unified. This means that the excluded debt component will only be recomputed on full snapshots.
No changes will be made to the external API, the fix is limited to one internal function only.
Upon issuance, the
DebtCache._updateCachedSynthDebtsWithRates function is called to update the contribution of
sUSD to the debt pool. During this procedure, code approximately equivalent to the following is executed:
// Compute the change to apply to the cached debt uint excludedDebt = _cachedSynthDebt[EXCLUDED_DEBT_KEY]; uint cachedSum = _cachedSynthDebt[sUSD].floorsub(excludedDebt); uint currentSum = sUSD.totalSupply().floorsub(excludedDebt); // Apply that change _cachedDebt = _cachedDebt.sub(cachedSum).add(currentSum);
floorsub function is the same as ordinary subtraction except that it clamps the result to
zero if it would be negative.
At the time of writing, the circulating sUSD supply is worth around $200 million, while the excluded debt component
is worth more than $500 million. Hence the values of
currentSum truncate to zero, and the
result is that
_cachedDebt is not updated.
As a result, debt balances underreported until the value of
_cachedDebt is updated by a full snapshot.
If the newly issued amount of synths is
x, then debt balances will be off by a factor about equal to
_cachedDebt / (_cachedDebt + x).
The solution proposed in the implementation
is to execute no logic related to
excludedDebt at all during the issuance process. Not only
does this resolve the issue, but it is actually unnecessary to be touching this value at all during issuance.
See the accompanying pull request.
Configurable Values (Via SCCP)
Copyright and related rights waived via CC0.