DeFi Audit #1: Synthetix
Understand the technical and economic workings of the second largest Ethereum synthetic asset protocol.
Hello DeFi’ers, today’s edition is a more technical analysis that I hope you enjoy! It takes a non-trivial amount of time to write these so your support is highly appreciated :)
I did a poll around what people wanted to be the first DeFi Audit for and by a large margin people wanted to see what was going on with Synthetix.
Before I get into the nitty gritty details, it's probably worth taking the time to understand how Synthetix works underneath the hood and what really makes it function.
Basics
Synthetix allows you to generate any asset (currency, gold, stocks etc) by using their native token SNX as collateral. A large benefit of this is that you don't need to go through the process of bringing those asset on-chain, but rather you can deal with a synthetic representation of them. MakerDAO is also a synthetic asset protocol where ETH is the underlying collateral and DAI is the synthetic US dollar produced.
Should the value of the SNX used to generate a synthetic asset drop, the minter must add more SNX to take back their SNX. This is similar to the way MakerDAO works except with Maker you lose 10% of your ETH collateral.
Here's a small example of how all of the above pieces together:
I'm a user with 750 SNX tokens (priced at $1.00)
I deposit my 750 SNX tokens to generate $10 synthetic-US dollars (750% collateralisation ratio required)
Should the value of SNX drops to $0.90 and I want my SNX back, I'll need to deposit $0.90 of sUSD to close this position out.
So far so good? Great. Now the next question is why would anyone purchase and hold SNX tokens to begin with - what's in it for them? I'll explain below.
Inflation based staking rewards. By minting SNX assets, you effectively become a staker of the protocol. Because of this, you're eligible to earn more SNX tokens that come out of the inflation of the protocol.
Exchange trading feeds. Every time someone transfers a SNX-based asset they have to pay a fee. The aggregate of these fees can be claimed by stakers in the protocol.
Uniswap LP rewards. This is part of the first point but is important to note: anyone who mints sETH with SNX and provides liquidity for the sETH and ETH liquidity pool on Uniswap receives additional rewards. This is extremely important as it maintains price parity between sETH and ETH creating a liquid gateway between all of Synthetix's "synths" (synthetic assets).
One of the final pieces to understanding how the Synthetix puzzle works is understanding the cost of minting an asset. In MakerDAO you have the notion of a stability fee which is set by MKR holders on an irregular basis. Synthetix has their own spin where you're actually in competition with other traders.
John uses 7500 SNX to mint 100 sUSD.
Jill also uses 7500 SNX to mint 100 sUSD
The network now has 200 sUSD worth of debt, where John and Jill account for 50% of the debt each
John decides to be a degen trader and purchase 100 sLINK (worth $1 each) with his sUSD (on which he'll pay trading fees for)
Now the price of sLINK actually increases to be worth $4 each so John's 100 sLINK represents $400 worth of value and Jill's position is still worth $100 (sUSD)
The network's total debt is now worth $500 in total. Since John and Jill are responsible for 50% of the debt each, John owes $250 to the network and Jill also owes $250 to the network
The difference between John and Jill is that John made $300 from the price appreciation so he's up a pure $150 ($100 + $300 - $250) while Jill is down $250.
Synthetix's term staking can be quite misleading in this way since it's actually just a incentivising people to open a trade position while taking on the risk of debt accumulating. There are no free lunches.
Note: you can trade synths without holding SNX or ‘staking’.
Let's jump in and do a run down of Synthetix against various criteria.
Ownership Structure and Admin Keys
Synthetix has one of the most complex DeFi architectures I've come across till date with a very heavy use of proxies throughout. Proxies are a way for someone to point to one address, but execute the code from another contract. Think of it as a placeholder that executes code on behalf of something else.
Below is the high level architectural overview of Synthetix's smart contracts and ownership structure:
There's quite a lot going on here but don't worry, I'll be breaking it down as usual.
Starting off, every interaction with the Synthetix ecosystem is the Proxy.sol
contract which has the address: 0xC011A72400E58ecD99Ee497CF89E3775d4bd732F. The two key properties of this contract are the targetAddress
and the owner
. Owner is self explanatory, however the target references the smart contract which all calls are essentially forwarded to. In this case the target is Synthetix.sol
which you can think of as the core of the system (and the token tracker). Owner has the ability to switch the entire implementation of Synthetix's contracts at will which can let them do literally whatever they want. This isn't anything new but I didn't think that the entire system's implementation can be changed at will. Some architectures use proxies in certain places which give users guarantees about what can and can't be changed.
Synthetix.sol
(0x8454190C164e52664Af2c9C24ab58c4e14D6bbE4) is the brain that orchestrates all interactions within the system itself. It has a few responsibilities:
Keeping track of all synth token balances
Listing all the valid synth addresses inside the system
A resolver to fetch the address of any contract in the ecosystem
This contract has the same owner as Proxy.sol
as well. However it has a peculiar variable called selfDestructBeneficiary
currently set to 0xde910777c787903f78c89e7a0bf7f4c435cbb1fe. There is a 28 day time delay before this beneficiary can receive all assets, however it strikes me as a particularly odd thing to include in. Furthermore, 0xde9 is just an ordinary Ethereum address with no multi-sig. It's basically someone's ledger.
I guess the bigger point here is who is the owner and how does it work? The address for the owner is 0xeb3107117fead7de89cd14d463d340a2e6917769 and is another Proxy contract. I couldn't view the implementation on Etherscan directly (due to the proxy) so I wrote some code to get the results directly
The results are as follows:
➜ node snx.js Owners: [ "0xa331986ec34E103D567937B293FF8103330FEAda", "0x9dDD076E9073732eB024195eb944E7eC7149bAF6", "0xD7e5c7eC37cDe3f42597A5018E9320070c288b82", "0x285669F472db908531Ed868B92FC0A39EF60D739", "0xDe910777C787903F78C89e7a0bf7F4C435cBB1Fe", "0x49BE88F0fcC3A8393a59d3688480d7D253C37D2A", "0xb0A23F40De7F776A4f20153e8995eD3E7D7c8487" ]
Threshold: 4
The good news here is that there's 4 addresses that are required to make changes to the main contract.
The bad news here is that there's no time lock so if everyone signs off they can make the change instantly.
Code Quality
Architecture
While I do appreciate small touches like having an on-chain address registry, I strongly dislike the complexity and upgrade controls proxies introduce into a smart contract system. Synthetix's entire architecture heavily relies on proxies. From an integration point of view, it means that the Synthetix system you interact with one block might actually look completely different the next block (if they push an upgrade). Other protocols might have upgrade controls implemented but you know which specific parts of the system can be upgraded since the contract you're interacting with will never change, only certain bits of it. This is when you make your code highly modular and admins can replace different modules within their system instead of replacing the entire system. Using a proxy architecture lets you defer that decision all together and just ship quick with complete control.
Even the tokens have their own proxies that they're deployed behind. I can understand the desire to make complete upgrades but if you have 1 proxy for every contract you're probably overusing them in my personal opinion. I'll cover other architectures in the future where they achieve similar upgrade benefits through more elegant, simple proxy-less structures.
Documentation
One area which I was highly impressed with was Synthetix's documentation. They've got diagrams showing inheritance structures, easy to access contract addresses and plenty more which you don't usually find from DeFi teams. While writing this guide, their documentation was able to assist me quite a bit in understanding how their system work in general.
Unit Testing
From digging through their code, it looks like they do use tests but one thing which stood out was that their tests are integration tests, not unit tests. The difference being that their tests check to make sure things work, not that they can work/defend against malicious or unintended inputs. Synthetix is more onto it with getting audits for major deployments, although my general feeling is that there's probably an exploit out there some where since audit companies are 80% effective at best. Show me a smart contract exploit and I'll show you the company who gave an audit.
General Commentary
I'm being a bit opinionated here but I found Synthetix's developer tooling to be just okay. Their Javascript library relies on JSON ABI files to be updated rather than using Typescript typings which provides integration guarantees. Set, dYdX & 0x use Typescript and to great benefit. Their Javascript library doesn't have extensive testing to ensure that any ABI changes are breaking throughout their system. It's not a major deal but tells me more so about how much a team cares about developer experience and the ease of which to integrate their smart contracts in an external system.
Liquidity Analysis
The two largest Uniswap pools at the moment are ETH/SETH and ETH/SNX. ETH/SETH is the size it is mainly due to the Synthetix inflation rewards that grant users more SNX tokens for providing liquidity on Uniswap.
However this is where I realise that Synthetix's model isn't really sustainable in the long run unless they manage to overcome some really hard challenges.
As noted earlier on, Synthetic assets are kind of guaranteed to have the collateral they claim since there's no liquidations - only debt that needs to be repaid. The system faces potential under-collateralisation issues (although at 750% it's quite far away). Future SIPs propose to fix this issue although implementation is to be seen.
Holders of Synthetic assets are holding something that isn't exactly redeemable for stable collateral. Should the price of SNX start dropping rapidly, many positions start becoming undercollateralised and even if you could redeem SNX it would be facing a bank run of sorts.
Due to staking incentives, only 20% of SNX supply is not actively being staked which begs the question that how will organic, healthy liquidity originate elsewhere if most of it is being sucked up? Remember: you actually need healthy demand for SNX outside of an incentive mechanisms for the synthetic assets to have true value.
I hate Coinmarket Cap just as much as anyone else but even their unreliable data gives the following data about Synthetix's liquidity OUTSIDE of Uniswap.
The point I'm trying to make here isn't that Synthetix is doomed, but rather that it needs a few things in order for it to truly succeed as a Synthetic asset protocol:
SNX to appreciate in price and gain liquidity outside of Uniswap or other incentivised mechanisms
SNX will only derive value/demand if it can generate trading fees large enough for people to care ($7m has been earned till date which suggests some chance)
Trading fees will only be accrued if people open synthetic positions, trade them and actually use the synthetic asset for it's intended use case
People will only treat the synthetic assets as a MoE if they hold value or perceive that it holds some sort of value
Until SNX gains liquidity, people won't have confidence to hold synths and neither will other ecosystem participants list on other exchanges.
Unless you noticed this is essentially a difficult chicken and egg problem where you need both to truly succeed. Maker avoided this issue by using ETH as the complete collateral base. Synthetix is taking cues by introducing ETH collateral but the tension always lies in the fact that token holders will want more SNX than ETH to ensure SNX can become more money-like.
Oracle Analysis
During the early days of Synthetix, an oracle went down and someone used it to mint 37m SNX. This was a large shake up in people's confidence however the team aggressively moved to using Chainlink oracles instead.
While I would like to do an analysis on Chainlink, that's out of scope for this piece. The team does run some of their own oracles which you can view directly over here: https://developer.synthetix.io/tokens/
It's essentially just one address that publishes prices to a smart contract and updates it. I'm not sure the opsec would be on these but we know for sure it's connect to a hot wallet. In Synthetix's case having multiple private keys internally that are connected to the internet is a massive danger. I've faced this issue working as a developer in places I've worked but unfortunately there is no good answer at this point in time. My only recommendation to the team is to slow down and figure out a more robust opsec procedure before adding another 10 centralised oracle price feeds to your network as Synthetix will eventually become a CEX honey-pot. The team does have an aggressive roadmap they're trying to achieve, hopefully they know when to slow down, focus on the base and when to aggressively ship.
Insurance Liquidity
At the moment, neither Nexus Mutual or Opyn cover Synthetix's contracts. It'd be good if down the line part of inflation rewards could be used to seed liquidity into providing insurance for Synthetix users in the case of a default. This may give more confidence to users about the viability of holding SNX or synths.
Wrapping Up
This marks the end of my deep dive into Synthetix and my findings after a deep dive into it. Overall, Synthetix is clearly a pioneer in the DeFi space with some highly unique and effective strategies towards ensuring a token can be an integral part of an ecosystem while capturing value.
The main challenge moving forward is whether the system can create synths which people want to hold, use in commerce or other non-speculative uses. Till date the team has made some very impressive pivots (from Havven) to the model they have today. I wouldn't write off the model although, I would say, the challenges they've got ahead aren't easy to solve!
My favourite part of this review was probably seeing the extensiveness of documentation and guides written for a 3rd party developer to understand how the entire system works and operates. Assuming they solve many other issues, I'm sure many other developers will come to appreciate this in due time.
Once again, hope you enjoyed reading this piece. Please feel free to reply directly to this email or reach out on Twitter about what you thought!