├── .babelrc ├── .eslintrc ├── .flake8 ├── .gitignore ├── .pylintrc ├── .travis.yml ├── INSTRUCTIONS.md ├── LICENSE ├── MINIME_README.md ├── MULTISIG.md ├── README.md ├── SPEC.md ├── audits ├── BlockchainLabs-SNT-audit-report.md ├── coinfabrik-2152b17aa2ef584a2aea95533c707a345c6ccf69.pdf └── prelim-smartcontractsolutions-ef163f1b6fd6fb0630a4b8c78d3b706f3fe1da33.md ├── contracts ├── ContributionWallet.sol ├── DevTokensHolder.sol ├── DynamicCeiling.sol ├── ERC20Token.sol ├── MiniMeToken.sol ├── MultiSigWallet.sol ├── Owned.sol ├── SGT.sol ├── SGTExchanger.sol ├── SNT.sol ├── SNTPlaceHolder.sol ├── SafeMath.sol ├── StatusContribution.sol └── test │ ├── DevTokensHolderMock.sol │ ├── ExternalToken.sol │ ├── Migrations.sol │ ├── SGTExchangerMock.sol │ ├── SGTMock.sol │ ├── SNTMock.sol │ ├── SNTPlaceHolderMock.sol │ └── StatusContributionMock.sol ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── mypy.ini ├── package.json ├── scripts ├── ceiling_curve_calc.py └── sgtGeneration │ ├── deploy.js │ ├── deploy_test.js │ ├── initialBalance.csv │ └── loadBalances.js ├── test ├── claimExternal.js ├── contribution.js ├── dynamicCeiling.js └── helpers │ ├── assertFail.js │ ├── assertGas.js │ ├── assertJump.js │ └── hiddenCurves.js ├── truffle.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "stage-3"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | "extends": "airbnb", 9 | "parser": "babel-eslint", 10 | "rules": { 11 | // indentation 12 | "indent": [ 2, 4 ], 13 | 14 | // spacing 15 | "template-curly-spacing": [ 2, "always" ], 16 | "array-bracket-spacing": [ 2, "always" ], 17 | "object-curly-spacing": [ 2, "always" ], 18 | "computed-property-spacing": [ 2, "always" ], 19 | "no-multiple-empty-lines": [ 2, { "max": 1, "maxEOF": 0, "maxBOF": 0 } ], 20 | 21 | // strings 22 | "quotes": [ 2, "double", "avoid-escape" ], 23 | 24 | // code arrangement matter 25 | "no-use-before-define": [ 2, { "functions": false } ], 26 | 27 | // make it meaningful 28 | "prefer-const": 1, 29 | 30 | // keep it simple 31 | "complexity": [ 1, 5 ], 32 | 33 | // Consisten return 34 | "consistent-return": 0, 35 | 36 | "import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.js", "**/*.spec.js", "**/compile.js", "**/test/*.js"]}], 37 | 38 | // react 39 | "react/prefer-es6-class": 0, 40 | "react/jsx-filename-extension": 0, 41 | "react/jsx-indent": [ 2, 4 ] 42 | }, 43 | "globals": { 44 | "artifacts": true, 45 | "web3": true, 46 | "contract": true, 47 | "assert": true 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 99 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | .DS_Store 4 | 5 | .mypy_cache 6 | __pycache__ 7 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [BASIC] 2 | good-names=i,j,k,n,ex,Run,_ 3 | 4 | [FORMAT] 5 | max-line-length = 99 6 | disable=locally-disabled,locally-enabled 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: 'trusty' 2 | group: 'beta' 3 | 4 | language: 'node_js' 5 | sudo: false 6 | node_js: 7 | - '8' 8 | 9 | cache: 10 | yarn: true 11 | 12 | script: 13 | - 'truffle compile' 14 | - 'testrpc --network-id 15 --mnemonic ''status mnemonic status mnemonic status mnemonic status mnemonic status mnemonic status mnemonic'' > /dev/null &' 15 | - 'truffle test --network development' 16 | - 'truffle migrate --reset --network development_migrate' 17 | -------------------------------------------------------------------------------- /INSTRUCTIONS.md: -------------------------------------------------------------------------------- 1 | ## Before starting 2 | 3 | * Install `yarn` and `npm`. 4 | * Run `yarn` at the repo root. 5 | * Use the same BIP39 compatible mnemonic in both `truffle.js` (can be set by environment variable `TEST_MNEMONIC`) and for your client. 6 | * Change the `from` key in `truffle.js` for any network that requires it. 7 | * Compile contracts: 8 | ``` 9 | ./node_modules/.bin/truffle compile 10 | ``` 11 | * Start your Ethereum client: 12 | ``` 13 | ./node_modules/.bin/testrpc --network-id 15 --mnemonic 'status mnemonic status mnemonic status mnemonic status mnemonic status mnemonic status mnemonic' 14 | ``` 15 | 16 | ## Run tests 17 | 18 | * Run tests 19 | ``` 20 | ./node_modules/.bin/truffle test --network development 21 | ``` 22 | 23 | ## Deploy 24 | 25 | * Change the config constants in `migrations/2_deploy_contracts.js` to match your addresses and parameters. 26 | * Deploy contracts (choose network from `truffle.js`). The following command deploys up to migration step 2: 27 | ``` 28 | ./node_modules/.bin/truffle migrate --network development_migrate -f 2 29 | ``` 30 | 31 | ## Calculations 32 | 33 | * Install `python3.6`. 34 | * Caculate outcomes of different ceilings by running: 35 | ``` 36 | ./scripts/ceiling_curve_calc.py --limit=6100000000000000000000 --curve-factor=300 --fee-token=0.1 37 | ``` 38 | * Run the following to see all options: 39 | ``` 40 | ./scripts/ceiling_curve_calc.py --help 41 | ``` 42 | -------------------------------------------------------------------------------- /MINIME_README.md: -------------------------------------------------------------------------------- 1 | # Status Network Token 2 | 3 | The MiniMeToken contract is a standard ERC20 token with extra functionality: 4 | 5 | ### The token is easy to clone! 6 | 7 | Anybody can create a new clone token from any token using this contract 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. 8 | 9 | function createCloneToken( 10 | string _cloneTokenName, 11 | uint8 _cloneDecimalUnits, 12 | string _cloneTokenSymbol, 13 | uint _snapshotBlock, 14 | bool _isConstant 15 | ) returns(address) { 16 | 17 | Once the clone token is created, it acts as a completely independent token, with it's own unique functionalities. 18 | 19 | ### Balance history is registered and available to be queried 20 | 21 | All MiniMe Tokens maintain a history of the balance changes that occur during each block. Two calls are introduced to read the totalSupply and the balance of any address at any block in the past. 22 | 23 | function totalSupplyAt(uint _blockNumber) constant returns(uint) 24 | 25 | function balanceOfAt(address _holder, uint _blockNumber) constant returns (uint) 26 | 27 | ### Optional token controller 28 | 29 | 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 token controller can be set to 0x0 and this functionality will be disabled. 30 | 31 | For example, a Token Creation 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 the 0x0 address, to guarantee that no new tokens will be created. 32 | 33 | To create and destroy tokens, these two functions are introduced: 34 | 35 | function generateTokens(address _holder, uint _value) onlyController 36 | 37 | function destroyTokens(address _holder, uint _value) onlyController 38 | 39 | ### The Token's Controller can freeze transfers. 40 | 41 | If transfersEnabled == false, tokens cannot be transferred by the users, however they can still be created, destroyed, and transferred by the controller. The controller can also toggle this flag. 42 | 43 | // Allows tokens to be transferred if true or frozen if false 44 | function enableTransfers(bool _transfersEnabled) onlyController 45 | 46 | 47 | ## Applications 48 | 49 | If this token contract is used as the base token, then clones of itself can be easily generated at any given block number, this 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: 50 | 51 | 1. Generating a voting token that is burned when you vote. 52 | 2. Generating a discount "coupon" that is redeemed when you use it. 53 | 3. Generating a token for a "spinoff" DAO. 54 | 4. Generating a token that can be used to give explicit support to an action or a campaign, like polling. 55 | 5. Generating a token to enable the token holders to collect daily, monthly or yearly payments. 56 | 6. Generating a token to limit participation in a contribution period or similar event to holders of a specific token. 57 | 7. Generating token that allows a central party complete control to transfer/generate/destroy tokens at will. 58 | 8. Lots of other applications including all the applications the standard ERC 20 token can be used for. 59 | 60 | All these applications and more are enabled by the MiniMe Token Contract. The most amazing part being that anyone that wants to add these features can, in a permissionless yet safe manner without affecting the parent token's intended functionality. 61 | 62 | # How to deploy a campaign 63 | 64 | 1. Deploy the MinimeTokenFactory 65 | 2. Deploy the MinimeToken 66 | 3. Deploy the campaign 67 | 4. Assign the controller of the MinimeToken to the campaign. 68 | -------------------------------------------------------------------------------- /MULTISIG.md: -------------------------------------------------------------------------------- 1 | # Status Community Multisig [0xbbf0cc1c63f509d48a4674e270d26d80ccaf6022](https://etherscan.io/address/0xbbf0cc1c63f509d48a4674e270d26d80ccaf6022) 2 | 3 | Required signatures: 3/5 4 | 5 | ## Signers 6 | 7 | - Jordi Baylina, [Giveth](http://www.giveth.io/). [Address](https://etherscan.io/address/0x6b9ef02657339310e28a7a9d4b5f25f7c1f68d61). 8 | - Joe Urgo, [district0x](https://district0x.io/), [Sourcerers](http://sourcerers.io/). [Address](https://etherscan.io/address/0x904Ef6ff8E82478c5604d99884EB9Bcd7F73Cc36). 9 | - Hudson Jameson, [Oaken Innovations](https://www.projectoaken.com/). [Address](https://etherscan.io/address/0x02E3F16cA21cf0508835B190933ECbdE2f7f14DF). 10 | - Jorge Izquierdo, [Aragon](https://aragon.one/). [Address](https://etherscan.io/address/0x4838eab6f43841e0d233db4cea47bd64f614f0c5). 11 | - Status Research & Development Multisig. [Address](https://etherscan.io/address/0xa646e29877d52b9e2de457eca09c724ff16d0a2b). 12 | 13 | 14 | ## Responsibilities 15 | 16 | - The Status Community Multisig will serve SNT holders and the broader crypto community to ensure Status' stated mission is carried. 17 | - The Status Community Multisig manages the 29% reserve SNT allocation. 18 | - The Status Community Multisig can be used to solve hypothetical deadlock problems in the Status Research & Development Multisig to ensure resources won't get locked and the project will continue its course. 19 | 20 | 21 | # Status Research & Development Multisig – [0xa646e29877d52b9e2de457eca09c724ff16d0a2b](https://etherscan.io/address/0xa646e29877d52b9e2de457eca09c724ff16d0a2b) 22 | 23 | Required signatures: 2/3 24 | 25 | ## Signers 26 | 27 | - Jarrad Hope, Status Research & Development CEO. [Address](https://etherscan.io/address/0x3ac6cb2ccfd8c8aae3ba31d7ed44c20d241b16a4). 28 | - Carl Bennetts, Status Research & Development CMO. [Address](https://etherscan.io/address/0xdBD6ffD3CB205576367915Dd2f8De0aF7edcCeeF). 29 | - Status Community Multisig – [Address](https://etherscan.io/address/0xbbf0cc1c63f509d48a4674e270d26d80ccaf6022). 30 | 31 | 32 | ## Responsibilities 33 | 34 | - Status Research & Development Multisig will hold the Status Research & Development ether funds and SNT tokens. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Status Network Token 2 | [![Build Status](https://travis-ci.org/status-im/status-network-token.svg?branch=master)](https://travis-ci.org/status-im/status-network-token) 3 | 4 | - [Whitepaper](https://status.im/whitepaper.pdf) 5 | - [Contribution Period Specification](/SPEC.md) 6 | - [The Importance of Distribution](https://blog.status.im/distribution-dynamic-ceilings-e2f427f5cca) blogpost. 7 | - [Encoding the Status ‘Genesis Block’](https://blog.status.im/encoding-the-status-genesis-block-d73d287a750) blogpost. 8 | 9 | ## Technical definition 10 | 11 | At the technical level SGT & SNT are a ERC20-compliant tokens, derived from the [MiniMe Token](https://github.com/Giveth/minime) that allows for token cloning (forking), which will be useful for many future use-cases. 12 | 13 | Also built in the token is a vesting schedule for limiting SNT transferability over time. Status Project Founders tokens are vesting. 14 | 15 | ## Contracts 16 | 17 | - [SNT.sol](/contracts/SNT.sol): Main contract for the token. 18 | - [SGT.sol](/contracts/SGT.sol): Token contract for early adopters. Deployed to [0xd248B0D48E44aaF9c49aea0312be7E13a6dc1468](https://etherscan.io/address/0xd248B0D48E44aaF9c49aea0312be7E13a6dc1468#readContract) 19 | - [MiniMeToken.sol](/contracts/MiniMeToken.sol): Token implementation. 20 | - [StatusContribution.sol](/contracts/StatusContribution.sol): Implementation of the initial distribution of SNT. 21 | - [DynamicCeiling.sol](/contracts/DynamicCeiling.sol): Auxiliary contract to manage the dynamic ceiling during the contribution period. 22 | - [SNTPlaceHolder.sol](/contracts/SNTPlaceHolder.sol): Placeholder for the Status Network before its deployment. 23 | - [ContributionWallet.sol](/contracts/ContributionWallet.sol): Simple contract that will hold all funds until final block of the contribution period. 24 | - [MultiSigWallet.sol](/contracts/MultiSigWallet.sol): ConsenSys multisig used for Status and community multisigs. 25 | - [DevTokensHolder.sol](/contracts/DevTokensHolder.sol): Contract where tokens belonging to developers will be held. This contract will release this tokens in a vested timing. 26 | - [SGTExchanger.sol](/contracts/SGTExchanger.sol): Contract responsible for crediting SNTs to the SGT holders after the contribution period ends. 27 | 28 | See [INSTRUCTIONS.md](/INSTRUCTIONS.md) for instructions on how to test and deploy the contracts. 29 | 30 | ## Reviewers and audits. 31 | 32 | Code for the SNT token and the offering is being reviewed by: 33 | 34 | - Jordi Baylina, Author. 35 | - [Smart Contract Solutions (OpenZeppelin)](https://smartcontractsolutions.com/). [Pending audit results](/) [Preliminary](/audits/prelim-smartcontractsolutions-ef163f1b6fd6fb0630a4b8c78d3b706f3fe1da33.md) 36 | - [CoinFabrik](http://www.coinfabrik.com/). [2152b17aa2ef584a2aea95533c707a345c6ccf69](/audits/coinfabrik-2152b17aa2ef584a2aea95533c707a345c6ccf69.pdf) 37 | - [BlockchainLabs.nz](http://blockchainlabs.nz/). [Audit results](/audits/BlockchainLabs-SNT-audit-report.md) 38 | - [Bok Consulting](https://www.bokconsulting.com.au/). [Pending audit results](/) 39 | - YYYYYY. [Pending audit results](/) 40 | 41 | A bug bounty for the SNT token and offering started on 14/6/2017. [More details](https://blog.status.im/status-network-token-bug-bounty-a66fc2324359) 42 | -------------------------------------------------------------------------------- /SPEC.md: -------------------------------------------------------------------------------- 1 | # Status Network Contribution Period 2 | ## Functional Specification 3 | 4 | ### Distribution 5 | - 29% is for reserve (multisig) 6 | - 20% goes to the status team and founders (multisig, 2 Year Vesting Contract, 6 month cliffs) 7 | - Remaining 51% is divided between the initial contribution period itself and SGT, where SGT is <= 10% of total supply. 8 | 9 | ### Whitelist 10 | Addresses can be whitelisted and have guaranteed participation up to a set maximum amount, ignores Dynamic Ceiling. Sending ETH to the smart contract address should be no different, whether whitelisted or not. 11 | Status Genesis Tokens 12 | SGT is a Minime Token that’s total supply of 500,000,000 maps to, and cannot exceed 10% of the total supply. 13 | ie. If 250,000,000 of SGT is allocated then SGT maps to 5% of the total supply. 14 | SGT can be redeemed for SNT after contribution period. 15 | 16 | ### Dynamic Ceiling 17 | A Curve that specifies a series of hidden hard caps at specific block intervals, that can be revealed at anytime during the contribution period. The entire curve must be revealed before finalising the contribution period. Finalising the contribution period can happen at any moment during the curve, as long as entire curve has been revealed. Whitelisted addresses ignore ceiling. 18 | 19 | ### Misc 20 | Tokens are not transferrable for 1 week after contribution period and are minted at 10,000 SNT per 1 ETH. 21 | -------------------------------------------------------------------------------- /audits/BlockchainLabs-SNT-audit-report.md: -------------------------------------------------------------------------------- 1 | # Status Network Token Audit 2 | 3 | ## Preamble 4 | This audit report was undertaken by BlockchainLabs.nz for the purpose of providing feedback to Status Research & Development Gmbh. It has subsequently been shared publicly without any express or implied warranty. 5 | 6 | Solidity contracts were sourced from the public Github repo [status-im/status-network-token](https://github.com/status-im/status-network-token) prior to commit [79419f86c1b4bfcfcd18f08aecee168ff1f73fc4](https://github.com/status-im/status-network-token/tree/79419f86c1b4bfcfcd18f08aecee168ff1f73fc4) - we would encourage all community members and token holders to make their own assessment of the contracts. 7 | 8 | ## Scope 9 | All Solidity code contained in [/contracts](https://github.com/status-im/status-network-token/tree/master/contracts) was considered in scope along with the tests contained in [/test](https://github.com/status-im/status-network-token/tree/master/test) as a basis for static and dynamic analysis. 10 | 11 | ## Focus Areas 12 | The audit report is focused on the following key areas - though this is *not an exhaustive list*. 13 | 14 | ### Correctness 15 | * No correctness defects uncovered during static analysis? 16 | * No implemented contract violations uncovered during execution? 17 | * No other generic incorrect behavior detected during execution? 18 | * Adherence to adopted standards such as ERC20? 19 | 20 | ### Testability 21 | * Test coverage across all functions and events? 22 | * Test cases for both expected behaviour and failure modes? 23 | * Settings for easy testing of a range of parameters? 24 | * No reliance on nested callback functions or console logs? 25 | * Avoidance of test scenarios calling other test scenarios? 26 | 27 | ### Security 28 | * No presence of known security weaknesses? 29 | * No funds at risk of malicious attempts to withdraw/transfer? 30 | * No funds at risk of control fraud? 31 | * Prevention of Integer Overflow or Underflow? 32 | 33 | ### Best Practice 34 | * Explicit labeling for the visibility of functions and state variables? 35 | * Proper management of gas limits and nested execution? 36 | * Latest version of the Solidity compiler? 37 | 38 | ## Classification 39 | 40 | ### Defect Severity 41 | * **Minor** - A defect that does not have a material impact on the contract execution and is likely to be subjective. 42 | * **Moderate** - A defect that could impact the desired outcome of the contract execution in a specific scenario. 43 | * **Major** - A defect that impacts the desired outcome of the contract execution or introduces a weakness that may be exploited. 44 | * **Critical** - A defect that presents a significant security vulnerability or failure of the contract across a range of scenarios. 45 | 46 | ## Findings 47 | ### Minor 48 | 49 | **SGT Exchanger** - While testing the correctness of SGTExchanger, we found that it was possible for a user with no SGT tokens to call the collect() function. It was also possible for a user with SGT tokens to call the collect() function for a second time, after having already collected their SNT. 50 | 51 | However, this is a minor issue because in both cases no SNT was issued. Since commit `450fbc85fd7a4a0dc17d22fc7a6ab5071277fb46`, PR 106 on 16th June an exception is now thrown: 52 | https://github.com/status-im/status-network-token/pull/106 53 | 54 | **Test Failures** - Tests designed to check for failures were encapsulated within a `catch` and were never properly evaluated. The fix for this issue was addressed on https://github.com/status-im/status-network-token#70 and later improved on https://github.com/status-im/status-network-token#133 55 | 56 | **SGT after Contribution** - As of 19 June, attempts to generate additional SGT tokens after the contribution period has begun do not throw an exception. 57 | 58 | Prior to 19 June this resulted in a thrown exception, which is preferable. 59 | 60 | ~~However, no new tokens are created so this is only a minor issue.~~ _Status have advised that SGT can be issued after the contribution period._ 61 | 62 | ### Moderate 63 | _No moderate defects were found during this audit._ 64 | 65 | ### Major 66 | 67 | **Short Address** - The current implementation of MiniMeToken is vulnerable to ERC20 Short Address 'Attack' 68 | 69 | http://vessenes.com/the-erc20-short-address-attack-explained/ 70 | https://blog.golemproject.net/how-to-find-10m-by-just-reading-blockchain-6ae9d39fcd95 71 | 72 | While this isn't a critical issue as it only comes into play with user error, we suggest making the fix to MiniMeToken. 73 | 74 | A simple fix would be to add a modifier to check address size, and apply this modifier to the transfer function of the MiniMeToken: 75 | ``` 76 | modifier onlyPayloadSize(uint size) { 77 | assert(msg.data.length == size + 4); 78 | _; 79 | } 80 | 81 | function transfer(address _to, uint256 _value) onlyPayloadSize(2 * 32) { 82 | //function body unchanged 83 | } 84 | ``` 85 | **DevTokensHolder** - The function collectTokens on the DevTokensHolder contract will mistakenly floor the division of any period of time by 24 months. Making it impossible for developers to collect any token until 2 years. The fix was merged on status-im/status-network-token#105 86 | 87 | ### Critical 88 | _No critical defects were found during this audit._ 89 | 90 | ## Conclusion 91 | Overall we have been satisfied with the quality of the code and responsiveness of the developers in fixing defects. There was good test coverage for some components such as MultiSigWallet and MiniMeToken as they've been used in other projects, meanwhile tests for the other contracts/functions were written during the audit period to improve the testability of the project as a whole. 92 | 93 | The developers have followed common best practices and demonstrated an awareness of security weaknesses that could put funds at risk. During the audit the **Short Address** advisory was published and we recommend that this be handled upstream as a modifier in the MiniMeToken contract itself. 94 | -------------------------------------------------------------------------------- /audits/coinfabrik-2152b17aa2ef584a2aea95533c707a345c6ccf69.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/status-im/status-network-token/cea17ed3d445a750815fa92bfe894901ff5c1a1c/audits/coinfabrik-2152b17aa2ef584a2aea95533c707a345c6ccf69.pdf -------------------------------------------------------------------------------- /audits/prelim-smartcontractsolutions-ef163f1b6fd6fb0630a4b8c78d3b706f3fe1da33.md: -------------------------------------------------------------------------------- 1 | Hi all! 2 | 3 | Here are the severe issues we've found so far: 4 | 5 | #### Cloning a MiniMeToken with snapshot block set to the current block is subject to a value change attack. 6 | 7 | If a `MiniMeToken` is cloned by calling the `createCloneToken` function on [line 384 of MiniMeToken.sol](https://github.com/status-im/status-network-token/blob/2152b17aa2ef584a2aea95533c707a345c6ccf69/contracts/MiniMeToken.sol#L384) with the `_snapshotBlock` parameter set to zero or `block.number`, the clone will be creating using the current block as `parentSnapShotBlock`. 8 | 9 | This opens a small window of time for an attacker to see this transaction in the network, and insert `transfer` transactions in the same block. Given these will be [in the context of the current block too](https://github.com/status-im/status-network-token/blob/2152b17aa2ef584a2aea95533c707a345c6ccf69/contracts/MiniMeToken.sol#L500), the values used for the clone token will contain the modifications by the attacker. This would be confusing for the user creating the clone at the very least, and potentially dangerous. 10 | 11 | Luckily this can be very easily fixed. Consider replacing [line 391 of MiniMeToken.sol](https://github.com/status-im/status-network-token/blob/2152b17aa2ef584a2aea95533c707a345c6ccf69/contracts/MiniMeToken.sol#L391) for a check that will throw if `_snapshotBlock` equals `block.number`. Consider adding the same check [in the MiniMeToken constructor](https://github.com/status-im/status-network-token/blob/2152b17aa2ef584a2aea95533c707a345c6ccf69/contracts/MiniMeToken.sol#L156) too, for `_parentSnapShotBlock`. 12 | 13 | #### ContributionWallet finalBlock can be different from StatusContribution stopBlock 14 | 15 | Withdrawal from the `ContributionWallet` contract is enabled once [the contribution is finalized](https://github.com/status-im/status-network-token/blob/2152b17aa2ef584a2aea95533c707a345c6ccf69/contracts/ContributionWallet.sol#L58) or when the current block is after `finalBlock`, a state variable. For the contract to serve its purpose, `finalBlock` should be the same as the contribution's `stopBlock` or greater. Otherwise, the funds could be withdrawn [before the contribution is finalized](https://github.com/status-im/status-network-token/blob/2152b17aa2ef584a2aea95533c707a345c6ccf69/contracts/ContributionWallet.sol#L57), which would defeat the purpose of `ContributionWallet`. 16 | 17 | Consider using `contribution.stopBlock()` in place of `finalBlock` or checking that `finalBlock` is greater or equal than `stopBlock`. 18 | 19 | **Addendum** 20 | 21 | We also have and additional comment about [a PR you merged adding gas price cap to transactions](https://github.com/status-im/status-network-token/pull/20/files) and [a pending PR adding a cap to call frequency by address](https://github.com/status-im/status-network-token/pull/67/files). Both are out of the audit's commit scope, but we want to let you guys know our thoughts anyway as a friendly help. We don't this this additions are a good idea, and recommend you revert them. 22 | 23 | See [Vitalik's post](http://vitalik.ca/general/2017/06/09/sales.html) about the gas price cap issue, and the frequency cap can be easily circumvented so it doesn't add security but does add complexity and attack surface. 24 | 25 | Let me know your thoughts/questions. The final report will be delivered on Friday, as agreed. 26 | 27 | Happy to be working on this together, 28 | 29 | Cheers, 30 | 31 | Manuel Araoz 32 | 33 | [openzeppelin.com](https://openzeppelin.com/) -------------------------------------------------------------------------------- /contracts/ContributionWallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /* 4 | Copyright 2017, Jordi Baylina 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /// @title ContributionWallet Contract 21 | /// @author Jordi Baylina 22 | /// @dev This contract will be hold the Ether during the contribution period. 23 | /// The idea of this contract is to avoid recycling Ether during the contribution 24 | /// period. So all the ETH collected will be locked here until the contribution 25 | /// period ends 26 | 27 | // @dev Contract to hold sale raised funds during the sale period. 28 | // Prevents attack in which the Aragon Multisig sends raised ether 29 | // to the sale contract to mint tokens to itself, and getting the 30 | // funds back immediately. 31 | 32 | 33 | import "./StatusContribution.sol"; 34 | 35 | 36 | contract ContributionWallet { 37 | 38 | // Public variables 39 | address public multisig; 40 | uint256 public endBlock; 41 | StatusContribution public contribution; 42 | 43 | // @dev Constructor initializes public variables 44 | // @param _multisig The address of the multisig that will receive the funds 45 | // @param _endBlock Block after which the multisig can request the funds 46 | // @param _contribution Address of the StatusContribution contract 47 | function ContributionWallet(address _multisig, uint256 _endBlock, address _contribution) { 48 | require(_multisig != 0x0); 49 | require(_contribution != 0x0); 50 | require(_endBlock != 0 && _endBlock <= 4000000); 51 | multisig = _multisig; 52 | endBlock = _endBlock; 53 | contribution = StatusContribution(_contribution); 54 | } 55 | 56 | // @dev Receive all sent funds without any further logic 57 | function () public payable {} 58 | 59 | // @dev Withdraw function sends all the funds to the wallet if conditions are correct 60 | function withdraw() public { 61 | require(msg.sender == multisig); // Only the multisig can request it 62 | require(block.number > endBlock || // Allow after end block 63 | contribution.finalizedBlock() != 0); // Allow when sale is finalized 64 | multisig.transfer(this.balance); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /contracts/DevTokensHolder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /* 4 | Copyright 2017, Jordi Baylina 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /// @title DevTokensHolder Contract 21 | /// @author Jordi Baylina 22 | /// @dev This contract will hold the tokens of the developers. 23 | /// Tokens will not be able to be collected until 6 months after the contribution 24 | /// period ends. And it will be increasing linearly until 2 years. 25 | 26 | 27 | // collectable tokens 28 | // | _/-------- vestedTokens rect 29 | // | _/ 30 | // | _/ 31 | // | _/ 32 | // | _/ 33 | // | _/ 34 | // | _/ 35 | // | _/ 36 | // | | 37 | // | . | 38 | // | . | 39 | // | . | 40 | // +===+======+--------------+----------> time 41 | // Contrib 6 Months 24 Months 42 | // End 43 | 44 | 45 | import "./MiniMeToken.sol"; 46 | import "./StatusContribution.sol"; 47 | import "./SafeMath.sol"; 48 | import "./ERC20Token.sol"; 49 | 50 | 51 | contract DevTokensHolder is Owned { 52 | using SafeMath for uint256; 53 | 54 | uint256 collectedTokens; 55 | StatusContribution contribution; 56 | MiniMeToken snt; 57 | 58 | function DevTokensHolder(address _owner, address _contribution, address _snt) { 59 | owner = _owner; 60 | contribution = StatusContribution(_contribution); 61 | snt = MiniMeToken(_snt); 62 | } 63 | 64 | 65 | /// @notice The Dev (Owner) will call this method to extract the tokens 66 | function collectTokens() public onlyOwner { 67 | uint256 balance = snt.balanceOf(address(this)); 68 | uint256 total = collectedTokens.add(balance); 69 | 70 | uint256 finalizedTime = contribution.finalizedTime(); 71 | 72 | require(finalizedTime > 0 && getTime() > finalizedTime.add(months(6))); 73 | 74 | uint256 canExtract = total.mul(getTime().sub(finalizedTime)).div(months(24)); 75 | 76 | canExtract = canExtract.sub(collectedTokens); 77 | 78 | if (canExtract > balance) { 79 | canExtract = balance; 80 | } 81 | 82 | collectedTokens = collectedTokens.add(canExtract); 83 | assert(snt.transfer(owner, canExtract)); 84 | 85 | TokensWithdrawn(owner, canExtract); 86 | } 87 | 88 | function months(uint256 m) internal returns (uint256) { 89 | return m.mul(30 days); 90 | } 91 | 92 | function getTime() internal returns (uint256) { 93 | return now; 94 | } 95 | 96 | 97 | ////////// 98 | // Safety Methods 99 | ////////// 100 | 101 | /// @notice This method can be used by the controller to extract mistakenly 102 | /// sent tokens to this contract. 103 | /// @param _token The address of the token contract that you want to recover 104 | /// set to 0 in case you want to extract ether. 105 | function claimTokens(address _token) public onlyOwner { 106 | require(_token != address(snt)); 107 | if (_token == 0x0) { 108 | owner.transfer(this.balance); 109 | return; 110 | } 111 | 112 | ERC20Token token = ERC20Token(_token); 113 | uint256 balance = token.balanceOf(this); 114 | token.transfer(owner, balance); 115 | ClaimedTokens(_token, owner, balance); 116 | } 117 | 118 | event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount); 119 | event TokensWithdrawn(address indexed _holder, uint256 _amount); 120 | } 121 | -------------------------------------------------------------------------------- /contracts/DynamicCeiling.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /* 4 | Copyright 2017, Jordi Baylina 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /// @title DynamicCeiling Contract 21 | /// @author Jordi Baylina 22 | /// @dev This contract calculates the ceiling from a series of curves. 23 | /// These curves are committed first and revealed later. 24 | /// All the curves must be in increasing order and the last curve is marked 25 | /// as the last one. 26 | /// This contract allows to hide and reveal the ceiling at will of the owner. 27 | 28 | 29 | import "./SafeMath.sol"; 30 | import "./Owned.sol"; 31 | 32 | 33 | contract DynamicCeiling is Owned { 34 | using SafeMath for uint256; 35 | 36 | struct Curve { 37 | bytes32 hash; 38 | // Absolute limit for this curve 39 | uint256 limit; 40 | // The funds remaining to be collected are divided by `slopeFactor` smooth ceiling 41 | // with a long tail where big and small buyers can take part. 42 | uint256 slopeFactor; 43 | // This keeps the curve flat at this number, until funds to be collected is less than this 44 | uint256 collectMinimum; 45 | } 46 | 47 | address public contribution; 48 | 49 | Curve[] public curves; 50 | uint256 public currentIndex; 51 | uint256 public revealedCurves; 52 | bool public allRevealed; 53 | 54 | /// @dev `contribution` is the only address that can call a function with this 55 | /// modifier 56 | modifier onlyContribution { 57 | require(msg.sender == contribution); 58 | _; 59 | } 60 | 61 | function DynamicCeiling(address _owner, address _contribution) { 62 | owner = _owner; 63 | contribution = _contribution; 64 | } 65 | 66 | /// @notice This should be called by the creator of the contract to commit 67 | /// all the curves. 68 | /// @param _curveHashes Array of hashes of each curve. Each hash is calculated 69 | /// by the `calculateHash` method. More hashes than actual curves can be 70 | /// committed in order to hide also the number of curves. 71 | /// The remaining hashes can be just random numbers. 72 | function setHiddenCurves(bytes32[] _curveHashes) public onlyOwner { 73 | require(curves.length == 0); 74 | 75 | curves.length = _curveHashes.length; 76 | for (uint256 i = 0; i < _curveHashes.length; i = i.add(1)) { 77 | curves[i].hash = _curveHashes[i]; 78 | } 79 | } 80 | 81 | 82 | /// @notice Anybody can reveal the next curve if he knows it. 83 | /// @param _limit Ceiling cap. 84 | /// (must be greater or equal to the previous one). 85 | /// @param _last `true` if it's the last curve. 86 | /// @param _salt Random number used to commit the curve 87 | function revealCurve(uint256 _limit, uint256 _slopeFactor, uint256 _collectMinimum, 88 | bool _last, bytes32 _salt) public { 89 | require(!allRevealed); 90 | 91 | require(curves[revealedCurves].hash == calculateHash(_limit, _slopeFactor, _collectMinimum, 92 | _last, _salt)); 93 | 94 | require(_limit != 0 && _slopeFactor != 0 && _collectMinimum != 0); 95 | if (revealedCurves > 0) { 96 | require(_limit >= curves[revealedCurves.sub(1)].limit); 97 | } 98 | 99 | curves[revealedCurves].limit = _limit; 100 | curves[revealedCurves].slopeFactor = _slopeFactor; 101 | curves[revealedCurves].collectMinimum = _collectMinimum; 102 | revealedCurves = revealedCurves.add(1); 103 | 104 | if (_last) allRevealed = true; 105 | } 106 | 107 | /// @notice Reveal multiple curves at once 108 | function revealMulti(uint256[] _limits, uint256[] _slopeFactors, uint256[] _collectMinimums, 109 | bool[] _lasts, bytes32[] _salts) public { 110 | // Do not allow none and needs to be same length for all parameters 111 | require(_limits.length != 0 && 112 | _limits.length == _slopeFactors.length && 113 | _limits.length == _collectMinimums.length && 114 | _limits.length == _lasts.length && 115 | _limits.length == _salts.length); 116 | 117 | for (uint256 i = 0; i < _limits.length; i = i.add(1)) { 118 | revealCurve(_limits[i], _slopeFactors[i], _collectMinimums[i], 119 | _lasts[i], _salts[i]); 120 | } 121 | } 122 | 123 | /// @notice Move to curve, used as a failsafe 124 | function moveTo(uint256 _index) public onlyOwner { 125 | require(_index < revealedCurves && // No more curves 126 | _index == currentIndex.add(1)); // Only move one index at a time 127 | currentIndex = _index; 128 | } 129 | 130 | /// @return Return the funds to collect for the current point on the curve 131 | /// (or 0 if no curves revealed yet) 132 | function toCollect(uint256 collected) public onlyContribution returns (uint256) { 133 | if (revealedCurves == 0) return 0; 134 | 135 | // Move to the next curve 136 | if (collected >= curves[currentIndex].limit) { // Catches `limit == 0` 137 | uint256 nextIndex = currentIndex.add(1); 138 | if (nextIndex >= revealedCurves) return 0; // No more curves 139 | currentIndex = nextIndex; 140 | if (collected >= curves[currentIndex].limit) return 0; // Catches `limit == 0` 141 | } 142 | 143 | // Everything left to collect from this limit 144 | uint256 difference = curves[currentIndex].limit.sub(collected); 145 | 146 | // Current point on the curve 147 | uint256 collect = difference.div(curves[currentIndex].slopeFactor); 148 | 149 | // Prevents paying too much fees vs to be collected; breaks long tail 150 | if (collect <= curves[currentIndex].collectMinimum) { 151 | if (difference > curves[currentIndex].collectMinimum) { 152 | return curves[currentIndex].collectMinimum; 153 | } else { 154 | return difference; 155 | } 156 | } else { 157 | return collect; 158 | } 159 | } 160 | 161 | /// @notice Calculates the hash of a curve. 162 | /// @param _limit Ceiling cap. 163 | /// @param _last `true` if it's the last curve. 164 | /// @param _salt Random number that will be needed to reveal this curve. 165 | /// @return The calculated hash of this curve to be used in the `setHiddenCurves` method 166 | function calculateHash(uint256 _limit, uint256 _slopeFactor, uint256 _collectMinimum, 167 | bool _last, bytes32 _salt) public constant returns (bytes32) { 168 | return keccak256(_limit, _slopeFactor, _collectMinimum, _last, _salt); 169 | } 170 | 171 | /// @return Return the total number of curves committed 172 | /// (can be larger than the number of actual curves on the curve to hide 173 | /// the real number of curves) 174 | function nCurves() public constant returns (uint256) { 175 | return curves.length; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /contracts/ERC20Token.sol: -------------------------------------------------------------------------------- 1 | // Abstract contract for the full ERC 20 Token standard 2 | // https://github.com/ethereum/EIPs/issues/20 3 | pragma solidity ^0.4.11; 4 | 5 | contract ERC20Token { 6 | /* This is a slight change to the ERC20 base standard. 7 | function totalSupply() constant returns (uint256 supply); 8 | is replaced with: 9 | uint256 public totalSupply; 10 | This automatically creates a getter function for the totalSupply. 11 | This is moved to the base contract since public getter functions are not 12 | currently recognised as an implementation of the matching abstract 13 | function by the compiler. 14 | */ 15 | /// total amount of tokens 16 | uint256 public totalSupply; 17 | 18 | /// @param _owner The address from which the balance will be retrieved 19 | /// @return The balance 20 | function balanceOf(address _owner) constant returns (uint256 balance); 21 | 22 | /// @notice send `_value` token to `_to` from `msg.sender` 23 | /// @param _to The address of the recipient 24 | /// @param _value The amount of token to be transferred 25 | /// @return Whether the transfer was successful or not 26 | function transfer(address _to, uint256 _value) returns (bool success); 27 | 28 | /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` 29 | /// @param _from The address of the sender 30 | /// @param _to The address of the recipient 31 | /// @param _value The amount of token to be transferred 32 | /// @return Whether the transfer was successful or not 33 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success); 34 | 35 | /// @notice `msg.sender` approves `_spender` to spend `_value` tokens 36 | /// @param _spender The address of the account able to transfer the tokens 37 | /// @param _value The amount of tokens to be approved for transfer 38 | /// @return Whether the approval was successful or not 39 | function approve(address _spender, uint256 _value) returns (bool success); 40 | 41 | /// @param _owner The address of the account owning tokens 42 | /// @param _spender The address of the account able to transfer the tokens 43 | /// @return Amount of remaining tokens allowed to spent 44 | function allowance(address _owner, address _spender) constant returns (uint256 remaining); 45 | 46 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 47 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 48 | } 49 | -------------------------------------------------------------------------------- /contracts/MiniMeToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | import "./ERC20Token.sol"; 5 | 6 | /* 7 | Copyright 2016, Jordi Baylina 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | */ 22 | 23 | /// @title MiniMeToken Contract 24 | /// @author Jordi Baylina 25 | /// @dev This token contract's goal is to make it easy for anyone to clone this 26 | /// token using the token distribution at a given block, this will allow DAO's 27 | /// and DApps to upgrade their features in a decentralized manner without 28 | /// affecting the original token 29 | /// @dev It is ERC20 compliant, but still needs to under go further testing. 30 | 31 | 32 | /// @dev The token controller contract must implement these functions 33 | contract TokenController { 34 | /// @notice Called when `_owner` sends ether to the MiniMe Token contract 35 | /// @param _owner The address that sent the ether to create tokens 36 | /// @return True if the ether is accepted, false if it throws 37 | function proxyPayment(address _owner) payable returns(bool); 38 | 39 | /// @notice Notifies the controller about a token transfer allowing the 40 | /// controller to react if desired 41 | /// @param _from The origin of the transfer 42 | /// @param _to The destination of the transfer 43 | /// @param _amount The amount of the transfer 44 | /// @return False if the controller does not authorize the transfer 45 | function onTransfer(address _from, address _to, uint _amount) returns(bool); 46 | 47 | /// @notice Notifies the controller about an approval allowing the 48 | /// controller to react if desired 49 | /// @param _owner The address that calls `approve()` 50 | /// @param _spender The spender in the `approve()` call 51 | /// @param _amount The amount in the `approve()` call 52 | /// @return False if the controller does not authorize the approval 53 | function onApprove(address _owner, address _spender, uint _amount) 54 | returns(bool); 55 | } 56 | 57 | contract Controlled { 58 | /// @notice The address of the controller is the only address that can call 59 | /// a function with this modifier 60 | modifier onlyController { if (msg.sender != controller) throw; _; } 61 | 62 | address public controller; 63 | 64 | function Controlled() { controller = msg.sender;} 65 | 66 | /// @notice Changes the controller of the contract 67 | /// @param _newController The new controller of the contract 68 | function changeController(address _newController) onlyController { 69 | controller = _newController; 70 | } 71 | } 72 | 73 | contract ApproveAndCallFallBack { 74 | function receiveApproval(address from, uint256 _amount, address _token, bytes _data); 75 | } 76 | 77 | /// @dev The actual token contract, the default controller is the msg.sender 78 | /// that deploys the contract, so usually this token will be deployed by a 79 | /// token controller contract, which Giveth will call a "Campaign" 80 | contract MiniMeToken is Controlled { 81 | 82 | string public name; //The Token's name: e.g. DigixDAO Tokens 83 | uint8 public decimals; //Number of decimals of the smallest unit 84 | string public symbol; //An identifier: e.g. REP 85 | string public version = 'MMT_0.1'; //An arbitrary versioning scheme 86 | 87 | 88 | /// @dev `Checkpoint` is the structure that attaches a block number to a 89 | /// given value, the block number attached is the one that last changed the 90 | /// value 91 | struct Checkpoint { 92 | 93 | // `fromBlock` is the block number that the value was generated from 94 | uint128 fromBlock; 95 | 96 | // `value` is the amount of tokens at a specific block number 97 | uint128 value; 98 | } 99 | 100 | // `parentToken` is the Token address that was cloned to produce this token; 101 | // it will be 0x0 for a token that was not cloned 102 | MiniMeToken public parentToken; 103 | 104 | // `parentSnapShotBlock` is the block number from the Parent Token that was 105 | // used to determine the initial distribution of the Clone Token 106 | uint public parentSnapShotBlock; 107 | 108 | // `creationBlock` is the block number that the Clone Token was created 109 | uint public creationBlock; 110 | 111 | // `balances` is the map that tracks the balance of each address, in this 112 | // contract when the balance changes the block number that the change 113 | // occurred is also included in the map 114 | mapping (address => Checkpoint[]) balances; 115 | 116 | // `allowed` tracks any extra transfer rights as in all ERC20 tokens 117 | mapping (address => mapping (address => uint256)) allowed; 118 | 119 | // Tracks the history of the `totalSupply` of the token 120 | Checkpoint[] totalSupplyHistory; 121 | 122 | // Flag that determines if the token is transferable or not. 123 | bool public transfersEnabled; 124 | 125 | // The factory used to create new clone tokens 126 | MiniMeTokenFactory public tokenFactory; 127 | 128 | //////////////// 129 | // Constructor 130 | //////////////// 131 | 132 | /// @notice Constructor to create a MiniMeToken 133 | /// @param _tokenFactory The address of the MiniMeTokenFactory contract that 134 | /// will create the Clone token contracts, the token factory needs to be 135 | /// deployed first 136 | /// @param _parentToken Address of the parent token, set to 0x0 if it is a 137 | /// new token 138 | /// @param _parentSnapShotBlock Block of the parent token that will 139 | /// determine the initial distribution of the clone token, set to 0 if it 140 | /// is a new token 141 | /// @param _tokenName Name of the new token 142 | /// @param _decimalUnits Number of decimals of the new token 143 | /// @param _tokenSymbol Token Symbol for the new token 144 | /// @param _transfersEnabled If true, tokens will be able to be transferred 145 | function MiniMeToken( 146 | address _tokenFactory, 147 | address _parentToken, 148 | uint _parentSnapShotBlock, 149 | string _tokenName, 150 | uint8 _decimalUnits, 151 | string _tokenSymbol, 152 | bool _transfersEnabled 153 | ) { 154 | tokenFactory = MiniMeTokenFactory(_tokenFactory); 155 | name = _tokenName; // Set the name 156 | decimals = _decimalUnits; // Set the decimals 157 | symbol = _tokenSymbol; // Set the symbol 158 | parentToken = MiniMeToken(_parentToken); 159 | parentSnapShotBlock = _parentSnapShotBlock; 160 | transfersEnabled = _transfersEnabled; 161 | creationBlock = getBlockNumber(); 162 | } 163 | 164 | 165 | /////////////////// 166 | // ERC20 Methods 167 | /////////////////// 168 | 169 | /// @notice Send `_amount` tokens to `_to` from `msg.sender` 170 | /// @param _to The address of the recipient 171 | /// @param _amount The amount of tokens to be transferred 172 | /// @return Whether the transfer was successful or not 173 | function transfer(address _to, uint256 _amount) returns (bool success) { 174 | if (!transfersEnabled) throw; 175 | return doTransfer(msg.sender, _to, _amount); 176 | } 177 | 178 | /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it 179 | /// is approved by `_from` 180 | /// @param _from The address holding the tokens being transferred 181 | /// @param _to The address of the recipient 182 | /// @param _amount The amount of tokens to be transferred 183 | /// @return True if the transfer was successful 184 | function transferFrom(address _from, address _to, uint256 _amount 185 | ) returns (bool success) { 186 | 187 | // The controller of this contract can move tokens around at will, 188 | // this is important to recognize! Confirm that you trust the 189 | // controller of this contract, which in most situations should be 190 | // another open source smart contract or 0x0 191 | if (msg.sender != controller) { 192 | if (!transfersEnabled) throw; 193 | 194 | // The standard ERC 20 transferFrom functionality 195 | if (allowed[_from][msg.sender] < _amount) return false; 196 | allowed[_from][msg.sender] -= _amount; 197 | } 198 | return doTransfer(_from, _to, _amount); 199 | } 200 | 201 | /// @dev This is the actual transfer function in the token contract, it can 202 | /// only be called by other functions in this contract. 203 | /// @param _from The address holding the tokens being transferred 204 | /// @param _to The address of the recipient 205 | /// @param _amount The amount of tokens to be transferred 206 | /// @return True if the transfer was successful 207 | function doTransfer(address _from, address _to, uint _amount 208 | ) internal returns(bool) { 209 | 210 | if (_amount == 0) { 211 | return true; 212 | } 213 | 214 | if (parentSnapShotBlock >= getBlockNumber()) throw; 215 | 216 | // Do not allow transfer to 0x0 or the token contract itself 217 | if ((_to == 0) || (_to == address(this))) throw; 218 | 219 | // If the amount being transfered is more than the balance of the 220 | // account the transfer returns false 221 | var previousBalanceFrom = balanceOfAt(_from, getBlockNumber()); 222 | if (previousBalanceFrom < _amount) { 223 | return false; 224 | } 225 | 226 | // Alerts the token controller of the transfer 227 | if (isContract(controller)) { 228 | if (!TokenController(controller).onTransfer(_from, _to, _amount)) 229 | throw; 230 | } 231 | 232 | // First update the balance array with the new value for the address 233 | // sending the tokens 234 | updateValueAtNow(balances[_from], previousBalanceFrom - _amount); 235 | 236 | // Then update the balance array with the new value for the address 237 | // receiving the tokens 238 | var previousBalanceTo = balanceOfAt(_to, getBlockNumber()); 239 | if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow 240 | updateValueAtNow(balances[_to], previousBalanceTo + _amount); 241 | 242 | // An event to make the transfer easy to find on the blockchain 243 | Transfer(_from, _to, _amount); 244 | 245 | return true; 246 | } 247 | 248 | /// @param _owner The address that's balance is being requested 249 | /// @return The balance of `_owner` at the current block 250 | function balanceOf(address _owner) constant returns (uint256 balance) { 251 | return balanceOfAt(_owner, getBlockNumber()); 252 | } 253 | 254 | /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on 255 | /// its behalf. This is a modified version of the ERC20 approve function 256 | /// to be a little bit safer 257 | /// @param _spender The address of the account able to transfer the tokens 258 | /// @param _amount The amount of tokens to be approved for transfer 259 | /// @return True if the approval was successful 260 | function approve(address _spender, uint256 _amount) returns (bool success) { 261 | if (!transfersEnabled) throw; 262 | 263 | // To change the approve amount you first have to reduce the addresses` 264 | // allowance to zero by calling `approve(_spender,0)` if it is not 265 | // already 0 to mitigate the race condition described here: 266 | // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 267 | if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw; 268 | 269 | // Alerts the token controller of the approve function call 270 | if (isContract(controller)) { 271 | if (!TokenController(controller).onApprove(msg.sender, _spender, _amount)) 272 | throw; 273 | } 274 | 275 | allowed[msg.sender][_spender] = _amount; 276 | Approval(msg.sender, _spender, _amount); 277 | return true; 278 | } 279 | 280 | /// @dev This function makes it easy to read the `allowed[]` map 281 | /// @param _owner The address of the account that owns the token 282 | /// @param _spender The address of the account able to transfer the tokens 283 | /// @return Amount of remaining tokens of _owner that _spender is allowed 284 | /// to spend 285 | function allowance(address _owner, address _spender 286 | ) constant returns (uint256 remaining) { 287 | return allowed[_owner][_spender]; 288 | } 289 | 290 | /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on 291 | /// its behalf, and then a function is triggered in the contract that is 292 | /// being approved, `_spender`. This allows users to use their tokens to 293 | /// interact with contracts in one function call instead of two 294 | /// @param _spender The address of the contract able to transfer the tokens 295 | /// @param _amount The amount of tokens to be approved for transfer 296 | /// @return True if the function call was successful 297 | function approveAndCall(address _spender, uint256 _amount, bytes _extraData 298 | ) returns (bool success) { 299 | if (!approve(_spender, _amount)) throw; 300 | 301 | ApproveAndCallFallBack(_spender).receiveApproval( 302 | msg.sender, 303 | _amount, 304 | this, 305 | _extraData 306 | ); 307 | 308 | return true; 309 | } 310 | 311 | /// @dev This function makes it easy to get the total number of tokens 312 | /// @return The total number of tokens 313 | function totalSupply() constant returns (uint) { 314 | return totalSupplyAt(getBlockNumber()); 315 | } 316 | 317 | 318 | //////////////// 319 | // Query balance and totalSupply in History 320 | //////////////// 321 | 322 | /// @dev Queries the balance of `_owner` at a specific `_blockNumber` 323 | /// @param _owner The address from which the balance will be retrieved 324 | /// @param _blockNumber The block number when the balance is queried 325 | /// @return The balance at `_blockNumber` 326 | function balanceOfAt(address _owner, uint _blockNumber) constant 327 | returns (uint) { 328 | 329 | // These next few lines are used when the balance of the token is 330 | // requested before a check point was ever created for this token, it 331 | // requires that the `parentToken.balanceOfAt` be queried at the 332 | // genesis block for that token as this contains initial balance of 333 | // this token 334 | if ((balances[_owner].length == 0) 335 | || (balances[_owner][0].fromBlock > _blockNumber)) { 336 | if (address(parentToken) != 0) { 337 | return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock)); 338 | } else { 339 | // Has no parent 340 | return 0; 341 | } 342 | 343 | // This will return the expected balance during normal situations 344 | } else { 345 | return getValueAt(balances[_owner], _blockNumber); 346 | } 347 | } 348 | 349 | /// @notice Total amount of tokens at a specific `_blockNumber`. 350 | /// @param _blockNumber The block number when the totalSupply is queried 351 | /// @return The total amount of tokens at `_blockNumber` 352 | function totalSupplyAt(uint _blockNumber) constant returns(uint) { 353 | 354 | // These next few lines are used when the totalSupply of the token is 355 | // requested before a check point was ever created for this token, it 356 | // requires that the `parentToken.totalSupplyAt` be queried at the 357 | // genesis block for this token as that contains totalSupply of this 358 | // token at this block number. 359 | if ((totalSupplyHistory.length == 0) 360 | || (totalSupplyHistory[0].fromBlock > _blockNumber)) { 361 | if (address(parentToken) != 0) { 362 | return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock)); 363 | } else { 364 | return 0; 365 | } 366 | 367 | // This will return the expected totalSupply during normal situations 368 | } else { 369 | return getValueAt(totalSupplyHistory, _blockNumber); 370 | } 371 | } 372 | 373 | //////////////// 374 | // Clone Token Method 375 | //////////////// 376 | 377 | /// @notice Creates a new clone token with the initial distribution being 378 | /// this token at `_snapshotBlock` 379 | /// @param _cloneTokenName Name of the clone token 380 | /// @param _cloneDecimalUnits Number of decimals of the smallest unit 381 | /// @param _cloneTokenSymbol Symbol of the clone token 382 | /// @param _snapshotBlock Block when the distribution of the parent token is 383 | /// copied to set the initial distribution of the new clone token; 384 | /// if the block is zero than the actual block, the current block is used 385 | /// @param _transfersEnabled True if transfers are allowed in the clone 386 | /// @return The address of the new MiniMeToken Contract 387 | function createCloneToken( 388 | string _cloneTokenName, 389 | uint8 _cloneDecimalUnits, 390 | string _cloneTokenSymbol, 391 | uint _snapshotBlock, 392 | bool _transfersEnabled 393 | ) returns(address) { 394 | if (_snapshotBlock == 0) _snapshotBlock = getBlockNumber(); 395 | MiniMeToken cloneToken = tokenFactory.createCloneToken( 396 | this, 397 | _snapshotBlock, 398 | _cloneTokenName, 399 | _cloneDecimalUnits, 400 | _cloneTokenSymbol, 401 | _transfersEnabled 402 | ); 403 | 404 | cloneToken.changeController(msg.sender); 405 | 406 | // An event to make the token easy to find on the blockchain 407 | NewCloneToken(address(cloneToken), _snapshotBlock); 408 | return address(cloneToken); 409 | } 410 | 411 | //////////////// 412 | // Generate and destroy tokens 413 | //////////////// 414 | 415 | /// @notice Generates `_amount` tokens that are assigned to `_owner` 416 | /// @param _owner The address that will be assigned the new tokens 417 | /// @param _amount The quantity of tokens generated 418 | /// @return True if the tokens are generated correctly 419 | function generateTokens(address _owner, uint _amount 420 | ) onlyController returns (bool) { 421 | uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber()); 422 | if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow 423 | updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); 424 | var previousBalanceTo = balanceOf(_owner); 425 | if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow 426 | updateValueAtNow(balances[_owner], previousBalanceTo + _amount); 427 | Transfer(0, _owner, _amount); 428 | return true; 429 | } 430 | 431 | 432 | /// @notice Burns `_amount` tokens from `_owner` 433 | /// @param _owner The address that will lose the tokens 434 | /// @param _amount The quantity of tokens to burn 435 | /// @return True if the tokens are burned correctly 436 | function destroyTokens(address _owner, uint _amount 437 | ) onlyController returns (bool) { 438 | uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber()); 439 | if (curTotalSupply < _amount) throw; 440 | updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); 441 | var previousBalanceFrom = balanceOf(_owner); 442 | if (previousBalanceFrom < _amount) throw; 443 | updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); 444 | Transfer(_owner, 0, _amount); 445 | return true; 446 | } 447 | 448 | //////////////// 449 | // Enable tokens transfers 450 | //////////////// 451 | 452 | 453 | /// @notice Enables token holders to transfer their tokens freely if true 454 | /// @param _transfersEnabled True if transfers are allowed in the clone 455 | function enableTransfers(bool _transfersEnabled) onlyController { 456 | transfersEnabled = _transfersEnabled; 457 | } 458 | 459 | //////////////// 460 | // Internal helper functions to query and set a value in a snapshot array 461 | //////////////// 462 | 463 | /// @dev `getValueAt` retrieves the number of tokens at a given block number 464 | /// @param checkpoints The history of values being queried 465 | /// @param _block The block number to retrieve the value at 466 | /// @return The number of tokens being queried 467 | function getValueAt(Checkpoint[] storage checkpoints, uint _block 468 | ) constant internal returns (uint) { 469 | if (checkpoints.length == 0) return 0; 470 | 471 | // Shortcut for the actual value 472 | if (_block >= checkpoints[checkpoints.length-1].fromBlock) 473 | return checkpoints[checkpoints.length-1].value; 474 | if (_block < checkpoints[0].fromBlock) return 0; 475 | 476 | // Binary search of the value in the array 477 | uint min = 0; 478 | uint max = checkpoints.length-1; 479 | while (max > min) { 480 | uint mid = (max + min + 1)/ 2; 481 | if (checkpoints[mid].fromBlock<=_block) { 482 | min = mid; 483 | } else { 484 | max = mid-1; 485 | } 486 | } 487 | return checkpoints[min].value; 488 | } 489 | 490 | /// @dev `updateValueAtNow` used to update the `balances` map and the 491 | /// `totalSupplyHistory` 492 | /// @param checkpoints The history of data being updated 493 | /// @param _value The new number of tokens 494 | function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value 495 | ) internal { 496 | if ((checkpoints.length == 0) 497 | || (checkpoints[checkpoints.length -1].fromBlock < getBlockNumber())) { 498 | Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ]; 499 | newCheckPoint.fromBlock = uint128(getBlockNumber()); 500 | newCheckPoint.value = uint128(_value); 501 | } else { 502 | Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1]; 503 | oldCheckPoint.value = uint128(_value); 504 | } 505 | } 506 | 507 | /// @dev Internal function to determine if an address is a contract 508 | /// @param _addr The address being queried 509 | /// @return True if `_addr` is a contract 510 | function isContract(address _addr) constant internal returns(bool) { 511 | uint size; 512 | if (_addr == 0) return false; 513 | assembly { 514 | size := extcodesize(_addr) 515 | } 516 | return size>0; 517 | } 518 | 519 | /// @dev Helper function to return a min betwen the two uints 520 | function min(uint a, uint b) internal returns (uint) { 521 | return a < b ? a : b; 522 | } 523 | 524 | /// @notice The fallback function: If the contract's controller has not been 525 | /// set to 0, then the `proxyPayment` method is called which relays the 526 | /// ether and creates tokens as described in the token controller contract 527 | function () payable { 528 | if (isContract(controller)) { 529 | if (! TokenController(controller).proxyPayment.value(msg.value)(msg.sender)) 530 | throw; 531 | } else { 532 | throw; 533 | } 534 | } 535 | 536 | 537 | ////////// 538 | // Testing specific methods 539 | ////////// 540 | 541 | /// @notice This function is overridden by the test Mocks. 542 | function getBlockNumber() internal constant returns (uint256) { 543 | return block.number; 544 | } 545 | 546 | ////////// 547 | // Safety Methods 548 | ////////// 549 | 550 | /// @notice This method can be used by the controller to extract mistakenly 551 | /// sent tokens to this contract. 552 | /// @param _token The address of the token contract that you want to recover 553 | /// set to 0 in case you want to extract ether. 554 | function claimTokens(address _token) onlyController { 555 | if (_token == 0x0) { 556 | controller.transfer(this.balance); 557 | return; 558 | } 559 | 560 | ERC20Token token = ERC20Token(_token); 561 | uint balance = token.balanceOf(this); 562 | token.transfer(controller, balance); 563 | ClaimedTokens(_token, controller, balance); 564 | } 565 | 566 | //////////////// 567 | // Events 568 | //////////////// 569 | 570 | event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount); 571 | event Transfer(address indexed _from, address indexed _to, uint256 _amount); 572 | event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock); 573 | event Approval( 574 | address indexed _owner, 575 | address indexed _spender, 576 | uint256 _amount 577 | ); 578 | 579 | } 580 | 581 | 582 | //////////////// 583 | // MiniMeTokenFactory 584 | //////////////// 585 | 586 | /// @dev This contract is used to generate clone contracts from a contract. 587 | /// In solidity this is the way to create a contract from a contract of the 588 | /// same class 589 | contract MiniMeTokenFactory { 590 | 591 | /// @notice Update the DApp by creating a new token with new functionalities 592 | /// the msg.sender becomes the controller of this clone token 593 | /// @param _parentToken Address of the token being cloned 594 | /// @param _snapshotBlock Block of the parent token that will 595 | /// determine the initial distribution of the clone token 596 | /// @param _tokenName Name of the new token 597 | /// @param _decimalUnits Number of decimals of the new token 598 | /// @param _tokenSymbol Token Symbol for the new token 599 | /// @param _transfersEnabled If true, tokens will be able to be transferred 600 | /// @return The address of the new token contract 601 | function createCloneToken( 602 | address _parentToken, 603 | uint _snapshotBlock, 604 | string _tokenName, 605 | uint8 _decimalUnits, 606 | string _tokenSymbol, 607 | bool _transfersEnabled 608 | ) returns (MiniMeToken) { 609 | MiniMeToken newToken = new MiniMeToken( 610 | this, 611 | _parentToken, 612 | _snapshotBlock, 613 | _tokenName, 614 | _decimalUnits, 615 | _tokenSymbol, 616 | _transfersEnabled 617 | ); 618 | 619 | newToken.changeController(msg.sender); 620 | return newToken; 621 | } 622 | } 623 | -------------------------------------------------------------------------------- /contracts/MultiSigWallet.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. 5 | /// @author Stefan George - 6 | contract MultiSigWallet { 7 | 8 | uint constant public MAX_OWNER_COUNT = 50; 9 | 10 | event Confirmation(address indexed _sender, uint indexed _transactionId); 11 | event Revocation(address indexed _sender, uint indexed _transactionId); 12 | event Submission(uint indexed _transactionId); 13 | event Execution(uint indexed _transactionId); 14 | event ExecutionFailure(uint indexed _transactionId); 15 | event Deposit(address indexed _sender, uint _value); 16 | event OwnerAddition(address indexed _owner); 17 | event OwnerRemoval(address indexed _owner); 18 | event RequirementChange(uint _required); 19 | 20 | mapping (uint => Transaction) public transactions; 21 | mapping (uint => mapping (address => bool)) public confirmations; 22 | mapping (address => bool) public isOwner; 23 | address[] public owners; 24 | uint public required; 25 | uint public transactionCount; 26 | 27 | struct Transaction { 28 | address destination; 29 | uint value; 30 | bytes data; 31 | bool executed; 32 | } 33 | 34 | modifier onlyWallet() { 35 | if (msg.sender != address(this)) 36 | throw; 37 | _; 38 | } 39 | 40 | modifier ownerDoesNotExist(address owner) { 41 | if (isOwner[owner]) 42 | throw; 43 | _; 44 | } 45 | 46 | modifier ownerExists(address owner) { 47 | if (!isOwner[owner]) 48 | throw; 49 | _; 50 | } 51 | 52 | modifier transactionExists(uint transactionId) { 53 | if (transactions[transactionId].destination == 0) 54 | throw; 55 | _; 56 | } 57 | 58 | modifier confirmed(uint transactionId, address owner) { 59 | if (!confirmations[transactionId][owner]) 60 | throw; 61 | _; 62 | } 63 | 64 | modifier notConfirmed(uint transactionId, address owner) { 65 | if (confirmations[transactionId][owner]) 66 | throw; 67 | _; 68 | } 69 | 70 | modifier notExecuted(uint transactionId) { 71 | if (transactions[transactionId].executed) 72 | throw; 73 | _; 74 | } 75 | 76 | modifier notNull(address _address) { 77 | if (_address == 0) 78 | throw; 79 | _; 80 | } 81 | 82 | modifier validRequirement(uint ownerCount, uint _required) { 83 | if ( ownerCount > MAX_OWNER_COUNT 84 | || _required > ownerCount 85 | || _required == 0 86 | || ownerCount == 0) 87 | throw; 88 | _; 89 | } 90 | 91 | /// @dev Fallback function allows to deposit ether. 92 | function() 93 | payable 94 | { 95 | if (msg.value > 0) 96 | Deposit(msg.sender, msg.value); 97 | } 98 | 99 | /* 100 | * Public functions 101 | */ 102 | /// @dev Contract constructor sets initial owners and required number of confirmations. 103 | /// @param _owners List of initial owners. 104 | /// @param _required Number of required confirmations. 105 | function MultiSigWallet(address[] _owners, uint _required) 106 | public 107 | validRequirement(_owners.length, _required) 108 | { 109 | for (uint i=0; i<_owners.length; i++) { 110 | if (isOwner[_owners[i]] || _owners[i] == 0) 111 | throw; 112 | isOwner[_owners[i]] = true; 113 | } 114 | owners = _owners; 115 | required = _required; 116 | } 117 | 118 | /// @dev Allows to add a new owner. Transaction has to be sent by wallet. 119 | /// @param owner Address of new owner. 120 | function addOwner(address owner) 121 | public 122 | onlyWallet 123 | ownerDoesNotExist(owner) 124 | notNull(owner) 125 | validRequirement(owners.length + 1, required) 126 | { 127 | isOwner[owner] = true; 128 | owners.push(owner); 129 | OwnerAddition(owner); 130 | } 131 | 132 | /// @dev Allows to remove an owner. Transaction has to be sent by wallet. 133 | /// @param owner Address of owner. 134 | function removeOwner(address owner) 135 | public 136 | onlyWallet 137 | ownerExists(owner) 138 | { 139 | isOwner[owner] = false; 140 | for (uint i=0; i owners.length) 147 | changeRequirement(owners.length); 148 | OwnerRemoval(owner); 149 | } 150 | 151 | /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. 152 | /// @param owner Address of owner to be replaced. 153 | /// @param owner Address of new owner. 154 | function replaceOwner(address owner, address newOwner) 155 | public 156 | onlyWallet 157 | ownerExists(owner) 158 | ownerDoesNotExist(newOwner) 159 | { 160 | for (uint i=0; i. 18 | */ 19 | 20 | /// @title SGTExchanger Contract 21 | /// @author Jordi Baylina 22 | /// @dev This contract will be used to distribute SNT between SGT holders. 23 | /// SGT token is not transferable, and we just keep an accounting between all tokens 24 | /// deposited and the tokens collected. 25 | /// The controllerShip of SGT should be transferred to this contract before the 26 | /// contribution period starts. 27 | 28 | 29 | import "./MiniMeToken.sol"; 30 | import "./SafeMath.sol"; 31 | import "./Owned.sol"; 32 | import "./StatusContribution.sol"; 33 | import "./ERC20Token.sol"; 34 | 35 | contract SGTExchanger is TokenController, Owned { 36 | using SafeMath for uint256; 37 | 38 | mapping (address => uint256) public collected; 39 | uint256 public totalCollected; 40 | MiniMeToken public sgt; 41 | MiniMeToken public snt; 42 | StatusContribution public statusContribution; 43 | 44 | function SGTExchanger(address _sgt, address _snt, address _statusContribution) { 45 | sgt = MiniMeToken(_sgt); 46 | snt = MiniMeToken(_snt); 47 | statusContribution = StatusContribution(_statusContribution); 48 | } 49 | 50 | /// @notice This method should be called by the SGT holders to collect their 51 | /// corresponding SNTs 52 | function collect() public { 53 | uint256 finalizedBlock = statusContribution.finalizedBlock(); 54 | 55 | require(finalizedBlock != 0); 56 | require(getBlockNumber() > finalizedBlock); 57 | 58 | uint256 total = totalCollected.add(snt.balanceOf(address(this))); 59 | 60 | uint256 balance = sgt.balanceOfAt(msg.sender, finalizedBlock); 61 | 62 | // First calculate how much correspond to him 63 | uint256 amount = total.mul(balance).div(sgt.totalSupplyAt(finalizedBlock)); 64 | 65 | // And then subtract the amount already collected 66 | amount = amount.sub(collected[msg.sender]); 67 | 68 | require(amount > 0); // Notify the user that there are no tokens to exchange 69 | 70 | totalCollected = totalCollected.add(amount); 71 | collected[msg.sender] = collected[msg.sender].add(amount); 72 | 73 | assert(snt.transfer(msg.sender, amount)); 74 | 75 | TokensCollected(msg.sender, amount); 76 | } 77 | 78 | function proxyPayment(address) public payable returns (bool) { 79 | throw; 80 | } 81 | 82 | function onTransfer(address, address, uint256) public returns (bool) { 83 | return false; 84 | } 85 | 86 | function onApprove(address, address, uint256) public returns (bool) { 87 | return false; 88 | } 89 | 90 | ////////// 91 | // Testing specific methods 92 | ////////// 93 | 94 | /// @notice This function is overridden by the test Mocks. 95 | function getBlockNumber() internal constant returns (uint256) { 96 | return block.number; 97 | } 98 | 99 | ////////// 100 | // Safety Method 101 | ////////// 102 | 103 | /// @notice This method can be used by the controller to extract mistakenly 104 | /// sent tokens to this contract. 105 | /// @param _token The address of the token contract that you want to recover 106 | /// set to 0 in case you want to extract ether. 107 | function claimTokens(address _token) public onlyOwner { 108 | require(_token != address(snt)); 109 | if (_token == 0x0) { 110 | owner.transfer(this.balance); 111 | return; 112 | } 113 | 114 | ERC20Token token = ERC20Token(_token); 115 | uint256 balance = token.balanceOf(this); 116 | token.transfer(owner, balance); 117 | ClaimedTokens(_token, owner, balance); 118 | } 119 | 120 | event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount); 121 | event TokensCollected(address indexed _holder, uint256 _amount); 122 | 123 | } 124 | -------------------------------------------------------------------------------- /contracts/SNT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /* 4 | Copyright 2017, Jarrad Hope (Status Research & Development GmbH) 5 | */ 6 | 7 | 8 | import "./MiniMeToken.sol"; 9 | 10 | 11 | contract SNT is MiniMeToken { 12 | // @dev SNT constructor just parametrizes the MiniMeIrrevocableVestedToken constructor 13 | function SNT(address _tokenFactory) 14 | MiniMeToken( 15 | _tokenFactory, 16 | 0x0, // no parent token 17 | 0, // no snapshot block number from parent 18 | "Status Network Token", // Token name 19 | 18, // Decimals 20 | "SNT", // Symbol 21 | true // Enable transfers 22 | ) {} 23 | } 24 | -------------------------------------------------------------------------------- /contracts/SNTPlaceHolder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /* 4 | Copyright 2017, Jordi Baylina 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /// @title SNTPlaceholder Contract 21 | /// @author Jordi Baylina 22 | /// @dev The SNTPlaceholder contract will take control over the SNT after the contribution 23 | /// is finalized and before the Status Network is deployed. 24 | /// The contract allows for SNT transfers and transferFrom and implements the 25 | /// logic for transferring control of the token to the network when the offering 26 | /// asks it to do so. 27 | 28 | 29 | import "./MiniMeToken.sol"; 30 | import "./StatusContribution.sol"; 31 | import "./SafeMath.sol"; 32 | import "./Owned.sol"; 33 | import "./ERC20Token.sol"; 34 | 35 | 36 | contract SNTPlaceHolder is TokenController, Owned { 37 | using SafeMath for uint256; 38 | 39 | MiniMeToken public snt; 40 | StatusContribution public contribution; 41 | uint256 public activationTime; 42 | address public sgtExchanger; 43 | 44 | /// @notice Constructor 45 | /// @param _owner Trusted owner for this contract. 46 | /// @param _snt SNT token contract address 47 | /// @param _contribution StatusContribution contract address 48 | /// @param _sgtExchanger SGT-SNT Exchange address. (During the first week 49 | /// only this exchanger will be able to move tokens) 50 | function SNTPlaceHolder(address _owner, address _snt, address _contribution, address _sgtExchanger) { 51 | owner = _owner; 52 | snt = MiniMeToken(_snt); 53 | contribution = StatusContribution(_contribution); 54 | sgtExchanger = _sgtExchanger; 55 | } 56 | 57 | /// @notice The owner of this contract can change the controller of the SNT token 58 | /// Please, be sure that the owner is a trusted agent or 0x0 address. 59 | /// @param _newController The address of the new controller 60 | 61 | function changeController(address _newController) public onlyOwner { 62 | snt.changeController(_newController); 63 | ControllerChanged(_newController); 64 | } 65 | 66 | 67 | ////////// 68 | // MiniMe Controller Interface functions 69 | ////////// 70 | 71 | // In between the offering and the network. Default settings for allowing token transfers. 72 | function proxyPayment(address) public payable returns (bool) { 73 | return false; 74 | } 75 | 76 | function onTransfer(address _from, address, uint256) public returns (bool) { 77 | return transferable(_from); 78 | } 79 | 80 | function onApprove(address _from, address, uint256) public returns (bool) { 81 | return transferable(_from); 82 | } 83 | 84 | function transferable(address _from) internal returns (bool) { 85 | // Allow the exchanger to work from the beginning 86 | if (activationTime == 0) { 87 | uint256 f = contribution.finalizedTime(); 88 | if (f > 0) { 89 | activationTime = f.add(1 weeks); 90 | } else { 91 | return false; 92 | } 93 | } 94 | return (getTime() > activationTime) || (_from == sgtExchanger); 95 | } 96 | 97 | 98 | ////////// 99 | // Testing specific methods 100 | ////////// 101 | 102 | /// @notice This function is overrided by the test Mocks. 103 | function getTime() internal returns (uint256) { 104 | return now; 105 | } 106 | 107 | 108 | ////////// 109 | // Safety Methods 110 | ////////// 111 | 112 | /// @notice This method can be used by the controller to extract mistakenly 113 | /// sent tokens to this contract. 114 | /// @param _token The address of the token contract that you want to recover 115 | /// set to 0 in case you want to extract ether. 116 | function claimTokens(address _token) public onlyOwner { 117 | if (snt.controller() == address(this)) { 118 | snt.claimTokens(_token); 119 | } 120 | if (_token == 0x0) { 121 | owner.transfer(this.balance); 122 | return; 123 | } 124 | 125 | ERC20Token token = ERC20Token(_token); 126 | uint256 balance = token.balanceOf(this); 127 | token.transfer(owner, balance); 128 | ClaimedTokens(_token, owner, balance); 129 | } 130 | 131 | event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount); 132 | event ControllerChanged(address indexed _newController); 133 | } 134 | -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | 4 | /** 5 | * Math operations with safety checks 6 | */ 7 | library SafeMath { 8 | function mul(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 div(uint a, uint b) internal returns (uint) { 15 | // assert(b > 0); // Solidity automatically throws when dividing by 0 16 | uint c = a / b; 17 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 18 | return c; 19 | } 20 | 21 | function sub(uint a, uint b) internal returns (uint) { 22 | assert(b <= a); 23 | return a - b; 24 | } 25 | 26 | function add(uint a, uint b) internal returns (uint) { 27 | uint c = a + b; 28 | assert(c >= a); 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 | -------------------------------------------------------------------------------- /contracts/StatusContribution.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /* 4 | Copyright 2017, Jordi Baylina 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /// @title StatusContribution Contract 21 | /// @author Jordi Baylina 22 | /// @dev This contract will be the SNT controller during the contribution period. 23 | /// This contract will determine the rules during this period. 24 | /// Final users will generally not interact directly with this contract. ETH will 25 | /// be sent to the SNT token contract. The ETH is sent to this contract and from here, 26 | /// ETH is sent to the contribution walled and SNTs are mined according to the defined 27 | /// rules. 28 | 29 | 30 | import "./Owned.sol"; 31 | import "./MiniMeToken.sol"; 32 | import "./DynamicCeiling.sol"; 33 | import "./SafeMath.sol"; 34 | import "./ERC20Token.sol"; 35 | 36 | 37 | contract StatusContribution is Owned, TokenController { 38 | using SafeMath for uint256; 39 | 40 | uint256 constant public failSafeLimit = 300000 ether; 41 | uint256 constant public maxGuaranteedLimit = 30000 ether; 42 | uint256 constant public exchangeRate = 10000; 43 | uint256 constant public maxGasPrice = 50000000000; 44 | uint256 constant public maxCallFrequency = 100; 45 | 46 | MiniMeToken public SGT; 47 | MiniMeToken public SNT; 48 | uint256 public startBlock; 49 | uint256 public endBlock; 50 | 51 | address public destEthDevs; 52 | 53 | address public destTokensDevs; 54 | address public destTokensReserve; 55 | uint256 public maxSGTSupply; 56 | address public destTokensSgt; 57 | DynamicCeiling public dynamicCeiling; 58 | 59 | address public sntController; 60 | 61 | mapping (address => uint256) public guaranteedBuyersLimit; 62 | mapping (address => uint256) public guaranteedBuyersBought; 63 | 64 | uint256 public totalGuaranteedCollected; 65 | uint256 public totalNormalCollected; 66 | 67 | uint256 public finalizedBlock; 68 | uint256 public finalizedTime; 69 | 70 | mapping (address => uint256) public lastCallBlock; 71 | 72 | bool public paused; 73 | 74 | modifier initialized() { 75 | require(address(SNT) != 0x0); 76 | _; 77 | } 78 | 79 | modifier contributionOpen() { 80 | require(getBlockNumber() >= startBlock && 81 | getBlockNumber() <= endBlock && 82 | finalizedBlock == 0 && 83 | address(SNT) != 0x0); 84 | _; 85 | } 86 | 87 | modifier notPaused() { 88 | require(!paused); 89 | _; 90 | } 91 | 92 | function StatusContribution() { 93 | paused = false; 94 | } 95 | 96 | 97 | /// @notice This method should be called by the owner before the contribution 98 | /// period starts This initializes most of the parameters 99 | /// @param _snt Address of the SNT token contract 100 | /// @param _sntController Token controller for the SNT that will be transferred after 101 | /// the contribution finalizes. 102 | /// @param _startBlock Block when the contribution period starts 103 | /// @param _endBlock The last block that the contribution period is active 104 | /// @param _dynamicCeiling Address of the contract that controls the ceiling 105 | /// @param _destEthDevs Destination address where the contribution ether is sent 106 | /// @param _destTokensReserve Address where the tokens for the reserve are sent 107 | /// @param _destTokensSgt Address of the exchanger SGT-SNT where the SNT are sent 108 | /// to be distributed to the SGT holders. 109 | /// @param _destTokensDevs Address where the tokens for the dev are sent 110 | /// @param _sgt Address of the SGT token contract 111 | /// @param _maxSGTSupply Quantity of SGT tokens that would represent 10% of status. 112 | function initialize( 113 | address _snt, 114 | address _sntController, 115 | 116 | uint256 _startBlock, 117 | uint256 _endBlock, 118 | 119 | address _dynamicCeiling, 120 | 121 | address _destEthDevs, 122 | 123 | address _destTokensReserve, 124 | address _destTokensSgt, 125 | address _destTokensDevs, 126 | 127 | address _sgt, 128 | uint256 _maxSGTSupply 129 | ) public onlyOwner { 130 | // Initialize only once 131 | require(address(SNT) == 0x0); 132 | 133 | SNT = MiniMeToken(_snt); 134 | require(SNT.totalSupply() == 0); 135 | require(SNT.controller() == address(this)); 136 | require(SNT.decimals() == 18); // Same amount of decimals as ETH 137 | 138 | require(_sntController != 0x0); 139 | sntController = _sntController; 140 | 141 | require(_startBlock >= getBlockNumber()); 142 | require(_startBlock < _endBlock); 143 | startBlock = _startBlock; 144 | endBlock = _endBlock; 145 | 146 | require(_dynamicCeiling != 0x0); 147 | dynamicCeiling = DynamicCeiling(_dynamicCeiling); 148 | 149 | require(_destEthDevs != 0x0); 150 | destEthDevs = _destEthDevs; 151 | 152 | require(_destTokensReserve != 0x0); 153 | destTokensReserve = _destTokensReserve; 154 | 155 | require(_destTokensSgt != 0x0); 156 | destTokensSgt = _destTokensSgt; 157 | 158 | require(_destTokensDevs != 0x0); 159 | destTokensDevs = _destTokensDevs; 160 | 161 | require(_sgt != 0x0); 162 | SGT = MiniMeToken(_sgt); 163 | 164 | require(_maxSGTSupply >= MiniMeToken(SGT).totalSupply()); 165 | maxSGTSupply = _maxSGTSupply; 166 | } 167 | 168 | /// @notice Sets the limit for a guaranteed address. All the guaranteed addresses 169 | /// will be able to get SNTs during the contribution period with his own 170 | /// specific limit. 171 | /// This method should be called by the owner after the initialization 172 | /// and before the contribution starts. 173 | /// @param _th Guaranteed address 174 | /// @param _limit Limit for the guaranteed address. 175 | function setGuaranteedAddress(address _th, uint256 _limit) public initialized onlyOwner { 176 | require(getBlockNumber() < startBlock); 177 | require(_limit > 0 && _limit <= maxGuaranteedLimit); 178 | guaranteedBuyersLimit[_th] = _limit; 179 | GuaranteedAddress(_th, _limit); 180 | } 181 | 182 | /// @notice If anybody sends Ether directly to this contract, consider he is 183 | /// getting SNTs. 184 | function () public payable notPaused { 185 | proxyPayment(msg.sender); 186 | } 187 | 188 | 189 | ////////// 190 | // MiniMe Controller functions 191 | ////////// 192 | 193 | /// @notice This method will generally be called by the SNT token contract to 194 | /// acquire SNTs. Or directly from third parties that want to acquire SNTs in 195 | /// behalf of a token holder. 196 | /// @param _th SNT holder where the SNTs will be minted. 197 | function proxyPayment(address _th) public payable notPaused initialized contributionOpen returns (bool) { 198 | require(_th != 0x0); 199 | if (guaranteedBuyersLimit[_th] > 0) { 200 | buyGuaranteed(_th); 201 | } else { 202 | buyNormal(_th); 203 | } 204 | return true; 205 | } 206 | 207 | function onTransfer(address, address, uint256) public returns (bool) { 208 | return false; 209 | } 210 | 211 | function onApprove(address, address, uint256) public returns (bool) { 212 | return false; 213 | } 214 | 215 | function buyNormal(address _th) internal { 216 | require(tx.gasprice <= maxGasPrice); 217 | 218 | // Antispam mechanism 219 | address caller; 220 | if (msg.sender == address(SNT)) { 221 | caller = _th; 222 | } else { 223 | caller = msg.sender; 224 | } 225 | 226 | // Do not allow contracts to game the system 227 | require(!isContract(caller)); 228 | 229 | require(getBlockNumber().sub(lastCallBlock[caller]) >= maxCallFrequency); 230 | lastCallBlock[caller] = getBlockNumber(); 231 | 232 | uint256 toCollect = dynamicCeiling.toCollect(totalNormalCollected); 233 | 234 | uint256 toFund; 235 | if (msg.value <= toCollect) { 236 | toFund = msg.value; 237 | } else { 238 | toFund = toCollect; 239 | } 240 | 241 | totalNormalCollected = totalNormalCollected.add(toFund); 242 | doBuy(_th, toFund, false); 243 | } 244 | 245 | function buyGuaranteed(address _th) internal { 246 | uint256 toCollect = guaranteedBuyersLimit[_th]; 247 | 248 | uint256 toFund; 249 | if (guaranteedBuyersBought[_th].add(msg.value) > toCollect) { 250 | toFund = toCollect.sub(guaranteedBuyersBought[_th]); 251 | } else { 252 | toFund = msg.value; 253 | } 254 | 255 | guaranteedBuyersBought[_th] = guaranteedBuyersBought[_th].add(toFund); 256 | totalGuaranteedCollected = totalGuaranteedCollected.add(toFund); 257 | doBuy(_th, toFund, true); 258 | } 259 | 260 | function doBuy(address _th, uint256 _toFund, bool _guaranteed) internal { 261 | assert(msg.value >= _toFund); // Not needed, but double check. 262 | assert(totalCollected() <= failSafeLimit); 263 | 264 | if (_toFund > 0) { 265 | uint256 tokensGenerated = _toFund.mul(exchangeRate); 266 | assert(SNT.generateTokens(_th, tokensGenerated)); 267 | destEthDevs.transfer(_toFund); 268 | NewSale(_th, _toFund, tokensGenerated, _guaranteed); 269 | } 270 | 271 | uint256 toReturn = msg.value.sub(_toFund); 272 | if (toReturn > 0) { 273 | // If the call comes from the Token controller, 274 | // then we return it to the token Holder. 275 | // Otherwise we return to the sender. 276 | if (msg.sender == address(SNT)) { 277 | _th.transfer(toReturn); 278 | } else { 279 | msg.sender.transfer(toReturn); 280 | } 281 | } 282 | } 283 | 284 | // NOTE on Percentage format 285 | // Right now, Solidity does not support decimal numbers. (This will change very soon) 286 | // So in this contract we use a representation of a percentage that consist in 287 | // expressing the percentage in "x per 10**18" 288 | // This format has a precision of 16 digits for a percent. 289 | // Examples: 290 | // 3% = 3*(10**16) 291 | // 100% = 100*(10**16) = 10**18 292 | // 293 | // To get a percentage of a value we do it by first multiplying it by the percentage in (x per 10^18) 294 | // and then divide it by 10**18 295 | // 296 | // Y * X(in x per 10**18) 297 | // X% of Y = ------------------------- 298 | // 100(in x per 10**18) 299 | // 300 | 301 | 302 | /// @notice This method will can be called by the owner before the contribution period 303 | /// end or by anybody after the `endBlock`. This method finalizes the contribution period 304 | /// by creating the remaining tokens and transferring the controller to the configured 305 | /// controller. 306 | function finalize() public initialized { 307 | require(getBlockNumber() >= startBlock); 308 | require(msg.sender == owner || getBlockNumber() > endBlock); 309 | require(finalizedBlock == 0); 310 | 311 | // Do not allow termination until all curves revealed. 312 | require(dynamicCeiling.allRevealed()); 313 | 314 | // Allow premature finalization if final limit is reached 315 | if (getBlockNumber() <= endBlock) { 316 | var (,lastLimit,,) = dynamicCeiling.curves(dynamicCeiling.revealedCurves().sub(1)); 317 | require(totalNormalCollected >= lastLimit); 318 | } 319 | 320 | finalizedBlock = getBlockNumber(); 321 | finalizedTime = now; 322 | 323 | uint256 percentageToSgt; 324 | if (SGT.totalSupply() >= maxSGTSupply) { 325 | percentageToSgt = percent(10); // 10% 326 | } else { 327 | 328 | // 329 | // SGT.totalSupply() 330 | // percentageToSgt = 10% * ------------------- 331 | // maxSGTSupply 332 | // 333 | percentageToSgt = percent(10).mul(SGT.totalSupply()).div(maxSGTSupply); 334 | } 335 | 336 | uint256 percentageToDevs = percent(20); // 20% 337 | 338 | 339 | // 340 | // % To Contributors = 41% + (10% - % to SGT holders) 341 | // 342 | uint256 percentageToContributors = percent(41).add(percent(10).sub(percentageToSgt)); 343 | 344 | uint256 percentageToReserve = percent(29); 345 | 346 | 347 | // SNT.totalSupply() -> Tokens minted during the contribution 348 | // totalTokens -> Total tokens that should be after the allocation 349 | // of devTokens, sgtTokens and reserve 350 | // percentageToContributors -> Which percentage should go to the 351 | // contribution participants 352 | // (x per 10**18 format) 353 | // percent(100) -> 100% in (x per 10**18 format) 354 | // 355 | // percentageToContributors 356 | // SNT.totalSupply() = -------------------------- * totalTokens => 357 | // percent(100) 358 | // 359 | // 360 | // percent(100) 361 | // => totalTokens = ---------------------------- * SNT.totalSupply() 362 | // percentageToContributors 363 | // 364 | uint256 totalTokens = SNT.totalSupply().mul(percent(100)).div(percentageToContributors); 365 | 366 | 367 | // Generate tokens for SGT Holders. 368 | 369 | // 370 | // percentageToReserve 371 | // reserveTokens = ----------------------- * totalTokens 372 | // percentage(100) 373 | // 374 | assert(SNT.generateTokens( 375 | destTokensReserve, 376 | totalTokens.mul(percentageToReserve).div(percent(100)))); 377 | 378 | // 379 | // percentageToSgt 380 | // sgtTokens = ----------------------- * totalTokens 381 | // percentage(100) 382 | // 383 | assert(SNT.generateTokens( 384 | destTokensSgt, 385 | totalTokens.mul(percentageToSgt).div(percent(100)))); 386 | 387 | 388 | // 389 | // percentageToDevs 390 | // devTokens = ----------------------- * totalTokens 391 | // percentage(100) 392 | // 393 | assert(SNT.generateTokens( 394 | destTokensDevs, 395 | totalTokens.mul(percentageToDevs).div(percent(100)))); 396 | 397 | SNT.changeController(sntController); 398 | 399 | Finalized(); 400 | } 401 | 402 | function percent(uint256 p) internal returns (uint256) { 403 | return p.mul(10**16); 404 | } 405 | 406 | /// @dev Internal function to determine if an address is a contract 407 | /// @param _addr The address being queried 408 | /// @return True if `_addr` is a contract 409 | function isContract(address _addr) constant internal returns (bool) { 410 | if (_addr == 0) return false; 411 | uint256 size; 412 | assembly { 413 | size := extcodesize(_addr) 414 | } 415 | return (size > 0); 416 | } 417 | 418 | 419 | ////////// 420 | // Constant functions 421 | ////////// 422 | 423 | /// @return Total tokens issued in weis. 424 | function tokensIssued() public constant returns (uint256) { 425 | return SNT.totalSupply(); 426 | } 427 | 428 | /// @return Total Ether collected. 429 | function totalCollected() public constant returns (uint256) { 430 | return totalNormalCollected.add(totalGuaranteedCollected); 431 | } 432 | 433 | 434 | ////////// 435 | // Testing specific methods 436 | ////////// 437 | 438 | /// @notice This function is overridden by the test Mocks. 439 | function getBlockNumber() internal constant returns (uint256) { 440 | return block.number; 441 | } 442 | 443 | 444 | ////////// 445 | // Safety Methods 446 | ////////// 447 | 448 | /// @notice This method can be used by the controller to extract mistakenly 449 | /// sent tokens to this contract. 450 | /// @param _token The address of the token contract that you want to recover 451 | /// set to 0 in case you want to extract ether. 452 | function claimTokens(address _token) public onlyOwner { 453 | if (SNT.controller() == address(this)) { 454 | SNT.claimTokens(_token); 455 | } 456 | if (_token == 0x0) { 457 | owner.transfer(this.balance); 458 | return; 459 | } 460 | 461 | ERC20Token token = ERC20Token(_token); 462 | uint256 balance = token.balanceOf(this); 463 | token.transfer(owner, balance); 464 | ClaimedTokens(_token, owner, balance); 465 | } 466 | 467 | 468 | /// @notice Pauses the contribution if there is any issue 469 | function pauseContribution() onlyOwner { 470 | paused = true; 471 | } 472 | 473 | /// @notice Resumes the contribution 474 | function resumeContribution() onlyOwner { 475 | paused = false; 476 | } 477 | 478 | event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount); 479 | event NewSale(address indexed _th, uint256 _amount, uint256 _tokens, bool _guaranteed); 480 | event GuaranteedAddress(address indexed _th, uint256 _limit); 481 | event Finalized(); 482 | } 483 | -------------------------------------------------------------------------------- /contracts/test/DevTokensHolderMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import '../DevTokensHolder.sol'; 4 | 5 | // @dev DevTokensHolderMock mocks current block number 6 | 7 | contract DevTokensHolderMock is DevTokensHolder { 8 | 9 | uint mock_time; 10 | 11 | function DevTokensHolderMock(address _owner, address _contribution, address _snt) 12 | DevTokensHolder(_owner, _contribution, _snt) { 13 | mock_time = now; 14 | } 15 | 16 | function getTime() internal returns (uint) { 17 | return mock_time; 18 | } 19 | 20 | function setMockedTime(uint _t) { 21 | mock_time = _t; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/ExternalToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "../MiniMeToken.sol"; 4 | 5 | contract ExternalToken is MiniMeToken { 6 | 7 | function ExternalToken(address _tokenFactory) 8 | MiniMeToken( 9 | _tokenFactory, 10 | 0x0, // no parent token 11 | 0, // no snapshot block number from parent 12 | "External Token for testing", // Token name 13 | 1, // Decimals 14 | "EXT", // Symbol 15 | true // Enable transfers 16 | ) {} 17 | } 18 | -------------------------------------------------------------------------------- /contracts/test/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 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 | -------------------------------------------------------------------------------- /contracts/test/SGTExchangerMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import '../SGTExchanger.sol'; 4 | 5 | // @dev SGTExchangerMock mocks current block number 6 | 7 | contract SGTExchangerMock is SGTExchanger { 8 | 9 | function SGTExchangerMock(address _sgt, address _snt, address _statusContribution) 10 | SGTExchanger(_sgt, _snt, _statusContribution) {} 11 | 12 | function getBlockNumber() internal constant returns (uint) { 13 | return mock_blockNumber; 14 | } 15 | 16 | function setMockedBlockNumber(uint _b) public { 17 | mock_blockNumber = _b; 18 | } 19 | 20 | uint public mock_blockNumber = 1; 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test/SGTMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import '../SGT.sol'; 4 | 5 | // @dev SGTMock mocks current block number 6 | 7 | contract SGTMock is SGT { 8 | 9 | function SGTMock(address _tokenFactory) SGT(_tokenFactory) {} 10 | 11 | function getBlockNumber() internal constant returns (uint) { 12 | return mock_blockNumber; 13 | } 14 | 15 | function setMockedBlockNumber(uint _b) public { 16 | mock_blockNumber = _b; 17 | } 18 | 19 | uint mock_blockNumber = 1; 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/SNTMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import '../SNT.sol'; 4 | 5 | // @dev SNTMock mocks current block number 6 | 7 | contract SNTMock is SNT { 8 | 9 | function SNTMock(address _tokenFactory) SNT(_tokenFactory) {} 10 | 11 | function getBlockNumber() internal constant returns (uint) { 12 | return mock_blockNumber; 13 | } 14 | 15 | function setMockedBlockNumber(uint _b) public { 16 | mock_blockNumber = _b; 17 | } 18 | 19 | uint mock_blockNumber = 1; 20 | } 21 | -------------------------------------------------------------------------------- /contracts/test/SNTPlaceHolderMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import '../SNTPlaceHolder.sol'; 4 | 5 | // @dev SNTPlaceHolderMock mocks current block number 6 | 7 | contract SNTPlaceHolderMock is SNTPlaceHolder { 8 | 9 | uint mock_time; 10 | 11 | function SNTPlaceHolderMock(address _owner, address _snt, address _contribution, address _sgtExchanger) 12 | SNTPlaceHolder(_owner, _snt, _contribution, _sgtExchanger) { 13 | mock_time = now; 14 | } 15 | 16 | function getTime() internal returns (uint) { 17 | return mock_time; 18 | } 19 | 20 | function setMockedTime(uint _t) public { 21 | mock_time = _t; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/test/StatusContributionMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import '../StatusContribution.sol'; 4 | 5 | // @dev StatusContributionMock mocks current block number 6 | 7 | contract StatusContributionMock is StatusContribution { 8 | 9 | function StatusContributionMock() StatusContribution() {} 10 | 11 | function getBlockNumber() internal constant returns (uint) { 12 | return mock_blockNumber; 13 | } 14 | 15 | function setMockedBlockNumber(uint _b) public { 16 | mock_blockNumber = _b; 17 | } 18 | 19 | uint mock_blockNumber = 1; 20 | } 21 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const randomBytes = require("random-bytes"); 2 | 3 | const MultiSigWallet = artifacts.require("MultiSigWallet"); 4 | const MiniMeTokenFactory = artifacts.require("MiniMeTokenFactory"); 5 | const SGT = artifacts.require("SGT"); 6 | const SNT = artifacts.require("SNT"); 7 | const StatusContribution= artifacts.require("StatusContribution"); 8 | const ContributionWallet = artifacts.require("ContributionWallet"); 9 | const DevTokensHolder = artifacts.require("DevTokensHolder"); 10 | const SGTExchanger = artifacts.require("SGTExchanger"); 11 | const DynamicCeiling = artifacts.require("DynamicCeiling"); 12 | const SNTPlaceHolder = artifacts.require("SNTPlaceHolder"); 13 | 14 | 15 | // Set hidden curves 16 | const setHiddenCurves = async function(dynamicCeiling, curves, nHiddenCurves) { 17 | let hashes = []; 18 | let i = 0; 19 | for (let c of curves) { 20 | let salt = await randomBytes(32); 21 | console.log(`Curve ${i} has salt: ${salt.toString("hex")}`); 22 | let h = await dynamicCeiling.calculateHash(c[0], c[1], c[2], i === curves.length - 1, salt); 23 | hashes.push(h); 24 | i += 1; 25 | } 26 | for (; i < nHiddenCurves; i += 1) { 27 | let salt = randomBytes(32); 28 | hashes.push(web3.sha3(salt)); 29 | } 30 | await dynamicCeiling.setHiddenCurves(hashes); 31 | console.log(`${i} curves set!`); 32 | }; 33 | 34 | 35 | // All of these constants need to be configured before deploy 36 | const addressOwner = "0xf93df8c288b9020e76583a6997362e89e0599e99"; 37 | const addressesStatus = [ 38 | "0x2ca9d4d0fd9622b08de76c1d484e69a6311db765", 39 | ]; 40 | const multisigStatusReqs = 1 41 | const addressesCommunity = [ 42 | "0x166ddbcfe4d5849b0c62063747966a13706a4af7", 43 | ]; 44 | const multisigCommunityReqs = 1 45 | const addressesReserve = [ 46 | "0x4781fee94e7257ffb6e3a3dcc5f8571ddcc02109", 47 | ]; 48 | const multisigReserveReqs = 1 49 | const addressesDevs = [ 50 | "0xcee9f54a23324867d8537589ba8dc6c8a6e9d0b9", 51 | ]; 52 | const multisigDevsReqs = 1 53 | const addressSGT = ""; 54 | 55 | const startBlock = 3800000; 56 | const endBlock = 3900000; 57 | 58 | const maxSGTSupply = 500000000; 59 | 60 | const curves = [ 61 | [web3.toWei(1000), 30, 10**12], 62 | [web3.toWei(21000), 30, 10**12], 63 | [web3.toWei(61000), 30, 10**12], 64 | ]; 65 | const nHiddenCurves = 7; 66 | 67 | 68 | module.exports = async function(deployer, network, accounts) { 69 | if (network === "development") return; // Don't deploy on tests 70 | 71 | // MultiSigWallet send 72 | let multisigStatusFuture = MultiSigWallet.new(addressesStatus, multisigStatusReqs); 73 | let multisigCommunityFuture = MultiSigWallet.new(addressesCommunity, multisigCommunityReqs); 74 | let multisigReserveFuture = MultiSigWallet.new(addressesReserve, multisigReserveReqs); 75 | let multisigDevsFuture = MultiSigWallet.new(addressesDevs, multisigDevsReqs); 76 | // MiniMeTokenFactory send 77 | let miniMeTokenFactoryFuture = MiniMeTokenFactory.new(); 78 | 79 | // MultiSigWallet wait 80 | let multisigStatus = await multisigStatusFuture; 81 | console.log("\nMultiSigWallet Status: " + multisigStatus.address); 82 | let multisigCommunity = await multisigCommunityFuture; 83 | console.log("MultiSigWallet Community: " + multisigCommunity.address); 84 | let multisigReserve = await multisigReserveFuture; 85 | console.log("MultiSigWallet Reserve: " + multisigReserve.address); 86 | let multisigDevs = await multisigDevsFuture; 87 | console.log("MultiSigWallet Devs: " + multisigDevs.address); 88 | // MiniMeTokenFactory wait 89 | let miniMeTokenFactory = await miniMeTokenFactoryFuture; 90 | console.log("MiniMeTokenFactory: " + miniMeTokenFactory.address); 91 | console.log(); 92 | 93 | // SGT send 94 | let sgtFuture; 95 | if (addressSGT.length === 0) { // Testnet 96 | sgtFuture = SGT.new(miniMeTokenFactory.address); 97 | } else { 98 | sgtFuture = SGT.at(addressSGT); 99 | } 100 | // SNT send 101 | let sntFuture = SNT.new(miniMeTokenFactory.address); 102 | // StatusContribution send 103 | let statusContributionFuture = StatusContribution.new(); 104 | 105 | // SGT wait 106 | let sgt = await sgtFuture; 107 | console.log("SGT: " + sgt.address); 108 | // SNT wait 109 | let snt = await sntFuture; 110 | console.log("SNT: " + snt.address); 111 | // StatusContribution wait 112 | let statusContribution = await statusContributionFuture; 113 | console.log("StatusContribution: " + statusContribution.address); 114 | console.log(); 115 | 116 | // SNT initialize checkpoints for 0th TX gas savings 117 | await snt.generateTokens('0x0', 1); 118 | await snt.destroyTokens('0x0', 1); 119 | 120 | // SNT changeController send 121 | let sntChangeControllerFuture = snt.changeController(statusContribution.address); 122 | // ContributionWallet send 123 | let contributionWalletFuture = ContributionWallet.new( 124 | multisigStatus.address, 125 | endBlock, 126 | statusContribution.address); 127 | // DevTokensHolder send 128 | let devTokensHolderFuture = DevTokensHolder.new( 129 | multisigDevs.address, 130 | statusContribution.address, 131 | snt.address); 132 | // SGTExchanger send 133 | let sgtExchangerFuture = SGTExchanger.new(sgt.address, snt.address, statusContribution.address); 134 | // DynamicCeiling send 135 | let dynamicCeilingFuture = DynamicCeiling.new(addressOwner, statusContribution.address); 136 | 137 | // SNT changeController wait 138 | await sntChangeControllerFuture; 139 | console.log("SNT changed controller!"); 140 | // ContributionWallet wait 141 | let contributionWallet = await contributionWalletFuture; 142 | console.log("ContributionWallet: " + contributionWallet.address); 143 | // DevTokensHolder wait 144 | let devTokensHolder = await devTokensHolderFuture; 145 | console.log("DevTokensHolder: " + devTokensHolder.address); 146 | // SGTExchanger wait 147 | let sgtExchanger = await sgtExchangerFuture; 148 | console.log("SGTExchanger: " + sgtExchanger.address); 149 | // DynamicCeiling wait 150 | let dynamicCeiling = await dynamicCeilingFuture; 151 | console.log("DynamicCeiling: " + dynamicCeiling.address); 152 | console.log(); 153 | 154 | // DynamicCeiling setHiddenCurves send 155 | let dynamicCeilingSetHiddenCurvesFuture = setHiddenCurves(dynamicCeiling, curves, nHiddenCurves); 156 | console.log(); 157 | // SNTPlaceHolder send 158 | let sntPlaceHolderFuture = SNTPlaceHolder.new( 159 | multisigCommunity.address, 160 | snt.address, 161 | statusContribution.address, 162 | sgtExchanger.address); 163 | 164 | // DynamicCeiling setHiddenCurves wait 165 | await dynamicCeilingSetHiddenCurvesFuture; 166 | console.log("DynamicCeiling hidden curves set!"); 167 | // SNTPlaceHolder wait 168 | let sntPlaceHolder = await sntPlaceHolderFuture; 169 | console.log("SNTPlaceHolder: " + sntPlaceHolder.address); 170 | console.log(); 171 | 172 | // StatusContribution initialize send/wait 173 | await statusContribution.initialize( 174 | snt.address, 175 | sntPlaceHolder.address, 176 | 177 | startBlock, 178 | endBlock, 179 | 180 | dynamicCeiling.address, 181 | 182 | contributionWallet.address, 183 | 184 | multisigReserve.address, 185 | sgtExchanger.address, 186 | devTokensHolder.address, 187 | 188 | sgt.address, 189 | maxSGTSupply); 190 | console.log("StatusContribution initialized!"); 191 | }; 192 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | warn_redundant_casts = True 3 | warn_unused_ignores = True 4 | strict_optional = True 5 | disallow_untyped_calls = True 6 | disallow_untyped_defs = True 7 | disallow_subclassing_any = True 8 | check_untyped_defs = True 9 | warn_return_any = True 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "status-network-token", 3 | "version": "0.1.0", 4 | "description": "Status network token", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/status-im/status-network-token.git" 8 | }, 9 | "license": "GPL-3.0", 10 | "bugs": { 11 | "url": "https://github.com/status-im/status-network-token/issues" 12 | }, 13 | "homepage": "https://github.com/status-im/status-network-token", 14 | "devDependencies": { 15 | "async": "^2.4.0", 16 | "bignumber.js": "^4.0.2", 17 | "ethereumjs-testrpc": "^3.0.5", 18 | "random-bytes": "^1.0.0", 19 | "truffle": "3.2.4", 20 | "truffle-hdwallet-provider": "0.0.3", 21 | "web3": "^0.18.4" 22 | }, 23 | "dependencies": {}, 24 | "main": "truffle.js", 25 | "directories": { 26 | "test": "test" 27 | }, 28 | "scripts": { 29 | "test": "echo \"Error: no test specified\" && exit 1" 30 | }, 31 | "author": "Status Research & Development" 32 | } 33 | -------------------------------------------------------------------------------- /scripts/ceiling_curve_calc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ''' Calculate ceiling characteristics based on curve parameters ''' 3 | 4 | 5 | import argparse 6 | import decimal 7 | from decimal import Decimal 8 | import math 9 | import statistics 10 | import sys 11 | from typing import List, Sequence 12 | 13 | 14 | decimal.getcontext().rounding = decimal.ROUND_DOWN 15 | 16 | 17 | def args_parse(arguments: Sequence[str] = None) -> argparse.Namespace: 18 | ''' Parse arguments ''' 19 | par0 = argparse.ArgumentParser( 20 | description='Calculate ceiling characteristics based on curve parameters') 21 | 22 | # Required 23 | par0.add_argument('--limit', metavar='LIMIT', required=True, type=Decimal, 24 | help='Ceiling limit') 25 | par0.add_argument('--curve-factor', metavar='FACTOR', required=True, type=Decimal, 26 | help='Curve factor') 27 | # Optional 28 | par0.add_argument('--collected-start', metavar='WEI', type=Decimal, 29 | default=Decimal('0'), help='Amount collected at start of curve') 30 | par0.add_argument('--gas-per-tx-1st', metavar='AMOUNT', type=Decimal, 31 | default=Decimal('151070'), help='Gas used per 1st transaction') 32 | par0.add_argument('--gas-per-tx-2nd', metavar='AMOUNT', type=Decimal, 33 | default=Decimal('123765'), help='Gas used for all subsequent transactions') 34 | par0.add_argument('--gas-price', metavar='WEI', type=Decimal, 35 | default=Decimal('50000000000'), help='Gas price') 36 | par0.add_argument('--fee-token', metavar='FRACTION', type=Decimal, 37 | default=Decimal('0.1'), help='Max fee cost as fraction of token value') 38 | par0.add_argument('--collect-min', metavar='WEI', type=Decimal, 39 | help='Minimum collection amount') 40 | par0.add_argument('--gas-limit', metavar='AMOUNT', type=Decimal, 41 | default=Decimal('4700000'), help='Gas limit per block') 42 | par0.add_argument('--secs-per-block', metavar='SECONDS', type=Decimal, 43 | default=Decimal('16.07'), help='Average seconds per block') 44 | par0.add_argument('--print-txs', action='store_true', 45 | default=False, help='Print every individual transaction') 46 | par0.add_argument('--txs-per-address', metavar='NUMBER', type=Decimal, 47 | default=Decimal('1'), help='Average number of TXs per address') 48 | par0.add_argument( 49 | '--congestion', metavar='FRACTION', type=Decimal, 50 | default=Decimal('0.8'), 51 | help='Chance of contribution TXs being confirmed due to network congestion' 52 | ) 53 | par0.add_argument('--collect-mean', metavar='FRACTION', type=Decimal, 54 | default=Decimal('0.9'), help='Collected of TX from amount possible') 55 | 56 | args0 = par0.parse_args(arguments) 57 | 58 | if args0.txs_per_address < 1: 59 | print('--txs-per-address can\'t be less than 1', file=sys.stderr) 60 | sys.exit(1) 61 | 62 | return args0 63 | 64 | 65 | def transactions_calc( 66 | limit: Decimal, 67 | curve_factor: Decimal, 68 | collect_minimum: Decimal, 69 | collect_mean: Decimal, 70 | collected_start: Decimal = Decimal(0), 71 | ) -> List[Decimal]: 72 | ''' Calculate transactions ''' 73 | collected = collected_start 74 | transactions = [] 75 | while True: 76 | difference = limit - collected 77 | to_collect = difference / curve_factor 78 | 79 | if to_collect <= collect_minimum: 80 | if difference > collect_minimum: 81 | to_collect = collect_minimum 82 | to_collect *= collect_mean 83 | else: 84 | to_collect = difference 85 | else: 86 | to_collect *= collect_mean 87 | 88 | collected += to_collect 89 | transactions.append(to_collect) 90 | 91 | if collected >= limit: 92 | break 93 | 94 | return transactions 95 | 96 | 97 | def fmt_wei(value: Decimal, shift: bool = True) -> str: 98 | ''' Format wei value ''' 99 | fmt_val = f'{value:.0f}' 100 | if shift: 101 | return f'{"w" + fmt_val: >26}' # type: ignore 102 | return f'{"w" + fmt_val}' # type: ignore 103 | 104 | 105 | def fmt_eth(value: Decimal, shift: bool = True) -> str: 106 | ''' Format wei value into ether ''' 107 | fmt_val = f'{value / 10**18:.18f}' 108 | if shift: 109 | return f'{"Ξ" + fmt_val: >26}' # type: ignore 110 | return f'{"Ξ" + fmt_val}' # type: ignore 111 | 112 | 113 | def main() -> None: # pylint: disable=too-many-locals 114 | ''' Main ''' 115 | if ARGS.txs_per_address == 1: 116 | gas_per_tx = ARGS.gas_per_tx_1st 117 | else: 118 | gas_per_tx = ((ARGS.gas_per_tx_1st + (ARGS.gas_per_tx_2nd * (ARGS.txs_per_address - 1))) 119 | / ARGS.txs_per_address).to_integral_value(rounding=decimal.ROUND_HALF_EVEN) 120 | tx_fee = gas_per_tx * ARGS.gas_price 121 | tx_fee_token_limit = tx_fee / ARGS.fee_token 122 | collect_min = ARGS.collect_min if ARGS.collect_min is not None else tx_fee_token_limit 123 | 124 | transactions = transactions_calc( 125 | ARGS.limit, 126 | ARGS.curve_factor, 127 | collect_min, 128 | ARGS.collect_mean, 129 | collected_start=ARGS.collected_start, 130 | ) 131 | 132 | collect_fee_total = 0 133 | collect_minimum_total = 0 134 | for n, transaction in enumerate(transactions): 135 | if transaction <= collect_min: 136 | collect_minimum_total += 1 137 | 138 | if transaction < tx_fee_token_limit: 139 | collect_fee_total += 1 140 | 141 | if ARGS.print_txs: 142 | print(f'{(n + 1): >4}: {fmt_wei(transaction, shift=True)} ' 143 | f' {fmt_eth(transaction, shift=True)}') 144 | print() 145 | 146 | print( 147 | f'Average gas per TX: {gas_per_tx}\n' 148 | f'Average TX fee: {fmt_wei(tx_fee)} {fmt_eth(tx_fee)}\n' 149 | f'Token fee limit: {fmt_wei(tx_fee_token_limit)} {fmt_eth(tx_fee_token_limit)}\n' 150 | f'Minimum collect: {fmt_wei(collect_min)} {fmt_eth(collect_min)}' 151 | ) 152 | 153 | print() 154 | 155 | transactions_len = len(transactions) 156 | print( 157 | f'Number of TXs: {transactions_len}\n' 158 | f'Number of TXs <= minimum collect: {collect_minimum_total}\n' 159 | f'Number of TXs < token fee limit: {collect_fee_total}\n' 160 | f'Number of addresses: {transactions_len / ARGS.txs_per_address:.0f}' 161 | ) 162 | decimal.getcontext().rounding = decimal.ROUND_HALF_EVEN 163 | blocks = math.ceil((transactions_len * gas_per_tx) / (ARGS.gas_limit * ARGS.congestion)) 164 | print( 165 | f'Minimum blocks: {blocks}\n' 166 | f'Minimum time: {blocks * ARGS.secs_per_block:.2f}s' 167 | ) 168 | decimal.getcontext().rounding = decimal.ROUND_DOWN 169 | 170 | print() 171 | 172 | max_ = max(transactions) 173 | min_ = min(transactions) 174 | mean = statistics.mean(transactions) 175 | median = statistics.median(transactions) 176 | print( 177 | 'Collected:\n' 178 | f'Max: {fmt_wei(max_)} {fmt_eth(max_)}\n' 179 | f'Min: {fmt_wei(min_)} {fmt_eth(min_)}\n' 180 | f'Mean: {fmt_wei(mean)} {fmt_eth(mean)}\n' 181 | f'Median: {fmt_wei(median)} {fmt_eth(median)}' 182 | ) 183 | if transactions_len >= 2: 184 | stdev = statistics.stdev(transactions) 185 | print( 186 | f'Stdev: {fmt_wei(stdev)} {fmt_eth(stdev)}\n' 187 | ) 188 | 189 | 190 | if __name__ == '__main__': 191 | ARGS = args_parse() 192 | main() 193 | -------------------------------------------------------------------------------- /scripts/sgtGeneration/initialBalance.csv: -------------------------------------------------------------------------------- 1 | 0xeD33259a056F4fb449FFB7B7E2eCB43a9B5685Bf,4166666 2 | 0x2962DE226076CCE421147b5268921eF88cF0069B,1666666 3 | 0x74748d441d2cF255a5EA6c032B08471b85D5218B,1041666 4 | 0xF2da5ADd7C6f8A47997EfA04049EE7888542744b,3333333 5 | 0xfF7b6a8D0EDe69158B0C6Ffc82942cD9C8E00FF1,16666666 6 | 0x1dc622a664505af72b5e6cc1be755335bfcc3834,5000000 7 | 0xa7Cfd581060EC66414790691681732dB249502BD,29166666 8 | 0x99AeF27CF577D599214C5e775D713aab2Bac5761,10416666 9 | 0x5276cb99f022398701b205ce97fc3e73e3379ffb,5000000 10 | 0x6068B12921e44A430fB6C5A0c9f7d72d836b5DBd,10416666 11 | 0x00a365457Ba80285C2D7208Fb0d04D50CfC1aC17,416666 12 | 0x88ECfD9B73858478C894FEecA993c960b19cDeB4,16666666 13 | 0x004769ac372a0c970522fe33c66d946863a52a8c,10416666 14 | 0x6e8276F4EDee6EEff01a25215770C326783d7147,104166 15 | 0x0071e681554a08aB1F1174D3746d16F2F4A43642,625000 16 | 0x283D0C13A8eFb35d0eCEbB9c71D9182102e663FA,291666 17 | 0x5d0C10Ebc713012f82F2859E2F47A9Ee8d1318a3,416666 18 | 0x00e18d54DB9d663d28f19eE139a94191410f9A4c,208333 19 | 0x6fD4E1Dbf05033A7C4290477D7448e9b0A25bABF,104166 20 | 0xf16CFD73Fe21E1d2ffdC68C6348489AD917e8546,208333 21 | 0xDaCC0fd259CE0DE2829B38A0765970e7ab65346C,104166 22 | 0x32d44db61df0b10Ccf0164Df3D9cbeE72E3dF02c,208333 23 | 0x0030B7B79D9F2A6b19D5b63f5e63D82678D94c24,104166 24 | 0x839395e20bbB182fa440d08F850E6c7A8f6F0780,416666 25 | 0xEc9cE6E59ce95248Ecef070408E7cF35bBc79989,104166 26 | 0xEe82DDb350f48dd5712d54072172AA1A97c677C8,41666 27 | 0x6Fe7DdeE460DF1511ccdC4F87140D3AA2A801a19,104166 28 | 0x9ea3baa124aeea99ce5cb980088d523625acd512,104166 29 | 0x2a2A57a98a07D3CA5a46A0e1d51dEFffBeF54E4F,104166 30 | 0x00C016c2BDe2f068142F0887889306fc2A447B39,416666 31 | 0x2d19FDE5B4Cac4e1AfA54ee749C368C68c18316c,208333 32 | 0x9A5E4d421d72bc1835E6a7b9658CA1924D12d0E0,416666 33 | 0x9FDcD86062Be5287A81B692Bd2793Fb1787f6197,20833 34 | 0xA47e5562f5264059D75C6BD2C1080D2736a1CDd5,833333 35 | 0x36a0488f0d80202c2a1b3eb2d83ba71fc62860da,104166 36 | 0x59795FccFBe2F166651c6B73d10a7a1522A0ed89,416666 37 | 0x2d9D60C2257e7F60c15e82912Fd72D2eC9B84Bb7,416666 38 | 0x00C4904FB17A6eCBfACb609273c89791aBA0a5cd,104166 39 | 0xcc7560eB40E336949b7d5e933D3744b49609de1B,104166 40 | 0x00D2C876544bcaF8761282f4c95F434763aCeF60,416666 41 | 0xb1a2b43a7433dd150bb82227ed519cd6b142d382,416666 42 | 0x9F4860c577C5D0928d9b38153db8cb69112E346b,416666 43 | 0x21F7DC526b2132BCC4772E1707FBc2E18a281ed9,416666 44 | 0x39F4662bF97200DBfA00ED05e3141c8959151cFa,250000 45 | 0x7155302F5534712A764E1D3c0b0F99dEFe4Ad1fe,416666 46 | 0x1cade0dd3da13883d73bac2385e46ba51d981156,625000 47 | 0x45E8ECA25654c21445d3e64886d552c6381fc621,208333 48 | 0x16893e10b99a59afd2c60331e0b49241d4d4d7cc,416666 49 | 0xe7414A2F1Aa6B34f01E8D631444F0f6886fBfc6E,416666 50 | 0x4ECC719aA2AF9498FaB3c2178391A85A9Ac914C7,104166 51 | 0x270B22Be16E84D1383c4F1A0B384089b492E88BC,104166 52 | 0xc8C281EDa1B79d1A385679b31dF3C918247FF04C,416666 53 | 0x5ce0B0D1A987F0f84CD2BBafbb36fb2339383b6f,104166 54 | 0x575AAfAFDdc1Dff3f4072532f5d580105b6835Cb,416666 55 | 0x518611649eE8fEFeD0A4DA3D47bAB213c79ce9b5,416666 56 | 0xAade95376d00c9e9cDc0d7049dE055f2df6114b5,208333 57 | 0x264E5e87C8Dae4D2b526e94Cb51B2665B20e76E1,104166 58 | 0x1dba1131000664b884a1ba238464159892252d3a,8333333 59 | 0xAb232434C1E6eFC8118D12DEA87F6457e47FAd5E,416666 60 | 0x8D8ED0BBB8C0DD8D14c8652DE6fE362DFdD401a9,416666 61 | 0x4b7d1cc8005dfd224bcbf71d83f35e9650418242,104166 62 | 0x25e1d410112AfC80691B85f3fFD0994A1e5Ea140,208333 63 | 0x00676ef58700aA3c0F2F0b7C935b31470e373Eb8,208333 64 | 0x719acb379d309cf8b0a020c4c94568b6016a840b,104166 65 | 0xE05268C92117CBA26852A387F4657E9578304059,83333 66 | 0x9c6f14119C5039AdE67e7383B1C89f53729C629f,1041666 67 | 0x37DD4e12E09CbF64FA8B1D2B1Ac37730ACDc7529,208333 68 | 0x78179dCAb7e906F02f3F172233B152E4A5178F13,416666 69 | 0xfB755331Bf33E9E8376a56054DdA19c47E4D0627,208333 70 | 0x80d63799b1e08a80f73fb7a83264b5c31600bf3a,416666 71 | 0x882aA9BA810892C606FC29E6DbA057e8A317A36C,416666 72 | 0x6Eee7B3E4e19518f31c009cC351bb3a6C0714AA5,416666 73 | 0x35bED8269Dd2De8389254Cc72De8DeDfa8298B17,104166 74 | 0xfDb33f8AC7ce72d7D4795Dd8610E323B4C122fbB,833333 75 | 0x02E816AFC1b5C0f39852131959D946eb3b07b5ad,833333 76 | 0x8EDDa5513D44aDF8e7C7541Ba0de80f8d7F2FC96,833333 77 | 0x0037A6B811ffeB6e072DA21179d11B1406371C63,416666 78 | 0x98D7cb23644eE749C2A5dc85b606ffc3Da749396,416666 79 | 0x1db3439a222c519ab44bb1144fc28167b4fa6ee6,833333 80 | 0x5ed8cee6b63b1c6afce3ad7c92f4fd7e1b8fad9f,4166666 81 | 0x8811FdF0F988f0CD1B7E9DE252ABfA5b18c1cDb1,416666 82 | 0x426ab035E8ae39f2a094f824A9e96B4152ed0c92,208333 83 | 0xA495A5fc30C3ea0b999a2d36A759fFf9fa5224d4,104166 84 | 0x24B139E561874594b738C2735A24B8ea52b82571,416666 85 | 0x00ec3b9e2e7ba7ffbd1d77d100a29f8055c258bb,1250000 86 | 0x6aBdC251e5e99B628EA4261d178e2661f59b90EA,833333 87 | 0x76dF436353Ae2266b0e45dbF5A6f7Ad47829098d,104166 88 | 0x0aba55c93cf7292f71067b0ba0d8b464592895ca,416666 89 | 0x5b1025b0c08bd9f8601139cc5fb5c392da31bb8e,416666 90 | 0x6edd429c77803606cBd6Bb501CC701a6CAD6be01,416666 91 | 0x71ce2aa5f171c89887101b2ee709d9138921a6ba,416666 92 | 0xf388A47e25495Bd3F1Ae276Bfbfdc4b29eD6161a,104166 93 | 0xe0eff9aace3d6e027fe063ab06149cc6a4a504c3,416666 94 | 0x04CB1AdE2Bd72aCD758d4f7abacbC633fC532857,208333 95 | 0x0AcE2fe8b189d069020b06DF341998FAc33e94Bb,20833 96 | 0xf4FdA67fA9B6e3829D41f01b9A710D2E2F6BA784,20833 97 | 0xb3990e73a2ddb005b11946fe00891518e0c65ab8,20833 98 | 0x7BE8dA14C273005B1186f3DeB5EA5BC1A18C1c4F,20833 99 | 0x8962a1EC854f95A1829EFB5D48fe2EC19aC24136,20833 100 | 0xBEc9c12c124bfCbf8546580cda997CA9c0042E4E,20833 101 | 0x964d220e848fb3e70898ec4c4bf8ac39c7350543,20833 102 | 0xc2059ee0e3cd4f440743972b2c1a120bc26581f4,20833 103 | 0xDDf8f475EE3b847117ED3df673e85c8b4593bBf3,20833 104 | 0x3FC90B9Ec3dEb1712aed78FA29ef304566aD42EC,20833 105 | 0x37F1D672c89671F886B9a6AF09E0144E7f470B05,20833 106 | 0x36e72fafa7a7b1a9ce0609ed057f8d104058e5b6,20833 107 | 0x9De7e51Db35Ad3898683A78712185ADBA1B2b163,20833 108 | 0xf1ceea80826a10c1c3d9460da60943662d29e7b3,20833 109 | 0xD74BaF7d6bbb294748394cf6bfD0A39872a76b79,20833 110 | 0x571ad50a227bc03d42e9565ee4bb002ef1fdb226,20833 111 | 0x0868c6a952e5c5b3b29263259ef98a8c53c05729,20833 112 | 0x66E8F875C6840aDEf012320Fb50C7e3264F321ec,20833 113 | 0xFDfBB121935e162834C27f64F1700198493d2c55,20833 114 | 0x3A9c4ab18aE51Bf3f342d4c8c3A6f54ac21A44C4,20833 115 | 0xa49b77305087d961612be78811c5d819928abef4,20833 116 | 0x0BdDc77c4d8fb1D3e0d8F41E0e5f8Bb6E59Ef579,20833 117 | 0xd1324ada7e026211d0cacd90cae5777e340de948,20833 118 | 0x6CCcbf48C39A387fb9265ED56Cb16ABF9d966411,20833 119 | 0x213B09561d0960B90E9A7eC7743317769F1c52b8,20833 120 | 0x6Dd9530b2Cb8B2d7d9f7D5D898b6456EC5D94f08,20833 121 | 0x004b8D39928b9318f7709599ac8d717A3E65e102,20833 122 | 0x9d23e95b11D0FDcff990F473179B66df259d8F4c,20833 123 | 0xa7739b58741e59f358d52ec6ec5f181866cc4cf6,20833 124 | 0x2D28996180b7A6e817bb0aEF6F7013c8c4a2Dfe6,20833 125 | 0xbF6F5e78E154799C7856f4Cf9F269cf7A7201310,20833 126 | 0x8B13796d142822d0CdC6f94Db402F05bB68DA0e6,20833 127 | 0xCC404633DEDf6eC890038e600fD9288Da8c6D143,20833 128 | 0x0008C3707f06Caf0AbF7e7aB296C844851610ccF,20833 129 | 0x6095929ef6bc1f458090d14fD700448CE0973E10,20833 130 | 0x77c5a7084B488A254e7e0643e4BfAB831f9239C2,20833 131 | 0x9BC47064057371a59d1c2A7c907cdC6D7568dB47,20833 132 | 0x0681e6274bd1D606E9D192B550Fb1d20DB47cA85,20833 133 | 0x72c28693308F423B142b755EB6828E51873AA5D2,20833 134 | 0xa9af49f28Fa939Aa6D7a9683DE9A4319D1D8eCBa,20833 135 | 0x7E30D3836a8844E308342ba2981ece5f9f4A1377,20833 136 | 0x3c32f264cc10a01b03c895858532b751748d22e7,20833 137 | 0x52bB24A71aAb5f2DC01Aa1C8730aa27d9548Cd3a,20833 138 | 0x333113fCcF6E55969d71257d8552d194147B160e,20833 139 | 0x62301720DBAcf7B1D9Cd724Adc599dd5a269130a,20833 140 | 0x337e47d72C03399a303ffBeAfB110537348A24f9,20833 141 | 0xc95f7d13e60bc7b916c3925dd74edc0be2189a7a,20833 142 | 0xcb33b5b3365e5A39d51d473D43A5dA783Dc23355,20833 143 | 0x008e04a1EFCBEDfe39F33bAB9bd7b584695fB5D9,20833 144 | 0x7a572856da6f9d19c7396a1c0174708cbb745203,20833 145 | 0xffeaf10d70651cd3112b1e97289433be2bc18f50,20833 146 | 0x7dAfD53AEE8982aB1D767830c2fe50f62E3a98E6,41666 147 | 0x6a8076e1b8fC43D5e07d03Ba090d7a19e1cdafd1,20833 148 | 0xe8c68e9a0b288bfc322f31da297e87af90ab7e83,20833 149 | 0xaF4F46e668Ff2eF4FcC4b34e0B80c4225894B6Cb,20833 150 | 0xd98b8BA6bEcf3694A8BDeBA0c9e9B1918E5C0CD8,20833 151 | 0x4EA888Bb919Df69d82F794012dC94106992b5E7D,20833 152 | 0xe51699E7483d6b51f37efe269048397B27D9FeDd,20833 153 | 0x60e63c2f43f8b5daf32e8f7f2dd2e370100e9432,20833 154 | 0xeb49f9553FA991e133fEa5C0c2724e4409aCAB4A,20833 155 | 0x6e8db8ccdC7F3bEC8AF245FfE7b829A48328AB49,20833 156 | 0xa572215Ba9d59Acb1dC6D3bb372125DE51868abd,20833 157 | 0x009C4632e1688e7a7fe96f4B6013d1DACA075238,20833 158 | 0x9D4428D8E0E2fD00dcBbE4D635AA2B2f75E51e56,20833 159 | 0x739E44719F1C8A439c0eb9F49d046c91D89900d6,20833 160 | 0x6ec1b4bafDA425598549940b96561c8Aa692227B,20833 161 | 0xa5C5720E94a7D19De8eAcB8D905FC862690c9b16,20833 162 | 0x00730Fe3aa6b1a1322933E6263f55FABabC17Eb9,20833 163 | 0x91B45bDcaE669471dDDb613DE2D0E6F50c5e05f3,20833 164 | 0xE0916528e3C5F0dABdF51cd00BDd364e014F48E8,20833 165 | 0xEB8664FCAc34e577D8A0C2FbF6d7AF68439FCC0C,20833 166 | 0xf2b314Dbecfdd47c247d1efcDd4Cf5EF847CD304,62500 167 | 0x003E66C8E6bDc69EcB672B2dACCb05f68851752f,20833 168 | 0x580346022a6a73592718034b905b38167431B71F,20833 169 | 0xf73947cE3d2482168C9F3819522d938DBD5b617b,20833 170 | 0x7D292d9A7fBFEc944f17147c9Eca36bbe8308530,20833 171 | 0x8C4164ea40029a09Aa20C404Ba5b7FD08a7e9777,20833 172 | 0x019EdcB493Bd91e2b25b70f26D5d9041Fd7EF946,20833 173 | 0xc7de06718bb4ae5c17e743781be6baf956c03357,20833 174 | 0x00E912b4080BEF232056E76d34c332dcBA4F1fcF,20833 175 | 0x9082e0eac4b7d13f6db1259dfc8829859ed90329,20833 176 | 0x2554eDB5D81EdAd07Be553090D230a403A4544fa,20833 177 | 0x4bef3eaa68f4ec276ab1e7ecaa96709e4d20b49e,20833 178 | 0x07Dae622630D1136381933D2aD6B22b839d82102,20833 179 | 0xdFB3c8fe62A90965A47a02aF0a34A9C8C78571D7,20833 180 | 0x18a1a57cEC0E8C05859d6a6Be589914ee1dF27B5,20833 181 | 0x6a85883FA4579C4F9A040206B6AfEDf36863cF9c,20833 182 | 0x5cb5F46a655C02889172323760d12d0e5D83CDAf,20833 183 | 0x00affc8138f1056249149a3dfa99fc305507df67,20833 184 | 0x8ad88087765f5e7d87350c31c4782d23dd96eb8a,20833 185 | 0x2628277359d9bDc3075A53654a3E1d9A178086Dc,20833 186 | 0x9d08F84787f26f1eFF9858B04aaF160491395e35,20833 187 | 0x422DD917816Ad1dE921293A9A0996cBAce3ab65E,20833 188 | 0x1608BD64c604379ba250532CB13f31A0e12FdBAf,20833 189 | 0x9e25a3E151323e0bd3dA61F446E2405EfF38Fb9A,20833 190 | 0x8b8Ce7E49669CAf6De14d202561B1b95BD09f6d0,20833 191 | 0xDA8775C5648A83b6BAc69c16E72106d10866e9E7,20833 192 | 0x08251D925C0CaFdc8669c4C4c54b2DEaaCc553A1,20833 193 | 0xc3cc847B10E8D6B95d6F835BbAd9D32F64ec160F,20833 194 | 0x12D9A38A8C34808ef406eb4d09ac2BCD56061112,20833 195 | 0x5D196b547c9A91208a89C6806Fc1A86cea998013,20833 196 | 0x00A10c4B072807B96fF309d32bB1e994c80c7e37,20833 197 | 0x6c0B6a5Ac81E07F89238dA658a9F0E61bE6A0076,20833 198 | 0xf5a61bBcBAba9d2a9a6D0443c84f3E258CeacbB2,20833 199 | 0xaAb37eA6871718F4eE5d499e15b55E9f61822A53,20833 200 | 0xd6988f0bbe339dd24f168d7ed4554751cfc0f8a9,20833 201 | 0x2f32263ef86eE0194689553BAE5F549B50b86B86,20833 202 | 0xa9d4a2bcbe5b9e0869d70f0fe2e1d6aacd45edc5,20833 203 | 0xDEAA2e18867c2c620E13C895d6200Ed096D01D6B,20833 204 | 0x632da81d534400f84c52d137d136a6ec89a59d77,20833 205 | 0x1b00Ffd60332c7B99e5B73f926001Fe4a5d1F6e6,20833 206 | 0x76d5c762Fe0aC7F3539e51e3Ff027Cdf8486CAf2,20833 207 | 0x00267bBA8584c0e2Ab8e8ac5154F42C94Dd7E5aF,20833 208 | 0x0067A53b3EebB967E2831Cf5F9E2B2310C5CA1c9,20833 209 | 0x0fa38abec02bd4dbb87f189df50b674b9db0b468,20833 210 | 0xcEfA4b3763186A6511485b3583560C01A767be7E,20833 211 | 0xbae7329a0d322919f3a4fc56886deeb5ae318faf,20833 212 | 0xFAb70d48021e72c449A6111Fc4F40A004a0E52cc,20833 213 | 0x8888889201803e468f55333B6b3992B262f61951,20833 214 | 0x149F3f7b862CA3711D4c3fAc466b82540C200aA1,20833 215 | 0xd76B2B4E25e7d028d79560ED3ad5f27396639FFc,20833 216 | 0x147af46ae9ccd18bb35ca01b353b51990e49dce1,20833 217 | 0x300Ce242a9101f2eE5e9E0c44784ED2a3d29aE02,20833 218 | 0x4106cF2AB83C71663ADF109f88Ad8c229A92Efe2,20833 219 | 0x2feb16Dc7a240414188521418C9222A182Ee69Ae,20833 220 | 0x9c16a8fd56fe0602ba179543dcc4fdc945c5966a,20833 221 | 0x93AA7363E7fAaD169Af5f170a02faA71b4910C08,20833 222 | 0x8b0a7eE663Ff83E3d340880A6819a319DFaA69cB,20833 223 | 0x54E13ca8E3bE520A6858AcEE04C6e1f562Ddf443,20833 224 | 0x19af83a3dfca021df89eac36372175bebe5b6ff7,20833 225 | 0x3cddcb8cf10c4facfbf960309806d8a3a3f19a40,20833 226 | 0x2F60519a289e2723727b02F3e36C83E1FD512163,20833 227 | 0xB9BEE73ED54E77e76339a78438f148c0E51D482B,20833 228 | 0xf35c15B90A636342d999DBd23CfC23F46caFb636,20833 229 | 0x0c11fa6623df9ce654d9b7e75841cf9156ba99cc,20833 230 | 0x001C9d31A368f89519232335885AA1fd2c9C9b75,20833 231 | 0x620f5f2e0ef2FE140b4270A0C6B624F95d04cb77,20833 232 | 0xaCF0834B453E5418aF7C6801049e7bE592b35915,20833 233 | 0xe6054a228B6dA740684AA4b2eeB9ea790834d48a,20833 234 | 0x2a75853e0d1D896e6A2731Af885Ba27053D0775c,208333 235 | 0x84A6FC38d3F8370f00827409188400E979fC8BB4,20833 236 | 0xC677E066284FACe940584D274200aa73b3f5D0d7,20833 237 | 0x020aC951935B2D31bed8e7030590D63f24A490EE,20833 238 | 0xCa0B5E2ba748422CC4b42C51858247F1567AAE2A,20833 239 | 0xA5Ba162c959ea6a8FcC93aea45Dc2451EE689A76,20833 240 | 0x24Fc2259bEa33690Ff5fB78aDfC0f7549A43BBC7,20833 241 | 0x3316D59f0731Bf91A1170C030882fd24cED4a099,20833 242 | 0xb43f8AA1B9A5E589Cf2f56855d0740f1D4bcd856,20833 243 | 0x0711f7A38d71d343c22635eBA3Cd87E5295Dde5B,20833 244 | 0x7D1cD61f6153efd679963d101C5c49374989C7E7,20833 245 | 0x682a72BFc1b9D4a4fb8BC41A0d35f3Ed082A96dE,20833 246 | 0xfDE06Aa76d3bA3B4C2b61Da4553Fde21f83496e0,20833 247 | 0xa07a7a1907F3CBcB995088cA5D08B74596fe741b,20833 248 | 0xB2EA97F18717f7431dCbBCe468A77a37F4e5B31C,20833 249 | 0xCf743E8b5DC5AF37697cAF0F3c4dd5c3bEa81b2D,20833 250 | 0xabFFC14B3754F34f059facF5E7EC8019B96f765a,20833 251 | 0x30c415a95986011bAcf74b75d8454ea893032fAE,20833 252 | 0x09884aF62d26D21442f130a1fCa4915958D40E0f,20833 253 | 0x67bc21d3e53298d8e7f0b63fe48f4c606c877bfb,20833 254 | 0x1d118f04b63d423fbd257ff79218f1a16711c4da,20833 255 | 0x0CE60Eb7aDd9B9112c8b306816a86162482503f0,20833 256 | 0x0C65122FF927F5b8453D74d0bdfAb0aAE4c876eb,20833 257 | 0x0489076A0D17394835aF93cd62ACFf703B6814a9,20833 258 | 0x3221E683701E7b5683A4c0e768f0EE481c5B67Fb,104166 259 | 0x9CDc2ec2AadA2E397e9Ae9d64B0b11c40Acd7b31,20833 260 | 0x56A7E72942A25D07Ba05fA1A4F736Cd11830E171,20833 261 | 0xD7f264e5cB3373dCc79beAd89aF6E6DD5Bc695c6,20833 262 | 0x277E4b7F5DaB01C8E4389B930d3Bd1c9690CE1E8,20833 263 | 0xe33695f8c7c73ea161272a7d3be795e129dc3596,20833 264 | 0x320b5f61E5ce5f386149Dd2F1F65019657724650,20833 265 | 0x00E10D6e2f62E907B4D389DcfD3c168d1C7a06D3,20833 266 | 0xdEE942d5CAF5FAc11421D86B010b458E5c392990,20833 267 | 0xE718617391af09683CAA67B50F4E7C9Fd820765e,20833 268 | 0x8e65B5da5725a6dA0dbf5c886Fbe760d658Fbf61,20833 269 | 0x54E0741624DA089e94d3422A25DF1df59E814b3d,20833 270 | 0xFf651EAD42b8EeA0B9cB88EDc92704ef6af372Ce,20833 271 | 0x075c71a069a1fb42f5933f652703f1c30ef56020,20833 272 | 0x97E121ed83dA3BD26cc63BAEdDC58DA5f203cB24,20833 273 | 0xed50B8B0aDf6b37Ad15635dbC7e53F4a705014B9,20833 274 | 0xCf22CF10072d6BDe4118B99c4deaDd63b1d08512,20833 275 | 0x7B9598468f5eC899fD67CF365B1120be47A694Eb,20833 276 | 0x8B344b8f12099EAb0E8e1E2F91A093a035e8E9B3,20833 277 | 0x70c4eBe752ED72867fea4086d8510778C77E5b87,20833 278 | 0x41ff8F8f50AdFECdBA8e5C0d7D247D246964e931,20833 279 | 0x01984361979195894d8C5262cC9E8560644d5eA7,20833 280 | 0xf0fEb4DA647161c501C6fA06E1A15707796C4589,20833 281 | 0x8b2A834e537ba164cbA8Cb9E27630553F4d14ba3,20833 282 | 0x8e87CB675E0f1F5BA60E06446fAa3CFe4816b9E5,20833 283 | 0x8b5D370Ab5a50CBF6b6b007B09A2EAF22FA57A02,20833 284 | 0x151c662C25E3e7636fc949A88D957642Ff422B48,20833 285 | 0x2B981863A0FBf4e07c8508623De8Bd6d4b28419C,20833 286 | 0xdC7Aa225964267c7E0EfB35f4931426209E90312,20833 287 | 0xba7Fe76cCF1Be61e12CFF94d72C51ab31C6C47b8,20833 288 | 0x01C52d07FED773FC14b5025b41D1742031cD7c8D,20833 289 | 0xf43fcdf0697b31a5adff5343ce21d154d1410116,20833 290 | 0x1140188B7d1EB4001D0A9612152Ee8EAdc196a08,20833 291 | 0xEceF419A072bBc04437c2D77cDE5E0B70848B48F,20833 292 | 0x94337a2880725312bD24766894d375dcf6568556,20833 293 | 0xC118977A7E19076F609689EC8D56E2F16C6Ba90A,20833 294 | 0x2cF75011aad4693d89fcfF1dA50d4DDa145294ff,20833 295 | 0xFD6C5aC853391486D25dDD5cEfB1f7040ee31C2C,20833 296 | 0x1051E355Adf5C153F656c3E950f1a75FE180eF79,20833 297 | 0x760739f49Fc2936A59745644a94a5400c62a4B22,20833 298 | 0x7D98fa238C3bBF0f2BCD1bcE19eFA028474B4E07,20833 299 | 0x9fb63721bE0b1259c41F35BfeEFB8ad543bbDdD2,20833 300 | 0xD9459cc85E78e0336aDb349EAbF257Dbaf9d5a2B,20833 301 | 0x1Fd717A0AD0d3A163Fc7359c449A4a8490866Ce6,20833 302 | 0xeE2E0E56e11faE260f3DE0D6767fc1e970FC69D3,20833 303 | 0x568FcF35D04f92e341883597E14728a5695C2578,20833 304 | 0x3A66369a46F343886bbAcD130Fc2bDA51423aBA0,20833 305 | 0x279D4a00CB930dc6202c2c6153a2289A105831EC,20833 306 | 0x8C51e00804250EbD931211b5fafD048098cF90E2,20833 307 | 0x77Dfdf520a34bBcD87d907310A7cfEF7439CB044,20833 308 | 0x765a2CB437ebF35d0B1EFa79F57EB0C093C5b354,20833 309 | 0xa91A30500A60258dC83F08bD7E29086F19E75E10,20833 310 | 0x003e11FC88fc30C3048087D2BdFEfA2D48C6644c,20833 311 | 0xC761C1896e90D4a88Ab615B2683024974F6C2f26,20833 312 | 0xc30F3e11B4f34259ce948A5558F73f64fDc9Ed7B,20833 313 | 0xD57f6A60958B0D3EAFcD293E90F324De16C5F16b,20833 314 | 0x9b1b1E9e37Eb424daCe0d8Dd7218a8a661105b2d,20833 315 | 0x970A14730b476788B99e3fb706a4B5c6b1777808,20833 316 | 0xc99f67433019D1DA18C311e767FAa2b8EC250886,20833 317 | 0x83662146196a61050D29636b9F9C782750187AFa,20833 318 | 0x5e4cddf0b9d95d4e849Ed22BE228283a2485967D,20833 319 | 0x2d1f8cc4a77DBbDb6cD8B220Fa5B8bc70CC6afbe,20833 320 | 0x0093F93A884Da1D86c07fB6F7DF016957a16cc88,20833 321 | 0x793e06C5c7d23a97B452F90FCC37F54078c56b7B,20833 322 | 0x50d1b9a839270229d63953BCCE253b0C145F9A4a,20833 323 | 0x01a590a5bb729f9d50c70524ba1d1872ae805e2d,20833 324 | 0xEB803dD1bba222fdFE824b7356405b39FcCBFEcD,20833 325 | 0xdb1899Ee356001B97193BC7DEA8Da27dC661E365,20833 326 | 327 | -------------------------------------------------------------------------------- /scripts/sgtGeneration/loadBalances.js: -------------------------------------------------------------------------------- 1 | // GENERAL PARAMS 2 | 3 | const sourceAccount = "0x1dba1131000664b884a1ba238464159892252d3a"; 4 | const tokenAddress = '0xd248b0d48e44aaf9c49aea0312be7e13a6dc1468'; 5 | 6 | const Web3 = require("web3"); 7 | const fs = require("fs"); 8 | const async = require("async"); 9 | const path = require("path"); 10 | // create an instance of web3 using the HTTP provider. 11 | // NOTE in mist web3 is already available, so check first if its available before instantiating 12 | const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); 13 | 14 | const BigNumber = require("bignumber.js"); 15 | 16 | const eth = web3.eth; 17 | 18 | var tokenAbi =[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"creationBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newController","type":"address"}],"name":"changeController","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_blockNumber","type":"uint256"}],"name":"balanceOfAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_cloneTokenName","type":"string"},{"name":"_cloneDecimalUnits","type":"uint8"},{"name":"_cloneTokenSymbol","type":"string"},{"name":"_snapshotBlock","type":"uint256"},{"name":"_transfersEnabled","type":"bool"}],"name":"createCloneToken","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"parentToken","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_amount","type":"uint256"}],"name":"generateTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_blockNumber","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"data","type":"uint256[]"}],"name":"multiMint","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"transfersEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"parentSnapShotBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"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"},{"constant":false,"inputs":[{"name":"_owner","type":"address"},{"name":"_amount","type":"uint256"}],"name":"destroyTokens","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenFactory","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_transfersEnabled","type":"bool"}],"name":"enableTransfers","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"controller","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[{"name":"_tokenFactory","type":"address"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_cloneToken","type":"address"},{"indexed":false,"name":"_snapshotBlock","type":"uint256"}],"name":"NewCloneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"}]; 19 | var token = web3.eth.contract(tokenAbi).at(tokenAddress); 20 | 21 | const multiple = 50; 22 | const D160 = new BigNumber("10000000000000000000000000000000000000000", 16); 23 | 24 | const loadCsv = (cb) => { 25 | const repeated = {}; 26 | const balances = []; 27 | fs.readFile(path.join(__dirname, "initialBalance.csv"), "utf8", (err, res) => { 28 | if (err) { 29 | cb(err); 30 | return; 31 | } 32 | const lines = res.split("\n"); 33 | for (let i = 0; i < lines.length; i += 1) { 34 | const line = lines[ i ].split(","); 35 | if (line.length === 2) { 36 | const addr = line[ 0 ].toLowerCase(); 37 | if (!web3.isAddress(addr)) { 38 | console.log("Invalid address: ", addr); 39 | cb(new Error(`Invalid address ${ addr }`)); 40 | return; 41 | } 42 | if (repeated[ addr ]) { 43 | console.log("Address is repeated: ", addr); 44 | cb(new Error(`Address is repeated: ${ addr }`)); 45 | return; 46 | } 47 | repeated[ addr ] = true; 48 | const amount = new BigNumber(line[ 1 ]); 49 | if (amount.isZero()) { 50 | console.log("Address with zero balance: ", addr); 51 | cb(new Error(`Address with zero balance ${ addr }`)); 52 | return; 53 | } 54 | balances.push({ 55 | address: line[ 0 ], 56 | amount: amount.toString(10), 57 | }); 58 | } 59 | } 60 | 61 | cb(null, balances); 62 | }); 63 | }; 64 | 65 | const multiSend = (balances, cb) => { 66 | let i; 67 | const packetbalances = []; 68 | for (i = 0; i < balances.length; i += 1) { 69 | packetbalances.push(pack(balances[ i ].address, balances[ i ].amount)); 70 | } 71 | 72 | let pos = 0; 73 | async.whilst( 74 | () => pos < packetbalances.length, 75 | (cb1) => { 76 | const sendBalances = packetbalances.slice(pos, pos + multiple); 77 | console.log("Transaction: " + pos + " Length: " + sendBalances.length); 78 | pos += multiple; 79 | 80 | token.multiMint( 81 | sendBalances, 82 | { from: sourceAccount, gas: 3700000, gasPrice: eth.gasPrice.mul(1.1).floor() }, 83 | (err, txHash) => { 84 | if (err) return cb(err); 85 | console.log("txHash: ", txHash); 86 | cb1(); 87 | }); 88 | }, 89 | cb); 90 | 91 | function pack(address, amount) { 92 | const addressNum = new BigNumber(address.substring(2), 16); 93 | const amountWei = new BigNumber(amount); 94 | const v = D160.mul(amountWei).add(addressNum); 95 | return v.toString(10); 96 | } 97 | }; 98 | 99 | loadCsv((err, balances) => { 100 | if (err) { 101 | console.log(err); 102 | } else { 103 | multiSend(balances, (err2) => { 104 | if (err2) { 105 | console.log(err2); 106 | } else { 107 | console.log("terminated succefully"); 108 | } 109 | }); 110 | } 111 | }); 112 | -------------------------------------------------------------------------------- /test/claimExternal.js: -------------------------------------------------------------------------------- 1 | // Simulate a an external claim 2 | 3 | const MultiSigWallet = artifacts.require("MultiSigWallet"); 4 | const MiniMeTokenFactory = artifacts.require("MiniMeTokenFactory"); 5 | const SGT = artifacts.require("SGT"); 6 | const SNT = artifacts.require("SNT"); 7 | const StatusContributionMock = artifacts.require("StatusContributionMock"); 8 | const ContributionWallet = artifacts.require("ContributionWallet"); 9 | const DevTokensHolder = artifacts.require("DevTokensHolderMock"); 10 | const SGTExchanger = artifacts.require("SGTExchanger"); 11 | const DynamicCeiling = artifacts.require("DynamicCeiling"); 12 | const SNTPlaceHolderMock = artifacts.require("SNTPlaceHolderMock"); 13 | const ExternalToken = artifacts.require("ExternalToken"); 14 | 15 | const setHiddenCurves = require("./helpers/hiddenCurves.js").setHiddenCurves; 16 | const assertFail = require("./helpers/assertFail"); 17 | 18 | contract("StatusContribution", function(accounts) { 19 | const addressStatus = accounts[0]; 20 | const addressCommunity = accounts[1]; 21 | const addressReserve = accounts[2]; 22 | const addressDevs = accounts[3]; 23 | const addressSGTHolder = accounts[4]; 24 | 25 | let multisigStatus; 26 | let multisigCommunity; 27 | let multisigReserve; 28 | let multisigDevs; 29 | let miniMeTokenFactory; 30 | let sgt; 31 | let snt; 32 | let statusContribution; 33 | let contributionWallet; 34 | let devTokensHolder; 35 | let sgtExchanger; 36 | let dynamicCeiling; 37 | let sntPlaceHolder; 38 | let externalToken; 39 | 40 | const curves = [ 41 | [web3.toWei(3), 30, 10**12], 42 | [web3.toWei(13), 30, 10**12], 43 | [web3.toWei(15), 30, 10**12], 44 | ]; 45 | const startBlock = 1000000; 46 | const endBlock = 1003000; 47 | 48 | const maxSGTSupply = 5000 * 2; 49 | 50 | it("Deploys all contracts", async function() { 51 | multisigStatus = await MultiSigWallet.new([addressStatus], 1); 52 | multisigCommunity = await MultiSigWallet.new([addressCommunity], 1); 53 | multisigReserve = await MultiSigWallet.new([addressReserve], 1); 54 | multisigDevs = await MultiSigWallet.new([addressDevs], 1); 55 | 56 | miniMeTokenFactory = await MiniMeTokenFactory.new(); 57 | 58 | sgt = await SGT.new(miniMeTokenFactory.address); 59 | await sgt.generateTokens(addressSGTHolder, 5000); 60 | 61 | snt = await SNT.new(miniMeTokenFactory.address); 62 | statusContribution = await StatusContributionMock.new(); 63 | contributionWallet = await ContributionWallet.new( 64 | multisigStatus.address, 65 | endBlock, 66 | statusContribution.address); 67 | devTokensHolder = await DevTokensHolder.new( 68 | multisigDevs.address, 69 | statusContribution.address, 70 | snt.address); 71 | sgtExchanger = await SGTExchanger.new(sgt.address, snt.address, statusContribution.address); 72 | dynamicCeiling = await DynamicCeiling.new(addressStatus, statusContribution.address); 73 | 74 | await setHiddenCurves(dynamicCeiling, curves); 75 | 76 | sntPlaceHolder = await SNTPlaceHolderMock.new( 77 | multisigCommunity.address, 78 | snt.address, 79 | statusContribution.address, 80 | sgtExchanger.address); 81 | 82 | await snt.changeController(statusContribution.address); 83 | 84 | await statusContribution.initialize( 85 | snt.address, 86 | sntPlaceHolder.address, 87 | 88 | startBlock, 89 | endBlock, 90 | 91 | dynamicCeiling.address, 92 | 93 | contributionWallet.address, 94 | 95 | multisigReserve.address, 96 | sgtExchanger.address, 97 | devTokensHolder.address, 98 | 99 | sgt.address, 100 | maxSGTSupply); 101 | 102 | externalToken = await ExternalToken.new(); 103 | await externalToken.generateTokens(addressStatus, 1000); 104 | }); 105 | 106 | it("Sends to and recover tokens from the StatusContribution", async function() { 107 | await externalToken.transfer(statusContribution.address, 100); 108 | const balanceBefore = await externalToken.balanceOf(addressStatus); 109 | assert.equal(balanceBefore.toNumber(), 900); 110 | 111 | await statusContribution.claimTokens(externalToken.address); 112 | const afterBefore = await externalToken.balanceOf(addressStatus); 113 | assert.equal(afterBefore.toNumber(), 1000); 114 | }); 115 | 116 | it("Recovers tokens sent to SNT", async function() { 117 | await externalToken.transfer(snt.address, 100); 118 | const balanceBefore = await externalToken.balanceOf(addressStatus); 119 | assert.equal(balanceBefore.toNumber(), 900); 120 | 121 | await statusContribution.claimTokens(externalToken.address); 122 | const afterBefore = await externalToken.balanceOf(addressStatus); 123 | assert.equal(afterBefore.toNumber(), 1000); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /test/contribution.js: -------------------------------------------------------------------------------- 1 | // Simulate a full contribution 2 | 3 | const MultiSigWallet = artifacts.require("MultiSigWallet"); 4 | const MiniMeTokenFactory = artifacts.require("MiniMeTokenFactory"); 5 | const SGT = artifacts.require("SGTMock"); 6 | const SNT = artifacts.require("SNTMock"); 7 | const StatusContributionMock = artifacts.require("StatusContributionMock"); 8 | const ContributionWallet = artifacts.require("ContributionWallet"); 9 | const DevTokensHolder = artifacts.require("DevTokensHolderMock"); 10 | const SGTExchangerMock = artifacts.require("SGTExchangerMock"); 11 | const DynamicCeiling = artifacts.require("DynamicCeiling"); 12 | const SNTPlaceHolderMock = artifacts.require("SNTPlaceHolderMock"); 13 | 14 | const setHiddenCurves = require("./helpers/hiddenCurves.js").setHiddenCurves; 15 | const assertFail = require("./helpers/assertFail"); 16 | 17 | contract("StatusContribution", function(accounts) { 18 | const addressStatus = accounts[0]; 19 | const addressCommunity = accounts[1]; 20 | const addressReserve = accounts[2]; 21 | const addressDevs = accounts[3]; 22 | const addressSGTHolder = accounts[4]; 23 | const addressGuaranteed0 = accounts[7]; 24 | const addressGuaranteed1 = accounts[8]; 25 | 26 | let multisigStatus; 27 | let multisigCommunity; 28 | let multisigReserve; 29 | let multisigDevs; 30 | let miniMeTokenFactory; 31 | let sgt; 32 | let snt; 33 | let statusContribution; 34 | let contributionWallet; 35 | let devTokensHolder; 36 | let sgtExchanger; 37 | let dynamicCeiling; 38 | let sntPlaceHolder; 39 | let lim; 40 | let cur; 41 | const divs = 30; 42 | 43 | const curves = [ 44 | [web3.toWei(3), 30, 10**12], 45 | [web3.toWei(8), 30, 10**12], 46 | [web3.toWei(15), 30, 10**12], 47 | ]; 48 | const startBlock = 1000000; 49 | const endBlock = 1040000; 50 | 51 | const maxSGTSupply = 5000 * 2; 52 | 53 | it("Deploys all contracts", async function() { 54 | multisigStatus = await MultiSigWallet.new([addressStatus], 1); 55 | multisigCommunity = await MultiSigWallet.new([addressCommunity], 1); 56 | multisigReserve = await MultiSigWallet.new([addressReserve], 1); 57 | multisigDevs = await MultiSigWallet.new([addressDevs], 1); 58 | 59 | miniMeTokenFactory = await MiniMeTokenFactory.new(); 60 | 61 | sgt = await SGT.new(miniMeTokenFactory.address); 62 | await sgt.generateTokens(addressSGTHolder, 2500); 63 | await sgt.generateTokens(addressStatus, 2500); 64 | 65 | snt = await SNT.new(miniMeTokenFactory.address); 66 | statusContribution = await StatusContributionMock.new(); 67 | contributionWallet = await ContributionWallet.new( 68 | multisigStatus.address, 69 | endBlock, 70 | statusContribution.address); 71 | devTokensHolder = await DevTokensHolder.new( 72 | multisigDevs.address, 73 | statusContribution.address, 74 | snt.address); 75 | sgtExchanger = await SGTExchangerMock.new(sgt.address, snt.address, statusContribution.address); 76 | dynamicCeiling = await DynamicCeiling.new(addressStatus, statusContribution.address); 77 | 78 | await setHiddenCurves(dynamicCeiling, curves); 79 | 80 | sntPlaceHolder = await SNTPlaceHolderMock.new( 81 | multisigCommunity.address, 82 | snt.address, 83 | statusContribution.address, 84 | sgtExchanger.address); 85 | 86 | await snt.changeController(statusContribution.address); 87 | 88 | await statusContribution.initialize( 89 | snt.address, 90 | sntPlaceHolder.address, 91 | 92 | startBlock, 93 | endBlock, 94 | 95 | dynamicCeiling.address, 96 | 97 | contributionWallet.address, 98 | 99 | multisigReserve.address, 100 | sgtExchanger.address, 101 | devTokensHolder.address, 102 | 103 | sgt.address, 104 | maxSGTSupply); 105 | }); 106 | 107 | it("Checks initial parameters", async function() { 108 | assert.equal(await snt.controller(), statusContribution.address); 109 | }); 110 | 111 | it("Checks that nobody can buy before the sale starts", async function() { 112 | await assertFail(async function() { 113 | await snt.send(web3.toWei(1)); 114 | }); 115 | }); 116 | 117 | it("Adds 2 guaranteed addresses ", async function() { 118 | await statusContribution.setGuaranteedAddress(addressGuaranteed0, web3.toWei(1)); 119 | await statusContribution.setGuaranteedAddress(addressGuaranteed1, web3.toWei(2)); 120 | 121 | const permited7 = await statusContribution.guaranteedBuyersLimit(addressGuaranteed0); 122 | const permited8 = await statusContribution.guaranteedBuyersLimit(addressGuaranteed1); 123 | 124 | assert.equal(web3.fromWei(permited7).toNumber(), 1); 125 | assert.equal(web3.fromWei(permited8).toNumber(), 2); 126 | }); 127 | 128 | it("Reveals a curve, moves time to start of the ICO, and does the first buy", async function() { 129 | await dynamicCeiling.revealCurve( 130 | curves[0][0], 131 | curves[0][1], 132 | curves[0][2], 133 | false, 134 | web3.sha3("pwd0")); 135 | 136 | await statusContribution.setMockedBlockNumber(1000000); 137 | await sgt.setMockedBlockNumber(1000000); 138 | await snt.setMockedBlockNumber(1000000); 139 | 140 | lim = 3; 141 | cur = 0; 142 | 143 | await snt.sendTransaction({value: web3.toWei(1), gas: 300000, gasPrice: "20000000000", from: addressStatus}); 144 | 145 | const b = Math.min(1, ((lim - cur) / divs)); 146 | cur += b; 147 | 148 | const balance = await snt.balanceOf(addressStatus); 149 | 150 | assert.equal(web3.fromWei(balance).toNumber(), b * 10000); 151 | }); 152 | it("Pauses and resumes the contribution ", async function() { 153 | await statusContribution.setMockedBlockNumber(1005000); 154 | await sgt.setMockedBlockNumber(1005000); 155 | await snt.setMockedBlockNumber(1005000); 156 | await statusContribution.pauseContribution(); 157 | await assertFail(async function() { 158 | await snt.sendTransaction({value: web3.toWei(5), gas: 300000, gasPrice: "20000000000"}); 159 | }); 160 | await statusContribution.resumeContribution(); 161 | }); 162 | it("Returns the remaining of the last transaction ", async function() { 163 | const initialBalance = await web3.eth.getBalance(addressStatus); 164 | await snt.sendTransaction({value: web3.toWei(5), gas: 300000, gasPrice: "20000000000"}); 165 | const finalBalance = await web3.eth.getBalance(addressStatus); 166 | 167 | const b = Math.min(5, ((lim - cur) / divs)); 168 | cur += b; 169 | 170 | const spent = web3.fromWei(initialBalance.sub(finalBalance)).toNumber(); 171 | assert.isAbove(spent, b); 172 | assert.isBelow(spent, b + 0.02); 173 | 174 | const totalCollected = await statusContribution.totalCollected(); 175 | assert.equal(web3.fromWei(totalCollected), cur); 176 | 177 | const balanceContributionWallet = await web3.eth.getBalance(contributionWallet.address); 178 | assert.equal(web3.fromWei(balanceContributionWallet), cur); 179 | }); 180 | 181 | it("Reveals second curve and checks that the limit is right", async function() { 182 | await dynamicCeiling.revealCurve( 183 | curves[1][0], 184 | curves[1][1], 185 | curves[1][2], 186 | false, 187 | web3.sha3("pwd1")); 188 | await dynamicCeiling.moveTo(1); 189 | 190 | await statusContribution.setMockedBlockNumber(1005200); 191 | await sgt.setMockedBlockNumber(1005200); 192 | await snt.setMockedBlockNumber(1005200); 193 | 194 | const initialBalance = await web3.eth.getBalance(addressStatus); 195 | await snt.sendTransaction({value: web3.toWei(10), gas: 300000, gasPrice: "20000000000"}); 196 | const finalBalance = await web3.eth.getBalance(addressStatus); 197 | 198 | lim = 8; 199 | const b = Math.min(5, ((lim - cur) / divs)); 200 | cur += b; 201 | 202 | const spent = web3.fromWei(initialBalance.sub(finalBalance)).toNumber(); 203 | assert.isAbove(spent, b); 204 | assert.isBelow(spent, b + 0.02); 205 | 206 | const totalCollected = await statusContribution.totalCollected(); 207 | assert.equal(web3.fromWei(totalCollected), cur); 208 | 209 | const balanceContributionWallet = await web3.eth.getBalance(contributionWallet.address); 210 | assert.equal(web3.fromWei(balanceContributionWallet), cur); 211 | }); 212 | 213 | it("Reveals last curve and fills the collaboration", async function() { 214 | await dynamicCeiling.revealCurve( 215 | curves[2][0], 216 | curves[2][1], 217 | curves[2][2], 218 | true, 219 | web3.sha3("pwd2")); 220 | await dynamicCeiling.moveTo(2); 221 | 222 | let blk = 1025100; 223 | await statusContribution.setMockedBlockNumber(blk); 224 | await sgt.setMockedBlockNumber(blk); 225 | await snt.setMockedBlockNumber(blk); 226 | blk += 100; 227 | 228 | const initialBalance = await web3.eth.getBalance(addressStatus); 229 | await statusContribution.proxyPayment( 230 | addressCommunity, 231 | {value: web3.toWei(15), gas: 300000, from: addressStatus, gasPrice: "20000000000"}); 232 | 233 | lim = 15; 234 | const b = Math.min(5, ((lim - cur) / divs)); 235 | cur += b; 236 | 237 | const finalBalance = await web3.eth.getBalance(addressStatus); 238 | 239 | const balance1 = await snt.balanceOf(addressCommunity); 240 | 241 | assert.equal(web3.fromWei(balance1).toNumber(), b * 10000); 242 | 243 | const spent = web3.fromWei(initialBalance.sub(finalBalance)).toNumber(); 244 | assert.isAbove(spent, b); 245 | assert.isBelow(spent, b + 0.02); 246 | 247 | const totalCollected = await statusContribution.totalCollected(); 248 | assert.equal(web3.fromWei(totalCollected), cur); 249 | 250 | const balanceContributionWallet = await web3.eth.getBalance(contributionWallet.address); 251 | assert.equal(web3.fromWei(balanceContributionWallet), cur); 252 | 253 | while (cur < 14) { 254 | await statusContribution.setMockedBlockNumber(blk); 255 | blk += 101; 256 | 257 | await statusContribution.proxyPayment( 258 | addressCommunity, 259 | {value: web3.toWei(15), gas: 300000, from: addressStatus, gasPrice: "20000000000"}); 260 | 261 | const b2 = Math.min(5, ((lim - cur) / divs)); 262 | cur += b2; 263 | 264 | 265 | const balanceContributionWallet2 = 266 | await web3.eth.getBalance(contributionWallet.address); 267 | 268 | assert.isBelow(Math.abs(web3.fromWei(balanceContributionWallet2).toNumber() - cur), 0.0001); 269 | } 270 | }); 271 | 272 | it("Doesn't allow transfers during contribution period", async function() { 273 | await assertFail(async function() { 274 | await snt.transfer(addressSGTHolder, web3.toWei(1000)); 275 | }); 276 | }); 277 | 278 | it("Checks that Guaranteed addresses are able to buy", async function() { 279 | await snt.sendTransaction({value: web3.toWei(3), gas: 300000, from: addressGuaranteed0}); 280 | await snt.sendTransaction({value: web3.toWei(3), gas: 300000, from: addressGuaranteed1}); 281 | 282 | const balance7 = await snt.balanceOf(addressGuaranteed0); 283 | const balance8 = await snt.balanceOf(addressGuaranteed1); 284 | 285 | assert.equal(web3.fromWei(balance7).toNumber(), 10000); 286 | assert.equal(web3.fromWei(balance8).toNumber(), 20000); 287 | }); 288 | 289 | it("Finalizes", async function() { 290 | await statusContribution.setMockedBlockNumber(endBlock + 1); 291 | await statusContribution.finalize(); 292 | 293 | const totalSupply = await snt.totalSupply(); 294 | 295 | assert.isBelow(web3.fromWei(totalSupply).toNumber() - (180000 / 0.46), 0.01); 296 | 297 | const balanceSGT = await snt.balanceOf(sgtExchanger.address); 298 | assert.equal(balanceSGT.toNumber(), totalSupply.mul(0.05).toNumber()); 299 | 300 | const balanceDevs = await snt.balanceOf(devTokensHolder.address); 301 | assert.equal(balanceDevs.toNumber(), totalSupply.mul(0.20).toNumber()); 302 | 303 | const balanceSecondary = await snt.balanceOf(multisigReserve.address); 304 | assert.equal(balanceSecondary.toNumber(), totalSupply.mul(0.29).toNumber()); 305 | }); 306 | 307 | it("Moves the Ether to the final multisig", async function() { 308 | await sgtExchanger.setMockedBlockNumber(endBlock + 5); 309 | await multisigStatus.submitTransaction( 310 | contributionWallet.address, 311 | 0, 312 | contributionWallet.contract.withdraw.getData()); 313 | 314 | const balance = await web3.eth.getBalance(multisigStatus.address); 315 | 316 | assert.isBelow(Math.abs(web3.fromWei(balance).toNumber() - (cur+3)), 0.00001); 317 | }); 318 | 319 | it("Exchanges SGT by SNT", async function() { 320 | await sgtExchanger.collect({from: addressSGTHolder}); 321 | 322 | const balance = await snt.balanceOf(addressSGTHolder); 323 | const totalSupply = await snt.totalSupply(); 324 | 325 | assert.equal(totalSupply.mul(0.025).toNumber(), balance.toNumber()); 326 | }); 327 | 328 | it("Doesn't allow transfers in the 1 week period", async function() { 329 | await assertFail(async function() { 330 | await snt.transfer(addressSGTHolder, web3.toWei(1000)); 331 | }); 332 | }); 333 | 334 | it("Allows transfers after 1 week period", async function() { 335 | const t = Math.floor(new Date().getTime() / 1000) + (86400 * 7) + 1000; 336 | await sntPlaceHolder.setMockedTime(t); 337 | 338 | await snt.transfer(accounts[5], web3.toWei(1000)); 339 | 340 | const balance2 = await snt.balanceOf(accounts[5]); 341 | 342 | assert.equal(web3.fromWei(balance2).toNumber(), 1000); 343 | }); 344 | 345 | it("Disallows devs from transfering before 6 months have past", async function() { 346 | const t = Math.floor(new Date().getTime() / 1000) + (86400 * 7) + 1000; 347 | await devTokensHolder.setMockedTime(t); 348 | 349 | // This function will fail in the multisig 350 | await multisigDevs.submitTransaction( 351 | devTokensHolder.address, 352 | 0, 353 | devTokensHolder.contract.collectTokens.getData(), 354 | {from: addressDevs, gas: 1000000}); 355 | 356 | const balance = await snt.balanceOf(multisigDevs.address); 357 | assert.equal(balance,0); 358 | }); 359 | 360 | it("Allows devs to extract after 6 months", async function() { 361 | const t = (await statusContribution.finalizedTime()).toNumber() + (86400 * 360); 362 | await devTokensHolder.setMockedTime(t); 363 | 364 | const totalSupply = await snt.totalSupply(); 365 | 366 | await multisigDevs.submitTransaction( 367 | devTokensHolder.address, 368 | 0, 369 | devTokensHolder.contract.collectTokens.getData(), 370 | {from: addressDevs}); 371 | 372 | const balance = await snt.balanceOf(multisigDevs.address); 373 | 374 | const calcTokens = web3.fromWei(totalSupply.mul(0.20).mul(0.5)).toNumber(); 375 | const realTokens = web3.fromWei(balance).toNumber(); 376 | 377 | assert.equal(realTokens, calcTokens); 378 | }); 379 | 380 | it("Allows devs to extract everything after 24 months", async function() { 381 | const t = Math.floor(new Date().getTime() / 1000) + (86400 * 360 * 2); 382 | await devTokensHolder.setMockedTime(t); 383 | 384 | const totalSupply = await snt.totalSupply(); 385 | 386 | await multisigDevs.submitTransaction( 387 | devTokensHolder.address, 388 | 0, 389 | devTokensHolder.contract.collectTokens.getData(), 390 | {from: addressDevs}); 391 | 392 | const balance = await snt.balanceOf(multisigDevs.address); 393 | 394 | const calcTokens = web3.fromWei(totalSupply.mul(0.20)).toNumber(); 395 | const realTokens = web3.fromWei(balance).toNumber(); 396 | 397 | assert.equal(calcTokens, realTokens); 398 | }); 399 | 400 | it("Checks that SNT's Controller is upgradeable", async function() { 401 | await multisigCommunity.submitTransaction( 402 | sntPlaceHolder.address, 403 | 0, 404 | sntPlaceHolder.contract.changeController.getData(accounts[6]), 405 | {from: addressCommunity}); 406 | 407 | const controller = await snt.controller(); 408 | 409 | assert.equal(controller, accounts[6]); 410 | }); 411 | }); 412 | -------------------------------------------------------------------------------- /test/dynamicCeiling.js: -------------------------------------------------------------------------------- 1 | // Simulate dynamic ceilings 2 | 3 | const DynamicCeiling = artifacts.require("DynamicCeiling"); 4 | 5 | const setHiddenCurves = require("./helpers/hiddenCurves.js").setHiddenCurves; 6 | 7 | contract("DynamicCeiling", function(accounts) { 8 | let dynamicCeiling; 9 | 10 | const curves = [ 11 | [web3.toWei(1000), 30, 10**12], 12 | [web3.toWei(21000), 30, 10**12], 13 | [web3.toWei(61000), 30, 10**12], 14 | ]; 15 | 16 | it("Deploys dynamicCeiling", async function() { 17 | dynamicCeiling = await DynamicCeiling.new(accounts[0], accounts[0]); 18 | 19 | assert.equal(await dynamicCeiling.currentIndex(), 0); 20 | }); 21 | 22 | it("Checks that toCollect is 0 before curves are set", async function() { 23 | assert.equal(await dynamicCeiling.toCollect.call(0), 0); 24 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(10)), 0); 25 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(15)), 0); 26 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(20)), 0); 27 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(30)), 0); 28 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(55)), 0); 29 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(676)), 0); 30 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(5555)), 0); 31 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(10**8)), 0); 32 | 33 | assert.equal(await dynamicCeiling.currentIndex(), 0); 34 | }); 35 | 36 | it("Sets the curves", async function() { 37 | await setHiddenCurves(dynamicCeiling, curves); 38 | assert.equal(await dynamicCeiling.nCurves(), 10); 39 | }); 40 | 41 | it("Checks that toCollect is 0 before curves are revealed", async function() { 42 | assert.equal(await dynamicCeiling.toCollect.call('0'), '0'); 43 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(10)), 0); 44 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(15)), 0); 45 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(20)), 0); 46 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(30)), 0); 47 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(55)), 0); 48 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(676)), 0); 49 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(5555)), 0); 50 | assert.equal(await dynamicCeiling.toCollect.call(web3.toWei(10**8)), 0); 51 | 52 | assert.equal(await dynamicCeiling.currentIndex(), 0); 53 | }); 54 | 55 | it("Reveals 1st curve", async function() { 56 | await dynamicCeiling.revealCurve( 57 | curves[0][0], 58 | curves[0][1], 59 | curves[0][2], 60 | false, 61 | web3.sha3("pwd0")); 62 | 63 | assert.equal(await dynamicCeiling.revealedCurves(), 1); 64 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '0'); 65 | assert.equal(await dynamicCeiling.allRevealed(), false); 66 | }); 67 | 68 | it("Checks toCollect after 1st curve is revealed", async function() { 69 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '0'); 70 | assert.equal((await dynamicCeiling.toCollect.call(0)).toFixed(), '33333333333333333333'); 71 | 72 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(10))).toFixed(), '33000000000000000000'); 73 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(15))).toFixed(), '32833333333333333333'); 74 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(20))).toFixed(), '32666666666666666666'); 75 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(30))).toFixed(), '32333333333333333333'); 76 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(55))).toFixed(), '31500000000000000000'); 77 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(676))).toFixed(), '10800000000000000000'); 78 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(999))).toFixed(), '33333333333333333'); 79 | 80 | assert.equal((await dynamicCeiling.toCollect.call('999999999998999999999')).toFixed(), '1000000001'); 81 | assert.equal((await dynamicCeiling.toCollect.call('999999999999000000000')).toFixed(), '1000000000'); 82 | assert.equal((await dynamicCeiling.toCollect.call('999999999999999999999')).toFixed(), '1'); 83 | 84 | await dynamicCeiling.toCollect(curves[0][0]); 85 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '0'); 86 | assert.equal((await dynamicCeiling.toCollect.call(curves[0][0])).toFixed(), '0'); 87 | }); 88 | 89 | it("Reveals 2nd curve", async function() { 90 | await dynamicCeiling.revealCurve( 91 | curves[1][0], 92 | curves[1][1], 93 | curves[1][2], 94 | false, 95 | web3.sha3("pwd1")); 96 | 97 | assert.equal(await dynamicCeiling.revealedCurves(), 2); 98 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '0'); 99 | assert.equal(await dynamicCeiling.allRevealed(), false); 100 | }); 101 | 102 | it("Checks toCollect after 2nd curve is revealed", async function() { 103 | await dynamicCeiling.toCollect(curves[0][0]); 104 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '1'); 105 | assert.equal((await dynamicCeiling.toCollect.call(curves[0][0])).toFixed(), '666666666666666666666') 106 | 107 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(1010))).toFixed(), '666333333333333333333'); 108 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(1015))).toFixed(), '666166666666666666666'); 109 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(1020))).toFixed(), '666000000000000000000'); 110 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(1030))).toFixed(), '665666666666666666666'); 111 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(1055))).toFixed(), '664833333333333333333'); 112 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(10676))).toFixed(), '344133333333333333333'); 113 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(10999))).toFixed(), '333366666666666666666'); 114 | 115 | assert.equal((await dynamicCeiling.toCollect.call('20999999999998999999999')).toFixed(), '1000000001'); 116 | assert.equal((await dynamicCeiling.toCollect.call('20999999999999000000000')).toFixed(), '1000000000'); 117 | assert.equal((await dynamicCeiling.toCollect.call('20999999999999999999999')).toFixed(), '1'); 118 | 119 | await dynamicCeiling.toCollect(curves[1][0]); 120 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '1'); 121 | assert.equal((await dynamicCeiling.toCollect.call(curves[1][0])).toFixed(), '0'); 122 | }); 123 | 124 | it("Reveals last curve", async function() { 125 | await dynamicCeiling.revealCurve( 126 | curves[2][0], 127 | curves[2][1], 128 | curves[2][2], 129 | true, 130 | web3.sha3("pwd2")); 131 | 132 | assert.equal(await dynamicCeiling.revealedCurves(), 3); 133 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '1'); 134 | assert.equal(await dynamicCeiling.allRevealed(), true); 135 | }); 136 | 137 | it("Checks toCollect after 3rd curve is revealed", async function() { 138 | await dynamicCeiling.toCollect(curves[1][0]); 139 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '2'); 140 | assert.equal((await dynamicCeiling.toCollect.call(curves[1][0])).toFixed(), '1333333333333333333333'); 141 | 142 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21010))).toFixed(), '1333000000000000000000'); 143 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21015))).toFixed(), '1332833333333333333333'); 144 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21020))).toFixed(), '1332666666666666666666'); 145 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21030))).toFixed(), '1332333333333333333333'); 146 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21055))).toFixed(), '1331500000000000000000'); 147 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21676))).toFixed(), '1310800000000000000000'); 148 | assert.equal((await dynamicCeiling.toCollect.call(web3.toWei(21999))).toFixed(), '1300033333333333333333'); 149 | 150 | assert.equal((await dynamicCeiling.toCollect.call('60999999999998999999999')).toFixed(), '1000000001'); 151 | assert.equal((await dynamicCeiling.toCollect.call('60999999999999000000000')).toFixed(), '1000000000'); 152 | assert.equal((await dynamicCeiling.toCollect.call('60999999999999999999999')).toFixed(), '1'); 153 | 154 | await dynamicCeiling.toCollect(curves[2][0]); 155 | assert.equal((await dynamicCeiling.currentIndex()).toFixed(), '2'); 156 | assert.equal((await dynamicCeiling.toCollect.call(curves[2][0])).toFixed(), '0'); 157 | }); 158 | 159 | 160 | it("Deploys dynamicCeiling", async function() { 161 | dynamicCeiling = await DynamicCeiling.new(accounts[0], accounts[0]); 162 | 163 | assert.equal(await dynamicCeiling.currentIndex(), 0); 164 | }); 165 | 166 | it("Sets the curves", async function() { 167 | await setHiddenCurves(dynamicCeiling, curves); 168 | assert.equal(await dynamicCeiling.nCurves(), 10); 169 | }); 170 | 171 | it("Reveals multiple curves", async function() { 172 | await dynamicCeiling.revealMulti( 173 | [ 174 | curves[0][0], 175 | curves[1][0], 176 | curves[2][0], 177 | ], 178 | [ 179 | curves[0][1], 180 | curves[1][1], 181 | curves[2][1], 182 | ], 183 | [ 184 | curves[0][2], 185 | curves[1][2], 186 | curves[2][2], 187 | ], 188 | [ 189 | false, 190 | false, 191 | true, 192 | ], 193 | [ 194 | web3.sha3("pwd0"), 195 | web3.sha3("pwd1"), 196 | web3.sha3("pwd2"), 197 | ] 198 | ); 199 | 200 | assert.equal(await dynamicCeiling.currentIndex(), 0); 201 | assert.equal(await dynamicCeiling.revealedCurves(), 3); 202 | assert.equal(await dynamicCeiling.allRevealed(), true); 203 | }); 204 | 205 | }); 206 | -------------------------------------------------------------------------------- /test/helpers/assertFail.js: -------------------------------------------------------------------------------- 1 | module.exports = async function(callback) { 2 | let web3_error_thrown = false; 3 | try { 4 | await callback(); 5 | } catch (error) { 6 | if (error.message.search("invalid opcode")) web3_error_thrown = true; 7 | } 8 | assert.ok(web3_error_thrown, "Transaction should fail"); 9 | }; 10 | -------------------------------------------------------------------------------- /test/helpers/assertGas.js: -------------------------------------------------------------------------------- 1 | module.exports = async function(error) { 2 | assert.isAbove(error.message.search('of gas'), -1, 'Out of gas error must be returned'); 3 | } 4 | -------------------------------------------------------------------------------- /test/helpers/assertJump.js: -------------------------------------------------------------------------------- 1 | module.exports = async function(error) { 2 | assert.isAbove(error.message.search('invalid JUMP'), -1, 'Invalid JUMP error must be returned'); 3 | } 4 | -------------------------------------------------------------------------------- /test/helpers/hiddenCurves.js: -------------------------------------------------------------------------------- 1 | exports.setHiddenCurves = async function(dynamicCeiling, curves) { 2 | const hashes = []; 3 | let i = 0; 4 | for (let c of curves) { 5 | const h = await dynamicCeiling.calculateHash( 6 | c[0], 7 | c[1], 8 | c[2], 9 | i === curves.length - 1, 10 | web3.sha3(`pwd${ i }`)); 11 | hashes.push(h); 12 | i += 1; 13 | } 14 | for (; i < 10; i += 1) { 15 | hashes.push(web3.sha3(`pwd${ i }`)); 16 | } 17 | await dynamicCeiling.setHiddenCurves(hashes); 18 | }; 19 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | const HDWalletProvider = require('truffle-hdwallet-provider'); 2 | 3 | const mnemonic = process.env.TEST_MNEMONIC || 'status mnemonic status mnemonic status mnemonic status mnemonic status mnemonic status mnemonic'; 4 | const providerRopsten = new HDWalletProvider(mnemonic, 'https://ropsten.infura.io/', 0); 5 | const providerKovan = new HDWalletProvider(mnemonic, 'https://kovan.infura.io', 0); 6 | 7 | module.exports = { 8 | networks: { 9 | development: { 10 | network_id: 15, 11 | host: "localhost", 12 | port: 8545, 13 | gas: 4000000, 14 | gasPrice: 20e9, 15 | }, 16 | development_migrate: { 17 | network_id: 15, 18 | host: "localhost", 19 | port: 8545, 20 | gas: 4000000, 21 | gasPrice: 20e9, 22 | from: "0xf93df8c288b9020e76583a6997362e89e0599e99", 23 | }, 24 | mainnet: { 25 | network_id: 1, 26 | host: "localhost", 27 | port: 8545, 28 | gas: 4000000, 29 | gasPrice: 20e9, 30 | from: "0xf93df8c288b9020e76583a6997362e89e0599e99", 31 | }, 32 | ropsten: { 33 | network_id: 3, 34 | provider: providerRopsten, 35 | gas: 4000000, 36 | gasPrice: 20e9, 37 | }, 38 | kovan: { 39 | network_id: 42, 40 | provider: providerKovan, 41 | gas: 4000000, 42 | gasPrice: 20e9, 43 | }, 44 | } 45 | }; 46 | --------------------------------------------------------------------------------