Appics token audit

Sponnet
Old School
Published in
3 min readMay 1, 2018

The Appics team asked us to review their ICO smart contract.

The code is located here. The GIT version used for this report is 4899235a668136369bd26943899e66fdb1225199

The ICO contract code is rather complex, but the code comes with an extensive suite of unit tests which simulate the different ICO phases.

Critical severity

No issues of critical severity.

High severity

No issues of high severity.

Medium severity

Some points of centralisation are present in the token sale setup that users should be aware of.

Price depends on external ‘oracle’

The ICO contract depends on an external Ethereum account to set the ETH/USD exchange rate and token exchange rate using the setRate() function during the different phases of the ICO. If the private key associated with the oracle account would be compromised, a faulty exchange rate could be set during one of the token sale phases, resulting in minting of wrong amounts of tokens.

Correctness of prices when purchases are made through controller

For investing in this ICO through other cryptocurrencies — a buyForInvestor() is called. ETH investors should trust that the controller addresses applies the same prices as the setRate() function at all times during the token sale.

Low severity

No checks on constructor values

There are no checks in place that verify if the provided parameters when deploying the contract are valid. One example would be that if the Company address was mistakenly set to 0x0 , the collected ETH would be burned and lost when calling the withdrawEther() function.
It is recommended to do basic verification on these constructor parameters to have the contract fail early and loudly.

Inline helper contracts

The inline SafeMath contract used is an outdated version and uses more checks than necessary. It is recommended to use industry standard libraries and include these in your solidity code and use tooling to flatten solidity code to have it verified on etherscan.

Not all custom functions are tested

A code coverage test reveals that the unit tests cover 90.23% of the Solidity code. Some ERC20 functions are not tested, even though they contain custom logic that checks if token transfers are frozen or not.
Following functions are not tested:

  • transferFrom()
  • approve()
  • allowance()
  • burnTokens()
  • register()

It is recommended to write additional tests for these too to achieve full code coverage.

ETH remains on the contract until manually removed

The received ETH remains on the ICO contract until the withdrawEther() function is called. This makes the contract an attractive target for a hacker.
It is recommended to send the ETH to the Company address immediately after each token purchase with ETH.

Function that seems to serve no purpose

There is a register() function to self-register your Ethereum address to the ICO contract, suggesting that this is a required step to purchase tokens (this step requires access to the private key of the corresponding account, since msg.sender is used). However these registered addresses are never used further within the contract logic. If it is the purpose to limit token purchases to registered addresses , this should be checked in the buyToken() and the buyForInvestor() functions. If the purpose of this function is just for registering the addresses on the blockchain, it is not necessary to save the addresses in a mapping, emitting the Event is sufficient, which would result in lower gas costs.

Remarks

The following remarks are not related to the Solidity code itself, but should be taken into account when working with this contract.

Token transfer can be frozen / unfrozen on arbitrary moments

The freeze() and unfreeze() function that disables / enables token transfers can be called at any moment by the manager of the contract. This means that it is not guaranteed that tokens remain non-transferrable until the end of the token sale, and they can be frozen at any time after the token sale. It should be verified if this is the intended behaviour.

Large contract bytecode results in processing wait times to deploy

The bytecode of the ICO contract requires approximately 6.6 million gas to deploy. An estimate from ethgasstation.info shows that it might take up to 30 minutes to find a block that can fit such a large contract on the Ethereum mainnet.

Conclusion

No critical or high severity issues were found. Some changes were proposed to follow best practices and reduce potential attack surface.

Update

After we audited the code the Appics Team decided not to use this contract in their token sale. Using the Smart Contract in conjunction with the KYC and AML procedures would not have allowed to revert any payments.

--

--

Sponnet
Old School

Ethereum developer. Nodejs, innovation, Ethereum smart contracts, contributor to https://ava.do