Skip to content

Instantly share code, notes, and snippets.

@Artazor
Last active December 4, 2017 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Artazor/a32bde50034b689f2bf4357d21aeb0a5 to your computer and use it in GitHub Desktop.
Save Artazor/a32bde50034b689f2bf4357d21aeb0a5 to your computer and use it in GitHub Desktop.
IungoToken SmartContract Audit

Iungo Audit

December, 2017 Authored by Anatoly Ressin & Jaro Satkevic

1. Introduction

Iungo Project requested that Blockvis perform an audit of the contracts in their iungo-token-crowdsale repository. The IungoToken contract is intended to serve as a tradeable token and the crowdsale at the same moment.

The contracts are hosted at:

https://github.com/iungonetwork/iungo-token-crowdsale

Single contract IungoToken.sol in the root folder is in scope.

The git commit hash we evaluated is: c938e8458e88858b9b839ef1f2788acb279bc197

2. Disclaimer

The audit makes no statements or warranties about utility of the code, safety of the code, suitability of the business model, regulatory regime for the business model, or any other statements about the fitness of the contracts to purpose, or their bugfree status. The audit documentation is for discussion purposes only.

3. Executive Summary

Overall the IungoToken codebase is a customized copy of the corresponding OpenZeppelin implementation flattened for the ability to attach the source code for verification on the services like etherscan.io. The project does not contain any tests, that is uncommon for the audited contracts and can't be considered as a good practice. However, the contract itself mostly conforms to the specification hosted at:

https://docs.google.com/document/d/1b9UmZeC5xeaW-uzL_JslYdGSJvc_SX0rmQsoDIPihPg/edit

Except for one formal error with dates:

  • In the IungoToken.sol:294 the date of the ICO end is defined as 31.01.2018 14:00:00 UTC
  • In the spec, the date of the ICO end is defined as 31.01.2018 23:59:59 UTC (#1) and 31.01.2018 23:59:50 UTC (#2)

3.1 Critical issues

We identified one CRITICAL ISSUE related to the race condition that could prevent uploading pre-sale data in case of quick reaching a hardcap. We recommend to upload any presale date BEFORE the start of the crowdsale.

3.2 Non-critical issues

We believe that the closing of ICO is not tested well (it is difficult to close an ICO on HARD_CAP), see below. As well contract lacks some important measures that mitigate reputational and regulatory risks, see below.

Some refactoring recommended for optimizing operations and reducing a dangerously big gas limit for contract deployment.

4. Contract Analysis

4.1. Regulatory/Reputational risk

4.1.1 Missing SoftCap/Refund logic.

We understand that the context of the given ICO is already reached the SoftCap, so the rest of the logic should never refund in normal circumstances. On Iungo website we see, that the project has reached its' soft cap with 435 ETH rewarding 30k ING for 1 ETH which is 30x (3000%) more than during the planned ICO. It is dangerous distribution yet still possible.

Nevertheless, assuming that the final goal (HARD_CAP) is significantly higher than the presale, we believe that it would be safer to have an ability to stop the ICO before the close-date and refund all collected ethers to the original buyers (make them able to withdraw their funds) in case of regulatory or reputational concerns. However, it would require more diligent mechanism than the onlyOwner (some kind of voting).

4.1.2. Avoid "investor" word in ICO code (and whitepaper).

  • We would recommend to rename _investor into _beneficiary in purchaseTokens() for the regulatory risk mitigation

4.1.3 ETH/Token rate control (contract health measure)

  • it is worth to have a possibility to change a token/eth rate in case Ethereum sudden growth during ICO (this ability should be implemented by voting).

4.2. Temporal logic analysis: TEMPORAL VULNERABILITY FOUND!

We believe that issueToken(...) and issueTokenMulti(...) are intended for uploading information gathered on the presale stage. However both methods are protected by the inProgress guard. This means that information about presale can be uploaded only during the active ICO phase that could be extremely short if ICO will close due to the HARD_CAP reaching. The issueTokenMulti transactions are in the race condition with successful closing ICO.

  • we recommend to upload any presale date BEFORE the start of the crowdsale.

4.3. Gas Analysis

Within current implementation IungoToken contract will require > 4M gas. That is considered dangerous. As it is close to the safe BlockGasLimit 4,712,388 gas that is not controlled by the sender of a transaction. You should deploy your contract in advance to be sure your transaction is mined. As well we recommend to refactor some parts described below:

4.3.1. Missing constant modifier on constant data

Using constant modifier on fields like dateDDMMMYYYY will reduce the size of the contract's mutable storage that is far more expensive than immutable opcodes.

4.3.2. tierDiscountPercentages[] can be moved into local scope

We recommend to move tierDiscountPercentages out of contract scope into computeTokenAmount(). It will not occupy any mutable storage.

4.3.3. currentTier() can be implemented cheaper

The following pseudocode reveals the idea:

require(now <= D4);
if (now > D3) return 3;
if (now > D2) return 2;
if (now > D1) return 1;
return 0;

4.3.4. currentTier() can be replaced by currentTierDiscountPercentage()

This will eliminate the need for array/indexing as well as fix the absence of exhaustive return-control flow

The following pseudocode reveals the idea:

require(now < D4);
if (now >= D3) return 0;
if (now >= D2) return 15;
if (now >= D1) return 35;
return 50;

4.3.5. Code duplication in close()

  • Token locking logic could be extracted to the separate method that will significantly shorten the bytecode of the contract and make the source code more readable (though the last one is subjective).

4.4 IungoToken semantics analysis

IungoToken itself is the descendant of OpenZeppelin's ERC20 StandardToken with custom Owned implementation that omits transferOwnership() that is considered a good practice.

4.4.1 Dates analysis

Dates checked and are correct except the last date date31Jan2018 should be 1517443199 to be equal to 2018-01-31 23:59:59 from the spec.

4.4.2 No need in uint256 arithmetic for <100 loop.

  • We would recommend using uint8 local variable for the loop in issueTokensMulti()

4.4.3 Simpler logic for finalizing token totalSupply

  • total tokens could be calculated in a simpler way: totalSupply.mul(100).div(64)

4.5 Closing ICO

  • we found problems with finishing ICO. There are cases when there will be left only a few tokens until the hard cap. If someone will send more that is allowed in a hard cap, the transaction will be reverted and ICO will be not closed. The even worse case will happen if there will left less than 100 ING until hard cap, it will be impossible to reach it by buying tokens (because of the minimum transaction of 0.1 ETH -> 100 ING).

  • The only way to close the ICO, in that case, will be calling issueTokens() function and giving tokens for someone manually.

  • Our recommendation is to split the msg.value and give a partial refund for the over-capped transaction, while exactly closing on the HARD_CAP.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment