Switch branches/tags
Nothing to show
Find file History
Pull request Compare This branch is 21 commits ahead, 1 commit behind AigangNetwork:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Aigang Crowdsale Contract Audit


Aigang intends to run a crowdsale commencing in Nov 2017.

Bok Consulting Pty Ltd was commissioned to perform an audit on the Aigang's crowdsale and token Ethereum smart contract.

This audit has been conducted on Aigang's source code in commits 8200037, fda7866, fd4e5d7 and 0638abf.

No potential vulnerabilities have been identified in the crowdsale and token contract.

Mainnet Addresses

Crowdsale Contract

Ethers contributed by participants to the crowdsale contract will result in AIX tokens being allocated to the participant's account in the token contract. The contributed ethers are immediately transferred to the crowdsale multisig wallet, reducing the risk of the loss of ethers in this bespoke smart contract.

Token Contract

The AIX contract is built on the MiniMeToken token contract.

There are some changes in the recently finalised ERC20 Token Standard that the MiniMeToken contract has not been updated for:

  • transfer(...) and transferFrom(...) will return false instead of throwing an error

The MiniMeToken token contract stores snapshots of an account's token balance and the totalSupply() in history. One side effect of this snapshot feature is that regular transfer operations consume a little more gas in transaction fees when compared to non-MiniMeToken token contracts.

Additionally, this token contract implements the approveAndCall(address _spender, uint256 _amount, bytes _extraData) function.

Table Of Contents


The following two recommendations are optional changes to the crowdsale and token contract:

  • LOW IMPORTANCE The modifiers Contribution.initialized() and Contribution.contributionOpen() use the assert(...) keyword rather than the require(...) keyword. Using the require(...) keyword instead of assert(...) will result in lower gas cost for participants when there is an error (e.g. sending ETH outside contribution period)

  • LOW IMPORTANCE MiniMeToken.approve(...) has a check requiring non-zero approval limits to be set to 0 before being set to a different non-zero value. The recently finalised ERC20 Token Standard suggest that the contract should not enforce this requirement

Potential Vulnerabilities

No potential vulnerabilities have been identified in the crowdsale and token contract.


This audit is into the technical aspects of the crowdsale contracts. The primary aim of this audit is to ensure that funds contributed to these contracts are not easily attacked or stolen by third parties. The secondary aim of this audit is that ensure the coded algorithms work as expected. This audit does not guarantee that that the code is bugfree, but intends to highlight any areas of weaknesses.


This audit makes no statements or warranties about the viability of the Aigang's business proposition, the individuals involved in this business or the regulatory regime for the business model.

Due Diligence

As always, potential participants in any crowdsale are encouraged to perform their due diligence on the business proposition before funding any crowdsales.

Potential participants are also encouraged to only send their funds to the official crowdsale Ethereum address, published on the crowdsale beneficiary's official communication channel.

Scammers have been publishing phishing address in the forums, twitter and other communication channels, and some go as far as duplicating crowdsale websites. Potential participants should NOT just click on any links received through these messages. Scammers have also hacked the crowdsale website to replace the crowdsale contract address with their scam address.

Potential participants should also confirm that the verified source code on EtherScan.io for the published crowdsale address matches the audited source code, and that the deployment parameters are correctly set, including the constant parameters.


  • This crowdsale contract has a low risk of having the ETH hacked or stolen, as any contributions by participants are immediately transferred to the crowdsale wallet.


The following functions were tested using the script test/01_test1.sh with the summary results saved in test/test1results.txt and the detailed output saved in test/test1output.txt:

  • Deploy APT token contract
  • Mint APT tokens
  • Deploy AIX token contract
  • Deploy Contribution contract
  • Set Contribution contract to be AIX controller
  • Deploy CommunityTokenHolder, DevTokensHolder, RemainderTokenHolder and Exchanger contracts
  • Initialise Contribution contract
  • Exchange Presale APT for AIX
  • Whitelist accounts for Contribution contract
  • Send contributions for whitelisted addresses and non-whitelisted address (expecting failure)
  • Finalise crowdsale and enable transfers
  • transfer(...), approve(...) and transferFrom(...) some tokens
  • Collect tokens from CommunityTokenHolder and DevTokensHolder (with modified withdrawal schedule)

