├── .gitattributes ├── .gitbook.yml ├── packages ├── sale │ ├── installed_contracts │ │ └── zeppelin │ │ │ ├── lock.uri │ │ │ ├── contracts │ │ │ ├── lifecycle │ │ │ │ ├── Killable.sol │ │ │ │ ├── Migrations.sol │ │ │ │ └── Pausable.sol │ │ │ ├── ownership │ │ │ │ ├── Contactable.sol │ │ │ │ ├── Ownable.sol │ │ │ │ ├── Claimable.sol │ │ │ │ ├── DelayedClaimable.sol │ │ │ │ ├── Multisig.sol │ │ │ │ └── Shareable.sol │ │ │ ├── token │ │ │ │ ├── ERC20Basic.sol │ │ │ │ ├── BasicToken.sol │ │ │ │ ├── SimpleToken.sol │ │ │ │ ├── ERC20.sol │ │ │ │ ├── CrowdsaleToken.sol │ │ │ │ ├── StandardToken.sol │ │ │ │ └── VestedToken.sol │ │ │ ├── LimitBalance.sol │ │ │ ├── payment │ │ │ │ └── PullPayment.sol │ │ │ ├── SafeMath.sol │ │ │ ├── Bounty.sol │ │ │ ├── DayLimit.sol │ │ │ └── MultisigWallet.sol │ │ │ └── ethpm.json │ ├── migrations │ │ ├── 1_initial_migration.js │ │ └── 2_deploy_sale.js │ ├── truffle.js │ ├── test │ │ ├── helpers │ │ │ ├── assertThrows.js │ │ │ ├── NetworkMock.sol │ │ │ ├── TokenReceiverMock.sol │ │ │ ├── timer.js │ │ │ ├── ThrowProxy.sol │ │ │ ├── AragonTokenSaleTokenMock.sol │ │ │ ├── AragonTokenSaleMock.sol │ │ │ └── MultisigMock.sol │ │ ├── TestMiniMeCloning.sol │ │ ├── StandardToken.js │ │ ├── IrrevocableVestedToken.js │ │ └── TestTokenSaleCap.sol │ ├── contracts │ │ ├── interface │ │ │ ├── ApproveAndCallReceiver.sol │ │ │ ├── Controlled.sol │ │ │ └── Controller.sol │ │ ├── misc │ │ │ └── Migrations.sol │ │ ├── ANT.sol │ │ ├── ANPlaceholder.sol │ │ ├── SaleWallet.sol │ │ └── MiniMeIrrevocableVestedToken.sol │ ├── package.json │ ├── README.md │ └── scripts │ │ ├── ganache-cli.sh │ │ ├── live.js │ │ └── presale.js ├── v2 │ ├── test │ │ └── helpers │ │ │ ├── tokens.js │ │ │ ├── assert.js │ │ │ ├── erc712.js │ │ │ ├── erc2612.js │ │ │ └── erc3009.js │ ├── e2e │ │ ├── holders.js │ │ ├── signers.js │ │ └── test │ │ │ └── pre-deploy-mainnet-fork.js │ ├── contracts │ │ ├── interfaces │ │ │ ├── ILockManager.sol │ │ │ ├── IERC20.sol │ │ │ ├── ApproveAndCallReceiver.sol │ │ │ └── IGuardiansRegistry.sol │ │ ├── libraries │ │ │ └── SafeMath.sol │ │ ├── ANTv2MultiMinter.sol │ │ ├── EscrowANTv2Migrator.sol │ │ ├── minters │ │ │ ├── ANJNoLockMinter.sol │ │ │ └── ANJLockMinter.sol │ │ ├── ANTv2Migrator.sol │ │ └── ANTv2.sol │ ├── buidler.config.4.js │ ├── buidler │ │ ├── utils.js │ │ └── cli.js │ ├── buidler.config.e2e-post.js │ ├── buidler.config.e2e-pre.js │ ├── deploy │ │ ├── deploy-mainnet.js │ │ └── deploy-testnet.js │ ├── package.json │ ├── buidler.config.js │ └── README.md └── controller │ ├── e2e │ └── signers.js │ ├── buidler.config.4.js │ ├── buidler.config.e2e-4.js │ ├── contracts │ ├── interfaces │ │ ├── IMiniMeLike.sol │ │ └── ITokenController.sol │ └── ANTController.sol │ ├── package.json │ ├── buidler.config.e2e.js │ ├── README.md │ └── buidler.config.js ├── security ├── v2-2020-10.pdf ├── v1controller-2020-08.pdf └── ANJ-merge-audit-report-v210121.pdf ├── docs ├── antv1 │ ├── assets │ │ ├── ant_buy.png │ │ ├── an_deploy.png │ │ ├── upgrade-sign.png │ │ ├── upgrade-amount.png │ │ ├── upgrade-connect.png │ │ ├── upgrade-abi-encode.png │ │ ├── upgrade-amount-all.png │ │ ├── upgrade-etherscan.png │ │ ├── upgrade-check-wallet.png │ │ ├── upgrade-portal-start.png │ │ ├── upgrade-sign-success.png │ │ ├── upgrade-check-balances.png │ │ ├── upgrade-remove-balancer.png │ │ ├── upgrade-remove-uniswap.png │ │ └── upgrade-abi-encoded-data.png │ ├── readme.md │ ├── upgrade-migrating-liquidity.md │ ├── non-standard.md │ ├── behaviour.md │ ├── minime.md │ ├── upgrade-portal.md │ ├── upgrade-contract-interaction.md │ ├── sale-flow.md │ └── upgrade.md ├── token │ ├── readme.md │ ├── sale.md │ └── about.md ├── developers │ ├── readme.md │ ├── deployments.md │ └── integration.md ├── security │ └── readme.md ├── SUMMARY.md └── README.md ├── .gitignore ├── .github └── workflows │ ├── ci_v2.yml │ ├── ci_sale.yml │ └── ci_controller.yml ├── SECURITY.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitbook.yml: -------------------------------------------------------------------------------- 1 | root: ./docs/ 2 | structure: 3 | readme: ./README.md 4 | summary: ./SUMMARY.md 5 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/lock.uri: -------------------------------------------------------------------------------- 1 | ipfs://Qmas6SZ4tXFfcrxSobqmETkGSzNzPEoqfmTztksKT3oY19 -------------------------------------------------------------------------------- /security/v2-2020-10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/security/v2-2020-10.pdf -------------------------------------------------------------------------------- /docs/antv1/assets/ant_buy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/ant_buy.png -------------------------------------------------------------------------------- /docs/antv1/assets/an_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/an_deploy.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-sign.png -------------------------------------------------------------------------------- /security/v1controller-2020-08.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/security/v1controller-2020-08.pdf -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-amount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-amount.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-connect.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-abi-encode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-abi-encode.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-amount-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-amount-all.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-etherscan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-etherscan.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-check-wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-check-wallet.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-portal-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-portal-start.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-sign-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-sign-success.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-check-balances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-check-balances.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-remove-balancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-remove-balancer.png -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-remove-uniswap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-remove-uniswap.png -------------------------------------------------------------------------------- /security/ANJ-merge-audit-report-v210121.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/security/ANJ-merge-audit-report-v210121.pdf -------------------------------------------------------------------------------- /docs/antv1/assets/upgrade-abi-encoded-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragon/aragon-network-token/HEAD/docs/antv1/assets/upgrade-abi-encoded-data.png -------------------------------------------------------------------------------- /packages/sale/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /docs/token/readme.md: -------------------------------------------------------------------------------- 1 | # About ANT 2 | 3 | General information about ANT, its uses, and details about the original token sale. 4 | 5 | 1. [About the Aragon Network Token](about.md) 6 | 3. [Historical token sale information](sale.md) 7 | -------------------------------------------------------------------------------- /packages/v2/test/helpers/tokens.js: -------------------------------------------------------------------------------- 1 | const { bigExp, bn } = require('@aragon/contract-helpers-test') 2 | 3 | function tokenAmount(amount) { 4 | return bigExp(amount, 18) 5 | } 6 | 7 | module.exports = { 8 | tokenAmount, 9 | } 10 | -------------------------------------------------------------------------------- /packages/sale/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | rpc: { 4 | network_id: 15, 5 | host: 'localhost', 6 | port: 8545, 7 | gas: 1e8, 8 | gasPrice: 1, 9 | }, 10 | }, 11 | build: {}, 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | **/**/node_modules 3 | 4 | # Build artifacts 5 | **/**/abi/ 6 | **/**/artifacts/ 7 | **/**/build/ 8 | **/**/cache/ 9 | 10 | # Ignore package-lock files (only use yarn.lock) 11 | package-lock.json 12 | 13 | # Misc 14 | .cache 15 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/assertThrows.js: -------------------------------------------------------------------------------- 1 | module.exports = function(error) { 2 | // Solidity 0.4.8 did not yet have the concept of reverts, so all throws lead to invalid jumps 3 | assert.isAbove(error.message.search('invalid JUMP'), -1, 'Invalid JUMP error must be returned'); 4 | } 5 | -------------------------------------------------------------------------------- /docs/developers/readme.md: -------------------------------------------------------------------------------- 1 | # Developers 2 | 3 | Useful information for developers wishing to programmatically interact with the token or initial sale. 4 | 5 | 1. [Getting started](getting-started.md) 6 | 2. [About the MiniMe token](minime.md) 7 | 3. [Initial token sale flow](sale-flow.md) 8 | -------------------------------------------------------------------------------- /packages/sale/contracts/interface/ApproveAndCallReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | /* 4 | Copyright 2017, Jordi Baylina (Giveth) 5 | */ 6 | 7 | contract ApproveAndCallReceiver { 8 | function receiveApproval(address _from, uint256 _amount, address _token, bytes _data); 9 | } 10 | -------------------------------------------------------------------------------- /packages/v2/e2e/holders.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '0xe93381fb4c4f14bda253907b18fad305d799241a', 3 | '0x03af24a6db8e011b86c32960ec6ede52ae5906fb', 4 | '0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98', 5 | '0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be', 6 | '0x1786727d7e5f93cb4bda4d13c6bfc32446ef1a8b', 7 | ] 8 | -------------------------------------------------------------------------------- /packages/controller/e2e/signers.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '0x75d83a0ae1543fd4b49594023977e1daf5a954c5', 3 | '0x939428c249a738990d4fb938509a5c43f3ecedcf', 4 | '0xfdbeebf23663577804248126559addb6785a5f8f', 5 | '0xD4bE3593eb07F97de7E27bE56Ff7aD2f27a72364', 6 | '0xd8684AcE445701c04Cb610d806BE568B2CBa0f2A', 7 | ] 8 | -------------------------------------------------------------------------------- /packages/v2/contracts/interfaces/ILockManager.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.5.17; 6 | 7 | interface ILockManager { 8 | /** 9 | * @dev Tell whether a user can unlock a certain amount of tokens 10 | */ 11 | function canUnlock(address user, uint256 amount) external view returns (bool); 12 | } -------------------------------------------------------------------------------- /packages/v2/e2e/signers.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '0x75d83a0ae1543fd4b49594023977e1daf5a954c5', 3 | '0x939428c249a738990d4fb938509a5c43f3ecedcf', 4 | '0xfdbeebf23663577804248126559addb6785a5f8f', 5 | '0xD4bE3593eb07F97de7E27bE56Ff7aD2f27a72364', 6 | '0xd8684AcE445701c04Cb610d806BE568B2CBa0f2A', 7 | '0x839395e20bbB182fa440d08F850E6c7A8f6F0780' 8 | ] 9 | -------------------------------------------------------------------------------- /packages/controller/buidler.config.4.js: -------------------------------------------------------------------------------- 1 | // This buidler configuration is only used to compile ANTMock.sol for testing, 2 | // which were deployed on an older solc version (0.4.8) 3 | module.exports = { 4 | solc: { 5 | version: "0.4.8", 6 | optimizer: { 7 | enabled: true, 8 | runs: 200, 9 | }, 10 | }, 11 | paths: { 12 | sources: "./mocks/", 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/v2/buidler.config.4.js: -------------------------------------------------------------------------------- 1 | // This buidler configuration is only used to compile ANT.sol and other mocks for testing, 2 | // which were deployed on an older solc version (0.4.8) 3 | module.exports = { 4 | solc: { 5 | version: "0.4.8", 6 | optimizer: { 7 | enabled: true, 8 | runs: 200, 9 | }, 10 | }, 11 | paths: { 12 | sources: "./mocks/", 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/lifecycle/Killable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "../ownership/Ownable.sol"; 5 | 6 | 7 | /* 8 | * Killable 9 | * Base contract that can be killed by owner. All funds in contract will be sent to the owner. 10 | */ 11 | contract Killable is Ownable { 12 | function kill() onlyOwner { 13 | selfdestruct(owner); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /docs/security/readme.md: -------------------------------------------------------------------------------- 1 | # Security policy 2 | 3 | The developers supporting the Aragon project take security and their users' well-being very seriously. 4 | 5 | You may find our security policy on [Github](https://github.com/aragon/aragon-network-token/security). 6 | 7 | If you believe you have found a potential vulnerability, please review the [instructions for responsible disclosures](https://wiki.aragon.org/association/security/#reporting). 8 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/ownership/Contactable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | import './Ownable.sol'; 4 | /* 5 | * Contactable token 6 | * Basic version of a contactable contract 7 | */ 8 | contract Contactable is Ownable{ 9 | 10 | string public contactInformation; 11 | 12 | function setContactInformation(string info) onlyOwner{ 13 | contactInformation = info; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/ERC20Basic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /* 5 | * ERC20Basic 6 | * Simpler version of ERC20 interface 7 | * see https://github.com/ethereum/EIPs/issues/20 8 | */ 9 | contract ERC20Basic { 10 | uint public totalSupply; 11 | function balanceOf(address who) constant returns (uint); 12 | function transfer(address to, uint value); 13 | event Transfer(address indexed from, address indexed to, uint value); 14 | } 15 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/NetworkMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import '../../contracts/interface/Controller.sol'; 4 | 5 | contract NetworkMock { 6 | function proxyPayment(address _owner) payable returns (bool) { 7 | return false; 8 | } 9 | 10 | function onTransfer(address _from, address _to, uint _amount) returns (bool) { 11 | return false; 12 | } 13 | 14 | function onApprove(address _owner, address _spender, uint _amount) returns (bool) { 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/controller/buidler.config.e2e-4.js: -------------------------------------------------------------------------------- 1 | // This buidler configuration is only used to compile the mocks for testing, 2 | // which were deployed on an older solc version (0.4.8) 3 | // Note that this can not be combined with the buidler.config.4.js file, as 4 | // AragonTokenSale.sol and ANT.sol clash 5 | module.exports = { 6 | solc: { 7 | version: "0.4.8", 8 | optimizer: { 9 | enabled: true, 10 | runs: 200, 11 | }, 12 | }, 13 | paths: { 14 | sources: "./e2e/mocks/", 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/lifecycle/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import '../ownership/Ownable.sol'; 5 | 6 | 7 | contract Migrations is Ownable { 8 | uint public lastCompletedMigration; 9 | 10 | function setCompleted(uint completed) onlyOwner { 11 | lastCompletedMigration = completed; 12 | } 13 | 14 | function upgrade(address newAddress) onlyOwner { 15 | Migrations upgraded = Migrations(newAddress); 16 | upgraded.setCompleted(lastCompletedMigration); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/v2/test/helpers/assert.js: -------------------------------------------------------------------------------- 1 | const { 2 | assertRevert: assertRevertHelper, 3 | } = require("@aragon/contract-helpers-test/src/asserts"); 4 | 5 | // this ctx will prevent the underlying library from doing 6 | // special geth node assert message processing 7 | const ctx = { 8 | web3: { 9 | eth: { 10 | getNodeInfo: () => [], 11 | }, 12 | }, 13 | }; 14 | 15 | const assertRevert = (blockOrPromise, expectedReason) => 16 | assertRevertHelper(blockOrPromise, expectedReason, ctx); 17 | 18 | module.exports = { 19 | assertRevert, 20 | }; 21 | -------------------------------------------------------------------------------- /docs/antv1/readme.md: -------------------------------------------------------------------------------- 1 | # ANTv1 2 | 3 | Technical materials related to ANTv1 and ANT's original token sale, and documentation covering ANTv1's upgrade to ANTv2. 4 | 5 | 1. [Upgrading to ANTv2](upgrade.md) 6 | 1. [Upgrade portal](upgrade-portal.md) 7 | 2. [Contract interaction](upgrade-contract-interaction.md) 8 | 3. [Migrating on-chain liquidity](upgrade-migrating-liquidity.md) 9 | 2. [Token behaviour](behaviour.md) 10 | 3. [Non-standard behaviours and gotchas](non-standard.md) 11 | 4. [About the MiniMe token](minime.md) 12 | 5. [The initial token sale flow](sale-flow.md) 13 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/TokenReceiverMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import '../../contracts/interface/ApproveAndCallReceiver.sol'; 4 | import "zeppelin/token/StandardToken.sol"; 5 | 6 | contract TokenReceiverMock is ApproveAndCallReceiver { 7 | bytes public extraData; 8 | uint public tokenBalance; 9 | 10 | function receiveApproval(address _from, uint256 _amount, address _token, bytes _data) { 11 | StandardToken(_token).transferFrom(_from, this, _amount); 12 | 13 | tokenBalance = StandardToken(_token).balanceOf(this); 14 | extraData = _data; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/v2/contracts/libraries/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | // A library for performing overflow-safe math, courtesy of DappHub: https://github.com/dapphub/ds-math/blob/d0ef6d6a5f/src/math.sol 5 | // Modified to include only the essentials 6 | library SafeMath { 7 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 8 | require((z = x + y) >= x, "MATH:ADD_OVERFLOW"); 9 | } 10 | 11 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 12 | require((z = x - y) <= x, "MATH:SUB_UNDERFLOW"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/controller/contracts/interfaces/IMiniMeLike.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.5.0; 3 | 4 | 5 | /** 6 | * @dev A sparse MiniMe-like interface containing just `generateTokens()`. 7 | */ 8 | interface IMiniMeLike { 9 | /** 10 | * @notice Generates `_amount` tokens that are assigned to `_owner` 11 | * @param _owner The address that will be assigned the new tokens 12 | * @param _amount The quantity of tokens generated 13 | * @return True if the tokens are generated correctly 14 | */ 15 | function generateTokens(address _owner, uint _amount) external returns (bool); 16 | } 17 | -------------------------------------------------------------------------------- /packages/v2/contracts/interfaces/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | interface IERC20 { 5 | function totalSupply() external view returns (uint256); 6 | function balanceOf(address account) external view returns (uint256); 7 | function allowance(address owner, address spender) external view returns (uint256); 8 | function approve(address spender, uint256 amount) external returns (bool); 9 | function transfer(address recipient, uint256 amount) external returns (bool); 10 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/ci_v2.yml: -------------------------------------------------------------------------------- 1 | name: ci:v2 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | paths: 8 | - '*' 9 | - 'packages/v2/**' 10 | 11 | defaults: 12 | run: 13 | working-directory: 'packages/v2' 14 | 15 | jobs: 16 | CI: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v1 20 | - name: Install node 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 10 24 | - name: yarn install 25 | run: yarn install --frozen-lockfile 26 | - name: test 27 | run: yarn test 28 | env: 29 | CI: true 30 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/timer.js: -------------------------------------------------------------------------------- 1 | // timer for tests specific to testrpc 2 | module.exports = s => { 3 | return new Promise((resolve, reject) => { 4 | web3.currentProvider.sendAsync({ 5 | jsonrpc: '2.0', 6 | method: 'evm_increaseTime', 7 | params: [s], // 60 seaconds, may need to be hex, I forget 8 | id: new Date().getTime() // Id of the request; anything works, really 9 | }, function(err) { 10 | if (err) return reject(err); 11 | resolve(); 12 | }); 13 | //setTimeout(() => resolve(), s * 1000 + 600) // 600ms breathing room for testrpc to sync 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /.github/workflows/ci_sale.yml: -------------------------------------------------------------------------------- 1 | name: ci:sale 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | paths: 8 | - '*' 9 | - 'packages/sale/**' 10 | 11 | defaults: 12 | run: 13 | working-directory: 'packages/sale' 14 | 15 | jobs: 16 | CI: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v1 20 | - name: Install node 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 10 24 | - name: yarn install 25 | run: yarn install --frozen-lockfile 26 | - name: test 27 | run: yarn test 28 | env: 29 | CI: true 30 | -------------------------------------------------------------------------------- /packages/sale/contracts/misc/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/ci_controller.yml: -------------------------------------------------------------------------------- 1 | name: ci:controller 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | paths: 8 | - '*' 9 | - 'packages/controller/**' 10 | 11 | defaults: 12 | run: 13 | working-directory: 'packages/controller' 14 | 15 | jobs: 16 | CI: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v1 20 | - name: Install node 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: 10 24 | - name: yarn install 25 | run: yarn install --frozen-lockfile 26 | - name: test 27 | run: yarn test 28 | env: 29 | CI: true 30 | -------------------------------------------------------------------------------- /packages/v2/test/helpers/erc712.js: -------------------------------------------------------------------------------- 1 | const abi = require('web3-eth-abi') 2 | const { keccak256 } = require('web3-utils') 3 | 4 | function createDomainSeparator(name, version, chainId, verifyingContract) { 5 | return keccak256( 6 | abi.encodeParameters( 7 | ['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'], 8 | [ 9 | keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), 10 | keccak256(name), 11 | keccak256(version), 12 | chainId, 13 | verifyingContract 14 | ] 15 | ) 16 | ) 17 | } 18 | 19 | module.exports = { 20 | createDomainSeparator, 21 | } 22 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/ownership/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /* 5 | * Ownable 6 | * 7 | * Base contract with an owner. 8 | * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. 9 | */ 10 | contract Ownable { 11 | address public owner; 12 | 13 | function Ownable() { 14 | owner = msg.sender; 15 | } 16 | 17 | modifier onlyOwner() { 18 | if (msg.sender != owner) { 19 | throw; 20 | } 21 | _; 22 | } 23 | 24 | function transferOwnership(address newOwner) onlyOwner { 25 | if (newOwner != address(0)) { 26 | owner = newOwner; 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/LimitBalance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /** 5 | * LimitBalance 6 | * Simple contract to limit the balance of child contract. 7 | * Note this doesn't prevent other contracts to send funds 8 | * by using selfdestruct(address); 9 | * See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account 10 | */ 11 | contract LimitBalance { 12 | 13 | uint public limit; 14 | 15 | function LimitBalance(uint _limit) { 16 | limit = _limit; 17 | } 18 | 19 | modifier limitedPayable() { 20 | if (this.balance > limit) { 21 | throw; 22 | } 23 | _; 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /packages/v2/contracts/interfaces/ApproveAndCallReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | interface ApproveAndCallReceiver { 5 | /** 6 | * @dev This allows users to use their tokens to interact with contracts in one function call instead of two 7 | * @param _from Address of the account transferring the tokens 8 | * @param _amount The amount of tokens approved for in the transfer 9 | * @param _token Address of the token contract calling this function 10 | * @param _data Optional data that can be used to add signalling information in more complex staking applications 11 | */ 12 | function receiveApproval(address _from, uint256 _amount, address _token, bytes calldata _data) external; 13 | } 14 | -------------------------------------------------------------------------------- /packages/sale/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aragon/network-token-sale", 3 | "version": "0.0.1", 4 | "private": "true", 5 | "description": "Aragon Network Token sale", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/aragon/aragon-network-token.git" 9 | }, 10 | "author": "Aragon Association ", 11 | "license": "GPL-3.0-or-later", 12 | "devDependencies": { 13 | "async": "^2.4.0", 14 | "ganache-cli": "^6.10.1", 15 | "moment": "^2.18.1", 16 | "truffle": "3.2.1" 17 | }, 18 | "resolutions": { 19 | "truffle-provisioner": "0.1.2" 20 | }, 21 | "scripts": { 22 | "compile": "truffle compile", 23 | "test": "./scripts/ganache-cli.sh" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/BasicToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import './ERC20Basic.sol'; 5 | import '../SafeMath.sol'; 6 | 7 | 8 | /* 9 | * Basic token 10 | * Basic version of StandardToken, with no allowances 11 | */ 12 | contract BasicToken is ERC20Basic, SafeMath { 13 | 14 | mapping(address => uint) balances; 15 | 16 | function transfer(address _to, uint _value) { 17 | balances[msg.sender] = safeSub(balances[msg.sender], _value); 18 | balances[_to] = safeAdd(balances[_to], _value); 19 | Transfer(msg.sender, _to, _value); 20 | } 21 | 22 | function balanceOf(address _owner) constant returns (uint balance) { 23 | return balances[_owner]; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/SimpleToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "./StandardToken.sol"; 5 | 6 | 7 | /* 8 | * SimpleToken 9 | * 10 | * Very simple ERC20 Token example, where all tokens are pre-assigned 11 | * to the creator. Note they can later distribute these tokens 12 | * as they wish using `transfer` and other `StandardToken` functions. 13 | */ 14 | contract SimpleToken is StandardToken { 15 | 16 | string public name = "SimpleToken"; 17 | string public symbol = "SIM"; 18 | uint public decimals = 18; 19 | uint public INITIAL_SUPPLY = 10000; 20 | 21 | function SimpleToken() { 22 | totalSupply = INITIAL_SUPPLY; 23 | balances[msg.sender] = INITIAL_SUPPLY; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /packages/v2/buidler/utils.js: -------------------------------------------------------------------------------- 1 | const rlp = require('rlp') 2 | const { keccak256 } = require('web3-utils') 3 | 4 | function calculateContractAddress(from, nonce) { 5 | let encodedNonce 6 | 7 | if (nonce === 0) { 8 | encodedNonce = `0x` 9 | } else { 10 | const hexNonce = nonce.toString('16') 11 | const hexLength = (hexNonce.length + 1) % 2 ? hexNonce.length + 1 : hexNonce.length 12 | encodedNonce = `0x${hexNonce.padStart(hexLength, '0')}` 13 | } 14 | 15 | // address = sha3(rlp_encode(creator_account, creator_account_nonce))[12:] 16 | const rlpEncoded = `0x${rlp.encode([from, encodedNonce]).toString('hex')}` 17 | return `0x${keccak256(rlpEncoded).substr(-40)}` 18 | } 19 | 20 | module.exports = { 21 | calculateContractAddress, 22 | } 23 | -------------------------------------------------------------------------------- /packages/sale/contracts/ANT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "./MiniMeIrrevocableVestedToken.sol"; 4 | 5 | /* 6 | Copyright 2017, Jorge Izquierdo (Aragon Association) 7 | */ 8 | 9 | contract ANT is MiniMeIrrevocableVestedToken { 10 | // @dev ANT constructor just parametrizes the MiniMeIrrevocableVestedToken constructor 11 | function ANT( 12 | address _tokenFactory 13 | ) MiniMeIrrevocableVestedToken( 14 | _tokenFactory, 15 | 0x0, // no parent token 16 | 0, // no snapshot block number from parent 17 | "Aragon Network Token", // Token name 18 | 18, // Decimals 19 | "ANT", // Symbol 20 | true // Enable transfers 21 | ) {} 22 | } 23 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/ownership/Claimable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | 4 | import './Ownable.sol'; 5 | 6 | 7 | /* 8 | * Claimable 9 | * 10 | * Extension for the Ownable contract, where the ownership needs to be claimed. This allows the new owner to accept the transfer. 11 | */ 12 | contract Claimable is Ownable { 13 | address public pendingOwner; 14 | 15 | modifier onlyPendingOwner() { 16 | if (msg.sender != pendingOwner) { 17 | throw; 18 | } 19 | _; 20 | } 21 | 22 | function transferOwnership(address newOwner) onlyOwner { 23 | pendingOwner = newOwner; 24 | } 25 | 26 | function claimOwnership() onlyPendingOwner { 27 | owner = pendingOwner; 28 | pendingOwner = 0x0; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /* 5 | * ERC20 interface 6 | * see https://github.com/ethereum/EIPs/issues/20 7 | */ 8 | contract ERC20 { 9 | function totalSupply() constant returns (uint); 10 | function balanceOf(address who) constant returns (uint); 11 | function allowance(address owner, address spender) constant returns (uint); 12 | 13 | function transfer(address to, uint value) returns (bool ok); 14 | function transferFrom(address from, address to, uint value) returns (bool ok); 15 | function approve(address spender, uint value) returns (bool ok); 16 | event Transfer(address indexed from, address indexed to, uint value); 17 | event Approval(address indexed owner, address indexed spender, uint value); 18 | } 19 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/ThrowProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "truffle/Assert.sol"; 4 | 5 | // Based on Simon de la Rouviere method: http://truffleframework.com/tutorials/testing-for-throws-in-solidity-tests 6 | 7 | // Proxy contract for testing throws 8 | contract ThrowProxy { 9 | address public target; 10 | bytes data; 11 | 12 | function ThrowProxy(address _target) { 13 | target = _target; 14 | } 15 | 16 | //prime the data using the fallback function. 17 | function() { 18 | data = msg.data; 19 | } 20 | 21 | function assertThrows(string msg) { 22 | Assert.isFalse(execute(), msg); 23 | } 24 | 25 | function assertItDoesntThrow(string msg) { 26 | Assert.isTrue(execute(), msg); 27 | } 28 | 29 | function execute() returns (bool) { 30 | return target.call(data); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/ownership/DelayedClaimable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import './Ownable.sol'; 5 | import './Claimable.sol'; 6 | 7 | 8 | /* 9 | * DelayedClaimable 10 | * Extension for the Claimable contract, where the ownership needs to be claimed before/after certain block number 11 | */ 12 | contract DelayedClaimable is Ownable, Claimable { 13 | 14 | uint public end; 15 | uint public start; 16 | 17 | function setLimits(uint _start, uint _end) onlyOwner { 18 | if (_start > _end) 19 | throw; 20 | end = _end; 21 | start = _start; 22 | } 23 | 24 | function claimOwnership() onlyPendingOwner { 25 | if ((block.number > end) || (block.number < start)) 26 | throw; 27 | owner = pendingOwner; 28 | pendingOwner = 0x0; 29 | end = 0; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /packages/sale/contracts/interface/Controlled.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | /* 4 | Copyright 2017, Jorge Izquierdo (Aragon Association) 5 | Copyright 2017, Jordi Baylina (Giveth) 6 | 7 | Based on MiniMeToken.sol from https://github.com/Giveth/minime 8 | */ 9 | 10 | contract Controlled { 11 | /// @notice The address of the controller is the only address that can call 12 | /// a function with this modifier 13 | modifier onlyController { if (msg.sender != controller) throw; _; } 14 | 15 | address public controller; 16 | 17 | function Controlled() { controller = msg.sender;} 18 | 19 | /// @notice Changes the controller of the contract 20 | /// @param _newController The new controller of the contract 21 | function changeController(address _newController) onlyController { 22 | controller = _newController; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/lifecycle/Pausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "../ownership/Ownable.sol"; 5 | 6 | 7 | /* 8 | * Pausable 9 | * Abstract contract that allows children to implement an 10 | * emergency stop mechanism. 11 | */ 12 | contract Pausable is Ownable { 13 | bool public stopped; 14 | 15 | modifier stopInEmergency { 16 | if (!stopped) { 17 | _; 18 | } 19 | } 20 | 21 | modifier onlyInEmergency { 22 | if (stopped) { 23 | _; 24 | } 25 | } 26 | 27 | // called by the owner on emergency, triggers stopped state 28 | function emergencyStop() external onlyOwner { 29 | stopped = true; 30 | } 31 | 32 | // called by the owner on end of emergency, returns to normal state 33 | function release() external onlyOwner onlyInEmergency { 34 | stopped = false; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | For more information about Aragon-related security and contact information, please visit our [security policy page](https://wiki.aragon.org/association/security/). 4 | 5 | ## Audits 6 | 7 | ### V2 8 | 9 | [Coinspect](https://coinspect.com/) conducted a security audit for ANTv2 in October 2020. [See report](security/v2-2020-10.pdf). 10 | 11 | ### Sale 12 | 13 | Numerous audits were conducted for the original Aragon Network Token sale. These included: 14 | 15 | - Jorge Izquierdo, Aragon (author). 16 | - Jordi Baylina, MiniMe author: [audit](https://medium.com/@jbaylina/aragon-token-and-token-sale-audit-21cade332f1d) 17 | - Piper Merriam, pyethereum: [audit](https://gist.github.com/pipermerriam/7f36f9c9446d4fb8d0e6d842d7212177) 18 | 19 | ### V1 Controller 20 | 21 | [Coinspect](https://coinspect.com/) conducted a security audit for ANTv1's final ANT controller in August 2020. [See report](security/v1controller-2020-08.pdf). 22 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | - [Aragon Network Token](README.md) 4 | 5 | ## About the Token 6 | 7 | - [About ANT](token/about.md) 8 | - [Historical token sale information](token/sale.md) 9 | 10 | ## Developers 11 | 12 | - [Quick start](developers/quick-start.md) 13 | - [Integrating ANT](developers/integration.md) 14 | - [Historical deployments](developers/deployments.md) 15 | 16 | ## ANTv1 17 | 18 | - [Upgrading to ANTv2](antv1/upgrade.md) 19 | - [Upgrade portal](antv1/upgrade-portal.md) 20 | - [Contract interaction](antv1/upgrade-contract-interaction.md) 21 | - [Migrating on-chain liquidity](antv1/upgrade-migrating-liquidity.md) 22 | - [ANTv1: Token behaviour](antv1/behaviour.md) 23 | - [ANTv1: Non-standard behaviours and gotchas](antv1/non-standard.md) 24 | - [ANTv1: About the MiniMe token](antv1/minime.md) 25 | - [The initial token sale flow](antv1/sale-flow.md) 26 | 27 | ## Security 28 | 29 | - [Security policy](security/readme.md) 30 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/payment/PullPayment.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /* 5 | * PullPayment 6 | * Base contract supporting async send for pull payments. 7 | * Inherit from this contract and use asyncSend instead of send. 8 | */ 9 | contract PullPayment { 10 | mapping(address => uint) public payments; 11 | 12 | // store sent amount as credit to be pulled, called by payer 13 | function asyncSend(address dest, uint amount) internal { 14 | payments[dest] += amount; 15 | } 16 | 17 | // withdraw accumulated balance, called by payee 18 | function withdrawPayments() { 19 | address payee = msg.sender; 20 | uint payment = payments[payee]; 21 | 22 | if (payment == 0) { 23 | throw; 24 | } 25 | 26 | if (this.balance < payment) { 27 | throw; 28 | } 29 | 30 | payments[payee] = 0; 31 | 32 | if (!payee.send(payment)) { 33 | throw; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/v2/contracts/interfaces/IGuardiansRegistry.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.5.17; 6 | 7 | interface IGuardiansRegistry { 8 | 9 | /** 10 | * @dev This allows users or managers to stake and activate coins on the GuardiansRegistry 11 | * @param _guardian Address of the guardian staking and activating tokens for 12 | * @param _amount Amount of tokens to be staked and activated 13 | */ 14 | function stakeAndActivate(address _guardian, uint256 _amount) external; 15 | 16 | /** 17 | * @dev This allows users to lock the active tokens. 18 | * @param _guardian Address of the guardian locking the activation for 19 | * @param _lockManager Address of the lock manager that will control the lock 20 | * @param _amount Amount of active tokens to be locked 21 | */ 22 | function lockActivation(address _guardian, address _lockManager, uint256 _amount) external; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /packages/v2/test/helpers/erc2612.js: -------------------------------------------------------------------------------- 1 | const abi = require('web3-eth-abi') 2 | const { keccak256, soliditySha3 } = require('web3-utils') 3 | 4 | const PERMIT_TYPEHASH = keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)') 5 | 6 | async function createPermitDigest(token, owner, spender, value, nonce, deadline) { 7 | const domainSeparator = await token.getDomainSeparator() 8 | 9 | // Tightly pack with soliditySha3 10 | return soliditySha3( 11 | { type: 'bytes1', value: '0x19' }, 12 | { type: 'bytes1', value: '0x01' }, 13 | { type: 'bytes32', value: domainSeparator }, 14 | { type: 'bytes32', value: 15 | keccak256( 16 | abi.encodeParameters( 17 | ['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'], 18 | [PERMIT_TYPEHASH, owner, spender, value, nonce, deadline] 19 | ) 20 | ) 21 | } 22 | ) 23 | } 24 | 25 | module.exports = { 26 | createPermitDigest, 27 | PERMIT_TYPEHASH 28 | } 29 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/CrowdsaleToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "./StandardToken.sol"; 5 | 6 | 7 | /* 8 | * CrowdsaleToken 9 | * 10 | * Simple ERC20 Token example, with crowdsale token creation 11 | */ 12 | contract CrowdsaleToken is StandardToken { 13 | 14 | string public name = "CrowdsaleToken"; 15 | string public symbol = "CRW"; 16 | uint public decimals = 18; 17 | 18 | // 1 ether = 500 example tokens 19 | uint PRICE = 500; 20 | 21 | function () payable { 22 | createTokens(msg.sender); 23 | } 24 | 25 | function createTokens(address recipient) payable { 26 | if (msg.value == 0) { 27 | throw; 28 | } 29 | 30 | uint tokens = safeMul(msg.value, getPrice()); 31 | 32 | totalSupply = safeAdd(totalSupply, tokens); 33 | balances[recipient] = safeAdd(balances[recipient], tokens); 34 | } 35 | 36 | // replace this with any other price function 37 | function getPrice() constant returns (uint result) { 38 | return PRICE; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/AragonTokenSaleTokenMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import './AragonTokenSaleMock.sol'; 4 | 5 | // @dev AragonTokenSaleTokenMock for ERC20 tests purpose. 6 | // As it also deploys MiniMeTokenFactory, nonce will increase and therefore will be broken for future deployments 7 | 8 | contract AragonTokenSaleTokenMock is AragonTokenSaleMock { 9 | function AragonTokenSaleTokenMock(address initialAccount, uint initialBalance) 10 | AragonTokenSaleMock(10, 20, msg.sender, msg.sender, 100, 50, 2) 11 | { 12 | ANT token = new ANT(new MiniMeTokenFactory()); 13 | ANPlaceholder networkPlaceholder = new ANPlaceholder(this, token); 14 | token.changeController(address(this)); 15 | 16 | setANT(token, networkPlaceholder, new SaleWallet(msg.sender, 20, address(this))); 17 | allocatePresaleTokens(initialAccount, initialBalance, uint64(now), uint64(now)); 18 | activateSale(); 19 | setMockedBlockNumber(21); 20 | finalizeSale(mock_hiddenCap, mock_capSecret); 21 | 22 | token.changeVestingWhitelister(msg.sender); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/controller/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aragon/network-token-controller", 3 | "version": "0.0.1", 4 | "private": "true", 5 | "description": "Aragon Network Token controller", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/aragon/aragon-network-token.git" 9 | }, 10 | "author": "Aragon Association ", 11 | "license": "GPL-3.0-or-later", 12 | "devDependencies": { 13 | "@aragon/contract-helpers-test": "^0.1.0-rc.3", 14 | "@nomiclabs/buidler": "^1.4.3", 15 | "@nomiclabs/buidler-ganache": "^1.3.3", 16 | "@nomiclabs/buidler-truffle5": "^1.3.4", 17 | "@nomiclabs/buidler-web3": "^1.3.4", 18 | "chai": "^4.2.0", 19 | "web3": "^1.2.11" 20 | }, 21 | "scripts": { 22 | "compile": "buidler compile --force", 23 | "compile:mocks": "buidler compile --config buidler.config.4.js", 24 | "compile:mocks-e2e": "buidler compile --config buidler.config.e2e-4.js", 25 | "test": "yarn compile:mocks && buidler test", 26 | "test:e2e": "yarn compile:mocks-e2e && buidler --config buidler.config.e2e.js --network ganache test e2e/test/mainnet-fork.js" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/AragonTokenSaleMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import '../../contracts/AragonTokenSale.sol'; 4 | 5 | // @dev AragonTokenSaleMock mocks current block number 6 | 7 | contract AragonTokenSaleMock is AragonTokenSale { 8 | 9 | function AragonTokenSaleMock ( 10 | uint _initialBlock, 11 | uint _finalBlock, 12 | address _aragonDevMultisig, 13 | address _communityMultisig, 14 | uint256 _initialPrice, 15 | uint256 _finalPrice, 16 | uint8 _priceStages 17 | ) AragonTokenSale(_initialBlock, _finalBlock, _aragonDevMultisig, _communityMultisig, _initialPrice, _finalPrice, _priceStages, computeCap(mock_hiddenCap, mock_capSecret)) { 18 | 19 | } 20 | 21 | function getBlockNumber() internal constant returns (uint) { 22 | return mock_blockNumber; 23 | } 24 | 25 | function setMockedBlockNumber(uint _b) { 26 | mock_blockNumber = _b; 27 | } 28 | 29 | function setMockedTotalCollected(uint _totalCollected) { 30 | totalCollected = _totalCollected; 31 | } 32 | 33 | uint mock_blockNumber = 1; 34 | 35 | uint public mock_hiddenCap = 100 finney; 36 | uint public mock_capSecret = 1; 37 | } 38 | -------------------------------------------------------------------------------- /packages/v2/test/helpers/erc3009.js: -------------------------------------------------------------------------------- 1 | const abi = require('web3-eth-abi') 2 | const { keccak256, soliditySha3 } = require('web3-utils') 3 | 4 | const TRANSFER_WITH_AUTHORIZATION_TYPEHASH = keccak256('TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)') 5 | 6 | async function createTransferWithAuthorizationDigest(token, from, to, value, validAfter, validBefore, nonce) { 7 | const domainSeparator = await token.getDomainSeparator() 8 | 9 | // Tightly pack with soliditySha3 10 | return soliditySha3( 11 | { type: 'bytes1', value: '0x19' }, 12 | { type: 'bytes1', value: '0x01' }, 13 | { type: 'bytes32', value: domainSeparator }, 14 | { type: 'bytes32', value: 15 | keccak256( 16 | abi.encodeParameters( 17 | ['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256', 'bytes32'], 18 | [TRANSFER_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce] 19 | ) 20 | ) 21 | } 22 | ) 23 | } 24 | 25 | module.exports = { 26 | createTransferWithAuthorizationDigest, 27 | TRANSFER_WITH_AUTHORIZATION_TYPEHASH 28 | } 29 | -------------------------------------------------------------------------------- /packages/controller/buidler.config.e2e.js: -------------------------------------------------------------------------------- 1 | // This buidler configuration is only used to run the E2E tests on a ganache-based fork of mainnet. 2 | const { usePlugin } = require('@nomiclabs/buidler/config') 3 | const signers = require('./e2e/signers') 4 | 5 | usePlugin("@nomiclabs/buidler-ganache") 6 | usePlugin('@nomiclabs/buidler-truffle5') 7 | 8 | // NOTE: USE YOUR OWN (OR AN INFURA) RPC HERE! 9 | const FORK_NODE = process.env.FORK_NODE 10 | if (!FORK_NODE) { 11 | throw new Error('Please run the e2e test with a `FORK_NODE` environment variable set!') 12 | } 13 | 14 | module.exports = { 15 | networks: { 16 | // Configuration for Ganache-based fork of mainnet 17 | ganache: { 18 | // Mainnet RPC URL 19 | fork: FORK_NODE, 20 | // Fork block number (may need to be updated if RPC URL is not an archive node) 21 | fork_block_number: '10684151', 22 | // Unlocked accounts that we can "impersonate" 23 | unlocked_accounts: signers, 24 | 25 | url: 'http://localhost:8545', 26 | gasLimit: 6000000, 27 | defaultBalanceEther: 100, 28 | }, 29 | }, 30 | solc: { 31 | version: '0.5.17', 32 | optimizer: { 33 | enabled: true, 34 | runs: 10000, 35 | }, 36 | }, 37 | } 38 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/ownership/Multisig.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /* 5 | * Multisig 6 | * Interface contract for multisig proxy contracts; see below for docs. 7 | */ 8 | contract Multisig { 9 | // EVENTS 10 | 11 | // logged events: 12 | // Funds has arrived into the wallet (record how much). 13 | event Deposit(address _from, uint value); 14 | // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). 15 | event SingleTransact(address owner, uint value, address to, bytes data); 16 | // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). 17 | event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); 18 | // Confirmation still needed for a transaction. 19 | event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); 20 | 21 | 22 | // FUNCTIONS 23 | 24 | // TODO: document 25 | function changeOwner(address _from, address _to) external; 26 | function execute(address _to, uint _value, bytes _data) external returns (bytes32); 27 | function confirm(bytes32 _h) returns (bool); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /packages/sale/README.md: -------------------------------------------------------------------------------- 1 | # Aragon Network Token sale 2 | 3 | [![Build Status](https://img.shields.io/github/workflow/status/aragon/aragon-network-token/ci:sale?style=flat-square)](https://github.com/aragon/aragon-network-token/actions?query=workflow%3Aci%3Asale) 4 | 5 | Smart contracts related to the [Aragon Network Token sale](https://aragon.org/blog/aragon-token-sale-technical-overview-9c2a4b910755). 6 | 7 | ## Status 8 | 9 | This package is in _preservation_ mode. 10 | 11 | Given that the Aragon Network Token ("ANTv1") sale occurred in May 2017, we have opted to pin dependencies and build tooling to their original form (or as close as possible). 12 | 13 | We therefore specifically target the chain environment and solidity versions used: 14 | 15 | - Tests are run on the `byzantium` hardfork 16 | - Contracts are compiled via `solc 0.4.8` (thereby relying on `truffle@3.2.1`) 17 | - `truffle-provisioner@0.1.2` was pinned to avoid a breaking change introduced in later patch versions for this version of `truffle` 18 | 19 | ## Development 20 | 21 | ```sh 22 | yarn install 23 | yarn test 24 | ``` 25 | 26 | CI for this package is run through the [`ci_sale` Github action](../../.github/workflows/ci_sale.yml). 27 | 28 | ## Credits 29 | 30 | - MiniMe: Jordi Baylina (Giveth) 31 | -------------------------------------------------------------------------------- /packages/v2/buidler.config.e2e-post.js: -------------------------------------------------------------------------------- 1 | // This buidler configuration is only used to run the E2E tests on a ganache-based fork of mainnet. 2 | const { usePlugin } = require('@nomiclabs/buidler/config') 3 | const holders = require('./e2e/holders') 4 | const signers = require('./e2e/signers') 5 | 6 | usePlugin("@nomiclabs/buidler-ganache") 7 | usePlugin('@nomiclabs/buidler-truffle5') 8 | 9 | // NOTE: USE YOUR OWN (OR AN INFURA) RPC HERE! 10 | const FORK_NODE = process.env.FORK_NODE 11 | if (!FORK_NODE) { 12 | throw new Error('Please run the e2e test with a `FORK_NODE` environment variable set!') 13 | } 14 | 15 | module.exports = { 16 | networks: { 17 | // Configuration for Ganache-based fork of mainnet 18 | ganache: { 19 | // Mainnet RPC URL 20 | fork: FORK_NODE, 21 | // Fork block number (may need to be updated if RPC URL is not an archive node) 22 | fork_block_number: '11060155', 23 | // Unlocked accounts that we can "impersonate" 24 | unlocked_accounts: [...signers, ...holders], 25 | 26 | url: 'http://localhost:8545', 27 | gasLimit: 6000000, 28 | defaultBalanceEther: 100, 29 | }, 30 | }, 31 | solc: { 32 | version: '0.5.17', 33 | optimizer: { 34 | enabled: true, 35 | runs: 999999, 36 | }, 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /packages/v2/buidler.config.e2e-pre.js: -------------------------------------------------------------------------------- 1 | // This buidler configuration is only used to run the E2E tests on a ganache-based fork of mainnet. 2 | const { usePlugin } = require('@nomiclabs/buidler/config') 3 | const holders = require('./e2e/holders') 4 | const signers = require('./e2e/signers') 5 | 6 | usePlugin("@nomiclabs/buidler-ganache") 7 | usePlugin('@nomiclabs/buidler-truffle5') 8 | 9 | // NOTE: USE YOUR OWN (OR AN INFURA) RPC HERE! 10 | const FORK_NODE = process.env.FORK_NODE 11 | if (!FORK_NODE) { 12 | throw new Error('Please run the e2e test with a `FORK_NODE` environment variable set!') 13 | } 14 | 15 | module.exports = { 16 | networks: { 17 | // Configuration for Ganache-based fork of mainnet 18 | ganache: { 19 | // // Mainnet RPC URL 20 | fork: FORK_NODE, 21 | // // Fork block number (may need to be updated if RPC URL is not an archive node) 22 | fork_block_number: '11035036', 23 | // Unlocked accounts that we can "impersonate" 24 | unlocked_accounts: [...signers, ...holders], 25 | 26 | url: 'http://localhost:8545', 27 | gasLimit: 6000000, 28 | defaultBalanceEther: 100, 29 | }, 30 | }, 31 | solc: { 32 | version: '0.5.17', 33 | optimizer: { 34 | enabled: true, 35 | runs: 999999, 36 | }, 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Aragon Network Token 2 | 3 | Welcome to Aragon Network Token's documentation. 4 | 5 | Aragon Network Token (ticker: [ANT](https://coinmarketcap.com/currencies/aragon/)) is the native token of the [Aragon Network](https://aragon.network/). ANT is primarily used for the governance of the Aragon Network, although additional uses have been proposed. 6 | 7 | {% hint style="info" %} 8 | 👉 ANT has [upgraded to ANTv2](https://aragon.org/blog/antv2). Visit the [Upgrade Portal](https://upgrade.aragon.org/#/) to upgrade your ANT! 9 | {% endhint %} 10 | 11 | - The [**About**](token/about.md) section contains general information about ANT, its uses, and the original token sale 12 | - The [**Developers**](developers/quick-start.md) section contains useful information for developers wishing to programmatically interact with ANT 13 | - The [**ANTv1**](antv1/behaviour.md) section contains technical materials related to ANTv1 and ANT's original token sale 14 | - The [**Upgrade**](antv1/upgrade.md) section contains user guides and documentation for upgrading ANTv1 to ANTv2 15 | - The [**Security**](security/readme.md) section contains information relevant for security specialists 16 | 17 | If you have any questions or just want to say hi, come join the community on [Discord](https://discord.com/invite/aragon)! 18 | -------------------------------------------------------------------------------- /packages/sale/scripts/ganache-cli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit script as soon as a command fails. 4 | set -o errexit 5 | 6 | # Executes cleanup function at script exit. 7 | trap cleanup EXIT 8 | 9 | cleanup() { 10 | # Kill the ganache instance that we started (if we started one and if it's still running). 11 | if [ -n "$rpc_pid" ] && ps -p $rpc_pid > /dev/null; then 12 | kill -9 $rpc_pid 13 | fi 14 | } 15 | 16 | setup_testing_variables() { 17 | PORT=${PORT-8545} 18 | BALANCE=${BALANCE-100000} 19 | # Allow yuge gas limit for solidity tests 20 | GAS_LIMIT=${GAS_LIMIT-100000000} 21 | NETWORK_ID=${NETWORK_ID-15} 22 | ACCOUNTS=${ACCOUNTS-200} 23 | } 24 | 25 | start_ganache() { 26 | echo "Starting ganache-cli on byzantium..." 27 | # Rollback to byzantium hardfork to more closely mirror 2017 hardfork state 28 | # Allow unlimited contract size for solidity tests 29 | npx ganache-cli --allowUnlimitedContractSize -k byzantium -i ${NETWORK_ID} -l ${GAS_LIMIT} -g ${GAS_PRICE} -a ${ACCOUNTS} -e ${BALANCE} -p ${PORT} > /dev/null & 30 | rpc_pid=$! 31 | sleep 3 32 | echo "Running ganache-cli with pid ${rpc_pid} in port ${PORT}" 33 | } 34 | 35 | run_tests() { 36 | echo "Running tests $@..." 37 | npx truffle test --network rpc $@ 38 | } 39 | 40 | setup_testing_variables 41 | start_ganache 42 | run_tests $@ 43 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /** 5 | * Math operations with safety checks 6 | */ 7 | contract SafeMath { 8 | function safeMul(uint a, uint b) internal returns (uint) { 9 | uint c = a * b; 10 | assert(a == 0 || c / a == b); 11 | return c; 12 | } 13 | 14 | function safeDiv(uint a, uint b) internal returns (uint) { 15 | assert(b > 0); 16 | uint c = a / b; 17 | assert(a == b * c + a % b); 18 | return c; 19 | } 20 | 21 | function safeSub(uint a, uint b) internal returns (uint) { 22 | assert(b <= a); 23 | return a - b; 24 | } 25 | 26 | function safeAdd(uint a, uint b) internal returns (uint) { 27 | uint c = a + b; 28 | assert(c>=a && c>=b); 29 | return c; 30 | } 31 | 32 | function max64(uint64 a, uint64 b) internal constant returns (uint64) { 33 | return a >= b ? a : b; 34 | } 35 | 36 | function min64(uint64 a, uint64 b) internal constant returns (uint64) { 37 | return a < b ? a : b; 38 | } 39 | 40 | function max256(uint256 a, uint256 b) internal constant returns (uint256) { 41 | return a >= b ? a : b; 42 | } 43 | 44 | function min256(uint256 a, uint256 b) internal constant returns (uint256) { 45 | return a < b ? a : b; 46 | } 47 | 48 | function assert(bool assertion) internal { 49 | if (!assertion) { 50 | throw; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/controller/README.md: -------------------------------------------------------------------------------- 1 | # Aragon Network Token V1 Controller 2 | 3 | [![Build Status](https://img.shields.io/github/workflow/status/aragon/aragon-network-token/ci:controller?style=flat-square)](https://github.com/aragon/aragon-network-token/actions?query=workflow%3Aci%3Acontroller) 4 | 5 | The final, non-changeable controller of ANTv1. 6 | 7 | Set as ANTv1's controller, it limits ANTv1's token controller related functionality to only allowing a specified address to call `generateTokens()`. 8 | 9 | ## Status 10 | 11 | This package is in _preservation_ mode. 12 | 13 | This token controller was deployed and set to be ANTv1's controller in August 2020. 14 | 15 | ## Development 16 | 17 | ```sh 18 | yarn install 19 | yarn test 20 | ``` 21 | 22 | This will compile the mocks in [`mocks/`](mocks/) and run the [unit tests](test/). 23 | 24 | CI for this package is run through the [`ci_controller` Github action](../../.github/workflows/ci_controller.yml). 25 | 26 | ## E2E tests 27 | 28 | [E2E tests](e2e) are performed through a Ganache-based fork of mainnet state. To run them: 29 | 30 | ```sh 31 | FORK_NODE= yarn test:e2e` 32 | ``` 33 | 34 | See [the `buidler.config.e2e.js` configuration](buidler.config.e2e.js) for more information about the fork's configuration. 35 | 36 | Note that `AragonTokenSale.sol` collides with `ANT.sol` during compilation, so E2E-mocks are [placed separately](e2e/mocks/) from the unit test mocks. 37 | -------------------------------------------------------------------------------- /packages/sale/contracts/ANPlaceholder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "./interface/Controller.sol"; 4 | import "./ANT.sol"; 5 | 6 | /* 7 | Copyright 2017, Jorge Izquierdo (Aragon Association) 8 | */ 9 | /* 10 | 11 | @notice The ANPlaceholder contract will take control over the ANT after the sale 12 | is finalized and before the Aragon Network is deployed. 13 | 14 | The contract allows for ANT transfers and transferFrom and implements the 15 | logic for transfering control of the token to the network when the sale 16 | asks it to do so. 17 | */ 18 | 19 | contract ANPlaceholder is Controller { 20 | address public sale; 21 | ANT public token; 22 | 23 | function ANPlaceholder(address _sale, address _ant) { 24 | sale = _sale; 25 | token = ANT(_ant); 26 | } 27 | 28 | function changeController(address network) public { 29 | if (msg.sender != sale) throw; 30 | token.changeController(network); 31 | suicide(network); 32 | } 33 | 34 | // In between the sale and the network. Default settings for allowing token transfers. 35 | function proxyPayment(address _owner) payable public returns (bool) { 36 | throw; 37 | return false; 38 | } 39 | 40 | function onTransfer(address _from, address _to, uint _amount) public returns (bool) { 41 | return true; 42 | } 43 | 44 | function onApprove(address _owner, address _spender, uint _amount) public returns (bool) { 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/controller/buidler.config.js: -------------------------------------------------------------------------------- 1 | const { usePlugin } = require('@nomiclabs/buidler/config') 2 | 3 | usePlugin("@nomiclabs/buidler-ganache") 4 | usePlugin('@nomiclabs/buidler-truffle5') 5 | 6 | const ACCOUNTS = (process.env.ETH_KEYS ? process.env.ETH_KEYS.split(',') : []) 7 | .map(key => key.trim()) 8 | 9 | module.exports = { 10 | networks: { 11 | // Local development network using ganache. You can set any of the 12 | // Ganache's options. All of them are supported, with the exception 13 | // of accounts. 14 | // https://github.com/trufflesuite/ganache-core#options 15 | ganache: { 16 | url: 'http://localhost:8545', 17 | gasLimit: 6000000000, 18 | defaultBalanceEther: 100 19 | }, 20 | // Mainnet network configured with Aragon node. 21 | mainnet: { 22 | url: 'https://mainnet.eth.aragon.network', 23 | accounts: ACCOUNTS, 24 | }, 25 | // Rinkeby network configured with Aragon node. 26 | rinkeby: { 27 | url: 'https://rinkeby.eth.aragon.network', 28 | accounts: ACCOUNTS, 29 | }, 30 | // Network configured to interact with Frame wallet. Requires 31 | // to have Frame running on your machine. Download it from: 32 | // https://frame.sh 33 | frame: { 34 | httpHeaders: { origin: 'buidler' }, 35 | url: 'http://localhost:1248', 36 | } 37 | }, 38 | solc: { 39 | version: '0.5.17', 40 | optimizer: { 41 | enabled: true, 42 | runs: 10000, 43 | }, 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /packages/sale/test/helpers/MultisigMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import './AragonTokenSaleMock.sol'; 4 | 5 | contract MultisigMock { 6 | function deployAndSetANT(address sale) { 7 | ANT token = new ANT(new MiniMeTokenFactory()); 8 | ANPlaceholder networkPlaceholder = new ANPlaceholder(sale, token); 9 | token.changeController(address(sale)); 10 | 11 | AragonTokenSale s = AragonTokenSale(sale); 12 | token.setCanCreateGrants(sale, true); 13 | s.setANT(token, networkPlaceholder, new SaleWallet(s.aragonDevMultisig(), s.finalBlock(), sale)); 14 | } 15 | 16 | function activateSale(address sale) { 17 | AragonTokenSale(sale).activateSale(); 18 | } 19 | 20 | function emergencyStopSale(address sale) { 21 | AragonTokenSale(sale).emergencyStopSale(); 22 | } 23 | 24 | function restartSale(address sale) { 25 | AragonTokenSale(sale).restartSale(); 26 | } 27 | 28 | function finalizeSale(address sale) { 29 | finalizeSale(sale, AragonTokenSaleMock(sale).mock_hiddenCap()); 30 | } 31 | 32 | function withdrawWallet(address sale) { 33 | SaleWallet(AragonTokenSale(sale).saleWallet()).withdraw(); 34 | } 35 | 36 | function finalizeSale(address sale, uint256 cap) { 37 | AragonTokenSale(sale).finalizeSale(cap, AragonTokenSaleMock(sale).mock_capSecret()); 38 | } 39 | 40 | function deployNetwork(address sale, address network) { 41 | AragonTokenSale(sale).deployNetwork(network); 42 | } 43 | 44 | function () payable {} 45 | } 46 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/ethpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "Manuel Araoz " 4 | ], 5 | "license": "MIT", 6 | "description": "Secure Smart Contract library for Solidity", 7 | "keywords": [ 8 | "solidity", 9 | "ethereum", 10 | "smart", 11 | "contracts", 12 | "security", 13 | "zeppelin" 14 | ], 15 | "links": {}, 16 | "sources": [ 17 | "./contracts/Bounty.sol", 18 | "./contracts/DayLimit.sol", 19 | "./contracts/LimitBalance.sol", 20 | "./contracts/MultisigWallet.sol", 21 | "./contracts/SafeMath.sol", 22 | "./contracts/lifecycle/Killable.sol", 23 | "./contracts/lifecycle/Migrations.sol", 24 | "./contracts/lifecycle/Pausable.sol", 25 | "./contracts/ownership/Claimable.sol", 26 | "./contracts/ownership/Contactable.sol", 27 | "./contracts/ownership/DelayedClaimable.sol", 28 | "./contracts/ownership/Multisig.sol", 29 | "./contracts/ownership/Ownable.sol", 30 | "./contracts/ownership/Shareable.sol", 31 | "./contracts/payment/PullPayment.sol", 32 | "./contracts/token/BasicToken.sol", 33 | "./contracts/token/CrowdsaleToken.sol", 34 | "./contracts/token/ERC20.sol", 35 | "./contracts/token/ERC20Basic.sol", 36 | "./contracts/token/SimpleToken.sol", 37 | "./contracts/token/StandardToken.sol", 38 | "./contracts/token/VestedToken.sol" 39 | ], 40 | "dependencies": {}, 41 | "manifest_version": 1, 42 | "package_name": "zeppelin", 43 | "version": "1.0.4" 44 | } -------------------------------------------------------------------------------- /packages/controller/contracts/interfaces/ITokenController.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | 4 | /** 5 | * @dev The MiniMe token controller contract must implement these functions 6 | * ANT was compiled with solc 0.4.8, so there is no point in marking any of the functions as `view`. 7 | */ 8 | interface ITokenController { 9 | /** 10 | * @notice Called when `_owner` sends ether to the MiniMe Token contract 11 | * @param _owner The address that sent the ether to create tokens 12 | * @return True if the ether is accepted, false if it throws 13 | */ 14 | function proxyPayment(address _owner) external payable returns (bool); 15 | 16 | /** 17 | * @notice Notifies the controller about a token transfer allowing the controller to react if desired 18 | * @param _from The origin of the transfer 19 | * @param _to The destination of the transfer 20 | * @param _amount The amount of the transfer 21 | * @return False if the controller does not authorize the transfer 22 | */ 23 | function onTransfer(address _from, address _to, uint _amount) external returns (bool); 24 | 25 | /** 26 | * @notice Notifies the controller about an approval allowing the controller to react if desired 27 | * @param _owner The address that calls `approve()` 28 | * @param _spender The spender in the `approve()` call 29 | * @param _amount The amount in the `approve()` call 30 | * @return False if the controller does not authorize the approval 31 | */ 32 | function onApprove(address _owner, address _spender, uint _amount) external returns (bool); 33 | } 34 | -------------------------------------------------------------------------------- /packages/sale/contracts/interface/Controller.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | /* 4 | Copyright 2017, Jorge Izquierdo (Aragon Association) 5 | Copyright 2017, Jordi Baylina (Giveth) 6 | 7 | Based on MiniMeToken.sol from https://github.com/Giveth/minime 8 | */ 9 | 10 | /// @dev The token controller contract must implement these functions 11 | contract Controller { 12 | /// @notice Called when `_owner` sends ether to the MiniMe Token contract 13 | /// @param _owner The address that sent the ether to create tokens 14 | /// @return True if the ether is accepted, false if it throws 15 | function proxyPayment(address _owner) payable returns(bool); 16 | 17 | /// @notice Notifies the controller about a token transfer allowing the 18 | /// controller to react if desired 19 | /// @param _from The origin of the transfer 20 | /// @param _to The destination of the transfer 21 | /// @param _amount The amount of the transfer 22 | /// @return False if the controller does not authorize the transfer 23 | function onTransfer(address _from, address _to, uint _amount) returns(bool); 24 | 25 | /// @notice Notifies the controller about an approval allowing the 26 | /// controller to react if desired 27 | /// @param _owner The address that calls `approve()` 28 | /// @param _spender The spender in the `approve()` call 29 | /// @param _amount The amount in the `approve()` call 30 | /// @return False if the controller does not authorize the approval 31 | function onApprove(address _owner, address _spender, uint _amount) 32 | returns(bool); 33 | } 34 | -------------------------------------------------------------------------------- /packages/sale/contracts/SaleWallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // @dev Contract to hold sale raised funds during the sale period. 4 | // Prevents attack in which the Aragon Multisig sends raised ether 5 | // to the sale contract to mint tokens to itself, and getting the 6 | // funds back immediately. 7 | 8 | contract AbstractSale { 9 | function saleFinalized() constant returns (bool); 10 | } 11 | 12 | contract SaleWallet { 13 | // Public variables 14 | address public multisig; 15 | uint public finalBlock; 16 | AbstractSale public tokenSale; 17 | 18 | // @dev Constructor initializes public variables 19 | // @param _multisig The address of the multisig that will receive the funds 20 | // @param _finalBlock Block after which the multisig can request the funds 21 | function SaleWallet(address _multisig, uint _finalBlock, address _tokenSale) { 22 | multisig = _multisig; 23 | finalBlock = _finalBlock; 24 | tokenSale = AbstractSale(_tokenSale); 25 | } 26 | 27 | // @dev Receive all sent funds without any further logic 28 | function () public payable {} 29 | 30 | // @dev Withdraw function sends all the funds to the wallet if conditions are correct 31 | function withdraw() public { 32 | if (msg.sender != multisig) throw; // Only the multisig can request it 33 | if (block.number > finalBlock) return doWithdraw(); // Allow after the final block 34 | if (tokenSale.saleFinalized()) return doWithdraw(); // Allow when sale is finalized 35 | } 36 | 37 | function doWithdraw() internal { 38 | if (!multisig.send(this.balance)) throw; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/Bounty.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import './payment/PullPayment.sol'; 5 | import './lifecycle/Killable.sol'; 6 | 7 | 8 | /* 9 | * Bounty 10 | * 11 | * This bounty will pay out to a researcher if they break invariant logic of the contract. 12 | */ 13 | contract Bounty is PullPayment, Killable { 14 | bool public claimed; 15 | mapping(address => address) public researchers; 16 | 17 | event TargetCreated(address createdAddress); 18 | 19 | function() payable { 20 | if (claimed) { 21 | throw; 22 | } 23 | } 24 | 25 | function createTarget() returns(Target) { 26 | Target target = Target(deployContract()); 27 | researchers[target] = msg.sender; 28 | TargetCreated(target); 29 | return target; 30 | } 31 | 32 | function deployContract() internal returns(address); 33 | 34 | function claim(Target target) { 35 | address researcher = researchers[target]; 36 | if (researcher == 0) { 37 | throw; 38 | } 39 | // Check Target contract invariants 40 | if (target.checkInvariant()) { 41 | throw; 42 | } 43 | asyncSend(researcher, this.balance); 44 | claimed = true; 45 | } 46 | 47 | } 48 | 49 | 50 | /* 51 | * Target 52 | * 53 | * Your main contract should inherit from this class and implement the checkInvariant method. This is a function that should check everything your contract assumes to be true all the time. If this function returns false, it means your contract was broken in some way and is in an inconsistent state. This is what security researchers will try to acomplish when trying to get the bounty. 54 | */ 55 | contract Target { 56 | function checkInvariant() returns(bool); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /packages/v2/contracts/ANTv2MultiMinter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import './ANTv2.sol'; 4 | 5 | 6 | contract ANTv2MultiMinter { 7 | string private constant ERROR_NOT_OWNER = "ANTV2_MM:NOT_OWNER"; 8 | string private constant ERROR_NOT_MINTER = "ANTV2_MM:NOT_MINTER"; 9 | 10 | address public owner; 11 | ANTv2 public ant; 12 | 13 | mapping (address => bool) public canMint; 14 | 15 | event AddedMinter(address indexed minter); 16 | event RemovedMinter(address indexed minter); 17 | event ChangedOwner(address indexed newOwner); 18 | 19 | modifier onlyOwner { 20 | require(msg.sender == owner, ERROR_NOT_OWNER); 21 | _; 22 | } 23 | 24 | modifier onlyMinter { 25 | require(canMint[msg.sender] || msg.sender == owner, ERROR_NOT_MINTER); 26 | _; 27 | } 28 | 29 | constructor(address _owner, ANTv2 _ant) public { 30 | owner = _owner; 31 | ant = _ant; 32 | } 33 | 34 | function mint(address to, uint256 value) external onlyMinter returns (bool) { 35 | return ant.mint(to, value); 36 | } 37 | 38 | function addMinter(address minter) external onlyOwner { 39 | canMint[minter] = true; 40 | 41 | emit AddedMinter(minter); 42 | } 43 | 44 | function removeMinter(address minter) external onlyOwner { 45 | canMint[minter] = false; 46 | 47 | emit RemovedMinter(minter); 48 | } 49 | 50 | function changeMinter(address newMinter) onlyOwner external { 51 | ant.changeMinter(newMinter); 52 | } 53 | 54 | function changeOwner(address newOwner) onlyOwner external { 55 | _changeOwner(newOwner); 56 | } 57 | 58 | function _changeOwner(address newOwner) internal { 59 | owner = newOwner; 60 | 61 | emit ChangedOwner(newOwner); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/v2/contracts/EscrowANTv2Migrator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import './interfaces/IERC20.sol'; 4 | import './ANTv2Migrator.sol'; 5 | 6 | 7 | contract EscrowANTv2Migrator { 8 | string private constant ERROR_NOT_ALLOWED = "ESCROW_MIG:NOT_ALLOWED"; 9 | string private constant ERROR_NO_BALANCE = "ESCROW_MIG:NO_BALANCE"; 10 | string private constant ERROR_MIGRATION_FAILED = "ESCROW_MIG:MIGRATION_FAILED"; 11 | 12 | IERC20 public constant antv1 = IERC20(0x960b236A07cf122663c4303350609A66A7B288C0); 13 | IERC20 public constant antv2 = IERC20(0xa117000000f279D81A1D3cc75430fAA017FA5A2e); 14 | ANTv2Migrator public constant antv2Migrator = ANTv2Migrator(0x078BEbC744B819657e1927bF41aB8C74cBBF912D); 15 | 16 | address public recipient; 17 | address public initiator; 18 | 19 | /** 20 | * @param _recipient Recipient of the migrated ANTv2 tokens 21 | * @param _initiator Account that can initiate the migration of ANTv1 held in this contract 22 | */ 23 | constructor(address _recipient, address _initiator) public { 24 | recipient = _recipient; 25 | initiator = _initiator; 26 | } 27 | 28 | /** 29 | * @notice Migrate ANTv1 balance held by this contract into ANTv2 and transfer to recipient 30 | */ 31 | function migrate() external { 32 | require(msg.sender == initiator || initiator == address(0), ERROR_NOT_ALLOWED); 33 | 34 | uint256 balance = antv1.balanceOf(address(this)); 35 | require(balance > 0, ERROR_NO_BALANCE); 36 | 37 | // Approve migrator, migrate ANTv2 into here, and transfer ANTv2 to recipient 38 | require(antv1.approve(address(antv2Migrator), balance), ERROR_MIGRATION_FAILED); 39 | antv2Migrator.migrate(balance); 40 | require(antv2.transfer(recipient, balance), ERROR_MIGRATION_FAILED); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/StandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import './ERC20.sol'; 5 | import '../SafeMath.sol'; 6 | 7 | 8 | /** 9 | * Standard ERC20 token 10 | * 11 | * https://github.com/ethereum/EIPs/issues/20 12 | * Based on code by FirstBlood: 13 | * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 14 | */ 15 | contract StandardToken is ERC20, SafeMath { 16 | 17 | mapping(address => uint) balances; 18 | mapping (address => mapping (address => uint)) allowed; 19 | 20 | function transfer(address _to, uint _value) returns (bool success) { 21 | balances[msg.sender] = safeSub(balances[msg.sender], _value); 22 | balances[_to] = safeAdd(balances[_to], _value); 23 | Transfer(msg.sender, _to, _value); 24 | return true; 25 | } 26 | 27 | function transferFrom(address _from, address _to, uint _value) returns (bool success) { 28 | var _allowance = allowed[_from][msg.sender]; 29 | 30 | // Check is not needed because safeSub(_allowance, _value) will already throw if this condition is not met 31 | // if (_value > _allowance) throw; 32 | 33 | balances[_to] = safeAdd(balances[_to], _value); 34 | balances[_from] = safeSub(balances[_from], _value); 35 | allowed[_from][msg.sender] = safeSub(_allowance, _value); 36 | Transfer(_from, _to, _value); 37 | return true; 38 | } 39 | 40 | function balanceOf(address _owner) constant returns (uint balance) { 41 | return balances[_owner]; 42 | } 43 | 44 | function approve(address _spender, uint _value) returns (bool success) { 45 | allowed[msg.sender][_spender] = _value; 46 | Approval(msg.sender, _spender, _value); 47 | return true; 48 | } 49 | 50 | function allowance(address _owner, address _spender) constant returns (uint remaining) { 51 | return allowed[_owner][_spender]; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /packages/v2/deploy/deploy-mainnet.js: -------------------------------------------------------------------------------- 1 | 2 | async function deployOnMainnet({ deploy }) { 3 | 4 | const deployer = (await web3.eth.getAccounts())[0]; 5 | 6 | const ANTv2MultiMinter = artifacts.require('ANTv2MultiMinter') 7 | const ANJNoLockMinter = artifacts.require('ANJNoLockMinter') 8 | const ANTv2 = artifacts.require('ANTv2') 9 | const MultiSig = artifacts.require('MultisigWallet'); 10 | 11 | const ANJAddress = "0xcD62b1C403fa761BAadFC74C525ce2B51780b184"; 12 | const ANTv2Address = "0xa117000000f279D81A1D3cc75430fAA017FA5A2e"; 13 | const MultiSigAddress = "0xbEEFbEeF03c7E5a1C29E0Aa675f8E16AEe0A5FAd"; 14 | 15 | const MultiSigInstance = await MultiSig.at(MultiSigAddress); 16 | const ANTv2Instance = await ANTv2.at(ANTv2Address); 17 | 18 | const ANTv2MultiMinterInstance = await ANTv2MultiMinter.new(deployer, ANTv2Address); 19 | const ANJNoLockMinterInstance = await ANJNoLockMinter.new(ANTv2MultiMinterInstance.address, ANTv2Address, ANJAddress); 20 | 21 | // This can be done only by the owner that we passed when creating ANTv2MultiMinter. Can be changed by changeOwner... 22 | await ANTv2MultiMinterInstance.addMinter(ANJNoLockMinterInstance.address); 23 | 24 | // This will not work from the current deployer, This action can be only done by one of the owners from multisig. 25 | // const changeMinterData = ANTv2Instance.contract.methods.changeMinter(ANTv2MultiMinterInstance.address).encodeABI(); 26 | // await MultiSigInstance.submitTransaction(ANTv2Address, 0, changeMinterData); 27 | 28 | await ANTv2MultiMinterInstance.changeOwner(MultiSigAddress); 29 | 30 | console.log(ANTv2MultiMinterInstance.address, " ANTv2 MultiMinter"); 31 | console.log(ANJNoLockMinterInstance.address, " ANJNoLockMinter"); 32 | 33 | } 34 | 35 | 36 | module.exports = { 37 | deployOnMainnet 38 | } -------------------------------------------------------------------------------- /docs/antv1/upgrade-migrating-liquidity.md: -------------------------------------------------------------------------------- 1 | # Migrating on-chain liquidity from ANTv1 to ANTv2 2 | 3 | ANTv1's on-chain liquidity has historically been concentrated across two platforms: 4 | 5 | - [Uniswap](#uniswap) 6 | - [Balancer](#balancer) 7 | 8 | ## Uniswap 9 | 10 | First, withdraw any ANTv1 liquidity you've provided. For example, from the [ANTv1/ETH UniswapV2 pool](https://app.uniswap.org/#/remove/0xfa19de406e8f5b9100e4dd5cad8a503a6d686efe/ETH): 11 | 12 | ![Withdraw liquidity from ANTv1/ETH UniswapV2](./assets/upgrade-remove-uniswap.png) 13 | 14 | Then, use [any of the upgrade paths](upgrade.md) to upgrade the ANTv1 in your wallet to ANTv2. 15 | 16 | Finally, you can now add liquidity to the equivalent (or new) ANTv2 pools on Uniswap. You can use [ANTv2's Uniswap analytics page](https://info.uniswap.org/token/0xa117000000f279d81a1d3cc75430faa017fa5a2e) to find ANTv2's liquidity pools. 17 | 18 | The equivalent ANTv2/ETH pool to the original ANTv1/ETH pool is [`0x9dEF9511fEc79f83AFCBFfe4776B1D817DC775aE`](https://info.uniswap.org/pair/0x9def9511fec79f83afcbffe4776b1d817dc775ae). 19 | 20 | ## Balancer 21 | 22 | First, withdraw any ANTv1 liquidity you've provided. For example, from the [ANTv1/WETH 80:20 pool](https://pools.balancer.exchange/#/pool/0x2cf9106faf2c5c8713035d40df655fb1b9b0f9b9/): 23 | 24 | ![Withdraw liquidity from ANTv1/WETH 80:20](./assets/upgrade-remove-balancer.png) 25 | 26 | Then, use [any of the upgrade paths](./upgrade) to upgrade the ANTv1 in your wallet to ANTv2. 27 | 28 | Finally, you can now add liquidity to the equivalent (or new) ANTv2 pools on Balancer. You can use [Balancer's ANTv2 pools page](https://pools.balancer.exchange/#/?token=0xa117000000f279d81a1d3cc75430faa017fa5a2e&filter=1) to find ANTv2's liquidity pools. 29 | 30 | At the moment there is an equivalent [ANTv2/WETH 80:20 pool](https://pools.balancer.exchange/#/pool/0x73eba399fbbea50852359ff8b8d0e3eba1f22500/) and a new [ANTv2/USDC 80:20 pool](https://pools.balancer.exchange/#/pool/0xde0999ee4e4bea6fecb03bf4ebef2626942ec6f5/). 31 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/DayLimit.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import './ownership/Shareable.sol'; 5 | 6 | 7 | /* 8 | * DayLimit 9 | * 10 | * inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable) 11 | * on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method 12 | * uses is specified in the modifier. 13 | */ 14 | contract DayLimit { 15 | 16 | uint public dailyLimit; 17 | uint public spentToday; 18 | uint public lastDay; 19 | 20 | 21 | function DayLimit(uint _limit) { 22 | dailyLimit = _limit; 23 | lastDay = today(); 24 | } 25 | 26 | // sets the daily limit. doesn't alter the amount already spent today 27 | function _setDailyLimit(uint _newLimit) internal { 28 | dailyLimit = _newLimit; 29 | } 30 | 31 | // resets the amount already spent today. 32 | function _resetSpentToday() internal { 33 | spentToday = 0; 34 | } 35 | 36 | // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and 37 | // returns true. otherwise just returns false. 38 | function underLimit(uint _value) internal returns (bool) { 39 | // reset the spend limit if we're on a different day to last time. 40 | if (today() > lastDay) { 41 | spentToday = 0; 42 | lastDay = today(); 43 | } 44 | // check to see if there's enough left - if so, subtract and return true. 45 | // overflow protection // dailyLimit check 46 | if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) { 47 | spentToday += _value; 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | // determines today's index. 54 | function today() private constant returns (uint) { 55 | return now / 1 days; 56 | } 57 | 58 | 59 | // simple modifier for daily limit. 60 | modifier limitedDaily(uint _value) { 61 | if (!underLimit(_value)) { 62 | throw; 63 | } 64 | _; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /packages/sale/scripts/live.js: -------------------------------------------------------------------------------- 1 | const trackedAddresses = ['0x209c4784AB1E8183Cf58cA33cb740efbF3FC18EF'].map(x => x.toLowerCase()) // addresses we are tracking pending tx to 2 | const balanceAddress = '0x32be343b94f860124dc4fee278fdcbd38c102d88'.toLowerCase() // address we are showing its consolidated balance 3 | const ethPrice = 80 4 | 5 | let txQueue = [] // tx that are pdending that we are showing their pending value 6 | let txValue = {} 7 | 8 | let pendingValue = 0 9 | let currentValue = 0 10 | 11 | const printState = () => { 12 | console.log(`Mined balance: ${currentValue.toFixed(2)} ETH ($${(currentValue * ethPrice).toFixed(2)}) Pending balance: ${pendingValue.toFixed(2)} ETH ($${(pendingValue * ethPrice).toFixed(2)})`) 13 | } 14 | 15 | const newTransaction = (txId) => { 16 | web3.eth.getTransaction(txId, (err, tx) => { 17 | if (err || !tx) return 18 | if (!tx.to) return 19 | 20 | if (trackedAddresses.indexOf(tx.to) == -1) return 21 | if (txQueue.indexOf(txId) > -1) return 22 | 23 | txQueue.push(txId) 24 | 25 | const value = web3.fromWei(tx.value, 'ether').toNumber() 26 | pendingValue += value 27 | txValue[txId] = value 28 | 29 | printState() 30 | }) 31 | } 32 | 33 | const newBlock = (blockHash) => { 34 | web3.eth.getBlock(blockHash, (err, block) => { 35 | if (err || !block) return 36 | block.transactions.forEach(tx => { 37 | if (txQueue.indexOf(tx) > -1) { 38 | txQueue.splice(txQueue.indexOf(tx), 1) 39 | pendingValue -= txValue[tx] 40 | } 41 | }) 42 | web3.eth.getBalance(balanceAddress, (err, balance) => { 43 | if (err) return 44 | currentValue = web3.fromWei(balance, 'ether').toNumber() 45 | console.log('New block') 46 | 47 | printState() 48 | }) 49 | }) 50 | } 51 | 52 | module.exports = function (callback) { 53 | web3.eth.filter('pending').watch((err, txId) => { 54 | if (!err) newTransaction(txId) 55 | }) 56 | 57 | web3.eth.filter('latest').watch((err, blockHash) => { 58 | if (!err) newBlock(blockHash) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /packages/sale/scripts/presale.js: -------------------------------------------------------------------------------- 1 | var AragonTokenSale = artifacts.require("AragonTokenSale"); 2 | var MultiSigWallet = artifacts.require("MultiSigWallet"); 3 | var moment = require('moment') 4 | var async = require('async') 5 | 6 | const contributors = [ 7 | { address: '0x92b7fce758d6d1b52204663e5d8cd4bcd018649e', usd: 10000 }, 8 | { address: '0x00e5cdd4b7b3a78a4277749957553371cb6b2310', usd: 40000 }, 9 | { address: '0x003de03e1b5a4ce31fbcbd6e2a2b574af5ad46a4', usd: 40000 }, 10 | { address: '0xd64A2d50f8858537188A24e0F50Df1681ab07ED7', usd: 10000 }, 11 | { address: '0x53C72d570fB57DB8bfAd81F2eb6b4a3910f49976', usd: 10000 }, 12 | ] 13 | 14 | const ethPrice = 87.91 15 | const jorgeAddress = '0x4838Eab6F43841E0D233Db4CeA47BD64F614f0c5' 16 | const saleAddress = '0xdcab5d235131b02ab93f7e9bf3daed22d464be8a' 17 | const multisigAddress = '0xcafE1A77e84698c83CA8931F54A755176eF75f2C' 18 | 19 | const now = +new Date() / 1000 20 | const month = 30 * 24 * 3600 21 | 22 | const cliff = now + 3 * month 23 | const vesting = now + 6 * month 24 | 25 | const calculateANT = usd => 120 * usd / ethPrice 26 | const formatDate = x => moment(1000 * x).format('MMMM Do YYYY, h:mm:ss a') 27 | 28 | module.exports = function (callback) { 29 | const sale = AragonTokenSale.at(saleAddress) 30 | 31 | async.eachSeries(contributors, ({ address, usd }, cb) => { 32 | const antAmount = calculateANT(usd) 33 | const wantAmount = web3.toWei(antAmount) 34 | 35 | const tx = sale.allocatePresaleTokens.request( 36 | address, 37 | wantAmount, 38 | cliff, 39 | vesting 40 | ) 41 | 42 | const data = tx.params[0].data 43 | 44 | console.log(`Assigning ${address} ${antAmount} ANT (${wantAmount} wANT). Cliff ${formatDate(cliff)} (${cliff}) Vesting ${formatDate(vesting)} (${vesting})\n${data}`) 45 | 46 | return MultiSigWallet 47 | .at(multisigAddress) 48 | .submitTransaction(saleAddress, 0, data, { gas: 3e5, from: jorgeAddress }) 49 | .then(() => { console.log('tx submitted yay'); cb() }) 50 | .catch(e => { console.log('stopping operation'); callback() }) 51 | }, callback) 52 | } 53 | -------------------------------------------------------------------------------- /packages/v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aragon/network-token-v2", 3 | "version": "1.0.0", 4 | "description": "Aragon Network Token v2", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/aragon/aragon-network-token.git" 8 | }, 9 | "author": "Aragon Association ", 10 | "license": "GPL-3.0-or-later", 11 | "files": [ 12 | "/abi", 13 | "/artifacts", 14 | "/contracts" 15 | ], 16 | "devDependencies": { 17 | "@aragon/contract-helpers-test": "^0.1.0", 18 | "@nomiclabs/buidler": "^1.4.3", 19 | "@nomiclabs/buidler-ganache": "^1.3.3", 20 | "@nomiclabs/buidler-truffle5": "^1.3.4", 21 | "@nomiclabs/buidler-web3": "^1.3.4", 22 | "buidler-extract": "^1.0.0", 23 | "buidler-local-networks-config-plugin": "^0.0.1", 24 | "chai": "^4.2.0", 25 | "ethereumjs-util": "^7.0.6", 26 | "inquirer": "^7.3.3", 27 | "rlp": "^2.2.6", 28 | "truffle-flattener": "^1.5.0", 29 | "web3": "^1.2.11", 30 | "web3-eth-abi": "^1.2.11", 31 | "web3-utils": "^1.2.11" 32 | }, 33 | "scripts": { 34 | "abi:extract": "buidler-extract --output abi/ --keys abi", 35 | "compile": "buidler compile --force", 36 | "compile:mocks": "buidler compile --config buidler.config.4.js", 37 | "test": "yarn compile:mocks && buidler test", 38 | "test:e2e:pre-deploy": "yarn compile:mocks && buidler --config buidler.config.e2e-pre.js --network ganache test e2e/test/pre-deploy-mainnet-fork.js", 39 | "test:e2e:post-deploy": "yarn compile:mocks && buidler --config buidler.config.e2e-post.js --network ganache test e2e/test/post-deploy-mainnet-fork.js", 40 | "prepublishOnly": "yarn compile && yarn abi:extract -- --no-compile", 41 | "deploy-ganache": "yarn compile && yarn compile:mocks && buidler --config buidler.config.js --network ganache deploy-testnet", 42 | "deploy-rinkeby": "yarn compile && yarn compile:mocks && buidler --config buidler.config.js --network rinkeby deploy-testnet", 43 | "deploy-mainnet": "yarn compile && yarn compile:mocks && buidler --config buidler.config.js --network mainnet deploy-mainnet" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/v2/buidler/cli.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer') 2 | const { calculateContractAddress } = require('./utils') 3 | 4 | async function deploy({ antv1, owner }) { 5 | if (!web3.utils.isAddress(owner)) { 6 | console.log('Error: --owner must be an Ethereum address') 7 | return 8 | } 9 | 10 | if (!web3.utils.isAddress(antv1)) { 11 | console.log('Error: --antv1 must be an Ethereum address') 12 | return 13 | } 14 | 15 | const ANTv2 = artifacts.require('ANTv2') 16 | const ANTv2Migrator = artifacts.require('ANTv2Migrator') 17 | 18 | const from = (await web3.eth.getAccounts())[0].toLowerCase() 19 | const currentNonce = await web3.eth.getTransactionCount(from) 20 | const antv2Addr = calculateContractAddress(from, currentNonce) 21 | const migratorAddr = calculateContractAddress(from, currentNonce + 1) 22 | 23 | console.log('Deploying ANTv2 and ANTv2Migrator...') 24 | console.log() 25 | 26 | console.log('Deploying from address:', web3.utils.toChecksumAddress(from)) 27 | console.log('Migration owner:', web3.utils.toChecksumAddress(owner)) 28 | console.log() 29 | console.log('ANTv2 address:', web3.utils.toChecksumAddress(antv2Addr)) 30 | console.log('ANTv2Migrator address:', web3.utils.toChecksumAddress(migratorAddr)) 31 | console.log() 32 | 33 | const { confirmed } = await inquirer.prompt([{ 34 | type: 'confirm', 35 | name: 'confirmed', 36 | message: 'Continue?', 37 | }]) 38 | 39 | if (confirmed) { 40 | await ANTv2.new(migratorAddr), 41 | await ANTv2Migrator.new(owner, antv1, antv2Addr) 42 | 43 | const deployedANTv2 = await ANTv2.at(antv2Addr) 44 | const deployedMigrator = await ANTv2Migrator.at(migratorAddr) 45 | console.log() 46 | console.log('ANTv2 minter:', await deployedANTv2.minter()) 47 | console.log('ANTv2Migrator owner:', await deployedMigrator.owner()) 48 | console.log('ANTv2Migrator ANTv1:', await deployedMigrator.antv1()) 49 | console.log('ANTv2Migrator ANTv2:', await deployedMigrator.antv2()) 50 | console.log() 51 | 52 | console.log('Complete!') 53 | } 54 | } 55 | 56 | module.exports = { 57 | deploy 58 | } 59 | -------------------------------------------------------------------------------- /docs/antv1/non-standard.md: -------------------------------------------------------------------------------- 1 | # ANTv1: Non-standard behaviour and gotchas 2 | 3 | Some specific behaviours encoded into ANTv1 may be useful to be aware of, even when not programmatically interacting with the contract. 4 | 5 | ## Gas 6 | 7 | Because ANTv1 includes historical balance records for each address, token transfers are slightly more expensive than some other tokens. 8 | 9 | The exact amount per transfer will differ between hardfork environments and whether the receiver has previously held an ANTv1 balance before, but it is safe to start with a gas limit of 150,000 and optimize lower over time. 10 | 11 | ## Changing non-zero allowances 12 | 13 | The ANTv1 contract reverts when attempting to modify an allowance (calling `ANT.approve()`) that is not zero. [More details of the rationale behind this](https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729). 14 | 15 | To successfully change an allowance in this circumstance, you must first ask the user to reset their allowance for that spender to zero (`ANT.approve(0)`), and then to the amount desired. 16 | 17 | It useful to note that this behaviour is considered non-standard for ERC20s, and may lead to problems if ANTv1 is used by a contract that is not designed to handle this behaviour. 18 | 19 | ## Initially vested tokens 20 | 21 | The ANTv1 contract includes specific functionality for whitelisted addresses to create irrevocable time-based token vests by transferring tokens from their own balance. 22 | 23 | Some token holders (founders, advisors, and early contributors) received tokens around the initial token sale that were granted in such a way to limit their transferability. 24 | 25 | Although all such tokens are now vested and transferable in full, [this blog post](https://aragon.org/blog/a-note-for-exchanges-or-holders-interacting-with-ant-in-an-automated-manner-fe13152c1b36) may still be a useful historical resource for exchanges or other automated market makers interested in integrating with ANTv1. Note that this is not relevant for ANTv2. 26 | 27 | ## Receiving ETH 28 | 29 | Sending ETH directly to the ANTv1 contract will revert. You should never have a reason to want to do this! 30 | -------------------------------------------------------------------------------- /packages/v2/contracts/minters/ANJNoLockMinter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import '../interfaces/ApproveAndCallReceiver.sol'; 4 | import '../interfaces/IERC20.sol'; 5 | import '../ANTv2MultiMinter.sol'; 6 | 7 | 8 | contract ANJNoLockMinter is ApproveAndCallReceiver { 9 | string private constant ERROR_WRONG_TOKEN = "ANJ_NO_LCK_MNTR:WRONG_TOKEN"; 10 | string private constant ERROR_ZERO_AMOUNT = "ANJ_NO_LCK_MNTR:ZERO_AMOUNT"; 11 | string private constant ERROR_TRANSFER_FAILED = "ANJ_NO_LCK_MNTR:TRANSFER_FAIL"; 12 | string private constant ERROR_MINT_FAILED = "ANJ_NO_LCK_MNTR:MINT_FAILED"; 13 | 14 | address private constant BURNED_ADDR = 0x000000000000000000000000000000000000dEaD; 15 | 16 | // Exchange rate is 0.015 ANT per ANJ 17 | uint256 private constant RATE = 15 * 10 ** 15; 18 | uint256 private constant RATE_BASE = 10 ** 18; 19 | 20 | ANTv2MultiMinter public minter; 21 | ANTv2 public ant; 22 | IERC20 public anj; 23 | 24 | constructor(ANTv2MultiMinter _minter, ANTv2 _ant, IERC20 _anj) public { 25 | minter = _minter; 26 | ant = _ant; 27 | anj = _anj; 28 | } 29 | 30 | function migrate(uint256 _amount) external { 31 | _migrate(msg.sender, _amount); 32 | } 33 | 34 | function migrateAll() external { 35 | uint256 amount = anj.balanceOf(msg.sender); 36 | _migrate(msg.sender, amount); 37 | } 38 | 39 | function receiveApproval(address _from, uint256 _amount, address _token, bytes calldata /*_data*/) external { 40 | require(_token == msg.sender && _token == address(anj), ERROR_WRONG_TOKEN); 41 | 42 | uint256 fromBalance = anj.balanceOf(_from); 43 | uint256 migrationAmount = _amount > fromBalance ? fromBalance : _amount; 44 | 45 | _migrate(_from, migrationAmount); 46 | } 47 | 48 | function _migrate(address _from, uint256 _amount) private { 49 | require(_amount > 0, ERROR_ZERO_AMOUNT); 50 | 51 | // Burn ANJ 52 | require(anj.transferFrom(_from, BURNED_ADDR, _amount), ERROR_TRANSFER_FAILED); 53 | // Mint ANT 54 | uint256 antAmount = _amount * RATE / RATE_BASE; 55 | require(minter.mint(_from, antAmount), ERROR_MINT_FAILED); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/v2/buidler.config.js: -------------------------------------------------------------------------------- 1 | const { types, usePlugin } = require('@nomiclabs/buidler/config') 2 | const { deploy } = require('./buidler/cli') 3 | const { deployOnTestnet } = require('./deploy/deploy-testnet') 4 | const { deployOnMainnet } = require('./deploy/deploy-mainnet') 5 | 6 | 7 | usePlugin("@nomiclabs/buidler-ganache") 8 | usePlugin('@nomiclabs/buidler-truffle5') 9 | usePlugin('buidler-local-networks-config-plugin') 10 | 11 | task('deploy', 'Deploy ANTv2 and migrator') 12 | .addParam('owner', "The migrator's owner") 13 | .addOptionalParam('antv1', 'The ANTv1 address to use', '0x960b236A07cf122663c4303350609A66A7B288C0') 14 | .setAction(deploy) 15 | 16 | 17 | task('deploy-testnet', 'This deploys the following contracts(ANJ, ANTv1, ANTv2, ANTv2Migrator, ANJNoLockMinter, ANTv2MultiMinter.sol') 18 | .setAction(deployOnTestnet) 19 | 20 | task('deploy-mainnet', 'This deploys the following contracts(ANJNoLockMinter, ANTv2MultiMinter.sol') 21 | .setAction(deployOnMainnet) 22 | 23 | 24 | const PRIVATE_KEY = ""; 25 | 26 | module.exports = { 27 | networks: { 28 | // Local development network using ganache. You can set any of the 29 | // Ganache's options. All of them are supported, with the exception 30 | // of accounts. 31 | // https://github.com/trufflesuite/ganache-core#options 32 | ganache: { 33 | url: 'http://localhost:7545', 34 | gasLimit: 6000000000, 35 | defaultBalanceEther: 100 36 | }, 37 | // Mainnet network configured with Aragon node. 38 | mainnet: { 39 | url: 'https://mainnet.infura.io/v3/7a03fcb37be7479da06f92c5117afd47', 40 | accounts: [`0x${PRIVATE_KEY}`] 41 | }, 42 | // Rinkeby network configured with Aragon node. 43 | rinkeby: { 44 | url: 'https://rinkeby.infura.io/v3/7a03fcb37be7479da06f92c5117afd47', 45 | accounts: [`0x${PRIVATE_KEY}`], 46 | }, 47 | // Network configured to interact with Frame wallet. Requires 48 | // to have Frame running on your machine. Download it from: 49 | // https://frame.sh 50 | frame: { 51 | httpHeaders: { origin: 'buidler' }, 52 | url: 'http://localhost:1248', 53 | } 54 | }, 55 | solc: { 56 | version: '0.5.17', 57 | optimizer: { 58 | enabled: true, 59 | runs: 999999, 60 | }, 61 | }, 62 | } 63 | -------------------------------------------------------------------------------- /docs/developers/deployments.md: -------------------------------------------------------------------------------- 1 | # Historical deployments 2 | 3 | Log of deployed Ethereum smart contracts related to ANT. 4 | 5 | ### ANTv2 6 | 7 | Address: [`0xa117000000f279D81A1D3cc75430fAA017FA5A2e`](https://etherscan.io/address/0xa117000000f279d81a1d3cc75430faa017fa5a2e) 8 | 9 | The ANTv2 token contract. 10 | 11 | ### ANTv2Migrator 12 | 13 | Address: [`0x078BEbC744B819657e1927bF41aB8C74cBBF912D`](https://etherscan.io/address/0x078BEbC744B819657e1927bF41aB8C74cBBF912D) 14 | 15 | The migration contract facilitating the ANTv1 to ANTv2 upgrade. 16 | 17 | ### ANTv1 18 | 19 | Address: [`0x960b236A07cf122663c4303350609A66A7B288C0`](https://etherscan.io/address/0x960b236A07cf122663c4303350609A66A7B288C0) 20 | 21 | The ANTv1 token contract. Now deprecated for ANTv2. 22 | 23 | ### ANTController 24 | 25 | Address: 26 | [`0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95`](https://etherscan.io/address/0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95) 27 | 28 | ANTv1's final, non-changeable controller. 29 | 30 | ### AragonTokenSale 31 | 32 | Address: [`0x0cEB0D54A7e87Dfa16dDF7656858cF7e29851fD7`](https://etherscan.io/address/0x0ceb0d54a7e87dfa16ddf7656858cf7e29851fd7#code) 33 | 34 | The initial token sale contract. 35 | 36 | ### SaleWallet 37 | 38 | Address: [`0x1B5cdf806E93E60A42EC5812600F8055Ad054865`](https://etherscan.io/address/0x1b5cdf806e93e60a42ec5812600f8055ad054865) 39 | 40 | The intermediary wallet holding proceeds from the token sale until it was finalized. 41 | 42 | ### ANPlaceholder 43 | 44 | Address: [`0xD39902f046B5885D70e9E66594b65f84D4d1c952`](https://etherscan.io/address/0xd39902f046b5885d70e9e66594b65f84d4d1c952) 45 | 46 | The placeholder controller for ANTv1, now replaced for `ANTController` by the [Community Multisig](#community-multisig). 47 | 48 | ### Aragon Association ("Dev") Multisig 49 | 50 | Address: [`0xcafE1A77e84698c83CA8931F54A755176eF75f2C`](https://etherscan.io/address/0xcafe1a77e84698c83ca8931f54a755176ef75f2c) 51 | 52 | Final recipient of the initial token sale proceeds. [More information](https://wiki.aragon.org/association/multisigs/association/). 53 | 54 | ### Community Multisig 55 | 56 | Address: [`0xbEEFbEeF03c7E5a1C29E0Aa675f8E16AEe0A5FAd`](https://etherscan.io/address/0xbeefbeef03c7e5a1c29e0aa675f8e16aee0a5fad) 57 | 58 | Controller of replacing the `ANPlaceholder` contract. [More information](https://wiki.aragon.org/association/multisigs/community/). 59 | -------------------------------------------------------------------------------- /packages/v2/contracts/ANTv2Migrator.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import './interfaces/ApproveAndCallReceiver.sol'; 4 | import './interfaces/IERC20.sol'; 5 | import './ANTv2.sol'; 6 | 7 | 8 | contract ANTv2Migrator is ApproveAndCallReceiver { 9 | string private constant ERROR_NOT_INITATOR = "ANTV2_MIG:NOT_INITIATOR"; 10 | string private constant ERROR_WRONG_TOKEN = "ANTV2_MIG:WRONG_TOKEN"; 11 | string private constant ERROR_ZERO_AMOUNT = "ANTV2_MIG:ZERO_AMOUNT"; 12 | string private constant ERROR_TRANSFER_FAILED = "ANTV2_MIG:TRANSFER_FAIL"; 13 | 14 | address private constant BURNED_ADDR = 0x000000000000000000000000000000000000dEaD; 15 | 16 | address public owner; 17 | IERC20 public antv1; 18 | ANTv2 public antv2; 19 | 20 | constructor(address _owner, IERC20 _antv1, ANTv2 _antv2) public { 21 | owner = _owner; 22 | antv1 = _antv1; 23 | antv2 = _antv2; 24 | } 25 | 26 | function initiate() external { 27 | require(msg.sender == owner, ERROR_NOT_INITATOR); 28 | 29 | // Mint an equal supply of ANTv2 as ANTv1 to this migration contract 30 | uint256 antv1Supply = antv1.totalSupply(); 31 | antv2.mint(address(this), antv1Supply); 32 | 33 | // Transfer ANTv2 minting role to owner 34 | antv2.changeMinter(owner); 35 | } 36 | 37 | function migrate(uint256 _amount) external { 38 | _migrate(msg.sender, _amount); 39 | } 40 | 41 | function migrateAll() external { 42 | uint256 amount = antv1.balanceOf(msg.sender); 43 | _migrate(msg.sender, amount); 44 | } 45 | 46 | function receiveApproval(address _from, uint256 _amount, address _token, bytes calldata /*_data*/) external { 47 | require(_token == msg.sender && _token == address(antv1), ERROR_WRONG_TOKEN); 48 | 49 | uint256 fromBalance = antv1.balanceOf(_from); 50 | uint256 migrationAmount = _amount > fromBalance ? fromBalance : _amount; 51 | 52 | _migrate(_from, migrationAmount); 53 | } 54 | 55 | function _migrate(address _from, uint256 _amount) private { 56 | require(_amount > 0, ERROR_ZERO_AMOUNT); 57 | 58 | // Burn ANTv1 59 | require(antv1.transferFrom(_from, BURNED_ADDR, _amount), ERROR_TRANSFER_FAILED); 60 | // Return ANTv2 61 | require(antv2.transfer(_from, _amount), ERROR_TRANSFER_FAILED); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/sale/migrations/2_deploy_sale.js: -------------------------------------------------------------------------------- 1 | var AragonTokenSale = artifacts.require("AragonTokenSale"); 2 | var MiniMeTokenFactory = artifacts.require("MiniMeTokenFactory"); 3 | var ANPlaceholder = artifacts.require("ANPlaceholder"); 4 | var ANT = artifacts.require("ANT"); 5 | var SaleWallet = artifacts.require("SaleWallet"); 6 | 7 | 8 | module.exports = function(deployer, network, accounts) { 9 | if (network.indexOf('dev') > -1) return // dont deploy on tests 10 | 11 | const aragonMs = '0xcafE1A77e84698c83CA8931F54A755176eF75f2C' 12 | const communityMs = '0xbEEFbEeF03c7E5a1C29E0Aa675f8E16AEe0A5FAd' 13 | 14 | const initialBlock = 3723000 15 | const finalBlock = 3881000 16 | 17 | deployer.deploy(MiniMeTokenFactory); 18 | deployer.deploy(AragonTokenSale, initialBlock, finalBlock, aragonMs, communityMs, 100, 66, 2, '0x663fae46b5b6ef5cc01783af56194e693b71fe529ac917716e460f18f86742b6') 19 | .then(() => { 20 | return MiniMeTokenFactory.deployed() 21 | .then(f => { 22 | factory = f 23 | return AragonTokenSale.deployed() 24 | }) 25 | .then(s => { 26 | sale = s 27 | return ANT.new(factory.address) 28 | }).then(a => { 29 | ant = a 30 | console.log('ANT:', ant.address) 31 | return ant.changeController(sale.address) 32 | }) 33 | .then(() => { 34 | return ant.setCanCreateGrants(sale.address, true) 35 | }) 36 | .then(() => { 37 | return ant.changeVestingWhitelister(aragonMs) 38 | }) 39 | .then(() => { 40 | return ANPlaceholder.new(sale.address, ant.address) 41 | }) 42 | .then(n => { 43 | networkPlaceholder = n 44 | console.log('Placeholder:', networkPlaceholder.address) 45 | return SaleWallet.new(aragonMs, finalBlock, sale.address) 46 | }) 47 | .then(wallet => { 48 | console.log('Wallet:', wallet.address) 49 | if (aragonMs != accounts[0]) { 50 | console.log(sale.setANT.request(ant.address, networkPlaceholder.address, wallet.address)) 51 | } else { 52 | console.log('Test mode, setting ANT') 53 | return sale.setANT(ant.address, networkPlaceholder.address, wallet.address) 54 | } 55 | }) 56 | .then(() => { 57 | if (aragonMs != accounts[0]) return 58 | sale.activateSale() 59 | }) 60 | }) 61 | }; 62 | -------------------------------------------------------------------------------- /docs/token/sale.md: -------------------------------------------------------------------------------- 1 | # Historical token sale information 2 | 3 | ANT was initially created and distributed as a result of a [public token sale](https://aragon.org/blog/announcing-the-aragon-network-token-sale-fe83fe36902c) that took place on [May 17, 2017](https://aragon.org/blog/final-token-sale-recap-1ac64ab7cfcd) - starting at [Ethereum block 3,723,000](https://etherscan.io/block/3723000) to be exact. Less than 100 blocks later, 275,000 ETH worth of ANT were sold. Added together with ANT sold in the pre-sale, ANT granted to the Aragon Institution (now the Aragon Association), and ANT granted to founders and early contributors, the total initial supply of ANT after the token sale is 39,609,523.80952380954 ANT. 4 | 5 | Related information: 6 | 7 | - [Aragon Network token sale terms](https://aragon.org/blog/aragon-network-token-sale-terms-8998f63a3429) 8 | - [Pre-sale transparency report](https://aragon.org/blog/pre-sale-transparency-report-333e310304c) 9 | - [The Aragon token sale: the numbers](https://aragon.org/blog/the-aragon-token-sale-the-numbers-12d03c8b97d3) 10 | 11 | ## Initial supply breakdown 12 | 13 | | Party | Amount (ANT) | % of initial supply | 14 | ----------------------------------------|----------------------------|---------------------| 15 | | Public sale + Pre-sale grant | 27,726,666.66666666668 ANT | 70% | 16 | | Aragon Foundation grant | 5,941,428.571428571431 ANT | 15% | 17 | | Early contributors and founders grant | 5,941,428.571428571431 ANT | 15% | 18 | 19 | **Total supply**: 39,609,523.80952380954 ANT 20 | 21 | _Total supply will be constant only until the Aragon Network is deployed. From then on, ANT holders will participate in Aragon Network governance to decide ANT's issuance policy and inflation rate going forward._ 22 | 23 | ## Historical contracts 24 | 25 | The public token sale was conducted through a single `AragonTokenSale` contract and supported by two multisigs: 26 | 27 | - Sale: [`0x0cEB0D54A7e87Dfa16dDF7656858cF7e29851fD7`](https://etherscan.io/address/0x0ceb0d54a7e87dfa16ddf7656858cf7e29851fd7#code) 28 | - Aragon Association ("Dev") multisig: [`0xcafE1A77e84698c83CA8931F54A755176eF75f2C`](https://etherscan.io/address/0xcafe1a77e84698c83ca8931f54a755176ef75f2c) 29 | - Community multisig: [`0xbEEFbEeF03c7E5a1C29E0Aa675f8E16AEe0A5FAd`](https://etherscan.io/address/0xbeefbeef03c7e5a1c29e0aa675f8e16aee0a5fad) 30 | 31 | For a comprehensive list of Ethereum smart contracts supporting the original token sale, please see [Developers - Historical deployments](../developers/deployments.md). 32 | -------------------------------------------------------------------------------- /docs/developers/integration.md: -------------------------------------------------------------------------------- 1 | # Integrating ANT 2 | 3 | The current version of ANT, ANTv2, is a lightweight, close-to-vanilla ERC-20 token. It is modelled after [Uniswap's liquidity provider (LP) tokens](https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol). 4 | 5 | {% hint style="info" %} 6 | Right off the bat, if your application handles vanilla ERC-20s (or the aforementioned Uniswap LP tokens) without requiring any other functionality, you should be safe to assume ANT can be integrated into your application. 7 | {% endhint %} 8 | 9 | ## Behaviour 10 | 11 | {% hint style="warn" %} 12 | Contracts integrating ANT **cannot** assume ANT will have a fixed total supply. This value can change through the `mint()` and `burn()` functionality. 13 | {% endhint %} 14 | 15 | Outside of the above note, and unless you want to leverage its extended functionality, you should be safe to assume that ANT is a completely vanilla ERC-20 implementation. 16 | 17 | ## "Gasless" functionality 18 | 19 | ANTv2 supports gasless transfers and approvals through [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612)'s `permit()` and [ERC-3009](https://eips.ethereum.org/EIPS/eip-3009)'s `transferWithAuthorization()`. 20 | 21 | ### `permit()` 22 | 23 | Allows for a third party to submit an approval on behalf of a signer. Commonly used in account delegation schemes with smart contracts pulling tokens from users (with `transferFrom()`). 24 | 25 | ### `transferWithAuthorization()` 26 | 27 | Allows for a third party to submit a transfer on behalf of a signer. 28 | 29 | {% hint style="warn" %} 30 | Note that ANTv2 does not protect against any frontrunning on the signature provided to `transferWithAuthorization()`. This method should **only** be used if the receiving contract can safely receive tokens through a vanilla `transfer()` (rather than a `transferFrom()`). 31 | {% endhint %} 32 | 33 | ### `getChainId()` 34 | 35 | Convenience getter for obtaining the current chain ID known by the token contract. 36 | 37 | ### `getDomainSeparator()` 38 | 39 | Convenience getter for obtaining the current [ERC-712 domain separator](https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator) used by the token contract to verify signatures. Useful when constructing and verifying signatures for `permit()` and `transferWithAuthorization()`. 40 | 41 | ## Permissionless functionality 42 | 43 | ### `burn()` 44 | 45 | Allows any holder to burn their tokens to reduce the token's total supply. 46 | 47 | ## Protected functionality 48 | 49 | ### `mint()` 50 | 51 | Allows the designated `minter` to mint new tokens to a specific account. 52 | 53 | Currently set to the Community Multisig. 54 | 55 | ### `changeMinter()` 56 | 57 | Allows the designated `minter` to transfer the minting role to another account. 58 | -------------------------------------------------------------------------------- /docs/antv1/behaviour.md: -------------------------------------------------------------------------------- 1 | # ANTv1: Token behaviour 2 | 3 | **👉 Important information**: 4 | 5 | - ANTv1's token contract address is [`0x960b236A07cf122663c4303350609A66A7B288C0`](https://etherscan.io/token/0x960b236A07cf122663c4303350609A66A7B288C0) 6 | - ANTv1 is a ERC20 token, specifically a [MiniMe token](../developers/minime.md) 7 | - Its current controller is a non-changeable controller, [`0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95`](https://etherscan.io/address/0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95), only allowing a specified address to mint more tokens 8 | - [ANTv1 has been deprecated in favour of ANTv2](upgrade.md), but ANTv1 will forever be a functional ERC-20 token that is transferable and tradable 9 | 10 | ## Introduction 11 | 12 | The original Aragon Network Token ("ANTv1" and sometimes "ANT (old)") is an ERC20-compliant token deployed to Ethereum mainnet. It was launched through the [Aragon Network Token sale](https://aragon.org/blog/announcing-the-aragon-network-token-sale-fe83fe36902c) in May, 2017. 13 | 14 | Although previously configurable, the token will now (and forever) closely mimic a vanilla ERC20 token: 15 | 16 | - Its supply, and holders' balances, are constant 17 | - All transfers are allowed, with no internal fee mechanisms 18 | - No functionality for blacklisting addresses 19 | 20 | In addition to the vanilla behaviour, it implemented a number of features that were intended be used when governing the Aragon Network: 21 | 22 | - Historical token balance records 23 | - Cloning, with balances from a specific block snapshot 24 | - An optional controller (see below) 25 | 26 | And finally, a specific behaviour was added for the initial launch period: 27 | 28 | - Irrevocable time-based vesting from a whitelisted granter, used during the token sale and afterwards to generate the [presale allocations](https://aragon.org/blog/pre-sale-transparency-report-333e310304c) 29 | 30 | ## Controller 31 | 32 | The controller of a MiniMe token holds significant power over the token itself (more information available in the [MiniMe section](minime.md#optional-token-controller)). 33 | 34 | The current controller of ANT is a **final, non-changeable** contract, [`ANTController`](https://etherscan.io/address/0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95#code). This contract limits the token controller related function of ANT to only allowing a specified address to call `generateTokens()`. 35 | 36 | Currently this address is the [Community Multisig](../developers/deployments.md#community-multisig) and will be revoked in the near future. 37 | 38 | ## ANTv2 upgrade 39 | 40 | In light of technical advancements in the Ethereum and decentralized technology ecosystems, [ANTv1 has been deprecated in favour of ANTv2 as the Aragon Network's governance token](upgrade.md). 41 | 42 | Despite being deprecated, ANTv1 is forever "unstoppable": it will continue to be transferable and tradable on Ethereum. 43 | -------------------------------------------------------------------------------- /packages/v2/contracts/minters/ANJLockMinter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import '../interfaces/ApproveAndCallReceiver.sol'; 4 | import '../interfaces/ILockManager.sol'; 5 | import '../interfaces/IGuardiansRegistry.sol'; 6 | import '../interfaces/IERC20.sol'; 7 | 8 | import '../ANTv2MultiMinter.sol'; 9 | 10 | contract ANJLockMinter is ApproveAndCallReceiver, ILockManager { 11 | 12 | string private constant ERROR_WRONG_TOKEN = "ANJ_LCK_MNTR:WRONG_TOKEN"; 13 | string private constant ERROR_ZERO_AMOUNT = "ANJ_LCK_MNTR:ZERO_AMOUNT"; 14 | string private constant ERROR_TRANSFER_FAILED = "ANJ_LCK_MNTR:TRANSFER_FAIL"; 15 | string private constant ERROR_MINT_FAILED = "ANJ_LCK_MNTR:MINT_FAILED"; 16 | 17 | address private constant BURNED_ADDR = 0x000000000000000000000000000000000000dEaD; 18 | 19 | // Timestamp of 5th October, 2021 20 | uint256 public constant LOCKED_UNTIL = 1633392000; 21 | 22 | // Exchange rate is 0.044 ANT per ANJ 23 | uint256 private constant RATE = 44 * 10 ** 15; 24 | uint256 private constant RATE_BASE = 10 ** 18; 25 | 26 | ANTv2MultiMinter public minter; 27 | ANTv2 public ant; 28 | IERC20 public anj; 29 | IGuardiansRegistry public guardiansRegistry; 30 | 31 | constructor(ANTv2MultiMinter _minter, ANTv2 _ant, IERC20 _anj, IGuardiansRegistry _guardiansRegistry) public { 32 | minter = _minter; 33 | ant = _ant; 34 | anj = _anj; 35 | guardiansRegistry = _guardiansRegistry; 36 | } 37 | 38 | function migrate(uint256 _amount) external { 39 | _migrate(msg.sender, _amount); 40 | } 41 | 42 | function migrateAll() external { 43 | uint256 amount = anj.balanceOf(msg.sender); 44 | _migrate(msg.sender, amount); 45 | } 46 | 47 | function receiveApproval(address _from, uint256 _amount, address _token, bytes calldata /*_data*/) external { 48 | require(_token == msg.sender && _token == address(anj), ERROR_WRONG_TOKEN); 49 | 50 | uint256 fromBalance = anj.balanceOf(_from); 51 | uint256 migrationAmount = _amount > fromBalance ? fromBalance : _amount; 52 | 53 | _migrate(_from, migrationAmount); 54 | } 55 | 56 | function canUnlock(address user, uint256 amount) external view returns (bool) { 57 | return block.timestamp >= LOCKED_UNTIL; 58 | } 59 | 60 | function _migrate(address _from, uint256 _amount) private { 61 | require(_amount > 0, ERROR_ZERO_AMOUNT); 62 | 63 | // Burn ANJ 64 | require(anj.transferFrom(_from, BURNED_ADDR, _amount), ERROR_TRANSFER_FAILED); 65 | 66 | // Mint ANT 67 | uint256 antAmount = _amount * RATE / RATE_BASE; 68 | require(minter.mint(address(this), antAmount), ERROR_MINT_FAILED); 69 | 70 | // Approve tokens for GuardianRegistry so it can execute TransferFrom on behalf of the user. 71 | ant.approve(address(guardiansRegistry), antAmount); 72 | 73 | // activate and lock the newly minted tokens. 74 | guardiansRegistry.stakeAndActivate(_from, antAmount); 75 | guardiansRegistry.lockActivation(_from, address(this), antAmount); 76 | } 77 | 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /packages/v2/deploy/deploy-testnet.js: -------------------------------------------------------------------------------- 1 | const { bigExp } = require('@aragon/contract-helpers-test') 2 | 3 | async function deployOnTestnet({ deploy }) { 4 | 5 | const deployer = (await web3.eth.getAccounts())[0]; 6 | 7 | const MiniTokenFactory = artifacts.require('MiniMeTokenFactory'); 8 | const ANJ = artifacts.require('ANJ'); 9 | const ANTv1 = artifacts.require('ANT') 10 | const ANTv2 = artifacts.require('ANTv2') 11 | const ANTv2MultiMinter = artifacts.require('ANTv2MultiMinter') 12 | const ANJNoLockMinter = artifacts.require('ANJNoLockMinter') 13 | const ANTv2Migrator = artifacts.require('ANTv2Migrator'); 14 | 15 | const MiniTokenFactoryInstance = await MiniTokenFactory.new(); 16 | const ANJInstance = await ANJ.new(MiniTokenFactoryInstance.address); 17 | const ANTv1Instance = await ANTv1.new(MiniTokenFactoryInstance.address); 18 | 19 | // deployer parameter will be the only one that can mint tokens or change the minter. 20 | const ANTv2Instance = await ANTv2.new(deployer); 21 | 22 | // deployer parameter will be the one that can add/remove/changeMinter. 23 | const ANTv2MultiMinterInstance = await ANTv2MultiMinter.new(deployer, ANTv2Instance.address); 24 | 25 | const ANJNoLockMinterInstance = await ANJNoLockMinter.new(ANTv2MultiMinterInstance.address, ANTv2Instance.address, ANJInstance.address); 26 | 27 | // deployer parameter will be the only one that can call `initiate` that mints the same amount of ANTv2 as ANTv1. 28 | const ANTv2MigratorInstance = await ANTv2Migrator.new(deployer, ANTv1Instance.address, ANTv2Instance.address); 29 | 30 | await ANTv2Instance.changeMinter(ANTv2MigratorInstance.address); 31 | await ANTv1Instance.generateTokens(deployer, bigExp(1000000, 18)); 32 | 33 | await ANTv2MigratorInstance.initiate(); 34 | 35 | // MultiMinter is the only one that should be able to mint on the ANTv2 instance. 36 | // This specific call will require to make a transaction to multisigwallet since multisigwallet is the owner of ANTv2. 37 | await ANTv2Instance.changeMinter(ANTv2MultiMinterInstance.address); 38 | // One of the minter in multiminters should be ANJNoLockMinter.. For mainnet, there'll be MultiSig too. 39 | await ANTv2MultiMinterInstance.addMinter(ANJNoLockMinterInstance.address); 40 | 41 | // let's generate ANJ so we can test if we can migrate them into ANTv2 42 | await ANJInstance.generateTokens(deployer, bigExp(1000000, 18)); 43 | 44 | // Transfer to someone so he can test on his own address. 45 | await ANTv1Instance.transfer("0x79BF8bBaC596794f1489e94bF4C15Fbf51EA70B5", bigExp(70, 18)); 46 | await ANJInstance.transfer("0x79BF8bBaC596794f1489e94bF4C15Fbf51EA70B5", bigExp(50, 18)); 47 | 48 | console.log(ANJInstance.address); 49 | console.log(ANTv1Instance.address); 50 | console.log(ANTv2Instance.address); 51 | console.log(ANTv2MigratorInstance.address); 52 | console.log(ANTv2MultiMinterInstance.address); 53 | console.log(ANJNoLockMinterInstance.address); 54 | 55 | } 56 | 57 | module.exports = { 58 | deployOnTestnet 59 | } 60 | -------------------------------------------------------------------------------- /packages/sale/test/TestMiniMeCloning.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "truffle/Assert.sol"; 4 | import '../contracts/ANT.sol'; 5 | import '../contracts/MiniMeToken.sol'; 6 | 7 | contract TestMiniMeCloning { 8 | MiniMeTokenFactory factory; 9 | ANT token; 10 | MiniMeToken clone1; 11 | MiniMeToken clone2; 12 | 13 | uint baseBlock; 14 | 15 | function beforeAll() { 16 | factory = new MiniMeTokenFactory(); 17 | token = new ANT(factory); 18 | token.generateTokens(this, 100); 19 | token.changeController(0xbeef); // so it doesn't ask this for callbacks 20 | baseBlock = block.number; 21 | } 22 | 23 | // Be ware that in order to test with block numbers all tests are asumed to occur 24 | // sequencially and may interact with each other. 25 | 26 | function testHasTokens() { 27 | Assert.equal(token.balanceOf(this), 100, 'should have tokens'); 28 | } 29 | 30 | function testCanClone() { 31 | clone1 = MiniMeToken(token.createCloneToken("ANT2", 18, "ANT2", block.number, true)); 32 | clone1.changeController(0xbeef); // so it doesn't ask this for callbacks 33 | Assert.equal(clone1.balanceOf(this), 100, 'should have tokens in cloned token'); 34 | Assert.equal(clone1.balanceOfAt(this, block.number - 1), 100, 'should have correct balance before creating it'); 35 | } 36 | 37 | function testCanTransfer() { 38 | token.transfer(0x1, 10); 39 | 40 | Assert.equal(token.balanceOf(this), 90, 'should have updated balance in token'); 41 | Assert.equal(token.balanceOfAt(this, block.number - 1), 100, 'should have previous balance in token'); 42 | Assert.equal(clone1.balanceOf(this), 100, 'should have previous balance in cloned token'); 43 | } 44 | 45 | function testCanCloneAfterTransfer() { 46 | clone2 = MiniMeToken(token.createCloneToken("ANT2", 18, "ANT2", block.number, true)); 47 | clone2.changeController(0xbeef); // so it doesn't ask this for callbacks 48 | 49 | Assert.equal(clone2.balanceOf(this), 90, 'should have updated balance in token'); 50 | Assert.equal(clone2.balanceOfAt(this, block.number - 2), 100, 'should have previous balance in token'); 51 | 52 | clone1.transfer(0x1, 10); 53 | Assert.equal(clone1.balanceOf(this), 90, 'should have updated balance in token'); 54 | } 55 | 56 | function testRecurringClones() { 57 | MiniMeToken lastClone = clone1; 58 | for (uint i = 0; i < 10; i++) { 59 | lastClone = MiniMeToken(lastClone.createCloneToken("ANTn", 18, "ANTn", block.number, true)); 60 | } 61 | lastClone.changeController(0xbeef); // so it doesn't ask this for callbacks 62 | 63 | Assert.equal(lastClone.balanceOf(this), 90, 'should have updated balance in token'); 64 | Assert.equal(lastClone.balanceOfAt(this, baseBlock), 100, 'should be able to travel back in time'); 65 | } 66 | 67 | function testMultitransfer1() { 68 | Assert.equal(token.balanceOf(this), 90, 'should have correct balance before'); 69 | token.transfer(0x32, 10); 70 | Assert.equal(token.balanceOf(this), 80, 'should have correct balance before'); 71 | } 72 | 73 | function testMultitransfer2() { 74 | token.transfer(0x32, 10); 75 | Assert.equal(token.balanceOf(this), 70, 'should have correct balance before'); 76 | } 77 | 78 | function testMultitransfer3() { 79 | token.transfer(0x32, 10); 80 | Assert.equal(token.balanceOf(this), 60, 'should have correct balance before'); 81 | Assert.equal(token.balanceOfAt(this, baseBlock), 100, 'should be able to travel back in time'); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/MultisigWallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "./ownership/Multisig.sol"; 5 | import "./ownership/Shareable.sol"; 6 | import "./DayLimit.sol"; 7 | 8 | 9 | /* 10 | * MultisigWallet 11 | * usage: 12 | * bytes32 h = Wallet(w).from(oneOwner).execute(to, value, data); 13 | * Wallet(w).from(anotherOwner).confirm(h); 14 | */ 15 | contract MultisigWallet is Multisig, Shareable, DayLimit { 16 | 17 | struct Transaction { 18 | address to; 19 | uint value; 20 | bytes data; 21 | } 22 | 23 | function MultisigWallet(address[] _owners, uint _required, uint _daylimit) 24 | Shareable(_owners, _required) 25 | DayLimit(_daylimit) { } 26 | 27 | // kills the contract sending everything to `_to`. 28 | function kill(address _to) onlymanyowners(sha3(msg.data)) external { 29 | suicide(_to); 30 | } 31 | 32 | // gets called when no other function matches 33 | function() payable { 34 | // just being sent some cash? 35 | if (msg.value > 0) 36 | Deposit(msg.sender, msg.value); 37 | } 38 | 39 | // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit. 40 | // If not, goes into multisig process. We provide a hash on return to allow the sender to provide 41 | // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value 42 | // and _data arguments). They still get the option of using them if they want, anyways. 43 | function execute(address _to, uint _value, bytes _data) external onlyOwner returns (bytes32 _r) { 44 | // first, take the opportunity to check that we're under the daily limit. 45 | if (underLimit(_value)) { 46 | SingleTransact(msg.sender, _value, _to, _data); 47 | // yes - just execute the call. 48 | if (!_to.call.value(_value)(_data)) { 49 | throw; 50 | } 51 | return 0; 52 | } 53 | // determine our operation hash. 54 | _r = sha3(msg.data, block.number); 55 | if (!confirm(_r) && txs[_r].to == 0) { 56 | txs[_r].to = _to; 57 | txs[_r].value = _value; 58 | txs[_r].data = _data; 59 | ConfirmationNeeded(_r, msg.sender, _value, _to, _data); 60 | } 61 | } 62 | 63 | // confirm a transaction through just the hash. we use the previous transactions map, txs, in order 64 | // to determine the body of the transaction from the hash provided. 65 | function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { 66 | if (txs[_h].to != 0) { 67 | if (!txs[_h].to.call.value(txs[_h].value)(txs[_h].data)) { 68 | throw; 69 | } 70 | MultiTransact(msg.sender, _h, txs[_h].value, txs[_h].to, txs[_h].data); 71 | delete txs[_h]; 72 | return true; 73 | } 74 | } 75 | 76 | function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { 77 | _setDailyLimit(_newLimit); 78 | } 79 | 80 | function resetSpentToday() onlymanyowners(sha3(msg.data)) external { 81 | _resetSpentToday(); 82 | } 83 | 84 | 85 | // INTERNAL METHODS 86 | 87 | function clearPending() internal { 88 | uint length = pendingsIndex.length; 89 | for (uint i = 0; i < length; ++i) { 90 | delete txs[pendingsIndex[i]]; 91 | } 92 | super.clearPending(); 93 | } 94 | 95 | 96 | // FIELDS 97 | 98 | // pending transactions we have at present. 99 | mapping (bytes32 => Transaction) txs; 100 | } 101 | -------------------------------------------------------------------------------- /packages/sale/test/StandardToken.js: -------------------------------------------------------------------------------- 1 | // Zeppelin tests for ERC20 StandardToken. 2 | 3 | const assertThrows = require('./helpers/assertThrows'); 4 | var AragonTokenSaleTokenMock = artifacts.require("AragonTokenSaleTokenMock"); 5 | var TokenReceiverMock = artifacts.require("TokenReceiverMock"); 6 | var StandardToken = artifacts.require("MiniMeToken.sol"); 7 | 8 | contract('StandardToken', function(accounts) { 9 | let token; 10 | beforeEach(async () => { 11 | const sale = await AragonTokenSaleTokenMock.new(accounts[0], 70) // 30 extra tokens are 30% extra at sale end 12 | token = StandardToken.at(await sale.token()) 13 | }) 14 | 15 | it("should return the correct totalSupply after construction", async function() { 16 | let totalSupply = await token.totalSupply(); 17 | 18 | assert.equal(totalSupply, 100); 19 | }) 20 | 21 | it("should return the correct allowance amount after approval", async function() { 22 | let approve = await token.approve(accounts[1], 100); 23 | let allowance = await token.allowance(accounts[0], accounts[1]); 24 | 25 | assert.equal(allowance, 100); 26 | }); 27 | 28 | it("should return correct balances after transfer", async function() { 29 | let transfer = await token.transfer(accounts[1], 100); 30 | let balance0 = await token.balanceOf(accounts[0]); 31 | assert.equal(balance0, 0); 32 | 33 | let balance1 = await token.balanceOf(accounts[1]); 34 | assert.equal(balance1, 100); 35 | }); 36 | 37 | it("should throw an error when trying to transfer more than balance", async function() { 38 | try { 39 | let transfer = await token.transfer(accounts[1], 101); 40 | } catch(error) { 41 | return assertThrows(error); 42 | } 43 | assert.fail('should have thrown before'); 44 | }); 45 | 46 | it("should return correct balances after transfering from another account", async function() { 47 | let approve = await token.approve(accounts[1], 100); 48 | let transferFrom = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]}); 49 | 50 | let balance0 = await token.balanceOf(accounts[0]); 51 | assert.equal(balance0, 0); 52 | 53 | let balance1 = await token.balanceOf(accounts[2]); 54 | assert.equal(balance1, 100); 55 | 56 | let balance2 = await token.balanceOf(accounts[1]); 57 | assert.equal(balance2, 0); 58 | }); 59 | 60 | it("should throw an error when trying to transfer more than allowed", async function() { 61 | let approve = await token.approve(accounts[1], 99); 62 | try { 63 | let transfer = await token.transferFrom(accounts[0], accounts[2], 100, {from: accounts[1]}); 64 | } catch (error) { 65 | return assertThrows(error); 66 | } 67 | assert.fail('should have thrown before'); 68 | }); 69 | 70 | it("should approve and call", async function() { 71 | let receiver = await TokenReceiverMock.new() 72 | await token.approveAndCall(receiver.address, 15, '0xbeef') 73 | 74 | assert.equal(await receiver.tokenBalance(), 15, 'Should have transfered tokens under the hood') 75 | assert.equal(await receiver.extraData(), '0xbeef', 'Should have correct extra data') 76 | }) 77 | 78 | it("approve and call should throw when transferring more than balance", async function() { 79 | let receiver = await TokenReceiverMock.new() 80 | try { 81 | let approveAndCall = await token.approveAndCall(receiver.address, 150, '0xbeef') 82 | } catch (error) { 83 | return assertThrows(error); 84 | } 85 | assert.fail('should have thrown before'); 86 | }) 87 | }); 88 | -------------------------------------------------------------------------------- /docs/token/about.md: -------------------------------------------------------------------------------- 1 | # About the Aragon Network Token 2 | 3 | A high-level overview of the Aragon Network Token can be found on its [token page](https://aragon.org/token/ant). 4 | 5 | ## ANT uses 6 | 7 | ### Governance 8 | 9 | The first use of ANT is as a Sybil-resistant mechanism for governance. 10 | 11 | Current community polls backed by ANT can be found on the [Aragon Project's Snapshot Space](https://snapshot.page/#/aragon/). Further uses of ANT are being researched and will be proposed following the launch of the Aragon Network, with ANT embedded as its governance token. 12 | 13 | In the past, ANT was used in the [Aragon Governance Proposal (AGP) process](https://github.com/aragon/AGPs/blob/master/AGPs/AGP-1.md#aragon-network-votes), [first approved by ANT holders](https://aragon.org/blog/final-results-from-the-agp-1-vote) on November 17, 2018. In this construction, one ANT was equal to one vote, allowing anyone to vote on AGPs--even while remaining pseudonymous, by acquiring and holding ANT at the time of a vote. In preparation for the Aragon Network's launch, ANT holders voted to [sunset the AGP process](https://aragon.org/blog/final-results-from-aragon-network-vote-6) on March 13, 2020. 14 | 15 | ### Further issuance 16 | 17 | ANT's total supply is currently fixed at 39,609,523 ANT. This total supply will be constant only until the Aragon Network is deployed. From then on, ANT holders will participate in Aragon Network governance to decide ANT's issuance policy and inflation rate going forward. 18 | 19 | ## ANT asset analysis 20 | 21 | On October 22, 2019, Aragon One Researcher Luke Duncan published an analysis of ANT entitled "[ANT Demand Modeling Framework](https://forum.aragon.org/t/ant-demand-modeling-framework/1389)". Duncan described the document as "a basic framework for thinking about the demand for the Aragon Network Token (ANT) based on its current and potential future utility value". 22 | 23 | On March 6, 2020, the venture capital firm Placeholder published an analysis of ANT entitled "[Aragon (ANT) Economics](https://www.placeholder.vc/blog/2020/3/6/aragon-ant-economics)". Placeholder refers to ANT in their analysis as "a governance asset, with planned future economics to reinforce its store-of-value status". 24 | 25 | ## Obtaining ANT 26 | 27 | _Disclaimer: Nothing on this page or a linked page should be taken as investment advice. Inclusion of a service does not constitute an endorsement of the service by the Aragon Association or any other member of the Aragon project. Please do your own research before trading ANT or using any of the listed services to trade ANT. Keep your funds safe and only do business with people for whom you have a good reason to trust._ 28 | 29 | ANT can be obtained across a variety of exchanges, on-chain and off-chain. Please consult [this page](https://aragon.org/token/exchanges) for some of the supported exchanges. 30 | 31 | ## Storing ANT 32 | 33 | In addition to self-custody tools available in hardware and software form, institutional custodians in the list below have begun offering ANT custody services to qualified clientele. 34 | 35 | _Disclaimer: Inclusion of a service does not constitute an endorsement of the service by the Aragon Association or any other member of the Aragon project. Please do your own research before giving custody of your assets to any entity on this page. Keep your funds safe and only do business with people for whom you have a good reason to trust._ 36 | 37 | - Coinbase Custody ([custody.coinbase.com](https://custody.coinbase.com/)) 38 | - Anchorage ([anchorage.com](https://anchorage.com/)) 39 | - BitGo ([bitgo.com](https://www.bitgo.com/)) 40 | -------------------------------------------------------------------------------- /packages/controller/contracts/ANTController.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import "./interfaces/IMiniMeLike.sol"; 4 | import "./interfaces/ITokenController.sol"; 5 | 6 | 7 | contract ANTController is ITokenController { 8 | string private constant ERROR_NOT_MINTER = "ANTC_SENDER_NOT_MINTER"; 9 | string private constant ERROR_NOT_ANT = "ANTC_SENDER_NOT_ANT"; 10 | 11 | IMiniMeLike public ant; 12 | address public minter; 13 | 14 | event ChangedMinter(address indexed minter); 15 | 16 | /** 17 | * @dev Ensure the msg.sender is the minter 18 | */ 19 | modifier onlyMinter { 20 | require(msg.sender == minter, ERROR_NOT_MINTER); 21 | _; 22 | } 23 | 24 | constructor(IMiniMeLike _ant, address _minter) public { 25 | ant = _ant; 26 | _changeMinter(_minter); 27 | } 28 | 29 | /** 30 | * @notice Generate ANT for a specified address 31 | * @dev Note that failure to generate the requested tokens will result in a revert 32 | * @param _owner Address to receive ANT 33 | * @param _amount Amount to generate 34 | * @return True if the tokens are generated correctly 35 | */ 36 | function generateTokens(address _owner, uint256 _amount) external onlyMinter returns (bool) { 37 | return ant.generateTokens(_owner, _amount); 38 | } 39 | 40 | /** 41 | * @notice Change the permitted minter to another address 42 | * @param _newMinter Address that will be permitted to mint ANT 43 | */ 44 | function changeMinter(address _newMinter) external onlyMinter { 45 | _changeMinter(_newMinter); 46 | } 47 | 48 | // Default ITokenController settings for allowing token transfers. 49 | // ANT was compiled with solc 0.4.8, so there is no point in marking any of these functions as `view`: 50 | // - The original interface does not specify these as `constant` 51 | // - ANT does not use a `staticcall` when calling into these functions 52 | 53 | /** 54 | * @dev Callback function called from MiniMe-like instances when ETH is sent into the token contract 55 | * It allows specifying a custom logic to control if the ETH should be accepted or not 56 | * @return Always false, this controller does not permit the ANT contract to receive ETH transfers 57 | */ 58 | function proxyPayment(address /* _owner */) external payable returns (bool) { 59 | // We only apply this extra check here to ensure `proxyPayment()` cannot be sent ETH from arbitrary addresses 60 | require(msg.sender == address(ant), ERROR_NOT_ANT); 61 | return false; 62 | } 63 | 64 | /** 65 | * @dev Callback function called from MiniMe-like instances when an ERC20 transfer is requested 66 | * It allows specifying a custom logic to control if a transfer should be allowed or not 67 | * @return Always true, this controller allows all transfers 68 | */ 69 | function onTransfer(address /* _from */, address /* _to */, uint /* _amount */) external returns (bool) { 70 | return true; 71 | } 72 | 73 | /** 74 | * @dev Callback function called from MiniMe-like instances when an ERC20 approval is requested 75 | * It allows specifying a custom logic to control if an approval should be allowed or not 76 | * @return Always true, this controller allows all approvals 77 | */ 78 | function onApprove(address /* _owner */, address /* _spender */, uint /* _amount */) external returns (bool) { 79 | return true; 80 | } 81 | 82 | // Internal fns 83 | 84 | function _changeMinter(address _newMinter) internal { 85 | minter = _newMinter; 86 | emit ChangedMinter(_newMinter); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /docs/antv1/minime.md: -------------------------------------------------------------------------------- 1 | # ANTv1: About the MiniMe token 2 | 3 | ANTv1 is an instance of a MiniMe token. 4 | 5 | MiniMe tokens are standard ERC20 tokens with additional functionality. 6 | 7 | ## Advanced functionality 8 | 9 | ### The token is easy to clone! 10 | 11 | Anybody can create a new clone token from a MiniMe token with an initial distribution identical to the original token at a specified block. The address calling the `createCloneToken()` function will become the token controller and the token's default settings can be specified in the function call. 12 | 13 | ``` 14 | function createCloneToken( 15 | string _cloneTokenName, 16 | uint8 _cloneDecimalUnits, 17 | string _cloneTokenSymbol, 18 | uint _snapshotBlock, 19 | bool _isConstant 20 | ) 21 | returns (address); 22 | ``` 23 | 24 | Once the clone token is created, it acts as a completely independent token, with its own unique functionalities. 25 | 26 | ### Balance history is registered and available to be queried 27 | 28 | All MiniMe tokens maintain a history of the balance changes that occur during each block. Two calls are introduced to read the total supply and the balance of any address at any block in the past. 29 | 30 | ``` 31 | function totalSupplyAt(uint _blockNumber) view returns (uint); 32 | 33 | function balanceOfAt(address _holder, uint _blockNumber) view returns (uint); 34 | ``` 35 | 36 | ### Optional token controller 37 | 38 | The controller of the contract can generate/destroy/transfer tokens at its own discretion. The controller can be a regular account, but the intention is for the controller to be another contract that imposes transparent rules on the token's issuance and functionality. The token controller is not required for the MiniMe token to function, if there is no reason to generate/destroy/transfer tokens, the controller can be set to `address(0)` and this functionality will be disabled. 39 | 40 | For example, a TokenCreation contract can be set as the controller of the MiniMe token and at the end of the token creation period, the controller can be transferred to `address(0)`, to guarantee that no new tokens will be created. 41 | 42 | To create and destroy tokens, these two functions are introduced: 43 | 44 | ``` 45 | function generateTokens(address _holder, uint _value) onlyController; 46 | 47 | function destroyTokens(address _holder, uint _value) onlyController; 48 | ``` 49 | 50 | ### The token controller can freeze transfers 51 | 52 | If `transfersEnabled == false`, tokens cannot be transferred directly by holders. However they can still be created, destroyed, and transferred by the controller. The controller can also toggle this flag. 53 | 54 | ``` 55 | // Allows tokens to be transferred if true or frozen if false 56 | function enableTransfers(bool _transfersEnabled) onlyController; 57 | ``` 58 | 59 | ## Applications 60 | 61 | The cloning functionality allows for incredibly powerful functionality, effectively the ability for anyone to give extra features to the token holders without having to migrate to a new contract. Some of the applications that the MiniMe token contract can be used for are: 62 | 63 | 1. Generating a voting token that is burned when you vote. 64 | 2. Generating a discount "coupon" that is redeemed when you use it. 65 | 3. Generating a token for a "spinoff" DAO. 66 | 4. Generating a token that can be used to give explicit support to an action or a campaign, like polling. 67 | 5. Generating a token to enable the token holders to collect daily, monthly or yearly payments. 68 | 6. Generating a token to limit participation in a token sale or similar event to holders of a specific token. 69 | 7. Generating token that allows a central party complete control to transfer/generate/destroy tokens at will. 70 | 8. Lots of other applications including all the applications the standard ERC20 token can be used for. 71 | 72 | All these applications and more are enabled by the MiniMe token contract. The most amazing part being that anyone who wants to add these features has the ability to, in a permissionless yet safe manner without affecting the parent token's intended functionality. 73 | -------------------------------------------------------------------------------- /packages/v2/README.md: -------------------------------------------------------------------------------- 1 | # Aragon Network Token V2 2 | 3 | [![Build Status](https://img.shields.io/github/workflow/status/aragon/aragon-network-token/ci:v2?style=flat-square)](https://github.com/aragon/aragon-network-token/actions?query=workflow%3Aci%3Av2) 4 | 5 | A lightweight token supporting [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612), [ERC-3009](https://eips.ethereum.org/EIPS/eip-3009), token mints, and token burns. Modelled after [UNI-LP](https://github.com/Uniswap/uniswap-v2-core/blob/v1.0.1/contracts/UniswapV2ERC20.sol) with minimal changes. 6 | 7 | ## Status 8 | 9 | This package is in _preservation_ mode. 10 | 11 | ANTv2 was deployed in October 2020. 12 | 13 | ## Development 14 | 15 | ```sh 16 | yarn install 17 | yarn test 18 | ``` 19 | 20 | This will compile the mocks in [`mocks/`](mocks/) and run the [unit tests](test/). 21 | 22 | CI for this package is run through the [`ci_v2` Github action](../../.github/workflows/ci_v2.yml). 23 | 24 | ### Code style 25 | 26 | To limit changes, [`ANTv2.sol`](contracts/ANTv2.sol) carries over the code style of the original `UNI-LP` codebase. 27 | 28 | All other contracts use the typical Aragon code style. 29 | 30 | ## E2E tests 31 | 32 | [E2E tests](e2e) are performed through a Ganache-based fork of mainnet state. To run them: 33 | 34 | ```sh 35 | FORK_NODE= yarn test:e2e:pre-deploy 36 | FORK_NODE= yarn test:e2e:post-deploy 37 | ``` 38 | 39 | ### Pre-deploy 40 | 41 | Tests a theoretical `ANTv2` and `ANTv2Migrator` deployment with mainnet's ANTv1. 42 | 43 | See [the `buidler.config.e2e-pre.js` configuration](buidler.config.e2e.js) for more information about the fork's configuration. 44 | 45 | ### Post-deploy 46 | 47 | Tests a deployed instance of `ANTv2` and `ANTv2Migrator` on mainnet with mainnet's ANTv1. 48 | 49 | See [the `buidler.config.e2e-post.js` configuration](buidler.config.e2e.js) for more information about the fork's configuration. 50 | 51 | 52 | ### Rinkeby Network Addresses 53 | 54 | * **ANTv1:** [0x59f24735b61e6ef7E5A52F5F7bB708D1c0141C5A](https://rinkeby.etherscan.io/address/0x59f24735b61e6ef7e5a52f5f7bb708d1c0141c5a) 55 | * **ANTv2:** [0xf0f8D83CdaB2F9514bEf0319F1b434267be36B5c](https://rinkeby.etherscan.io/address/0xf0f8d83cdab2f9514bef0319f1b434267be36b5c) 56 | * **ANJ:** [0x96286BbCac30Cef8dCB99593d0e28Fabe95F3572](https://rinkeby.etherscan.io/address/0x96286BbCac30Cef8dCB99593d0e28Fabe95F3572) 57 | * **ANTv2Migrator:** [0xF45C53D13bF1F5f757E3331e258589a6f30e662F](https://rinkeby.etherscan.io/address/0xF45C53D13bF1F5f757E3331e258589a6f30e662F) 58 | * **ANJNoLockMinter:** [0xEE25745890bc04bCF926436Ef3Ce490089d89F05](https://rinkeby.etherscan.io/address/0xEE25745890bc04bCF926436Ef3Ce490089d89F05) 59 | * **ANJLockMinter:** [0x8A3475C25452B280a3Af1A8a9B4440e9f70f2f30](https://rinkeby.etherscan.io/address/0x8A3475C25452B280a3Af1A8a9B4440e9f70f2f30) 60 | * **ANTv2MultiMinter**: [0xF64bf861b8A85927FAdd9724E80C2987f82a9259](https://rinkeby.etherscan.io/address/0xF64bf861b8A85927FAdd9724E80C2987f82a9259) 61 | 62 | **Deployed By** : 0x94C34FB5025e054B24398220CBDaBE901bd8eE5e (Giorgi Lagidze) 63 | 64 | ### Mainnet Network Addresses 65 | 66 | * **ANTv1:** [0x960b236A07cf122663c4303350609A66A7B288C0](https://etherscan.io/address/0x960b236a07cf122663c4303350609a66a7b288c0) 67 | * **ANTv2:** [0xa117000000f279D81A1D3cc75430fAA017FA5A2e](https://etherscan.io/address/0xa117000000f279D81A1D3cc75430fAA017FA5A2e) 68 | * **ANJ:** [0xcD62b1C403fa761BAadFC74C525ce2B51780b184](https://etherscan.io/address/0xcD62b1C403fa761BAadFC74C525ce2B51780b184) 69 | * **ANTv2Migrator:** [0x078BEbC744B819657e1927bF41aB8C74cBBF912D](https://etherscan.io/address/0x078BEbC744B819657e1927bF41aB8C74cBBF912D) 70 | * **ANJNoLockMinter:** [0xf6271c8eBF1C1C0384CBBD6Df8b84380623555Ef](https://etherscan.io/address/0xf6271c8eBF1C1C0384CBBD6Df8b84380623555Ef) 71 | * **ANJLockMinter:** [0xAb788183FfAD7D3fAa54Cfc1f9EEf7ff981F4cfD](https://etherscan.io/address/0xab788183ffad7d3faa54cfc1f9eef7ff981f4cfd) 72 | * **ANTv2MultiMinter**: [0xdFf202A4238458bF49A1eeE7Bc5582A45bbe0e38](https://etherscan.io/address/0xdFf202A4238458bF49A1eeE7Bc5582A45bbe0e38) 73 | 74 | **Deployed By** : 0x9416C2191B49bC4E1E614f7d63035b294Ad30D19 (Sam Furter) 75 | 76 | 77 | * ANTv2's minter can only be changed by ANTv2MultiMinter. 78 | * ANJNoLockMinter is added as one of the minters in ANTv2MultiMinter. 79 | 80 | 81 | ## Credits 82 | 83 | - UNI-LP: Uniswap 84 | - SafeMath: DappHub 85 | 86 | 87 | 88 | 1. containerHash => -------------------------------------------------------------------------------- /docs/antv1/upgrade-portal.md: -------------------------------------------------------------------------------- 1 | # Upgrade Portal (user guide) 2 | 3 | The Upgrade Portal is recommended as the easiest way to upgrade an existing ANTv1 balance to ANTv2 if the wallet can be connected to a dapp. 4 | 5 | First, navigate to [upgrade.aragon.org](https://upgrade.aragon.org/#/): 6 | 7 | ![The Upgrade Portal](./assets/upgrade-portal-start.png) 8 | 9 | ### Connect a wallet 10 | 11 | Connect your wallet to see your ANTv1 balance and begin the upgrade process: 12 | 13 | ![Connect your wallet](./assets/upgrade-connect.png) 14 | 15 | If you would like to double check your connected wallet or re-connect to a different one, click the 16 | "Connect to a different wallet" or the Account Module in the top-right corner: 17 | 18 | ![Check your wallet](./assets/upgrade-check-wallet.png) 19 | 20 | Note that some wallets, including Metamask, may require further interaction with their own interface to re-connect with a different wallet. 21 | 22 | Once your wallet has been connected, you should now be able to see your wallet's ANTv1 and ANTv2 balances: 23 | 24 | ![Your account's ANT balances](./assets/upgrade-check-balances.png) 25 | 26 | If you have provided liquidity to any of the popular ANT liquidity pools on-chain, you will also be 27 | able to see those associated balances at this point. 28 | 29 | ### Upgrade to ANTv2 30 | 31 | Go ahead and smash that "Upgrade ANTv1 -> ANTv2" button to get started on upgrading! 32 | 33 | You should now see this interface to select how much you'd like to upgrade: 34 | 35 | ![Select how much ANTv1 to upgrade](./assets/upgrade-amount.png) 36 | 37 | {% hint style="info" %} 38 | Note that the ANTv1 to ANTv2 upgrade is one-way. The ANTv1 you choose to upgrade will be burned and an equivalent amount of ANTv2 will be returned to your wallet. 39 | {% endhint %} 40 | 41 | I'm going to go ahead and migrate all of my ANTv1 to ANTv2: 42 | 43 | ![Migrating all of my ANTv1](./assets/upgrade-amount-all.png) 44 | 45 | Continuing further, you'll now be prompted to sign the transactions that will facilitate the upgrade (may depend on your wallet): 46 | 47 | ![Signing the upgrade transaction](./assets/upgrade-sign.png) 48 | 49 | Note that you may need to sign multiple transactions to reset your ANT approval if you've previously used the interface before or interacted directly with the contract using the currently connected wallet. 50 | 51 | Once you've signed the transaction(s), you should now see the success screen: 52 | 53 | ![Signing success](./assets/upgrade-sign-success.png) 54 | 55 | **🎉 Congratulations, you're done! 🍾** 56 | 57 | If you have other accounts holding ANTv1, you may now go back and repeat this flow. 58 | 59 | ### Troubleshooting 60 | 61 | #### My wallet doesn't detect my ANTv2 balance 62 | 63 | Your wallet may not immediately detect your upgraded ANTv2 balance. In this case, you should be able to find documentation on how to add custom tokens to your wallet's interface. You will want to use `0xa117000000f279D81A1D3cc75430fAA017FA5A2e` as the token address. 64 | 65 | For example, if you use Metamask, you can follow [this guide to add custom tokens](https://metamask.zendesk.com/hc/en-us/articles/360015489031-How-to-View-See-Your-Tokens-in-Metamask). 66 | 67 | #### My wallet isn't connecting 68 | 69 | Your wallet may be experiencing issues. If this issue persists, please reach out to us on [Discord](https://discord.com/invite/aragon). 70 | 71 | {% hint style="info" %} 72 | We are currently investigating issues with connecting to [WalletConnect](https://walletconnect.org/)-enabled wallets. We hope to enable WalletConnect soon. 73 | {% endhint %} 74 | 75 | #### My transactions aren't being propagated 76 | 77 | You may not have specified a high enough gas price or your wallet may be experiencing issues. 78 | 79 | If you're not familiar with the concept of gas on Ethereum, [ethgas.io](https://ethgas.io/) is a great primer. Your wallet will have built-in controls to help adjust the gas price used in your transactions so that they get mined in a timely fashion (for example, see [Metamask's documentation](https://metamask.zendesk.com/hc/en-us/articles/360015488771-How-to-Adjust-Gas-Price-and-Gas-Limit-)). 80 | 81 | #### My transactions are failing 82 | 83 | Please double check that your transaction has specified a high enough gas limit and that your account holds enough ETH. Using 250,000 as the gas limit for any transactions requested by the Upgrade Portal will be sufficient. 84 | 85 | If you're not familiar with the concept of gas on Ethereum, [ethgas.io](https://ethgas.io/) is a great primer. 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 18 | 19 | # Aragon Network Token 20 | 21 |

