Tierion Presale Audit

The Tierion team asked us to review and audit their Tierion Presale code. We looked at the contract and now publish our results.

The audited contract is in the file TierionPresale.sol with SHA-1 hash e9462ae354e2b4acbd75ff46c7dc1c476a1d1925. The version of OpenZeppelin used is 1.1.0.

The code is very lean and well commented. Good work reusing code!

Here’s our assessment and recommendations, in order of importance.

UPDATE: Tierion has implemented all of our suggestions and fixed all issues. We have found no problems in the updated code. The new TierionPresale.sol file has SHA-1 hash c7cb106b0627bfb3f1f35b019968451947fa0081. We have added a notice to each entry accordingly.

Severe

Unsafe use of Destructible

As seen in line 7, the contract is Destructible: the owner can destroy it at any moment, forwarding the funds to an address. It should be first noted that similar functionality is provided by the contract’s own withdraw function, insofar as all of the contract’s funds can be forwarded to the owner. There are two problems with this.

Unlike withdraw, Destructible’s destroy can be called at any moment, including when the contract is paused. It seems wrong that there is a deliberate restriction on when withdraw can be called, while the same purpose can be achieved via destroy at any time.

Withdrawing a contract’s balance, however, is not destroy’s raison d’être. It is removing the smart contract’s code from its address. A huge implication of this is that after a contract is destroyed, transactions sent to the address will no longer be interpreted as function calls, but as simple ETH transfers. Ether sent to a destroyed contract is effectively lost forever. We strongly recommend against making any contract whose purpose is to receive payments an instance ofDestructible. Someone could send a payment after destruction without knowing their payment won’t be received and their money would be lost.

Consider not inheriting from Destructible, and using the contract’s withdraw function for withdrawal.

UPDATE: Our recommendation was followed and the contract is no longer Destructible. In this way the contract is no longer vulnerable to the potential loss of Ether described above.

Potential Problems

Receipt event has confusing semantics

The Receipt event is emitted, in line 29, for each payment processed. It logs the investor address, payment hash, and amount of ether received. The same event is emitted, with an empty hash, when the owner withdraws the collected ether in line 37. This overloading of the event’s semantics seems confusing. Consider emitting instead a separate Withdrawal event in withdraw.

UPDATE: The event has been removed from the withdraw function. It is now only emitted for payments, which are clearer semantics.

Warnings

Avoid using var to define variables

There are uses of var in the code, where the type of the variable is inferred from the expression on the right hand of the definition. We recommend avoiding this feature because in some cases it might infer a smaller integer type than the developer might think. It is best to be explicit regarding types.

UPDATE: Following our recommendation, the only use of var has been removed.

State variable “hash” could have a better name

It is customary for a mapping’s name to describe the value_s associated to its keys. In the case of the hash state variable defined in line 15, the name describes the mapping’s _keys instead. Since the purpose is to map hashes to the amount of ether received associated to each hash, consider renaming the state variable to weiReceived, for example.

UPDATE: Following our recommendation, the variable has been renamed weiReceived.

Use safe math

There is only one math operation in the contract, in line 28. It’s always better to be safe and perform checks for overflow. Consider using OpenZeppelin’s safe math library.

UPDATE: Following our recommendation, the math operation mentioned above has been secured with an overflow check.

Solidity version

The contract requires version 0.4.11 of Solidity. It should be noted that 0.4.13 was released a few days ago. Consider changing the solidity version pragma to the latest version (pragma solidity ^0.4.13;) to enforce latest compiler version to be used.

UPDATE: For external reasons concerning the tooling used, the Solidity version was kept at 0.4.11. Despite not following the recommendation there is no security risk incurred.

OpenZeppelin version

The project uses version 1.1.0 of OpenZeppelin. It should be noted that 1.2.0 was released a few days ago. Consider changing the OpenZeppelin version to the latest in the project’s package.json.

UPDATE: Following our recommendation, OpenZeppelin was updated to version 1.2.0.

Notes and Additional Information

  • Good job using OpenZeppelin!

Conclusions

One severe security issue was found. Some small changes were proposed to follow best practices and reduce potential attack surface. Tierion implemented all our suggestions and we found no problems in the updated code.

Note that as of the date of publishing, the above review reflects the current understanding of known security patterns as they relate to the Tierion Presale contract. We have not reviewed the related Tierion project. The above should not be construed as investment advice or an offering of tokens. For general information about smart contract security, check out our thoughts here.