Code Review

Presale Contracts

The Presale contracts were audited in Aigang Presale audit.

Following are the main components of the Presale contracts:

Not Reviewed

  • ../contracts/MultiSigWallet.sol The ConsenSys/Gnosis multisig wallet is the same as used in the Aigang Presale.

    The only difference is in the Solidity version number:

    $ diff MultiSigWallet.sol ../../AigangPresaleContractAudit/contracts/MultiSigWallet.sol 
    < pragma solidity 0.4.15;
    > pragma solidity 0.4.11;
  • ../contracts/Migrations.sol

    This is a part of the Truffles testing framework

Differences In MiniMeToken.sol Between The Aigang Presale And Crowdsale Contracts

There are some small changes to the MiniMeToken contract between the Presale and Crowdsale versions. These changes seem to have been made to account for the upgrade in the compiler version.

$ diff -w ../../AigangPresaleContractAudit/contracts/MiniMeToken.sol MiniMeToken.sol 
< pragma solidity ^0.4.11;
< import "./ERC20.sol";
> pragma solidity ^0.4.15;
<     modifier onlyController { if (msg.sender != controller) throw; _; }
>     modifier onlyController { require(msg.sender == controller); _; }
<         if (!transfersEnabled) throw;
>         require(transfersEnabled);
<             if (!transfersEnabled) throw;
>             require(transfersEnabled);
<            if (parentSnapShotBlock >= block.number) throw;
>            require(parentSnapShotBlock < block.number);
<            if ((_to == 0) || (_to == address(this))) throw;
>            require((_to != 0) && (_to != address(this)));
<                if (!TokenController(controller).onTransfer(_from, _to, _amount))
<                throw;
>                require(TokenController(controller).onTransfer(_from, _to, _amount));
<            if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
>            require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
<         if (!transfersEnabled) throw;
>         require(transfersEnabled);
<         if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw;
>         require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
<             if (!TokenController(controller).onApprove(msg.sender, _spender, _amount))
<                 throw;
>             require(TokenController(controller).onApprove(msg.sender, _spender, _amount));
<         if (!approve(_spender, _amount)) throw;
>         require(approve(_spender, _amount));
<         uint curTotalSupply = getValueAt(totalSupplyHistory, block.number);
<         if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow
>         uint curTotalSupply = totalSupply();
>         require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
>         uint previousBalanceTo = balanceOf(_owner);
>         require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
<         var previousBalanceTo = balanceOf(_owner);
<         if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
<         uint curTotalSupply = getValueAt(totalSupplyHistory, block.number);
<         if (curTotalSupply < _amount) throw;
>         uint curTotalSupply = totalSupply();
>         require(curTotalSupply >= _amount);
>         uint previousBalanceFrom = balanceOf(_owner);
>         require(previousBalanceFrom >= _amount);
<         var previousBalanceFrom = balanceOf(_owner);
<         if (previousBalanceFrom < _amount) throw;
<                Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ];
>                Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
<                Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1];
>                Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
<         if (isContract(controller)) {
<             if (! TokenController(controller).proxyPayment.value(msg.value)(msg.sender))
<                 throw;
<         } else {
<             throw;
<         }
>         require(isContract(controller));
>         require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
<     function claimTokens(address _token) public onlyController {
>     function claimTokens(address _token) onlyController {
<       ERC20 token = ERC20(_token);
<       uint256 balance = token.balanceOf(this);
>         MiniMeToken token = MiniMeToken(_token);
>         uint balance = token.balanceOf(this);
<     event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount);
>     event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);

(c) BokkyPooBah / Bok Consulting Pty Ltd for Aigang - Nov 15 2017. The MIT Licence.