Overview and description of the contract system
For readers not familiar with the inner workings of the 0xProject's contract system, we provide a brief summary which we hope will provide greater context for understanding this report.
The contract system examined in this review forms the core of the 0x protocol, for on-chain trading of ERC-20 compatible tokens. The system is deployed as 5 contracts, outlined below.
Exchange contract is the primary interface for users to trade through the 0x protocol, and provides a number of
public functions which can be used to fill or cancel orders.
The basic flow for completing an order is:
makersigns a message creating an order according to the protocol specification. This message specifies the parameters of the order, including the token pair (
takerToken) and the corresponding amounts to be traded. Optionally, a
takeraddress may be specified, which will ensure that only that address can fill the order.
- This order and signature can then be communicated off-chain, by any means. The expectations is that 'Relayers' will maintain an order book with a list of orders. In order to be compensated for their services, Relayers may choose to list only orders which specify their address as a
feeRecipient, along with a
makerFee, which must be paid in the 0x protocol token (ZRX). Fees can only be paid in ZRX and this is a key element in the utility of the ZRX token.
- NOTE: Everything up until this point is done off-chain.
takerwho wishes to fill the order may then submit the parameters of the order and its signature to the
Exchangecontract. If both parties have sufficient transferable balances of their respective tokens (and
Exchangewill complete the transfer. Otherwise the call will error.
- Several different functions are available, which provide a
takerwith various options for filling one or more maker orders:
fillOrdersUpTo(). It is important to note that the
takerbears the full gas cost of execution, and loses their gas payment if a transaction is an error.
makersmay also cancel previously signed orders by calling
- If an order has an amount either
cancelled, the amount is recorded in the corresponding
mapping (bytes32 => uint). This is essential for preventing a maker's order from being filled more than once, or filled after cancellation.
Our investigation looked at several risks associated with this contract, including reentrancy, griefing, front running, and rounding errors, which are discussed later in this report.
Proxy is the central contract in the system. Traders must
approve() it to spend a sufficient amount of their token to successfully complete an order.
Proxy has an
authorities array which lists
Exchange contract addresses. To fill an order, these addresses can call
Proxy::transferFrom(), which in turn calls
transferFrom() on both tokens in an exchange.
authorities array can be updated by the
owner address, making it the upgrade mechanism for deploying new versions of the
Exchange contract, and deprecating old ones.
Proxy is the least changeable component in the system, and is necessarily simple in nature. After our initial review, this contract was renamed to
TokenTransferProxy for clarity.
TokenRegistry contract contains a curated list of contract addresses for popular, existing ERC-20 tokens, as well as related metadata including the name, ticker symbol, and hashes for querying Swarm and IPFS. This token metadata is used to populate the UI developed by the 0xProject team.
It is owned and controlled by a single address (see Governance below), which can add or remove token addresses from the listing.
TokenRegistry is separate from the core business logic of the protocol, and supports the use of the system's UI designed by the 0xProject team. The protocol is not limited to tokens listed in the registry, and users are free to specify other tokens for exchange. It is not truly a part of the protocol, but has some curatorial influence on which tokens are likely to be traded most often.
This contract takes a novel approach to the
ZRX token sale, by making use of the 0x protocol itself, but also obscuring that fact from the end user so that the process closely resembles the format of other recent token sales.
The mechanism of the sale looks like this:
- Prior to the start of the sale
- Prospective contributors must register to participate through an off-chain process
ownerof the contract adds these addresses to the
registeredmapping, which serves as a whitelist for allowed contributors
- To initialize the sale:
init()function, which sets the parameters for a large maker order, in which:
owneraddress is the order
TokenDistributionWithRegistrycontract is the order
- Despite the use of the 0x protocol, contributors can participate simply by send
ETHdirectly to the contract. Internally, the process is more complex:
- The fallback function calls
- The contributor's
ETHpayment is deposited to an ERC-20
EtherTokencontract, enabling it to be transferred using the ERC-20 interface. The deposit amount belongs to this
Exchange::fillOrKillOrder()function is called, transferring the
EtherTokenbalance to the
owneraddress, and the
ZRXtoken balance to this
ZRXtoken balance is then forwarded to the contributor.
- The fallback function calls
- There is also a
capPerAddressvalue which increases with time during the sale. Initially, the maximum contribution amount will be set low enough so that all registered addresses can contribute, and over time this will increase until the full value of the
ZRXtoken offering has been distributed. The same address can contribute multiple times up, so long as it does not exceed the current
After our initial review, this contract was renamed to
TokenSale for clarity.
This contract adds two features to the Gnosis Multisig-Wallet.
One is a time lock, which introduces a minimum time delay of
secondsTimeLocked between the submission and execution of any transaction, this feature is implemented in
The second feature allows for this time delay to be bypassed in the specific case that the transaction is a call to
Proxy::removeAuthorizedAddress(). This is intended to allow an
Exchange contract to be quickly disabled in an emergency situation.