[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\"flow\", \"env\"]\n}\n"
  },
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  build:\n    docker:\n      # specify the version you desire here\n      - image: circleci/node:10.2-stretch\n\n    working_directory: ~/repo\n\n    steps:\n      - checkout\n\n      # Download and cache dependencies\n      - restore_cache:\n          keys:\n            - v1-dependencies-{{ checksum \"package-lock.json\" }}\n            # fallback to using the latest cache if no exact match is found\n            - v1-dependencies-\n\n      - run:\n          name: Install packages\n          command: npm install\n\n      - save_cache:\n          paths:\n            - node_modules\n          key: v1-dependencies-{{ checksum \"package-lock.json\" }}\n\n      # run tests!\n      - run:\n          name: Run tests\n          command: npm run test\n  release:\n    docker:\n      # specify the version you desire here\n      - image: circleci/node:10.2-stretch\n\n    working_directory: ~/repo\n\n    steps:\n      - checkout\n      - restore_cache:\n          keys:\n            - v1-dependencies-{{ checksum \"package-lock.json\" }}\n            # fallback to using the latest cache if no exact match is found\n            - v1-dependencies-\n\n      - run:\n          name: Install packages\n          command: npm install\n\n      # Compile contracts!\n      - run:\n          name: Compile contracts\n          command: npm run compile\n\n      - run:\n          name: Semantic release\n          command: npm run semantic-release\n\nworkflows:\n  version: 2\n  test-build-release:\n    jobs:\n      - build\n      - release:\n          requires:\n            - build\n          filters:\n            branches:\n              only:\n                - master\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\n\n[*.sol]\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": [\"standard\"],\n  \"env\": {\n    \"node\": true,\n    \"jasmine\": true\n  },\n  \"globals\": {\n    \"web3\": false,\n    \"artifacts\": false,\n    \"contract\": false\n  }\n}\n"
  },
  {
    "path": ".flowconfig",
    "content": "[ignore]\n\n[include]\n\n[libs]\n\n[options]\n\n[lints]\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.sol linguist-language=Solidity\n*.js linguist-vendored\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nbuild/\n"
  },
  {
    "path": ".npmignore",
    "content": "migrations\ntest\nzos.*\nscripts \nflow-typed\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"7\"\ncache:\n  directories:\n    - \"node_modules\"\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2017 The Decentraland Team\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use files included in this repository except in compliance\nwith the License. You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "![](https://decentraland.org/favicon.ico)\n\nDecentraland MANA Token\n=======================\n\n[![Build Status](https://travis-ci.org/decentraland/mana.svg?branch=master)](https://travis-ci.org/decentraland/mana)\n\nSolidity smart contracts for the Decentraland Crowdsale and MANA Token\n"
  },
  {
    "path": "contracts/BurnableToken.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport 'zeppelin-solidity/contracts/token/StandardToken.sol';\n\n/**\n * @title Burnable Token\n * @dev A token that can be irreversibly burned.\n */\ncontract BurnableToken is StandardToken {\n\n    event Burn(address indexed burner, uint256 value);\n\n    /**\n     * @dev Burns a specified amount of tokens.\n     * @param _value The amount of tokens to burn. \n     */\n    function burn(uint256 _value) public {\n        require(_value > 0);\n\n        address burner = msg.sender;\n        balances[burner] = balances[burner].sub(_value);\n        totalSupply = totalSupply.sub(_value);\n        Burn(msg.sender, _value);\n    }\n\n}\n"
  },
  {
    "path": "contracts/ContinuousSale.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport \"zeppelin-solidity/contracts/token/MintableToken.sol\";\n\n/**\n * @title ContinuousSale\n * @dev ContinuousSale implements a contract for managing a continuous token sale\n */\ncontract ContinuousSale {\n    using SafeMath for uint256;\n\n    // time bucket size\n    uint256 public constant BUCKET_SIZE = 12 hours;\n\n    // the token being sold\n    MintableToken public token;\n\n    // address where funds are collected\n    address public wallet;\n\n    // amount of tokens emitted per wei\n    uint256 public rate;\n\n    // amount of raised money in wei\n    uint256 public weiRaised;\n\n    // max amount of tokens to mint per time bucket\n    uint256 public issuance;\n\n    // last time bucket from which tokens have been purchased\n    uint256 public lastBucket = 0;\n\n    // amount issued in the last bucket\n    uint256 public bucketAmount = 0;\n\n    event TokenPurchase(address indexed investor, address indexed beneficiary, uint256 weiAmount, uint256 tokens);\n\n    function ContinuousSale(\n        uint256 _rate,\n        address _wallet,\n        MintableToken _token\n    ) {\n        require(_rate != 0);\n        require(_wallet != 0);\n        // require(address(token) != 0x0);\n\n        rate = _rate;\n        wallet = _wallet;\n        token = _token;\n    }\n\n    function() payable {\n        buyTokens(msg.sender);\n    }\n\n    function buyTokens(address beneficiary) public payable {\n        require(beneficiary != 0x0);\n        require(msg.value != 0);\n\n        prepareContinuousPurchase();\n        uint256 tokens = processPurchase(beneficiary);\n        checkContinuousPurchase(tokens);\n    }\n\n    function prepareContinuousPurchase() internal {\n        uint256 timestamp = block.timestamp;\n        uint256 bucket = timestamp - (timestamp % BUCKET_SIZE);\n\n        if (bucket > lastBucket) {\n            lastBucket = bucket;\n            bucketAmount = 0;\n        }\n    }\n\n    function checkContinuousPurchase(uint256 tokens) internal {\n        uint256 updatedBucketAmount = bucketAmount.add(tokens);\n        require(updatedBucketAmount <= issuance);\n\n        bucketAmount = updatedBucketAmount;\n    }\n\n    function processPurchase(address beneficiary) internal returns(uint256) {\n        uint256 weiAmount = msg.value;\n\n        // calculate token amount to be created\n        uint256 tokens = weiAmount.mul(rate);\n\n        // update state\n        weiRaised = weiRaised.add(weiAmount);\n\n        token.mint(beneficiary, tokens);\n        TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);\n\n        forwardFunds();\n\n        return tokens;\n    }\n\n    function forwardFunds() internal {\n        wallet.transfer(msg.value);\n    }\n}\n"
  },
  {
    "path": "contracts/MANAContinuousSale.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport \"zeppelin-solidity/contracts/ownership/Ownable.sol\";\nimport \"./ContinuousSale.sol\";\nimport \"./MANAToken.sol\";\n\ncontract MANAContinuousSale is ContinuousSale, Ownable {\n\n    uint256 public constant INFLATION = 8;\n\n    bool public started = false;\n\n    event RateChange(uint256 amount);\n\n    event WalletChange(address wallet);\n\n    function MANAContinuousSale(\n        uint256 _rate,\n        address _wallet,\n        MintableToken _token\n    ) ContinuousSale(_rate, _wallet, _token) {\n    }\n\n    modifier whenStarted() {\n        require(started);\n        _;\n    }\n\n    function start() onlyOwner {\n        require(!started);\n\n        // initialize issuance\n        uint256 finalSupply = token.totalSupply();\n        uint256 annualIssuance = finalSupply.mul(INFLATION).div(100);\n        issuance = annualIssuance.mul(BUCKET_SIZE).div(1 years);\n\n        started = true;\n    }\n\n    function buyTokens(address beneficiary) whenStarted public payable {\n        super.buyTokens(beneficiary);\n    }\n\n    function setWallet(address _wallet) onlyOwner {\n        require(_wallet != 0x0);\n        wallet = _wallet;\n        WalletChange(_wallet);\n    }\n\n    function setRate(uint256 _rate) onlyOwner {\n        rate = _rate;\n        RateChange(_rate);\n    }\n\n    function unpauseToken() onlyOwner {\n        MANAToken(token).unpause();\n    }\n\n    function pauseToken() onlyOwner {\n        MANAToken(token).pause();\n    }\n}\n"
  },
  {
    "path": "contracts/MANACrowdsale.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport \"zeppelin-solidity/contracts/crowdsale/CappedCrowdsale.sol\";\nimport \"zeppelin-solidity/contracts/crowdsale/Crowdsale.sol\";\nimport \"zeppelin-solidity/contracts/crowdsale/FinalizableCrowdsale.sol\";\nimport \"./WhitelistedCrowdsale.sol\";\nimport \"./MANAContinuousSale.sol\";\nimport \"./MANAToken.sol\";\n\ncontract MANACrowdsale is WhitelistedCrowdsale, CappedCrowdsale, FinalizableCrowdsale {\n\n    uint256 public constant TOTAL_SHARE = 100;\n    uint256 public constant CROWDSALE_SHARE = 40;\n    uint256 public constant FOUNDATION_SHARE = 60;\n\n    // price at which whitelisted buyers will be able to buy tokens\n    uint256 public preferentialRate;\n\n    // customize the rate for each whitelisted buyer\n    mapping (address => uint256) public buyerRate;\n\n    // initial rate at which tokens are offered\n    uint256 public initialRate;\n\n    // end rate at which tokens are offered\n    uint256 public endRate;\n\n    // continuous crowdsale contract\n    MANAContinuousSale public continuousSale;\n\n    event WalletChange(address wallet);\n\n    event PreferentialRateChange(address indexed buyer, uint256 rate);\n\n    event InitialRateChange(uint256 rate);\n\n    event EndRateChange(uint256 rate);\n\n    function MANACrowdsale(\n        uint256 _startBlock,\n        uint256 _endBlock,\n        uint256 _initialRate,\n        uint256 _endRate,\n        uint256 _preferentialRate,\n        address _wallet\n    )\n        CappedCrowdsale(86206 ether)\n        WhitelistedCrowdsale()\n        FinalizableCrowdsale()\n        Crowdsale(_startBlock, _endBlock, _initialRate, _wallet)\n    {\n        require(_initialRate > 0);\n        require(_endRate > 0);\n        require(_preferentialRate > 0);\n\n        initialRate = _initialRate;\n        endRate = _endRate;\n        preferentialRate = _preferentialRate;\n\n        continuousSale = createContinuousSaleContract();\n\n        MANAToken(token).pause();\n    }\n\n    function createTokenContract() internal returns(MintableToken) {\n        return new MANAToken();\n    }\n\n    function createContinuousSaleContract() internal returns(MANAContinuousSale) {\n        return new MANAContinuousSale(rate, wallet, token);\n    }\n\n    function setBuyerRate(address buyer, uint256 rate) onlyOwner public {\n        require(rate != 0);\n        require(isWhitelisted(buyer));\n        require(block.number < startBlock);\n\n        buyerRate[buyer] = rate;\n\n        PreferentialRateChange(buyer, rate);\n    }\n\n    function setInitialRate(uint256 rate) onlyOwner public {\n        require(rate != 0);\n        require(block.number < startBlock);\n\n        initialRate = rate;\n\n        InitialRateChange(rate);\n    }\n\n    function setEndRate(uint256 rate) onlyOwner public {\n        require(rate != 0);\n        require(block.number < startBlock);\n\n        endRate = rate;\n\n        EndRateChange(rate);\n    }\n\n    function getRate() internal returns(uint256) {\n        // some early buyers are offered a discount on the crowdsale price\n        if (buyerRate[msg.sender] != 0) {\n            return buyerRate[msg.sender];\n        }\n\n        // whitelisted buyers can purchase at preferential price before crowdsale ends\n        if (isWhitelisted(msg.sender)) {\n            return preferentialRate;\n        }\n\n        // otherwise compute the price for the auction\n        uint256 elapsed = block.number - startBlock;\n        uint256 rateRange = initialRate - endRate;\n        uint256 blockRange = endBlock - startBlock;\n\n        return initialRate.sub(rateRange.mul(elapsed).div(blockRange));\n    }\n\n    // low level token purchase function\n    function buyTokens(address beneficiary) payable {\n        require(beneficiary != 0x0);\n        require(validPurchase());\n\n        uint256 weiAmount = msg.value;\n        uint256 updatedWeiRaised = weiRaised.add(weiAmount);\n\n        uint256 rate = getRate();\n        // calculate token amount to be created\n        uint256 tokens = weiAmount.mul(rate);\n\n        // update state\n        weiRaised = updatedWeiRaised;\n\n        token.mint(beneficiary, tokens);\n        TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);\n\n        forwardFunds();\n    }\n\n    function setWallet(address _wallet) onlyOwner public {\n        require(_wallet != 0x0);\n        wallet = _wallet;\n        continuousSale.setWallet(_wallet);\n        WalletChange(_wallet);\n    }\n\n    function unpauseToken() onlyOwner {\n        require(isFinalized);\n        MANAToken(token).unpause();\n    }\n\n    function pauseToken() onlyOwner {\n        require(isFinalized);\n        MANAToken(token).pause();\n    }\n\n\n    function beginContinuousSale() onlyOwner public {\n        require(isFinalized);\n\n        token.transferOwnership(continuousSale);\n\n        continuousSale.start();\n        continuousSale.transferOwnership(owner);\n    }\n\n    function finalization() internal {\n        uint256 totalSupply = token.totalSupply();\n        uint256 finalSupply = TOTAL_SHARE.mul(totalSupply).div(CROWDSALE_SHARE);\n\n        // emit tokens for the foundation\n        token.mint(wallet, FOUNDATION_SHARE.mul(finalSupply).div(TOTAL_SHARE));\n\n        // NOTE: cannot call super here because it would finish minting and\n        // the continuous sale would not be able to proceed\n    }\n\n}\n"
  },
  {
    "path": "contracts/MANAToken.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport \"zeppelin-solidity/contracts/token/PausableToken.sol\";\nimport \"zeppelin-solidity/contracts/token/MintableToken.sol\";\nimport \"./BurnableToken.sol\";\n\ncontract MANAToken is BurnableToken, PausableToken, MintableToken {\n\n    string public constant symbol = \"MANA\";\n\n    string public constant name = \"Decentraland MANA\";\n\n    uint8 public constant decimals = 18;\n\n    function burn(uint256 _value) whenNotPaused public {\n        super.burn(_value);\n    }\n}\n"
  },
  {
    "path": "contracts/Migrations.sol",
    "content": "pragma solidity ^0.4.4;\n\ncontract Migrations {\n  address public owner;\n  uint public last_completed_migration;\n\n  modifier restricted() {\n    if (msg.sender == owner) _;\n  }\n\n  function Migrations() {\n    owner = msg.sender;\n  }\n\n  function setCompleted(uint completed) restricted {\n    last_completed_migration = completed;\n  }\n\n  function upgrade(address new_address) restricted {\n    Migrations upgraded = Migrations(new_address);\n    upgraded.setCompleted(last_completed_migration);\n  }\n}\n"
  },
  {
    "path": "contracts/WhitelistedCrowdsale.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport 'zeppelin-solidity/contracts/math/SafeMath.sol';\nimport 'zeppelin-solidity/contracts/crowdsale/Crowdsale.sol';\n\n/**\n * @title WhitelistedCrowdsale\n * @dev Extension of Crowsdale where an owner can whitelist addresses\n * which can buy in crowdsale before it opens to the public \n */\ncontract WhitelistedCrowdsale is Crowdsale, Ownable {\n    using SafeMath for uint256;\n\n    // list of addresses that can purchase before crowdsale opens\n    mapping (address => bool) public whitelist;\n\n    function addToWhitelist(address buyer) public onlyOwner {\n        require(buyer != 0x0);\n        whitelist[buyer] = true; \n    }\n\n    // @return true if buyer is whitelisted\n    function isWhitelisted(address buyer) public constant returns (bool) {\n        return whitelist[buyer];\n    }\n\n    // overriding Crowdsale#validPurchase to add whitelist logic\n    // @return true if buyers can buy at the moment\n    function validPurchase() internal constant returns (bool) {\n        // [TODO] issue with overriding and associativity of logical operators\n        return super.validPurchase() || (!hasEnded() && isWhitelisted(msg.sender)); \n    }\n\n}\n"
  },
  {
    "path": "flow-typed/defs.js",
    "content": "declare var contract: (string, Function) => void\ndeclare var it: (string, Function) => void\ndeclare var describe: (string, Function) => void\ndeclare var beforeEach: Function => void\n\ndeclare var artifacts: {\n  require: string => any\n}\n\ndeclare var web3: any\n"
  },
  {
    "path": "migrations/1_initial_migration.js",
    "content": "var Migrations = artifacts.require(\"./Migrations.sol\");\n\nmodule.exports = function(deployer) {\n  deployer.deploy(Migrations);\n};\n"
  },
  {
    "path": "migrations/2_deploy_contracts.js",
    "content": "const MANACrowdsale = artifacts.require(\"./MANACrowdsale.sol\");\n\nmodule.exports = function(deployer) {\n  // deployer.deploy(MANACrowdsale);\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"decentraland-mana\",\n  \"version\": \"0.0.0-semantic-release\",\n  \"description\": \"Solidity Contracts for the Decentraland MANA Token\",\n  \"main\": \"y\",\n  \"directories\": {\n    \"test\": \"test\"\n  },\n  \"dependencies\": {\n    \"sha3\": \"^1.2.1\",\n    \"truffle-hdwallet-provider\": \"0.0.3\",\n    \"zeppelin-solidity\": \"1.2.0\"\n  },\n  \"devDependencies\": {\n    \"babel-polyfill\": \"^6.26.0\",\n    \"babel-preset-env\": \"^1.6.1\",\n    \"babel-preset-flow\": \"^6.23.0\",\n    \"babel-register\": \"^6.26.0\",\n    \"chai\": \"^4.1.2\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"chai-bignumber\": \"^2.0.2\",\n    \"eslint\": \"^4.19.1\",\n    \"eslint-config-standard\": \"^10.2.1\",\n    \"eslint-plugin-import\": \"^2.11.0\",\n    \"eslint-plugin-node\": \"^5.2.1\",\n    \"eslint-plugin-promise\": \"^3.7.0\",\n    \"eslint-plugin-standard\": \"^3.1.0\",\n    \"ganache-cli\": \"^6.1.0\",\n    \"husky\": \"^1.3.1\",\n    \"semantic-release\": \"^15.13.3\",\n    \"truffle\": \"^3.4.11\",\n    \"validate-commit-msg\": \"^2.14.0\"\n  },\n  \"scripts\": {\n    \"test\": \"./scripts/test.sh\",\n    \"eslint\": \"eslint test/\",\n    \"semantic-release\": \"semantic-release\",\n    \"commit-msg\": \"validate-commit-msg\",\n    \"compile\": \"truffle compile\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/decentraland/mana.git\"\n  },\n  \"keywords\": [\n    \"decentraland\",\n    \"mana\",\n    \"solidity\",\n    \"truffle\",\n    \"crowdsale\"\n  ],\n  \"author\": \"Federico Bond\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/decentraland/mana/issues\"\n  },\n  \"homepage\": \"https://github.com/decentraland/mana#readme\"\n}\n"
  },
  {
    "path": "scripts/test.sh",
    "content": "#!/usr/bin/env bash\n\n# Executes cleanup function at script exit.\ntrap cleanup EXIT\n\ncleanup() {\n  # Kill the testrpc instance that we started (if we started one and if it's still running).\n  if [ -n \"$testrpc_pid\" ] && ps -p $testrpc_pid > /dev/null; then\n    kill -9 $testrpc_pid\n  fi\n}\n\ntestrpc_running() {\n  nc -z localhost 8545\n}\n\nif testrpc_running; then\n  echo \"Using existing testrpc instance\"\nelse\n  echo \"Starting our own testrpc instance\"\n  # We define 10 accounts with balance 1M ether, needed for high-value tests.\n  ganache-cli -l 10000000 \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208,1000000000000000000000000\"  \\\n    --account=\"0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000\"  \\\n  > /dev/null &\n  testrpc_pid=$!\nfi\n\nnode_modules/.bin/truffle test \"$@\"\n"
  },
  {
    "path": "test/BurnableToken.js",
    "content": "// @flow\n'use strict'\n\nconst { should, EVMThrow } = require('./utils.js')\nconst BurnableTokenMock = artifacts.require('./helpers/BurnableTokenMock.sol')\n\nconst BigNumber = web3.BigNumber\n\ncontract('BurnableToken', function (accounts) {\n  let token\n  let expectedTokenSupply = new BigNumber(900)\n\n  beforeEach(async function () {\n    token = await BurnableTokenMock.new(accounts[1], 1000)\n  })\n\n  it('owner should be able to burn tokens', async function () {\n    const { logs } = await token.burn(100, { from: accounts[1] })\n\n    const balance = await token.balanceOf(accounts[1])\n    balance.should.be.bignumber.equal(expectedTokenSupply)\n\n    const totalSupply = await token.totalSupply()\n    totalSupply.should.be.bignumber.equal(expectedTokenSupply)\n\n    const event = logs.find(e => e.event === 'Burn')\n    should.exist(event)\n  })\n\n  it('cannot burn more tokens that you have', async function () {\n    await token.burn(2000, { from: accounts[1] })\n      .should.be.rejectedWith(EVMThrow)\n  })\n})\n"
  },
  {
    "path": "test/MANAContinuousSale.js",
    "content": "// @flow\n'use strict'\n\nconst expect = require('chai').expect\nconst { advanceTime, EVMRevert } = require('./utils')\nconst MANAContinuousSale = artifacts.require('./MANAContinuousSale.sol')\nconst MANAToken = artifacts.require('./MANAToken.sol')\n\nconst BigNumber = web3.BigNumber\n\ncontract('MANAContinuousSale', function([owner, wallet, buyer, wallet2]) {\n  const rate = new BigNumber(1)\n  const newRate = new BigNumber(2)\n  const value = new BigNumber(100)\n\n  let token, sale\n\n  beforeEach(async function() {\n    token = await MANAToken.new()\n    await token.pause()\n    await token.mint(owner, new BigNumber(1000000))\n\n    sale = await MANAContinuousSale.new(rate, wallet, token.address)\n    await token.transferOwnership(sale.address)\n  })\n\n  it('should start with continuous sale disabled', async function() {\n    let started = await sale.started()\n    started.should.equal(false)\n\n    await sale.start()\n    started = await sale.started()\n    started.should.equal(true)\n  })\n\n  it('owner should be able to pause/unpause token', async function() {\n    await sale.unpauseToken().should.be.fulfilled\n    let paused = await token.paused()\n    paused.should.equal(false)\n\n    await sale.pauseToken().should.be.fulfilled\n    paused = await token.paused()\n    paused.should.equal(true)\n  })\n\n  it('non-owners should not be able to pause/unpause token', async function() {\n    await sale.unpauseToken({ from: buyer }).should.be.rejectedWith(EVMRevert)\n\n    await sale.unpauseToken({ from: owner })\n    await sale.pauseToken({ from: buyer }).should.be.rejectedWith(EVMRevert)\n  })\n\n  it('should accept payments only if sale has started', async function() {\n    await sale.send(value, { from: buyer }).should.be.rejectedWith(EVMRevert)\n    await sale.start().should.be.fulfilled\n    await sale.buyTokens(buyer, {\n      value: new BigNumber(100),\n      from: buyer\n    }).should.be.fulfilled\n  })\n\n  it('only owner can change rate', async function() {\n    await sale\n      .setRate(newRate, { from: buyer })\n      .should.be.rejectedWith(EVMRevert)\n\n    const { logs } = await sale.setRate(newRate)\n\n    const event = logs.find(e => e.event === 'RateChange')\n    expect(event).to.exist\n\n    const updatedRate = await sale.rate()\n    updatedRate.should.be.bignumber.equal(newRate)\n  })\n\n  it('only owner can change wallet', async function() {\n    await sale\n      .setRate(newRate, { from: buyer })\n      .should.be.rejectedWith(EVMRevert)\n\n    const { logs } = await sale.setRate(newRate)\n\n    const event = logs.find(e => e.event === 'RateChange')\n    expect(event).to.exist\n\n    const updatedRate = await sale.rate()\n    updatedRate.should.be.bignumber.equal(newRate)\n  })\n\n  it('should reject payments if amount of tokens is bigger than block bucket', async function() {\n    await sale.start()\n    await sale\n      .send(new BigNumber(1000), { from: buyer })\n      .should.be.rejectedWith(EVMRevert)\n  })\n\n  it('should start with a fixed issuance rate', async function() {\n    // total tokens emitted are 1000000 so issuance must be:\n    //\n    // seconds in 12 hours: 43200\n    // seconds in a year: 31536000\n    // 1000000 * 0.08 * 43200 / 31536000 = 109\n    let issuance = await sale.issuance()\n    issuance.should.be.bignumber.equal(new BigNumber(0))\n\n    await sale.start()\n    issuance = await sale.issuance()\n    issuance.should.be.bignumber.equal(new BigNumber(109))\n  })\n\n  it('should handle time buckets for token issuance', async function() {\n    await sale.start()\n\n    await sale.buyTokens(buyer, { value, from: buyer }).should.be.fulfilled\n    await sale\n      .buyTokens(buyer, { value, from: buyer })\n      .should.be.rejectedWith(EVMRevert)\n\n    const bucketSize = await sale.BUCKET_SIZE()\n    await advanceTime(bucketSize)\n\n    await sale.buyTokens(buyer, { value, from: buyer }).should.be.fulfilled\n  })\n})\n"
  },
  {
    "path": "test/MANACrowdsale.js",
    "content": "// @flow\n'use strict'\n\nconst expect = require('chai').expect\n\nconst { advanceToBlock, ether, should, EVMRevert } = require('./utils')\nconst MANACrowdsale = artifacts.require('./MANACrowdsale.sol')\nconst MANAContinuousSale = artifacts.require('./MANAContinuousSale.sol')\nconst MANAToken = artifacts.require('./MANAToken.sol')\n\nconst BigNumber = web3.BigNumber\n\ncontract('MANACrowdsale', function([\n  _,\n  wallet,\n  wallet2,\n  buyer,\n  purchaser,\n  buyer2,\n  purchaser2\n]) {\n  const initialRate = new BigNumber(1000)\n  const endRate = new BigNumber(900)\n\n  const newRate = new BigNumber(500)\n  const preferentialRate = new BigNumber(2000)\n  const value = ether(1)\n\n  const expectedFoundationTokens = new BigNumber(6000)\n  const expectedTokenSupply = new BigNumber(10000)\n\n  let startBlock, endBlock\n  let crowdsale, token\n\n  beforeEach(async function() {\n    startBlock = web3.eth.blockNumber + 10\n    endBlock = web3.eth.blockNumber + 20\n\n    crowdsale = await MANACrowdsale.new(\n      startBlock,\n      endBlock,\n      initialRate,\n      endRate,\n      preferentialRate,\n      wallet\n    )\n    token = MANAToken.at(await crowdsale.token())\n  })\n\n  it('starts with token paused', async function() {\n    const paused = await token.paused()\n    paused.should.equal(true)\n  })\n\n  it('owner should be able to change wallet', async function() {\n    await crowdsale.setWallet(wallet2)\n    let wallet = await crowdsale.wallet()\n    wallet.should.equal(wallet2)\n\n    const continuousSale = MANAContinuousSale.at(\n      await crowdsale.continuousSale()\n    )\n    wallet = await continuousSale.wallet()\n    wallet.should.equal(wallet2)\n  })\n\n  it('non-owner should not be able to change wallet', async function() {\n    await crowdsale\n      .setWallet(wallet2, { from: purchaser })\n      .should.be.rejectedWith(EVMRevert)\n  })\n\n  it('owner should be able to start continuous sale', async function() {\n    await crowdsale.beginContinuousSale().should.be.rejectedWith(EVMRevert)\n\n    await advanceToBlock(endBlock)\n    await crowdsale.finalize()\n\n    const sale = MANAContinuousSale.at(await crowdsale.continuousSale())\n\n    let started = await sale.started()\n    started.should.equal(false)\n\n    await crowdsale.beginContinuousSale().should.be.fulfilled\n\n    started = await sale.started()\n    started.should.equal(true)\n  })\n\n  it('owner should be able to unpause token after crowdsale ends', async function() {\n    await advanceToBlock(endBlock)\n\n    await crowdsale.unpauseToken().should.be.rejectedWith(EVMRevert)\n\n    await crowdsale.finalize()\n\n    let paused = await token.paused()\n    paused.should.equal(true)\n\n    await crowdsale.unpauseToken()\n\n    paused = await token.paused()\n    paused.should.equal(false)\n  })\n\n  it('non-owners should not be able to start continuous sale', async function() {\n    await crowdsale\n      .beginContinuousSale({ from: purchaser })\n      .should.be.rejectedWith(EVMRevert)\n  })\n\n  describe('rate during auction should decrease at a fixed step every block', async function() {\n    let balance, startBlock, endBlock\n\n    let initialRate = 9166\n    let endRate = 5500\n    let preferentialRate = initialRate\n    const rateAtBlock10 = new BigNumber(9165)\n    const rateAtBlock20 = new BigNumber(9164)\n    const rateAtBlock100 = new BigNumber(9155)\n    const rateAtBlock2 = new BigNumber(9166)\n    const rateAtBlock10000 = new BigNumber(7973)\n    const rateAtBlock30000 = new BigNumber(5586)\n\n    beforeEach(async function() {\n      startBlock = web3.eth.blockNumber + 10\n      endBlock = web3.eth.blockNumber + 10 + 30720\n\n      crowdsale = await MANACrowdsale.new(\n        startBlock,\n        endBlock,\n        initialRate,\n        endRate,\n        preferentialRate,\n        wallet\n      )\n      token = MANAToken.at(await crowdsale.token())\n    })\n    it('at start', async function() {\n      await advanceToBlock(startBlock - 1)\n\n      await crowdsale.buyTokens(buyer, { value, from: purchaser })\n      balance = await token.balanceOf(buyer)\n      balance.should.be.bignumber.equal(value.mul(initialRate))\n    })\n\n    it('at block 10', async function() {\n      await advanceToBlock(startBlock + 9)\n\n      await crowdsale.buyTokens(buyer2, { value, from: purchaser2 })\n      balance = await token.balanceOf(buyer2)\n\n      balance.should.be.bignumber.equal(value.mul(rateAtBlock10))\n    })\n\n    it('at block 20', async function() {\n      await advanceToBlock(startBlock + 19)\n\n      await crowdsale.buyTokens(buyer2, { value, from: purchaser2 })\n      balance = await token.balanceOf(buyer2)\n\n      balance.should.be.bignumber.equal(value.mul(rateAtBlock20))\n    })\n\n    it('at block 100', async function() {\n      await advanceToBlock(startBlock + 99)\n\n      await crowdsale.buyTokens(buyer2, { value, from: purchaser2 })\n      balance = await token.balanceOf(buyer2)\n\n      balance.should.be.bignumber.equal(value.mul(rateAtBlock100))\n    })\n\n    it('at block 2', async function() {\n      await advanceToBlock(startBlock + 1)\n\n      await crowdsale.buyTokens(buyer2, { value, from: purchaser2 })\n      balance = await token.balanceOf(buyer2)\n\n      balance.should.be.bignumber.equal(value.mul(rateAtBlock2))\n    })\n\n    it.skip('at block 10000', async function() {\n      await advanceToBlock(startBlock + 9999)\n\n      await crowdsale.buyTokens(buyer2, { value, from: purchaser2 })\n      balance = await token.balanceOf(buyer2)\n\n      balance.should.be.bignumber.equal(value.mul(rateAtBlock10000))\n    })\n\n    it.skip('at block 30000', async function() {\n      await advanceToBlock(startBlock + 29999)\n\n      await crowdsale.buyTokens(buyer2, { value, from: purchaser2 })\n      balance = await token.balanceOf(buyer2)\n\n      balance.should.be.bignumber.equal(value.mul(rateAtBlock30000))\n    })\n  })\n\n  it('whitelisted buyers should access tokens at reduced price until end of auction', async function() {\n    await crowdsale.addToWhitelist(buyer)\n\n    await crowdsale.buyTokens(buyer, { value, from: buyer })\n    const balance = await token.balanceOf(buyer)\n    balance.should.be.bignumber.equal(value.mul(preferentialRate))\n  })\n\n  it('whitelisted big whale investor should not exceed the cap', async function() {\n    const cap = await crowdsale.cap()\n    const overCap = cap.mul(2)\n    await crowdsale.addToWhitelist(buyer)\n    await crowdsale\n      .buyTokens(buyer, { value: overCap, from: buyer })\n      .should.be.rejectedWith(EVMRevert)\n    const balance = await token.balanceOf(buyer)\n    const raised = await crowdsale.weiRaised()\n    balance.should.be.bignumber.equal(0)\n    raised.should.be.bignumber.most(cap)\n  })\n\n  it('owner can set the price for a particular buyer', async function() {\n    await crowdsale.addToWhitelist(buyer)\n\n    const preferentialRateForBuyer = new BigNumber(200)\n    const { logs } = await crowdsale.setBuyerRate(\n      buyer,\n      preferentialRateForBuyer\n    )\n\n    const event = logs.find(e => e.event === 'PreferentialRateChange')\n    expect(event).to.exist\n\n    await crowdsale.buyTokens(buyer, { value, from: buyer })\n    const balance = await token.balanceOf(buyer)\n    balance.should.be.bignumber.equal(value.mul(preferentialRateForBuyer))\n    balance.should.not.be.bignumber.equal(value.mul(preferentialRate))\n\n    // cannot change rate after crowdsale starts\n    await advanceToBlock(startBlock - 1)\n    await crowdsale\n      .setBuyerRate(buyer, preferentialRateForBuyer)\n      .should.be.rejectedWith(EVMRevert)\n  })\n\n  it('owner cannot set a custom rate before whitelisting a buyer', async function() {\n    await crowdsale\n      .setBuyerRate(buyer, new BigNumber(200))\n      .should.be.rejectedWith(EVMRevert)\n  })\n\n  it('beneficiary is not the same as buyer', async function() {\n    const beneficiary = buyer2\n\n    await crowdsale.addToWhitelist(buyer)\n    await crowdsale.addToWhitelist(beneficiary)\n\n    const preferentialRateForBuyer = new BigNumber(200)\n    const invalidRate = new BigNumber(100)\n    await crowdsale.setBuyerRate(buyer, preferentialRateForBuyer)\n    await crowdsale.setBuyerRate(beneficiary, invalidRate)\n\n    await crowdsale.buyTokens(beneficiary, { value, from: buyer })\n    const balance = await token.balanceOf(beneficiary)\n    balance.should.be.bignumber.equal(value.mul(preferentialRateForBuyer))\n  })\n\n  it('tokens should be assigned correctly to foundation when finalized', async function() {\n    await advanceToBlock(startBlock - 1)\n\n    // since price at first block is 1000, total tokens emitted will be 4000\n    await crowdsale.buyTokens(buyer, { value: 4, from: purchaser })\n\n    await advanceToBlock(endBlock)\n    await crowdsale.finalize()\n\n    const balance = await token.balanceOf(wallet)\n    balance.should.be.bignumber.equal(expectedFoundationTokens)\n\n    const totalSupply = await token.totalSupply()\n    totalSupply.should.be.bignumber.equal(expectedTokenSupply)\n  })\n})\n"
  },
  {
    "path": "test/MANAToken.js",
    "content": "// @flow\n'use strict'\n\nconst { EVMRevert } = require('./utils')\nconst MANAToken = artifacts.require('./MANAToken.sol')\n\nconst BigNumber = web3.BigNumber\n\ncontract('MANACrowdsale', function([owner, holder]) {\n  let token\n\n  beforeEach(async function() {\n    token = await MANAToken.new()\n  })\n\n  it('cannot burn tokens while paused', async function() {\n    await token.mint(holder, 1000)\n    await token.pause()\n    await token.burn(500, { from: holder }).should.be.rejectedWith(EVMRevert)\n\n    await token.unpause()\n    await token.burn(500, { from: holder }).should.be.fulfilled\n  })\n})\n"
  },
  {
    "path": "test/WhitelistedCrowdsale.js",
    "content": "// @flow\nimport { ether, EVMRevert } from './utils'\n\nconst BigNumber = web3.BigNumber\n\nconst WhitelistedCrowdsale = artifacts.require(\n  './helpers/WhitelistedCrowdsaleImpl.sol'\n)\nconst MintableToken = artifacts.require(\n  'zeppelin-solidity/contracts/tokens/MintableToken'\n)\n\ncontract('WhitelistCrowdsale', function([\n  _,\n  owner,\n  wallet,\n  beneficiary,\n  sender\n]) {\n  const rate = new BigNumber(1000)\n\n  beforeEach(async function() {\n    this.startBlock = web3.eth.blockNumber + 10\n    this.endBlock = this.startBlock + 10\n\n    this.crowdsale = await WhitelistedCrowdsale.new(\n      this.startBlock,\n      this.endBlock,\n      rate,\n      wallet,\n      { from: owner }\n    )\n\n    this.token = MintableToken.at(await this.crowdsale.token())\n  })\n\n  describe('whitelisting', function() {\n    const amount = ether(1)\n\n    it('should add address to whitelist', async function() {\n      let whitelisted = await this.crowdsale.isWhitelisted(sender)\n      whitelisted.should.equal(false)\n      await this.crowdsale.addToWhitelist(sender, { from: owner })\n      whitelisted = await this.crowdsale.isWhitelisted(sender)\n      whitelisted.should.equal(true)\n    })\n\n    it('should reject non-whitelisted sender', async function() {\n      await this.crowdsale\n        .buyTokens(beneficiary, { value: amount, from: sender })\n        .should.be.rejectedWith(EVMRevert)\n    })\n\n    it('should sell to whitelisted address', async function() {\n      await this.crowdsale.addToWhitelist(sender, { from: owner })\n      await this.crowdsale.buyTokens(beneficiary, {\n        value: amount,\n        from: sender\n      }).should.be.fulfilled\n    })\n  })\n})\n"
  },
  {
    "path": "test/helpers/BurnableTokenMock.sol",
    "content": "pragma solidity ^0.4.11;\n\nimport '../../contracts/BurnableToken.sol';\n\ncontract BurnableTokenMock is BurnableToken {\n\n  function BurnableTokenMock(address initialAccount, uint initialBalance) {\n    balances[initialAccount] = initialBalance;\n    totalSupply = initialBalance;\n  }\n\n}\n"
  },
  {
    "path": "test/helpers/WhitelistedCrowdsaleImpl.sol",
    "content": "pragma solidity ^0.4.11;\n\n\nimport '../../contracts/WhitelistedCrowdsale.sol';\n\n\ncontract WhitelistedCrowdsaleImpl is WhitelistedCrowdsale {\n\n  function WhitelistedCrowdsaleImpl (\n    uint256 _startBlock,\n    uint256 _endBlock,\n    uint256 _rate,\n    address _wallet\n  )\n    Crowdsale(_startBlock, _endBlock, _rate, _wallet)\n    WhitelistedCrowdsale() \n  {\n  }\n\n}\n"
  },
  {
    "path": "test/utils.js",
    "content": "// @flow\n\nconst BigNumber = web3.BigNumber\n\nexport const should = require('chai')\n  .use(require('chai-as-promised'))\n  .use(require('chai-bignumber')(BigNumber))\n  .should()\n\ntype RPCParams = {\n  method: string,\n  params?: Array<mixed>\n}\n\nfunction rpcCall(call: RPCParams) {\n  const payload: any = Object.assign(\n    {},\n    {\n      jsonrpc: '2.0',\n      id: Date.now()\n    },\n    call\n  )\n\n  return new Promise((resolve, reject) => {\n    web3.currentProvider.sendAsync(payload, (err, res) => {\n      return err ? reject(err) : resolve(res)\n    })\n  })\n}\n\nexport function advanceBlock() {\n  return rpcCall({ method: 'evm_mine' })\n}\n\nexport function increaseTime(seconds: number) {\n  return rpcCall({ method: 'evm_increaseTime', params: [+seconds] })\n}\n\n// Advances the block number so that the last mined block is `number`.\nexport async function advanceToBlock(number: number) {\n  if (web3.eth.blockNumber > number) {\n    throw Error(\n      `block number ${number} is in the past (current is ${\n        web3.eth.blockNumber\n      })`\n    )\n  }\n\n  while (web3.eth.blockNumber < number) {\n    await advanceBlock()\n  }\n}\n\nexport async function advanceToTime(timestamp: number) {\n  const block = await web3.eth.getBlock('latest')\n  if (block.timestamp > timestamp) {\n    throw Error(\n      `time ${timestamp} is in the past (current is ${block.timestamp})`\n    )\n  }\n\n  await advanceTime(timestamp - block.timestamp)\n}\n\nexport async function advanceTime(time: number) {\n  await increaseTime(time)\n  await advanceBlock()\n}\n\nexport function ether(n: number) {\n  return new web3.BigNumber(web3.toWei(n, 'ether'))\n}\n\nexport const EVMThrow = 'invalid opcode'\nexport const EVMRevert = 'revert'\n"
  },
  {
    "path": "truffle.js",
    "content": "require('babel-register');\nrequire('babel-polyfill');\n\nmodule.exports = {\n  networks: {\n    livenet: {\n      host: \"localhost\",\n      port: 8546,\n      network_id: \"1\" // Match any network id\n    },\n    development: {\n      host: \"localhost\",\n      port: 8545,\n      gas: 10000000,\n      network_id: \"*\" // Match any network id\n    }\n  }\n};\n"
  }
]