22 | 23 | 24 | Security 25 | 26 |

27 | 28 | Smart contracts and additional resources for the [Aragon Network Token](https://aragon.org/token/ant). 29 | 30 | > 👉 ANT has [upgraded to ANTv2](https://aragon.org/blog/antv2) 31 | 32 | - ✅ ANT's token address is [`0xa117000000f279D81A1D3cc75430fAA017FA5A2e`](https://etherscan.io/address/0xa117000000f279d81a1d3cc75430faa017fa5a2e) 33 | - 🧮 ANTv1 can be upgraded to the latest token through the [Upgrade Portal](http://upgrade.aragon.org/) 34 | - 🔍 Audits and security details are available in the [security policy](SECURITY.md) 35 | - 🔑 Information about the original token sale are available [in the docs](https://docs.aragon.org/ant/about-the-token/sale) 36 | - 📚 Additional documentation and user guides are available [in the repo](docs/) or as a [Gitbook](https://docs.aragon.org/ant) 37 | 38 | > 👇 ANTv1 is considered deprecated and should be upgraded to ANTv2 39 | 40 | - ANTv1's token address is [`0x960b236A07cf122663c4303350609A66A7B288C0`](https://etherscan.io/token/0x960b236A07cf122663c4303350609A66A7B288C0) 41 | - ANTv1's is controlled by a non-changeable controller, [`0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95`](https://etherscan.io/address/0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95) 42 | 43 | ## Structure 44 | 45 | This repo is divided into multiple independent sub-packages: 46 | 47 | - [`v2`](packages/v2): the latest ANT token contract, [ANTv2](packages/v2/contracts/ANTv2.sol) 48 | - [`sale`](packages/sale): the original v1 token sale contracts, including the [ANTv1 token contract](packages/sale/contracts/ANT.sol) 49 | - [`controller`](packages/controller): the final v1 token controller, severely limiting exposed controller functionality. 50 | 51 | ## Important contracts 52 | 53 | ### ANTv2 54 | 55 | - [ANTv2.sol](packages/v2/contracts/ANTv2.sol): Main contract for the token. Lightweight and supports [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612), [ERC-3009](https://eips.ethereum.org/EIPS/eip-3009), token mints, and token burns. 56 | - [ANTv2Migrator.sol](packages/v2/contracts/ANTv2Migrator.sol): ANTv1 -> ANTv2 token migrator 57 | 58 | Both `ANTv2.sol` and `ANTv2Migrator.sol` contracts were deployed (to [`0xa117000000f279D81A1D3cc75430fAA017FA5A2e`](https://etherscan.io/address/0xa117000000f279d81a1d3cc75430faa017fa5a2e) and [`0x078BEbC744B819657e1927bF41aB8C74cBBF912D`](https://etherscan.io/address/0x078BEbC744B819657e1927bF41aB8C74cBBF912D), respectively) 59 | 60 | ### ANTv1 61 | 62 | - [ANT.sol](packages/sale/contracts/ANT.sol): Main contract for the token. Derives MiniMeIrrevocableVestedToken. 63 | - [MiniMeIrrevocableVestedToken.sol](packages/sale/contracts/MiniMeIrrevocableVestedToken.sol): Adds vesting to MiniMeToken. Derives MiniMeToken. 64 | - [MiniMeToken.sol](packages/sale/contracts/MiniMeToken.sol): MiniMe token implementation 65 | 66 | Only the `ANT.sol` contract was deployed (to [`0x960b236A07cf122663c4303350609A66A7B288C0`](https://etherscan.io/token/0x960b236A07cf122663c4303350609A66A7B288C0)). 67 | 68 | ### Sale 69 | 70 | - [AragonTokenSale.sol](packages/sale/contracts/AragonTokenSale.sol): Implementation of the initial distribution of ANT 71 | - [ANPlaceholder.sol](packages/sale/contracts/ANPlaceholder.sol): Placeholder for the Aragon Network before its deployment 72 | - [SaleWallet.sol](packages/sale/contracts/SaleWallet.sol): Simple contract that will hold all funds until final block of the sale 73 | - [MultisigWallet.sol](packages/sale/contracts/MultisigWallet.sol): Gnosis multisig used for Aragon and community multisigs 74 | 75 | Deployment addresses for each contract can be [found in the documentation](docs/token/sale-resources.md#deployments). 76 | 77 | ### Controller 78 | 79 | - [`ANTController.sol`](packages/controller/contracts/ANTController.sol): The final, non-changeable controller of ANTv1 80 | 81 | Only the `ANTController.sol` contract was deployed (to [`0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95`](https://etherscan.io/address/0x2443d44325bb07861Cd8C9C8Ba1569b6c39D9d95#code)). 82 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/token/VestedToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | import "./StandardToken.sol"; 5 | 6 | 7 | contract VestedToken is StandardToken { 8 | struct TokenGrant { 9 | address granter; 10 | uint256 value; 11 | uint64 cliff; 12 | uint64 vesting; 13 | uint64 start; 14 | } 15 | 16 | mapping (address => TokenGrant[]) public grants; 17 | 18 | modifier canTransfer(address _sender, uint _value) { 19 | if (_value > transferableTokens(_sender, uint64(now))) throw; 20 | _; 21 | } 22 | 23 | function transfer(address _to, uint _value) canTransfer(msg.sender, _value) returns (bool success) { 24 | return super.transfer(_to, _value); 25 | } 26 | 27 | function transferFrom(address _from, address _to, uint _value) canTransfer(_from, _value) returns (bool success) { 28 | return super.transferFrom(_from, _to, _value); 29 | } 30 | 31 | function grantVestedTokens( 32 | address _to, 33 | uint256 _value, 34 | uint64 _start, 35 | uint64 _cliff, 36 | uint64 _vesting) { 37 | 38 | if (_cliff < _start) { 39 | throw; 40 | } 41 | if (_vesting < _start) { 42 | throw; 43 | } 44 | if (_vesting < _cliff) { 45 | throw; 46 | } 47 | 48 | 49 | TokenGrant memory grant = TokenGrant(msg.sender, _value, _cliff, _vesting, _start); 50 | grants[_to].push(grant); 51 | 52 | transfer(_to, _value); 53 | } 54 | 55 | function revokeTokenGrant(address _holder, uint _grantId) { 56 | TokenGrant grant = grants[_holder][_grantId]; 57 | 58 | if (grant.granter != msg.sender) { 59 | throw; 60 | } 61 | uint256 nonVested = nonVestedTokens(grant, uint64(now)); 62 | 63 | // remove grant from array 64 | delete grants[_holder][_grantId]; 65 | grants[_holder][_grantId] = grants[_holder][grants[_holder].length - 1]; 66 | grants[_holder].length -= 1; 67 | 68 | balances[msg.sender] = safeAdd(balances[msg.sender], nonVested); 69 | balances[_holder] = safeSub(balances[_holder], nonVested); 70 | Transfer(_holder, msg.sender, nonVested); 71 | } 72 | 73 | function tokenGrantsCount(address _holder) constant returns (uint index) { 74 | return grants[_holder].length; 75 | } 76 | 77 | function tokenGrant(address _holder, uint _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting) { 78 | TokenGrant grant = grants[_holder][_grantId]; 79 | 80 | granter = grant.granter; 81 | value = grant.value; 82 | start = grant.start; 83 | cliff = grant.cliff; 84 | vesting = grant.vesting; 85 | 86 | vested = vestedTokens(grant, uint64(now)); 87 | } 88 | 89 | function vestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) { 90 | return calculateVestedTokens( 91 | grant.value, 92 | uint256(time), 93 | uint256(grant.start), 94 | uint256(grant.cliff), 95 | uint256(grant.vesting) 96 | ); 97 | } 98 | 99 | function calculateVestedTokens( 100 | uint256 tokens, 101 | uint256 time, 102 | uint256 start, 103 | uint256 cliff, 104 | uint256 vesting) constant returns (uint256 vestedTokens) 105 | { 106 | 107 | if (time < cliff) { 108 | return 0; 109 | } 110 | if (time > vesting) { 111 | return tokens; 112 | } 113 | 114 | uint256 cliffTokens = safeDiv(safeMul(tokens, safeSub(cliff, start)), safeSub(vesting, start)); 115 | vestedTokens = cliffTokens; 116 | 117 | uint256 vestingTokens = safeSub(tokens, cliffTokens); 118 | 119 | vestedTokens = safeAdd(vestedTokens, safeDiv(safeMul(vestingTokens, safeSub(time, cliff)), safeSub(vesting, start))); 120 | } 121 | 122 | function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) { 123 | return safeSub(grant.value, vestedTokens(grant, time)); 124 | } 125 | 126 | function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) { 127 | date = uint64(now); 128 | uint256 grantIndex = grants[holder].length; 129 | for (uint256 i = 0; i < grantIndex; i++) { 130 | date = max64(grants[holder][i].vesting, date); 131 | } 132 | } 133 | 134 | function transferableTokens(address holder, uint64 time) constant public returns (uint256 nonVested) { 135 | uint256 grantIndex = grants[holder].length; 136 | 137 | for (uint256 i = 0; i < grantIndex; i++) { 138 | nonVested = safeAdd(nonVested, nonVestedTokens(grants[holder][i], time)); 139 | } 140 | 141 | return safeSub(balances[holder], nonVested); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /docs/antv1/upgrade-contract-interaction.md: -------------------------------------------------------------------------------- 1 | # Upgrading with contract interactions 2 | 3 | ## TL;DR 4 | 5 | From a wallet holding ANTv1, you can upgrade all of its ANTv1 balance into ANTv2 by sending this `approveAndCall()` transaction: 6 | 7 | ``` 8 | to: 0x960b236A07cf122663c4303350609A66A7B288C0 (this is ANTv1's contract address) 9 | data: 0xcae9ca51000000000000000000000000078bebc744b819657e1927bf41ab8c74cbbf912dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000 10 | ``` 11 | 12 | ## The `ANTv2Migrator` contract 13 | 14 | The migration contract, deployed to [`0x078BEbC744B819657e1927bF41aB8C74cBBF912D`](https://etherscan.io/address/0x078BEbC744B819657e1927bF41aB8C74cBBF912D), facilitates the official upgrade of ANTv1 to ANTv2. 15 | 16 | When a wallet upgrades its ANT balance through an on-chain interaction with this contract, the following happens: 17 | 18 | 1. `ANTv2Migrator` pulls the requested amount of ANTv1 from the wallet's balance into its own balance 19 | 2. `ANTv2Migrator` burns the pulled ANTv1 by transferring it to [`0x000000000000000000000000000000000000dEaD`](https://etherscan.io/address/0x000000000000000000000000000000000000dead) 20 | 3. From its own balance, `ANTv2Migrator` sends the equivalent amount of ANTv2 back to the wallet's balance 21 | 22 | ## Interacting with the `ANTv2Migrator` 23 | 24 | ### `approveAndCall()` 25 | 26 | The simplest approach to completing the migration is by sending a single transaction, calling `ANTv1.approveAndCall()`. `approveAndCall()` allows you to create an allowance to a contract and spend it in the same transaction. 27 | 28 | To create such a call, you may pull out your favourite tool and use the following ABI: 29 | 30 | ``` 31 | [{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}] 32 | ``` 33 | 34 | For the purposes of demonstration, I'll use the [online ABI encoder provided by HashEx](https://abi.hashex.org/#): 35 | 36 | ![Using HashEx's online ABI encoding tool](./assets/upgrade-abi-encode.png) 37 | 38 | For the arguments, use: 39 | 40 | - `_spender`: `0x078BEbC744B819657e1927bF41aB8C74cBBF912D` (this is the migration contract's address) 41 | - `_amount`: amount of ANTv1 to upgrade, denominated in wei (i.e. multiply by 1e18 for "decimal-correct" amounts) 42 | - `_extraData`: can be left blank (not used) 43 | 44 | Next, take the encoded data: 45 | 46 | ![Encoded ABI data](./assets/upgrade-abi-encoded-data.png) 47 | 48 | In the above screenshot, I've specified to only upgrade one (1) ANTv1. 49 | 50 | You may now take this data and use it in a transaction where the `to` address is `0x960b236A07cf122663c4303350609A66A7B288C0` (this is ANTv1's address). 51 | 52 | For example, to use the above data, you would send a transaction with the following data: 53 | 54 | ``` 55 | to: 0x960b236A07cf122663c4303350609A66A7B288C0 56 | data: cae9ca51000000000000000000000000078bebc744b819657e1927bf41ab8c74cbbf912d0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000 57 | ``` 58 | 59 | {% hint style="info" %} 60 | For contract-based smart accounts and multisig wallets, they will typically have functions specifying the `to` and `data` components of a transaction. 61 | 62 | Specify those accordingly based on the above output. 63 | {% endhint %} 64 | 65 | If you'd prefer to use Etherscan, you may submit the above arguments directly to their [ANTv1 contract interface](https://etherscan.io/address/0x960b236a07cf122663c4303350609a66a7b288c0#writeContract): 66 | 67 | ![approveAndCall() with Etherscan](./assets/upgrade-etherscan.png) 68 | 69 | ### Separate token allowances 70 | 71 | If you prefer to not use an `approveAndCall()` transaction, you can upgrade in two steps by first creating an allowance (through `ANTv1.approve()`) for at least the amount desired to the migration contract at `0x078BEbC744B819657e1927bF41aB8C74cBBF912D`, and then calling one of: 72 | 73 | - `migrate(uint256)`: Allows you to directly specify how much you'd like to upgrade 74 | - `migrateAll()`: Assumes you want to upgrade all of your balance (and that you've set an 75 | appropriate allowance) 76 | 77 | On the migration contract. 78 | 79 | ### Using an `EscrowANTv2Migrator` contract 80 | 81 | In some situations, you may prefer to complete the upgrade without ever creating a token approval and only using vanilla `ANTv1.transfer()` interactions. 82 | 83 | To do so, first deploy an [`EscrowANTv2Migrator`](https://github.com/aragon/aragon-network-token/blob/master/packages/v2/contracts/EscrowANTv2Migrator.sol) configured with your desired recipient of the upgraded tokens and the account allowed to initiate the migration. 84 | 85 | Once this contract is deployed, simply transfer ANTv1 into the contract as desired and then call 86 | `migrate()` to complete the upgrade. 87 | 88 | {% hint style="warn" %} 89 | The `EscrowANTv2Migrator` contract does not contain logic to return any ANTv1 tokens transferred to it. All ANTv1 tokens transferred to it will be eventually converted to ANTv2 and returned to the configured recipient. 90 | {% endhint %} 91 | -------------------------------------------------------------------------------- /packages/sale/test/IrrevocableVestedToken.js: -------------------------------------------------------------------------------- 1 | // Slightly modified Zeppelin tests for ERC20 VestedToken. 2 | 3 | const assertThrows = require('./helpers/assertThrows'); 4 | var AragonTokenSaleTokenMock = artifacts.require("./helpers/AragonTokenSaleTokenMock"); 5 | var MiniMeIrrevocableVestedToken = artifacts.require("MiniMeIrrevocableVestedToken"); 6 | const timer = require('./helpers/timer'); 7 | 8 | contract('MiniMeIrrevocableVestedToken', function(accounts) { 9 | let token = null 10 | let now = 0 11 | 12 | const tokenAmount = 50 13 | 14 | const granter = accounts[0] 15 | const receiver = accounts[1] 16 | 17 | beforeEach(async () => { 18 | const sale = await AragonTokenSaleTokenMock.new(granter, 100); 19 | token = MiniMeIrrevocableVestedToken.at(await sale.token()); 20 | now = web3.eth.getBlock(web3.eth.blockNumber).timestamp; 21 | }) 22 | 23 | it('granter can grant tokens without vesting', async () => { 24 | await token.transfer(receiver, tokenAmount, { from: granter }) 25 | 26 | assert.equal(await token.balanceOf(receiver), tokenAmount); 27 | assert.equal(await token.transferableTokens(receiver, now), tokenAmount); 28 | }) 29 | 30 | it('cannot create token grants after losing whitelisting ability', async () => { 31 | await token.changeVestingWhitelister(accounts[2]); 32 | try { 33 | await token.grantVestedTokens(receiver, tokenAmount, now, now + 10000, now + 20000, { from: granter }) 34 | } catch(error) { 35 | return assertThrows(error); 36 | } 37 | }) 38 | 39 | it('can create token grants after being whitelisted', async () => { 40 | await token.changeVestingWhitelister(accounts[2]); 41 | await token.setCanCreateGrants(accounts[0], true, { from: accounts[2] }); 42 | await token.grantVestedTokens(receiver, tokenAmount, now, now + 10000, now + 20000, { from: granter }); 43 | assert.equal(await token.balanceOf(receiver), tokenAmount); 44 | }) 45 | 46 | it('cannot whitelist ppl after losing vesting whitelisting ability', async () => { 47 | await token.changeVestingWhitelister(accounts[2]); 48 | try { 49 | await token.setCanCreateGrants(accounts[2], true, { from: accounts[0] }); 50 | } catch(error) { 51 | return assertThrows(error); 52 | } 53 | }) 54 | 55 | describe('getting a token grant', async () => { 56 | const cliff = 10000 57 | const vesting = 20000 // seconds 58 | 59 | beforeEach(async () => { 60 | await token.grantVestedTokens(receiver, tokenAmount, now, now + cliff, now + vesting, { from: granter }) 61 | }) 62 | 63 | it('tokens are received', async () => { 64 | assert.equal(await token.balanceOf(receiver), tokenAmount); 65 | }) 66 | 67 | it('has 0 transferable tokens before cliff', async () => { 68 | assert.equal(await token.transferableTokens(receiver, now), 0); 69 | }) 70 | 71 | it('all tokens are transferable after vesting', async () => { 72 | assert.equal(await token.transferableTokens(receiver, now + vesting), tokenAmount); 73 | }) 74 | 75 | it('throws when trying to transfer non vested tokens', async () => { 76 | try { 77 | await token.transfer(accounts[7], 1, { from: receiver }) 78 | } catch(error) { 79 | return assertThrows(error); 80 | } 81 | assert.fail('should have thrown before'); 82 | }) 83 | 84 | it('throws when trying to transfer from non vested tokens', async () => { 85 | try { 86 | await token.approve(accounts[7], 1, { from: receiver }) 87 | await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] }) 88 | } catch(error) { 89 | return assertThrows(error); 90 | } 91 | assert.fail('should have thrown before'); 92 | }) 93 | 94 | it('cannot be revoked', async () => { 95 | try { 96 | await token.revokeTokenGrant(receiver, 0, { from: granter }); 97 | } catch(error) { 98 | return assertThrows(error); 99 | } 100 | }) 101 | 102 | it('can transfer all tokens after vesting ends', async () => { 103 | await timer(vesting); 104 | await token.transfer(accounts[7], tokenAmount, { from: receiver }) 105 | assert.equal(await token.balanceOf(accounts[7]), tokenAmount); 106 | }) 107 | 108 | it('can approve and transferFrom all tokens after vesting ends', async () => { 109 | await timer(vesting); 110 | await token.approve(accounts[7], tokenAmount, { from: receiver }) 111 | await token.transferFrom(receiver, accounts[7], tokenAmount, { from: accounts[7] }) 112 | assert.equal(await token.balanceOf(accounts[7]), tokenAmount); 113 | }) 114 | 115 | it('can handle composed vesting schedules', async () => { 116 | await timer(cliff); 117 | await token.transfer(accounts[7], 12, { from: receiver }) 118 | assert.equal(await token.balanceOf(accounts[7]), 12); 119 | 120 | let newNow = web3.eth.getBlock(web3.eth.blockNumber).timestamp 121 | 122 | await token.grantVestedTokens(receiver, tokenAmount, newNow, newNow + cliff, newNow + vesting, { from: granter }) 123 | await token.transfer(accounts[7], 13, { from: receiver }) 124 | assert.equal(await token.balanceOf(accounts[7]), tokenAmount / 2); 125 | 126 | assert.equal(await token.balanceOf(receiver), 3 * tokenAmount / 2) 127 | assert.equal(await token.transferableTokens(receiver, newNow), 0) 128 | await timer(vesting); 129 | await token.transfer(accounts[7], 3 * tokenAmount / 2, { from: receiver }) 130 | assert.equal(await token.balanceOf(accounts[7]), tokenAmount * 2) 131 | }) 132 | }) 133 | }); 134 | -------------------------------------------------------------------------------- /docs/antv1/sale-flow.md: -------------------------------------------------------------------------------- 1 | # Initial token sale flow 2 | 3 | [Example of a successful testnet sale on Kovan](https://kovan.etherscan.io/address/0x506E1db7DA1B3876eAcd2EdDf6ED551A7F2787D0). 4 | 5 | _(Note: gas amounts are calculated for May 2017 and parameters chosen for ANTv1)_ 6 | 7 | ### Instantiation 8 | 9 | #### 1. Deploy sale – 1,425,663 gas 10 | `AragonTokenSale` will be deployed 1 week prior to the beginning of the sale with the following parameters: 11 | 12 | - Initial block: TBC 13 | - Final block: Initial block + 172,800 (4 weeks) 14 | - Aragon Dev Multisig: TBC (2/3 confirms multisig with Jorge, Luis, Security key that can only be reconstructed by Jorge and Luis). 15 | - Community Multisig: TBC (3/5 confirms with Aragon Dev Multisig + 4 trusted members of community) 16 | - Initial price: 100 17 | - Final price: 66 18 | - Price stages: 2 19 | - Cap commitment: sealed commitment for the soft hidden cap. 20 | 21 | #### 2. `sale.setANT()` – 95,427 gas 22 | `setANT()` needs to called from the Aragon Multisig. Its parameters are: 23 | 24 | - ANT: An empty deployed instance of ANTv1. 25 | - ANPlaceholder: An Aragon Network placeholder contract with references to the `AragonTokenSale` and `ANT`. 26 | - Sale wallet: A contract that holds sale funds until final block. 27 | 28 | Aragon Dev will perform `setANT()` immediately after deploying the sale so it is instantiated as soon as possible. 29 | 30 | After `deployANT()` has been called, the sale contract will have two public addresses available: 31 | 32 | - token: The address of the official MiniMe ERC20-compatible ANTv1 token. 33 | - networkPlaceholder: The placeholder for the Aragon Network until its eventual deployment. 34 | 35 | The sale will be the token controller during the sale. After the sale it will be the `networkPlaceholder`. 36 | 37 | Aragon Dev will at this point prove the source code of the contracts in blockchain explorers. 38 | 39 | ### Presale 40 | 41 | The presale is the period between full sale instantiation to the initialBlock of the sale. 42 | 43 | During the presale it is required that the sale is activated, failing to activate the sale during this period, will cause the sale to never start. 44 | 45 | #### 3. `sale.allocatePresaleTokens()` – 209,075 gas 46 | 47 | Aragon Dev will be able to allocate at its own discretion as many presale tokens as needed before the sale is activated. 48 | 49 | Aragon Dev will only issue presale token to presale partners that took part in a private sale done for gathering the funds needed for the sale. 50 | 51 | Presale tokens have cliff and vesting for avoiding market dumps. 52 | 53 | #### 4. `sale.activateSale()` – 2 * 42,862 gas 54 | 55 | Both Aragon Dev and the Community Multisig must call `activateSale()` in order to consider the sale activated. 56 | 57 | When both multisigs have called this function, the sale will be activated and no more presale allocations will be allowed. 58 | 59 | ### Sale 60 | 61 | If the presale is successful in activating the sale, the sale will start on the initial block. 62 | 63 | #### 5. Buy tokens: `sale.fallback()` || `token.fallback()` – 108,242 gas || 118,912 gas 64 | 65 | After the sale is started, sending an ether amount greater than the dust value (1 finney) will result in tokens getting minted and assigned to the sender of the payment. 66 | 67 | All the funds collected will be instantly sent to the Aragon Dev multisig for security. 68 | 69 | Disclaimer: Please do not send directly from exchanges. 70 | 71 | 72 | 73 | #### 6. `sale.revealCap()` 74 | 75 | During the sale, Aragon can reveal the hidden cap and cap secret resulting in the hard cap of the contract being modified by this new cap. 76 | 77 | In case the cap is revealed and the sale contract has already raised an amount higher than the cap, the sale is automatically finalized. 78 | 79 | #### 7. `sale.emergencyStopSale()` – 43,864 gas 80 | 81 | After the sale is activated, Aragon Dev will be able to stop the sale for an emergency. 82 | 83 | #### 8. `sale.restartSale()` – 14,031 gas 84 | 85 | After the sale has been stopped for an emergency and the sale is still ongoing, Aragon Dev will be able to restart it. 86 | 87 | After the sale has ended, it cannot be restarted. The sale can end in a stopped state without problem, but if enabled to restart after ending it could allow Aragon Dev to block the deployment of the network by the Community Multisig. 88 | 89 | ### After sale 90 | 91 | The after sale period is considered from the final block (inclusive) until the sale contract is destroyed. 92 | 93 | #### 9. `sale.finalizeSale()` – 105,348 gas 94 | 95 | This method will mint an additional 3/7 of tokens so at the end of the sale Aragon Dev will own 30% of all the ANTv1 supply. 96 | 97 | In the process of doing so, it will make the ANPlaceholder the controller of the token contract. Which will make the token supply be constant until the Aragon Network is deployed and it implements a new minting policy. 98 | 99 | #### 10. `sale.deployNetwork()` – 22,338 gas 100 | 101 | After the sale is finalized, the Community Multisig will eventually be able to provide the address of an already deployed Aragon Network. 102 | 103 | The ANPlaceholder will transfer its Token Controller power of ANTv1 to the deployed Aragon Network, allowing the Network to mint further tokens if the Network's governance decides so. 104 | 105 | The sale contract is now selfdestructed in favor of the Aragon Network, though it shouldn't have any ether. 106 | 107 | 108 | 109 | ### Token operations 110 | 111 | #### `transfer()` – 95,121 gas 112 | #### `grantVestedTokens()` – 163,094 gas 113 | -------------------------------------------------------------------------------- /packages/sale/installed_contracts/zeppelin/contracts/ownership/Shareable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | 4 | /* 5 | * Shareable 6 | * 7 | * Based on https://github.com/ethereum/dapp-bin/blob/master/wallet/wallet.sol 8 | * 9 | * inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a single, or, crucially, each of a number of, designated owners. 10 | * 11 | * usage: 12 | * use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by some number (specified in constructor) of the set of owners (specified in the constructor) before the interior is executed. 13 | */ 14 | contract Shareable { 15 | // struct for the status of a pending operation. 16 | struct PendingState { 17 | uint yetNeeded; 18 | uint ownersDone; 19 | uint index; 20 | } 21 | 22 | // the number of owners that must confirm the same operation before it is run. 23 | uint public required; 24 | 25 | // list of owners 26 | uint[256] owners; 27 | // index on the list of owners to allow reverse lookup 28 | mapping(uint => uint) ownerIndex; 29 | // the ongoing operations. 30 | mapping(bytes32 => PendingState) pendings; 31 | bytes32[] pendingsIndex; 32 | 33 | 34 | // this contract only has six types of events: it can accept a confirmation, in which case 35 | // we record owner and operation (hash) alongside it. 36 | event Confirmation(address owner, bytes32 operation); 37 | event Revoke(address owner, bytes32 operation); 38 | 39 | 40 | // simple single-sig function modifier. 41 | modifier onlyOwner { 42 | if (!isOwner(msg.sender)) { 43 | throw; 44 | } 45 | _; 46 | } 47 | 48 | // multi-sig function modifier: the operation must have an intrinsic hash in order 49 | // that later attempts can be realised as the same underlying operation and 50 | // thus count as confirmations. 51 | modifier onlymanyowners(bytes32 _operation) { 52 | if (confirmAndCheck(_operation)) { 53 | _; 54 | } 55 | } 56 | 57 | // constructor is given number of sigs required to do protected "onlymanyowners" transactions 58 | // as well as the selection of addresses capable of confirming them. 59 | function Shareable(address[] _owners, uint _required) { 60 | owners[1] = uint(msg.sender); 61 | ownerIndex[uint(msg.sender)] = 1; 62 | for (uint i = 0; i < _owners.length; ++i) { 63 | owners[2 + i] = uint(_owners[i]); 64 | ownerIndex[uint(_owners[i])] = 2 + i; 65 | } 66 | required = _required; 67 | } 68 | 69 | // Revokes a prior confirmation of the given operation 70 | function revoke(bytes32 _operation) external { 71 | uint index = ownerIndex[uint(msg.sender)]; 72 | // make sure they're an owner 73 | if (index == 0) { 74 | return; 75 | } 76 | uint ownerIndexBit = 2**index; 77 | var pending = pendings[_operation]; 78 | if (pending.ownersDone & ownerIndexBit > 0) { 79 | pending.yetNeeded++; 80 | pending.ownersDone -= ownerIndexBit; 81 | Revoke(msg.sender, _operation); 82 | } 83 | } 84 | 85 | // Gets an owner by 0-indexed position (using numOwners as the count) 86 | function getOwner(uint ownerIndex) external constant returns (address) { 87 | return address(owners[ownerIndex + 1]); 88 | } 89 | 90 | function isOwner(address _addr) constant returns (bool) { 91 | return ownerIndex[uint(_addr)] > 0; 92 | } 93 | 94 | function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { 95 | var pending = pendings[_operation]; 96 | uint index = ownerIndex[uint(_owner)]; 97 | 98 | // make sure they're an owner 99 | if (index == 0) { 100 | return false; 101 | } 102 | 103 | // determine the bit to set for this owner. 104 | uint ownerIndexBit = 2**index; 105 | return !(pending.ownersDone & ownerIndexBit == 0); 106 | } 107 | 108 | function confirmAndCheck(bytes32 _operation) internal returns (bool) { 109 | // determine what index the present sender is: 110 | uint index = ownerIndex[uint(msg.sender)]; 111 | // make sure they're an owner 112 | if (index == 0) { 113 | return; 114 | } 115 | 116 | var pending = pendings[_operation]; 117 | // if we're not yet working on this operation, switch over and reset the confirmation status. 118 | if (pending.yetNeeded == 0) { 119 | // reset count of confirmations needed. 120 | pending.yetNeeded = required; 121 | // reset which owners have confirmed (none) - set our bitmap to 0. 122 | pending.ownersDone = 0; 123 | pending.index = pendingsIndex.length++; 124 | pendingsIndex[pending.index] = _operation; 125 | } 126 | // determine the bit to set for this owner. 127 | uint ownerIndexBit = 2**index; 128 | // make sure we (the message sender) haven't confirmed this operation previously. 129 | if (pending.ownersDone & ownerIndexBit == 0) { 130 | Confirmation(msg.sender, _operation); 131 | // ok - check if count is enough to go ahead. 132 | if (pending.yetNeeded <= 1) { 133 | // enough confirmations: reset and run interior. 134 | delete pendingsIndex[pendings[_operation].index]; 135 | delete pendings[_operation]; 136 | return true; 137 | } else { 138 | // not enough: record that this owner in particular confirmed. 139 | pending.yetNeeded--; 140 | pending.ownersDone |= ownerIndexBit; 141 | } 142 | } 143 | } 144 | 145 | function clearPending() internal { 146 | uint length = pendingsIndex.length; 147 | for (uint i = 0; i < length; ++i) { 148 | if (pendingsIndex[i] != 0) { 149 | delete pendings[pendingsIndex[i]]; 150 | } 151 | } 152 | delete pendingsIndex; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /docs/antv1/upgrade.md: -------------------------------------------------------------------------------- 1 | # Upgrading to ANTv2 2 | 3 | **[ANT is upgrading to ANTv2](https://aragon.org/blog/antv2) 🦅 !** 4 | 5 | The original ANT ([`0x960b...88C0`](https://etherscan.io/address/0x960b236A07cf122663c4303350609A66A7B288C0)), now known affectionately as "ANTv1" (or sometimes as "ANT (old)") has been deprecated in favour of ANTv2 ([`0xa117...5A2e`](https://etherscan.io/address/0xa117000000f279d81a1d3cc75430faa017fa5a2e), now "ANT"). Future governance decisions related to the Aragon Network will use ANTv2. 6 | 7 | {% hint style="info" %} 8 | If you directly hold ANT, the easiest method of upgrading your ANTv1 is to use the [Upgrade Portal](https://upgrade.aragon.org). 9 | {% endhint %} 10 | 11 | ## Upgrade paths 12 | 13 | {% hint style="info" %} 14 | The ANTv2 upgrade **does not** end at any date. As long as you hold some ANTv1, you will be able to upgrade it 1:1 to an equivalent ANTv2 balance. 15 | {% endhint %} 16 | 17 | Depending on where your ANTv1 is held, you may have different options for upgrading to ANTv2. 18 | 19 | In general, if you are able to use the [Upgrade Portal](https://upgrade.aragon.org), it is recommended as the easiest method of upgrading. A [user guide](upgrade-portal.md) for the Upgrade Portal is available. 20 | 21 | If you prefer raw contract interactions, need to encode one due to a smart wallet, or want to get into the details, you may find the [contract interaction page](upgrade-contract-interaction.md) helpful. 22 | 23 | ### Wallet 24 | 25 | ANTv1 held directly in a non-contract wallet that can connect to dapps (e.g. web wallets, some mobile wallets, most hardware wallets through browser extensions) should find the Upgrade Portal most convenient. 26 | 27 | If you'd prefer to send a raw transaction or use the Etherscan interface, please see the [contract interaction page](upgrade-contract-interaction.md). 28 | 29 | {% hint style="info" %} 30 | Your wallet may not immediately detect your upgraded ANTv2 balance. In this case, you should be able to find documentation on how to add custom tokens to your wallet's interface. You will want to use `0xa117000000f279D81A1D3cc75430fAA017FA5A2e` as the token address. 31 | 32 | For example, if you use Metamask, you can follow [this guide to add custom tokens](https://metamask.zendesk.com/hc/en-us/articles/360015489031-How-to-View-See-Your-Tokens-in-Metamask). 33 | {% endhint %} 34 | 35 | ### "Smart" / contract wallet 36 | 37 | Depending on the type of contract wallet being used, you may or may not be able to use the Upgrade Portal directly. 38 | 39 | Assuming your contract wallet can send outward transactions (i.e. interact with other Ethereum smart contracts like Uniswap through arbitrary calls), you will be able to upgrade your ANTv1 without transferring it out of this wallet. 40 | 41 | If you cannot connect this wallet to dapps, for example some multisig wallets, you may be forced to send a raw transaction. Please see the [contract interaction page](upgrade-contract-interaction.md). 42 | 43 | ### Aragon DAO 44 | 45 | The available upgrade paths for ANTv1 held in Aragon DAOs depends on whether the DAO has the Agent app installed or not. 46 | 47 | **If the DAO has the Agent app installed**, you will be able to directly upgrade the ANTv1 through an Agent transaction. You can accomplish by connecting [Frame](http://frame.sh/) to your DAO and using the Upgrade Portal, using the in-app console with a raw call, or aragonCLI with a raw call. 48 | 49 | For those latter options, please understand the [contract interaction 50 | page](upgrade-contract-interaction.md), and use the `act` command with the appropriate arguments for an `approveAndCall()` interaction. 51 | 52 | **If the DAO does not have the Agent app installed**, your options for upgrading the ANTv1 become limited to: 53 | 54 | - Either transferring the ANTv1 out to a wallet that can directly interact with the Upgrade Portal or contracts 55 | - Deploying an [`EscrowANTv2Migrator`](https://github.com/aragon/aragon-network-token/blob/master/packages/v2/contracts/EscrowANTv2Migrator.sol) contract parameterized to your DAO's Vault (or other asset-holding app), transferring the ANTv1 to that new contract, and then finally calling the `migrate()` function 56 | 57 | ### On-chain exchange 58 | 59 | No automatic migration of on-chain liquidity is currently provided. If you have provided ANT as on-chain liquidity, please withdraw your ANT and migrate through the other options listed here. 60 | 61 | You may find the information about [migrating on-chain liquidity](upgrade-migrating-liquidity.md) to be useful, especially if you are interested in re-providing ANTv2 as on-chain liquidity. 62 | 63 | ### Off-chain ("centralized") exchange 64 | 65 | Most exchanges and related service providers have been notified of the upgrade and are committed to completing the migration as quickly as possible for their customers. Please check with your service on their upgrade schedule separately. 66 | 67 | A mostly-up-to-date list of supporting exchanges can be found on [this page](https://aragon.org/token/exchanges). 68 | 69 | ## Terminology 70 | 71 | ANT still represents the same underlying asset: a governance token over the Aragon Network. 72 | 73 | ANTv2, as the latest version of the token and the one used for Aragon Network governance, currently carries the official "ANT" moniker. User interfaces across services and dapps are being updated to reflect this. 74 | 75 | You may identify if an interface lists ANTv2 as "ANT" if it uses the new token icon ("white eagle" rather than ANTv1's "blue eagle"), or by double checking its token address to be [`0xa117000000f279D81A1D3cc75430fAA017FA5A2e`](https://etherscan.io/address/0xa117000000f279d81a1d3cc75430faa017fa5a2e) ("a117 saze", after the first four and last four characters). 76 | 77 | ANTv1 should now appear as "ANTv1" or "ANT (old)". Its name should be updated to "Aragon Network Token v1", but associated with the same logo as before ("blue eagle"). 78 | -------------------------------------------------------------------------------- /packages/v2/contracts/ANTv2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import './interfaces/IERC20.sol'; 4 | import './libraries/SafeMath.sol'; 5 | 6 | 7 | // Lightweight token modelled after UNI-LP: https://github.com/Uniswap/uniswap-v2-core/blob/v1.0.1/contracts/UniswapV2ERC20.sol 8 | // Adds: 9 | // - An exposed `mint()` with minting role 10 | // - An exposed `burn()` 11 | // - ERC-3009 (`transferWithAuthorization()`) 12 | contract ANTv2 is IERC20 { 13 | using SafeMath for uint256; 14 | 15 | // bytes32 private constant EIP712DOMAIN_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") 16 | bytes32 private constant EIP712DOMAIN_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; 17 | // bytes32 private constant NAME_HASH = keccak256("Aragon Network Token") 18 | bytes32 private constant NAME_HASH = 0x711a8013284a3c0046af6c0d6ed33e8bbc2c7a11d615cf4fdc8b1ac753bda618; 19 | // bytes32 private constant VERSION_HASH = keccak256("1") 20 | bytes32 private constant VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; 21 | 22 | // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 23 | bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; 24 | // bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 25 | // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"); 26 | bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; 27 | 28 | string public constant name = "Aragon Network Token"; 29 | string public constant symbol = "ANT"; 30 | uint8 public constant decimals = 18; 31 | 32 | address public minter; 33 | uint256 public totalSupply; 34 | mapping (address => uint256) public balanceOf; 35 | mapping (address => mapping (address => uint256)) public allowance; 36 | 37 | // ERC-2612, ERC-3009 state 38 | mapping (address => uint256) public nonces; 39 | mapping (address => mapping (bytes32 => bool)) public authorizationState; 40 | 41 | event Approval(address indexed owner, address indexed spender, uint256 value); 42 | event Transfer(address indexed from, address indexed to, uint256 value); 43 | event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); 44 | event ChangeMinter(address indexed minter); 45 | 46 | modifier onlyMinter { 47 | require(msg.sender == minter, "ANTV2:NOT_MINTER"); 48 | _; 49 | } 50 | 51 | constructor(address initialMinter) public { 52 | _changeMinter(initialMinter); 53 | } 54 | 55 | function _validateSignedData(address signer, bytes32 encodeData, uint8 v, bytes32 r, bytes32 s) internal view { 56 | bytes32 digest = keccak256( 57 | abi.encodePacked( 58 | "\x19\x01", 59 | getDomainSeparator(), 60 | encodeData 61 | ) 62 | ); 63 | address recoveredAddress = ecrecover(digest, v, r, s); 64 | // Explicitly disallow authorizations for address(0) as ecrecover returns address(0) on malformed messages 65 | require(recoveredAddress != address(0) && recoveredAddress == signer, "ANTV2:INVALID_SIGNATURE"); 66 | } 67 | 68 | function _changeMinter(address newMinter) internal { 69 | minter = newMinter; 70 | emit ChangeMinter(newMinter); 71 | } 72 | 73 | function _mint(address to, uint256 value) internal { 74 | totalSupply = totalSupply.add(value); 75 | balanceOf[to] = balanceOf[to].add(value); 76 | emit Transfer(address(0), to, value); 77 | } 78 | 79 | function _burn(address from, uint value) internal { 80 | // Balance is implicitly checked with SafeMath's underflow protection 81 | balanceOf[from] = balanceOf[from].sub(value); 82 | totalSupply = totalSupply.sub(value); 83 | emit Transfer(from, address(0), value); 84 | } 85 | 86 | function _approve(address owner, address spender, uint256 value) private { 87 | allowance[owner][spender] = value; 88 | emit Approval(owner, spender, value); 89 | } 90 | 91 | function _transfer(address from, address to, uint256 value) private { 92 | require(to != address(this) && to != address(0), "ANTV2:RECEIVER_IS_TOKEN_OR_ZERO"); 93 | 94 | // Balance is implicitly checked with SafeMath's underflow protection 95 | balanceOf[from] = balanceOf[from].sub(value); 96 | balanceOf[to] = balanceOf[to].add(value); 97 | emit Transfer(from, to, value); 98 | } 99 | 100 | function getChainId() public pure returns (uint256 chainId) { 101 | assembly { chainId := chainid() } 102 | } 103 | 104 | function getDomainSeparator() public view returns (bytes32) { 105 | return keccak256( 106 | abi.encode( 107 | EIP712DOMAIN_HASH, 108 | NAME_HASH, 109 | VERSION_HASH, 110 | getChainId(), 111 | address(this) 112 | ) 113 | ); 114 | } 115 | 116 | function mint(address to, uint256 value) external onlyMinter returns (bool) { 117 | _mint(to, value); 118 | return true; 119 | } 120 | 121 | function changeMinter(address newMinter) external onlyMinter { 122 | _changeMinter(newMinter); 123 | } 124 | 125 | function burn(uint256 value) external returns (bool) { 126 | _burn(msg.sender, value); 127 | return true; 128 | } 129 | 130 | function approve(address spender, uint256 value) external returns (bool) { 131 | _approve(msg.sender, spender, value); 132 | return true; 133 | } 134 | 135 | function transfer(address to, uint256 value) external returns (bool) { 136 | _transfer(msg.sender, to, value); 137 | return true; 138 | } 139 | 140 | function transferFrom(address from, address to, uint256 value) external returns (bool) { 141 | uint256 fromAllowance = allowance[from][msg.sender]; 142 | if (fromAllowance != uint256(-1)) { 143 | // Allowance is implicitly checked with SafeMath's underflow protection 144 | allowance[from][msg.sender] = fromAllowance.sub(value); 145 | } 146 | _transfer(from, to, value); 147 | return true; 148 | } 149 | 150 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { 151 | require(deadline >= block.timestamp, "ANTV2:AUTH_EXPIRED"); 152 | 153 | bytes32 encodeData = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)); 154 | _validateSignedData(owner, encodeData, v, r, s); 155 | 156 | _approve(owner, spender, value); 157 | } 158 | 159 | function transferWithAuthorization( 160 | address from, 161 | address to, 162 | uint256 value, 163 | uint256 validAfter, 164 | uint256 validBefore, 165 | bytes32 nonce, 166 | uint8 v, 167 | bytes32 r, 168 | bytes32 s 169 | ) 170 | external 171 | { 172 | require(block.timestamp > validAfter, "ANTV2:AUTH_NOT_YET_VALID"); 173 | require(block.timestamp < validBefore, "ANTV2:AUTH_EXPIRED"); 174 | require(!authorizationState[from][nonce], "ANTV2:AUTH_ALREADY_USED"); 175 | 176 | bytes32 encodeData = keccak256(abi.encode(TRANSFER_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce)); 177 | _validateSignedData(from, encodeData, v, r, s); 178 | 179 | authorizationState[from][nonce] = true; 180 | emit AuthorizationUsed(from, nonce); 181 | 182 | _transfer(from, to, value); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /packages/sale/test/TestTokenSaleCap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "truffle/Assert.sol"; 4 | import "zeppelin/token/ERC20.sol"; 5 | import "./helpers/AragonTokenSaleMock.sol"; 6 | import "./helpers/ThrowProxy.sol"; 7 | import "./helpers/MultisigMock.sol"; 8 | import "./helpers/NetworkMock.sol"; 9 | 10 | contract TestTokenSaleCap { 11 | uint public initialBalance = 250 finney; 12 | 13 | address factory; 14 | 15 | ThrowProxy throwProxy; 16 | 17 | function beforeAll() { 18 | factory = address(new MiniMeTokenFactory()); 19 | } 20 | 21 | function beforeEach() { 22 | throwProxy = new ThrowProxy(address(this)); 23 | } 24 | 25 | function testCantFinalizeNotEndedSale() { 26 | TestTokenSaleCap(throwProxy).throwsWhenFinalizingNotEndedSale(); 27 | throwProxy.assertThrows("Should have thrown when sale is ended"); 28 | } 29 | 30 | function throwsWhenFinalizingNotEndedSale() { 31 | MultisigMock ms = new MultisigMock(); 32 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 3, 1, 2); 33 | ms.deployAndSetANT(sale); 34 | ms.activateSale(sale); 35 | sale.setMockedBlockNumber(19); 36 | ms.finalizeSale(sale); 37 | } 38 | 39 | function testCantFinalizeIfNotMultisig() { 40 | TestTokenSaleCap(throwProxy).throwsWhenFinalizingIfNotMultisig(); 41 | throwProxy.assertThrows("Should have thrown if not multisig"); 42 | } 43 | 44 | function throwsWhenFinalizingIfNotMultisig() { 45 | MultisigMock ms = new MultisigMock(); 46 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 3, 1, 2); 47 | ms.deployAndSetANT(sale); 48 | ms.activateSale(sale); 49 | sale.setMockedBlockNumber(30); 50 | sale.finalizeSale(1, 1); 51 | } 52 | 53 | function testCantFinalizeWithIncorrectCap() { 54 | TestTokenSaleCap(throwProxy).throwsWhenFinalizingWithIncorrectCap(); 55 | throwProxy.assertThrows("Should have thrown if incorrect cap"); 56 | } 57 | 58 | function throwsWhenFinalizingWithIncorrectCap() { 59 | MultisigMock ms = new MultisigMock(); 60 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 5, 1, 2); 61 | ms.deployAndSetANT(sale); 62 | ms.activateSale(sale); 63 | sale.setMockedBlockNumber(21); 64 | ms.finalizeSale(sale, 101 finney); // cap is 100 65 | } 66 | 67 | function testCanFinalizeOnCap() { 68 | MultisigMock ms = new MultisigMock(); 69 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 5, 1, 2); 70 | ms.deployAndSetANT(sale); 71 | ms.activateSale(sale); 72 | sale.setMockedBlockNumber(12); 73 | sale.proxyPayment.value(100 finney)(address(this)); 74 | 75 | sale.revealCap(100 finney, sale.mock_capSecret()); 76 | 77 | Assert.isTrue(sale.saleFinalized(), 'Sale should be finished after revealing cap'); 78 | } 79 | 80 | function testFinalizingBeforeCapChangesHardCap() { 81 | MultisigMock ms = new MultisigMock(); 82 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 5, 1, 2); 83 | ms.deployAndSetANT(sale); 84 | ms.activateSale(sale); 85 | sale.setMockedBlockNumber(12); 86 | sale.proxyPayment.value(98 finney)(address(this)); 87 | 88 | sale.revealCap(100 finney, sale.mock_capSecret()); 89 | 90 | Assert.equal(sale.hardCap(), 100 finney, 'Revealing cap should change hard cap'); 91 | Assert.isFalse(sale.saleFinalized(), 'Revealing cap shouldnt end sale if not reached yet'); 92 | } 93 | 94 | function testHardCap() { 95 | TestTokenSaleCap(throwProxy).throwsWhenHittingHardCap(); 96 | throwProxy.assertThrows("Should have thrown when hitting hard cap"); 97 | } 98 | 99 | function throwsWhenHittingHardCap() { 100 | MultisigMock ms = new MultisigMock(); 101 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 5, 1, 2); 102 | ms.deployAndSetANT(sale); 103 | ms.activateSale(sale); 104 | sale.setMockedBlockNumber(12); 105 | sale.setMockedTotalCollected(999999 ether + 950 finney); // hard cap is 1m 106 | sale.proxyPayment.value(60 finney)(address(this)); 107 | } 108 | 109 | function testCanFinalizeEndedSale() { 110 | MultisigMock ms = new MultisigMock(); 111 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 5, 1, 2); 112 | ms.deployAndSetANT(sale); 113 | ms.activateSale(sale); 114 | sale.setMockedBlockNumber(12); 115 | sale.proxyPayment.value(14 finney)(address(this)); 116 | 117 | Assert.equal(ERC20(sale.token()).balanceOf(address(this)), 70 finney, 'Should have correct balance after allocation'); 118 | Assert.equal(ERC20(sale.token()).totalSupply(), 70 finney, 'Should have correct supply before ending sale'); 119 | 120 | sale.setMockedBlockNumber(21); 121 | ms.finalizeSale(sale); 122 | 123 | Assert.equal(ERC20(sale.token()).balanceOf(address(ms)), 30 finney, 'Should have correct balance after ending sale'); 124 | Assert.equal(ERC20(sale.token()).totalSupply(), 100 finney, 'Should have correct supply after ending sale'); 125 | } 126 | 127 | function testTokensAreTransferrableAfterSale() { 128 | MultisigMock ms = new MultisigMock(); 129 | AragonTokenSaleMock sale = new AragonTokenSaleMock(10, 20, address(ms), address(ms), 3, 1, 2); 130 | ms.deployAndSetANT(sale); 131 | ms.activateSale(sale); 132 | 133 | Assert.equal(ANT(sale.token()).controller(), address(sale), "Sale is controller during sale"); 134 | 135 | sale.setMockedBlockNumber(12); 136 | sale.proxyPayment.value(15 finney)(address(this)); 137 | sale.setMockedBlockNumber(22); 138 | ms.finalizeSale(sale); 139 | 140 | Assert.equal(ANT(sale.token()).controller(), sale.networkPlaceholder(), "Network placeholder is controller after sale"); 141 | 142 | ERC20(sale.token()).transfer(0x1, 10 finney); 143 | Assert.equal(ERC20(sale.token()).balanceOf(0x1), 10 finney, 'Should have correct balance after receiving tokens'); 144 | } 145 | 146 | /********************************************************************************************* 147 | * * 148 | * These tests are not passing on a Byzantium chain and Truffle v3.2.1. * 149 | * Efforts were attempted to rollback to a pre-Byzantium test chain but these proved futile. * 150 | * Given that the sale was successfully conducted in 2017-05, we are safe to assume that the * 151 | * intended functionality worked :). * 152 | * * 153 | ********************************************************************************************* 154 | 155 | function testFundsAreTransferrableAfterSale() { 156 | MultisigMock ms = new MultisigMock(); 157 | AragonTokenSaleMock sale = new AragonTokenSaleMock(1000000, 60000000, address(ms), address(ms), 3, 1, 2); 158 | ms.deployAndSetANT(sale); 159 | ms.activateSale(sale); 160 | 161 | Assert.equal(ANT(sale.token()).controller(), address(sale), "Sale is controller during sale"); 162 | 163 | sale.setMockedBlockNumber(1000000); 164 | sale.proxyPayment.value(15 finney)(address(this)); 165 | sale.setMockedBlockNumber(60000000); 166 | ms.finalizeSale(sale); 167 | 168 | ms.withdrawWallet(sale); 169 | Assert.equal(ms.balance, 15 finney, "Funds are collected after sale"); 170 | Assert.equal(sale.saleWallet().balance, 0 finney, "Funds shouldnt have been transfered"); 171 | } 172 | 173 | function testFundsAreLockedDuringSale() { 174 | MultisigMock ms = new MultisigMock(); 175 | AragonTokenSaleMock sale = new AragonTokenSaleMock(1000000, 60000000, address(ms), address(ms), 3, 1, 2); 176 | ms.deployAndSetANT(sale); 177 | ms.activateSale(sale); 178 | 179 | Assert.equal(ANT(sale.token()).controller(), address(sale), "Sale is controller during sale"); 180 | 181 | sale.setMockedBlockNumber(1000000); 182 | sale.proxyPayment.value(15 finney)(address(this)); 183 | 184 | ms.withdrawWallet(sale); 185 | Assert.equal(ms.balance, 0 finney, "Funds shouldnt have been transfered"); 186 | Assert.equal(sale.saleWallet().balance, 15 finney, "Funds shouldnt have been transfered"); 187 | } 188 | */ 189 | } 190 | -------------------------------------------------------------------------------- /packages/v2/e2e/test/pre-deploy-mainnet-fork.js: -------------------------------------------------------------------------------- 1 | const { bigExp, bn, getEventArgument, MAX_UINT256 } = require('@aragon/contract-helpers-test') 2 | const { assertBn, assertEvent, assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 3 | const BIG_HOLDERS = require('../holders') 4 | const MULTISIG_SIGNERS = require('../signers') 5 | 6 | const ANTv1 = artifacts.require('ANT') 7 | const MultisigWallet = artifacts.require('MultisigWallet') 8 | 9 | const ANTv2 = artifacts.require('ANTv2') 10 | const ANTv2Migrator = artifacts.require('ANTv2Migrator') 11 | 12 | const ANTV1_ADDRESS = '0x960b236A07cf122663c4303350609A66A7B288C0' 13 | const AA_MULTISIG_ADDRESS = '0xcafe1a77e84698c83ca8931f54a755176ef75f2c' 14 | const COMMUNITY_MULTISIG_ADDRESS = '0xbeefbeef03c7e5a1c29e0aa675f8e16aee0a5fad' 15 | 16 | const CHAIN_ID = 1 17 | 18 | function tokenAmount(amount) { 19 | return bigExp(amount, 18) 20 | } 21 | 22 | // Note that these tests are meant to be run serially, and each later test is expected to rely 23 | // on state changes from an earlier test! 24 | // Also note that ANT was compiled on 0.4.8, and therefore throws invalid JUMPs rather than reverts 25 | contract('ANTv2 migration (pre-deploy mainnet)', ([_, interimOwner, seed]) => { 26 | let antv1, cMultisig 27 | let antv2, migrator 28 | 29 | before('get spoofed accounts flush with cash', async () => { 30 | for (const holder of BIG_HOLDERS) { 31 | await web3.eth.sendTransaction({ from: seed, to: holder, value: tokenAmount(5) }) 32 | } 33 | 34 | for (const signer of MULTISIG_SIGNERS) { 35 | await web3.eth.sendTransaction({ from: seed, to: signer, value: tokenAmount(5) }) 36 | } 37 | }) 38 | 39 | before('fetch ANTs', async () => { 40 | antv1 = await ANTv1.at(ANTV1_ADDRESS) 41 | cMultisig = await MultisigWallet.at(COMMUNITY_MULTISIG_ADDRESS) 42 | }) 43 | 44 | before('deploy ANTv2', async () => { 45 | antv2 = await ANTv2.new(interimOwner) 46 | }) 47 | 48 | before('deploy ANTv2Migrator', async () => { 49 | migrator = await ANTv2Migrator.new(cMultisig.address, antv1.address, antv2.address) 50 | await antv2.changeMinter(migrator.address, { from: interimOwner }) 51 | }) 52 | 53 | before('initiate migration', async () => { 54 | const [submitter, ...signers] = MULTISIG_SIGNERS 55 | const initiateMigrationData = migrator.contract.methods.initiate().encodeABI() 56 | 57 | const receipt = await cMultisig.submitTransaction(migrator.address, 0, initiateMigrationData, { from: submitter }) 58 | const id = getEventArgument(receipt, 'Submission', 'transactionId') 59 | 60 | for (const signer of signers) { 61 | await cMultisig.confirmTransaction(id, { from: signer }) 62 | } 63 | 64 | assert.isTrue(await cMultisig.isConfirmed(id)) 65 | }) 66 | 67 | it('passes sanity checks', async () => { 68 | // Double check supply 69 | assertBn(await antv1.totalSupply(), await antv2.totalSupply()) 70 | 71 | // Double check top three hodlers 72 | assertBn(await antv1.balanceOf(AA_MULTISIG_ADDRESS), '4966833066039263962000000') 73 | assertBn(await antv1.balanceOf('0xe93381fb4c4f14bda253907b18fad305d799241a'), '3299785361099560000000000') 74 | assertBn(await antv1.balanceOf('0x03af24a6db8e011b86c32960ec6ede52ae5906fb'), '3000000000000000000000000') 75 | 76 | assertBn(await antv2.balanceOf(AA_MULTISIG_ADDRESS), 0) 77 | assertBn(await antv2.balanceOf('0xe93381fb4c4f14bda253907b18fad305d799241a'), 0) 78 | assertBn(await antv2.balanceOf('0x03af24a6db8e011b86c32960ec6ede52ae5906fb'), 0) 79 | }) 80 | 81 | describe('migrate', () => { 82 | const owner = BIG_HOLDERS[0] 83 | let initialV1Balance 84 | let currentV1Balance, currentV2Balance 85 | 86 | before(async () => { 87 | initialV1Balance = await antv1.balanceOf(owner) 88 | }) 89 | 90 | beforeEach(async () => { 91 | currentV1Balance = await antv1.balanceOf(owner) 92 | currentV2Balance = await antv2.balanceOf(owner) 93 | }) 94 | 95 | it('can migrate', async () => { 96 | const amount = tokenAmount(5) 97 | 98 | await antv1.approve(migrator.address, amount, { from: owner }) 99 | await migrator.migrate(amount, { from: owner }) 100 | 101 | assertBn(await antv1.balanceOf(owner), currentV1Balance.sub(amount), 'antv1: post migration balance') 102 | assertBn(await antv2.balanceOf(owner), amount, 'antv2: post migration balance') 103 | }) 104 | 105 | it('can migrate multiple times', async () => { 106 | const amount = tokenAmount(5) 107 | 108 | await antv1.approve(migrator.address, amount, { from: owner }) 109 | await migrator.migrate(amount, { from: owner }) 110 | 111 | await antv1.approve(migrator.address, amount, { from: owner }) 112 | await migrator.migrate(amount, { from: owner }) 113 | 114 | assertBn(await antv1.balanceOf(owner), currentV1Balance.sub(amount.mul(bn(2))), 'antv1: post migration balance') 115 | assertBn(await antv2.balanceOf(owner), currentV2Balance.add(amount.mul(bn(2))), 'antv2: post migration balance') 116 | }) 117 | 118 | it('can migrate rest', async () => { 119 | await antv1.approve(migrator.address, currentV1Balance, { from: owner }) 120 | await migrator.migrate(currentV1Balance, { from: owner }) 121 | assertBn(await antv1.balanceOf(owner), 0, 'antv1: all migrated') 122 | assertBn(await antv2.balanceOf(owner), initialV1Balance, 'antv2: all migrated') 123 | }) 124 | }) 125 | 126 | describe('migrateAll', () => { 127 | const owner = BIG_HOLDERS[1] 128 | let initialV1Balance 129 | 130 | before(async () => { 131 | initialV1Balance = await antv1.balanceOf(owner) 132 | }) 133 | 134 | it('can migrate all', async () => { 135 | await antv1.approve(migrator.address, MAX_UINT256, { from: owner }) 136 | await migrator.migrateAll({ from: owner }) 137 | assertBn(await antv1.balanceOf(owner), 0, 'antv1: all migrated') 138 | assertBn(await antv2.balanceOf(owner), initialV1Balance, 'antv2: all migrated') 139 | }) 140 | }) 141 | 142 | describe('approveAndCall', () => { 143 | const owner = BIG_HOLDERS[2] 144 | let initialV1Balance 145 | let currentV1Balance, currentV2Balance 146 | 147 | before(async () => { 148 | initialV1Balance = await antv1.balanceOf(owner) 149 | }) 150 | 151 | beforeEach(async () => { 152 | currentV1Balance = await antv1.balanceOf(owner) 153 | currentV2Balance = await antv2.balanceOf(owner) 154 | }) 155 | 156 | it('can migrate', async () => { 157 | const amount = tokenAmount(5) 158 | 159 | await antv1.approveAndCall(migrator.address, amount, '0x', { from: owner }) 160 | 161 | assertBn(await antv1.balanceOf(owner), currentV1Balance.sub(amount), 'antv1: post migration balance') 162 | assertBn(await antv2.balanceOf(owner), amount, 'antv2: post migration balance') 163 | }) 164 | 165 | it('can migrate multiple times', async () => { 166 | const amount = tokenAmount(5) 167 | 168 | await antv1.approveAndCall(migrator.address, amount, '0x', { from: owner }) 169 | await antv1.approveAndCall(migrator.address, amount, '0x', { from: owner }) 170 | 171 | assertBn(await antv1.balanceOf(owner), currentV1Balance.sub(amount.mul(bn(2))), 'antv1: post migration balance') 172 | assertBn(await antv2.balanceOf(owner), currentV2Balance.add(amount.mul(bn(2))), 'antv2: post migration balance') 173 | }) 174 | 175 | it('can migrate rest', async () => { 176 | await antv1.approveAndCall(migrator.address, currentV1Balance, '0x', { from: owner }) 177 | assertBn(await antv1.balanceOf(owner), 0, 'antv1: all migrated') 178 | assertBn(await antv2.balanceOf(owner), initialV1Balance, 'antv2: all migrated') 179 | }) 180 | }) 181 | 182 | describe('raw encoded infinity approveAndCall', () => { 183 | const owner = BIG_HOLDERS[3] 184 | let initialV1Balance 185 | 186 | before(async () => { 187 | initialV1Balance = await antv1.balanceOf(owner) 188 | }) 189 | 190 | it('can migrate all', async () => { 191 | // Raw transaction for infinity approveAndCall 192 | const calldata = 193 | `0xcae9ca51000000000000000000000000${migrator.address.slice(2)}ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000` 194 | await antv1.sendTransaction({ from: owner, data: calldata }) 195 | 196 | assertBn(await antv1.balanceOf(owner), 0, 'antv1: all migrated') 197 | assertBn(await antv2.balanceOf(owner), initialV1Balance, 'antv2: all migrated') 198 | }) 199 | }) 200 | }) 201 | -------------------------------------------------------------------------------- /packages/sale/contracts/MiniMeIrrevocableVestedToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // Slightly modified Zeppelin's Vested Token deriving MiniMeToken 4 | 5 | import "./MiniMeToken.sol"; 6 | import "zeppelin/SafeMath.sol"; 7 | 8 | /* 9 | Copyright 2017, Jorge Izquierdo (Aragon Association) 10 | 11 | Based on VestedToken.sol from https://github.com/OpenZeppelin/zeppelin-solidity 12 | 13 | SafeMath – Copyright (c) 2016 Smart Contract Solutions, Inc. 14 | MiniMeToken – Copyright 2017, Jordi Baylina (Giveth) 15 | */ 16 | 17 | // @dev MiniMeIrrevocableVestedToken is a derived version of MiniMeToken adding the 18 | // ability to createTokenGrants which are basically a transfer that limits the 19 | // receiver of the tokens how can he spend them over time. 20 | 21 | // For simplicity, token grants are not saved in MiniMe type checkpoints. 22 | // Vanilla cloning ANT will clone it into a MiniMeToken without vesting. 23 | // More complex cloning could account for past vesting calendars. 24 | 25 | contract MiniMeIrrevocableVestedToken is MiniMeToken, SafeMath { 26 | // Keep the struct at 2 sstores (1 slot for value + 64 * 3 (dates) + 20 (address) = 2 slots (2nd slot is 212 bytes, lower than 256)) 27 | struct TokenGrant { 28 | address granter; 29 | uint256 value; 30 | uint64 cliff; 31 | uint64 vesting; 32 | uint64 start; 33 | } 34 | 35 | event NewTokenGrant(address indexed from, address indexed to, uint256 value, uint64 start, uint64 cliff, uint64 vesting); 36 | 37 | mapping (address => TokenGrant[]) public grants; 38 | 39 | mapping (address => bool) canCreateGrants; 40 | address vestingWhitelister; 41 | 42 | modifier canTransfer(address _sender, uint _value) { 43 | if (_value > spendableBalanceOf(_sender)) throw; 44 | _; 45 | } 46 | 47 | modifier onlyVestingWhitelister { 48 | if (msg.sender != vestingWhitelister) throw; 49 | _; 50 | } 51 | 52 | function MiniMeIrrevocableVestedToken ( 53 | address _tokenFactory, 54 | address _parentToken, 55 | uint _parentSnapShotBlock, 56 | string _tokenName, 57 | uint8 _decimalUnits, 58 | string _tokenSymbol, 59 | bool _transfersEnabled 60 | ) MiniMeToken(_tokenFactory, _parentToken, _parentSnapShotBlock, _tokenName, _decimalUnits, _tokenSymbol, _transfersEnabled) { 61 | vestingWhitelister = msg.sender; 62 | doSetCanCreateGrants(vestingWhitelister, true); 63 | } 64 | 65 | // @dev Add canTransfer modifier before allowing transfer and transferFrom to go through 66 | function transfer(address _to, uint _value) 67 | canTransfer(msg.sender, _value) 68 | public 69 | returns (bool success) { 70 | return super.transfer(_to, _value); 71 | } 72 | 73 | function transferFrom(address _from, address _to, uint _value) 74 | canTransfer(_from, _value) 75 | public 76 | returns (bool success) { 77 | return super.transferFrom(_from, _to, _value); 78 | } 79 | 80 | function spendableBalanceOf(address _holder) constant public returns (uint) { 81 | return transferableTokens(_holder, uint64(now)); 82 | } 83 | 84 | function grantVestedTokens( 85 | address _to, 86 | uint256 _value, 87 | uint64 _start, 88 | uint64 _cliff, 89 | uint64 _vesting) public { 90 | 91 | // Check start, cliff and vesting are properly order to ensure correct functionality of the formula. 92 | if (_cliff < _start) throw; 93 | if (_vesting < _start) throw; 94 | if (_vesting < _cliff) throw; 95 | 96 | if (!canCreateGrants[msg.sender]) throw; 97 | if (tokenGrantsCount(_to) > 20) throw; // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting). 98 | 99 | TokenGrant memory grant = TokenGrant(msg.sender, _value, _cliff, _vesting, _start); 100 | grants[_to].push(grant); 101 | 102 | if (!transfer(_to, _value)) throw; 103 | 104 | NewTokenGrant(msg.sender, _to, _value, _cliff, _vesting, _start); 105 | } 106 | 107 | function setCanCreateGrants(address _addr, bool _allowed) 108 | onlyVestingWhitelister public { 109 | doSetCanCreateGrants(_addr, _allowed); 110 | } 111 | 112 | function doSetCanCreateGrants(address _addr, bool _allowed) 113 | internal { 114 | canCreateGrants[_addr] = _allowed; 115 | } 116 | 117 | function changeVestingWhitelister(address _newWhitelister) onlyVestingWhitelister public { 118 | doSetCanCreateGrants(vestingWhitelister, false); 119 | vestingWhitelister = _newWhitelister; 120 | doSetCanCreateGrants(vestingWhitelister, true); 121 | } 122 | 123 | // @dev Not allow token grants 124 | function revokeTokenGrant(address _holder, uint _grantId) public { 125 | throw; 126 | } 127 | 128 | // 129 | function tokenGrantsCount(address _holder) constant public returns (uint index) { 130 | return grants[_holder].length; 131 | } 132 | 133 | function tokenGrant(address _holder, uint _grantId) constant public returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting) { 134 | TokenGrant grant = grants[_holder][_grantId]; 135 | 136 | granter = grant.granter; 137 | value = grant.value; 138 | start = grant.start; 139 | cliff = grant.cliff; 140 | vesting = grant.vesting; 141 | 142 | vested = vestedTokens(grant, uint64(now)); 143 | } 144 | 145 | function vestedTokens(TokenGrant grant, uint64 time) internal constant returns (uint256) { 146 | return calculateVestedTokens( 147 | grant.value, 148 | uint256(time), 149 | uint256(grant.start), 150 | uint256(grant.cliff), 151 | uint256(grant.vesting) 152 | ); 153 | } 154 | 155 | // transferableTokens 156 | // | _/-------- NonVestedTokens 157 | // | _/ 158 | // | _/ 159 | // | _/ 160 | // | _/ 161 | // | / 162 | // | .| 163 | // | . | 164 | // | . | 165 | // | . | 166 | // | . | 167 | // | . | 168 | // +===+===========+---------+----------> time 169 | // Start Clift Vesting 170 | 171 | function calculateVestedTokens( 172 | uint256 tokens, 173 | uint256 time, 174 | uint256 start, 175 | uint256 cliff, 176 | uint256 vesting) internal constant returns (uint256) 177 | { 178 | 179 | // Shortcuts for before cliff and after vesting cases. 180 | if (time < cliff) return 0; 181 | if (time >= vesting) return tokens; 182 | 183 | // Interpolate all vested tokens. 184 | // As before cliff the shortcut returns 0, we can use just this function to 185 | // calculate it. 186 | 187 | // vestedTokens = tokens * (time - start) / (vesting - start) 188 | uint256 vestedTokens = safeDiv( 189 | safeMul( 190 | tokens, 191 | safeSub(time, start) 192 | ), 193 | safeSub(vesting, start) 194 | ); 195 | 196 | return vestedTokens; 197 | } 198 | 199 | function nonVestedTokens(TokenGrant grant, uint64 time) internal constant returns (uint256) { 200 | // Of all the tokens of the grant, how many of them are not vested? 201 | // grantValue - vestedTokens 202 | return safeSub(grant.value, vestedTokens(grant, time)); 203 | } 204 | 205 | // @dev The date in which all tokens are transferable for the holder 206 | // Useful for displaying purposes (not used in any logic calculations) 207 | function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) { 208 | date = uint64(now); 209 | uint256 grantIndex = tokenGrantsCount(holder); 210 | for (uint256 i = 0; i < grantIndex; i++) { 211 | date = max64(grants[holder][i].vesting, date); 212 | } 213 | return date; 214 | } 215 | 216 | // @dev How many tokens can a holder transfer at a point in time 217 | function transferableTokens(address holder, uint64 time) constant public returns (uint256) { 218 | uint256 grantIndex = tokenGrantsCount(holder); 219 | 220 | if (grantIndex == 0) return balanceOf(holder); // shortcut for holder without grants 221 | 222 | // Iterate through all the grants the holder has, and add all non-vested tokens 223 | uint256 nonVested = 0; 224 | for (uint256 i = 0; i < grantIndex; i++) { 225 | nonVested = safeAdd(nonVested, nonVestedTokens(grants[holder][i], time)); 226 | } 227 | 228 | // Balance - totalNonVested is the amount of tokens a holder can transfer at any given time 229 | return safeSub(balanceOf(holder), nonVested); 230 | } 231 | } 232 | --------------------------------------------------------------------------------