From 19f69dd76c5a0aa19658dd0dd689387e15261d42 Mon Sep 17 00:00:00 2001 From: Antoine Cote Date: Thu, 30 Nov 2017 18:27:46 +0800 Subject: [PATCH] Implement changes requested by Bluzelle including 1) Config constants, 2) whitelist batch take stage uint256 instead of uint256 [], 3) beneficiary stage to bonus mapping, 4) beneficiary to tokens purchased mapping, 5) ability to set current stage to a smaller value (only check that current stage > 0). Added updateWhitelist.js script showing how to upload the whitelist 1-by-1 or in batch. This commit also includes moving to ganache-cli with the Byzantium features and a bunch of code refactoring. --- build/deploy_results.txt | 32 +- build/test_results.txt | 923 ++++++++++-------- contracts/BluzelleToken.sol | 2 +- contracts/BluzelleTokenSale.sol | 65 +- contracts/BluzelleTokenSaleConfig.sol | 21 +- contracts/{ => Enuma}/ERC20Interface.sol | 4 +- contracts/{ => Enuma}/ERC20Token.sol | 4 +- contracts/{ => Enuma}/Finalizable.sol | 4 +- contracts/{ => Enuma}/FinalizableToken.sol | 4 +- contracts/{ => Enuma}/FlexibleTokenSale.sol | 48 +- .../{ => Enuma}/FlexibleTokenSaleMock.sol | 4 +- contracts/{ => Enuma}/Math.sol | 4 +- contracts/{ => Enuma}/MathTest.sol | 4 +- contracts/{ => Enuma}/OpsManaged.sol | 4 +- contracts/{ => Enuma}/Owned.sol | 4 +- tools/package.json => package.json | 6 +- tests/BluzelleEndToEnd.js | 46 +- tests/BluzelleTokenSale.js | 269 +++-- tests/{ => Enuma}/ERC20Token.js | 25 +- tests/{ => Enuma}/Finalizable.js | 9 +- tests/{ => Enuma}/FinalizableToken.js | 155 ++- tests/{ => Enuma}/FlexibleTokenSale.js | 105 +- tests/Enuma/FlexibleTokenSale_EndToEnd.js | 355 +++++++ .../FlexibleTokenSale_buyTokens.js | 39 +- tests/{ => Enuma}/Math.js | 30 +- tests/{ => Enuma}/OpsManaged.js | 13 +- tests/{ => Enuma}/Owned.js | 17 +- tests/{ => Enuma}/lib/StdTestUtils.js | 15 +- tests/lib/BluzelleTestUtils.js | 159 ++- tools/build.sh | 2 +- tools/deploy.js | 41 +- tools/runTests.js | 73 +- tools/{startTestRpc.sh => startGanache.sh} | 2 +- tools/testlib.js | 52 +- tools/updateWhitelist.js | 155 +++ tools/utils.js | 29 +- 36 files changed, 1864 insertions(+), 860 deletions(-) rename contracts/{ => Enuma}/ERC20Interface.sol (96%) rename contracts/{ => Enuma}/ERC20Token.sol (97%) rename contracts/{ => Enuma}/Finalizable.sol (92%) rename contracts/{ => Enuma}/FinalizableToken.sol (97%) rename contracts/{ => Enuma}/FlexibleTokenSale.sol (85%) rename contracts/{ => Enuma}/FlexibleTokenSaleMock.sol (93%) rename contracts/{ => Enuma}/Math.sol (93%) rename contracts/{ => Enuma}/MathTest.sol (93%) rename contracts/{ => Enuma}/OpsManaged.sol (95%) rename contracts/{ => Enuma}/Owned.sol (96%) rename tools/package.json => package.json (77%) rename tests/{ => Enuma}/ERC20Token.js (93%) rename tests/{ => Enuma}/Finalizable.js (87%) rename tests/{ => Enuma}/FinalizableToken.js (89%) rename tests/{ => Enuma}/FlexibleTokenSale.js (86%) create mode 100644 tests/Enuma/FlexibleTokenSale_EndToEnd.js rename tests/{ => Enuma}/FlexibleTokenSale_buyTokens.js (87%) rename tests/{ => Enuma}/Math.js (87%) rename tests/{ => Enuma}/OpsManaged.js (94%) rename tests/{ => Enuma}/Owned.js (86%) rename tests/{ => Enuma}/lib/StdTestUtils.js (97%) rename tools/{startTestRpc.sh => startGanache.sh} (99%) create mode 100755 tools/updateWhitelist.js diff --git a/build/deploy_results.txt b/build/deploy_results.txt index dfc0ce8..35f9400 100644 --- a/build/deploy_results.txt +++ b/build/deploy_results.txt @@ -1,42 +1,42 @@ ---------------------------------------------------------------------------------- Deploying contract BluzelleToken -TxID : 0xdda9d5411797c44409c82923407d14db9211a15fa696daa4d150f417b6424b74 -Address : 0x70F9c3eE1F5C3B271c69F82B9F2473fe5ae87Ca2 +TxID : 0x2e360c9b6f24ef727b77f8ea46cdd9ea6ab73c336f050aeae960cae9d17ca219 +Address : 0x5eB79615b559068283567D11171278CFA9D89272 Gas used : 1082317 Deploying contract BluzelleTokenSale -TxID : 0xc52703c5aec67f40de52fb7f7df599f8b0e390d6e9c4a34b946664f186c7676c -Address : 0xef76D94108608F5a7585E703A73B5091bfBc2c7A -Gas used : 2128016 +TxID : 0xcc5d751e721b2c5d8643704e52817c53a0d4bb1b184db557e71ddc7714157d5d +Address : 0xbE94A9985E0f55ED1D61f74F26e7E7D6B67720A1 +Gas used : 2180737 Initializing the sale contract -TxID : 0xfaa14081b318a0909243cfbfc280801be8464b53727e3b1199f0072b08769739 -Gas used : 69539 +TxID : 0xc33aaf3a2fc0446055cccb4ba621ba4c0b052ab2622c379dfbdb4c5718658259 +Gas used : 69517 Setting the ops key of the token to the sale contract -TxID : 0xa1e6d923fadfeaeaa972860a47a94a2f1ab5901c758185bcbea0bf0e6f764887 +TxID : 0xbd818a6afad7bed3918c744e151e30c17938cda479aa88d34d69b450e204d235 Gas used : 45481 Setting the ops key of the sale to a ops key -TxID : 0xe4908bfbc8d2c64fa3edf682cbf419ffc05350812c0958b486bceac330a9eef3 -Gas used : 45808 +TxID : 0x8755ab19742f78ac16eed928b6cb26eccc716abd91f483b306268fdf7d790161 +Gas used : 45830 -Sending presale tokens to the sale contract -TxID : 0xf5e2a77c5db5108056f0423b8de66437d1494185909cd24c47e3ed4f5b149e79 +Sending initial sale tokens to the sale contract +TxID : 0xaee571bea6b7eac5e3ceb81ab0e295e64ba826a017f42f77194586ef5a5bb2f6 Gas used : 52999 ---------------------------------------------------------------------------------- Gas usage summary ---------------------------------------------------------------------------------- BluzelleToken.new 1082317 -BluzelleTokenSale.new 2128016 -BluzelleTokenSale.initialize 69539 +BluzelleTokenSale.new 2180737 +BluzelleTokenSale.initialize 69517 BluzelleToken.setOpsAddress 45481 -BluzelleTokenSale.setOpsAddress 45808 +BluzelleTokenSale.setOpsAddress 45830 BluzelleToken.transfer 52999 ---------------------------------------------------------------------------------- -Total gas recorded 3424160 +Total gas recorded 3476881 Deployment completed successfully. diff --git a/build/test_results.txt b/build/test_results.txt index fc43cd9..5b5700d 100644 --- a/build/test_results.txt +++ b/build/test_results.txt @@ -1,134 +1,9 @@ - Bluzelle End-To-End Scenario - Initial deployment -Deploying contract BluzelleToken -TxID : 0xe0b76b43e5ce802ccdc45b237b4d47345ff70d0965e8be45622efcba814612f1 -Address : 0x833a3108D7b2C745778AeaD093f940eA82A7D9eB -Gas used : 1082317 - ✓ Deploy the token contract (101ms) -Deploying contract BluzelleTokenSaleMock -TxID : 0x1f93cd3370f8f83e944872bb52d5cf4169aaa295e1a053e183c2c6d844009fdc -Address : 0x05Ec4018ABE16bCd11D6a5e46CED5B7A5addbad0 -Gas used : 2177150 - ✓ Deploy the sale contract (137ms) - ✓ Initialize the sale contract (119ms) - ✓ Set the ops key of the token to the sale contract (54ms) - ✓ Set the ops key of the sale to a ops key (52ms) - Before presale - ✓ Update the whitelist 1-by-1 (135ms) - ✓ Update the whitelist in batch (202ms) - ✓ Set the sale window (95ms) - ✓ Set bonus amount (50ms) - ✓ Set per account contribution limit (55ms) - ✓ Set the token price (49ms) - ✓ Give tokens to the sale contract (82ms) - During Presale - ✓ Contributor makes purchase (618ms) - ✓ Add another person to the whitelist (52ms) - ✓ Contributor makes purchase on behalf of another whitelisted account (626ms) - ✓ Suspend the sale (48ms) - ✓ Change the bonus amount to 11500 (15% bonus) (53ms) - ✓ Resume the sale (44ms) - ✓ Contributor makes purchase (609ms) - ✓ Change the time window to end the presale early (320ms) - After Presale - ✓ Reclaim unsold tokens (123ms) - Before Public Sale - ✓ Set new time window for the public sale (67ms) - ✓ Set a new bonus amount (51ms) - ✓ Update whitelist with new applicants (124ms) - ✓ Set per account contribution limit (51ms) - ✓ Set the token price (52ms) - ✓ Give tokens to the sale contract (53ms) - ✓ Set current stage (51ms) - During Public Sale - ✓ Contributor buys max allowed tokens (615ms) - ✓ Raise per account contribution limit (56ms) - ✓ Contributor buys max allowed tokens (623ms) - ✓ Remove per account contribution limit - ✓ Contributor buys all remaining tokens (659ms) - After Public Sale - ✓ Reclaim unsold tokens (111ms) - ✓ Finalize the token (69ms) - ✓ Finalize the sale (77ms) - - BluzelleTokenSale Contract -Deploying contract BluzelleToken -TxID : 0xb817dc8d2b4edec9ddf77fdd14090f5bedea41cfc3acc73f2a70ebff04e6042c -Address : 0x9797B7d055A13310da357b8cE42Ab6BA5Dd5152f -Gas used : 1082317 -Deploying contract BluzelleTokenSaleMock -TxID : 0x44d57c93445110d2071edea4a53656fd43af0eaa932831952a614f96c2bb83d5 -Address : 0xaD75829ac68cA98f932Bcacc0948DbDAD7445f93 -Gas used : 2177150 - Construction and basic properties - ✓ startTime - ✓ endTime - ✓ suspended - ✓ tokensPerKEther (50ms) - ✓ bonus - ✓ maxTokensPerAccount - ✓ contributionMin - ✓ walletAddress - ✓ token - ✓ totalTokensSold - ✓ totalEtherCollected - ✓ finalized - ✓ opsAddress - ✓ owner - ✓ proposedOwner - ✓ whitelist - ✓ currentStage - setCurrentStage - ✓ setCurrentStage(0) - ✓ setCurrentStage(1) (53ms) - ✓ setCurrentStage(2) (96ms) - ✓ setCurrentStage(1) - ✓ setCurrentStage(0) - ✓ setCurrentStage(100) (107ms) - setWhitelistedStatus - ✓ setWhitelistedStatus(0, 1) - ✓ setWhitelistedStatus(this, 1) - ✓ setWhitelistedStatus(wallet, 1) - ✓ setWhitelistedStatus(owner, 1) (85ms) - ✓ setWhitelistedStatus(ops, 1) (110ms) - ✓ setWhitelistedStatus(normal, 0) (87ms) - ✓ setWhitelistedStatus(normal, 1) (88ms) - ✓ setWhitelistedStatus(normal, 2) (77ms) - ✓ setWhitelistedStatus(normal, 1) (83ms) - ✓ setWhitelistedStatus(normal, 0) (85ms) - ✓ setWhitelistedStatus as ops (92ms) - ✓ setWhitelistedStatus as normal - setWhitelistedBatch - ✓ setWhitelistedBatch with empty batch - ✓ setWhitelistedBatch with array length mismatch - ✓ setWhitelistedBatch as normal - ✓ setWhitelistedBatch as ops (272ms) - ✓ setWhitelistedBatch as owner (240ms) - ✓ rerun same batch again (232ms) - ✓ remove everybody from whitelist (243ms) - buyTokens - whitelist -Deploying contract BluzelleToken -TxID : 0x203226b3546573841ee888eb6e41566fa1015ff21408937de512ccb12fd6b086 -Address : 0xb8D4E82442c42325eF09c7c5a0FaCDEAf6963a78 -Gas used : 1082317 -Deploying contract BluzelleTokenSaleMock -TxID : 0xdd3d4b3ad83300194b48790253be7d1c75ae3b09ba0d74b5f8409d95dd146ead -Address : 0x536A9898837d7fF8e74a534af118efF196C6A8d6 -Gas used : 2177150 - ✓ buyTokens without being whitelisted (225ms) - ✓ buyTokens stage 1, whitelisted stage 1 (611ms) - ✓ buyTokens stage 1, whitelisted stage 2 (274ms) - ✓ buyTokens stage 2, whitelisted stage 2 (622ms) - ✓ buyTokens stage 2, whitelisted stage 3 (252ms) - ✓ buyTokens stage 3, whitelisted stage 1 (620ms) - ✓ buyTokens stage 2, removed from whitelist (237ms) - ERC20Token Contract Deploying contract ERC20Token -TxID : 0xa1b4d74196cd8a665075c0f349b432e5d2230295d528815c7d6e564f9a4bd864 -Address : 0xfD0aD48b0B727b26586FBC1F4daCcE2DaE5B14A1 +TxID : 0x9a60f587a3b756fd52f76be7e3377b3a38808d939665a2e54c78753dbf0650b0 +Address : 0x5E3238B7f3Db413411DFB991881553524e66a4aD Gas used : 620094 Construction and basic properties ✓ name @@ -144,62 +19,62 @@ Gas used : 620094 ✓ balanceOf(this) ✓ balanceOf(tokenHolder) transfer function - ✓ transfer 0 tokens (63ms) - ✓ transfer 1 to address 0 (66ms) - ✓ transfer 1 to this (70ms) - ✓ transfer > balance to other account (39ms) - ✓ transfer balance to other account (146ms) - ✓ transfer 1 to other account, while balance = 0 (39ms) - ✓ transfer all tokens back to token holder (127ms) + ✓ transfer 0 tokens (45ms) + ✓ transfer 1 to address 0 (45ms) + ✓ transfer 1 to this (43ms) + ✓ transfer > balance to other account + ✓ transfer balance to other account (71ms) + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back to token holder (130ms) transferFrom function - ✓ transferFrom 0 address 0 to other account (76ms) - ✓ transferFrom 1 address 0 to other account (38ms) - ✓ transferFrom 0 tokenHolder to address 0 (71ms) - ✓ transferFrom 1 tokenHolder to address 0 (104ms) - ✓ transferFrom 0 tokenHolder to this (74ms) - ✓ transferFrom 1 tokenHolder to this (103ms) - ✓ transferFrom 0 tokenHolder to other account (74ms) - ✓ transferFrom 1 tokenHolder to other account, no allowance - ✓ transferFrom 1 tokenHolder to other account (100ms) - ✓ transferFrom 1 other account to this (145ms) - ✓ transferFrom 1 while allowance is 0 (98ms) - ✓ transferFrom 10 while allowance is 1 (75ms) - ✓ transferFrom 10 while allowance is 10 (139ms) - ✓ transferFrom 10 again (47ms) - ✓ transferFrom 5 while allowance is 10 (143ms) - ✓ transferFrom 1 after allowance changed from 5 -> 0 (77ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (248ms) + ✓ transferFrom 0 address 0 to other account (45ms) + ✓ transferFrom 1 address 0 to other account + ✓ transferFrom 0 tokenHolder to address 0 (49ms) + ✓ transferFrom 1 tokenHolder to address 0 (74ms) + ✓ transferFrom 0 tokenHolder to this (44ms) + ✓ transferFrom 1 tokenHolder to this (70ms) + ✓ transferFrom 0 tokenHolder to other account (44ms) + ✓ transferFrom 1 tokenHolder to other account, no allowance + ✓ transferFrom 1 tokenHolder to other account (101ms) + ✓ transferFrom 1 other account to this (86ms) + ✓ transferFrom 1 while allowance is 0 (86ms) + ✓ transferFrom 10 while allowance is 1 (47ms) + ✓ transferFrom 10 while allowance is 10 (91ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (102ms) + ✓ transferFrom 1 after allowance changed from 5 -> 0 (51ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (174ms) approve function ✓ approve(0, 0) ✓ approve(0, 1) ✓ approve(this, 1) ✓ approve(other account, 0) ✓ approve(other account, 1) - ✓ approve(other account, > balance) (120ms) - ✓ approve amount without approving 0 first (89ms) + ✓ approve(other account, > balance) (124ms) + ✓ approve amount without approving 0 first (77ms) allowance function ✓ allowance(0,0) ✓ allowance(this,this) ✓ allowance(this,other account) - ✓ allowance(other account, yet another account) (41ms) + ✓ allowance(other account, yet another account) (43ms) Finalizable Contract Deploying contract Finalizable -TxID : 0xae0e0abad4e42d1f1323517bd8c5a1a1ad40d42a25adb22ddf198028cb1c50c7 -Address : 0x2c97C75E189B840d8a6B7FEFfb888f52Af679c13 +TxID : 0xa2b1cfab3a13cf255fa58aa467b3ddee6194febb42d46a571d65e3b0364026fb +Address : 0x264f7FA55598bed601cB03565464Cb419FB22dE4 Gas used : 335643 Construction and basic properties ✓ finalized ✓ owner finalize function ✓ finalize as normal - ✓ finalize as owner (53ms) + ✓ finalize as owner (39ms) ✓ finalize as owner, when already finalized FinalizableToken Contract Deploying contract FinalizableToken -TxID : 0xdb1399910c62e680de9dbd82fdfb1b931fd04d6d5b7ab7749dd3d37b1d5f65d7 -Address : 0x5210BEAaB692FF3A0D95CA20691da99D427390b5 +TxID : 0xd6d33c511e2579f27aec36d1f0c7df0b692864316c0cb3a92378336f4e1ebf77 +Address : 0x45Cde522077817E7E887Eb6AaF284d931424Ef1F Gas used : 1007986 Construction and basic properties ✓ name @@ -220,217 +95,217 @@ Gas used : 1007986 before finalize transfer function sender = owner - ✓ transfer 0 tokens (84ms) - ✓ transfer 1 to address 0 (73ms) - ✓ transfer 1 to this (106ms) - ✓ transfer > balance to other account (52ms) - ✓ transfer balance to other account (139ms) - ✓ transfer 1 to other account, while balance = 0 (59ms) - ✓ transfer all tokens back to token holder (67ms) + ✓ transfer 0 tokens (48ms) + ✓ transfer 1 to address 0 (43ms) + ✓ transfer 1 to this (46ms) + ✓ transfer > balance to other account + ✓ transfer balance to other account (73ms) + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back to token holder (54ms) sender = ops Deploying contract FinalizableToken -TxID : 0x2aa9db93fa3d8a839c997faaf3fff82dc0fa57b437021863d3d1ac80eec574a5 -Address : 0xEe597dFe24047F7BF96749ba10728B2CfdaAF2E6 +TxID : 0x946e0c6a82977fd5d1b055b078296467fdfea6f344d77515f4f70acc0bbff6f4 +Address : 0x75D199cc4B51BA3CcA64D3E83Bc5018DBefaE94c Gas used : 1007986 - ✓ transfer 0 tokens (90ms) - ✓ transfer 1 to address 0 (88ms) - ✓ transfer 1 to this (81ms) - ✓ transfer > balance to other account (68ms) - ✓ transfer balance to other account (148ms) - ✓ transfer 1 to other account, while balance = 0 (58ms) - ✓ transfer all tokens back (84ms) + ✓ transfer 0 tokens (49ms) + ✓ transfer 1 to address 0 (44ms) + ✓ transfer 1 to this (48ms) + ✓ transfer > balance to other account + ✓ transfer balance to other account (73ms) + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back (39ms) sender = normal ✓ transfer 0 tokens - ✓ transfer 1 to address 0 (42ms) - ✓ transfer 1 to this (38ms) - ✓ transfer > balance to other account (57ms) - ✓ transfer balance to other account (64ms) - ✓ transfer 1 to other account, while balance = 0 (57ms) - ✓ transfer all tokens back to account1 (61ms) + ✓ transfer 1 to address 0 + ✓ transfer 1 to this + ✓ transfer > balance to other account + ✓ transfer balance to other account + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back to account1 transferFrom function sender = owner - ✓ transferFrom 0 address 0 to other account (82ms) - ✓ transferFrom 1 address 0 to other account (51ms) - ✓ transferFrom 0 owner to address 0 (88ms) - ✓ transferFrom 1 owner to address 0 (118ms) - ✓ transferFrom 0 owner to this (91ms) - ✓ transferFrom 1 owner to this (113ms) - ✓ transferFrom 0 owner to other account (88ms) - ✓ transferFrom 0 ops to other account (108ms) - ✓ transferFrom 1 ops to other account (151ms) - ✓ transferFrom 1 owner to other account, no allowance (42ms) - ✓ transferFrom 1 owner to other account (117ms) - ✓ transferFrom 1 other account to this (134ms) - ✓ transferFrom 1 while allowance is 0 (89ms) - ✓ transferFrom 10 while allowance is 1 (93ms) - ✓ transferFrom 10 while allowance is 10 (148ms) - ✓ transferFrom 10 again (59ms) - ✓ transferFrom 5 while allowance is 10 (154ms) - ✓ transferFrom 1 after allowance changed from 5 -> 0 (133ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (329ms) + ✓ transferFrom 0 address 0 to other account (48ms) + ✓ transferFrom 1 address 0 to other account + ✓ transferFrom 0 owner to address 0 (48ms) + ✓ transferFrom 1 owner to address 0 (73ms) + ✓ transferFrom 0 owner to this (43ms) + ✓ transferFrom 1 owner to this (99ms) + ✓ transferFrom 0 owner to other account (48ms) + ✓ transferFrom 0 ops to other account (41ms) + ✓ transferFrom 1 ops to other account (101ms) + ✓ transferFrom 1 owner to other account, no allowance + ✓ transferFrom 1 owner to other account (74ms) + ✓ transferFrom 1 other account to this (84ms) + ✓ transferFrom 1 while allowance is 0 (133ms) + ✓ transferFrom 10 while allowance is 1 (61ms) + ✓ transferFrom 10 while allowance is 10 (118ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (94ms) + ✓ transferFrom 1 after allowance changed from 5 -> 0 (60ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (200ms) sender = ops - ✓ transferFrom 0 address 0 to other account (100ms) - ✓ transferFrom 1 address 0 to other account (51ms) - ✓ transferFrom 0 owner to address 0 (98ms) - ✓ transferFrom 1 owner to address 0 (120ms) - ✓ transferFrom 0 owner to this (125ms) - ✓ transferFrom 1 owner to this (126ms) - ✓ transferFrom 0 owner to other account (97ms) - ✓ transferFrom 1 owner to other account, no allowance (56ms) - ✓ transferFrom 1 owner to other account (124ms) - ✓ transferFrom 0 ops to other account (94ms) - ✓ transferFrom 1 ops to other account (132ms) - ✓ transferFrom 1 other account to this (150ms) - ✓ transferFrom 1 while allowance is 0 (86ms) - ✓ transferFrom 10 while allowance is 1 (122ms) - ✓ transferFrom 10 while allowance is 10 (167ms) - ✓ transferFrom 10 again (75ms) - ✓ transferFrom 5 while allowance is 10 (164ms) - ✓ transferFrom 1 after allowance changed from 5 -> 0 (99ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (380ms) + ✓ transferFrom 0 address 0 to other account (44ms) + ✓ transferFrom 1 address 0 to other account + ✓ transferFrom 0 owner to address 0 (43ms) + ✓ transferFrom 1 owner to address 0 (74ms) + ✓ transferFrom 0 owner to this (49ms) + ✓ transferFrom 1 owner to this (74ms) + ✓ transferFrom 0 owner to other account (43ms) + ✓ transferFrom 1 owner to other account, no allowance + ✓ transferFrom 1 owner to other account (75ms) + ✓ transferFrom 0 ops to other account (47ms) + ✓ transferFrom 1 ops to other account (75ms) + ✓ transferFrom 1 other account to this (82ms) + ✓ transferFrom 1 while allowance is 0 (67ms) + ✓ transferFrom 10 while allowance is 1 (52ms) + ✓ transferFrom 10 while allowance is 10 (102ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (93ms) + ✓ transferFrom 1 after allowance changed from 5 -> 0 (54ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (172ms) sender = normal - ✓ transferFrom 0 address 0 to other account (40ms) + ✓ transferFrom 0 address 0 to other account ✓ transferFrom 1 address 0 to other account - ✓ transferFrom 0 owner to address 0 (38ms) - ✓ transferFrom 1 owner to address 0 (66ms) - ✓ transferFrom 0 owner to this (38ms) - ✓ transferFrom 1 owner to this (66ms) + ✓ transferFrom 0 owner to address 0 + ✓ transferFrom 1 owner to address 0 (42ms) + ✓ transferFrom 0 owner to this + ✓ transferFrom 1 owner to this ✓ transferFrom 0 owner to other account - ✓ transferFrom 1 owner to other account, no allowance (45ms) - ✓ transferFrom 1 owner to other account (63ms) + ✓ transferFrom 1 owner to other account, no allowance + ✓ transferFrom 1 owner to other account (40ms) ✓ transferFrom 0 ops to other account - ✓ transferFrom 1 ops to other account (69ms) - ✓ transferFrom 1 other account to this (46ms) - ✓ transferFrom 1 yet another account to another account (78ms) - ✓ transferFrom 1 while allowance is 0 (116ms) - ✓ transferFrom 10 while allowance is 1 (80ms) - ✓ transferFrom 10 while allowance is 10 (111ms) - ✓ transferFrom 10 again (53ms) - ✓ transferFrom 5 while allowance is 10 (83ms) - ✓ transferFrom 1 after allowance changed from 10 -> 0 (115ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (100ms) + ✓ transferFrom 1 ops to other account + ✓ transferFrom 1 other account to this (61ms) + ✓ transferFrom 1 yet another account to another account (41ms) + ✓ transferFrom 1 while allowance is 0 (54ms) + ✓ transferFrom 10 while allowance is 1 (48ms) + ✓ transferFrom 10 while allowance is 10 (51ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (51ms) + ✓ transferFrom 1 after allowance changed from 10 -> 0 (54ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (58ms) after finalize transfer function sender = owner - ✓ transfer 0 tokens (67ms) - ✓ transfer 1 to address 0 (71ms) - ✓ transfer 1 to this (66ms) - ✓ transfer > balance to other account (48ms) - ✓ transfer balance to other account (148ms) - ✓ transfer 1 to other account, while balance = 0 (43ms) - ✓ transfer all tokens back (56ms) + ✓ transfer 0 tokens (43ms) + ✓ transfer 1 to address 0 (41ms) + ✓ transfer 1 to this (44ms) + ✓ transfer > balance to other account + ✓ transfer balance to other account (78ms) + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back sender = ops Deploying contract FinalizableToken -TxID : 0xc079e7a8a4bdc66d10962ca31cab6844565e065ce701322d2efc259aaa296b27 -Address : 0xF7a4fF922A75f6e9FC31C8D8b769fA71c8313192 +TxID : 0x2e808d82aa9f0fbe2d5d5edc21e0af2ca76e20c3a130458ad5dc69cd46cfda42 +Address : 0x6C4348622aDB7E92c74469a8D52d72d3f4AFE003 Gas used : 1007986 - ✓ transfer 0 tokens (71ms) - ✓ transfer 1 to address 0 (69ms) - ✓ transfer 1 to this (80ms) - ✓ transfer > balance to other account (42ms) - ✓ transfer balance to other account (129ms) - ✓ transfer 1 to other account, while balance = 0 (51ms) - ✓ transfer all tokens back to ops (116ms) + ✓ transfer 0 tokens (69ms) + ✓ transfer 1 to address 0 (40ms) + ✓ transfer 1 to this (45ms) + ✓ transfer > balance to other account + ✓ transfer balance to other account (71ms) + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back to ops (40ms) sender = normal - ✓ transfer 0 tokens (96ms) - ✓ transfer 1 to address 0 (77ms) - ✓ transfer 1 to this (84ms) - ✓ transfer > balance to other account (41ms) - ✓ transfer balance to other account (130ms) - ✓ transfer 1 to other account, while balance = 0 (75ms) - ✓ transfer all tokens back (52ms) + ✓ transfer 0 tokens (43ms) + ✓ transfer 1 to address 0 (41ms) + ✓ transfer 1 to this (44ms) + ✓ transfer > balance to other account + ✓ transfer balance to other account (72ms) + ✓ transfer 1 to other account, while balance = 0 + ✓ transfer all tokens back (41ms) transferFrom function sender = owner - ✓ transferFrom 0 address 0 to other account (84ms) + ✓ transferFrom 0 address 0 to other account (45ms) ✓ transferFrom 1 address 0 to other account - ✓ transferFrom 0 owner to address 0 (87ms) - ✓ transferFrom 1 owner to address 0 (103ms) - ✓ transferFrom 0 owner to this (77ms) - ✓ transferFrom 1 owner to this (105ms) - ✓ transferFrom 0 owner to other account (85ms) - ✓ transferFrom 0 ops to other account (91ms) - ✓ transferFrom 1 ops to other account (142ms) + ✓ transferFrom 0 owner to address 0 (40ms) + ✓ transferFrom 1 owner to address 0 (71ms) + ✓ transferFrom 0 owner to this (45ms) + ✓ transferFrom 1 owner to this (94ms) + ✓ transferFrom 0 owner to other account (43ms) + ✓ transferFrom 0 ops to other account (46ms) + ✓ transferFrom 1 ops to other account (102ms) ✓ transferFrom 1 owner to other account, no allowance - ✓ transferFrom 1 owner to other account (133ms) - ✓ transferFrom 1 other account to this (127ms) - ✓ transferFrom 1 while allowance is 0 (89ms) - ✓ transferFrom 10 while allowance is 1 (85ms) - ✓ transferFrom 10 while allowance is 10 (148ms) - ✓ transferFrom 10 again (51ms) - ✓ transferFrom 5 while allowance is 10 (145ms) - ✓ transferFrom 1 after allowance changed from 5 -> 0 (105ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (331ms) + ✓ transferFrom 1 owner to other account (77ms) + ✓ transferFrom 1 other account to this (80ms) + ✓ transferFrom 1 while allowance is 0 (44ms) + ✓ transferFrom 10 while allowance is 1 (49ms) + ✓ transferFrom 10 while allowance is 10 (89ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (92ms) + ✓ transferFrom 1 after allowance changed from 5 -> 0 (59ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (167ms) sender = ops - ✓ transferFrom 0 address 0 to other account (81ms) + ✓ transferFrom 0 address 0 to other account (47ms) ✓ transferFrom 1 address 0 to other account - ✓ transferFrom 0 owner to address 0 (85ms) - ✓ transferFrom 1 owner to address 0 (108ms) - ✓ transferFrom 0 owner to this (76ms) - ✓ transferFrom 1 owner to this (105ms) - ✓ transferFrom 0 owner to other account (82ms) + ✓ transferFrom 0 owner to address 0 (71ms) + ✓ transferFrom 1 owner to address 0 (68ms) + ✓ transferFrom 0 owner to this (48ms) + ✓ transferFrom 1 owner to this (73ms) + ✓ transferFrom 0 owner to other account (41ms) ✓ transferFrom 1 owner to other account, no allowance - ✓ transferFrom 1 owner to other account (114ms) - ✓ transferFrom 0 ops to other account (77ms) - ✓ transferFrom 1 ops to other account (107ms) - ✓ transferFrom 1 other account to this (152ms) - ✓ transferFrom 1 while allowance is 0 (80ms) - ✓ transferFrom 10 while allowance is 1 (79ms) - ✓ transferFrom 10 while allowance is 10 (156ms) - ✓ transferFrom 10 again (59ms) - ✓ transferFrom 5 while allowance is 10 (151ms) - ✓ transferFrom 1 after allowance changed from 5 -> 0 (83ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (307ms) + ✓ transferFrom 1 owner to other account (68ms) + ✓ transferFrom 0 ops to other account (44ms) + ✓ transferFrom 1 ops to other account (72ms) + ✓ transferFrom 1 other account to this (83ms) + ✓ transferFrom 1 while allowance is 0 (44ms) + ✓ transferFrom 10 while allowance is 1 (52ms) + ✓ transferFrom 10 while allowance is 10 (89ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (90ms) + ✓ transferFrom 1 after allowance changed from 5 -> 0 (50ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (165ms) sender = normal - ✓ transferFrom 0 address 0 to other account (108ms) + ✓ transferFrom 0 address 0 to other account (51ms) ✓ transferFrom 1 address 0 to other account - ✓ transferFrom 0 owner to address 0 (83ms) - ✓ transferFrom 1 owner to address 0 (106ms) - ✓ transferFrom 0 owner to this (74ms) - ✓ transferFrom 1 owner to this (114ms) - ✓ transferFrom 0 owner to other account (87ms) + ✓ transferFrom 0 owner to address 0 (46ms) + ✓ transferFrom 1 owner to address 0 (74ms) + ✓ transferFrom 0 owner to this (41ms) + ✓ transferFrom 1 owner to this (71ms) + ✓ transferFrom 0 owner to other account (48ms) ✓ transferFrom 1 owner to other account, no allowance - ✓ transferFrom 1 owner to other account (109ms) - ✓ transferFrom 0 ops to other account (74ms) - ✓ transferFrom 1 ops to other account (112ms) - ✓ transferFrom 1 other account to this (122ms) - ✓ transferFrom 1 while allowance is 0 (81ms) - ✓ transferFrom 10 while allowance is 1 (104ms) - ✓ transferFrom 10 while allowance is 10 (155ms) - ✓ transferFrom 10 again (57ms) - ✓ transferFrom 5 while allowance is 10 (156ms) - ✓ transferFrom 1 after allowance changed from 5 -> 0 (76ms) - ✓ transferFrom 10 + 10 + 1 while allowance is 20 (335ms) + ✓ transferFrom 1 owner to other account (70ms) + ✓ transferFrom 0 ops to other account (41ms) + ✓ transferFrom 1 ops to other account (72ms) + ✓ transferFrom 1 other account to this (80ms) + ✓ transferFrom 1 while allowance is 0 (48ms) + ✓ transferFrom 10 while allowance is 1 (46ms) + ✓ transferFrom 10 while allowance is 10 (87ms) + ✓ transferFrom 10 again + ✓ transferFrom 5 while allowance is 10 (88ms) + ✓ transferFrom 1 after allowance changed from 5 -> 0 (44ms) + ✓ transferFrom 10 + 10 + 1 while allowance is 20 (214ms) approve function ✓ approve(0, 0) ✓ approve(0, 1) ✓ approve(this, 1) ✓ approve(other account, 0) - ✓ approve(other account, 1) (55ms) - ✓ approve(other account, > balance) (91ms) - ✓ approve amount without approving 0 first (93ms) + ✓ approve(other account, 1) + ✓ approve(other account, > balance) (87ms) + ✓ approve amount without approving 0 first (66ms) allowance function ✓ allowance(0,0) ✓ allowance(this,this) ✓ allowance(this,other account) - ✓ allowance(other account, yet another account) (42ms) + ✓ allowance(other account, yet another account) finalize function Deploying contract FinalizableToken -TxID : 0x24d229850c427edebcfce48ee94ff11a4372c96d168e7fa22f0d634cd4c392fc -Address : 0xd55337bCB23A61c70c690265aA53732a24E68C30 +TxID : 0x6ebb3f112eeb7d5c4183aac992bf58b47bb6e5cd1618e13fdc53a049d4862a20 +Address : 0xE23bcA48d04491cb3Eef622865a2c31223f90307 Gas used : 1007986 - ✓ other account cannot call finalize (38ms) - ✓ ops cannot call finalize (61ms) - ✓ owner can call finalize (118ms) + ✓ other account cannot call finalize + ✓ ops cannot call finalize + ✓ owner can call finalize (62ms) FlexibleTokenSale Contract Deploying contract FinalizableToken -TxID : 0x26f27ba27df90025e9193fd4b46b1b058e0ca8b6d680ee2a00785daf0de1222a -Address : 0x64d17098e234a732769b6BCC4Fed0ff40E28302D +TxID : 0x860485aa9a1c8bd96a25ae8e6f1ec42ae2c82bfeb8ad956d1d630331ef88f650 +Address : 0xDAFA4dCdfa4663F2444b94aa842DbE564ce44Dcf Gas used : 1007986 Deploying contract FlexibleTokenSale -TxID : 0x46702c6f8b39eb200b2bf18466c714f1875b2b25441dc3774314b45d485c0985 -Address : 0xF10F0F070Ac34c9D3200d35dE550F3597b13EE94 -Gas used : 1631164 +TxID : 0xa25961f6bd8562a9c05796262faa97855a0b8fadc350820f6464a6577cd28ca8 +Address : 0xf68e24016E432b5FfC45C20D78F9aba21f3A3ba8 +Gas used : 1627834 Construction and basic properties ✓ startTime ✓ endTime @@ -445,7 +320,7 @@ Gas used : 1631164 ✓ totalEtherCollected ✓ finalized ✓ opsAddress - ✓ owner (46ms) + ✓ owner ✓ proposedOwner isOwner (inherited) ✓ isOwner(owner) @@ -466,150 +341,198 @@ Gas used : 1631164 ✓ initialize(0) ✓ initialize(this) ✓ initialize(owner) - ✓ initialize(ops) (41ms) + ✓ initialize(ops) ✓ initialize(wallet) ✓ initialize as normal ✓ initialize as ops - ✓ initialize as owner (102ms) + ✓ initialize as owner (57ms) setWalletAddress ✓ setWalletAddress(0) ✓ setWalletAddress(this) ✓ setWalletAddress(owner) ✓ setWalletAddress(ops) - ✓ setWalletAddress(wallet) (117ms) + ✓ setWalletAddress(wallet) (61ms) ✓ setWalletAddress(token) ✓ setWalletAddress as normal ✓ setWalletAddress as ops - ✓ setWalletAddress as owner (193ms) + ✓ setWalletAddress as owner (98ms) setMaxTokensPerAccount - ✓ setMaxTokensPerAccount(0) (53ms) - ✓ setMaxTokensPerAccount(1) (54ms) - ✓ setMaxTokensPerAccount(100,000,000 * 10**18) (51ms) - ✓ setMaxTokensPerAccount as ops (44ms) + ✓ setMaxTokensPerAccount(0) (40ms) + ✓ setMaxTokensPerAccount(1) + ✓ setMaxTokensPerAccount(100,000,000 * 10**18) (40ms) + ✓ setMaxTokensPerAccount as ops ✓ setMaxTokensPerAccount as normal setTokensPerKEther ✓ setTokensPerKEther(0) - ✓ setTokensPerKEther(1) (51ms) - ✓ setTokensPerKEther(100,000,000 * 10**18) (52ms) - ✓ setTokensPerKEther as ops (39ms) + ✓ setTokensPerKEther(1) (39ms) + ✓ setTokensPerKEther(100,000,000 * 10**18) + ✓ setTokensPerKEther as ops ✓ setTokensPerKEther as normal setBonus - ✓ setBonus(0) - ✓ setBonus(9999) - ✓ setBonus(10000) (55ms) - ✓ setBonus(20000) (46ms) - ✓ setBonus(20100) + ✓ setBonus(0) (38ms) + ✓ setBonus(750) + ✓ setBonus(1500) (40ms) + ✓ setBonus(10000) + ✓ setBonus(10001) ✓ setBonus as ops ✓ setBonus as normal setSaleWindow ✓ setSaleWindow(0, 0) ✓ setSaleWindow(0, 1) - ✓ setSaleWindow(1, 2) (57ms) - ✓ setSaleWindow(now - 1000, now - 500) (71ms) + ✓ setSaleWindow(1, 2) + ✓ setSaleWindow(now - 1000, now - 500) (68ms) ✓ setSaleWindow(now, now) - ✓ setSaleWindow(now, now + 1) (64ms) - ✓ setSaleWindow(now + 1 month, now + 2 months) (108ms) + ✓ setSaleWindow(now, now + 1) + ✓ setSaleWindow(now + 1 month, now + 2 months) (41ms) ✓ setSaleWindow as ops ✓ setSaleWindow as normal suspend and resume Deploying contract FlexibleTokenSaleMock -TxID : 0xe476498be88b47ad9d9002a08175d65a84945c7d8b61fbabc09bdd25b298bb7b -Address : 0x9a27dD8D1245Ea7827cE65EA0D4f010FF8b519Dc -Gas used : 1680586 - ✓ suspend / resume before sale (178ms) - ✓ suspend / resume during sale (166ms) - ✓ suspend / resume after sale (194ms) - ✓ suspend before sale, resume during sale (194ms) - ✓ suspend during sale, resume after sale (220ms) - ✓ suspend when suspended (233ms) - ✓ resume when resumed (354ms) - ✓ suspend / resume as ops (112ms) - ✓ suspend / resume as normal (95ms) +TxID : 0x91a38f0779fd12aa59972d37be8821678137eaf1d472a9cfd31608bedf9c3c27 +Address : 0xA30fe0F715cc0d9148c5bcF474404Cca0c8C6919 +Gas used : 1677384 + ✓ suspend / resume before sale (119ms) + ✓ suspend / resume during sale (119ms) + ✓ suspend / resume after sale (121ms) + ✓ suspend before sale, resume during sale (143ms) + ✓ suspend during sale, resume after sale (144ms) + ✓ suspend when suspended (165ms) + ✓ resume when resumed (92ms) + ✓ suspend / resume as ops (54ms) + ✓ suspend / resume as normal (58ms) finalize Deploying contract FlexibleTokenSaleMock -TxID : 0x2aa975dfb87cf397676d3523b131908a9568459a91594c8555f7dcc8c7da9445 -Address : 0x300ea48c7401aF1502D9D97A5F2A303d6D8905D5 -Gas used : 1680586 - ✓ finalize as normal (50ms) - ✓ finalize as ops (58ms) - ✓ finalize as owner (152ms) +TxID : 0xffc3937018e7933d00a7e358073a33a012c54179a91f0f00f5b776a81fca08cd +Address : 0xFf182f18CFdFc83f258858626382079f7fD69b14 +Gas used : 1677384 + ✓ finalize as normal + ✓ finalize as ops + ✓ finalize as owner (72ms) reclaimTokens Deploying contract FlexibleTokenSaleMock -TxID : 0x8e1abfb6e27f16e956650ed5bfcb2b32722c5d24ab175720190721fac3e2ac2c -Address : 0xB8B6f9c2C8fBe365b8AFb7Ae0eF085Fb2A089196 -Gas used : 1680586 +TxID : 0x7ada60ee9bfddb8da03d583e97226cb5d776ab09ab0e6606ccaebfdbb0e1b944 +Address : 0xF2b8058b4e2195e55dF338C79c47f4525CFc2961 +Gas used : 1677384 before finalize - ✓ reclaimTokens as owner (239ms) + ✓ reclaimTokens as owner (111ms) after finalize ✓ reclaimTokens as normal ✓ reclaimTokens as ops - ✓ reclaimTokens as owner (248ms) - ✓ reclaimTokens as owner when 0 balance (90ms) + ✓ reclaimTokens as owner (111ms) + ✓ reclaimTokens as owner when 0 balance (42ms) + + FlexibleTokenSale End-To-End Scenario + Initial deployment +Deploying contract FinalizableToken +TxID : 0xfd9a33fcb85105bb967c16c9bb28944cf56d4cfe908e3a16bb5958cb7ea46704 +Address : 0xF6a28414Dd1A8B6671eE14164a6630643119C69f +Gas used : 1009202 + ✓ Deploy the token contract (52ms) +Deploying contract FlexibleTokenSaleMock +TxID : 0xca02be7c5db6e9ba2b33fc2f405752ae6532133a38cb65ab3cfd53a7dece46d8 +Address : 0x3eeEf2daf4B18209EB3b3E74078B14E6BcF5F744 +Gas used : 1677384 + ✓ Deploy the sale contract (59ms) + ✓ Initialize the sale contract (77ms) + ✓ Set the ops key of the token to the sale contract + ✓ Set the ops key of the sale to a ops key (38ms) + Before presale + ✓ Set the sale window (46ms) + ✓ Set bonus amount + ✓ Set per account contribution limit + ✓ Set the token price (42ms) + ✓ Give tokens to the sale contract (39ms) + During Presale + ✓ Contributor makes purchase (245ms) + ✓ Contributor makes purchase on behalf of another account (244ms) + ✓ Suspend the sale + ✓ Change the bonus amount to 1500 (15.00% bonus) (38ms) + ✓ Resume the sale + ✓ Contributor makes purchase (271ms) + ✓ Change the time window to end the presale early (162ms) + After Presale + ✓ Reclaim unsold tokens (79ms) + Before Public Sale + ✓ Set new time window for the public sale (42ms) + ✓ Set a new bonus amount + ✓ Set per account contribution limit + ✓ Set the token price (50ms) + ✓ Give tokens to the sale contract (41ms) + During Public Sale + ✓ Contributor buys max allowed tokens (304ms) + ✓ Raise per account contribution limit + ✓ Contributor buys max allowed tokens (306ms) + ✓ Remove per account contribution limit + ✓ Contributor buys all remaining tokens (339ms) + After Public Sale + ✓ Reclaim unsold tokens (63ms) + ✓ Finalize the token (47ms) + ✓ Finalize the sale (49ms) FlexibleTokenSale Contract - buyTokens tests Deploying contract FinalizableToken -TxID : 0x6a7055e2719726f02804d640772ec1dae7d2c6d795ef926de10c466dd7502600 -Address : 0x6A405f08F69a0788Be5a2F5200c7056D51c2e233 +TxID : 0x1e532ec7729b654367c3ccb0115db59b4bc5b48940e544cd926a744ceaa42b9c +Address : 0xb17d7843F36BC1B46250B990A00f4eeeB24c747a Gas used : 1008050 Deploying contract FlexibleTokenSaleMock -TxID : 0xc82993fc3e0c61f9a615fc1555b5985301d05d5f5b2e7955767d3d0462312497 -Address : 0x29CF44D0cA2CE03dd70f57a3FE6744d61EfEe225 -Gas used : 1680586 +TxID : 0x28461e2697242fe3133814e45d4f12961c8c25088fa5415decb0cecb8bd5a402 +Address : 0x5c30e193A9B80167Ae9B0665E00C3A981Ba74F12 +Gas used : 1677384 buyTokens - ✓ buyTokens as normal (520ms) - ✓ buyTokens as ops (543ms) - ✓ buyTokens as owner (564ms) - ✓ buyTokens for another account (556ms) - ✓ buyTokens for this (223ms) - ✓ buyTokens for wallet (511ms) - ✓ buyTokens for owner (521ms) - ✓ buyTokens for ops (526ms) - ✓ buyTokens for token contract (243ms) - ✓ buyTokens with 0 ETH (224ms) - ✓ buyTokens with 1 wei (265ms) - ✓ buyTokens with minimum contribution - 1 wei (223ms) - ✓ buyTokens with minimum contribution (523ms) - ✓ buyTokens with more ETH than maxTokensPerAccount allows (692ms) - ✓ buyTokens with bonus = 10000 (544ms) - ✓ buyTokens with bonus = 10755 (560ms) - ✓ buyTokens with bonus = 20000 (543ms) - ✓ buyTokens with more ETH than left for sale (622ms) + ✓ buyTokens as normal (262ms) + ✓ buyTokens as ops (245ms) + ✓ buyTokens as owner (241ms) + ✓ buyTokens for another account (239ms) + ✓ buyTokens for this (106ms) + ✓ buyTokens for wallet (269ms) + ✓ buyTokens for owner (239ms) + ✓ buyTokens for ops (234ms) + ✓ buyTokens for token contract (110ms) + ✓ buyTokens with 0 ETH (108ms) + ✓ buyTokens with 1 wei (105ms) + ✓ buyTokens with minimum contribution - 1 wei (133ms) + ✓ buyTokens with minimum contribution (247ms) + ✓ buyTokens with more ETH than maxTokensPerAccount allows (349ms) + ✓ buyTokens with bonus = 0 (267ms) + ✓ buyTokens with bonus = 755 (293ms) + ✓ buyTokens with bonus = 10000 (268ms) + ✓ buyTokens with more ETH than left for sale (293ms) Deploying contract FinalizableToken -TxID : 0x98d3f671d77bcc01bd3dfb5f3ce9709ba2177ecd42b621a2e1439bb41ece9e95 -Address : 0x47e3b4276fa93D3F22614b9B47779731c8841D7D +TxID : 0x7b0e003e15dd39502b07b6125604df8ea435c22f619d2172c8981f175ed15b89 +Address : 0x44589BcB8Fb1Da2C0991a20018abC86bE9FE55eF Gas used : 1008050 Deploying contract FlexibleTokenSaleMock -TxID : 0xaccd1505e4e4aa82068fb5d5e610a0a516baef2b81f4ef931a99975b37f7fc96 -Address : 0x569Dba4B8B4Fba62F38d4031cAff5488e8f09bE4 -Gas used : 1680586 - ✓ buyTokens with less tokens left than maxTokensPerAccount (936ms) +TxID : 0x0f844ecc0f861af8894412342f00ade743212b0708f93f762c042f78b15de9e7 +Address : 0xa483Ed26b14db6c12346FC8e6197B20B87579bB1 +Gas used : 1677384 + ✓ buyTokens with less tokens left than maxTokensPerAccount (616ms) Deploying contract FinalizableToken -TxID : 0xeb5f4bb3109f251fb0bbe0bad6545c8fde11238f3d4ccd5af380e66974806ee4 -Address : 0x1dA98ce2fEbf71D3849AB79f702a7204bdf3aA3F +TxID : 0x9fb5fb4c19a651ef2f90228ed61c6bd2fc48989e0842d5b4db05698396a0e8b0 +Address : 0x0De7d76574A5Fc586A71F843f80bFd105f97126c Gas used : 1008050 Deploying contract FlexibleTokenSaleMock -TxID : 0x10e5947abc57a15c83a12c40539ef88b16af8d2ce447358ec05b50d2aeb12021 -Address : 0x427365f977eEF971Ca224F10CE990C9a717eD5E3 -Gas used : 1680586 - ✓ buyTokens with enough ETH to buy all tokens in a single transaction, and more... (884ms) - ✓ buyTokens with an additional minimum contribution (250ms) +TxID : 0x414afdadc30eeb59f12a1020fd022a4497a6f3ce001af6682ca0fa2ab3e3b08e +Address : 0xE06CFD6699BC62B84f0061Fe9363FfFCC9cD977D +Gas used : 1677384 + ✓ buyTokens with enough ETH to buy all tokens in a single transaction, and more... (552ms) + ✓ buyTokens with an additional minimum contribution (113ms) Deploying contract FinalizableToken -TxID : 0xc2281d9338c054b6e40c285186a458f5a0cb6d4e2ba546114a427753747b0d98 -Address : 0x402b01Bf27aFA8c45Eec4eD9BA350D3419F8ece3 +TxID : 0x4b0eeccdc66efe9650258d018c6b8ea3b13749c32287336fbe80bb3ba54e93dd +Address : 0xf732224fA6E973701970D3FF6e6FB3c08bF27CA2 Gas used : 1008050 Deploying contract FlexibleTokenSaleMock -TxID : 0x050d801098c7f805bbcae092d4e7c785657cbb79d008a1a51c9cd0dadef03977 -Address : 0x42E1380131045f788b5B787466D36E24a2BcBC45 -Gas used : 1680586 - ✓ buyTokens before start time (600ms) - ✓ buyTokens during sale time (533ms) - ✓ buyTokens after end time (283ms) - ✓ buyTokens after finalized (273ms) +TxID : 0x3dcef5c23523598c7076c735b92ef05a28bbb2b5540634496a1811662d4f4f6d +Address : 0x66637Cb78E3685e489d06d591EEF6083d43AA27B +Gas used : 1677384 + ✓ buyTokens before start time (409ms) + ✓ buyTokens during sale time (300ms) + ✓ buyTokens after end time (133ms) + ✓ buyTokens after finalized (158ms) Math Library Deploying contract MathTest -TxID : 0x0ef218ac00d2f4c355963c6e93667336506a0ea8e5f192050d6f7015e892ab66 -Address : 0x56C98741A9503aa28ec38Ef445705bf1efF8e0C5 +TxID : 0xa00a00afaeeca57197b9af8649889f483a6a3926f4a08714f68fa70ea0ea179c +Address : 0xb1615b35F3872D916698d05fdEA54Fa2829e0229 Gas used : 168890 add ✓ add(0, 0) @@ -658,13 +581,13 @@ Gas used : 168890 ✓ div(uint256, 1) ✓ div(uint256, 2) ✓ div(0, uint256) - ✓ div(1, uint256) (50ms) + ✓ div(1, uint256) ✓ div(uint256, uint256) Owned Contract Deploying contract OpsManaged -TxID : 0xdd909a4392272c89c4920032d8a0a3e749506d15ffd709f6a9b6435992e4bf76 -Address : 0x96FB55c6508Eebff0757fEdcF90EE07F3bb1714d +TxID : 0xe50a9e8a8661af9d7c208a1583ec8e00b8893c72a499f193e6c602533712df72 +Address : 0x3a623EA7645fC49C03E2E8EA1096B49d25D97180 Gas used : 379989 Construction and basic properties ✓ opsAddress @@ -701,18 +624,18 @@ Gas used : 379989 ✓ isOwner(owner) ✓ isOwner(ops) setOpsAddress - ✓ setOpsAddress(0) (88ms) + ✓ setOpsAddress(0) (48ms) ✓ setOpsAddress(this) ✓ setOpsAddress(owner) ✓ setOpsAddress(other account) as non-owner - ✓ setOpsAddress(other account) as ops (71ms) - ✓ setOpsAddress(other account) as owner (88ms) - ✓ setOpsAddress(other account) as owner and then set owner to that same address (325ms) + ✓ setOpsAddress(other account) as ops (49ms) + ✓ setOpsAddress(other account) as owner (45ms) + ✓ setOpsAddress(other account) as owner and then set owner to that same address (158ms) Owned Contract Deploying contract Owned -TxID : 0x85c6940a17d64414acffdbcb6035399bf60c28d473b45c88cdc25a36213aa731 -Address : 0xf04cE50F5B377c5Da7d4f4123D63c56aB490073C +TxID : 0x2070f9fa257c840f7ad5cf0744d395aefeb0c0adbbe089dfd0f53c61c4d193c4 +Address : 0xF5C6B8323Bfd6832725D1eCA8eCB0578E6076DaA Gas used : 264774 Construction and basic properties ✓ owner @@ -728,12 +651,156 @@ Gas used : 264774 ✓ initiateOwnershipTransfer(this) ✓ initiateOwnershipTransfer(owner) ✓ initiateOwnershipTransfer(other account), as non-owner - ✓ initiateOwnershipTransfer(other account), as owner (150ms) + ✓ initiateOwnershipTransfer(other account), as owner (80ms) completeOwnershipTransfer ✓ completeOwnershipTransfer(owner) ✓ completeOwnershipTransfer(yet another account) - ✓ completeOwnershipTransfer(other account), as owner (147ms) + ✓ completeOwnershipTransfer(other account), as owner (77ms) + + Bluzelle End-To-End Scenario + Initial deployment +Deploying contract BluzelleToken +TxID : 0x87975f6b8db34239b8349ebda69ca6e65de334be5ace443522cd8401126baa21 +Address : 0x2C42ebA2B1aA8645716E4A647e8AF742534bbDBE +Gas used : 1082317 + ✓ Deploy the token contract (80ms) +Deploying contract BluzelleTokenSaleMock +TxID : 0x11c2837f8433e457cae8b579e5db1749af6101a4e3ef7b0d3c010d01e3a9465a +Address : 0xDdA3e1C7160A7899701148B720B6f417a54d00b7 +Gas used : 2229935 + ✓ Deploy the sale contract (69ms) + ✓ Initialize the sale contract (62ms) + ✓ Set the ops key of the token to the sale contract (39ms) + ✓ Set the ops key of the sale to a ops key (41ms) + Before presale + ✓ Update the whitelist 1-by-1 (89ms) + ✓ Update the whitelist in batch (98ms) + ✓ Set the sale window (51ms) + ✓ Set bonus amount + ✓ Set per account contribution limit (39ms) + ✓ Set the token price + ✓ Give tokens to the sale contract (44ms) + During Presale + ✓ Contributor makes purchase (294ms) + ✓ Add another person to the whitelist (38ms) + ✓ Contributor makes purchase on behalf of another whitelisted account (323ms) + ✓ Suspend the sale (41ms) + ✓ Change the bonus amount to 1500 (15.00% bonus) + ✓ Resume the sale (39ms) + ✓ Contributor makes purchase (292ms) + ✓ Change the time window to end the presale early (203ms) + After Presale + ✓ Reclaim unsold tokens (82ms) + Before Public Sale + ✓ Set new time window for the public sale (50ms) + ✓ Set a new bonus amount + ✓ Update whitelist with new applicants (54ms) + ✓ Set per account contribution limit (64ms) + ✓ Set the token price + ✓ Give tokens to the sale contract (40ms) + ✓ Set current stage + During Public Sale + ✓ Contributor buys max allowed tokens (331ms) + ✓ Raise per account contribution limit + ✓ Contributor buys max allowed tokens (again) (323ms) + ✓ Remove per account contribution limit + ✓ Contributor buys all remaining tokens (351ms) + After Public Sale + ✓ Reclaim unsold tokens (69ms) + ✓ Finalize the token (50ms) + ✓ Finalize the sale (48ms) + + BluzelleTokenSale Contract +Deploying contract BluzelleToken +TxID : 0x81c5878b85b1bfbb975a4e86f05e7c227847ed2802690577d26b628ddbbb7112 +Address : 0x055590B3f1Dec8310B0A5a902b60ee932660518b +Gas used : 1082317 +Deploying contract BluzelleTokenSaleMock +TxID : 0x0a432ab9ffbf3b28d6dbc67ce18430109456a98da8372442f0e3df056ec6d378 +Address : 0x1AEbC8900f7749B137DB2c2232b0F1Bcb52AF18D +Gas used : 2229935 + Construction and basic properties + ✓ startTime + ✓ endTime + ✓ suspended + ✓ tokensPerKEther + ✓ bonus + ✓ maxTokensPerAccount + ✓ contributionMin + ✓ walletAddress + ✓ token + ✓ totalTokensSold + ✓ totalEtherCollected + ✓ finalized + ✓ opsAddress + ✓ owner + ✓ proposedOwner + ✓ whitelist + ✓ currentStage + ✓ stageBonus + ✓ accountTokensPurchased + setCurrentStage + ✓ setCurrentStage(0) + ✓ setCurrentStage(1) + ✓ setCurrentStage(2) (60ms) + ✓ setCurrentStage(1) (53ms) + ✓ setCurrentStage(0) + ✓ setCurrentStage(100) (45ms) + ✓ setCurrentStage as ops + ✓ setCurrentStage as normal + setStageBonus + ✓ setStageBonus(0, 5) + ✓ setStageBonus(1, 5) (46ms) + ✓ setStageBonus(1, 10000) (52ms) + ✓ setStageBonus(1, 10001) + ✓ setStageBonus(2, 750) (83ms) + ✓ setStageBonus(1, 0) (52ms) + ✓ setStageBonus as ops + ✓ setStageBonus as normal + setWhitelistedStatus + ✓ setWhitelistedStatus(0, 1) + ✓ setWhitelistedStatus(this, 1) + ✓ setWhitelistedStatus(wallet, 1) + ✓ setWhitelistedStatus(owner, 1) (52ms) + ✓ setWhitelistedStatus(ops, 1) (56ms) + ✓ setWhitelistedStatus(normal, 0) (51ms) + ✓ setWhitelistedStatus(normal, 1) (56ms) + ✓ setWhitelistedStatus(normal, 2) (54ms) + ✓ setWhitelistedStatus(normal, 1) (49ms) + ✓ setWhitelistedStatus(normal, 0) (53ms) + ✓ setWhitelistedStatus as ops (55ms) + ✓ setWhitelistedStatus as normal + setWhitelistedBatch + ✓ setWhitelistedBatch with empty batch + ✓ setWhitelistedBatch as normal + ✓ setWhitelistedBatch as ops (116ms) + ✓ setWhitelistedBatch as owner (119ms) + ✓ rerun same batch again (125ms) + ✓ remove everybody from whitelist (119ms) + buyTokens - whitelist +Deploying contract BluzelleToken +TxID : 0xe9b694100cbd17d7e9c600a208e97e14227f0e1f4dd8aa6ef886656951557f4c +Address : 0x395F5648E584e0DBf568C9a76Ab016394a689D04 +Gas used : 1082317 +Deploying contract BluzelleTokenSaleMock +TxID : 0x35deb2d881457a1ce64cce9533b6e1bfb876b64a669f4972bac8616362c6c2fe +Address : 0x3D7ABD249F83a678B53Df009Cae7a4ea09d859EC +Gas used : 2229935 + ✓ buyTokens where neither sender nor receiver whitelisted (157ms) + ✓ buyTokens where sender not whitelisted (195ms) + ✓ buyTokens where beneficiary not whitelisted (159ms) + ✓ buyTokens stage 1, whitelisted stage 1 (313ms) + ✓ buyTokens stage 1, whitelisted stage 2 (199ms) + ✓ buyTokens stage 2, whitelisted stage 2 (335ms) + ✓ buyTokens stage 2, whitelisted stage 3 (166ms) + ✓ buyTokens stage 3, whitelisted stage 1 (479ms) + ✓ buyTokens stage 3, after removed from whitelist (181ms) + ✓ buyTokens stage 3, whitelisted stage 1, beneficiary stage bonus > base bonus (420ms) + ✓ buyTokens stage 3, whitelisted stage 1, beneficiary stage bonus > base bonus (551ms) + ✓ buyTokens with limit of 1000 tokens (408ms) + ✓ buyTokens with limit of 1000 tokens and someone else did a proxy purchase already (608ms) + ✓ buyTokens with limit of 1000 tokens and owner already assigned tokens out-of-band (416ms) - 525 passing (54s) + 574 passing (37s) diff --git a/contracts/BluzelleToken.sol b/contracts/BluzelleToken.sol index 1ac156d..06989e7 100644 --- a/contracts/BluzelleToken.sol +++ b/contracts/BluzelleToken.sol @@ -9,7 +9,7 @@ pragma solidity ^0.4.17; // The MIT Licence. // ---------------------------------------------------------------------------- -import "./FinalizableToken.sol"; +import "./Enuma/FinalizableToken.sol"; import "./BluzelleTokenConfig.sol"; diff --git a/contracts/BluzelleTokenSale.sol b/contracts/BluzelleTokenSale.sol index 8dbced4..0174eb7 100644 --- a/contracts/BluzelleTokenSale.sol +++ b/contracts/BluzelleTokenSale.sol @@ -9,7 +9,7 @@ pragma solidity ^0.4.17; // The MIT Licence. // ---------------------------------------------------------------------------- -import "./FlexibleTokenSale.sol"; +import "./Enuma/FlexibleTokenSale.sol"; import "./BluzelleTokenSaleConfig.sol"; @@ -24,6 +24,13 @@ contract BluzelleTokenSale is FlexibleTokenSale, BluzelleTokenSaleConfig { // contribute in the current stage. uint256 public currentStage; + // Keeps track of the amount of bonus to apply for a given stage. If set + // to 0, the base class bonus will be used. + mapping(uint256 => uint256) public stageBonus; + + // Keeps track of the amount of tokens that a specific account has received. + mapping(address => uint256) public accountTokensPurchased; + // This a mapping of address -> stage that they are allowed to participate in. // For example, if someone has been whitelisted for stage 2, they will be able // to participate for stages 2 and above but they would not be able to participate @@ -35,13 +42,14 @@ contract BluzelleTokenSale is FlexibleTokenSale, BluzelleTokenSaleConfig { // Events // event CurrentStageUpdated(uint256 _newStage); + event StageBonusUpdated(uint256 _stage, uint256 _bonus); event WhitelistedStatusUpdated(address indexed _address, uint256 _stage); function BluzelleTokenSale(address wallet) public - FlexibleTokenSale(STAGE1_STARTTIME, STAGE1_ENDTIME, wallet) + FlexibleTokenSale(INITIAL_STARTTIME, INITIAL_ENDTIME, wallet) { - currentStage = 1; + currentStage = INITIAL_STAGE; tokensPerKEther = TOKENS_PER_KETHER; bonus = BONUS; maxTokensPerAccount = TOKENS_ACCOUNT_MAX; @@ -52,7 +60,7 @@ contract BluzelleTokenSale is FlexibleTokenSale, BluzelleTokenSaleConfig { // Allows the admin to determine what is the current stage for // the sale. It can only move forward. function setCurrentStage(uint256 _stage) public onlyOwner returns(bool) { - require(_stage >= currentStage); + require(_stage > 0); if (currentStage == _stage) { return false; @@ -66,6 +74,24 @@ contract BluzelleTokenSale is FlexibleTokenSale, BluzelleTokenSaleConfig { } + // Allows the admin to set a bonus amount to apply for a specific stage. + function setStageBonus(uint256 _stage, uint256 _bonus) public onlyOwner returns(bool) { + require(_stage > 0); + require(_bonus <= 10000); + + if (stageBonus[_stage] == _bonus) { + // Nothing to change. + return false; + } + + stageBonus[_stage] = _bonus; + + StageBonusUpdated(_stage, _bonus); + + return true; + } + + // Allows the owner or ops to add/remove people from the whitelist. function setWhitelistedStatus(address _address, uint256 _stage) public onlyOwnerOrOps returns (bool) { return setWhitelistedStatusInternal(_address, _stage); @@ -88,12 +114,11 @@ contract BluzelleTokenSale is FlexibleTokenSale, BluzelleTokenSaleConfig { // Allows the owner or ops to add/remove people from the whitelist, in batches. This makes // it easier/cheaper/faster to upload whitelist data in bulk. Note that the function is using an // unbounded loop so the call should take care to not exceed the tx gas limit or block gas limit. - function setWhitelistedBatch(address[] _addresses, uint256[] _stages) public onlyOwnerOrOps returns (bool) { + function setWhitelistedBatch(address[] _addresses, uint256 _stage) public onlyOwnerOrOps returns (bool) { require(_addresses.length > 0); - require(_addresses.length == _stages.length); for (uint256 i = 0; i < _addresses.length; i++) { - require(setWhitelistedStatusInternal(_addresses[i], _stages[i])); + require(setWhitelistedStatusInternal(_addresses[i], _stage)); } return true; @@ -103,13 +128,31 @@ contract BluzelleTokenSale is FlexibleTokenSale, BluzelleTokenSaleConfig { // This is an extension to the buyToken function in FlexibleTokenSale which also takes // care of checking contributors against the whitelist. Since buyTokens supports proxy payments // we check that both the sender and the beneficiary have been whitelisted. - function buyTokens(address beneficiary) public payable returns (bool) { + function buyTokensInternal(address _beneficiary, uint256 _bonus) internal returns (uint256) { require(whitelist[msg.sender] > 0); - require(whitelist[beneficiary] > 0); + require(whitelist[_beneficiary] > 0); require(currentStage >= whitelist[msg.sender]); - require(currentStage >= whitelist[beneficiary]); - return super.buyTokens(beneficiary); + uint256 _beneficiaryStage = whitelist[_beneficiary]; + require(currentStage >= _beneficiaryStage); + + uint256 applicableBonus = stageBonus[_beneficiaryStage]; + if (applicableBonus == 0) { + applicableBonus = _bonus; + } + + uint256 tokensPurchased = super.buyTokensInternal(_beneficiary, applicableBonus); + + accountTokensPurchased[_beneficiary] = accountTokensPurchased[_beneficiary].add(tokensPurchased); + + return tokensPurchased; + } + + + // Returns the number of tokens that the user has purchased. We keep a separate balance from + // the token contract in case we'd like to do additional sales with new purchase limits. + function getUserTokenBalance(address _beneficiary) internal view returns (uint256) { + return accountTokensPurchased[_beneficiary]; } } diff --git a/contracts/BluzelleTokenSaleConfig.sol b/contracts/BluzelleTokenSaleConfig.sol index dffc0a6..4b88acf 100644 --- a/contracts/BluzelleTokenSaleConfig.sol +++ b/contracts/BluzelleTokenSaleConfig.sol @@ -17,20 +17,9 @@ contract BluzelleTokenSaleConfig is BluzelleTokenConfig { // // Time // - - uint256 public constant STAGE1_STARTTIME = 1511870400; // 2017-11-28, 12:00:00 UTC - uint256 public constant STAGE1_ENDTIME = 1512043200; // 2017-11-30, 12:00:00 UTC - - - // - // Token Supply - // - uint256 public constant DECIMALSFACTOR = 10**18; - uint256 public constant TOKENS_TOTAL = 500000000 * DECIMALSFACTOR; - uint256 public constant TOKENS_SALE = 200000000 * DECIMALSFACTOR; - uint256 public constant TOKENS_FOUNDERS = 50000000 * DECIMALSFACTOR; - uint256 public constant TOKENS_PARTNERS = 50000000 * DECIMALSFACTOR; - uint256 public constant TOKENS_FUTURE = 200000000 * DECIMALSFACTOR; + uint256 public constant INITIAL_STARTTIME = 1511870400; // 2017-11-28, 12:00:00 UTC + uint256 public constant INITIAL_ENDTIME = 1512043200; // 2017-11-30, 12:00:00 UTC + uint256 public constant INITIAL_STAGE = 1; // @@ -43,8 +32,8 @@ contract BluzelleTokenSaleConfig is BluzelleTokenConfig { // Price of tokens, based on the 1 ETH = 1700 BLZ conversion ratio. uint256 public constant TOKENS_PER_KETHER = 1700000; - // Amount of bonus applied to the sale. 12000 = 20% bonus, 10750 = 7.5% bonus, 10000 = no bonus. - uint256 public constant BONUS = 12000; + // Amount of bonus applied to the sale. 2000 = 20.00% bonus, 750 = 7.50% bonus, 0 = no bonus. + uint256 public constant BONUS = 2000; // Maximum amount of tokens that can be purchased for each account. uint256 public constant TOKENS_ACCOUNT_MAX = 17000 * DECIMALSFACTOR; diff --git a/contracts/ERC20Interface.sol b/contracts/Enuma/ERC20Interface.sol similarity index 96% rename from contracts/ERC20Interface.sol rename to contracts/Enuma/ERC20Interface.sol index 4054d6b..be9b182 100644 --- a/contracts/ERC20Interface.sol +++ b/contracts/Enuma/ERC20Interface.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // ERC20Interface - Standard ERC20 Interface Definition -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- diff --git a/contracts/ERC20Token.sol b/contracts/Enuma/ERC20Token.sol similarity index 97% rename from contracts/ERC20Token.sol rename to contracts/Enuma/ERC20Token.sol index cf62f6d..94fe2c5 100644 --- a/contracts/ERC20Token.sol +++ b/contracts/Enuma/ERC20Token.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // ERC20Token - Standard ERC20 Implementation -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- import "./ERC20Interface.sol"; diff --git a/contracts/Finalizable.sol b/contracts/Enuma/Finalizable.sol similarity index 92% rename from contracts/Finalizable.sol rename to contracts/Enuma/Finalizable.sol index 7d1a016..c87229b 100644 --- a/contracts/Finalizable.sol +++ b/contracts/Enuma/Finalizable.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // Finalizable - Basic implementation of the finalization pattern -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- diff --git a/contracts/FinalizableToken.sol b/contracts/Enuma/FinalizableToken.sol similarity index 97% rename from contracts/FinalizableToken.sol rename to contracts/Enuma/FinalizableToken.sol index 76fe91a..9f61132 100644 --- a/contracts/FinalizableToken.sol +++ b/contracts/Enuma/FinalizableToken.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // FinalizableToken - Extension to ERC20Token with ops and finalization -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- import "./ERC20Token.sol"; diff --git a/contracts/FlexibleTokenSale.sol b/contracts/Enuma/FlexibleTokenSale.sol similarity index 85% rename from contracts/FlexibleTokenSale.sol rename to contracts/Enuma/FlexibleTokenSale.sol index db9f164..50ebe0c 100644 --- a/contracts/FlexibleTokenSale.sol +++ b/contracts/Enuma/FlexibleTokenSale.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // FlexibleTokenSale - Token Sale Contract -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- import "./FinalizableToken.sol"; @@ -85,7 +85,7 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { // Use some defaults config values. Classes deriving from FlexibleTokenSale // should set their own defaults tokensPerKEther = 100000; - bonus = 10000; + bonus = 0; maxTokensPerAccount = 0; contributionMin = 0.1 ether; @@ -113,7 +113,7 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { // This factor is used when converting cost <-> tokens. // 18 is because of the ETH -> Wei conversion. // 3 because prices are in K ETH instead of just ETH. - // 2 because bonuses are expressed as 10000 for no bonus, 12500 for 25%, etc. + // 4 because bonuses are expressed as 0 - 10000 for 0.00% - 100.00% (with 2 decimals). tokenConversionFactor = 10**(uint256(18).sub(_token.decimals()).add(3).add(4)); require(tokenConversionFactor > 0); @@ -169,11 +169,10 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { // Allows the owner to set a bonus to apply to all purchases. - // For example, setting it to 12000 means that instead of receiving 200 tokens, - // for a given price, contributors would receive 240 tokens. + // For example, setting it to 2000 means that instead of receiving 200 tokens, + // for a given price, contributors would receive 240 tokens (20.00% bonus). function setBonus(uint256 _bonus) external onlyOwner returns(bool) { - require(_bonus >= 10000); - require(_bonus <= 20000); + require(_bonus <= 10000); bonus = _bonus; @@ -237,15 +236,21 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { } - function buyTokens(address beneficiary) public payable returns (bool) { + // Allows the caller to purchase tokens for a specific beneficiary (proxy purchase). + function buyTokens(address _beneficiary) public payable returns (uint256) { + return buyTokensInternal(_beneficiary, bonus); + } + + + function buyTokensInternal(address _beneficiary, uint256 _bonus) internal returns (uint256) { require(!finalized); require(!suspended); require(currentTime() >= startTime); require(currentTime() <= endTime); require(msg.value >= contributionMin); - require(beneficiary != address(0)); - require(beneficiary != address(this)); - require(beneficiary != address(token)); + require(_beneficiary != address(0)); + require(_beneficiary != address(this)); + require(_beneficiary != address(token)); // We don't want to allow the wallet collecting ETH to // directly be used to purchase tokens. @@ -256,7 +261,7 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { require(saleBalance > 0); // Calculate how many tokens the contributor could purchase based on ETH received. - uint256 tokens = msg.value.mul(tokensPerKEther).mul(bonus).div(tokenConversionFactor); + uint256 tokens = msg.value.mul(tokensPerKEther).mul(_bonus.add(10000)).div(tokenConversionFactor); require(tokens > 0); uint256 cost = msg.value; @@ -269,7 +274,7 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { if (maxTokensPerAccount > 0) { // There is a maximum amount of tokens per account in place. // Check if the user already hit that limit. - uint256 userBalance = token.balanceOf(beneficiary); + uint256 userBalance = getUserTokenBalance(_beneficiary); require(userBalance < maxTokensPerAccount); uint256 quotaBalance = maxTokensPerAccount.sub(userBalance); @@ -287,7 +292,7 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { tokens = maxTokens; // Calculate the actual cost for that new amount of tokens. - cost = tokens.mul(tokenConversionFactor).div(tokensPerKEther.mul(bonus)); + cost = tokens.mul(tokenConversionFactor).div(tokensPerKEther.mul(_bonus.add(10000))); if (msg.value > cost) { // If the contributor sent more ETH than needed to buy the tokens, @@ -305,16 +310,23 @@ contract FlexibleTokenSale is Finalizable, OpsManaged { totalEtherCollected = totalEtherCollected.add(contribution); // Transfer tokens to the beneficiary. - require(token.transfer(beneficiary, tokens)); + require(token.transfer(_beneficiary, tokens)); // Issue a refund for the excess ETH, as needed. if (refund > 0) { msg.sender.transfer(refund); } - TokensPurchased(beneficiary, cost, tokens); + TokensPurchased(_beneficiary, cost, tokens); - return true; + return tokens; + } + + + // Returns the number of tokens that the user has purchased. Will be checked against the + // maximum allowed. Can be overriden in a sub class to change the calculations. + function getUserTokenBalance(address _beneficiary) internal view returns (uint256) { + return token.balanceOf(_beneficiary); } diff --git a/contracts/FlexibleTokenSaleMock.sol b/contracts/Enuma/FlexibleTokenSaleMock.sol similarity index 93% rename from contracts/FlexibleTokenSaleMock.sol rename to contracts/Enuma/FlexibleTokenSaleMock.sol index ea168d0..a513b73 100644 --- a/contracts/FlexibleTokenSaleMock.sol +++ b/contracts/Enuma/FlexibleTokenSaleMock.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // FlexibleTokenSaleMock - Mock Token Sale Contract -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- import "./FlexibleTokenSale.sol"; diff --git a/contracts/Math.sol b/contracts/Enuma/Math.sol similarity index 93% rename from contracts/Math.sol rename to contracts/Enuma/Math.sol index 1f24360..2f7692d 100644 --- a/contracts/Math.sol +++ b/contracts/Enuma/Math.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // Math - General Math Utility Library -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- diff --git a/contracts/MathTest.sol b/contracts/Enuma/MathTest.sol similarity index 93% rename from contracts/MathTest.sol rename to contracts/Enuma/MathTest.sol index 18f3e9a..4c025ea 100644 --- a/contracts/MathTest.sol +++ b/contracts/Enuma/MathTest.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // MathTest - Simple wrapper contract to test the Math library -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- diff --git a/contracts/OpsManaged.sol b/contracts/Enuma/OpsManaged.sol similarity index 95% rename from contracts/OpsManaged.sol rename to contracts/Enuma/OpsManaged.sol index 5e97067..ccdaa0f 100644 --- a/contracts/OpsManaged.sol +++ b/contracts/Enuma/OpsManaged.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // OpsManaged - Implements an Owner and Ops Permission Model -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- diff --git a/contracts/Owned.sol b/contracts/Enuma/Owned.sol similarity index 96% rename from contracts/Owned.sol rename to contracts/Enuma/Owned.sol index c96cc1a..0f62b58 100644 --- a/contracts/Owned.sol +++ b/contracts/Enuma/Owned.sol @@ -2,10 +2,10 @@ pragma solidity ^0.4.17; // ---------------------------------------------------------------------------- // Owned - Ownership model with 2 phase transfers -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- diff --git a/tools/package.json b/package.json similarity index 77% rename from tools/package.json rename to package.json index 22705b2..7951199 100644 --- a/tools/package.json +++ b/package.json @@ -5,10 +5,10 @@ "main": "deployLib.js", "dependencies": { "async-file": "^2.0.2", - "bignumber.js": "^4.1.0", + "bignumber.js": "^5.0.0", "chai": "^4.1.2", - "moment": "^2.19.1", - "web3": "^1.0.0-beta.24" + "moment": "^2.19.2", + "web3": "^1.0.0-beta.26" }, "devDependencies": { "mocha": "^4.0.1" diff --git a/tests/BluzelleEndToEnd.js b/tests/BluzelleEndToEnd.js index 85ba639..07546cc 100644 --- a/tests/BluzelleEndToEnd.js +++ b/tests/BluzelleEndToEnd.js @@ -7,11 +7,11 @@ // // Based on FlexibleTokenSale end-to-end tests from Enuma Technologies. // Copyright (c) 2017 Enuma Technologies -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') -const StdUtils = require('./lib/StdTestUtils.js') +const StdUtils = require('./Enuma/lib/StdTestUtils.js') +const Utils = require('./lib/BluzelleTestUtils.js') // ---------------------------------------------------------------------------- @@ -36,7 +36,7 @@ const StdUtils = require('./lib/StdTestUtils.js') // - Add another person to the whitelist // - Contributor makes purchase on behalf of another whitelisted account // - Suspend the presale -// - Change the bonus amount to 11500 (15% bonus) +// - Change the bonus amount to 1500 (15.00% bonus) // - Resume the presale // - Contributor makes purchase // - Change the window end time to end presale early @@ -52,7 +52,7 @@ const StdUtils = require('./lib/StdTestUtils.js') // - During Public Sale // - Contributor buys max allowed tokens // - Raise per account contribution limit -// - Contributor buys max allowed tokens +// - Contributor buys max allowed tokens (again) // - Remove the account contribution limit // - Contributor buys all remaining tokens // - After Public Sale @@ -74,7 +74,7 @@ describe('Bluzelle End-To-End Scenario', () => { // Presale configuration const PRESALE_TOKENS = new BigNumber("15000000").mul(DECIMALS_FACTOR) const PRESALE_TOKENSPERKETHER = 1700000 - const PRESALE_BONUS = 12000 + const PRESALE_BONUS = 2000 const PRESALE_MAXTOKENSPERACCOUNT = new BigNumber(17000).mul(DECIMALS_FACTOR) const PRESALE_STARTTIME = Moment().add(1, 'months') const PRESALE_ENDTIME = Moment().add(2, 'months') @@ -82,7 +82,7 @@ describe('Bluzelle End-To-End Scenario', () => { // Public sale configuration const PUBLICSALE_TOKENS = new BigNumber("85000000").mul(DECIMALS_FACTOR) const PUBLICSALE_TOKENSPERKETHER = 1700000 - const PUBLICSALE_BONUS = 10000 + const PUBLICSALE_BONUS = 0 const PUBLICSALE_MAXTOKENSPERACCOUNT = new BigNumber(50000).mul(DECIMALS_FACTOR) const PUBLICSALE_STARTTIME = Moment(PRESALE_STARTTIME).add(2, 'days') const PUBLICSALE_ENDTIME = Moment(PUBLICSALE_STARTTIME).add(1, 'months') @@ -106,7 +106,7 @@ describe('Bluzelle End-To-End Scenario', () => { const buyTokens = async (from, to, amount) => { - return StdUtils.buyTokens( + return Utils.buyTokens( token, sale, owner, @@ -188,13 +188,12 @@ describe('Bluzelle End-To-End Scenario', () => { assert.equal(await sale.methods.currentStage().call(), 1) var addresses = [ account3, account4, account5 ] - var stages = [ 1, 1, 2 ] - assert.equal(await sale.methods.setWhitelistedBatch(addresses, stages).call({ from: ops }), true) - receipt = await sale.methods.setWhitelistedBatch(addresses, stages).send({ from: ops }) + assert.equal(await sale.methods.setWhitelistedBatch(addresses, 1).call({ from: ops }), true) + receipt = await sale.methods.setWhitelistedBatch(addresses, 1).send({ from: ops }) for (i = 0; i < addresses.length; i++) { - assert.equal(await sale.methods.whitelist(addresses[i]).call(), stages[i]) + assert.equal(await sale.methods.whitelist(addresses[i]).call(), 1) } }) @@ -234,7 +233,7 @@ describe('Bluzelle End-To-End Scenario', () => { it('Contributor makes purchase', async () => { - await buyTokens(account1, account1, new BigNumber(web3.utils.toWei(0.1, 'ether'))) + await buyTokens(account1, account1, new BigNumber(web3.utils.toWei('0.1', 'ether'))) }) it('Add another person to the whitelist', async () => { @@ -243,7 +242,7 @@ describe('Bluzelle End-To-End Scenario', () => { }) it('Contributor makes purchase on behalf of another whitelisted account', async () => { - await buyTokens(account6, account2, new BigNumber(web3.utils.toWei(0.1, 'ether'))) + await buyTokens(account6, account2, new BigNumber(web3.utils.toWei('0.1', 'ether'))) }) it('Suspend the sale', async () => { @@ -251,9 +250,9 @@ describe('Bluzelle End-To-End Scenario', () => { assert.equal(await sale.methods.suspended().call(), true) }) - it('Change the bonus amount to 11500 (15% bonus)', async () => { - await sale.methods.setBonus(11500).send({ from: owner }) - assert.equal(await sale.methods.bonus().call(), 11500) + it('Change the bonus amount to 1500 (15.00% bonus)', async () => { + await sale.methods.setBonus(1500).send({ from: owner }) + assert.equal(await sale.methods.bonus().call(), 1500) }) it('Resume the sale', async () => { @@ -262,13 +261,13 @@ describe('Bluzelle End-To-End Scenario', () => { }) it('Contributor makes purchase', async () => { - await buyTokens(account2, account2, new BigNumber(web3.utils.toWei(0.1, 'ether'))) + await buyTokens(account2, account2, new BigNumber(web3.utils.toWei('0.1', 'ether'))) }) it('Change the time window to end the presale early', async () => { await sale.methods.setSaleWindow(PRESALE_STARTTIME.unix(), Moment(PRESALE_ENDTIME).subtract(3, 'days').unix()).send({ from: owner }) await sale.methods.changeTime(Moment(PRESALE_ENDTIME).subtract(1, 'days').unix()).send({ from: owner }) - await TestLib.assertThrows(buyTokens(account2, account2, new BigNumber(web3.utils.toWei(0.1, 'ether')))) + await TestLib.assertCallFails(buyTokens(account2, account2, new BigNumber(web3.utils.toWei('0.1', 'ether')))) }) }) @@ -307,13 +306,12 @@ describe('Bluzelle End-To-End Scenario', () => { it('Update whitelist with new applicants', async () => { var addresses = [ account7 ] - var stages = [ 2 ] - assert.equal(await sale.methods.setWhitelistedBatch(addresses, stages).call({ from: ops }), true) - receipt = await sale.methods.setWhitelistedBatch(addresses, stages).send({ from: ops }) + assert.equal(await sale.methods.setWhitelistedBatch(addresses, 2).call({ from: ops }), true) + receipt = await sale.methods.setWhitelistedBatch(addresses, 2).send({ from: ops }) for (i = 0; i < addresses.length; i++) { - assert.equal(await sale.methods.whitelist(addresses[i]).call(), stages[i]) + assert.equal(await sale.methods.whitelist(addresses[i]).call(), 2) } }) @@ -359,7 +357,7 @@ describe('Bluzelle End-To-End Scenario', () => { await sale.methods.setMaxTokensPerAccount(PUBLICSALE_MAXTOKENSPERACCOUNT.mul(2)).send({ from: owner }) }) - it('Contributor buys max allowed tokens', async () => { + it('Contributor buys max allowed tokens (again)', async () => { await buyTokens(account7, account7, -1) }) diff --git a/tests/BluzelleTokenSale.js b/tests/BluzelleTokenSale.js index f5fdaac..4c3d109 100644 --- a/tests/BluzelleTokenSale.js +++ b/tests/BluzelleTokenSale.js @@ -7,8 +7,7 @@ // The MIT Licence. // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') -const StdUtils = require('./lib/StdTestUtils.js') +const StdUtils = require('./Enuma/lib/StdTestUtils.js') const Utils = require('./lib/BluzelleTestUtils.js') @@ -18,6 +17,8 @@ const Utils = require('./lib/BluzelleTestUtils.js') // Construction and basic properties // - whitelist // - currentStage +// - stageBonus +// - accountTokensPurchased // - startTime // - endTime // - suspended @@ -40,6 +41,17 @@ const Utils = require('./lib/BluzelleTestUtils.js') // - setCurrentStage(1) // - setCurrentStage(0) // - setCurrentStage(100) +// - setCurrentStage as ops +// - setCurrentStage as normal +// setStageBonus +// - setStageBonus(0, 5) +// - setStageBonus(1, 5) +// - setStageBonus(1, 10000) +// - setStageBonus(1, 10001) +// - setStageBonus(2, 750) +// - setStageBonus(1, 0) +// - setStageBonus as ops +// - setStageBonus as normal // setWhitelistedStatus // - setWhitelistedStatus(0, 1) // - setWhitelistedStatus(this, 1) @@ -56,24 +68,29 @@ const Utils = require('./lib/BluzelleTestUtils.js') // - setWhitelistedStatus as normal // setWhitelistedBatch // - setWhitelistedBatch with empty batch -// - setWhitelistedBatch with array length mismatch // - setWhitelistedBatch as normal // - setWhitelistedBatch as ops // - setWhitelistedBatch as owner // - setWhitelistedBatch - rerun the same batch again // - setWhitelistedBatch - remove everybody from whitelist // buyTokens -// - buyTokens where sender nor receiver whitelisted -// x buyTokens where sender not whitelisted -// x buyTokens where sender beneficiary not whitelisted +// - buyTokens where neither sender nor receiver whitelisted +// - buyTokens where sender not whitelisted +// - buyTokens where beneficiary not whitelisted // - buyTokens stage 1, whitelisted stage 1 // - buyTokens stage 1, whitelisted stage 2 // - buyTokens stage 2, whitelisted stage 2 // - buyTokens stage 2, whitelisted stage 3 // - buyTokens stage 3, whitelisted stage 1 // - buyTokens stage 3, after removed from whitelist +// - buyTokens stage 3, whitelisted stage 1, beneficiary stage bonus > base bonus +// - buyTokens stage 3, whitelisted stage 1, beneficiary stage bonus = 0 +// - buyTokens with limit of 1000 tokens +// - buyTokens with limit of 1000 tokens and someone else did a proxy purchase already +// - buyTokens with limit of 1000 tokens and owner already assigned tokens out-of-band // Events // - CurrentStageUpdated +// - StageBonusUpdated // - WhitelistedStatusUpdated // * Covered when appropriate in the different function tests. // @@ -86,7 +103,7 @@ describe('BluzelleTokenSale Contract', () => { const TOKEN_TOTALSUPPLY = new BigNumber("500000000").mul(DECIMALS_FACTOR) const TOKENSPERKETHER = 1700000 - const BONUS = 12000 + const BONUS = 2000 const MAXTOKENSPERACCOUNT = new BigNumber("17000").mul(DECIMALS_FACTOR) const CONTRIBUTION_MIN = new BigNumber(0.1).mul(DECIMALS_FACTOR) const START_TIME = 1511870400 @@ -108,8 +125,8 @@ describe('BluzelleTokenSale Contract', () => { var account5 = null - const buyTokens = async (from, to, amount) => { - return StdUtils.buyTokens( + const buyTokens = async (from, to, amount, actualBonus) => { + return Utils.buyTokens( token, sale, owner, @@ -145,8 +162,8 @@ describe('BluzelleTokenSale Contract', () => { sale = deploymentResult.instance const initialSaleTokens = new BigNumber("1000000").mul(DECIMALS_FACTOR) - await token.methods.setOpsAddress(sale._address).send({ from: owner }); - await sale.methods.setOpsAddress(ops).send({ from: owner }); + await token.methods.setOpsAddress(sale._address).send({ from: owner }) + await sale.methods.setOpsAddress(ops).send({ from: owner }) await token.methods.transfer(sale._address, initialSaleTokens).send({ from: owner }) await sale.methods.initialize(token._address).send({ from: owner }) }) @@ -221,13 +238,21 @@ describe('BluzelleTokenSale Contract', () => { it('currentStage', async () => { assert.equal(await sale.methods.currentStage().call(), 1) }) + + it('stageBonus', async () => { + assert.equal(await sale.methods.stageBonus(1).call(), 0) + }) + + it('accountTokensPurchased', async () => { + assert.equal(await sale.methods.accountTokensPurchased(account1).call(), 0) + }) }) context('setCurrentStage', async () => { it('setCurrentStage(0)', async () => { - await TestLib.assertThrows(sale.methods.setCurrentStage(0).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setCurrentStage(0).call({ from: owner })) }) it('setCurrentStage(1)', async () => { @@ -243,34 +268,88 @@ describe('BluzelleTokenSale Contract', () => { }) it('setCurrentStage(1)', async () => { - await TestLib.assertThrows(sale.methods.setCurrentStage(1).call({ from: owner })) + assert.equal(await sale.methods.currentStage().call(), 2) + assert.equal(await sale.methods.setCurrentStage(1).call({ from: owner }), true) + Utils.checkSetCurrentStage(await sale.methods.setCurrentStage(1).send({ from: owner }), 1) + assert.equal(await sale.methods.currentStage().call(), 1) }) it('setCurrentStage(0)', async () => { - await TestLib.assertThrows(sale.methods.setCurrentStage(0).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setCurrentStage(0).call({ from: owner })) }) it('setCurrentStage(100)', async () => { - assert.equal(await sale.methods.currentStage().call(), 2) assert.equal(await sale.methods.setCurrentStage(100).call({ from: owner }), true) Utils.checkSetCurrentStage(await sale.methods.setCurrentStage(100).send({ from: owner }), 100) assert.equal(await sale.methods.currentStage().call(), 100) }) + + it('setCurrentStage as ops', async () => { + await TestLib.assertCallFails(sale.methods.setCurrentStage(1).call({ from: ops })) + }) + + it('setCurrentStage as normal', async () => { + await TestLib.assertCallFails(sale.methods.setCurrentStage(1).call({ from: account1 })) + }) + }) + + + context('setStageBonus', async () => { + + it('setStageBonus(0, 5)', async () => { + await TestLib.assertCallFails(sale.methods.setStageBonus(0, 5).call({ from: owner })) + }) + + it('setStageBonus(1, 5)', async () => { + assert.equal(await sale.methods.setStageBonus(1, 5).call({ from: owner }), true) + Utils.checkSetStageBonus(await sale.methods.setStageBonus(1, 5).send({ from: owner }), 1, 5) + assert.equal(await sale.methods.stageBonus(1).call(), 5) + }) + + it('setStageBonus(1, 10000)', async () => { + assert.equal(await sale.methods.setStageBonus(1, 10000).call({ from: owner }), true) + Utils.checkSetStageBonus(await sale.methods.setStageBonus(1, 10000).send({ from: owner }), 1, 10000) + assert.equal(await sale.methods.stageBonus(1).call(), 10000) + }) + + it('setStageBonus(1, 10001)', async () => { + await TestLib.assertCallFails(sale.methods.setStageBonus(1, 10001).call({ from: owner })) + }) + + it('setStageBonus(2, 750)', async () => { + assert.equal(await sale.methods.setStageBonus(2, 750).call({ from: owner }), true) + Utils.checkSetStageBonus(await sale.methods.setStageBonus(2, 750).send({ from: owner }), 2, 750) + assert.equal(await sale.methods.stageBonus(2).call(), 750) + }) + + it('setStageBonus(1, 0)', async () => { + assert.equal(await sale.methods.setStageBonus(1, 0).call({ from: owner }), true) + Utils.checkSetStageBonus(await sale.methods.setStageBonus(1, 0).send({ from: owner }), 1, 0) + assert.equal(await sale.methods.stageBonus(1).call(), 0) + }) + + it('setStageBonus as ops', async () => { + await TestLib.assertCallFails(sale.methods.setStageBonus(1, 0).call({ from: ops })) + }) + + it('setStageBonus as normal', async () => { + await TestLib.assertCallFails(sale.methods.setStageBonus(1, 0).call({ from: account1 })) + }) }) context('setWhitelistedStatus', async () => { it('setWhitelistedStatus(0, 1)', async () => { - await TestLib.assertThrows(sale.methods.setWhitelistedStatus(0, 1).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWhitelistedStatus(0, 1).call({ from: owner })) }) it('setWhitelistedStatus(this, 1)', async () => { - await TestLib.assertThrows(sale.methods.setWhitelistedStatus(sale._address, 1).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWhitelistedStatus(sale._address, 1).call({ from: owner })) }) it('setWhitelistedStatus(wallet, 1)', async () => { - await TestLib.assertThrows(sale.methods.setWhitelistedStatus(wallet, 1).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWhitelistedStatus(wallet, 1).call({ from: owner })) }) it('setWhitelistedStatus(owner, 1)', async () => { @@ -322,7 +401,7 @@ describe('BluzelleTokenSale Contract', () => { }) it('setWhitelistedStatus as normal', async () => { - await TestLib.assertThrows(sale.methods.setWhitelistedStatus(account2, 2).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setWhitelistedStatus(account2, 2).call({ from: account1 })) }) }) @@ -331,74 +410,61 @@ describe('BluzelleTokenSale Contract', () => { it('setWhitelistedBatch with empty batch', async () => { var addresses = [] - var stages = [] - await TestLib.assertThrows(sale.methods.setWhitelistedBatch(addresses, stages).call({ from: owner })) - }) - - it('setWhitelistedBatch with array length mismatch', async () => { - var addresses = [account1] - var stages = [1, 2] - - await TestLib.assertThrows(sale.methods.setWhitelistedBatch(addresses, stages).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWhitelistedBatch(addresses, 1).call({ from: owner })) }) it('setWhitelistedBatch as normal', async () => { var addresses = [ account1, account2, account3, account4, account5 ] - var stages = [ 1, 2, 5, 0, 1000 ] - await TestLib.assertThrows(sale.methods.setWhitelistedBatch(addresses, stages).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setWhitelistedBatch(addresses, 5).call({ from: account1 })) }) it('setWhitelistedBatch as ops', async () => { var addresses = [ account1, account2, account3, account4, account5 ] - var stages = [ 1, 2, 5, 0, 1000 ] - assert.equal(await sale.methods.setWhitelistedBatch(addresses, stages).call({ from: ops }), true) - receipt = await sale.methods.setWhitelistedBatch(addresses, stages).send({ from: ops }) - Utils.checkSetWhitelistedBatch(receipt, addresses, stages) + assert.equal(await sale.methods.setWhitelistedBatch(addresses, 5).call({ from: ops }), true) + receipt = await sale.methods.setWhitelistedBatch(addresses, 5).send({ from: ops }) + Utils.checkSetWhitelistedBatch(receipt, addresses, 5) for (i = 0; i < addresses.length; i++) { - assert.equal(await sale.methods.whitelist(addresses[i]).call(), stages[i]) + assert.equal(await sale.methods.whitelist(addresses[i]).call(), 5) } }) it('setWhitelistedBatch as owner', async () => { var addresses = [ account1, account2, account3, account4, account5 ] - var stages = [ 0, 1, 7, 3, 0 ] - assert.equal(await sale.methods.setWhitelistedBatch(addresses, stages).call({ from: ops }), true) - receipt = await sale.methods.setWhitelistedBatch(addresses, stages).send({ from: ops }) - Utils.checkSetWhitelistedBatch(receipt, addresses, stages) + assert.equal(await sale.methods.setWhitelistedBatch(addresses, 10).call({ from: ops }), true) + receipt = await sale.methods.setWhitelistedBatch(addresses, 10).send({ from: ops }) + Utils.checkSetWhitelistedBatch(receipt, addresses, 10) for (i = 0; i < addresses.length; i++) { - assert.equal(await sale.methods.whitelist(addresses[i]).call(), stages[i]) + assert.equal(await sale.methods.whitelist(addresses[i]).call(), 10) } }) it('rerun same batch again', async () => { var addresses = [ account1, account2, account3, account4, account5 ] - var stages = [ 0, 1, 7, 3, 0 ] - assert.equal(await sale.methods.setWhitelistedBatch(addresses, stages).call({ from: ops }), true) - receipt = await sale.methods.setWhitelistedBatch(addresses, stages).send({ from: ops }) - Utils.checkSetWhitelistedBatch(receipt, addresses, stages) + assert.equal(await sale.methods.setWhitelistedBatch(addresses, 10).call({ from: ops }), true) + receipt = await sale.methods.setWhitelistedBatch(addresses, 10).send({ from: ops }) + Utils.checkSetWhitelistedBatch(receipt, addresses, 10) for (i = 0; i < addresses.length; i++) { - assert.equal(await sale.methods.whitelist(addresses[i]).call(), stages[i]) + assert.equal(await sale.methods.whitelist(addresses[i]).call(), 10) } }) it('remove everybody from whitelist', async () => { var addresses = [ account1, account2, account3, account4, account5 ] - var stages = [ 0, 0, 0, 0, 0 ] - assert.equal(await sale.methods.setWhitelistedBatch(addresses, stages).call({ from: ops }), true) - receipt = await sale.methods.setWhitelistedBatch(addresses, stages).send({ from: ops }) - Utils.checkSetWhitelistedBatch(receipt, addresses, stages) + assert.equal(await sale.methods.setWhitelistedBatch(addresses, 0).call({ from: ops }), true) + receipt = await sale.methods.setWhitelistedBatch(addresses, 0).send({ from: ops }) + Utils.checkSetWhitelistedBatch(receipt, addresses, 0) for (i = 0; i < addresses.length; i++) { - assert.equal(await sale.methods.whitelist(addresses[i]).call(), stages[i]) + assert.equal(await sale.methods.whitelist(addresses[i]).call(), 0) } }) }) @@ -414,8 +480,8 @@ describe('BluzelleTokenSale Contract', () => { sale = deploymentResult.instance const initialSaleTokens = new BigNumber("1000000").mul(DECIMALS_FACTOR) - await token.methods.setOpsAddress(sale._address).send({ from: owner }); - await sale.methods.setOpsAddress(ops).send({ from: owner }); + await token.methods.setOpsAddress(sale._address).send({ from: owner }) + await sale.methods.setOpsAddress(ops).send({ from: owner }) await token.methods.transfer(sale._address, initialSaleTokens).send({ from: owner }) await sale.methods.initialize(token._address).send({ from: owner }) await sale.methods.changeTime(START_TIME + 1).send({ from: owner }) @@ -423,10 +489,28 @@ describe('BluzelleTokenSale Contract', () => { }) - it('buyTokens without being whitelisted', async () => { + it('buyTokens where neither sender nor receiver whitelisted', async () => { + assert.equal(await sale.methods.currentStage().call(), 1) + assert.equal(await sale.methods.whitelist(account1).call(), 0) + + await TestLib.assertCallFails(buyTokens(account1, account1, CONTRIBUTION_MIN)) + }) + + it('buyTokens where sender not whitelisted', async () => { + assert.equal(await sale.methods.currentStage().call(), 1) + assert.equal(await sale.methods.whitelist(account1).call(), 0) + await sale.methods.setWhitelistedStatus(account2, 1).send({ from: owner }) + assert.equal(await sale.methods.whitelist(account2).call(), 1) + + await TestLib.assertCallFails(buyTokens(account1, account2, CONTRIBUTION_MIN)) + }) + + it('buyTokens where beneficiary not whitelisted', async () => { + assert.equal(await sale.methods.currentStage().call(), 1) assert.equal(await sale.methods.whitelist(account1).call(), 0) + assert.equal(await sale.methods.whitelist(account2).call(), 1) - await TestLib.assertThrows(buyTokens(account1, account1, CONTRIBUTION_MIN)) + await TestLib.assertCallFails(buyTokens(account2, account1, CONTRIBUTION_MIN)) }) it('buyTokens stage 1, whitelisted stage 1', async () => { @@ -440,7 +524,7 @@ describe('BluzelleTokenSale Contract', () => { assert.equal(await sale.methods.currentStage().call(), 1) await sale.methods.setWhitelistedStatus(account1, 2).send({ from: owner }) - await TestLib.assertThrows(buyTokens(account1, account1, CONTRIBUTION_MIN)) + await TestLib.assertCallFails(buyTokens(account1, account1, CONTRIBUTION_MIN)) }) it('buyTokens stage 2, whitelisted stage 2', async () => { @@ -454,7 +538,7 @@ describe('BluzelleTokenSale Contract', () => { it('buyTokens stage 2, whitelisted stage 3', async () => { await sale.methods.setWhitelistedStatus(account1, 3).send({ from: owner }) - await TestLib.assertThrows(buyTokens(account1, account1, CONTRIBUTION_MIN)) + await TestLib.assertCallFails(buyTokens(account1, account1, CONTRIBUTION_MIN)) }) it('buyTokens stage 3, whitelisted stage 1', async () => { @@ -464,10 +548,79 @@ describe('BluzelleTokenSale Contract', () => { await buyTokens(account1, account1, CONTRIBUTION_MIN) }) - it('buyTokens stage 2, removed from whitelist', async () => { + it('buyTokens stage 3, after removed from whitelist', async () => { await sale.methods.setWhitelistedStatus(account1, 0).send({ from: owner }) - await TestLib.assertThrows(buyTokens(account1, account1, CONTRIBUTION_MIN)) + await TestLib.assertCallFails(buyTokens(account1, account1, CONTRIBUTION_MIN)) + }) + + it('buyTokens stage 3, whitelisted stage 1, beneficiary stage bonus > base bonus', async () => { + assert.equal(await sale.methods.currentStage().call(), 3) + await sale.methods.setWhitelistedStatus(account1, 1).send({ from: owner }) + + await sale.methods.setBonus(100).send({ from: owner }) + await sale.methods.setStageBonus(1, 200).send({ from: owner }) + await sale.methods.setStageBonus(2, 300).send({ from: owner }) + await sale.methods.setStageBonus(3, 400).send({ from: owner }) + + await buyTokens(account1, account1, CONTRIBUTION_MIN) + }) + + it('buyTokens stage 3, whitelisted stage 1, beneficiary stage bonus > base bonus', async () => { + assert.equal(await sale.methods.currentStage().call(), 3) + await sale.methods.setWhitelistedStatus(account1, 1).send({ from: owner }) + + await sale.methods.setBonus(100).send({ from: owner }) + await sale.methods.setStageBonus(1, 200).send({ from: owner }) + await sale.methods.setStageBonus(2, 300).send({ from: owner }) + await sale.methods.setStageBonus(3, 400).send({ from: owner }) + + await buyTokens(account1, account1, CONTRIBUTION_MIN) + + await sale.methods.setStageBonus(1, 0).send({ from: owner }) + await sale.methods.setStageBonus(2, 0).send({ from: owner }) + await sale.methods.setStageBonus(3, 0).send({ from: owner }) + }) + + it('buyTokens with limit of 1000 tokens', async () => { + await sale.methods.setCurrentStage(1).send({ from: owner }) + await sale.methods.setWhitelistedStatus(account3, 1).send({ from: owner }) + await sale.methods.setMaxTokensPerAccount(1000).send({ from: owner }) + assert.equal(await sale.methods.accountTokensPurchased(account3).call(), 0) + + await buyTokens(account3, account3, CONTRIBUTION_MIN) + + assert.equal(await sale.methods.accountTokensPurchased(account3).call(), 1000) + }) + + it('buyTokens with limit of 1000 tokens and someone else did a proxy purchase already', async () => { + await sale.methods.setWhitelistedStatus(account4, 1).send({ from: owner }) + await sale.methods.setMaxTokensPerAccount(1000).send({ from: owner }) + assert.equal(await sale.methods.accountTokensPurchased(account3).call(), 1000) + assert.equal(await sale.methods.accountTokensPurchased(account4).call(), 0) + + await buyTokens(account3, account4, CONTRIBUTION_MIN) + await TestLib.assertCallFails(buyTokens(account4, account4, CONTRIBUTION_MIN)) + + assert.equal(await sale.methods.accountTokensPurchased(account3).call(), 1000) + assert.equal(await sale.methods.accountTokensPurchased(account4).call(), 1000) + + assert.equal(await token.methods.balanceOf(account3).call(), 1000) + assert.equal(await token.methods.balanceOf(account4).call(), 1000) + }) + + it('buyTokens with limit of 1000 tokens and owner already assigned tokens out-of-band', async () => { + await sale.methods.setWhitelistedStatus(account5, 1).send({ from: owner }) + await sale.methods.setMaxTokensPerAccount(1000).send({ from: owner }) + assert.equal(await sale.methods.accountTokensPurchased(account5).call(), 0) + + await token.methods.transfer(account5, 500).send({ from: owner }) + + await buyTokens(account5, account5, CONTRIBUTION_MIN) + + assert.equal(await sale.methods.accountTokensPurchased(account5).call(), 1000) + + assert.equal(await token.methods.balanceOf(account5).call(), 1500) }) }) }) diff --git a/tests/ERC20Token.js b/tests/Enuma/ERC20Token.js similarity index 93% rename from tests/ERC20Token.js rename to tests/Enuma/ERC20Token.js index f9014f0..8984d50 100644 --- a/tests/ERC20Token.js +++ b/tests/Enuma/ERC20Token.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // ERC20Token Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -181,7 +180,7 @@ describe('ERC20Token Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(tokenHolder).call()) assert.equal(balance.gt(0), true, "Expected tokenHolder balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(otherAccount, balance.add(1)).call({ from: tokenHolder })) + await TestLib.assertCallFails(token.methods.transfer(otherAccount, balance.add(1)).call({ from: tokenHolder })) }) it('transfer balance to other account', async () => { @@ -198,7 +197,7 @@ describe('ERC20Token Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(tokenHolder).call()) assert.equal(balance, 0) - await TestLib.assertThrows(token.methods.transfer(otherAccount, 1).call({ from: tokenHolder })) + await TestLib.assertCallFails(token.methods.transfer(otherAccount, 1).call({ from: tokenHolder })) }) it('transfer all tokens back to token holder', async () => { @@ -221,7 +220,7 @@ describe('ERC20Token Contract', () => { }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, otherAccount, 1).call({ from: tokenHolder })) + await TestLib.assertCallFails(token.methods.transferFrom(0, otherAccount, 1).call({ from: tokenHolder })) }) it('transferFrom 0 tokenHolder to address 0', async () => { @@ -253,8 +252,8 @@ describe('ERC20Token Contract', () => { await Utils.checkTransfer(await token.methods.transferFrom(tokenHolder, otherAccount, 0).send({ from: tokenHolder }), tokenHolder, otherAccount, 0) }) - it('transferFrom 1 tokenHolder to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(tokenHolder, otherAccount, 1).call({ from: tokenHolder })) + it('transferFrom 1 tokenHolder to other account, no allowance', async () => { + await TestLib.assertCallFails(token.methods.transferFrom(tokenHolder, otherAccount, 1).call({ from: tokenHolder })) }) it('transferFrom 1 tokenHolder to other account', async () => { @@ -281,7 +280,7 @@ describe('ERC20Token Contract', () => { await token.methods.approve(otherAccount, 0).send({ from: tokenHolder }) - await TestLib.assertThrows(token.methods.transferFrom(otherAccount, token._address, 1).call({ from: otherAccount })) + await TestLib.assertCallFails(token.methods.transferFrom(otherAccount, token._address, 1).call({ from: otherAccount })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -289,7 +288,7 @@ describe('ERC20Token Contract', () => { await token.methods.approve(otherAccount, 1).send({ from: tokenHolder }) - await TestLib.assertThrows(token.methods.transferFrom(otherAccount, token._address, 10).call({ from: otherAccount })) + await TestLib.assertCallFails(token.methods.transferFrom(otherAccount, token._address, 10).call({ from: otherAccount })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -306,7 +305,7 @@ describe('ERC20Token Contract', () => { it('transferFrom 10 again', async () => { assert.equal((await token.methods.balanceOf(otherAccount).call()), 45) - await TestLib.assertThrows(token.methods.transferFrom(otherAccount, token._address, 10).call({ from: otherAccount })) + await TestLib.assertCallFails(token.methods.transferFrom(otherAccount, token._address, 10).call({ from: otherAccount })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -325,7 +324,7 @@ describe('ERC20Token Contract', () => { await token.methods.approve(otherAccount, 0).send({ from: otherAccount }) - await TestLib.assertThrows(token.methods.transferFrom(otherAccount, token._address, 5).call({ from: otherAccount })) + await TestLib.assertCallFails(token.methods.transferFrom(otherAccount, token._address, 5).call({ from: otherAccount })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -341,7 +340,7 @@ describe('ERC20Token Contract', () => { assert.equal(await token.methods.transferFrom(otherAccount, token._address, 10).call({ from: otherAccount }), true) await Utils.checkTransfer(await token.methods.transferFrom(otherAccount, token._address, 10).send({ from: otherAccount }), otherAccount, token._address, 10) - await TestLib.assertThrows(token.methods.transferFrom(otherAccount, token._address, 1).call({ from: otherAccount })) + await TestLib.assertCallFails(token.methods.transferFrom(otherAccount, token._address, 1).call({ from: otherAccount })) const balanceAfter = new BigNumber(await token.methods.balanceOf(otherAccount).call()) diff --git a/tests/Finalizable.js b/tests/Enuma/Finalizable.js similarity index 87% rename from tests/Finalizable.js rename to tests/Enuma/Finalizable.js index 5833a6a..f45b106 100644 --- a/tests/Finalizable.js +++ b/tests/Enuma/Finalizable.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // Finalizable Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -62,7 +61,7 @@ describe('Finalizable Contract', () => { context('finalize function', async () => { it('finalize as normal', async () => { - await TestLib.assertThrows(instance.methods.finalize().call({ from: account1 })) + await TestLib.assertCallFails(instance.methods.finalize().call({ from: account1 })) }) it('finalize as owner', async () => { @@ -71,7 +70,7 @@ describe('Finalizable Contract', () => { }) it('finalize as owner, when already finalized', async () => { - await TestLib.assertThrows(instance.methods.finalize().call({ from: owner })) + await TestLib.assertCallFails(instance.methods.finalize().call({ from: owner })) }) }) }) diff --git a/tests/FinalizableToken.js b/tests/Enuma/FinalizableToken.js similarity index 89% rename from tests/FinalizableToken.js rename to tests/Enuma/FinalizableToken.js index f4cc396..04dcafb 100644 --- a/tests/FinalizableToken.js +++ b/tests/Enuma/FinalizableToken.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // FinalizableToken Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -226,7 +225,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(owner).call()) assert.equal(balance.gt(0), true, "Expected owner balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(account1, balance.add(1)).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance.add(1)).call({ from: owner })) }) it('transfer balance to other account', async () => { @@ -243,7 +242,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(owner).call()) assert.equal(balance, 0) - await TestLib.assertThrows(token.methods.transfer(account1, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transfer(account1, 1).call({ from: owner })) }) it('transfer all tokens back to token holder', async () => { @@ -286,7 +285,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(ops).call()) assert.equal(balance.gt(0), true, "Expected owner balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(account1, balance.add(1)).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance.add(1)).call({ from: ops })) }) it('transfer balance to other account', async () => { @@ -303,13 +302,13 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(ops).call()) assert.equal(balance, 0) - await TestLib.assertThrows(token.methods.transfer(account1, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transfer(account1, 1).call({ from: ops })) }) it('transfer all tokens back', async () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) - await TestLib.assertThrows(token.methods.transfer(ops, balance).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(ops, balance).call({ from: account1 })) }) }) @@ -324,41 +323,41 @@ describe('FinalizableToken Contract', () => { it('transfer 0 tokens', async () => { - await TestLib.assertThrows(token.methods.transfer(account2, 0).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(account2, 0).call({ from: account1 })) }) it('transfer 1 to address 0', async () => { - await TestLib.assertThrows(token.methods.transfer(0, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(0, 1).call({ from: account1 })) }) it('transfer 1 to this', async () => { - await TestLib.assertThrows(token.methods.transfer(token._address, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(token._address, 1).call({ from: account1 })) }) it('transfer > balance to other account', async () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) assert.equal(balance.gt(0), true, "Expected owner balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(account1, balance.add(1)).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance.add(1)).call({ from: account1 })) }) it('transfer balance to other account', async () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) - await TestLib.assertThrows(token.methods.transfer(account1, balance).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance).call({ from: account1 })) }) it('transfer 1 to other account, while balance = 0', async () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) assert.equal(balance, 1998) - await TestLib.assertThrows(token.methods.transfer(account1, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(account1, 1).call({ from: account1 })) }) it('transfer all tokens back to account1', async () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) - await TestLib.assertThrows(token.methods.transfer(account1, balance).call({ from: account2 })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance).call({ from: account2 })) }) }) }) @@ -379,7 +378,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account1, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account1, 1).call({ from: owner })) }) it('transferFrom 0 owner to address 0', async () => { @@ -425,7 +424,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 owner to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 1).call({ from: owner })) }) it('transferFrom 1 owner to other account', async () => { @@ -451,7 +450,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(owner, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -460,7 +459,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(owner, 1).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -480,7 +479,7 @@ describe('FinalizableToken Contract', () => { const balanceBefore = new BigNumber(await token.methods.balanceOf(account1).call()) assert.isTrue(balanceBefore.gte(10)) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -503,7 +502,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(owner, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 5).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 5).call({ from: owner })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -519,7 +518,7 @@ describe('FinalizableToken Contract', () => { assert.equal(await token.methods.transferFrom(account1, account3, 10).call({ from: owner }), true) Utils.checkTransfer(await token.methods.transferFrom(account1, account3, 10).send({ from: owner }), account1, account3, 10) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) const balance1After = new BigNumber(await token.methods.balanceOf(account1).call()) const balance3After = new BigNumber(await token.methods.balanceOf(account3).call()) @@ -542,7 +541,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account1, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account1, 1).call({ from: ops })) }) it('transferFrom 0 owner to address 0', async () => { @@ -575,7 +574,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 owner to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 1).call({ from: ops })) }) it('transferFrom 1 owner to other account', async () => { @@ -614,7 +613,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(ops, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -623,7 +622,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(ops, 1).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -643,7 +642,7 @@ describe('FinalizableToken Contract', () => { const balanceBefore = new BigNumber(await token.methods.balanceOf(account1).call()) assert.isTrue(balanceBefore.gte(10)) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -665,7 +664,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(ops, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 5).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 5).call({ from: ops })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -681,7 +680,7 @@ describe('FinalizableToken Contract', () => { assert.equal(await token.methods.transferFrom(account1, account3, 10).call({ from: ops }), true) Utils.checkTransfer(await token.methods.transferFrom(account1, account3, 10).send({ from: ops }), account1, account3, 10) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) const balance1After = new BigNumber(await token.methods.balanceOf(account1).call()) const balance3After = new BigNumber(await token.methods.balanceOf(account3).call()) @@ -700,55 +699,55 @@ describe('FinalizableToken Contract', () => { it('transferFrom 0 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account1, 0).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account1, 0).call({ from: account1 })) }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account1, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account1, 1).call({ from: account1 })) }) it('transferFrom 0 owner to address 0', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, 0, 0).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, 0, 0).call({ from: account1 })) }) it('transferFrom 1 owner to address 0', async () => { await token.methods.approve(account1, 1).send({ from: owner }) - await TestLib.assertThrows(token.methods.transferFrom(owner, 0, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, 0, 1).call({ from: account1 })) }) it('transferFrom 0 owner to this', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, token._address, 0).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, token._address, 0).call({ from: account1 })) }) it('transferFrom 1 owner to this', async () => { await token.methods.approve(account1, 1).send({ from: owner }) - await TestLib.assertThrows(token.methods.transferFrom(owner, token._address, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, token._address, 1).call({ from: account1 })) }) it('transferFrom 0 owner to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 0).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 0).call({ from: account1 })) }) it('transferFrom 1 owner to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 1).call({ from: account1 })) }) it('transferFrom 1 owner to other account', async () => { await token.methods.approve(account1, 1).send({ from: owner }) - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 1).call({ from: account1 })) }) it('transferFrom 0 ops to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(ops, account1, 0).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(ops, account1, 0).call({ from: account1 })) }) it('transferFrom 1 ops to other account', async () => { await token.methods.approve(account1, 1).send({ from: ops }) - await TestLib.assertThrows(token.methods.transferFrom(ops, account1, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(ops, account1, 1).call({ from: account1 })) }) it('transferFrom 1 other account to this', async () => { @@ -757,7 +756,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 1).send({ from: account2 }) - TestLib.assertThrows(token.methods.transferFrom(account2, token._address, 1).call({ from: account1 })) + TestLib.assertCallFails(token.methods.transferFrom(account2, token._address, 1).call({ from: account1 })) }) it('transferFrom 1 yet another account to another account', async () => { @@ -766,7 +765,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 1).send({ from: account2 }) - TestLib.assertThrows(token.methods.transferFrom(account2, account1, 1).call({ from: account1 })) + TestLib.assertCallFails(token.methods.transferFrom(account2, account1, 1).call({ from: account1 })) }) it('transferFrom 1 while allowance is 0', async () => { @@ -775,7 +774,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 0).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 1).call({ from: account1 })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -784,7 +783,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 1).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -793,14 +792,14 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 10).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) }) it('transferFrom 10 again', async () => { const balanceBefore = new BigNumber(await token.methods.balanceOf(account2).call()) assert.isTrue(balanceBefore.gte(10)) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -809,7 +808,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 10).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 5).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 5).call({ from: account1 })) }) it('transferFrom 1 after allowance changed from 10 -> 0', async () => { @@ -820,7 +819,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 0).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 5).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 5).call({ from: account1 })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -830,7 +829,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 20).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) }) }) }) @@ -869,7 +868,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(owner).call()) assert.equal(balance.gt(0), true, "Expected owner balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(account1, balance.add(1)).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance.add(1)).call({ from: owner })) }) it('transfer balance to other account', async () => { @@ -890,7 +889,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(owner).call()) assert.equal(balance, 0) - await TestLib.assertThrows(token.methods.transfer(account1, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transfer(account1, 1).call({ from: owner })) }) it('transfer all tokens back', async () => { @@ -936,7 +935,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(ops).call()) assert.equal(balance.gt(0), true, "Expected owner balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(account1, balance.add(1)).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transfer(account1, balance.add(1)).call({ from: ops })) }) it('transfer balance to other account', async () => { @@ -953,7 +952,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(ops).call()) assert.equal(balance, 0) - await TestLib.assertThrows(token.methods.transfer(account1, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transfer(account1, 1).call({ from: ops })) }) it('transfer all tokens back to ops', async () => { @@ -991,7 +990,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) assert.equal(balance.gt(0), true, "Expected owner balance to be > 0.") - await TestLib.assertThrows(token.methods.transfer(account2, balance.add(1)).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(account2, balance.add(1)).call({ from: account1 })) }) it('transfer balance to other account', async () => { @@ -1008,7 +1007,7 @@ describe('FinalizableToken Contract', () => { const balance = new BigNumber(await token.methods.balanceOf(account1).call()) assert.equal(balance, 0) - await TestLib.assertThrows(token.methods.transfer(account2, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transfer(account2, 1).call({ from: account1 })) }) it('transfer all tokens back', async () => { @@ -1036,7 +1035,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account1, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account1, 1).call({ from: owner })) }) it('transferFrom 0 owner to address 0', async () => { @@ -1082,7 +1081,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 owner to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 1).call({ from: owner })) }) it('transferFrom 1 owner to other account', async () => { @@ -1108,7 +1107,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(owner, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -1117,7 +1116,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(owner, 1).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -1137,7 +1136,7 @@ describe('FinalizableToken Contract', () => { const balanceBefore = new BigNumber(await token.methods.balanceOf(account1).call()) assert.isTrue(balanceBefore.gte(10)) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: owner })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -1160,7 +1159,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(owner, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 5).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 5).call({ from: owner })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -1176,7 +1175,7 @@ describe('FinalizableToken Contract', () => { assert.equal(await token.methods.transferFrom(account1, account3, 10).call({ from: owner }), true) Utils.checkTransfer(await token.methods.transferFrom(account1, account3, 10).send({ from: owner }), account1, account3, 10) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: owner })) const balance1After = new BigNumber(await token.methods.balanceOf(account1).call()) const balance3After = new BigNumber(await token.methods.balanceOf(account3).call()) @@ -1199,7 +1198,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account1, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account1, 1).call({ from: ops })) }) it('transferFrom 0 owner to address 0', async () => { @@ -1232,7 +1231,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 owner to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account1, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account1, 1).call({ from: ops })) }) it('transferFrom 1 owner to other account', async () => { @@ -1271,7 +1270,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(ops, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -1280,7 +1279,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(ops, 1).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -1300,7 +1299,7 @@ describe('FinalizableToken Contract', () => { const balanceBefore = new BigNumber(await token.methods.balanceOf(account1).call()) assert.isTrue(balanceBefore.gte(10)) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 10).call({ from: ops })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -1322,7 +1321,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(ops, 0).send({ from: account1 }) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 5).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 5).call({ from: ops })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -1338,7 +1337,7 @@ describe('FinalizableToken Contract', () => { assert.equal(await token.methods.transferFrom(account1, account3, 10).call({ from: ops }), true) Utils.checkTransfer(await token.methods.transferFrom(account1, account3, 10).send({ from: ops }), account1, account3, 10) - await TestLib.assertThrows(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) + await TestLib.assertCallFails(token.methods.transferFrom(account1, account3, 1).call({ from: ops })) const balance1After = new BigNumber(await token.methods.balanceOf(account1).call()) const balance3After = new BigNumber(await token.methods.balanceOf(account3).call()) @@ -1362,7 +1361,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 address 0 to other account', async () => { - await TestLib.assertThrows(token.methods.transferFrom(0, account2, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(0, account2, 1).call({ from: account1 })) }) it('transferFrom 0 owner to address 0', async () => { @@ -1395,7 +1394,7 @@ describe('FinalizableToken Contract', () => { }) it('transferFrom 1 owner to other account, no allowance ', async () => { - await TestLib.assertThrows(token.methods.transferFrom(owner, account2, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(owner, account2, 1).call({ from: account1 })) }) it('transferFrom 1 owner to other account', async () => { @@ -1433,7 +1432,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 0).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 1).call({ from: account1 })) }) it('transferFrom 10 while allowance is 1', async () => { @@ -1442,7 +1441,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 1).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) }) it('transferFrom 10 while allowance is 10', async () => { @@ -1462,7 +1461,7 @@ describe('FinalizableToken Contract', () => { const balanceBefore = new BigNumber(await token.methods.balanceOf(account2).call()) assert.isTrue(balanceBefore.gte(10)) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 10).call({ from: account1 })) }) it('transferFrom 5 while allowance is 10', async () => { @@ -1484,7 +1483,7 @@ describe('FinalizableToken Contract', () => { await token.methods.approve(account1, 0).send({ from: account2 }) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 5).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 5).call({ from: account1 })) }) it('transferFrom 10 + 10 + 1 while allowance is 20', async () => { @@ -1501,7 +1500,7 @@ describe('FinalizableToken Contract', () => { assert.equal(await token.methods.transferFrom(account2, account3, 10).call({ from: account1 }), true) Utils.checkTransfer(await token.methods.transferFrom(account2, account3, 10).send({ from: account1 }), account2, account3, 10) - await TestLib.assertThrows(token.methods.transferFrom(account2, account3, 1).call({ from: account1 })) + await TestLib.assertCallFails(token.methods.transferFrom(account2, account3, 1).call({ from: account1 })) const balance1After = new BigNumber(await token.methods.balanceOf(account1).call()) const balance2After = new BigNumber(await token.methods.balanceOf(account2).call()) @@ -1590,13 +1589,13 @@ describe('FinalizableToken Contract', () => { it('other account cannot call finalize', async () => { assert.equal(await token.methods.finalized().call(), false) - await TestLib.assertThrows(token.methods.finalize().call({ from: account1 })) + await TestLib.assertCallFails(token.methods.finalize().call({ from: account1 })) }) it('ops cannot call finalize', async () => { assert.equal(await token.methods.finalized().call(), false) assert.equal(await token.methods.opsAddress().call(), ops) - await TestLib.assertThrows(token.methods.finalize().call({ from: ops })) + await TestLib.assertCallFails(token.methods.finalize().call({ from: ops })) }) it('owner can call finalize', async () => { diff --git a/tests/FlexibleTokenSale.js b/tests/Enuma/FlexibleTokenSale.js similarity index 86% rename from tests/FlexibleTokenSale.js rename to tests/Enuma/FlexibleTokenSale.js index 7ffc01c..b1aba5b 100644 --- a/tests/FlexibleTokenSale.js +++ b/tests/Enuma/FlexibleTokenSale.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // FlexibleTokenSale Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -77,10 +76,10 @@ const Utils = require('./lib/StdTestUtils.js') // - setTokensPerKEther as normal // setBonus // - setBonus(0) -// - setBonus(9999) +// - setBonus(750) +// - setBonus(1500) // - setBonus(10000) -// - setBonus(20000) -// - setBonus(20100) +// - setBonus(10001) // - setBonus as ops // - setBonus as normal // setSaleWindow @@ -160,7 +159,7 @@ describe('FlexibleTokenSale Contract', () => { const TOKEN_TOTALSUPPLY = new BigNumber("1000000").mul(DECIMALS_FACTOR) const DEFAULT_TOKENSPERKETHER = 100000 - const DEFAULT_BONUS = 10000 + const DEFAULT_BONUS = 0 const START_TIME = Moment().add(1, 'M').unix() const END_TIME = Moment().add(2, 'M').unix() @@ -227,7 +226,7 @@ describe('FlexibleTokenSale Contract', () => { }) it('contributionMin', async () => { - assert.equal(await sale.methods.contributionMin().call(), web3.utils.toWei(0.1, 'ether')) + assert.equal(await sale.methods.contributionMin().call(), web3.utils.toWei('0.1', 'ether')) }) it('walletAddress', async () => { @@ -344,31 +343,31 @@ describe('FlexibleTokenSale Contract', () => { it('initialize(0)', async () => { - await TestLib.assertThrows(sale.methods.initialize(0).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.initialize(0).call({ from: owner })) }) it('initialize(this)', async () => { - await TestLib.assertThrows(sale.methods.initialize(sale._address).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.initialize(sale._address).call({ from: owner })) }) it('initialize(owner)', async () => { - await TestLib.assertThrows(sale.methods.initialize(owner).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.initialize(owner).call({ from: owner })) }) it('initialize(ops)', async () => { - await TestLib.assertThrows(sale.methods.initialize(ops).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.initialize(ops).call({ from: owner })) }) it('initialize(wallet)', async () => { - await TestLib.assertThrows(sale.methods.initialize(wallet).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.initialize(wallet).call({ from: owner })) }) it('initialize as normal', async () => { - await TestLib.assertThrows(sale.methods.initialize(token._address).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.initialize(token._address).call({ from: account1 })) }) it('initialize as ops', async () => { - await TestLib.assertThrows(sale.methods.initialize(token._address).call({ from: ops })) + await TestLib.assertCallFails(sale.methods.initialize(token._address).call({ from: ops })) }) it('initialize as owner', async () => { @@ -386,19 +385,19 @@ describe('FlexibleTokenSale Contract', () => { it('setWalletAddress(0)', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(0).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(0).call({ from: owner })) }) it('setWalletAddress(this)', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(sale._address).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(sale._address).call({ from: owner })) }) it('setWalletAddress(owner)', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(owner).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(owner).call({ from: owner })) }) it('setWalletAddress(ops)', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(ops).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(ops).call({ from: owner })) }) it('setWalletAddress(wallet)', async () => { @@ -409,15 +408,15 @@ describe('FlexibleTokenSale Contract', () => { }) it('setWalletAddress(token)', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(token._address).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(token._address).call({ from: owner })) }) it('setWalletAddress as normal', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(account1).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(account1).call({ from: account1 })) }) it('setWalletAddress as ops', async () => { - await TestLib.assertThrows(sale.methods.setWalletAddress(account1).call({ from: ops })) + await TestLib.assertCallFails(sale.methods.setWalletAddress(account1).call({ from: ops })) }) it('setWalletAddress as owner', async () => { @@ -451,11 +450,11 @@ describe('FlexibleTokenSale Contract', () => { it('setMaxTokensPerAccount as ops', async () => { assert.equal(await sale.methods.opsAddress().call(), ops) - await TestLib.assertThrows(sale.methods.setMaxTokensPerAccount(1).call({ from: ops })) + await TestLib.assertCallFails(sale.methods.setMaxTokensPerAccount(1).call({ from: ops })) }) it('setMaxTokensPerAccount as normal', async () => { - await TestLib.assertThrows(sale.methods.setMaxTokensPerAccount(1).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setMaxTokensPerAccount(1).call({ from: account1 })) }) }) @@ -463,7 +462,7 @@ describe('FlexibleTokenSale Contract', () => { context('setTokensPerKEther', async () => { it('setTokensPerKEther(0)', async () => { - await TestLib.assertThrows(sale.methods.setTokensPerKEther(0).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setTokensPerKEther(0).call({ from: owner })) }) it('setTokensPerKEther(1)', async () => { @@ -479,11 +478,11 @@ describe('FlexibleTokenSale Contract', () => { it('setTokensPerKEther as ops', async () => { assert.equal(await sale.methods.opsAddress().call(), ops) - await TestLib.assertThrows(sale.methods.setTokensPerKEther(1).call({ from: ops })) + await TestLib.assertCallFails(sale.methods.setTokensPerKEther(1).call({ from: ops })) }) it('setTokensPerKEther as normal', async () => { - await TestLib.assertThrows(sale.methods.setTokensPerKEther(1).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setTokensPerKEther(1).call({ from: account1 })) }) }) @@ -491,11 +490,18 @@ describe('FlexibleTokenSale Contract', () => { context('setBonus', async () => { it('setBonus(0)', async () => { - await TestLib.assertThrows(sale.methods.setBonus(0).call({ from: owner })) + assert.equal(await sale.methods.setBonus(0).call({ from: owner }), true) + Utils.checkSetBonus(await sale.methods.setBonus(0).send({ from: owner }), 0) }) - it('setBonus(9999)', async () => { - await TestLib.assertThrows(sale.methods.setBonus(9999).call({ from: owner })) + it('setBonus(750)', async () => { + assert.equal(await sale.methods.setBonus(750).call({ from: owner }), true) + Utils.checkSetBonus(await sale.methods.setBonus(750).send({ from: owner }), 750) + }) + + it('setBonus(1500)', async () => { + assert.equal(await sale.methods.setBonus(1500).call({ from: owner }), true) + Utils.checkSetBonus(await sale.methods.setBonus(1500).send({ from: owner }), 1500) }) it('setBonus(10000)', async () => { @@ -503,22 +509,17 @@ describe('FlexibleTokenSale Contract', () => { Utils.checkSetBonus(await sale.methods.setBonus(10000).send({ from: owner }), 10000) }) - it('setBonus(20000)', async () => { - assert.equal(await sale.methods.setBonus(20000).call({ from: owner }), true) - Utils.checkSetBonus(await sale.methods.setBonus(20000).send({ from: owner }), 20000) - }) - - it('setBonus(20100)', async () => { - await TestLib.assertThrows(sale.methods.setBonus(20100).call({ from: owner })) + it('setBonus(10001)', async () => { + await TestLib.assertCallFails(sale.methods.setBonus(10001).call({ from: owner })) }) it('setBonus as ops', async () => { assert.equal(await sale.methods.opsAddress().call(), ops) - await TestLib.assertThrows(sale.methods.setBonus(12000).call({ from: ops })) + await TestLib.assertCallFails(sale.methods.setBonus(2000).call({ from: ops })) }) it('setBonus as normal', async () => { - await TestLib.assertThrows(sale.methods.setBonus(12000).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setBonus(2000).call({ from: account1 })) }) }) @@ -526,11 +527,11 @@ describe('FlexibleTokenSale Contract', () => { context('setSaleWindow', async () => { it('setSaleWindow(0, 0)', async () => { - await TestLib.assertThrows(sale.methods.setSaleWindow(0, 0).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setSaleWindow(0, 0).call({ from: owner })) }) it('setSaleWindow(0, 1)', async () => { - await TestLib.assertThrows(sale.methods.setSaleWindow(0, 1).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setSaleWindow(0, 1).call({ from: owner })) }) it('setSaleWindow(1, 2)', async () => { @@ -546,7 +547,7 @@ describe('FlexibleTokenSale Contract', () => { it('setSaleWindow(now, now)', async () => { const now = Moment().unix() - await TestLib.assertThrows(sale.methods.setSaleWindow(now, now).call({ from: owner })) + await TestLib.assertCallFails(sale.methods.setSaleWindow(now, now).call({ from: owner })) }) it('setSaleWindow(now, now + 1)', async () => { @@ -565,12 +566,12 @@ describe('FlexibleTokenSale Contract', () => { it('setSaleWindow as ops', async () => { const now = Moment().unix() - await TestLib.assertThrows(sale.methods.setSaleWindow(now, now + 1).call({ from: ops })) + await TestLib.assertCallFails(sale.methods.setSaleWindow(now, now + 1).call({ from: ops })) }) it('setSaleWindow as normal', async () => { const now = Moment().unix() - await TestLib.assertThrows(sale.methods.setSaleWindow(now, now + 1).call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.setSaleWindow(now, now + 1).call({ from: account1 })) }) }) @@ -677,16 +678,16 @@ describe('FlexibleTokenSale Contract', () => { await sale.methods.changeTime(startTime + 1).send({ from: owner }) assert.equal(await sale.methods.suspended().call(), false) - await TestLib.assertThrows(sale.methods.suspend().call({ from: ops })) - await TestLib.assertThrows(sale.methods.resume().call({ from: ops })) + await TestLib.assertCallFails(sale.methods.suspend().call({ from: ops })) + await TestLib.assertCallFails(sale.methods.resume().call({ from: ops })) }) it('suspend / resume as normal', async () => { await sale.methods.changeTime(startTime + 1).send({ from: owner }) assert.equal(await sale.methods.suspended().call(), false) - await TestLib.assertThrows(sale.methods.suspend().call({ from: account1 })) - await TestLib.assertThrows(sale.methods.resume().call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.suspend().call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.resume().call({ from: account1 })) }) }) @@ -706,14 +707,14 @@ describe('FlexibleTokenSale Contract', () => { it('finalize as normal', async () => { assert.equal(await sale.methods.finalized().call(), false) - await TestLib.assertThrows(sale.methods.finalize().call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.finalize().call({ from: account1 })) }) it('finalize as ops', async () => { assert.equal(await sale.methods.finalized().call(), false) assert.equal(await sale.methods.opsAddress().call(), ops) - await TestLib.assertThrows(sale.methods.finalize().call({ from: ops })) + await TestLib.assertCallFails(sale.methods.finalize().call({ from: ops })) }) it('finalize as owner', async () => { @@ -780,11 +781,11 @@ describe('FlexibleTokenSale Contract', () => { it('reclaimTokens as normal', async () => { - await TestLib.assertThrows(sale.methods.reclaimTokens().call({ from: account1 })) + await TestLib.assertCallFails(sale.methods.reclaimTokens().call({ from: account1 })) }) it('reclaimTokens as ops', async () => { - await TestLib.assertThrows(sale.methods.reclaimTokens().call({ from: ops })) + await TestLib.assertCallFails(sale.methods.reclaimTokens().call({ from: ops })) }) it('reclaimTokens as owner', async () => { diff --git a/tests/Enuma/FlexibleTokenSale_EndToEnd.js b/tests/Enuma/FlexibleTokenSale_EndToEnd.js new file mode 100644 index 0000000..04c5b7b --- /dev/null +++ b/tests/Enuma/FlexibleTokenSale_EndToEnd.js @@ -0,0 +1,355 @@ +// ---------------------------------------------------------------------------- +// FlexibleTokenSale End-To-End Scenario Test +// Enuma Blockchain Platform +// +// Copyright (c) 2017 Enuma Technologies. +// https://www.enuma.io/ +// ---------------------------------------------------------------------------- + +const StdUtils = require('./lib/StdTestUtils.js') + + +// ---------------------------------------------------------------------------- +// Tests Summary +// ---------------------------------------------------------------------------- +// - Initial Deployment +// - Deploy the token contract +// - Deploy the sale contract +// - Initialize the sale contract +// - Set the ops key of token to the sale contract +// - Set the ops key of sale to a ops key +// - Before Presale +// - Set the sale window +// - Set the bonus amount +// - Set the per account contribution limit +// - Set the token price +// - Give tokens to the sale contract +// - During Presale +// - Contributor makes purchase +// - Contributor makes purchase on behalf of another account +// - Suspend the presale +// - Change the bonus amount to 1500 (15.00% bonus) +// - Resume the presale +// - Contributor makes purchase +// - Change the window end time to end presale early +// - After Presale +// - Reclaim unsold tokens +// - Before Public Sale +// - Set new time window for the public sale +// - Set a new bonus amount +// - Set the per account contribution limit +// - Assign new amount of tokens for sale +// - During Public Sale +// - Contributor buys max allowed tokens +// - Raise per account contribution limit +// - Contributor buys max allowed tokens +// - Remove the account contribution limit +// - Contributor buys all remaining tokens +// - After Public Sale +// - Reclaim tokens (should be 0) +// - Finalize the token +// - Finalize the sale +// +describe('FlexibleTokenSale End-To-End Scenario', () => { + + const TOKEN_NAME = "FinalizableToken" + const TOKEN_SYMBOL = "FNT" + const TOKEN_DECIMALS = 18 + const DECIMALS_FACTOR = new BigNumber(10).pow(TOKEN_DECIMALS) + const TOKEN_TOTALSUPPLY = new BigNumber("500000000").mul(DECIMALS_FACTOR) + + const CONTRIBUTION_MIN = new BigNumber(0.1).mul(DECIMALS_FACTOR) + + + // Presale configuration + const PRESALE_TOKENS = new BigNumber("15000000").mul(DECIMALS_FACTOR) + const PRESALE_TOKENSPERKETHER = 1700000 + const PRESALE_BONUS = 2000 + const PRESALE_MAXTOKENSPERACCOUNT = new BigNumber(17000).mul(DECIMALS_FACTOR) + const PRESALE_STARTTIME = Moment().add(1, 'months') + const PRESALE_ENDTIME = Moment().add(2, 'months') + + // Public sale configuration + const PUBLICSALE_TOKENS = new BigNumber("85000000").mul(DECIMALS_FACTOR) + const PUBLICSALE_TOKENSPERKETHER = 1700000 + const PUBLICSALE_BONUS = 0 + const PUBLICSALE_MAXTOKENSPERACCOUNT = new BigNumber(50000).mul(DECIMALS_FACTOR) + const PUBLICSALE_STARTTIME = Moment(PRESALE_STARTTIME).add(2, 'days') + const PUBLICSALE_ENDTIME = Moment(PUBLICSALE_STARTTIME).add(1, 'months') + + + var sale = null + var token = null + var accounts = null + + // Accounts used for testing + var owner = null + var ops = null + var wallet = null + var account1 = null + var account2 = null + var account3 = null + var account4 = null + var account5 = null + var account6 = null + var account7 = null + + + const buyTokens = async (from, to, amount) => { + return StdUtils.buyTokens( + token, + sale, + owner, + wallet, + DECIMALS_FACTOR, + from, + to, + amount + ) + } + + + before(async () => { + await TestLib.initialize() + + accounts = await web3.eth.getAccounts() + + owner = accounts[1] + ops = accounts[2] + wallet = accounts[3] + account1 = accounts[4] + account2 = accounts[5] + account3 = accounts[6] + account4 = accounts[7] + account5 = accounts[8] + account6 = accounts[9] + account7 = accounts[10] + + var deploymentResult = null + }) + + + context('Initial deployment', async () => { + + it('Deploy the token contract', async () => { + deploymentResult = await TestLib.deploy('FinalizableToken', [ TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS, TOKEN_TOTALSUPPLY ], { from: owner }) + token = deploymentResult.instance + + assert.equal(new BigNumber(await token.methods.balanceOf(owner).call()), TOKEN_TOTALSUPPLY) + }) + + it('Deploy the sale contract', async () => { + deploymentResult = await TestLib.deploy('FlexibleTokenSaleMock', [ PRESALE_STARTTIME.unix(), PRESALE_ENDTIME.unix(), wallet, Moment().unix() ], { from: owner }) + sale = deploymentResult.instance + + assert.equal(await sale.methods.owner().call(), owner) + }) + + it('Initialize the sale contract', async () => { + await sale.methods.initialize(token._address).send({ from: owner }) + assert.equal(await sale.methods.token().call(), token._address) + assert.equal(new BigNumber(await sale.methods.tokenConversionFactor().call()), new BigNumber(10).pow(18 - TOKEN_DECIMALS + 3 + 4)) + }) + + it('Set the ops key of the token to the sale contract', async () => { + await token.methods.setOpsAddress(sale._address).send({ from: owner }) + assert.equal(await token.methods.opsAddress().call(), sale._address) + }) + + it('Set the ops key of the sale to a ops key', async () => { + await sale.methods.setOpsAddress(ops).send({ from: owner }) + assert.equal(await sale.methods.opsAddress().call(), ops) + }) + }) + + + context('Before presale', async () => { + + it('Set the sale window', async () => { + await sale.methods.setSaleWindow(PRESALE_STARTTIME.unix(), PRESALE_ENDTIME.unix()).send({ from: owner }) + assert.equal(await sale.methods.startTime().call(), PRESALE_STARTTIME.unix()) + assert.equal(await sale.methods.endTime().call(), PRESALE_ENDTIME.unix()) + }) + + it('Set bonus amount', async () => { + await sale.methods.setBonus(PRESALE_BONUS).send({ from: owner }) + assert.equal(await sale.methods.bonus().call(), PRESALE_BONUS) + }) + + it('Set per account contribution limit', async () => { + await sale.methods.setMaxTokensPerAccount(PRESALE_MAXTOKENSPERACCOUNT).send({ from: owner }) + assert.equal(new BigNumber(await sale.methods.maxTokensPerAccount().call()), PRESALE_MAXTOKENSPERACCOUNT) + }) + + it('Set the token price', async () => { + await sale.methods.setTokensPerKEther(PRESALE_TOKENSPERKETHER).send({ from: owner }) + assert.equal(await sale.methods.tokensPerKEther().call(), PRESALE_TOKENSPERKETHER) + }) + + it('Give tokens to the sale contract', async () => { + await token.methods.transfer(sale._address, PRESALE_TOKENS).send({ from: owner }) + assert.equal(new BigNumber(await token.methods.balanceOf(sale._address).call()), PRESALE_TOKENS) + }) + }) + + + context('During Presale', async () => { + + before(async () => { + await sale.methods.changeTime(PRESALE_STARTTIME.unix() + 1).send({ from: owner }) + }) + + + it('Contributor makes purchase', async () => { + await buyTokens(account1, account1, new BigNumber(web3.utils.toWei('0.1', 'ether'))) + }) + + it('Contributor makes purchase on behalf of another account', async () => { + await buyTokens(account6, account2, new BigNumber(web3.utils.toWei('0.1', 'ether'))) + }) + + it('Suspend the sale', async () => { + await sale.methods.suspend().send({ from: owner }) + assert.equal(await sale.methods.suspended().call(), true) + }) + + it('Change the bonus amount to 1500 (15.00% bonus)', async () => { + await sale.methods.setBonus(1500).send({ from: owner }) + assert.equal(await sale.methods.bonus().call(), 1500) + }) + + it('Resume the sale', async () => { + await sale.methods.resume().send({ from: owner }) + assert.equal(await sale.methods.suspended().call(), false) + }) + + it('Contributor makes purchase', async () => { + await buyTokens(account2, account2, new BigNumber(web3.utils.toWei('0.1', 'ether'))) + }) + + it('Change the time window to end the presale early', async () => { + await sale.methods.setSaleWindow(PRESALE_STARTTIME.unix(), Moment(PRESALE_ENDTIME).subtract(3, 'days').unix()).send({ from: owner }) + await sale.methods.changeTime(Moment(PRESALE_ENDTIME).subtract(1, 'days').unix()).send({ from: owner }) + await TestLib.assertCallFails(buyTokens(account2, account2, new BigNumber(web3.utils.toWei('0.1', 'ether')))) + }) + }) + + + context('After Presale', async() => { + + it('Reclaim unsold tokens', async () => { + const ownerTokensBefore = new BigNumber(await token.methods.balanceOf(owner).call()) + const saleTokensBefore = new BigNumber(await token.methods.balanceOf(sale._address).call()) + + await sale.methods.reclaimTokens().send({ from: owner }) + + const ownerTokensAfter = new BigNumber(await token.methods.balanceOf(owner).call()) + const saleTokensAfter = new BigNumber(await token.methods.balanceOf(sale._address).call()) + + assert.isTrue(saleTokensBefore.gt(0)) + assert.equal(saleTokensAfter, new BigNumber(0)) + + assert.equal(ownerTokensAfter.sub(ownerTokensBefore), saleTokensBefore) + }) + }) + + + context('Before Public Sale', async () => { + + it('Set new time window for the public sale', async () => { + await sale.methods.setSaleWindow(PUBLICSALE_STARTTIME.unix(), PUBLICSALE_ENDTIME.unix()).send({ from: owner }) + assert.equal(await sale.methods.startTime().call(), PUBLICSALE_STARTTIME.unix()) + assert.equal(await sale.methods.endTime().call(), PUBLICSALE_ENDTIME.unix()) + }) + + it('Set a new bonus amount', async () => { + await sale.methods.setBonus(PUBLICSALE_BONUS).send({ from: owner }) + assert.equal(await sale.methods.bonus().call(), PUBLICSALE_BONUS) + }) + + it('Set per account contribution limit', async () => { + await sale.methods.setMaxTokensPerAccount(PUBLICSALE_MAXTOKENSPERACCOUNT).send({ from: owner }) + assert.equal(new BigNumber(await sale.methods.maxTokensPerAccount().call()), PUBLICSALE_MAXTOKENSPERACCOUNT) + }) + + it('Set the token price', async () => { + await sale.methods.setTokensPerKEther(PUBLICSALE_TOKENSPERKETHER).send({ from: owner }) + assert.equal(await sale.methods.tokensPerKEther().call(), PUBLICSALE_TOKENSPERKETHER) + }) + + it('Give tokens to the sale contract', async () => { + await token.methods.transfer(sale._address, PUBLICSALE_TOKENS).send({ from: owner }) + assert.equal(new BigNumber(await token.methods.balanceOf(sale._address).call()), PUBLICSALE_TOKENS) + }) + }) + + + context('During Public Sale', async () => { + + var tokenSoldPresale = null + + + before(async () => { + await sale.methods.changeTime(PUBLICSALE_STARTTIME.unix() + 1).send({ from: owner }) + + tokensSoldPresale = new BigNumber(await sale.methods.totalTokensSold().call()) + }) + + + it('Contributor buys max allowed tokens', async () => { + await buyTokens(account7, account7, -1) + }) + + it('Raise per account contribution limit', async () => { + await sale.methods.setMaxTokensPerAccount(PUBLICSALE_MAXTOKENSPERACCOUNT.mul(2)).send({ from: owner }) + }) + + it('Contributor buys max allowed tokens', async () => { + await buyTokens(account7, account7, -1) + }) + + it('Remove per account contribution limit', async () => { + await sale.methods.setMaxTokensPerAccount(0).send({ from: owner }) + }) + + it('Contributor buys all remaining tokens', async () => { + await buyTokens(account7, account7, -1) + + assert.equal(new BigNumber(await sale.methods.totalTokensSold().call()), tokensSoldPresale.add(PUBLICSALE_TOKENS)) + }) + }) + + + context('After Public Sale', async () => { + + it('Reclaim unsold tokens', async () => { + const ownerTokensBefore = new BigNumber(await token.methods.balanceOf(owner).call()) + const saleTokensBefore = new BigNumber(await token.methods.balanceOf(sale._address).call()) + + await sale.methods.reclaimTokens().send({ from: owner }) + + const ownerTokensAfter = new BigNumber(await token.methods.balanceOf(owner).call()) + const saleTokensAfter = new BigNumber(await token.methods.balanceOf(sale._address).call()) + + assert.isTrue(saleTokensBefore.eq(0)) + assert.equal(saleTokensAfter, new BigNumber(0)) + + assert.equal(ownerTokensAfter.sub(ownerTokensBefore), 0) + }) + + it('Finalize the token', async () => { + assert.equal(await token.methods.finalized().call(), false) + await token.methods.finalize().send({ from: owner }) + assert.equal(await token.methods.finalized().call(), true) + }) + + it('Finalize the sale', async () => { + // IMPORTANT: Finalizing the sale contract means that it can never be used for later sales. Another + // sale contract would need to be deployed. If the contract should be used to more sales + // in the future, consider calling 'suspend' instead. + assert.equal(await sale.methods.finalized().call(), false) + await sale.methods.finalize().send({ from: owner }) + assert.equal(await sale.methods.finalized().call(), true) + }) + }) +}) diff --git a/tests/FlexibleTokenSale_buyTokens.js b/tests/Enuma/FlexibleTokenSale_buyTokens.js similarity index 87% rename from tests/FlexibleTokenSale_buyTokens.js rename to tests/Enuma/FlexibleTokenSale_buyTokens.js index 01ba3b9..5f2d781 100644 --- a/tests/FlexibleTokenSale_buyTokens.js +++ b/tests/Enuma/FlexibleTokenSale_buyTokens.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // FlexibleTokenSale Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -31,9 +30,9 @@ const Utils = require('./lib/StdTestUtils.js') // - buyTokens with minimum contribution - 1 wei // - buyTokens with minimum contribution // - buyTokens with more ETH than maxTokensPerAccount allows +// - buyTokens with bonus = 0 +// - buyTokens with bonus = 755 // - buyTokens with bonus = 10000 -// - buyTokens with bonus = 10755 -// - buyTokens with bonus = 20000 // - buyTokens with more ETH than left for sale // - buyTokens with less tokens left than maxTokensPerAccount // - buyTokens with enough ETH to buy all tokens in a single transaction, and more... @@ -141,7 +140,7 @@ describe('FlexibleTokenSale Contract - buyTokens tests', () => { //}) it('buyTokens for this', async () => { - await TestLib.assertThrows(buyTokens(account1, sale._address, contributionMin)) + await TestLib.assertCallFails(buyTokens(account1, sale._address, contributionMin)) }) it('buyTokens for wallet', async () => { @@ -157,19 +156,19 @@ describe('FlexibleTokenSale Contract - buyTokens tests', () => { }) it('buyTokens for token contract', async () => { - await TestLib.assertThrows(buyTokens(account1, token._address, contributionMin)) + await TestLib.assertCallFails(buyTokens(account1, token._address, contributionMin)) }) it('buyTokens with 0 ETH', async () => { - await TestLib.assertThrows(buyTokens(account1, account1, new BigNumber(0))) + await TestLib.assertCallFails(buyTokens(account1, account1, new BigNumber(0))) }) it('buyTokens with 1 wei', async () => { - await TestLib.assertThrows(buyTokens(account1, account1, new BigNumber(1))) + await TestLib.assertCallFails(buyTokens(account1, account1, new BigNumber(1))) }) it('buyTokens with minimum contribution - 1 wei', async () => { - await TestLib.assertThrows(buyTokens(account1, account1, contributionMin.sub(1))) + await TestLib.assertCallFails(buyTokens(account1, account1, contributionMin.sub(1))) }) it('buyTokens with minimum contribution', async () => { @@ -183,18 +182,18 @@ describe('FlexibleTokenSale Contract - buyTokens tests', () => { await sale.methods.setMaxTokensPerAccount(0).send({ from: owner }) }) - it('buyTokens with bonus = 10000', async () => { - await sale.methods.setBonus(10000).send({ from: owner }) + it('buyTokens with bonus = 0', async () => { + await sale.methods.setBonus(0).send({ from: owner }) await buyTokens(account1, account1, contributionMin) }) - it('buyTokens with bonus = 10755', async () => { - await sale.methods.setBonus(10755).send({ from: owner }) + it('buyTokens with bonus = 755', async () => { + await sale.methods.setBonus(755).send({ from: owner }) await buyTokens(account1, account1, contributionMin) }) - it('buyTokens with bonus = 20000', async () => { - await sale.methods.setBonus(20000).send({ from: owner }) + it('buyTokens with bonus = 10000', async () => { + await sale.methods.setBonus(10000).send({ from: owner }) await buyTokens(account1, account1, contributionMin) }) @@ -220,7 +219,7 @@ describe('FlexibleTokenSale Contract - buyTokens tests', () => { }) it('buyTokens with an additional minimum contribution', async () => { - await TestLib.assertThrows(buyTokens(account1, account1, contributionMin)) + await TestLib.assertCallFails(buyTokens(account1, account1, contributionMin)) }) it('buyTokens before start time', async () => { @@ -228,7 +227,7 @@ describe('FlexibleTokenSale Contract - buyTokens tests', () => { await sale.methods.changeTime(START_TIME - 1000).send({ from: owner }) - await TestLib.assertThrows(buyTokens(account1, account1, contributionMin)) + await TestLib.assertCallFails(buyTokens(account1, account1, contributionMin)) }) it('buyTokens during sale time', async () => { @@ -240,14 +239,14 @@ describe('FlexibleTokenSale Contract - buyTokens tests', () => { it('buyTokens after end time', async () => { await sale.methods.changeTime(END_TIME + 1).send({ from: owner }) - await TestLib.assertThrows(buyTokens(account1, account1, contributionMin)) + await TestLib.assertCallFails(buyTokens(account1, account1, contributionMin)) }) it('buyTokens after finalized', async () => { await sale.methods.changeTime(START_TIME + 1).send({ from: owner }) await sale.methods.finalize().send({ from: owner }) - await TestLib.assertThrows(buyTokens(account1, account1, contributionMin)) + await TestLib.assertCallFails(buyTokens(account1, account1, contributionMin)) }) }) }) diff --git a/tests/Math.js b/tests/Enuma/Math.js similarity index 87% rename from tests/Math.js rename to tests/Enuma/Math.js index eb5abd9..0df215b 100644 --- a/tests/Math.js +++ b/tests/Enuma/Math.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // Math Library Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -104,16 +103,17 @@ describe('Math Library', () => { }) it('add(uint256, 1)', async () => { - await TestLib.assertThrows(math.methods.add(uint256Max, 1).call()) + await TestLib.assertCallFails(math.methods.add(uint256Max, 1).call()) }) it('add(1, uint256)', async () => { - await TestLib.assertThrows(math.methods.add(1, uint256Max).call()) + await TestLib.assertCallFails(math.methods.add(1, uint256Max).call()) }) it('add(uint256, uint256)', async () => { - await TestLib.assertThrows(math.methods.add(uint256Max, uint256Max).call()) + await TestLib.assertCallFails(math.methods.add(uint256Max, uint256Max).call()) }) + }) @@ -124,7 +124,7 @@ describe('Math Library', () => { }) it('sub(0, 1)', async () => { - await TestLib.assertThrows(math.methods.sub(0, 1).call()) + await TestLib.assertCallFails(math.methods.sub(0, 1).call()) }) it('sub(1, 0)', async () => { @@ -144,11 +144,11 @@ describe('Math Library', () => { }) it('add(0, uint256)', async () => { - await TestLib.assertThrows(math.methods.sub(0, uint256Max).call()) + await TestLib.assertCallFails(math.methods.sub(0, uint256Max).call()) }) it('add(1, uint256)', async () => { - await TestLib.assertThrows(math.methods.sub(1, uint256Max).call()) + await TestLib.assertCallFails(math.methods.sub(1, uint256Max).call()) }) it('sub(uint256, uint256)', async () => { @@ -188,15 +188,15 @@ describe('Math Library', () => { }) it('mul(uint256, 2)', async () => { - await TestLib.assertThrows(math.methods.mul(uint256Max, 2).call()) + await TestLib.assertCallFails(math.methods.mul(uint256Max, 2).call()) }) it('mul(2, uint256)', async () => { - await TestLib.assertThrows(math.methods.mul(2, uint256Max).call()) + await TestLib.assertCallFails(math.methods.mul(2, uint256Max).call()) }) it('mul(uint256, uint256)', async () => { - await TestLib.assertThrows(math.methods.mul(uint256Max, uint256Max).call()) + await TestLib.assertCallFails(math.methods.mul(uint256Max, uint256Max).call()) }) }) @@ -222,7 +222,7 @@ describe('Math Library', () => { context('div', async () => { it('div(0, 0)', async () => { - await TestLib.assertThrows(math.methods.div(0, 0).call()) + await TestLib.assertCallFails(math.methods.div(0, 0).call()) }) it('div(0, 1)', async () => { @@ -234,7 +234,7 @@ describe('Math Library', () => { }) it('div(1, 0)', async () => { - await TestLib.assertThrows(math.methods.div(1, 0).call()) + await TestLib.assertCallFails(math.methods.div(1, 0).call()) }) it('div(1, 1)', async () => { @@ -270,7 +270,7 @@ describe('Math Library', () => { }) it('div(uint256, 0)', async () => { - await TestLib.assertThrows(math.methods.div(uint256Max, 0).call()) + await TestLib.assertCallFails(math.methods.div(uint256Max, 0).call()) }) it('div(uint256, 1)', async () => { diff --git a/tests/OpsManaged.js b/tests/Enuma/OpsManaged.js similarity index 94% rename from tests/OpsManaged.js rename to tests/Enuma/OpsManaged.js index a370105..6081de0 100644 --- a/tests/OpsManaged.js +++ b/tests/Enuma/OpsManaged.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // OpsManaged Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -252,22 +251,22 @@ describe('Owned Contract', () => { }) it('setOpsAddress(this)', async () => { - await TestLib.assertThrows(instance.methods.setOpsAddress(instance._address).call({ from: owner })) + await TestLib.assertCallFails(instance.methods.setOpsAddress(instance._address).call({ from: owner })) }) it('setOpsAddress(owner)', async () => { - await TestLib.assertThrows(instance.methods.setOpsAddress(owner).call({ from: owner })) + await TestLib.assertCallFails(instance.methods.setOpsAddress(owner).call({ from: owner })) }) it('setOpsAddress(other account) as non-owner', async () => { - await TestLib.assertThrows(instance.methods.setOpsAddress(otherAccount).call({ from: otherAccount2 })) + await TestLib.assertCallFails(instance.methods.setOpsAddress(otherAccount).call({ from: otherAccount2 })) }) it('setOpsAddress(other account) as ops', async () => { await instance.methods.setOpsAddress(ops).send({ from: owner }) assert.equal(await instance.methods.opsAddress().call(), ops) - await TestLib.assertThrows(instance.methods.setOpsAddress(otherAccount).call({ from: ops })) + await TestLib.assertCallFails(instance.methods.setOpsAddress(otherAccount).call({ from: ops })) }) it('setOpsAddress(other account) as owner', async () => { diff --git a/tests/Owned.js b/tests/Enuma/Owned.js similarity index 86% rename from tests/Owned.js rename to tests/Enuma/Owned.js index 3b057bc..293d174 100644 --- a/tests/Owned.js +++ b/tests/Enuma/Owned.js @@ -1,12 +1,11 @@ // ---------------------------------------------------------------------------- // Owned Contract Tests -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../tools/testlib.js') const Utils = require('./lib/StdTestUtils.js') @@ -108,19 +107,19 @@ describe('Owned Contract', () => { context('initiateOwnershipTransfer', async () => { it('initiateOwnershipTransfer(0)', async () => { - await TestLib.assertThrows(instance.methods.initiateOwnershipTransfer(0).call()) + await TestLib.assertCallFails(instance.methods.initiateOwnershipTransfer(0).call()) }) it('initiateOwnershipTransfer(this)', async () => { - await TestLib.assertThrows(instance.methods.initiateOwnershipTransfer(instance._address).call()) + await TestLib.assertCallFails(instance.methods.initiateOwnershipTransfer(instance._address).call()) }) it('initiateOwnershipTransfer(owner)', async () => { - await TestLib.assertThrows(instance.methods.initiateOwnershipTransfer(owner).call()) + await TestLib.assertCallFails(instance.methods.initiateOwnershipTransfer(owner).call()) }) it('initiateOwnershipTransfer(other account), as non-owner', async () => { - await TestLib.assertThrows(instance.methods.initiateOwnershipTransfer(otherAccount).call({ from: otherAccount2 })) + await TestLib.assertCallFails(instance.methods.initiateOwnershipTransfer(otherAccount).call({ from: otherAccount2 })) }) it('initiateOwnershipTransfer(other account), as owner', async () => { @@ -139,11 +138,11 @@ describe('Owned Contract', () => { context('completeOwnershipTransfer', async () => { it('completeOwnershipTransfer(owner)', async () => { - await TestLib.assertThrows(instance.methods.completeOwnershipTransfer().call({ from: owner })) + await TestLib.assertCallFails(instance.methods.completeOwnershipTransfer().call({ from: owner })) }) it('completeOwnershipTransfer(yet another account)', async () => { - await TestLib.assertThrows(instance.methods.completeOwnershipTransfer().call({ from: otherAccount2 })) + await TestLib.assertCallFails(instance.methods.completeOwnershipTransfer().call({ from: otherAccount2 })) }) it('completeOwnershipTransfer(other account), as owner', async () => { diff --git a/tests/lib/StdTestUtils.js b/tests/Enuma/lib/StdTestUtils.js similarity index 97% rename from tests/lib/StdTestUtils.js rename to tests/Enuma/lib/StdTestUtils.js index 8c1e5fc..8ac32e6 100644 --- a/tests/lib/StdTestUtils.js +++ b/tests/Enuma/lib/StdTestUtils.js @@ -1,13 +1,13 @@ // ---------------------------------------------------------------------------- // Standard Contract Testing Utility Library -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- -const TestLib = require('../../tools/testlib.js') +const TestLib = require('../../../tools/testlib.js') module.exports.tokensFromWei = function (tokensPerKEther, weiAmount, bonus) { @@ -16,7 +16,7 @@ module.exports.tokensFromWei = function (tokensPerKEther, weiAmount, bonus) { function tokensFromWei(tokensPerKEther, weiAmount, bonus) { - return weiAmount.mul(tokensPerKEther).mul(bonus).div(1000).div(10000) + return weiAmount.mul(tokensPerKEther).mul(bonus.add(10000)).div(1000).div(10000) } @@ -26,7 +26,7 @@ module.exports.weiFromTokens = function (tokensPerKEther, tokenAmount, bonus) { function weiFromTokens(tokensPerKEther, tokenAmount, bonus) { - return tokenAmount.mul(1000).mul(10000).div(tokensPerKEther.mul(bonus)) + return tokenAmount.mul(1000).mul(10000).div(tokensPerKEther.mul(bonus.add(10000))) } @@ -322,6 +322,7 @@ module.exports.buyTokens = async (token, sale, owner, wallet, DECIMALS_FACTOR, f const totalEtherCollectedBefore = new BigNumber(await sale.methods.totalEtherCollected().call()) const bonus = new BigNumber(await sale.methods.bonus().call()) + var tokensPerKEther = new BigNumber(await sale.methods.tokensPerKEther().call()) const maxTokensPerAccount = new BigNumber(await sale.methods.maxTokensPerAccount().call()) @@ -359,7 +360,7 @@ module.exports.buyTokens = async (token, sale, owner, wallet, DECIMALS_FACTOR, f await sale.methods.setTokensPerKEther(tokensPerKEther).send({ from: owner }) // Send 2 ETH to make sure we send way over the cost for purchasing the entire window - transferAmount = new BigNumber(web3.utils.toWei(2, 'ether')) + transferAmount = new BigNumber(web3.utils.toWei('2', 'ether')) expectedTokens = maxTokens expectedTokenCost = weiFromTokens(tokensPerKEther, expectedTokens, bonus).trunc() @@ -379,7 +380,7 @@ module.exports.buyTokens = async (token, sale, owner, wallet, DECIMALS_FACTOR, f // // Purchase the tokens // - assert.equal(await sale.methods.buyTokens(to).call({ from: from, value: transferAmount, gasPrice: gasPrice }), true) + assert.equal(new BigNumber(await sale.methods.buyTokens(to).call({ from: from, value: transferAmount, gasPrice: gasPrice })), expectedTokens) const receipt = await sale.methods.buyTokens(to).send({ from: from, value: transferAmount, gasPrice: gasPrice }) // diff --git a/tests/lib/BluzelleTestUtils.js b/tests/lib/BluzelleTestUtils.js index 2abbe63..21c7f90 100644 --- a/tests/lib/BluzelleTestUtils.js +++ b/tests/lib/BluzelleTestUtils.js @@ -8,6 +8,7 @@ // ---------------------------------------------------------------------------- const TestLib = require('../../tools/testlib.js') +const StdUtils = require('../Enuma/lib/StdTestUtils.js') module.exports.checkSetCurrentStage = (receipt, newStage) => { @@ -22,6 +23,19 @@ module.exports.checkSetCurrentStage = (receipt, newStage) => { } +module.exports.checkSetStageBonus = (receipt, stage, bonus) => { + + TestLib.checkStatus(receipt) + + assert.equal(Object.keys(receipt.events).length, 1) + assert.equal(typeof receipt.events.StageBonusUpdated, 'object') + const eventArgs = receipt.events.StageBonusUpdated.returnValues + assert.equal(Object.keys(eventArgs).length, 4) + assert.equal(eventArgs._stage, stage) + assert.equal(eventArgs._bonus, bonus) +} + + module.exports.checkSetWhitelistedStatus = (receipt, address, stage) => { TestLib.checkStatus(receipt) @@ -35,7 +49,7 @@ module.exports.checkSetWhitelistedStatus = (receipt, address, stage) => { } -module.exports.checkSetWhitelistedBatch = (receipt, addresses, stages) => { +module.exports.checkSetWhitelistedBatch = (receipt, addresses, stage) => { TestLib.checkStatus(receipt) @@ -50,6 +64,147 @@ module.exports.checkSetWhitelistedBatch = (receipt, addresses, stages) => { assert.equal(e.event, 'WhitelistedStatusUpdated') assert.equal(Object.keys(e.returnValues).length, 4) assert.equal(e.returnValues._address, addresses[i]) - assert.equal(e.returnValues._stage, stages[i]) + assert.equal(e.returnValues._stage, stage) + } +} + + +// Modified version of Enuma's StdTestLib.buyTokens to cover additional cases with: +// - bonus amounts per stage +// - tokens purchased per account +// +module.exports.buyTokens = async (token, sale, owner, wallet, DECIMALS_FACTOR, from, to, amount) => { + + // + // Capture state before purchase + // + const saleTokensBefore = new BigNumber(await token.methods.balanceOf(sale._address).call()) + const fromTokensBefore = new BigNumber(await token.methods.balanceOf(from).call()) + const toTokensBefore = new BigNumber(await token.methods.balanceOf(to).call()) + + const saleEthBefore = new BigNumber(await TestLib.getBalance(sale._address)) + const fromEthBefore = new BigNumber(await TestLib.getBalance(from)) + const toEthBefore = new BigNumber(await TestLib.getBalance(to)) + const walletEthBefore = new BigNumber(await TestLib.getBalance(wallet)) + + const totalTokensSoldBefore = new BigNumber(await sale.methods.totalTokensSold().call()) + const totalEtherCollectedBefore = new BigNumber(await sale.methods.totalEtherCollected().call()) + + const beneficiaryStage = await sale.methods.whitelist(to).call() + + var bonus = new BigNumber(await sale.methods.stageBonus(beneficiaryStage).call()) + if (bonus.eq(0)) { + bonus = new BigNumber(await sale.methods.bonus().call()) + } + + var tokensPerKEther = new BigNumber(await sale.methods.tokensPerKEther().call()) + const maxTokensPerAccount = new BigNumber(await sale.methods.maxTokensPerAccount().call()) + + const gasPrice = await web3.eth.getGasPrice() + + + // + // Calculate costs, tokens, refund + // + var expectedTokens = null + var expectedTokenCost = null + var transferAmount = null + + var maxTokens = saleTokensBefore + if (maxTokensPerAccount.gt(0)) { + if (maxTokensPerAccount.lt(saleTokensBefore)) { + maxTokens = maxTokensPerAccount + } + + const tokensPurchased = await sale.methods.accountTokensPurchased(to).call() + + const quotaLeft = maxTokensPerAccount.sub(tokensPurchased) + + if (quotaLeft.gt(0) && quotaLeft.lt(maxTokens)) { + maxTokens = quotaLeft + } + } + + if (amount == -1) { + // Set the token price to that we can buy everything with 1 ETH, to make it easier for testing + tokensPerKEther = maxTokens.mul(1000).div(DECIMALS_FACTOR).trunc() + + if (tokensPerKEther.eq(0)) { + tokensPerKEther = new BigNumber(1) + } + + await sale.methods.setTokensPerKEther(tokensPerKEther).send({ from: owner }) + + // Send 2 ETH to make sure we send way over the cost for purchasing the entire window + transferAmount = new BigNumber(web3.utils.toWei('2', 'ether')) + + expectedTokens = maxTokens + expectedTokenCost = StdUtils.weiFromTokens(tokensPerKEther, expectedTokens, bonus).trunc() + } else { + transferAmount = amount + expectedTokens = StdUtils.tokensFromWei(tokensPerKEther, transferAmount, bonus).trunc() + + if (expectedTokens.gt(maxTokens)) { + expectedTokens = maxTokens + expectedTokenCost = StdUtils.weiFromTokens(tokensPerKEther, expectedTokens, bonus).trunc() + } else { + expectedTokenCost = transferAmount + } + } + + + // + // Purchase the tokens + // + assert.equal(new BigNumber(await sale.methods.buyTokens(to).call({ from: from, value: transferAmount, gasPrice: gasPrice })), expectedTokens) + const receipt = await sale.methods.buyTokens(to).send({ from: from, value: transferAmount, gasPrice: gasPrice }) + + // + // Capture state after purchase + // + const saleTokensAfter = new BigNumber(await token.methods.balanceOf(sale._address).call()) + const fromTokensAfter = new BigNumber(await token.methods.balanceOf(from).call()) + const toTokensAfter = new BigNumber(await token.methods.balanceOf(to).call()) + + const saleEthAfter = new BigNumber(await TestLib.getBalance(sale._address)) + const fromEthAfter = new BigNumber(await TestLib.getBalance(from)) + const toEthAfter = new BigNumber(await TestLib.getBalance(to)) + const walletEthAfter = new BigNumber(await TestLib.getBalance(wallet)) + + const totalTokensSoldAfter = new BigNumber(await sale.methods.totalTokensSold().call()) + const totalEtherCollectedAfter = new BigNumber(await sale.methods.totalEtherCollected().call()) + + + // + // Validate state change + // + assert.equal(saleTokensAfter.sub(saleTokensBefore), expectedTokens.mul(-1)) + + if (from !== to) { + assert.equal(fromTokensAfter.sub(fromTokensBefore), new BigNumber(0)) + assert.equal(toTokensAfter.sub(toTokensBefore), expectedTokens) + } else { + assert.equal(toTokensAfter.sub(toTokensBefore), expectedTokens) } + + assert.equal(totalTokensSoldAfter.sub(totalTokensSoldBefore), expectedTokens) + assert.equal(totalEtherCollectedAfter.sub(totalEtherCollectedBefore), expectedTokenCost) + + const fromEthSpent = fromEthBefore.sub(fromEthAfter) + const gasUsed = new BigNumber(receipt.gasUsed) + const expectedEthSpent = expectedTokenCost.add(gasUsed.mul(gasPrice)) + assert.equal(fromEthSpent, expectedEthSpent) + + if (from !== to && to != wallet) { + assert.equal(toEthAfter.sub(toEthBefore), new BigNumber(0)) + } + + assert.equal(walletEthAfter.sub(walletEthBefore), expectedTokenCost) + + + // + // Validate events + // + StdUtils.checkBuyTokens(receipt, from, to, expectedTokenCost, expectedTokens) } + diff --git a/tools/build.sh b/tools/build.sh index 4fa9b81..94fd565 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -1,4 +1,4 @@ #!/bin/bash mkdir -p "../build/" -solc --bin --abi --optimize --optimize-runs 200 --overwrite -o "../build/" "../contracts"/*.sol +solc --bin --abi --optimize --optimize-runs 200 --overwrite -o "../build/" "../contracts"/*.sol "../contracts/Enuma"/*.sol diff --git a/tools/deploy.js b/tools/deploy.js index 874ef19..f3db042 100644 --- a/tools/deploy.js +++ b/tools/deploy.js @@ -7,6 +7,7 @@ // The MIT Licence. // ---------------------------------------------------------------------------- +const fs = require('fs') const Web3 = require('web3') const BigNumber = require('bignumber.js') const Moment = require('moment') @@ -28,7 +29,7 @@ const Utils = require('./utils.js') // 3. Initialize the sale contract. // 4. Set the ops key of token to the sale contract. // 5. Set the ops key of sale to a ops key. -// 6. Send presale tokens from owner to sale contract. +// 6. Send initial sale tokens from owner to sale contract. // ** For each step, validate all properties and events. // ---------------------------------------------------------------------------- @@ -42,13 +43,13 @@ const TOKEN_TOTALSUPPLY = new BigNumber("500000000").mul(DECIMALS_FACTOR) const CONTRIBUTION_MIN = new BigNumber(0.1).mul(DECIMALS_FACTOR) -// Presale configuration -const PRESALE_TOKENS = new BigNumber("15000000").mul(DECIMALS_FACTOR) -const PRESALE_TOKENSPERKETHER = new BigNumber("1700000") -const PRESALE_BONUS = new BigNumber("12000") -const PRESALE_MAXTOKENSPERACCOUNT = new BigNumber("17000").mul(DECIMALS_FACTOR) -const PRESALE_STARTTIME = 1511870400 -const PRESALE_ENDTIME = 1512043200 +// Initial Sale Configuration +const INITIAL_SALE_TOKENS = new BigNumber("15000000").mul(DECIMALS_FACTOR) +const INITIAL_TOKENSPERKETHER = new BigNumber("1700000") +const INITIAL_BONUS = new BigNumber("2000") // 20.00% +const INITIAL_MAXTOKENSPERACCOUNT = new BigNumber("17000").mul(DECIMALS_FACTOR) +const INITIAL_STARTTIME = 1511870400 +const INITIAL_ENDTIME = 1512043200 var sale = null @@ -75,7 +76,9 @@ function recordTransaction(description, receipt, display) { async function run() { - const web3 = await Utils.buildWeb3('http://localhost:8545') + const config = JSON.parse(fs.readFileSync('../config.json')) + + const web3 = await Utils.buildWeb3(config.web3Url) accounts = await web3.eth.getAccounts() @@ -129,11 +132,11 @@ async function run() { sale = deploymentResult.instance assert.equal(await sale.methods.owner().call(), owner) assert.equal(await sale.methods.currentStage().call(), 1) - assert.equal(await sale.methods.bonus().call(), PRESALE_BONUS) - assert.equal(await sale.methods.tokensPerKEther().call(), PRESALE_TOKENSPERKETHER.toNumber()) - assert.equal(await sale.methods.maxTokensPerAccount().call(), PRESALE_MAXTOKENSPERACCOUNT.toNumber()) - assert.equal(await sale.methods.startTime().call(), PRESALE_STARTTIME) - assert.equal(await sale.methods.endTime().call(), PRESALE_ENDTIME) + assert.equal(await sale.methods.bonus().call(), INITIAL_BONUS.toNumber()) + assert.equal(await sale.methods.tokensPerKEther().call(), INITIAL_TOKENSPERKETHER.toNumber()) + assert.equal(await sale.methods.maxTokensPerAccount().call(), INITIAL_MAXTOKENSPERACCOUNT.toNumber()) + assert.equal(await sale.methods.startTime().call(), INITIAL_STARTTIME) + assert.equal(await sale.methods.endTime().call(), INITIAL_ENDTIME) console.log('') // @@ -168,10 +171,10 @@ async function run() { console.log('') // - // Send presale tokens to the sale contract + // Send initial sale tokens to the sale contract // - console.log('Sending presale tokens to the sale contract') - o = await token.methods.transfer(sale._address, PRESALE_TOKENS).send({ from: owner }) + console.log('Sending initial sale tokens to the sale contract') + o = await token.methods.transfer(sale._address, INITIAL_SALE_TOKENS).send({ from: owner }) recordTransaction('BluzelleToken.transfer', o, true) // Check that the Bluzelle constructor did properly fire the transfer event. assert.equal(Object.keys(o.events).length, 1) @@ -179,8 +182,8 @@ async function run() { assert.equal(Object.keys(returnValues).length, 6) assert.equal(returnValues._from, owner) assert.equal(returnValues._to, sale._address) - assert.equal(returnValues._value, PRESALE_TOKENS.toNumber()) - assert.isTrue(new BigNumber(await token.methods.balanceOf(sale._address).call()).eq(PRESALE_TOKENS)) + assert.equal(returnValues._value, INITIAL_SALE_TOKENS.toNumber()) + assert.isTrue(new BigNumber(await token.methods.balanceOf(sale._address).call()).eq(INITIAL_SALE_TOKENS)) console.log('') // diff --git a/tools/runTests.js b/tools/runTests.js index 38f6c40..51d8f18 100644 --- a/tools/runTests.js +++ b/tools/runTests.js @@ -1,9 +1,9 @@ // ---------------------------------------------------------------------------- // runTests.js - Utility for running unit tests under Mocha -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- const fs = require('fs') @@ -13,6 +13,23 @@ const Mocha = require('mocha') const AsyncFile = require('async-file') + +async function findTestFiles(folderPath) { + const fileNames = await AsyncFile.readdir(folderPath) + + var filePaths = [] + for (i = 0; i < fileNames.length; i++) { + if (fileNames[i].endsWith(".js") !== true) { + continue + } + + filePaths.push(Path.join(folderPath, fileNames[i])) + } + + return filePaths +} + + async function run(filePath) { var filePaths = null @@ -21,44 +38,66 @@ async function run(filePath) { filePaths = [ filePath ] } else { const testDir = Path.join(__dirname, '../tests') - - const fileNames = await AsyncFile.readdir(testDir) + const enumaTestDir = Path.join(testDir, 'Enuma') filePaths = [] - for (i = 0; i < fileNames.length; i++) { - if (fileNames[i].endsWith(".js") !== true) { - continue - } - - filePaths.push(Path.join(testDir, fileNames[i])) + if (fs.existsSync(enumaTestDir)) { + filePaths = filePaths.concat(await findTestFiles(enumaTestDir)) } + + filePaths = filePaths.concat(await findTestFiles(testDir)) } + const configFilePath = findConfigFilePath(Path.resolve('./')) + const rootPath = Path.dirname(configFilePath) + + const testLib = require(Path.join(rootPath, 'tools/testlib.js')) + testLib.initialize() + + global.TestLib = testLib var mocha = new Mocha({ ui : 'bdd', reporter : 'spec', //reporter: 'json' timeout : 20000 // 20 seconds timeout since deployment to test/main net may take time... - }); + }) for (i = 0; i < filePaths.length; i++) { - if (filePaths[i].endsWith(".js") !== true) { - continue - } - mocha.addFile( filePaths[i] ) } - var runner = mocha.run( (errors) => { - }) + try { + var runner = mocha.run( (errors) => { + }) + } catch (error) { + console.log(error.stack) + } } + if (process.argv.length > 2) { run(process.argv[2]) } else { run() } + + +function findConfigFilePath(folderPath) { + const filePath = Path.join(folderPath, "config.json") + + if (fs.existsSync(filePath)) { + return filePath + } + + const parentFolderPath = Path.dirname(folderPath) + + if (folderPath == '/' || parentFolderPath == folderPath) { + return null + } + + return findConfigFilePath(parentFolderPath) +} diff --git a/tools/startTestRpc.sh b/tools/startGanache.sh similarity index 99% rename from tools/startTestRpc.sh rename to tools/startGanache.sh index eaa8343..78f71c5 100755 --- a/tools/startTestRpc.sh +++ b/tools/startGanache.sh @@ -1,5 +1,5 @@ #!/bin/bash -testrpc \ +ganache-cli \ --network-id 88 \ --account=0x42551cf8b134329c39e611b6cf21aac0f0eeae787f378c69b4ddf76aa1b006c0,100000000000000000000000000000000000 \ --account=0x42551cf8b134329c39e611b6cf21aac0f0eeae787f378c69b4ddf76aa1b006c1,100000000000000000000000000000000000 \ diff --git a/tools/testlib.js b/tools/testlib.js index 072ac21..f70a601 100644 --- a/tools/testlib.js +++ b/tools/testlib.js @@ -1,23 +1,26 @@ // ---------------------------------------------------------------------------- // testlib.js (Lite) - Library used for writing unit tests. -// Enuma Blockchain Framework +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- +const fs = require('fs') const Web3 = require('web3') const Path = require('path') const Chai = require('chai') + const Utils = require('./utils.js') // GLOBALS -BigNumber = require('bignumber.js') -assert = Chai.assert -Moment = require('moment') -web3 = null +global.BigNumber = require('bignumber.js') +global.assert = Chai.assert +global.Moment = require('moment') +global.web3 = null + var fn = assert.equal @@ -32,7 +35,10 @@ assert.equal = (a, b, c) => { module.exports.initialize = async () => { - web3 = await Utils.buildWeb3('http://localhost:8545') + const configFilePath = Utils.findConfigFilePath(Path.resolve('./')) + const config = JSON.parse(fs.readFileSync(configFilePath)) + + global.web3 = await Utils.buildWeb3(config.web3Url) } @@ -49,11 +55,7 @@ module.exports.checkStatus = (receipt) => { function checkStatus(receipt) { // Since the Ethereum Byzantium fork, there is a status field in the receipt. - // That flag doesn't exist in the testrpc implementation yet. Remove the typeof check once it's added. - // https://ethereum.stackexchange.com/questions/28077/how-do-i-detect-a-failed-transaction-after-the-byzantium-fork-as-the-revert-opco - if (typeof receipt.status !== 'undefined') { - assert.equal(receipt.status, 1) - } + assert.equal(receipt.status, 1, "Transaction receipt 'status' != 1") } @@ -88,18 +90,34 @@ module.exports.assertNoEvents = (receipt) => { } -module.exports.assertThrows = async (promise) => { +module.exports.assertSendFails = async (promise) => { + try { + const receipt = await promise + assert(receipt.status == '0x0', "Expected transaction receipt to have status 0") + } catch (error) { + const isRevert = /^.+VM Exception.+revert$/.test(error.message) + //const isInvalidOpcode = error.message.indexOf('invalid opcode') > -1 + //const isOutOfGas = error.message.indexOf('out of gas') > -1 + + //assert(isInvalidOpcode || isOutOfGas || isDecode, "Expected transaction to fail, but got an error instead: " + error) + assert(isRevert, "Expected transaction to fail, but got an error instead: " + error) + } +} + + +module.exports.assertCallFails = async (promise) => { try { await promise } catch (error) { - const isInvalidOpcode = error.message.indexOf('invalid opcode') > -1 - const isOutOfGas = error.message.indexOf('out of gas') > -1 - const isDecode = error.message.indexOf("Couldn't decode") > -1 && error.message.indexOf("from ABI: 0x") > -1 + const isInvalidOpcode = /^.+VM Exception.+invalid opcode$/.test(error.message) + const isCallFailure = /^.+decode.+from ABI: 0x$/.test(error.message) + const isRevert = /^.+VM Exception.+revert$/.test(error.message) - assert(isInvalidOpcode || isOutOfGas || isDecode, "Expected transaction to fail, but got an error instead: " + error) + assert(isCallFailure || isRevert || isInvalidOpcode, "Expected 'call' to fail, but got an error instead: " + error) return } assert(false, "Did not throw as expected") } + diff --git a/tools/updateWhitelist.js b/tools/updateWhitelist.js new file mode 100755 index 0000000..3d6408d --- /dev/null +++ b/tools/updateWhitelist.js @@ -0,0 +1,155 @@ + +const fs = require('fs') +const Web3 = require('web3') +const Chai = require('chai') +const BigNumber = require('bignumber.js') +const assert = Chai.assert +const Moment = require('moment') + + +// +// Configuration +// +const config = JSON.parse(fs.readFileSync('../config.json')) +const web3Url = config.web3Url +const abiFilePath = '../build/BluzelleTokenSale.abi' +const opsAddress = '0xb0030c1cc4b979ee749e71b17c082b915dcd3c92' +const contractAddress = '0x15E43D1Cdec9640Dc8cdDaB30C1578F40993b935' +const startTime = 1511870400 +const maxBatchSize = 20 // We don't want the batches to be too big else it will consume more gas than allowed in a block. + + + +const web3 = new Web3(new Web3.providers.HttpProvider(web3Url)) + + + +// +// Main function to process the whitelist +// +async function run() { + + // Load the Bluzelle sale contract ABI. + const abi = JSON.parse(fs.readFileSync(abiFilePath).toString()); + + // Set default options (e.g. maximum gas allowed for transactions). + const options = { + gas : '4700000' + } + + // Get instance of the contract. + const instance = new web3.eth.Contract(abi, contractAddress, options) + + // Do some checks to make sure we are talking to the right contract. + assert.equal(await instance.methods.startTime().call(), startTime) + + // Get the list of addresses to whitelist + const addresses = await getAddresses() + + if (!addresses || addresses.length == 0) { + console.log('No address to process') + system.exit(1) + } + + // Update the whitelist 1 address at a time as opposed to in batch. + const stage = 1 + await updateWhitelist1By1(instance, addresses, stage) + //await updateWhitelistBatch(instance, addresses, stage) + + console.log(addresses.length + ' addresses processed successfully.') +} + + + +async function updateWhitelist1By1(instance, addresses, stage) { + + for (var i = 0; i < addresses.length; i++) { + const address = addresses[0] + + // Just do a sanity check first to make sure everything is ok. It should return true. + assert.equal(await instance.methods.setWhitelistedStatus(address, 1).call({ from: opsAddress }), true) + + // Send the transaction to the blockchain. + const receipt = await instance.methods.setWhitelistedStatus(address, 1).send({ from: opsAddress }) + console.log(receipt) + + // Make sure the receipt looks correct + assert.equal(receipt.events.WhitelistedStatusUpdated.event, "WhitelistedStatusUpdated") + assert.equal(receipt.events.WhitelistedStatusUpdated.returnValues._address, address) + assert.equal(receipt.events.WhitelistedStatusUpdated.returnValues._stage, stage) + + // Verify the whitelisted state in the sale contract + assert.equal(await instance.methods.whitelist(address).call(), stage) + } +} + + +async function updateWhitelistBatch(instance, addresses, stage) { + + // Break the list of addresses into batches + var batches = [] + var tempArray = addresses.slice() + while (tempArray.length > 0) { + const batch = tempArray.splice(0, maxBatchSize) + batches.push(batch) + } + + // Process addresses in batch + console.log('Sending whitelist info in ' + batches.length + ' batches.') + for (var i = 0; i < batches.length; i++) { + console.log('Processing batch ' + (i + 1) + ' of ' + batches.length) + + const batch = batches[i] + const stages = Array(batch.length).fill(stage) + + // Just do a sanity check first to make sure everything is ok. It should return true. + assert.equal(await instance.methods.setWhitelistedBatch(batch, stages).call({ from: opsAddress }), true) + + // Send the transaction to the blockchain. + const receipt = await instance.methods.setWhitelistedBatch(batch, stages).send({ from: opsAddress }) + console.log(receipt) + + // Validate the receipt + const events = receipt.events.WhitelistedStatusUpdated + assert.equal(Object.keys(events).length, batch.length) + for (var j = 0; j < batch.length; j++) { + assert.equal(events[j].returnValues._address, batch[j]) + assert.equal(events[j].returnValues._stage, stages[j]) + + // Verify the whitelisted state in the sale contract + assert.equal(await instance.methods.whitelist(batch[j]).call(), stages[j]) + } + } +} + + +// +// getAddresses - Helper function to read whitelist addresses from stdin +// +async function getAddresses() { + + const lines = fs.readFileSync(0).toString().trim().split("\n") + + if (lines.length == 0) { + console.log("No lines to process from stdin.") + system.exit(1) + } + + var addresses = [] + for (var i = 0; i < lines.length; i++) { + const address = lines[i].trim() + + // Make sure that all addresses we read from stdin are checksumed. + if (web3.utils.checkAddressChecksum(address) !== true) { + console.log("Invalid checksumed address: " + address) + system.exit(1) + } + + addresses.push(address) + } + + return addresses +} + + +run() diff --git a/tools/utils.js b/tools/utils.js index eec270b..75cf88f 100644 --- a/tools/utils.js +++ b/tools/utils.js @@ -1,9 +1,9 @@ // ---------------------------------------------------------------------------- -// deployLib.js - Library for deploying smart contracts to the blockchain. -// Enuma Blockchain Framework +// utils.js - Set of helpers used by the tools. +// Enuma Blockchain Platform // // Copyright (c) 2017 Enuma Technologies. -// http://www.enuma.io/ +// https://www.enuma.io/ // ---------------------------------------------------------------------------- const fs = require('fs') @@ -11,7 +11,7 @@ const Web3 = require('web3') const Path = require('path') -const projectRoot = '../' +const projectRoot = Path.resolve('../') module.exports.buildWeb3 = async (url) => { @@ -115,4 +115,25 @@ async function deployContract(web3, name, abi, bytecode, args, options) { } +module.exports.findConfigFilePath = (folderPath) => { + return findConfigFilePath(folderPath) +} + + +function findConfigFilePath(folderPath) { + const filePath = Path.join(folderPath, "config.json") + + if (fs.existsSync(filePath)) { + return filePath + } + + const parentFolderPath = Path.dirname(folderPath) + + if (folderPath == '/' || parentFolderPath == folderPath) { + return null + } + + return findConfigFilePath(parentFolderPath) +} +