Circle's USDC Technical Overview
Coinbase adds its first stablecoin tied to the US Dollar. We have entered a new era of legitimate stablecoins. USDC issued by Circle, which is backed by Goldman Sachs. Making it as official and legal as it can be compared to shady Tether.
Contrary to Tether, which is implemented on Bitcoin's Omni layer, USDC implements on Ethereum as an ERC-20 Token.
Written by centre.io USDC source code available on GitHub and has a decent documentation. The source code is verified on etherscan. Let's take a look.
Proxy Contract
Original address points to the proxy contract, written by OpenZeppelin. Essentially it enables admin to replace the implementation of USDC with whatever code at any time. That totally destroys entire point of EVM contracts immutability and thus trustlessness of smart contract. In other words, USDC is 100% trust based venture. I guess old banks really like to keep power in their hands to screw anyone at any time.
Yet at the same, it represents a critical point of failure for USDC itself. In case admin private key is stolen, entire USDC token can be destroyed and all users/balances lost, because a new contract can overwrite data on purpose. That would be a catastrophe.
ERC-20
The proxy itself points to ERC-20 Token implementation. The code is available and verified on etherscan. The contract is called FiatTokenV1
and it is a custom implementation of ERC-20 Token heavy influenced by OpenZeppelin with the addition of many features.
contract FiatTokenV1 is Ownable, ERC20, Pausable, Blacklistable
FiatTokenV1 implements ERC-20 protocol with few exceptions:
- A blacklisted address will be unable to call transfer, transferFrom, or approve, and will be unable to receive tokens.
- transfer, transferFrom, and approve will fail if the contract has been paused.
In other words, they can kick out anyone from the system, rendering his funds useless. And they can stop entire USDC token from moving anything.
Governance
What I really like about FiatTokenV1 is a separation of roles. It defines the following roles:
- masterMinter - adds and removes minters and increases their minting allowance
- minters - create and destroy tokens
- pauser - pause the contract, which prevents all transfers, minting, and burning
- blacklister - prevent all transfers to or from a particular address, and prevents that address from minting or burning
- owner - re-assign any of the roles except for admin
- admin - upgrade the contract, and re-assign itself
Such model looks like proper governance on blockchain! Again compare to Tether there, essentially one company/person probably runs it all. We have a clear separation of jobs here baked into a smart contract.
Implementation Details
Let's dig into the source code. We are not going to cover implementations of Ownable, Pausable and Blacklistable, because they are trivial. Instead, we focus on FiatTokenV1.
The first thing which seems strange as that all internal
variables named using camelCase notation, like balances
, allowed
and etc. Yet totalSupply_
has the underscore at the end. This is confusing. It is not consistent with other fields and contradicts OpenZeppelin notation of using an underscore at the start to mark contract's fields _field
.
Moreover, FiatTokenV1 use the _parameter
notation for some functions and parameter
for other. Highly inconsistent.
I would say it probably caused by different people writing different parts of the code, but I don't see why it can be reviewed and fixed.
FiatTokenV1 is inconsistent with its assertions as well. At some functions, it does check for addresses not being 0
, but at some, it doesn't. Proof.
Again, it is just inconsistent and may lead to confusion and mistakes in the future. Canonical ERC-20 implementation by OpenZeppelin does check for 0
address equality every time.
Solidity Style Guide defines the order of functions. I understand it is just a recommendation, but FiatTokenV1 has all its functions mixed up. They go in the following order:
- constructor
- modifier
- public
- modifier
- public
...
I believe it is reasonable to put some effort in 200 lines of code, which are going to handle billions of dollars on daily basis.
There are a decent amount of tests on GitHub, which is always a good thing. I haven't looked too much into them.
Attack Vectors
Only three roles represent obvious attack target: admin, owner, and masterMinter. One way or another they all can provide access to minting USDC to an attacker. Assuming that in the near future USDC will be supported on many exchanges including decentralized exchanges. The attacker can quickly swap USDC for Monero or other private coins. As a result, constant monitoring of these address has to be established in order to prevent such things. In the case of admin role, things are even worse. Admin can actually replace the contract with attacker's contract, which may corrupt user's balances and other data. Recovery can take a lot of time using logs and be questionable. Also, only the admin key can resolve issues with owner/masterMinter key. Because of that protection of admin private key is critical for whole USDC token ecosystem.
Conclusion
Circe's USDC is an ERC-20 implementation of stablecoin bound to US Dollar. Looking at Tether success as a trading currency, USDC may have even more success being supported by Coinbase and Goldman Sachs. It is crucial for all parties involved to have a clear understanding of technical nuances because they matter in the blockchain world. Infamous example is Parity wallet hacks. We do not want our money stuck one day due to someone stealing admin private key.
TL;DR
- Uses proxy contract for upgradability.
- Provides transparency and interoperability.
- God separation of governance using roles model.
- Implemented as ERC-20 Token.
- Users can be blacklisted and the whole Token can be paused.
- Proxy contract model is a critical vulnerability because it allows to replace the contract with completely new contract and erase/rewrite all data.