├── .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 | [](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 |
--------------------------------------------------------------------------------