SIPs
SIPs copied to clipboard
Rethinking frozen iSynths
iSynths are currently frozen the first time a rate comes in to the ExchangeRates contract via the Synthetix Oracle that hits, or is beyond, either of its limits. The price remains fixed - or frozen - at that limit and remains that way until a purge and price reset from the protocolDAO. This technique won't work with the upcoming migration to Chainlink Phase 2, and needs a replacement.
SIP-61 has been proposed to replace the entire freeze, purge and reset actions with keeper functions, but in its current form does potentially open up attack vectors. Another option of addressing the need to purge and reprice is to replace these functions with a version of Fee Reclamation SIP-37 to track the price diff the user owes. This is much cleaner but still presents the problem of how to freeze iSynths when oracle pricing is decentralized.
Background
The current ExchangeRates contract provides rates pushed from the Synthetix centralized oracle and rates pushed from decentralized oracles onto various Chainlink Aggregator contracts that ExchangeRates reads from. The former is being phased out completely in favor of the latter in SIP-36 and this presents a problem - how do we flag an inverse synth as having reached its limit? We currently perform this in ExchangeRates when the centralized oracle pushes a price (as you can see here), but with decentralized oracles, we cannot hook into their price push transactions the way we can with the centralized oracle.
The frozen flag is what we use within the protocol to indicate that no more price updates will be received for the iSynth until it is purged of holders and the pricing bands reset. Currently users can still exchange in and out of a frozen iSynth, though exchanging into a frozen iSynth is not profitable as the price is fixed and they will eventually have to exchange out again (or be purged) and pay a fee.
That being said, even without the frozen flag, the ExchangeRates contract has to ability to limit the prices for iSynths that are above or below the limits by simply capping the price at the limit that was breeched. For example, let's say iETH has a $200 entryPrice, an upper limit of $300 and lower of $100. When the price of sETH is $350 say, then iETH would be max((2 * 200) - 350, 100) and thereby effectively frozen at 100.
So, given all that, do we still need the frozen flag and if so, how can we decentralize the enabling of it?
Option 1: Remove the frozen flag altogether
We could opt to remove the frozen flag on a synth price completely. Removing the flag altogether reduces the complexity of this SIP considerably but has a few implications.
The primary concern is how to determine, on-chain, that the price has indeed gone outside of the limits in the past if it is currently within it's limits. In other words, in the above example if sETH returns to $250 after temporarily hitting $350, then without the frozen flag the rate of iETH would drop to max((2 * 200) - 250, 100) which is $150.
The flag itself is useful for a few reasons:
-
It is a simple indicator for users to read on and off-chain that no more price updates will occur, until a purge and reset.
-
It allows the system to prevent an iSynth that has received a price outside its limits, from ever getting another price until it is reset.
-
The
purgeaction, currently performed by theprotocolDAO, can check for it as it currently does to allow system-level purging regardless of how much supply is in circulation.
Possible Mitigations
Without the flag, there are still some ways to mitigate these concerns:
-
We can look back on-chain using the
roundIdincrementer inExchangeRatesfor each price provider until the iSynth was created or last repriced, and determine if it was ever outside its limits. The major downside to this approach is it is gas intensive for any on-chain lookup, and could potentially be too gas intensive for a single transaction. We'd need to cap it to looking back the lastNrounds, and how bigNis depends on how much gas to spend looking back. While this is probably manageable from a purging perspective (as it's currently managed by theprotocolDAO), it would be too expensive for any contract to query this on-chain. -
Alternatively we could simply allow iSynths to reactivate if the price returns to within the bounds. Because it is frozen outside the bounds, there is no advantage to exchanging into it as the only way price action will occur is if the price returns to back within the bounds, which is less and less likely the further from the bounds it is. However, the concern here is that when the
protocolDAOpurges, the price could have again returned to within the bounds, and so on-chain the check forfrozenwould fail and purge would be blocked. In that case either a) we prevent purging in those instances, requiring theprotocolDAOwait until the price goes out of bounds again or b) we allow purging of an iSynth regardless of it being frozen. The former is probably an acceptable compromise as long as purging/resetting can be done in a timely manner, the latter however is probably too concerning for iSynth holders knowing that they could be purged at any time. As for usability, it's arguable that not having iSynths frozen and allowing them to regain pricing if they return to in-bounds pricing is useful. It means that if iSynths momentarily hit their limits, they could still remain useful without requiring the friction of a purge and reset.
Option 2: Bundle frozen into exchanges & transfers
Like Fee Reclamation handles settlement within subsequent exchanges, we could amend any exchange in (unlikely as the price will be capped already from ExchangeRates) or out of an iSynth or a transfer of it to check the current rate in ExchangeRates and if outside the limit, then to enable the frozen.
Concerns
-
Gas. This will cost the user who is transacting successfully to exchange or more gas, but shouldn't be more than about 50k, and only for the first user who transacts with that iSynth once it is outside the range.
-
If no user activity happens during a time when the price is outside of the range, and if the price returns to the range,
frozenwill not be applied.
Option 3: Incentivize freeze
As with Option 2 above, but freeze would be incentivized with SNX, say, as per a keeper function as per the freezing in SIP-61.
Proposal
So either we remove the frozen flag and accept that an iSynth, while always capped within its upper and lower bounds, can still yet move again once hitting its bounds, yet only allow purging when the current price is out of bounds; or we bundle freezing into exchange/transfers and potentially look incentivize it if there's concern it won't be frozen as soon as an out-of-bounds price appears.
The other issue for option 1 is it is essentially a free option in the case where the iSynth has hit its lower bound. For example if the lower bound of iETH is $45 and it is $44 I can buy in and know that I will only lose the exchange fee and could have significant profit if the price moves back in my favour. That alone should exclude it from consideration. I still think a direct fee to flag a frozen Synth is the most efficient and given it is not exploitable in that it can only be called once for each synth doesn't feel very gameable to me.
So I strongly favour option 3.
Even with option 1, the price will never go outside the bands; recall I mentioned that regardless of the flag we can ensure that the ExchangeRates contract always shows a fixed price whenever the current price is above or below a limit. So you can never buy for $44 as per your example. You could still buy it at 45, but why would you? You hope that it will again over 45 but for every dollar it goes under 45 it’s further and further away from coming back.
Sorry my example was wrong, you would have to buy at $45, but the logic remains. If you can buy something with no downside and +ve upside it is a huge free option and the cost is only the exchange fee.
Ah yeah that makes sense, so scrap option 1. A combined option 2 & 3 could be the safest option though. We can embed the freeze action in an exchange/transfer as well as allowing anyone to call it. Additionally I suggest the reward be in sETH that way we can pay back relative to the gas paid (with an upper limit of gwei to prevent any kind of free ETH/sETH conversion from it)
Where does the sETH come from though? We have no easy way to ensure sufficient sETH reserves to pay for this over time vs paying in SNX from a pool diverted from inflation.
From the debt pool - we could issue it like we do with fee reclamation rebates.
I really don't love the idea of just creating debt out of thin air for protocol incentives.