- DoS when the list has more than ~5527 items. Every time the median is calculated, it loops through the entire list to get the size and then loop
size / 2to get the median itself. Consider storing and update the size of the list every time the user insert or remove a node along with the two nodes which are part of the median in thestruct. The worst scenario for an insert could ben - 1loop wherenis the size of the list. Consider using a binary search in order to reduce as much as possible a possible DoS.
- If the size of the sorted list is 0,
medianwill throw. I recommend to check the size and if it is equal to 0, returns 0.
- Possible arithmetic overflow at
medianwhen trying to get the avg of middle values. For rates wouldn't be a problem, but if it is used as a library for other values, could be.
-
elements / 2it is repeated three times atmedianfunction. -
LEFTandRIGHTcan just be namedPREVandNEXT. -
Dev notation of
getAdjacentseems to be out of date._directionis not an expected parameter. -
Lines 59, 109, 130, 147, 179 are unused lines.
PausedandStartedmay have indexed the caller in order to allow traceability of who paused or started it.
-
If for some reason an Oracle is not being provided by a specific time and/or the network is congested, it will return outdated values. Consider adding an on-chain strategy to prevent
readSamplereturns an outdated value. E.g: Check if, at n time passed, at least 51% of signers had provided a new value where n can be something in seconds configurable by each Oracle or global. -
setUpgradeis not checking if the new implementation implements the standard Diaspore RCN Oracle interface0xa265d8e0found atRateOracle.sol. Oracles without the core methods described in the interface can prevent the systems to operate.
setUpgradeshould check if the address is different fromaddress(this)in order to prevent infinite recursion.
-
If a contract is created on top of the oracle, variables as
isymbol,iname,idecimals,itoken,icurrentcould be useful. Consider changing the visibility type fromprivatetointernal. -
Check if
PausedProvideris a contract. -
Function
urlis not used and based on the documentation could end up in a possible misunderstood. Consider removing it. -
_ratecan not be the maximum uint96. Consider making the check inclusive. -
When a signer is removed, an event defined at
OracleFactory.solis emitted:RemoveSigner. This event is also emitted when the signer had been removed before. Emitting the event when actually no signer is removed could generate an inconsistency when tracing/indexing signer addition and removals because, at signer addition, theAddSignerevent is not emitted if the signer had been added. Consider reverting withrequire(isSigner[_signer],, "signer not defined")instead of return.
-
name,symbolanddecimalsare part of the ERC20 standard. Receiving them as parameters could end up differing from the token implementation. Consider getting the value directly from the token contract if applicable because not all of them will be for ERC20. -
Use
address(0)for FIAT like USD or ARS (Argentinian peso) can be mixed with the invalid address. Consider using a specific address to represent coins outside the Ethereum Blockchain. -
Methods can be forwarded to upgrade implementation if apply in order to reduce undesired calls.
-
Move up the check if the signer is already a signer or not to reduce gas cost at
removeSigner. -
nameOfSignerandprovidedByare not being cleared when removing a signer. -
readSample (external)do nothing with its parameter. Consider removing or using it. -
Check if
_equivalentis distinct to 0. -
Consider using uint256 at
providedByto reduce the gas cost of a mask. -
Consider check if
providedBy[_signer] == _ratebefore removing and inserting the same value.
-
If for some reason an oracle should be paused, all the oracles will be paused to. That will end up in pausing all the operations related to the oracles making an outage of the full system. Consider adding granular management to the paused functionality along with the global one.
-
Lack of ownership transfer and resilience. If an Oracle should be upgraded to a new implementation, a new Factory should be deployed too in order to use a new implementation to deploy the Oracles. Also, the maintenance of the two factories will be needed. Consider adding a way to transfer the ownership of the Oracles, and to deploy Oracles based on an implementation stored as a contract variable. Another solution could be to use a Proxy for the Oracles to change its implementation and sort out the upgrade functionality that they already have.
- Events have no indexed parameters and they are all emitted by the factory. That would make really hard for tracking, indexing and debugging. Consider moving Oracle's related events as
RemoveSigner,AddSigner,Provideto the implementation or add the indexed keyword to the parameters which allow filtering and traceability.
-
A re-entrancy ward could be added to methods that call the Oracles in order to prevent undesired behaviors and complex attack vectors.
-
provideMultiplereceives a bytets32 array. Each position has the oracle address and the rate where 12 bytes are for the rate and 20 bytes are for the address. This encoded is being performed outside of the contract and error-prone for non-experienced developers or non-trained operators. Consider adding a new method which based on an array of addresses and uints 96, returns an array of bytes32 or consume more gas by changing the input parameter ofprovideMultipleto two different arrays. -
Possible infinite loop if
provideorprovideMultiplereceives by mistake theOracleFactoryaddress as aMultiSourceOracle. Doingrequire(_oracle != address(this), "_oracle should be different from this")or a re-entrtancy ward as suggested above will prevent it.
- Dev notation is missing in the contracts: function responsibility, notes, params, and return values.
September 6, 2019. Ignacio Mazzara