├── test ├── testpassword ├── 00_runGethNoInit.sh ├── 00_runGethNoInitIPC.sh ├── 00_runGeth.sh ├── testchain │ └── keystore │ │ ├── UTC--2017-05-20T02-37-30.360937280Z--a00af22d07c87d96eeeb0ed583f8f6ac7812827e │ │ ├── UTC--2017-05-20T02-37-58.104082316Z--a11aae29840fbb5c86e6fd4cf809eba183aef433 │ │ ├── UTC--2017-05-20T02-38-21.169063367Z--a22ab8a9d641ce77e06d98b7d7065d324d3d6976 │ │ ├── UTC--2017-05-20T02-38-41.707647638Z--a33a6c312d9ad0e0f2e95541beed0cc081621fd0 │ │ ├── UTC--2017-05-20T02-39-04.584040707Z--a44a08d3f6933c69212114bb66e2df1813651844 │ │ ├── UTC--2017-05-20T02-42-01.013666678Z--a55a151eb00fded1634d27d1127b4be4627079ea │ │ ├── UTC--2017-05-20T02-42-37.426191216Z--a66a85ede0cbe03694aa9d9de0bb19c99ff55bd9 │ │ ├── UTC--2017-05-20T02-42-58.899396047Z--a77a2b9d4b1c010a22a7c565dc418cef683dbcec │ │ ├── UTC--2017-05-20T02-47-09.150580614Z--a88a05d2b88283ce84c8325760b72a64591279a2 │ │ ├── UTC--2017-05-20T02-47-34.770098207Z--a99a0ae3354c06b1459fd441a32a3f71005d7da0 │ │ ├── UTC--2017-05-20T02-58-17.400859858Z--aaaa9de1e6c564446ebca0fd102d8bd92093c756 │ │ ├── UTC--2017-05-20T02-58-35.122799190Z--abba43e7594e3b76afb157989e93c6621497fd4b │ │ ├── UTC--2017-05-20T02-58-51.178332229Z--acca534c9f62ab495bd986e002ddf0f054caae4f │ │ ├── UTC--2017-05-20T02-59-14.034178720Z--adda9b762a00ff12711113bfdc36958b73d7f915 │ │ ├── UTC--2017-05-20T02-59-33.471449656Z--aeea63b5479b50f79583ec49dacdcf86ddeff392 │ │ ├── UTC--2017-05-20T02-59-51.697690594Z--affa4d3a80add8ce4018540e056dacb649589394 │ │ └── UTC--2017-06-19T11-23-24.451455213Z--bbbb34fa53a801b5f298744490a1596438bbbe50 ├── settings.txt ├── ERC20Interface.sol ├── SafeMath.sol ├── Owned.sol ├── genesis.json ├── README.md ├── OpenANXTokenConfig.sol ├── LockedTokens.sol ├── LockedTokens.js ├── deploymentData.txt ├── functions.js ├── OpenANXToken.sol └── 01_test1.sh ├── images ├── oax_icon-p.png ├── OAX_Token_28x28.png ├── openANX-WebpageStatistics-20170706-155350.png ├── openANX-PublicCrowdsaleChart-20170704-154010.png ├── openANX-EtherScan-AfterFinalise-20170704153213.png └── openANX-PublicCrowdsaleStatistics-20170704153927.png ├── .gitignore ├── scripts ├── tokenBalancesByAccounts.xls ├── TokensBought_20170625_015900.xls ├── TokensBought_20170703_223138.xls ├── TokensBought_20170704_150051-final.xls ├── tokenStats.txt ├── generateSingleSol.pl ├── Main_20170625_015900.txt ├── Main_20170704_150051-final.txt ├── getTokenBalances.sh └── getOpenANXTokenDetails.sh ├── audits ├── DarrylMorris_OpenANXOAXContractCodeReview_20170622.docx └── DarrylMorris_OpenANXOAXContractCodeReview_20170622.pdf ├── LICENSE ├── contracts ├── ERC20Interface.sol ├── SafeMath.sol ├── Owned.sol ├── OpenANXTokenConfig.sol ├── LockedTokens.sol ├── LockedTokens-deployed.sol └── OpenANXToken.sol └── README.md /test/testpassword: -------------------------------------------------------------------------------- 1 | testtest 2 | -------------------------------------------------------------------------------- /images/oax_icon-p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/images/oax_icon-p.png -------------------------------------------------------------------------------- /images/OAX_Token_28x28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/images/OAX_Token_28x28.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .project 3 | private 4 | private/* 5 | test/testchain/geth* 6 | test/testchain/history* 7 | .settings 8 | -------------------------------------------------------------------------------- /scripts/tokenBalancesByAccounts.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/scripts/tokenBalancesByAccounts.xls -------------------------------------------------------------------------------- /scripts/TokensBought_20170625_015900.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/scripts/TokensBought_20170625_015900.xls -------------------------------------------------------------------------------- /scripts/TokensBought_20170703_223138.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/scripts/TokensBought_20170703_223138.xls -------------------------------------------------------------------------------- /scripts/TokensBought_20170704_150051-final.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/scripts/TokensBought_20170704_150051-final.xls -------------------------------------------------------------------------------- /images/openANX-WebpageStatistics-20170706-155350.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/images/openANX-WebpageStatistics-20170706-155350.png -------------------------------------------------------------------------------- /images/openANX-PublicCrowdsaleChart-20170704-154010.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/images/openANX-PublicCrowdsaleChart-20170704-154010.png -------------------------------------------------------------------------------- /images/openANX-EtherScan-AfterFinalise-20170704153213.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/images/openANX-EtherScan-AfterFinalise-20170704153213.png -------------------------------------------------------------------------------- /images/openANX-PublicCrowdsaleStatistics-20170704153927.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/images/openANX-PublicCrowdsaleStatistics-20170704153927.png -------------------------------------------------------------------------------- /audits/DarrylMorris_OpenANXOAXContractCodeReview_20170622.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/audits/DarrylMorris_OpenANXOAXContractCodeReview_20170622.docx -------------------------------------------------------------------------------- /audits/DarrylMorris_OpenANXOAXContractCodeReview_20170622.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OAXFoundation/OpenANXToken/HEAD/audits/DarrylMorris_OpenANXOAXContractCodeReview_20170622.pdf -------------------------------------------------------------------------------- /test/00_runGethNoInit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | geth --datadir ./testchain --unlock 0 --password ./testpassword --rpc --rpcapi "eth,net,web3,debug" --rpccorsdomain '*' --rpcport 8646 --port 32323 --mine --minerthreads 1 --maxpeers 0 --cache 1024 --targetgaslimit 994712388 console 4 | 5 | -------------------------------------------------------------------------------- /test/00_runGethNoInitIPC.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | geth --datadir ./testchain --unlock 0 --password ./testpassword --rpc --rpccorsdomain '*' --rpcport 8646 --port 32323 --mine --minerthreads 1 --maxpeers 0 --ipcpath /Users/bok/Library/Ethereum/geth.ipc --targetgaslimit 994712388 console 4 | 5 | -------------------------------------------------------------------------------- /scripts/tokenStats.txt: -------------------------------------------------------------------------------- 1 | snapshot at block=3971324 time=1499144474 Tue, 04 Jul 2017 05:01:14 UTC 2 | number of accounts, some may have a zero balances=3702 3 | Total balance=100000000000000000000000000 4 | totalSupply=100000000000000000000000000 5 | number of accounts+balances, only with non-zero balances=0 6 | -------------------------------------------------------------------------------- /test/00_runGeth.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f ./testchain/geth/chaindata/* 4 | 5 | geth --datadir ./testchain init genesis.json 6 | 7 | geth --datadir ./testchain --unlock 0 --password ./testpassword --rpc --rpccorsdomain '*' --rpcport 8646 --rpcapi "eth,net,web3,debug" --port 32323 --mine --minerthreads 1 --maxpeers 0 --targetgaslimit 994712388 console 8 | 9 | -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-37-30.360937280Z--a00af22d07c87d96eeeb0ed583f8f6ac7812827e: -------------------------------------------------------------------------------- 1 | {"address":"a00af22d07c87d96eeeb0ed583f8f6ac7812827e","crypto":{"cipher":"aes-128-ctr","ciphertext":"beaef6b3628c1acf80877369d1dacdfb7f2faeab7d3f42a4de23260a804e0888","cipherparams":{"iv":"80a37ffc267761f99c868a66bb9c1db7"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"807a4ca34e14b5916400fbe53c3c1c6ce862b16dffb6bd4efa53ea582f88a6e1"},"mac":"d51a3264971d95448048670d83f76a5d070eaaafceb4cbb83434d1f9d2d0bdcc"},"id":"e3f0faf2-6ec2-4f29-a14e-c48d2d751b94","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-37-58.104082316Z--a11aae29840fbb5c86e6fd4cf809eba183aef433: -------------------------------------------------------------------------------- 1 | {"address":"a11aae29840fbb5c86e6fd4cf809eba183aef433","crypto":{"cipher":"aes-128-ctr","ciphertext":"3f764510b47491063a67abf19f2d169e18292174000f9d31a7e66f6e0f09fdf0","cipherparams":{"iv":"49642a5e48f18cc96eb5d710c1f4edd4"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c7c9cb821a91da4571397d6c9dacb879059038d82b80048599f59c4825816367"},"mac":"7e829480c58972bb4403b332a1bdf3e8ed05d80b817a3c4126673e377bd87255"},"id":"f1b73a4b-8229-4c3b-8844-f59f398087b0","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-38-21.169063367Z--a22ab8a9d641ce77e06d98b7d7065d324d3d6976: -------------------------------------------------------------------------------- 1 | {"address":"a22ab8a9d641ce77e06d98b7d7065d324d3d6976","crypto":{"cipher":"aes-128-ctr","ciphertext":"16c725aff83d80ec865546cf647e9c19fd0a203d5095c6b9fbf4bfd4e80defc1","cipherparams":{"iv":"2b683eed143011528c8e4b6ccccdf2a9"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4895b93d546ac49b1dbb5bbde462b0a7ee6edca1c7548ddcc3abbc332a1c9ea6"},"mac":"88d98792f510c7db3f3339352c3a368634bf67bfcfc7c724d4f855a09daac9b4"},"id":"baa6c875-74cf-4c02-ace7-c8c6aa32765f","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-38-41.707647638Z--a33a6c312d9ad0e0f2e95541beed0cc081621fd0: -------------------------------------------------------------------------------- 1 | {"address":"a33a6c312d9ad0e0f2e95541beed0cc081621fd0","crypto":{"cipher":"aes-128-ctr","ciphertext":"1178ff5250ac5d55af191d0d01d19ee66ec650004bca969b3518571073435197","cipherparams":{"iv":"a785a27c1ea95e8bcdf13c4d4fb130a5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"87ab24da9fc7f4420fba7d57eb684f07cd2b18882360dc9d8cdde5b8312a551c"},"mac":"aeef485337dc9062f94249a83e61ec0a96c811efddf5d61ec69f74f920e265b0"},"id":"7ab0d4a9-c6b5-426f-9445-1604929628c2","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-39-04.584040707Z--a44a08d3f6933c69212114bb66e2df1813651844: -------------------------------------------------------------------------------- 1 | {"address":"a44a08d3f6933c69212114bb66e2df1813651844","crypto":{"cipher":"aes-128-ctr","ciphertext":"b8a8948cf419bde53ff3de97af0907fb10d5de7f945bf29ae08fd22b62b69c7a","cipherparams":{"iv":"9e6cb5ec4c4a27a3650e2b6521f49ca4"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"500e2951d0c3dc0d8499ced102b96e101e70228a13fd950f2900534feffc04b6"},"mac":"c2e95a9e9fbe604f0e779bc776070dfc65527e20d473aef83a9d1078b8dc840a"},"id":"356880ad-1c9d-4bd1-92ce-48de1a668c8f","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-42-01.013666678Z--a55a151eb00fded1634d27d1127b4be4627079ea: -------------------------------------------------------------------------------- 1 | {"address":"a55a151eb00fded1634d27d1127b4be4627079ea","crypto":{"cipher":"aes-128-ctr","ciphertext":"9fc73c394da6d80a9154f3b85b62685d9798306008d6298fe838183e0ddbd37e","cipherparams":{"iv":"43de6ac066de8bc6a6890c764c3f3b59"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"ce145ac99bc65b15a1284a1daa4ec852334277cfd67063b9f656098837cb52f1"},"mac":"79e641b1e9b9d8e0ee19b63c54bbad9a17efcdcc26ae0aa46154cd2c37c52ed8"},"id":"47bfbb81-5b55-4cf4-9289-80e2fe0fbd9c","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-42-37.426191216Z--a66a85ede0cbe03694aa9d9de0bb19c99ff55bd9: -------------------------------------------------------------------------------- 1 | {"address":"a66a85ede0cbe03694aa9d9de0bb19c99ff55bd9","crypto":{"cipher":"aes-128-ctr","ciphertext":"eb554404102ed496717838cbfbd6130d374abf0729f4f4e3f1e4ca3f128dbf88","cipherparams":{"iv":"b6ba2e970b40c9f679d25e1568614e92"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"530bf5db5d6ea5e0b3a6ef6fbe20040584bba9820ed99c2f8ff7935b9f75e760"},"mac":"acd41fff725d190b7e2d722180f637c5c57c0b923642bf9dca3b601ae4c9e2a2"},"id":"396c5807-24e8-4a78-a76e-5dfcd0e2dfe4","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-42-58.899396047Z--a77a2b9d4b1c010a22a7c565dc418cef683dbcec: -------------------------------------------------------------------------------- 1 | {"address":"a77a2b9d4b1c010a22a7c565dc418cef683dbcec","crypto":{"cipher":"aes-128-ctr","ciphertext":"47a7bded5ad5be47b4926df495839157e106efeb07297e544324de67484a4634","cipherparams":{"iv":"f4c6ca1d4e20e4bc301c125f55215559"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c2fe20e92bfb4de1b3687884a5377563b39ce434e0825dbd6f50ad62ec60f77b"},"mac":"048deb6bb3fbf078b55f1242fcdfd6b7ca240b89df6789c88b3295b2da0743d4"},"id":"46a2e757-d4e9-40ba-aec4-6640d7b74347","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-47-09.150580614Z--a88a05d2b88283ce84c8325760b72a64591279a2: -------------------------------------------------------------------------------- 1 | {"address":"a88a05d2b88283ce84c8325760b72a64591279a2","crypto":{"cipher":"aes-128-ctr","ciphertext":"13eaaf2bc741112428b479e577ca6d31d94fe1dbf757ae4cf6489f82ea3d9d2e","cipherparams":{"iv":"00a52fd7e30c9b10e0d3a9be02e6efa5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"86edf79626c1eed5ea7570bfa3fb964a219ea19e74aeabec53631922dd283474"},"mac":"3fc1ea0270c373fbd7c505f97e6f6de7e7df54aa9f84b1746aad298664b78d59"},"id":"5083b6d1-3059-457f-bdae-6a413a222428","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-47-34.770098207Z--a99a0ae3354c06b1459fd441a32a3f71005d7da0: -------------------------------------------------------------------------------- 1 | {"address":"a99a0ae3354c06b1459fd441a32a3f71005d7da0","crypto":{"cipher":"aes-128-ctr","ciphertext":"44b634314e512dcb94d89df97489d763de2d40de303c9dbf05b73808cf5fc251","cipherparams":{"iv":"dba264a6e7ae77499d724beed1cd3e17"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d89197404b33e64bb02f1cbddf7d2236fbddac047681b0744eedd26a5a07a1b1"},"mac":"1b9c130c652bb02f47313a24efe15363a69761a81afb0eff6baec6dce3a4db6d"},"id":"47cc9299-fea0-46c6-88e8-452fb714ca4c","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-58-17.400859858Z--aaaa9de1e6c564446ebca0fd102d8bd92093c756: -------------------------------------------------------------------------------- 1 | {"address":"aaaa9de1e6c564446ebca0fd102d8bd92093c756","crypto":{"cipher":"aes-128-ctr","ciphertext":"2aa64a851aac3841c67f9108b8c69db08b458691ddfa6ee8c463eb3e4516a6f4","cipherparams":{"iv":"2f8362a6189ba7f73659fb9ea2fe186f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"30cd1ce601b8925b46fb4d64fdb1d04d97efc8ba38788b285940cf0189ae6ea7"},"mac":"b49a521a1281ecba5e70a5f121e4472c80e669a0ab27b417023ab89178f4bfc7"},"id":"9ae73070-0b07-472c-bfe5-e07cbe66f289","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-58-35.122799190Z--abba43e7594e3b76afb157989e93c6621497fd4b: -------------------------------------------------------------------------------- 1 | {"address":"abba43e7594e3b76afb157989e93c6621497fd4b","crypto":{"cipher":"aes-128-ctr","ciphertext":"18d4959c00b52dbeb67a78956e60667fb37c82f8b26f8991a2b53386fd5819d8","cipherparams":{"iv":"3dfd2e5245fe28ad8572ddad27e869d9"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"518e58f54d75f20171611129dc7d04a94e5f56324964904d2c393409de8284ee"},"mac":"149669967b7f3cf6cacb395e57e0b8d48e11fa58f557c044cf9aea6c82a9a60f"},"id":"be47d6a6-f747-4741-bdfd-33182d2f674d","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-58-51.178332229Z--acca534c9f62ab495bd986e002ddf0f054caae4f: -------------------------------------------------------------------------------- 1 | {"address":"acca534c9f62ab495bd986e002ddf0f054caae4f","crypto":{"cipher":"aes-128-ctr","ciphertext":"b240ff5186c18714ff791124bd15a755adc371b7967e619e892f4af96e7bdba1","cipherparams":{"iv":"52da035b0fec4ca386cbd03b03ccad0d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"6de8376d2b00d63704cc6fceda260f276c878c28dfde8d7bd090f22238aa7304"},"mac":"f4b1409d40bdb0dc593f8d0303abd59ca4739185aa0cce2b09645de37c7b86a2"},"id":"3cae58b4-bdd3-43ee-929c-b1eff57e10ad","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-59-14.034178720Z--adda9b762a00ff12711113bfdc36958b73d7f915: -------------------------------------------------------------------------------- 1 | {"address":"adda9b762a00ff12711113bfdc36958b73d7f915","crypto":{"cipher":"aes-128-ctr","ciphertext":"477bfaef18a1c182af1c6bd05579f1aed334b218bb52d2349dec39b8894af897","cipherparams":{"iv":"78e45d798cacecff6c6899c283b57b0d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f76550251e7b02fbe099f9996681b17dfe0d318559080778308be7983b7d43cf"},"mac":"dcb9c3e48e4ce95fc3eac9a75661fb3071c01a1c23ee70eb273b24f30ee2810b"},"id":"b6b3a3ab-35fe-48aa-a74a-681a08cfea36","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-59-33.471449656Z--aeea63b5479b50f79583ec49dacdcf86ddeff392: -------------------------------------------------------------------------------- 1 | {"address":"aeea63b5479b50f79583ec49dacdcf86ddeff392","crypto":{"cipher":"aes-128-ctr","ciphertext":"89ffb39f14534c36902a6c2b4b8cb22074b0a0910d81fb794f1c2b3811719991","cipherparams":{"iv":"1e3ac8bc6fa20515b8d62fee8053b777"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"183ceaf9806607298919b141012c589ba5b4a8c98015505e3b008fa5b3b09e68"},"mac":"5f08180c70ab20ec9c6d69ca933f025987ceb095c5f3997a40256843ed44d3bf"},"id":"b6dcecff-9bbb-40c5-bcea-501e3d610c7b","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-05-20T02-59-51.697690594Z--affa4d3a80add8ce4018540e056dacb649589394: -------------------------------------------------------------------------------- 1 | {"address":"affa4d3a80add8ce4018540e056dacb649589394","crypto":{"cipher":"aes-128-ctr","ciphertext":"72b01c60d2a2fc2c3665375ef270733913cb6abbbc9a678888e6341e54dd1f72","cipherparams":{"iv":"4ef77b53a633b0300208001698ec8b40"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a214816a6067f9d4b4835ccec8d9a685b14133861d14c32d2f9fd08ac9c59ec7"},"mac":"aeffd474ce26ee99e49938e0360e7e94728c5bfc2ec498baab7eae45b923ca1f"},"id":"8af80842-e108-4256-b33f-9fa5fb5f6de5","version":3} -------------------------------------------------------------------------------- /test/testchain/keystore/UTC--2017-06-19T11-23-24.451455213Z--bbbb34fa53a801b5f298744490a1596438bbbe50: -------------------------------------------------------------------------------- 1 | {"address":"bbbb34fa53a801b5f298744490a1596438bbbe50","crypto":{"cipher":"aes-128-ctr","ciphertext":"e0c451ce3787c50d965e82682d48d0c0515ef65809dfae49cf970a3a19995248","cipherparams":{"iv":"2e3945b92834f1e83a5038a4b7453b19"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"402f5bbe8ae477647c75abb80a7c9cb2d073be19e89442723847c3b959c96f7e"},"mac":"f294136b60656f7ec37151b54f4c78328c05e149b65d855e32d96feaf8c46718"},"id":"30c3bd74-9469-4075-8319-cb4aaee801ab","version":3} -------------------------------------------------------------------------------- /test/settings.txt: -------------------------------------------------------------------------------- 1 | IPCFILE=ipc:./testchain/geth.ipc 2 | PASSWORD=testtest 3 | 4 | ERC20INTERFACESOL=../contracts/ERC20Interface.sol 5 | ERC20INTERFACETEMPSOL=ERC20Interface.sol 6 | OWNEDSOL=../contracts/Owned.sol 7 | OWNEDTEMPSOL=Owned.sol 8 | SAFEMATHSOL=../contracts/SafeMath.sol 9 | SAFEMATHTEMPSOL=SafeMath.sol 10 | LOCKEDTOKENSSOL=../contracts/LockedTokens.sol 11 | LOCKEDTOKENSTEMPSOL=LockedTokens.sol 12 | TOKENCONFIGSOL=../contracts/OpenANXTokenConfig.sol 13 | TOKENCONFIGTEMPSOL=OpenANXTokenConfig.sol 14 | TOKENSOL=../contracts/OpenANXToken.sol 15 | TOKENTEMPSOL=OpenANXToken.sol 16 | TOKENJS=OpenANXToken.js 17 | 18 | DEPLOYMENTDATA=deploymentData.txt 19 | 20 | INCLUDEJS=./include.js 21 | TEST1OUTPUT=test1output.txt 22 | TEST1RESULTS=test1results.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 BokkyPooBah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/generateSingleSol.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # ---------------------------------------------------------------------------------------------- 3 | # Combining the individual .sol files into a single .sol 4 | # 5 | # Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2017. The MIT Licence. 6 | # ---------------------------------------------------------------------------------------------- 7 | 8 | my $CONTRACTSDIR="../contracts"; 9 | my $MAINSOL="OpenANXToken.sol"; 10 | 11 | open (MAIN, "<$CONTRACTSDIR/$MAINSOL") 12 | or die "Cannot open $CONTRACTSDIR/$MAINSOL. Stopped"; 13 | 14 | while (my $line =
) { 15 | chomp $line; 16 | if ($line =~ /^import/) { 17 | my $importfile = $line; 18 | $importfile =~ s/^import \"\.\///; 19 | $importfile =~ s/\";//; 20 | # print $importfile; 21 | open (INCLUDE, "<$CONTRACTSDIR/$importfile") 22 | or die "Cannot open $CONTRACTSDIR/$importfile. Stopped"; 23 | while (my $line1 = ) { 24 | chomp $line1; 25 | if ($line1 =~ /^import/) { 26 | } elsif ($line1 =~ /^pragma/) { 27 | } else { 28 | print $line1 . "\n"; 29 | } 30 | } 31 | close (INCLUDE) 32 | or die "Cannot close $CONTRACTSDIR/$importfile. Stopped"; 33 | print $line1 . "\n"; 34 | } else { 35 | print $line . "\n"; 36 | } 37 | } 38 | 39 | close (MAIN) 40 | or die "Cannot close $CONTRACTSDIR/$MAINSOL. Stopped"; 41 | -------------------------------------------------------------------------------- /test/ERC20Interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - ERC20 Token Interface 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | 13 | // ---------------------------------------------------------------------------- 14 | // ERC Token Standard #20 Interface 15 | // https://github.com/ethereum/EIPs/issues/20 16 | // ---------------------------------------------------------------------------- 17 | contract ERC20Interface { 18 | uint public totalSupply; 19 | function balanceOf(address _owner) constant returns (uint balance); 20 | function transfer(address _to, uint _value) returns (bool success); 21 | function transferFrom(address _from, address _to, uint _value) 22 | returns (bool success); 23 | function approve(address _spender, uint _value) returns (bool success); 24 | function allowance(address _owner, address _spender) constant 25 | returns (uint remaining); 26 | event Transfer(address indexed _from, address indexed _to, uint _value); 27 | event Approval(address indexed _owner, address indexed _spender, 28 | uint _value); 29 | } -------------------------------------------------------------------------------- /contracts/ERC20Interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - ERC20 Token Interface 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | 13 | // ---------------------------------------------------------------------------- 14 | // ERC Token Standard #20 Interface 15 | // https://github.com/ethereum/EIPs/issues/20 16 | // ---------------------------------------------------------------------------- 17 | contract ERC20Interface { 18 | uint public totalSupply; 19 | function balanceOf(address _owner) constant returns (uint balance); 20 | function transfer(address _to, uint _value) returns (bool success); 21 | function transferFrom(address _from, address _to, uint _value) 22 | returns (bool success); 23 | function approve(address _spender, uint _value) returns (bool success); 24 | function allowance(address _owner, address _spender) constant 25 | returns (uint remaining); 26 | event Transfer(address indexed _from, address indexed _to, uint _value); 27 | event Approval(address indexed _owner, address indexed _spender, 28 | uint _value); 29 | } -------------------------------------------------------------------------------- /test/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | 13 | // ---------------------------------------------------------------------------- 14 | // Safe maths, borrowed from OpenZeppelin 15 | // ---------------------------------------------------------------------------- 16 | library SafeMath { 17 | 18 | // ------------------------------------------------------------------------ 19 | // Add a number to another number, checking for overflows 20 | // ------------------------------------------------------------------------ 21 | function add(uint a, uint b) internal returns (uint) { 22 | uint c = a + b; 23 | assert(c >= a && c >= b); 24 | return c; 25 | } 26 | 27 | // ------------------------------------------------------------------------ 28 | // Subtract a number from another number, checking for underflows 29 | // ------------------------------------------------------------------------ 30 | function sub(uint a, uint b) internal returns (uint) { 31 | assert(b <= a); 32 | return a - b; 33 | } 34 | } -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | 13 | // ---------------------------------------------------------------------------- 14 | // Safe maths, borrowed from OpenZeppelin 15 | // ---------------------------------------------------------------------------- 16 | library SafeMath { 17 | 18 | // ------------------------------------------------------------------------ 19 | // Add a number to another number, checking for overflows 20 | // ------------------------------------------------------------------------ 21 | function add(uint a, uint b) internal returns (uint) { 22 | uint c = a + b; 23 | assert(c >= a && c >= b); 24 | return c; 25 | } 26 | 27 | // ------------------------------------------------------------------------ 28 | // Subtract a number from another number, checking for underflows 29 | // ------------------------------------------------------------------------ 30 | function sub(uint a, uint b) internal returns (uint) { 31 | assert(b <= a); 32 | return a - b; 33 | } 34 | } -------------------------------------------------------------------------------- /scripts/Main_20170625_015900.txt: -------------------------------------------------------------------------------- 1 | token.address=0x701c244b988a513c945973defa05de933b23fe1d 2 | token.lockedTokensAddress=0x3866259bc60e5b69d5c438db238d3b4c9db37bcb 3 | currentTime=1498319940.794 Sat, 24 Jun 2017 15:59:00 UTC / Sat, 24 Jun 2017 15:59:00 GMT 4 | token.START_DATE=1498136400 Thu, 22 Jun 2017 13:00:00 UTC / Thu, 22 Jun 2017 13:00:00 GMT 5 | token.END_DATE=1500728400 Sat, 22 Jul 2017 13:00:00 UTC / Sat, 22 Jul 2017 13:00:00 GMT 6 | token.symbol=OAX 7 | token.name=openANX Token 8 | token.decimals=18 9 | token.DECIMALSFACTOR=1000000000000000000 10 | token.TOKENS_SOFT_CAP=13000000 11 | token.TOKENS_HARD_CAP=30000000 12 | token.TOKENS_TOTAL=100000000 13 | token.finalised=false 14 | token.tokensPerKEther=478680 15 | token.totalSupply=21891419.576925276757073699 16 | token.totalSupplyLocked(1Y/2Y)=0 / 0 17 | token.totalSupplyLocked=0 18 | token.totalSupplyUnlocked=0 19 | token.LOCKED_1Y_DATE=1529672400 Fri, 22 Jun 2018 13:00:00 UTC / Fri, 22 Jun 2018 13:00:00 GMT 20 | token.LOCKED_2Y_DATE=1561208400 Sat, 22 Jun 2019 13:00:00 UTC / Sat, 22 Jun 2019 13:00:00 GMT 21 | lockedToken.TOKENS_LOCKED_1Y_TOTAL=14000000 22 | lockedToken.TOKENS_LOCKED_2Y_TOTAL=26000000 23 | lockedToken.totalSupplyLocked1Y=14000000 24 | lockedToken.totalSupplyLocked2Y=26000000 25 | lockedToken.totalSupplyLocked=40000000 26 | token.owner=0x030dd623523019f1608d0a9b9b1277a71f8c52cd 27 | token.newOwner=0x030dd623523019f1608d0a9b9b1277a71f8c52cd 28 | OwnershipTransferred Event 0: from=0xa0c81e7bca6b95b6cb1d6d4e5db744228ed6f5c1 to=0x030dd623523019f1608d0a9b9b1277a71f8c52cd 3912183 29 | TokensPerKEtherUpdated Event 0: tokensPerKEther=478680 block=3910899 30 | -------------------------------------------------------------------------------- /scripts/Main_20170704_150051-final.txt: -------------------------------------------------------------------------------- 1 | token.address=0x701c244b988a513c945973defa05de933b23fe1d 2 | token.lockedTokensAddress=0x3866259bc60e5b69d5c438db238d3b4c9db37bcb 3 | currentTime=1499144452.075 Tue, 04 Jul 2017 05:00:52 UTC / Tue, 04 Jul 2017 05:00:52 GMT 4 | token.START_DATE=1498136400 Thu, 22 Jun 2017 13:00:00 UTC / Thu, 22 Jun 2017 13:00:00 GMT 5 | token.END_DATE=1500728400 Sat, 22 Jul 2017 13:00:00 UTC / Sat, 22 Jul 2017 13:00:00 GMT 6 | token.symbol=OAX 7 | token.name=openANX Token 8 | token.decimals=18 9 | token.DECIMALSFACTOR=1000000000000000000 10 | token.TOKENS_SOFT_CAP=13000000 11 | token.TOKENS_HARD_CAP=30000000 12 | token.TOKENS_TOTAL=100000000 13 | token.finalised=false 14 | token.tokensPerKEther=478680 15 | token.totalSupply=25009091.612443181137264487 16 | token.totalSupplyLocked(1Y/2Y)=0 / 0 17 | token.totalSupplyLocked=0 18 | token.totalSupplyUnlocked=0 19 | token.LOCKED_1Y_DATE=1529672400 Fri, 22 Jun 2018 13:00:00 UTC / Fri, 22 Jun 2018 13:00:00 GMT 20 | token.LOCKED_2Y_DATE=1561208400 Sat, 22 Jun 2019 13:00:00 UTC / Sat, 22 Jun 2019 13:00:00 GMT 21 | lockedToken.TOKENS_LOCKED_1Y_TOTAL=14000000 22 | lockedToken.TOKENS_LOCKED_2Y_TOTAL=26000000 23 | lockedToken.totalSupplyLocked1Y=14000000 24 | lockedToken.totalSupplyLocked2Y=26000000 25 | lockedToken.totalSupplyLocked=40000000 26 | token.owner=0x030dd623523019f1608d0a9b9b1277a71f8c52cd 27 | token.newOwner=0x030dd623523019f1608d0a9b9b1277a71f8c52cd 28 | OwnershipTransferred Event 0: from=0xa0c81e7bca6b95b6cb1d6d4e5db744228ed6f5c1 to=0x030dd623523019f1608d0a9b9b1277a71f8c52cd 3912183 29 | TokensPerKEtherUpdated Event 0: tokensPerKEther=478680 block=3910899 30 | -------------------------------------------------------------------------------- /test/Owned.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - Owned contracts 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | // ---------------------------------------------------------------------------- 13 | // Owned contract 14 | // ---------------------------------------------------------------------------- 15 | contract Owned { 16 | 17 | // ------------------------------------------------------------------------ 18 | // Current owner, and proposed new owner 19 | // ------------------------------------------------------------------------ 20 | address public owner; 21 | address public newOwner; 22 | 23 | // ------------------------------------------------------------------------ 24 | // Constructor - assign creator as the owner 25 | // ------------------------------------------------------------------------ 26 | function Owned() { 27 | owner = msg.sender; 28 | } 29 | 30 | 31 | // ------------------------------------------------------------------------ 32 | // Modifier to mark that a function can only be executed by the owner 33 | // ------------------------------------------------------------------------ 34 | modifier onlyOwner { 35 | require(msg.sender == owner); 36 | _; 37 | } 38 | 39 | 40 | // ------------------------------------------------------------------------ 41 | // Owner can initiate transfer of contract to a new owner 42 | // ------------------------------------------------------------------------ 43 | function transferOwnership(address _newOwner) onlyOwner { 44 | newOwner = _newOwner; 45 | } 46 | 47 | 48 | // ------------------------------------------------------------------------ 49 | // New owner has to accept transfer of contract 50 | // ------------------------------------------------------------------------ 51 | function acceptOwnership() { 52 | require(msg.sender == newOwner); 53 | OwnershipTransferred(owner, newOwner); 54 | owner = newOwner; 55 | } 56 | event OwnershipTransferred(address indexed _from, address indexed _to); 57 | } -------------------------------------------------------------------------------- /contracts/Owned.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - Owned contracts 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | // ---------------------------------------------------------------------------- 13 | // Owned contract 14 | // ---------------------------------------------------------------------------- 15 | contract Owned { 16 | 17 | // ------------------------------------------------------------------------ 18 | // Current owner, and proposed new owner 19 | // ------------------------------------------------------------------------ 20 | address public owner; 21 | address public newOwner; 22 | 23 | // ------------------------------------------------------------------------ 24 | // Constructor - assign creator as the owner 25 | // ------------------------------------------------------------------------ 26 | function Owned() { 27 | owner = msg.sender; 28 | } 29 | 30 | 31 | // ------------------------------------------------------------------------ 32 | // Modifier to mark that a function can only be executed by the owner 33 | // ------------------------------------------------------------------------ 34 | modifier onlyOwner { 35 | require(msg.sender == owner); 36 | _; 37 | } 38 | 39 | 40 | // ------------------------------------------------------------------------ 41 | // Owner can initiate transfer of contract to a new owner 42 | // ------------------------------------------------------------------------ 43 | function transferOwnership(address _newOwner) onlyOwner { 44 | newOwner = _newOwner; 45 | } 46 | 47 | 48 | // ------------------------------------------------------------------------ 49 | // New owner has to accept transfer of contract 50 | // ------------------------------------------------------------------------ 51 | function acceptOwnership() { 52 | require(msg.sender == newOwner); 53 | OwnershipTransferred(owner, newOwner); 54 | owner = newOwner; 55 | } 56 | event OwnershipTransferred(address indexed _from, address indexed _to); 57 | } -------------------------------------------------------------------------------- /test/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "homesteadBlock": 1 4 | }, 5 | "nonce": "0", 6 | "difficulty": "0x400", 7 | "mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578", 8 | "coinbase": "0x0000000000000000000000000000000000000000", 9 | "timestamp": "0x00", 10 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 11 | "extraData": "0x", 12 | "gasLimit": "0x3B4A1B44", 13 | "alloc": { 14 | "0xa00af22d07c87d96eeeb0ed583f8f6ac7812827e": { 15 | "balance": "10000000000000000000000000" 16 | }, 17 | "0xa11aae29840fbb5c86e6fd4cf809eba183aef433": { 18 | "balance": "10000000000000000000000000" 19 | }, 20 | "0xa22ab8a9d641ce77e06d98b7d7065d324d3d6976": { 21 | "balance": "10000000000000000000000000" 22 | }, 23 | "0xa33a6c312d9ad0e0f2e95541beed0cc081621fd0": { 24 | "balance": "10000000000000000000000000" 25 | }, 26 | "0xa44a08d3f6933c69212114bb66e2df1813651844": { 27 | "balance": "10000000000000000000000000" 28 | }, 29 | "0xa55a151eb00fded1634d27d1127b4be4627079ea": { 30 | "balance": "10000000000000000000000000" 31 | }, 32 | "0xa66a85ede0cbe03694aa9d9de0bb19c99ff55bd9": { 33 | "balance": "10000000000000000000000000" 34 | }, 35 | "0xa77a2b9d4b1c010a22a7c565dc418cef683dbcec": { 36 | "balance": "10000000000000000000000000" 37 | }, 38 | "0xa88a05d2b88283ce84c8325760b72a64591279a2": { 39 | "balance": "10000000000000000000000000" 40 | }, 41 | "0xa99a0ae3354c06b1459fd441a32a3f71005d7da0": { 42 | "balance": "10000000000000000000000000" 43 | }, 44 | "0xaaaa9de1e6c564446ebca0fd102d8bd92093c756": { 45 | "balance": "10000000000000000000000000" 46 | }, 47 | "0xabba43e7594e3b76afb157989e93c6621497fd4b": { 48 | "balance": "10000000000000000000000000" 49 | }, 50 | "0xacca534c9f62ab495bd986e002ddf0f054caae4f": { 51 | "balance": "10000000000000000000000000" 52 | }, 53 | "0xadda9b762a00ff12711113bfdc36958b73d7f915": { 54 | "balance": "10000000000000000000000000" 55 | }, 56 | "0xaeea63b5479b50f79583ec49dacdcf86ddeff392": { 57 | "balance": "10000000000000000000000000" 58 | }, 59 | "0xaffa4d3a80add8ce4018540e056dacb649589394": { 60 | "balance": "10000000000000000000000000" 61 | }, 62 | "0xbbbb34fa53a801b5f298744490a1596438bbbe50": { 63 | "balance": "10000000000000000000000000" 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # OpenANX Token - Testing 2 | 3 |
4 | 5 |
6 | 7 | # Table of contents 8 | 9 | * [Requirements](#requirements) 10 | * [Executing The Tests](#executing-the-tests) 11 | * [The Tests](#the-tests) 12 | * [Notes](#notes) 13 | 14 |
15 | 16 |
17 | 18 | # Requirements 19 | 20 | * The tests works on OS/X. Should work in Linux. May work in Windows with Cygwin 21 | * Geth/v1.6.5-stable-cf87713d/darwin-amd64/go1.8.3 22 | * Solc 0.4.11+commit.68ef5810.Darwin.appleclang 23 | 24 |
25 | 26 |
27 | 28 | # Executing The Tests 29 | 30 | * Run `geth` in dev mode 31 | 32 | ./00_runGeth.sh 33 | 34 | * Run the test in [01_test1.sh](01_test1.sh) 35 | 36 | ./01_test1.sh 37 | 38 | * See [test1results.txt](test1results.txt) for the results and [test1output.txt](test1output.txt) for the full output. 39 | 40 |
41 | 42 |
43 | 44 | # The Tests 45 | 46 | * Test 1 Before The Crowdsale 47 | * Test 1.1 Deploy Token Contract 48 | * Test 1.2 Add Precommitments, Change The tokensPerKEther Rate From 343,734 To 1,000,000 And Change Wallet 49 | * Test 2 During The Crowdsale 50 | * Test 2.1 Buy tokens 51 | * Test 3 Cannot Move Tokens Without Finalising 52 | * `transfer(...)`, `approve(...)` and `transferFrom(...)` 53 | * Test 4 Finalising 54 | * Test 5 KYC Verify 55 | * Test 6 Move Tokens After Finalising 56 | * Test 7 Unlock Tokens 1 57 | * Test 7.1 Unlock 1Y Locked Token 58 | * Test 7.2 Unsuccessfully Unlock 2Y Locked Token 59 | * Test 8 Unlock Tokens 2 60 | * Test 8.1 Successfully Unlock 2Y Locked Token 61 | * Test 8.2 Successfully Unlock All Tokens including Tranche 1 remaining + Tranche 2 30M 62 | * Test 9 Burn Tokens 63 | 64 | ## Todo 65 | * Execute un-permissioned functions 66 | * Safe maths 67 | * Other edge cases 68 | 69 |
70 | 71 |
72 | 73 | # Notes 74 | 75 | * The tests were conducted using bash shell scripts running Geth/v1.6.5-stable-cf87713d/darwin-amd64/go1.8.3 JavaScript commands 76 | * The smart contracts were compiled using Solidity 0.4.11+commit.68ef5810.Darwin.appleclang 77 | * The test script can be found in [01_test1.sh](01_test1.sh) 78 | * The test results can be found in [test1results.txt](test1results.txt) with details in [test1output.txt](test1output.txt) 79 | * The test can be run on OS/X, should run on Linux and may run on Windows with Cygwin 80 | * The [genesis.json](genesis.json) allocates ethers to the test accounts, and specifies a high block gas limit to accommodate many transactions in the same block 81 | * The [00_runGeth.sh](00_runGeth.sh) scripts starts `geth` with the parameter `--targetgaslimit 994712388` to keep the high block gas limit 82 | * The reasons for using the test environment as listed above, instead of truffles/testrpc are: 83 | * The test are conducted using the actual blockchain client software as is running on Mainnet and not just a mock environment like testrpc 84 | * It is easy to change parameters like dates, addresses or blocknumbers using the Unix search/replace tools 85 | * There have been issues in the part with version incompatibility between testrpc and solidity, i.e., version mismatches 86 | * The intermediate and key results are all saved to later viewing -------------------------------------------------------------------------------- /contracts/OpenANXTokenConfig.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - Configuration 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | // ---------------------------------------------------------------------------- 13 | // openANX crowdsale token smart contract - configuration parameters 14 | // ---------------------------------------------------------------------------- 15 | contract OpenANXTokenConfig { 16 | 17 | // ------------------------------------------------------------------------ 18 | // Token symbol(), name() and decimals() 19 | // ------------------------------------------------------------------------ 20 | string public constant SYMBOL = "OAX"; 21 | string public constant NAME = "openANX Token"; 22 | uint8 public constant DECIMALS = 18; 23 | 24 | 25 | // ------------------------------------------------------------------------ 26 | // Decimal factor for multiplications from OAX unit to OAX natural unit 27 | // ------------------------------------------------------------------------ 28 | uint public constant DECIMALSFACTOR = 10**uint(DECIMALS); 29 | 30 | // ------------------------------------------------------------------------ 31 | // Tranche 1 soft cap and hard cap, and total tokens 32 | // ------------------------------------------------------------------------ 33 | uint public constant TOKENS_SOFT_CAP = 13000000 * DECIMALSFACTOR; 34 | uint public constant TOKENS_HARD_CAP = 30000000 * DECIMALSFACTOR; 35 | uint public constant TOKENS_TOTAL = 100000000 * DECIMALSFACTOR; 36 | 37 | // ------------------------------------------------------------------------ 38 | // Tranche 1 crowdsale start date and end date 39 | // Do not use the `now` function here 40 | // Start - Thursday, 22-Jun-17 13:00:00 UTC / 1pm GMT 22 June 2017 41 | // End - Saturday, 22-Jul-17 13:00:00 UTC / 1pm GMT 22 July 2017 42 | // ------------------------------------------------------------------------ 43 | uint public constant START_DATE = 1498136400; 44 | uint public constant END_DATE = 1500728400; 45 | 46 | // ------------------------------------------------------------------------ 47 | // 1 year and 2 year dates for locked tokens 48 | // Do not use the `now` function here 49 | // ------------------------------------------------------------------------ 50 | uint public constant LOCKED_1Y_DATE = START_DATE + 365 days; 51 | uint public constant LOCKED_2Y_DATE = START_DATE + 2 * 365 days; 52 | 53 | // ------------------------------------------------------------------------ 54 | // Individual transaction contribution min and max amounts 55 | // Set to 0 to switch off, or `x ether` 56 | // ------------------------------------------------------------------------ 57 | uint public CONTRIBUTIONS_MIN = 0 ether; 58 | uint public CONTRIBUTIONS_MAX = 0 ether; 59 | } -------------------------------------------------------------------------------- /test/OpenANXTokenConfig.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - Configuration 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | // ---------------------------------------------------------------------------- 13 | // openANX crowdsale token smart contract - configuration parameters 14 | // ---------------------------------------------------------------------------- 15 | contract OpenANXTokenConfig { 16 | 17 | // ------------------------------------------------------------------------ 18 | // Token symbol(), name() and decimals() 19 | // ------------------------------------------------------------------------ 20 | string public constant SYMBOL = "OAX"; 21 | string public constant NAME = "openANX Token"; 22 | uint8 public constant DECIMALS = 18; 23 | 24 | 25 | // ------------------------------------------------------------------------ 26 | // Decimal factor for multiplications from OAX unit to OAX natural unit 27 | // ------------------------------------------------------------------------ 28 | uint public constant DECIMALSFACTOR = 10**uint(DECIMALS); 29 | 30 | // ------------------------------------------------------------------------ 31 | // Tranche 1 soft cap and hard cap, and total tokens 32 | // ------------------------------------------------------------------------ 33 | uint public constant TOKENS_SOFT_CAP = 13000000 * DECIMALSFACTOR; 34 | uint public constant TOKENS_HARD_CAP = 30000000 * DECIMALSFACTOR; 35 | uint public constant TOKENS_TOTAL = 100000000 * DECIMALSFACTOR; 36 | 37 | // ------------------------------------------------------------------------ 38 | // Tranche 1 crowdsale start date and end date 39 | // Do not use the `now` function here 40 | // Start - Thursday, 22-Jun-17 13:00:00 UTC / 1pm GMT 22 June 2017 41 | // End - Saturday, 22-Jul-17 13:00:00 UTC / 1pm GMT 22 July 2017 42 | // ------------------------------------------------------------------------ 43 | uint public constant START_DATE = 1497946561; // Tue 20 Jun 2017 08:16:01 UTC 44 | uint public constant END_DATE = 1497946671; // Tue 20 Jun 2017 08:17:51 UTC 45 | 46 | // ------------------------------------------------------------------------ 47 | // 1 year and 2 year dates for locked tokens 48 | // Do not use the `now` function here 49 | // ------------------------------------------------------------------------ 50 | uint public constant LOCKED_1Y_DATE = START_DATE + 3 minutes; 51 | uint public constant LOCKED_2Y_DATE = START_DATE + 4 minutes; 52 | 53 | // ------------------------------------------------------------------------ 54 | // Individual transaction contribution min and max amounts 55 | // Set to 0 to switch off, or `x ether` 56 | // ------------------------------------------------------------------------ 57 | uint public CONTRIBUTIONS_MIN = 0 ether; 58 | uint public CONTRIBUTIONS_MAX = 0 ether; 59 | } -------------------------------------------------------------------------------- /scripts/getTokenBalances.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------------------------- 3 | # Extract Token Balances for openANX 4 | # 5 | # Based on https://github.com/BitySA/whetcwithdraw/tree/master/daobalance 6 | # 7 | # Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2017. The MIT Licence. 8 | # ---------------------------------------------------------------------------------------------- 9 | 10 | # geth attach rpc:http://192.168.4.120:8545 << EOF 11 | # geth attach << EOF 12 | geth attach << EOF > tokenBalances.txt 13 | 14 | var tokenAddress = "0x701C244b988a513c945973dEFA05de933b23Fe1D"; 15 | var tokenABI = [{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"totalSupply","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]; 16 | var token = web3.eth.contract(tokenABI).at(tokenAddress); 17 | var fromBlock = 3908947; 18 | var toBlock = 3971324; // Finalised in https://etherscan.io/tx/0xf7ba25c71bedc47d5237fd0e92cba266e627f32ca0de2946254359fa1dcedd0e 19 | // var toBlock = parseInt(fromBlock) + 10000; 20 | var block = eth.getBlock(toBlock); 21 | console.log("STATS: snapshot at block=" + block.number + " time=" + block.timestamp + " " + new Date(block.timestamp * 1000).toUTCString()); 22 | 23 | function getAccounts() { 24 | var accounts = {}; 25 | // Add accounts in precommitments 26 | accounts["0x3866259bc60e5b69d5c438db238d3b4c9db37bcb"] = 1; 27 | var transferEventsFilter = token.Transfer({}, {fromBlock: fromBlock, toBlock: toBlock}); 28 | var transferEvents = transferEventsFilter.get(); 29 | for (var i = 0; i < transferEvents.length; i++) { 30 | var transferEvent = transferEvents[i]; 31 | console.log(JSON.stringify(transferEvent)); 32 | accounts[transferEvent.args._from] = 1; 33 | accounts[transferEvent.args._to] = 1; 34 | } 35 | return Object.keys(accounts); 36 | } 37 | 38 | function getBalances(accounts) { 39 | var balances = []; 40 | var totalBalance = new BigNumber(0); 41 | for (var i = 0; i < accounts.length; i++) { 42 | var addressNum = new BigNumber(accounts[i].substring(2), 16); 43 | var amount = token.balanceOf(accounts[i], toBlock); 44 | if (amount.greaterThan(0)) { 45 | totalBalance = totalBalance.add(amount); 46 | // if (i%100 === 0) console.log("Processed: " + i); 47 | console.log("BALANCE: " + i + "\t" + accounts[i] + "\t" + amount.shift(-18) + "\t" + totalBalance.shift(-18)); 48 | } 49 | } 50 | console.log("STATS: Total balance=" + totalBalance.toString(10)); 51 | console.log("STATS: totalSupply=" + token.totalSupply().toString(10)); 52 | return balances; 53 | } 54 | 55 | var accounts = getAccounts(); 56 | // console.log(JSON.stringify(accounts)); 57 | console.log("STATS: number of accounts, some may have a zero balances=" + accounts.length); 58 | var balances = getBalances(accounts); 59 | console.log("STATS: number of accounts+balances, only with non-zero balances=" + balances.length); 60 | // console.log(JSON.stringify(balances, null, 2)); 61 | // console.log(JSON.stringify(balances)); 62 | 63 | EOF 64 | 65 | grep "BALANCE: " tokenBalances.txt | sed "s/BALANCE: //" > tokenBalancesByAccounts.tsv 66 | grep "STATS: " tokenBalances.txt | sed "s/STATS: //" > tokenStats.txt 67 | -------------------------------------------------------------------------------- /test/LockedTokens.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - locked tokens 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | import "./ERC20Interface.sol"; 13 | import "./SafeMath.sol"; 14 | import "./OpenANXTokenConfig.sol"; 15 | 16 | 17 | // ---------------------------------------------------------------------------- 18 | // Contract that holds the 1Y and 2Y locked token information 19 | // ---------------------------------------------------------------------------- 20 | contract LockedTokens is OpenANXTokenConfig { 21 | using SafeMath for uint; 22 | 23 | // ------------------------------------------------------------------------ 24 | // 1y and 2y locked totals, not including unsold tranche1 and all tranche2 25 | // tokens 26 | // ------------------------------------------------------------------------ 27 | uint public constant TOKENS_LOCKED_1Y_TOTAL = 14000000 * DECIMALSFACTOR; 28 | uint public constant TOKENS_LOCKED_2Y_TOTAL = 26000000 * DECIMALSFACTOR; 29 | 30 | // ------------------------------------------------------------------------ 31 | // Tokens locked for 1 year for sale 2 in the following account 32 | // ------------------------------------------------------------------------ 33 | address public TRANCHE2_ACCOUNT = 0xBbBB34FA53A801b5F298744490a1596438bbBe50; 34 | 35 | // ------------------------------------------------------------------------ 36 | // Current totalSupply of 1y and 2y locked tokens 37 | // ------------------------------------------------------------------------ 38 | uint public totalSupplyLocked1Y; 39 | uint public totalSupplyLocked2Y; 40 | 41 | // ------------------------------------------------------------------------ 42 | // Locked tokens mapping 43 | // ------------------------------------------------------------------------ 44 | mapping (address => uint) public balancesLocked1Y; 45 | mapping (address => uint) public balancesLocked2Y; 46 | 47 | // ------------------------------------------------------------------------ 48 | // Address of openANX crowdsale token contract 49 | // ------------------------------------------------------------------------ 50 | ERC20Interface public tokenContract; 51 | 52 | 53 | // ------------------------------------------------------------------------ 54 | // Constructor - called by crowdsale token contract 55 | // ------------------------------------------------------------------------ 56 | function LockedTokens(address _tokenContract) { 57 | tokenContract = ERC20Interface(_tokenContract); 58 | 59 | // --- 1y locked tokens --- 60 | // Advisors 61 | add1Y(0xaBBa43E7594E3B76afB157989e93c6621497FD4b, 2000000 * DECIMALSFACTOR); 62 | // Directors 63 | add1Y(0xacCa534c9f62Ab495bd986e002DdF0f054caAE4f, 2000000 * DECIMALSFACTOR); 64 | // Early backers 65 | add1Y(0xAddA9B762A00FF12711113bfDc36958B73d7F915, 2000000 * DECIMALSFACTOR); 66 | // Developers 67 | add1Y(0xaeEa63B5479B50F79583ec49DACdcf86DDEff392, 8000000 * DECIMALSFACTOR); 68 | // Confirm 1Y totals 69 | assert(totalSupplyLocked1Y == TOKENS_LOCKED_1Y_TOTAL); 70 | 71 | // --- 2y locked tokens --- 72 | // Foundation 73 | add2Y(0xAAAA9De1E6C564446EBCA0fd102D8Bd92093c756, 20000000 * DECIMALSFACTOR); 74 | // Advisors 75 | add2Y(0xaBBa43E7594E3B76afB157989e93c6621497FD4b, 2000000 * DECIMALSFACTOR); 76 | // Directors 77 | add2Y(0xacCa534c9f62Ab495bd986e002DdF0f054caAE4f, 2000000 * DECIMALSFACTOR); 78 | // Early backers 79 | add2Y(0xAddA9B762A00FF12711113bfDc36958B73d7F915, 2000000 * DECIMALSFACTOR); 80 | // Confirm 2Y totals 81 | assert(totalSupplyLocked2Y == TOKENS_LOCKED_2Y_TOTAL); 82 | } 83 | 84 | 85 | // ------------------------------------------------------------------------ 86 | // Add remaining tokens to locked 1y balances 87 | // ------------------------------------------------------------------------ 88 | function addRemainingTokens() { 89 | // Only the crowdsale contract can call this function 90 | require(msg.sender == address(tokenContract)); 91 | // Total tokens to be created 92 | uint remainingTokens = TOKENS_TOTAL; 93 | // Minus precommitments and public crowdsale tokens 94 | remainingTokens = remainingTokens.sub(tokenContract.totalSupply()); 95 | // Minus 1y locked tokens 96 | remainingTokens = remainingTokens.sub(totalSupplyLocked1Y); 97 | // Minus 2y locked tokens 98 | remainingTokens = remainingTokens.sub(totalSupplyLocked2Y); 99 | // Unsold tranche1 and tranche2 tokens to be locked for 1y 100 | add1Y(TRANCHE2_ACCOUNT, remainingTokens); 101 | } 102 | 103 | 104 | // ------------------------------------------------------------------------ 105 | // Add to 1y locked balances and totalSupply 106 | // ------------------------------------------------------------------------ 107 | function add1Y(address account, uint value) private { 108 | balancesLocked1Y[account] = balancesLocked1Y[account].add(value); 109 | totalSupplyLocked1Y = totalSupplyLocked1Y.add(value); 110 | } 111 | 112 | 113 | // ------------------------------------------------------------------------ 114 | // Add to 2y locked balances and totalSupply 115 | // ------------------------------------------------------------------------ 116 | function add2Y(address account, uint value) private { 117 | balancesLocked2Y[account] = balancesLocked2Y[account].add(value); 118 | totalSupplyLocked2Y = totalSupplyLocked2Y.add(value); 119 | } 120 | 121 | 122 | // ------------------------------------------------------------------------ 123 | // 1y locked balances for an account 124 | // ------------------------------------------------------------------------ 125 | function balanceOfLocked1Y(address account) constant returns (uint balance) { 126 | return balancesLocked1Y[account]; 127 | } 128 | 129 | 130 | // ------------------------------------------------------------------------ 131 | // 2y locked balances for an account 132 | // ------------------------------------------------------------------------ 133 | function balanceOfLocked2Y(address account) constant returns (uint balance) { 134 | return balancesLocked2Y[account]; 135 | } 136 | 137 | 138 | // ------------------------------------------------------------------------ 139 | // 1y and 2y locked balances for an account 140 | // ------------------------------------------------------------------------ 141 | function balanceOfLocked(address account) constant returns (uint balance) { 142 | return balancesLocked1Y[account].add(balancesLocked2Y[account]); 143 | } 144 | 145 | 146 | // ------------------------------------------------------------------------ 147 | // 1y and 2y locked total supply 148 | // ------------------------------------------------------------------------ 149 | function totalSupplyLocked() constant returns (uint) { 150 | return totalSupplyLocked1Y + totalSupplyLocked2Y; 151 | } 152 | 153 | 154 | // ------------------------------------------------------------------------ 155 | // An account can unlock their 1y locked tokens 1y after token launch date 156 | // ------------------------------------------------------------------------ 157 | function unlock1Y() { 158 | require(now >= LOCKED_1Y_DATE); 159 | uint amount = balancesLocked1Y[msg.sender]; 160 | require(amount > 0); 161 | balancesLocked1Y[msg.sender] = 0; 162 | totalSupplyLocked1Y = totalSupplyLocked1Y.sub(amount); 163 | if (!tokenContract.transfer(msg.sender, amount)) throw; 164 | } 165 | 166 | 167 | // ------------------------------------------------------------------------ 168 | // An account can unlock their 2y locked tokens 2y after token launch date 169 | // ------------------------------------------------------------------------ 170 | function unlock2Y() { 171 | require(now >= LOCKED_2Y_DATE); 172 | uint amount = balancesLocked2Y[msg.sender]; 173 | require(amount > 0); 174 | balancesLocked2Y[msg.sender] = 0; 175 | totalSupplyLocked2Y = totalSupplyLocked2Y.sub(amount); 176 | if (!tokenContract.transfer(msg.sender, amount)) throw; 177 | } 178 | } -------------------------------------------------------------------------------- /contracts/LockedTokens.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - locked tokens 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | import "./ERC20Interface.sol"; 13 | import "./SafeMath.sol"; 14 | import "./OpenANXTokenConfig.sol"; 15 | 16 | 17 | // ---------------------------------------------------------------------------- 18 | // Contract that holds the 1Y and 2Y locked token information 19 | // ---------------------------------------------------------------------------- 20 | contract LockedTokens is OpenANXTokenConfig { 21 | using SafeMath for uint; 22 | 23 | // ------------------------------------------------------------------------ 24 | // 1y and 2y locked totals, not including unsold tranche1 and all tranche2 25 | // tokens 26 | // ------------------------------------------------------------------------ 27 | uint public constant TOKENS_LOCKED_1Y_TOTAL = 14000000 * DECIMALSFACTOR; 28 | uint public constant TOKENS_LOCKED_2Y_TOTAL = 26000000 * DECIMALSFACTOR; 29 | 30 | // ------------------------------------------------------------------------ 31 | // Tokens locked for 1 year for sale 2 in the following account 32 | // ------------------------------------------------------------------------ 33 | address public TRANCHE2_ACCOUNT = 0xBbBB34FA53A801b5F298744490a1596438bbBe50; 34 | 35 | // ------------------------------------------------------------------------ 36 | // Current totalSupply of 1y and 2y locked tokens 37 | // ------------------------------------------------------------------------ 38 | uint public totalSupplyLocked1Y; 39 | uint public totalSupplyLocked2Y; 40 | 41 | // ------------------------------------------------------------------------ 42 | // Locked tokens mapping 43 | // ------------------------------------------------------------------------ 44 | mapping (address => uint) public balancesLocked1Y; 45 | mapping (address => uint) public balancesLocked2Y; 46 | 47 | // ------------------------------------------------------------------------ 48 | // Address of openANX crowdsale token contract 49 | // ------------------------------------------------------------------------ 50 | ERC20Interface public tokenContract; 51 | 52 | 53 | // ------------------------------------------------------------------------ 54 | // Constructor - called by crowdsale token contract 55 | // ------------------------------------------------------------------------ 56 | function LockedTokens(address _tokenContract) { 57 | tokenContract = ERC20Interface(_tokenContract); 58 | 59 | // --- 1y locked tokens --- 60 | // Advisors 61 | add1Y(0xaBBa43E7594E3B76afB157989e93c6621497FD4b, 2000000 * DECIMALSFACTOR); 62 | // Directors 63 | add1Y(0xacCa534c9f62Ab495bd986e002DdF0f054caAE4f, 2000000 * DECIMALSFACTOR); 64 | // Early backers 65 | add1Y(0xAddA9B762A00FF12711113bfDc36958B73d7F915, 2000000 * DECIMALSFACTOR); 66 | // Developers 67 | add1Y(0xaeEa63B5479B50F79583ec49DACdcf86DDEff392, 8000000 * DECIMALSFACTOR); 68 | // Confirm 1Y totals 69 | assert(totalSupplyLocked1Y == TOKENS_LOCKED_1Y_TOTAL); 70 | 71 | // --- 2y locked tokens --- 72 | // Foundation 73 | add2Y(0xAAAA9De1E6C564446EBCA0fd102D8Bd92093c756, 20000000 * DECIMALSFACTOR); 74 | // Advisors 75 | add2Y(0xaBBa43E7594E3B76afB157989e93c6621497FD4b, 2000000 * DECIMALSFACTOR); 76 | // Directors 77 | add2Y(0xacCa534c9f62Ab495bd986e002DdF0f054caAE4f, 2000000 * DECIMALSFACTOR); 78 | // Early backers 79 | add2Y(0xAddA9B762A00FF12711113bfDc36958B73d7F915, 2000000 * DECIMALSFACTOR); 80 | // Confirm 2Y totals 81 | assert(totalSupplyLocked2Y == TOKENS_LOCKED_2Y_TOTAL); 82 | } 83 | 84 | 85 | // ------------------------------------------------------------------------ 86 | // Add remaining tokens to locked 1y balances 87 | // ------------------------------------------------------------------------ 88 | function addRemainingTokens() { 89 | // Only the crowdsale contract can call this function 90 | require(msg.sender == address(tokenContract)); 91 | // Total tokens to be created 92 | uint remainingTokens = TOKENS_TOTAL; 93 | // Minus precommitments and public crowdsale tokens 94 | remainingTokens = remainingTokens.sub(tokenContract.totalSupply()); 95 | // Minus 1y locked tokens 96 | remainingTokens = remainingTokens.sub(totalSupplyLocked1Y); 97 | // Minus 2y locked tokens 98 | remainingTokens = remainingTokens.sub(totalSupplyLocked2Y); 99 | // Unsold tranche1 and tranche2 tokens to be locked for 1y 100 | add1Y(TRANCHE2_ACCOUNT, remainingTokens); 101 | } 102 | 103 | 104 | // ------------------------------------------------------------------------ 105 | // Add to 1y locked balances and totalSupply 106 | // ------------------------------------------------------------------------ 107 | function add1Y(address account, uint value) private { 108 | balancesLocked1Y[account] = balancesLocked1Y[account].add(value); 109 | totalSupplyLocked1Y = totalSupplyLocked1Y.add(value); 110 | } 111 | 112 | 113 | // ------------------------------------------------------------------------ 114 | // Add to 2y locked balances and totalSupply 115 | // ------------------------------------------------------------------------ 116 | function add2Y(address account, uint value) private { 117 | balancesLocked2Y[account] = balancesLocked2Y[account].add(value); 118 | totalSupplyLocked2Y = totalSupplyLocked2Y.add(value); 119 | } 120 | 121 | 122 | // ------------------------------------------------------------------------ 123 | // 1y locked balances for an account 124 | // ------------------------------------------------------------------------ 125 | function balanceOfLocked1Y(address account) constant returns (uint balance) { 126 | return balancesLocked1Y[account]; 127 | } 128 | 129 | 130 | // ------------------------------------------------------------------------ 131 | // 2y locked balances for an account 132 | // ------------------------------------------------------------------------ 133 | function balanceOfLocked2Y(address account) constant returns (uint balance) { 134 | return balancesLocked2Y[account]; 135 | } 136 | 137 | 138 | // ------------------------------------------------------------------------ 139 | // 1y and 2y locked balances for an account 140 | // ------------------------------------------------------------------------ 141 | function balanceOfLocked(address account) constant returns (uint balance) { 142 | return balancesLocked1Y[account].add(balancesLocked2Y[account]); 143 | } 144 | 145 | 146 | // ------------------------------------------------------------------------ 147 | // 1y and 2y locked total supply 148 | // ------------------------------------------------------------------------ 149 | function totalSupplyLocked() constant returns (uint) { 150 | return totalSupplyLocked1Y + totalSupplyLocked2Y; 151 | } 152 | 153 | 154 | // ------------------------------------------------------------------------ 155 | // An account can unlock their 1y locked tokens 1y after token launch date 156 | // ------------------------------------------------------------------------ 157 | function unlock1Y() { 158 | require(now >= LOCKED_1Y_DATE); 159 | uint amount = balancesLocked1Y[msg.sender]; 160 | require(amount > 0); 161 | balancesLocked1Y[msg.sender] = 0; 162 | totalSupplyLocked1Y = totalSupplyLocked1Y.sub(amount); 163 | if (!tokenContract.transfer(msg.sender, amount)) throw; 164 | } 165 | 166 | 167 | // ------------------------------------------------------------------------ 168 | // An account can unlock their 2y locked tokens 2y after token launch date 169 | // ------------------------------------------------------------------------ 170 | function unlock2Y() { 171 | require(now >= LOCKED_2Y_DATE); 172 | uint amount = balancesLocked2Y[msg.sender]; 173 | require(amount > 0); 174 | balancesLocked2Y[msg.sender] = 0; 175 | totalSupplyLocked2Y = totalSupplyLocked2Y.sub(amount); 176 | if (!tokenContract.transfer(msg.sender, amount)) throw; 177 | } 178 | } -------------------------------------------------------------------------------- /test/LockedTokens.js: -------------------------------------------------------------------------------- 1 | var lockedTokensOutput={"contracts":{"ERC20Interface.sol:ERC20Interface":{"abi":"[{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]","bin":""},"LockedTokens.sol:LockedTokens":{"abi":"[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balancesLocked2Y\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOfLocked1Y\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"START_DATE\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"unlock1Y\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"tokenContract\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupplyLocked\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupplyLocked2Y\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balancesLocked1Y\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"DATE_1Y\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOfLocked2Y\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupplyLocked1Y\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"DATE_2Y\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"unlock2Y\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"inputs\":[{\"name\":\"_tokenContract\",\"type\":\"address\"}],\"payable\":false,\"type\":\"constructor\"}]","bin":"6060604052341561000c57fe5b60405160208061098a83398101604052515b60048054600160a060020a031916600160a060020a0383811691909117808355604080516000602091820181905282517f313ce567000000000000000000000000000000000000000000000000000000008152925190958695949094169363313ce567938082019392919082900301818787803b151561009a57fe5b6102c65a03f115156100a857fe5b50506040515160ff16600a0a92506100e8905073a88a05d2b88283ce84c8325760b72a64591279a2621e848084026401000000006105026103a582021704565b61011873a99a0ae3354c06b1459fd441a32a3f71005d7da0621e848084026401000000006105026103a582021704565b61014873aaaa9de1e6c564446ebca0fd102d8bd92093c756621e848084026401000000006105026103a582021704565b61017873abba43e7594e3b76afb157989e93c6621497fd4b627a120084026401000000006105026103a582021704565b60005462d59f8083021461018857fe5b6101b973a77a2b9d4b1c010a22a7c565dc418cef683dbcec6301312d00840264010000000061052b6103ce82021704565b6101e973a88a05d2b88283ce84c8325760b72a64591279a2621e8480840264010000000061052b6103ce82021704565b61021973a99a0ae3354c06b1459fd441a32a3f71005d7da0621e8480840264010000000061052b6103ce82021704565b61024973aaaa9de1e6c564446ebca0fd102d8bd92093c756621e8480840264010000000061052b6103ce82021704565b60015463018cba8083021461025a57fe5b600154600054600460009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401809050602060405180830381600087803b15156102d457fe5b6102c65a03f115156102e257fe5b5050506040518051905084600460009054906101000a9004600160a060020a0316600160a060020a0316630b7430216000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401809050602060405180830381600087803b151561036157fe5b6102c65a03f1151561036f57fe5b5050506040518051905002030303905061039c83826103a564010000000002610502176401000000009004565b5b5050506103f9565b600160a060020a03821660009081526002602052604081208054830190558054820190555b5050565b600160a060020a038216600090815260036020526040902080548201905560018054820190555b5050565b610582806104086000396000f300606060405236156100c25763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416631a6f56a081146100c45780631ab3c515146100f2578063372c65331461012057806338c808571461014257806355a373d6146101545780635834192214610180578063622c77fe146101a257806376e403ec146101c4578063b3da8f64146101f2578063bc5dc16814610214578063c0c3da9c14610242578063d87c65e814610264578063e5494be114610286575bfe5b34156100cc57fe5b6100e0600160a060020a0360043516610298565b60408051918252519081900360200190f35b34156100fa57fe5b6100e0600160a060020a03600435166102aa565b60408051918252519081900360200190f35b341561012857fe5b6100e06102c9565b60408051918252519081900360200190f35b341561014a57fe5b6101526102d1565b005b341561015c57fe5b6101646103b6565b60408051600160a060020a039092168252519081900360200190f35b341561018857fe5b6100e06103c5565b60408051918252519081900360200190f35b34156101aa57fe5b6100e06103d0565b60408051918252519081900360200190f35b34156101cc57fe5b6100e0600160a060020a03600435166103d6565b60408051918252519081900360200190f35b34156101fa57fe5b6100e06103e8565b60408051918252519081900360200190f35b341561021c57fe5b6100e0600160a060020a03600435166103f0565b60408051918252519081900360200190f35b341561024a57fe5b6100e061040f565b60408051918252519081900360200190f35b341561026c57fe5b6100e0610415565b60408051918252519081900360200190f35b341561028e57fe5b61015261041d565b005b60036020526000908152604090205481565b600160a060020a0381166000908152600260205260409020545b919050565b63594b088081565b6000635b2c3c004210156102e55760006000fd5b50600160a060020a03331660009081526002602052604090205480151561030c5760006000fd5b600160a060020a033381166000818152600260209081526040808320839055600480548251840185905282517fa9059cbb00000000000000000000000000000000000000000000000000000000815291820195909552602481018790529051939094169363a9059cbb9360448083019491928390030190829087803b151561039057fe5b6102c65a03f1151561039e57fe5b505060405151151590506103b25760006000fd5b5b50565b600454600160a060020a031681565b600154600054015b90565b60015481565b60026020526000908152604090205481565b635b2c3c0081565b600160a060020a0381166000908152600360205260409020545b919050565b60005481565b635d0d6f8081565b6000635d0d6f804210156104315760006000fd5b50600160a060020a0333166000908152600360205260409020548015156104585760006000fd5b600160a060020a033381166000818152600360209081526040808320839055600480548251840185905282517fa9059cbb00000000000000000000000000000000000000000000000000000000815291820195909552602481018790529051939094169363a9059cbb9360448083019491928390030190829087803b151561039057fe5b6102c65a03f1151561039e57fe5b505060405151151590506103b25760006000fd5b5b50565b600160a060020a03821660009081526002602052604081208054830190558054820190555b5050565b600160a060020a038216600090815260036020526040902080548201905560018054820190555b50505600a165627a7a72305820bb14ddfac544bfef1b7f41df0070c095c239698d5bd6492d80aedb75d10779bf0029"},"LockedTokens.sol:OpenANXInterface":{"abi":"[{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"TOKENS_TOTAL\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]","bin":""}},"version":"0.4.11+commit.68ef5810.Darwin.appleclang"}; 2 | -------------------------------------------------------------------------------- /contracts/LockedTokens-deployed.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract - locked tokens 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | import "./ERC20Interface.sol"; 13 | import "./SafeMath.sol"; 14 | import "./OpenANXTokenConfig.sol"; 15 | 16 | 17 | // ---------------------------------------------------------------------------- 18 | // Contract that holds the 1Y and 2Y locked token information 19 | // ---------------------------------------------------------------------------- 20 | contract LockedTokens is OpenANXTokenConfig { 21 | using SafeMath for uint; 22 | 23 | // ------------------------------------------------------------------------ 24 | // 1y and 2y locked totals, not including unsold tranche1 and all tranche2 25 | // tokens 26 | // ------------------------------------------------------------------------ 27 | uint public constant TOKENS_LOCKED_1Y_TOTAL = 14000000 * DECIMALSFACTOR; 28 | uint public constant TOKENS_LOCKED_2Y_TOTAL = 26000000 * DECIMALSFACTOR; 29 | 30 | // ------------------------------------------------------------------------ 31 | // Tokens locked for 1 year for sale 2 in the following account 32 | // ------------------------------------------------------------------------ 33 | address public TRANCHE2_ACCOUNT = 0x813703Eb676f3B6C76dA75cBa0cbC49DdbCA7B37; 34 | 35 | // ------------------------------------------------------------------------ 36 | // Current totalSupply of 1y and 2y locked tokens 37 | // ------------------------------------------------------------------------ 38 | uint public totalSupplyLocked1Y; 39 | uint public totalSupplyLocked2Y; 40 | 41 | // ------------------------------------------------------------------------ 42 | // Locked tokens mapping 43 | // ------------------------------------------------------------------------ 44 | mapping (address => uint) public balancesLocked1Y; 45 | mapping (address => uint) public balancesLocked2Y; 46 | 47 | // ------------------------------------------------------------------------ 48 | // Address of openANX crowdsale token contract 49 | // ------------------------------------------------------------------------ 50 | ERC20Interface public tokenContract; 51 | 52 | 53 | // ------------------------------------------------------------------------ 54 | // Constructor - called by crowdsale token contract 55 | // ------------------------------------------------------------------------ 56 | function LockedTokens(address _tokenContract) { 57 | tokenContract = ERC20Interface(_tokenContract); 58 | 59 | // --- 1y locked tokens --- 60 | 61 | // Confirm 1Y totals 62 | add1Y(0x4beE088efDBCC610EEEa101ded7204150AF1C8b9,1000000 * DECIMALSFACTOR); 63 | add1Y(0x839551201f866907Eb5017bE79cEB48aDa58650c,925000 * DECIMALSFACTOR); 64 | add1Y(0xa92d4Cd3412862386c234Be572Fe4A8FA4BB09c6,925000 * DECIMALSFACTOR); 65 | add1Y(0xECf2B5fce33007E5669D63de39a4c663e56958dD,925000 * DECIMALSFACTOR); 66 | add1Y(0xD6B7695bc74E2C950eb953316359Eab283C5Bda8,925000 * DECIMALSFACTOR); 67 | add1Y(0xBE3463Eae26398D55a7118683079264BcF3ab24B,150000 * DECIMALSFACTOR); 68 | add1Y(0xf47428Fb9A61c9f3312cB035AEE049FBa76ba62a,150000 * DECIMALSFACTOR); 69 | add1Y(0xfCcc77165D822Ef9004714d829bDC267C743658a,50000 * DECIMALSFACTOR); 70 | add1Y(0xaf8df2aCAec3d5d92dE42a6c19d7706A4F3E8D8b,50000 * DECIMALSFACTOR); 71 | add1Y(0x22a6f9693856374BF2922cd837d07F6670E7FA4d,250000 * DECIMALSFACTOR); 72 | add1Y(0x3F720Ca8FfF598F00a51DE32A8Cb58Ca73f22aDe,50000 * DECIMALSFACTOR); 73 | add1Y(0xBd0D1954B301E414F0b5D0827A69EC5dD559e50B,50000 * DECIMALSFACTOR); 74 | add1Y(0x2ad6B011FEcDE830c9cc4dc0d0b77F055D6b5990,50000 * DECIMALSFACTOR); 75 | add1Y(0x0c5cD0E971cA18a0F0E0d581f4B93FaD31D608B0,2000085 * DECIMALSFACTOR); 76 | add1Y(0xFaaDC4d80Eaf430Ab604337CB67d77eC763D3e23,200248 * DECIMALSFACTOR); 77 | add1Y(0xDAef46f89c264182Cd87Ce93B620B63c7AfB14f7,1616920 * DECIMALSFACTOR); 78 | add1Y(0x19cc59C30cE54706633dC29EdEbAE1efF1757b25,224980 * DECIMALSFACTOR); 79 | add1Y(0xa130fE5D399104CA5AF168fbbBBe19F95d739741,745918 * DECIMALSFACTOR); 80 | add1Y(0xC0cD1bf6F2939095a56B0DFa085Ba2886b84E7d1,745918 * DECIMALSFACTOR); 81 | add1Y(0xf2C26e79eD264B0E3e5A5DFb1Dd91EA61f512C6e,745918 * DECIMALSFACTOR); 82 | add1Y(0x5F876a8A5F1B66fbf3D0D119075b62aF4386e319,745918 * DECIMALSFACTOR); 83 | add1Y(0xb8E046570800Dd76720aF6d42d3cCae451F54f15,745920 * DECIMALSFACTOR); 84 | add1Y(0xA524fa65Aac4647fa7bA2c20D22F64450c351bBd,714286 * DECIMALSFACTOR); 85 | add1Y(0x27209b276C15a936BCE08D7D70f0c97aeb3CE8c3,13889 * DECIMALSFACTOR); 86 | 87 | assert(totalSupplyLocked1Y == TOKENS_LOCKED_1Y_TOTAL); 88 | 89 | // --- 2y locked tokens --- 90 | add2Y(0x4beE088efDBCC610EEEa101ded7204150AF1C8b9,1000000 * DECIMALSFACTOR); 91 | add2Y(0x839551201f866907Eb5017bE79cEB48aDa58650c,925000 * DECIMALSFACTOR); 92 | add2Y(0xa92d4Cd3412862386c234Be572Fe4A8FA4BB09c6,925000 * DECIMALSFACTOR); 93 | add2Y(0xECf2B5fce33007E5669D63de39a4c663e56958dD,925000 * DECIMALSFACTOR); 94 | add2Y(0xD6B7695bc74E2C950eb953316359Eab283C5Bda8,925000 * DECIMALSFACTOR); 95 | add2Y(0xBE3463Eae26398D55a7118683079264BcF3ab24B,150000 * DECIMALSFACTOR); 96 | add2Y(0xf47428Fb9A61c9f3312cB035AEE049FBa76ba62a,150000 * DECIMALSFACTOR); 97 | add2Y(0xfCcc77165D822Ef9004714d829bDC267C743658a,50000 * DECIMALSFACTOR); 98 | add2Y(0xDAef46f89c264182Cd87Ce93B620B63c7AfB14f7,500000 * DECIMALSFACTOR); 99 | add2Y(0xaf8df2aCAec3d5d92dE42a6c19d7706A4F3E8D8b,50000 * DECIMALSFACTOR); 100 | add2Y(0x22a6f9693856374BF2922cd837d07F6670E7FA4d,250000 * DECIMALSFACTOR); 101 | add2Y(0x3F720Ca8FfF598F00a51DE32A8Cb58Ca73f22aDe,50000 * DECIMALSFACTOR); 102 | add2Y(0xBd0D1954B301E414F0b5D0827A69EC5dD559e50B,50000 * DECIMALSFACTOR); 103 | add2Y(0x2ad6B011FEcDE830c9cc4dc0d0b77F055D6b5990,50000 * DECIMALSFACTOR); 104 | 105 | //treasury 106 | add2Y(0x990a2D172398007fcbd5078D84696BdD8cCDf7b2,20000000 * DECIMALSFACTOR); 107 | 108 | assert(totalSupplyLocked2Y == TOKENS_LOCKED_2Y_TOTAL); 109 | } 110 | 111 | 112 | // ------------------------------------------------------------------------ 113 | // Add remaining tokens to locked 1y balances 114 | // ------------------------------------------------------------------------ 115 | function addRemainingTokens() { 116 | // Only the crowdsale contract can call this function 117 | require(msg.sender == address(tokenContract)); 118 | // Total tokens to be created 119 | uint remainingTokens = TOKENS_TOTAL; 120 | // Minus precommitments and public crowdsale tokens 121 | remainingTokens = remainingTokens.sub(tokenContract.totalSupply()); 122 | // Minus 1y locked tokens 123 | remainingTokens = remainingTokens.sub(totalSupplyLocked1Y); 124 | // Minus 2y locked tokens 125 | remainingTokens = remainingTokens.sub(totalSupplyLocked2Y); 126 | // Unsold tranche1 and tranche2 tokens to be locked for 1y 127 | add1Y(TRANCHE2_ACCOUNT, remainingTokens); 128 | } 129 | 130 | 131 | // ------------------------------------------------------------------------ 132 | // Add to 1y locked balances and totalSupply 133 | // ------------------------------------------------------------------------ 134 | function add1Y(address account, uint value) private { 135 | balancesLocked1Y[account] = balancesLocked1Y[account].add(value); 136 | totalSupplyLocked1Y = totalSupplyLocked1Y.add(value); 137 | } 138 | 139 | 140 | // ------------------------------------------------------------------------ 141 | // Add to 2y locked balances and totalSupply 142 | // ------------------------------------------------------------------------ 143 | function add2Y(address account, uint value) private { 144 | balancesLocked2Y[account] = balancesLocked2Y[account].add(value); 145 | totalSupplyLocked2Y = totalSupplyLocked2Y.add(value); 146 | } 147 | 148 | 149 | // ------------------------------------------------------------------------ 150 | // 1y locked balances for an account 151 | // ------------------------------------------------------------------------ 152 | function balanceOfLocked1Y(address account) constant returns (uint balance) { 153 | return balancesLocked1Y[account]; 154 | } 155 | 156 | 157 | // ------------------------------------------------------------------------ 158 | // 2y locked balances for an account 159 | // ------------------------------------------------------------------------ 160 | function balanceOfLocked2Y(address account) constant returns (uint balance) { 161 | return balancesLocked2Y[account]; 162 | } 163 | 164 | 165 | // ------------------------------------------------------------------------ 166 | // 1y and 2y locked balances for an account 167 | // ------------------------------------------------------------------------ 168 | function balanceOfLocked(address account) constant returns (uint balance) { 169 | return balancesLocked1Y[account].add(balancesLocked2Y[account]); 170 | } 171 | 172 | 173 | // ------------------------------------------------------------------------ 174 | // 1y and 2y locked total supply 175 | // ------------------------------------------------------------------------ 176 | function totalSupplyLocked() constant returns (uint) { 177 | return totalSupplyLocked1Y + totalSupplyLocked2Y; 178 | } 179 | 180 | 181 | // ------------------------------------------------------------------------ 182 | // An account can unlock their 1y locked tokens 1y after token launch date 183 | // ------------------------------------------------------------------------ 184 | function unlock1Y() { 185 | require(now >= LOCKED_1Y_DATE); 186 | uint amount = balancesLocked1Y[msg.sender]; 187 | require(amount > 0); 188 | balancesLocked1Y[msg.sender] = 0; 189 | totalSupplyLocked1Y = totalSupplyLocked1Y.sub(amount); 190 | if (!tokenContract.transfer(msg.sender, amount)) throw; 191 | } 192 | 193 | 194 | // ------------------------------------------------------------------------ 195 | // An account can unlock their 2y locked tokens 2y after token launch date 196 | // ------------------------------------------------------------------------ 197 | function unlock2Y() { 198 | require(now >= LOCKED_2Y_DATE); 199 | uint amount = balancesLocked2Y[msg.sender]; 200 | require(amount > 0); 201 | balancesLocked2Y[msg.sender] = 0; 202 | totalSupplyLocked2Y = totalSupplyLocked2Y.sub(amount); 203 | if (!tokenContract.transfer(msg.sender, amount)) throw; 204 | } 205 | } -------------------------------------------------------------------------------- /test/deploymentData.txt: -------------------------------------------------------------------------------- 1 | tokenABI=[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_tokensPerKEther","type":"uint256"}],"name":"setTokensPerKEther","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"lockedTokens","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"}],"name":"kycVerify","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked1Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"finalised","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MIN","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALS","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"START_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"},{"name":"balance","type":"uint256"}],"name":"addPrecommitment","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_SOFT_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"wallet","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"END_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked2Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_amount","type":"uint256"}],"name":"burnFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_HARD_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALSFACTOR","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MAX","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_2Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"NAME","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finalise","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokensPerKEther","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"kycRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked2Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked1Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyUnlocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferAnyERC20Token","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_wallet","type":"address"}],"name":"setWallet","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_1Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"}],"name":"proxyPayment","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"SYMBOL","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_wallet","type":"address"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newWallet","type":"address"}],"name":"WalletUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokensPerKEther","type":"uint256"}],"name":"TokensPerKEtherUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"ethers","type":"uint256"},{"indexed":false,"name":"newEtherBalance","type":"uint256"},{"indexed":false,"name":"tokens","type":"uint256"},{"indexed":false,"name":"newTotalSupply","type":"uint256"},{"indexed":false,"name":"tokensPerKEther","type":"uint256"}],"name":"TokensBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"},{"indexed":false,"name":"balance","type":"uint256"}],"name":"PrecommitmentAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"}],"name":"KycVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}] 2 | lockedTokensAbi=[{"constant":true,"inputs":[],"name":"TOKENS_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balancesLocked2Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked1Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MIN","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALS","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"START_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unlock1Y","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_SOFT_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_LOCKED_1Y_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"END_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked2Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balancesLocked1Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TRANCHE2_ACCOUNT","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_HARD_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALSFACTOR","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MAX","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_2Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"NAME","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked2Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked1Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unlock2Y","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"addRemainingTokens","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_1Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"SYMBOL","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_LOCKED_2Y_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"_tokenContract","type":"address"}],"payable":false,"type":"constructor"}] 3 | tokenAddress=0x0a91add9e3e97057980da7826043aab2a2d4c35b 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openANX Decentralised Exchange Token Sale Smart Contract 2 | 3 | # *Notice: Further development on the broader project is moved to the [OAX GitLab group](https://gitlab.com/oax)* 4 | 5 | Website: [http://openanx.org/](http://openanx.org/) 6 | 7 | Whitepaper: [https://www.openanx.org/en/assets/whitepaper/openANX_White_Paper_ENU.pdf](https://www.openanx.org/en/assets/whitepaper/openANX_White_Paper_ENU.pdf) 8 | 9 | Reddit: [https://www.reddit.com/r/OpenANX/](https://www.reddit.com/r/OpenANX/) 10 | 11 | Slack: [https://openanx.slack.com/](https://openanx.slack.com/) 12 | 13 | Bug bounty program: [https://medium.com/@OAX_Foundation/openanx-bug-bounty-program-ccc6e981fd6a](https://medium.com/@OAX_Foundation/openanx-bug-bounty-program-ccc6e981fd6a) 14 | 15 |
16 | 17 |
18 | 19 | # Table of contents 20 | 21 | * [Finalised Crowdsale Statistics](#finalised-crowdsale-statistics) 22 | * [Updates](#updates) 23 | * [Requirements](#requirements) 24 | * [TODO](#todo) 25 | * [Operations On The Crowdsale Contract](#operations-on-the-crowdsale-contract) 26 | * [Anytime](#anytime) 27 | * [Before Start Date](#before-start-date) 28 | * [After Start Date And Before End Date Or Finalised](#after-start-date-and-before-end-date-or-finalised) 29 | * [After Finalised](#after-finalised) 30 | * [After 1 Year And 2 Years](#after-1-year-and-2-years) 31 | * [Testing](#testing) 32 | * [Deployment Checklist](#deployment-checklist) 33 | 34 |
35 | 36 |
37 | 38 | # Finalised Crowdsale Statistics 39 | 40 | The public crowdsale was finalised at Jul-04-2017 05:01:14 AM +UTC, 12 days after the start, in block [3971324](https://etherscan.io/block/3971324) with tx [0xf7ba25c7](https://etherscan.io/tx/0xf7ba25c71bedc47d5237fd0e92cba266e627f32ca0de2946254359fa1dcedd0e). 41 | 42 | Following are the statistics for the public contributions to this crowdsale: 43 | 44 | 45 | 46 |
47 | 48 | Following is a chart of the public contributions to this crowdsale: 49 | 50 | 51 | 52 |
53 | 54 | Raw data for the statistics above can be found in [scripts/TokensBought_20170704_150051-final.xls](scripts/TokensBought_20170704_150051-final.xls). 55 | 56 |
57 | 58 | Following is the [EtherScan token page for openANX](https://etherscan.io/token/0x701C244b988a513c945973dEFA05de933b23Fe1D): 59 | 60 | 61 | 62 |
63 | 64 | The token distribution at the end of the crowdsale can be found in [scripts/tokenBalancesByAccounts.xls](scripts/tokenBalancesByAccounts.xls). 65 | 66 |
67 | 68 | Statistics from [https://www.openanx.org/en/](https://www.openanx.org/en/): 69 | 70 | 71 | 72 | 73 |
74 | 75 |
76 | 77 | # Updates 78 | 79 | * Jun 19 2017 - **LOW IMPORTANCE** - Jordi Baylina has pointed out that the first part of `require(msg.value > 0 && msg.value >= CONTRIBUTIONS_MIN);` is not necessary. This first part has been removed. 80 | * Jun 20 2017 - **LOW IMPORTANCE** - Darryl Morris has pointed out that `CONTRIBUTION_MIN` and `CONTRIBUTION_MAX` should be marked as **const**. The code has been left as-is. 81 | * Jun 21 2017 - Loi Luu - [openANX Audit](https://gist.github.com/loiluu/a80ba9c6df0d3a18773698644d988969) 82 | * Jun 21 2017 - **LOW IMPORTANCE** - Brian See has pointed out that `PrecommitmentAdded(...)` is not used, as the `Transfer(0x, participant, balance)` provides the same data. The code has been left as-is. 83 | * Jun 21 2017 - Hugh Madden has provided provided the following, and will update the OpenANXToken.sol code or update this rate using `setTokensPerKEther(...)`: 84 | 85 | 20 June 2017 23:59:59 eth close data = $359.01 86 | Price in USD 359.01 87 | Value of 1k ETH 359010 88 | price per token 0.75 89 | tokens per K of ETH 478680 90 | 91 | I have confirmed using an alternate method: 92 | 93 | ETH per token = .75 / 359.01 = 0.002089078 94 | This is the same as 1 / .002089078 = 478.680000001 OAX per ETH 95 | tokensPerEther = 478.680000001 96 | tokensPerKEther = 478,680 97 | 98 | * Jun 22 2017 - Darryl Morris - [code review report](https://docs.google.com/document/d/1HBMY3v-_-JGZ9hzzcNYnyMyfQRukfetNifNUr57MW8g/edit) with [local copy](audits/DarrylMorris_OpenANXOAXContractCodeReview_20170622.pdf). From his review, Darryl has also reported to the Solidity team bug [#2441 Address literals not being treated as "compile-time constant"](https://github.com/ethereum/solidity/issues/2441) as [`address public TRANCHE2_ACCOUNT = 0xBbBB34FA53A801b5F298744490a1596438bbBe50;`](https://github.com/openanx/OpenANXToken/blob/master/contracts/LockedTokens.sol#L33) could not be set as a constant. 99 | * Jun 22 2017 - OpenANXToken deployed to [0x701c244b988a513c945973defa05de933b23fe1d](https://etherscan.io/address/0x701c244b988a513c945973defa05de933b23fe1d#code), LockedTokens address is [0x3866259bc60e5b69d5c438db238d3b4c9db37bcb](https://etherscan.io/address/0x3866259bc60e5b69d5c438db238d3b4c9db37bcb#code) and wallet at [0x2b3b67c6dffc2bcdda2315491eac9bbe868fbcdd](https://etherscan.io/address/0x2b3b67c6dffc2bcdda2315491eac9bbe868fbcdd) 100 | * Jul 04 2017 - Hugh Madden executed the `finalise()` function in transaction [0xf7ba25c7](https://etherscan.io/tx/0xf7ba25c71bedc47d5237fd0e92cba266e627f32ca0de2946254359fa1dcedd0e) at Jul-04-2017 05:01:14 AM +UTC. 101 | 102 |
103 | 104 |
105 | 106 | # Requirements 107 | 108 | * Token Identifier 109 | * symbol `OAX` 110 | * name `openANX Token` 111 | * decimals `18` 112 | 113 | * Tranche 1 30,000,000 (30%) OAX Crowdsale Dates 114 | * START_DATE = 1498136400 Thursday, 22-Jun-17 13:00:00 UTC / 1pm GMT 22 June 2017 115 | * END_DATE = 1500728400 Saturday, 22-Jul-17 13:00:00 UTC / 1pm GMT 22 July 2017 116 | 117 | * Total of 100,000,000 OAX tokens 118 | * Tranche 1 30,000,000 (30%) OAX Crowdsale 119 | * Soft cap of 13,000,000 OAX (ETH equivalence of USD 9,750,000) 120 | * Hard cap of 30,000,000 OAX (ETH equivalence of USD 22,500,000) 121 | * Tokens from precommitments funded via fiat and other currencies will be included in this tranche 122 | * Tranche 2 30,000,000 (30%) OAX - locked for 1 year from token launch for Additional Token Sale (ATS) 123 | * 30,000,000 Tranche 2 token sale - Additional Token Sale (ATS). These tokens are subject to a lock-up for 1 year from token launch 124 | * 20,000,000 (20%) OAX retained by the foundation, locked for 2 years from token launch 125 | * 20,000,000 allocated to founding supporters (directors, advisors and early backers) of the foundation, consisting of: 126 | * 14,000,000 (70%) locked for 1 year from token launch 127 | * 6,000,000 (30%) locked for 2 years from token launch 128 | 129 | * OAX Token Price 130 | * The price for an OAX token will be set as the equivalence of USD 0.75 ETH based on ETH/USD @ 12:00 GMT Jun 21 2017 131 | * The indicative price per OAX token is 0.00290923 ETH as of 8 June 2017 132 | * 1 OAX = 0.00290923 ETH 133 | * 1 ETH = 1 / 0.00290923 = 343.733565238912015 OAX 134 | * This will be encoded as an unsigned integer, 1,000 ETH = 343734 OAX with six significant figures 135 | tokensPerKEther = 343734 136 | 137 | * Precommitments 138 | * Some participants will be able to purchase the crowdsale tokens through precommitments using fiat and other currencies 139 | * openANX will allocate the tokens for these participants prior to the public crowdsale commencement 140 | 141 | * Minimum Funding And Refunds 142 | * There is no minimum funding level in the smart contract as the precommitments already exceed the minimum funding level 143 | * There is therefore no requirements for this crowdsale contract to provide refunds to participants 144 | * ETH contributed to the crowdsale smart contract will be immediately transferred into a wallet specified by openANX 145 | 146 | * Crowdsale Wallet 147 | * The wallet receiving the ethers during the crowdsale has to be specifed as a parameter during the deployment of the crowdsale contract 148 | * This wallet address can be changed at any time during the crowdsale period 149 | 150 | * Minimum and Maximum Contributions 151 | * There is the ability to set a minimum and maximum contribution per transaction. Set one or both of these to 0 if this restriction is not required 152 | 153 | * Soft And Hard Cap 154 | * There will be a soft cap and a hard cap specified in the crowdsale smart contract 155 | * openANX will be able to execute the `finalise()` function to close the crowdsale once the number of issued tokens exceeds the soft cap 156 | * openANX will also be able to execute the `finalise()` function after the crowdsale end date if the soft cap is not reached 157 | 158 | * `finalise()` The Crowdsale 159 | * openANX calls `finalise()` to close the crowdsale, either after the soft cap is breached to the end of the crowdsale period, or after the crowdsale end date 160 | * The `finalise()` function will allocate the 1 and 2 year locked tokens, unsold tranche1 tokens and tranche2 tokens 161 | 162 | * KYC 163 | * Participants can contribute to the crowdsale by sending ETH to the OAX 'openANX Token' smart contract and will be issued OAX tokens 164 | * Participants will only be able to transfer their issued OAX tokens after being successfully KYC verified by openANX and unless otherwise agreed by Open ANX Foundation, in no circumstance shall such transfer be possibly prior to the Release Date scheduled for July 29th (being one week after the scheduled end of the Contribution Window). 165 | * There will be no refund if the KYC verification is determined to be unsuccessful 166 | * The KYC smart contract will just encode a simple Yes/No on an accounts KYC status 167 | * Once KYC approved, an account cannot be KYC disapproved 168 | * OAX tokens are otherwise able to be transferred freely without further KYC requirements 169 | 170 | * Unsold Tranche 1 Tokens 171 | * Any tokens unsold from the tranche 1 quota will be locked away for 1 year along with the tranche 2 tokens 172 | 173 | * Locked Tokens 174 | * Accounts holding 1 or 2 year locked tokens will also be able to participant in the public crowdsale 175 | 176 | * Burn function 177 | * [ ] Work out who can burn the tokens - owner only, or anyone 178 | 179 |
180 | 181 |
182 | 183 | ## TODO 184 | 185 | * [x] BK Complete KYC functions 186 | * [x] BK Testing different scenarios 187 | * [x] Scenario where funding below soft cap and above soft cap 188 | * [x] Unlocking of tokens in 1 year and 2 years 189 | * [x] Public crowdsale participant can also have locked tokens 190 | * [x] BK Add functions in the main contract to query the locked balances for accounts 191 | * [x] BK Develop and test token contract upgrade path 192 | * [x] BK Develop test membership contract to test users burning tokens for membership 193 | * [ ] BK Complete [Security Audit](SecurityAudit.md). 194 | * [ ] JB Security audit 195 | 196 |
197 | 198 |
199 | 200 | # Operations On The Crowdsale Contract 201 | 202 | Following are the functions that can be called at the different phases of the crowdsale contract 203 | 204 | ## Anytime 205 | 206 | * Owner can call `setWallet(...)` to set the wallet address 207 | 208 | * Owner can call `kycVerify(...)` to verify participants, but this should be done after the crowdsale as the participants can transfer tokens immediately after being verified 209 | 210 | ## Before Start Date 211 | 212 | * Owner can call `setTokensPerKEther(...)` to set the token issuance rate 213 | * Owner can call `addPrecommitment(...)` to add precommitment balances 214 | 215 | ## After Start Date And Before End Date Or Finalised 216 | 217 | * Participants can send ETH to the default `()` function and receive tokens 218 | * Owner can call `finalise()` if soft cap breached or we are past the end date 219 | 220 | ## After Finalised 221 | 222 | * Owner calls `kycVerify(...)` to verify participants, but this should be done after the crowdsale as the participants can transfer tokens immediately after being verified 223 | * Participant can call the normal `transfer(...)`, `approve(...)` and `transferFrom(...)` to transfer tokens 224 | 225 | ## After 1 Year And 2 Years 226 | 227 | * Participants with locked tokens can called the `lockedTokens.unlock1Y()` and `lockedTokens.unlock2Y()` to unlock their tokens 228 | * Find the address of the LockedTokens contract from the lockedTokens variable in the token contract 229 | * Watch the LockedTokens address using the LockedTokens Application Binary Interface 230 | * Execute `unlock1Y()` after 1 year has passed, or `unlock2Y()` after 2 years has passed, to unlock the tokens 231 | 232 |
233 | 234 |
235 | 236 | # Testing 237 | 238 | See [test](test) for details. 239 | 240 |
241 | 242 |
243 | 244 | # Deployment Checklist 245 | 246 | * Check START_DATE and END_DATE 247 | * Check Solidity [release history](https://github.com/ethereum/solidity/releases) for potential bugs 248 | * Deploy contract to Mainnet with specified wallet address as the deployment parameter 249 | * Verify the source code on EtherScan.io 250 | * Verify the main openANXToken contract 251 | * Verify the LockedToken contract 252 | 253 |
254 | 255 |
256 | 257 | Enjoy. (c) OpenANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. The MIT Licence. 258 | -------------------------------------------------------------------------------- /test/functions.js: -------------------------------------------------------------------------------- 1 | // Jun 12 2017 2 | var ethPriceUSD = 380.39; 3 | 4 | // ----------------------------------------------------------------------------- 5 | // Accounts 6 | // ----------------------------------------------------------------------------- 7 | var accounts = []; 8 | var accountNames = {}; 9 | 10 | addAccount(eth.accounts[0], "Account #0 - Miner"); 11 | addAccount(eth.accounts[1], "Account #1 - Token Owner"); 12 | addAccount(eth.accounts[2], "Account #2 - KYCed"); 13 | addAccount(eth.accounts[3], "Account #3 - KYCed"); 14 | addAccount(eth.accounts[4], "Account #4"); 15 | addAccount(eth.accounts[5], "Account #5"); 16 | addAccount(eth.accounts[6], "Account #6"); 17 | addAccount(eth.accounts[7], "Account #7"); 18 | addAccount(eth.accounts[8], "Account #8"); 19 | addAccount(eth.accounts[9], "Account #9 - Crowdfund Wallet"); 20 | addAccount(eth.accounts[10], "Account #10 - Foundation"); 21 | addAccount(eth.accounts[11], "Account #11 - Advisors"); 22 | addAccount(eth.accounts[12], "Account #12 - Directors"); 23 | addAccount(eth.accounts[13], "Account #13 - Early Backers"); 24 | addAccount(eth.accounts[14], "Account #14 - Developers"); 25 | addAccount(eth.accounts[15], "Account #15 - Precommitments"); 26 | addAccount(eth.accounts[16], "Account #16 - Tranche 2 Locked"); 27 | addAccount("0x0000000000000000000000000000000000000000", "Burn Account"); 28 | 29 | 30 | 31 | var minerAccount = eth.accounts[0]; 32 | var tokenOwnerAccount = eth.accounts[1]; 33 | var account2 = eth.accounts[2]; 34 | var account3 = eth.accounts[3]; 35 | var account4 = eth.accounts[4]; 36 | var account5 = eth.accounts[5]; 37 | var account6 = eth.accounts[6]; 38 | var account7 = eth.accounts[7]; 39 | var account8 = eth.accounts[8]; 40 | var crowdfundWallet = eth.accounts[9]; 41 | var foundationAccount = eth.accounts[10]; 42 | var advisorsAccount = eth.accounts[11]; 43 | var directorsAccount = eth.accounts[12]; 44 | var earlyBackersAccount = eth.accounts[13]; 45 | var developersAccount = eth.accounts[14]; 46 | var precommitmentsAccount = eth.accounts[15]; 47 | var tranche2Account = eth.accounts[16]; 48 | 49 | var baseBlock = eth.blockNumber; 50 | 51 | function unlockAccounts(password) { 52 | for (var i = 0; i < eth.accounts.length; i++) { 53 | personal.unlockAccount(eth.accounts[i], password, 100000); 54 | } 55 | } 56 | 57 | function addAccount(account, accountName) { 58 | accounts.push(account); 59 | accountNames[account] = accountName; 60 | } 61 | 62 | 63 | // ----------------------------------------------------------------------------- 64 | // Token Contract 65 | // ----------------------------------------------------------------------------- 66 | var tokenContractAddress = null; 67 | var tokenContractAbi = null; 68 | var lockedTokenContractAbi = null; 69 | 70 | function addTokenContractAddressAndAbi(address, tokenAbi, lockedTokenAbi) { 71 | tokenContractAddress = address; 72 | tokenContractAbi = tokenAbi; 73 | lockedTokenContractAbi = lockedTokenAbi; 74 | } 75 | 76 | 77 | // ----------------------------------------------------------------------------- 78 | // Account ETH and token balances 79 | // ----------------------------------------------------------------------------- 80 | function printBalances() { 81 | var token = tokenContractAddress == null || tokenContractAbi == null ? null : web3.eth.contract(tokenContractAbi).at(tokenContractAddress); 82 | var decimals = token == null ? 18 : token.decimals(); 83 | var lockedTokenContract = token == null || lockedTokenContractAbi == null ? null : web3.eth.contract(lockedTokenContractAbi).at(token.lockedTokens()); 84 | var i = 0; 85 | var totalTokenBalanceUnlocked = new BigNumber(0); 86 | var totalTokenBalance1Y = new BigNumber(0); 87 | var totalTokenBalance2Y = new BigNumber(0); 88 | var totalTokenBalance = new BigNumber(0); 89 | console.log("RESULT: # Account EtherBalanceChange Unlocked Token Locked 1Y Locked 2Y Total Name"); 90 | console.log("RESULT: -- ------------------------------------------ --------------------------- ------------------------------ ------------------------------ ------------------------------ ------------------------------ ---------------------------"); 91 | accounts.forEach(function(e) { 92 | i++; 93 | var etherBalanceBaseBlock = eth.getBalance(e, baseBlock); 94 | var etherBalance = web3.fromWei(eth.getBalance(e).minus(etherBalanceBaseBlock), "ether"); 95 | var tokenBalanceUnlocked = token == null ? new BigNumber(0) : token.balanceOf(e).shift(-decimals); 96 | var tokenBalance1Y = lockedTokenContract == null ? new BigNumber(0) : lockedTokenContract.balanceOfLocked1Y(e).shift(-decimals); 97 | var tokenBalance2Y = lockedTokenContract == null ? new BigNumber(0) : lockedTokenContract.balanceOfLocked2Y(e).shift(-decimals); 98 | var tokenBalance = tokenBalanceUnlocked.add(tokenBalance1Y).add(tokenBalance2Y); 99 | totalTokenBalanceUnlocked = totalTokenBalanceUnlocked.add(tokenBalanceUnlocked); 100 | totalTokenBalance1Y = totalTokenBalance1Y.add(tokenBalance1Y); 101 | totalTokenBalance2Y = totalTokenBalance2Y.add(tokenBalance2Y); 102 | totalTokenBalance = totalTokenBalance.add(tokenBalance); 103 | console.log("RESULT: " + pad2(i) + " " + e + " " + pad(etherBalance) + " " + padToken(tokenBalanceUnlocked, decimals) + " " + padToken(tokenBalance1Y, decimals) + " " + 104 | padToken(tokenBalance2Y, decimals) + " " + padToken(tokenBalance, decimals) + " " + accountNames[e]); 105 | }); 106 | console.log("RESULT: -- ------------------------------------------ --------------------------- ------------------------------ ------------------------------ ------------------------------ ------------------------------ ---------------------------"); 107 | console.log("RESULT: " + padToken(totalTokenBalanceUnlocked, decimals) + " " + 108 | padToken(totalTokenBalance1Y, decimals) + " " + padToken(totalTokenBalance2Y, decimals) + " " + padToken(totalTokenBalance, decimals) + " Total Token Balances *"); 109 | console.log("RESULT: -- ------------------------------------------ --------------------------- ------------------------------ ------------------------------ ------------------------------ ------------------------------ ---------------------------"); 110 | console.log("RESULT: * Note that the sum of all the locked tokens is represented in the unlocked balance at the token contract address, and this will be double counted in the grand total balance above"); 111 | console.log("RESULT: "); 112 | } 113 | 114 | function pad2(s) { 115 | var o = s.toFixed(0); 116 | while (o.length < 2) { 117 | o = " " + o; 118 | } 119 | return o; 120 | } 121 | 122 | function pad(s) { 123 | var o = s.toFixed(18); 124 | while (o.length < 27) { 125 | o = " " + o; 126 | } 127 | return o; 128 | } 129 | 130 | function padToken(s, decimals) { 131 | var o = s.toFixed(decimals); 132 | var l = parseInt(decimals)+12; 133 | while (o.length < l) { 134 | o = " " + o; 135 | } 136 | return o; 137 | } 138 | 139 | 140 | // ----------------------------------------------------------------------------- 141 | // Transaction status 142 | // ----------------------------------------------------------------------------- 143 | function printTxData(name, txId) { 144 | var tx = eth.getTransaction(txId); 145 | var txReceipt = eth.getTransactionReceipt(txId); 146 | var gasPrice = tx.gasPrice; 147 | var gasCostETH = tx.gasPrice.mul(txReceipt.gasUsed).div(1e18); 148 | var gasCostUSD = gasCostETH.mul(ethPriceUSD); 149 | console.log("RESULT: " + name + " gas=" + tx.gas + " gasUsed=" + txReceipt.gasUsed + " costETH=" + gasCostETH + 150 | " costUSD=" + gasCostUSD + " @ ETH/USD=" + ethPriceUSD + " gasPrice=" + gasPrice + " block=" + 151 | txReceipt.blockNumber + " txId=" + txId); 152 | } 153 | 154 | function assertEtherBalance(account, expectedBalance) { 155 | var etherBalance = web3.fromWei(eth.getBalance(account), "ether"); 156 | if (etherBalance == expectedBalance) { 157 | console.log("RESULT: OK " + account + " has expected balance " + expectedBalance); 158 | } else { 159 | console.log("RESULT: FAILURE " + account + " has balance " + etherBalance + " <> expected " + expectedBalance); 160 | } 161 | } 162 | 163 | function gasEqualsGasUsed(tx) { 164 | var gas = eth.getTransaction(tx).gas; 165 | var gasUsed = eth.getTransactionReceipt(tx).gasUsed; 166 | return (gas == gasUsed); 167 | } 168 | 169 | function failIfGasEqualsGasUsed(tx, msg) { 170 | var gas = eth.getTransaction(tx).gas; 171 | var gasUsed = eth.getTransactionReceipt(tx).gasUsed; 172 | if (gas == gasUsed) { 173 | console.log("RESULT: FAIL " + msg); 174 | return 0; 175 | } else { 176 | console.log("RESULT: PASS " + msg); 177 | return 1; 178 | } 179 | } 180 | 181 | function passIfGasEqualsGasUsed(tx, msg) { 182 | var gas = eth.getTransaction(tx).gas; 183 | var gasUsed = eth.getTransactionReceipt(tx).gasUsed; 184 | if (gas == gasUsed) { 185 | console.log("RESULT: PASS " + msg); 186 | return 1; 187 | } else { 188 | console.log("RESULT: FAIL " + msg); 189 | return 0; 190 | } 191 | } 192 | 193 | function failIfGasEqualsGasUsedOrContractAddressNull(contractAddress, tx, msg) { 194 | if (contractAddress == null) { 195 | console.log("RESULT: FAIL " + msg); 196 | return 0; 197 | } else { 198 | var gas = eth.getTransaction(tx).gas; 199 | var gasUsed = eth.getTransactionReceipt(tx).gasUsed; 200 | if (gas == gasUsed) { 201 | console.log("RESULT: FAIL " + msg); 202 | return 0; 203 | } else { 204 | console.log("RESULT: PASS " + msg); 205 | return 1; 206 | } 207 | } 208 | } 209 | 210 | 211 | // ----------------------------------------------------------------------------- 212 | // Token Contract details 213 | // ----------------------------------------------------------------------------- 214 | function printTokenContractStaticDetails() { 215 | if (tokenContractAddress != null && tokenContractAbi != null) { 216 | var contract = eth.contract(tokenContractAbi).at(tokenContractAddress); 217 | var decimals = contract.decimals(); 218 | console.log("RESULT: token.symbol=" + contract.symbol()); 219 | console.log("RESULT: token.name=" + contract.name()); 220 | console.log("RESULT: token.decimals=" + decimals); 221 | console.log("RESULT: token.DECIMALSFACTOR=" + contract.DECIMALSFACTOR()); 222 | var startDate = contract.START_DATE(); 223 | console.log("RESULT: token.START_DATE=" + startDate + " " + new Date(startDate * 1000).toUTCString() + 224 | " / " + new Date(startDate * 1000).toGMTString()); 225 | var endDate = contract.END_DATE(); 226 | console.log("RESULT: token.END_DATE=" + endDate + " " + new Date(endDate * 1000).toUTCString() + 227 | " / " + new Date(endDate * 1000).toGMTString()); 228 | console.log("RESULT: token.TOKENS_SOFT_CAP=" + contract.TOKENS_SOFT_CAP().shift(-decimals)); 229 | console.log("RESULT: token.TOKENS_HARD_CAP=" + contract.TOKENS_HARD_CAP().shift(-decimals)); 230 | console.log("RESULT: token.TOKENS_TOTAL=" + contract.TOKENS_TOTAL().shift(-decimals)); 231 | } 232 | } 233 | 234 | var dynamicDetailsFromBlock = 0; 235 | function printTokenContractDynamicDetails() { 236 | if (tokenContractAddress != null && tokenContractAbi != null && lockedTokenContractAbi != null) { 237 | var contract = eth.contract(tokenContractAbi).at(tokenContractAddress); 238 | var lockedTokenContract = eth.contract(lockedTokenContractAbi).at(contract.lockedTokens()); 239 | var decimals = contract.decimals(); 240 | console.log("RESULT: token.finalised=" + contract.finalised()); 241 | console.log("RESULT: token.tokensPerKEther=" + contract.tokensPerKEther()); 242 | console.log("RESULT: token.totalSupply=" + contract.totalSupply().shift(-decimals)); 243 | console.log("RESULT: token.totalSupplyLocked(1Y/2Y)=" + contract.totalSupplyLocked1Y().shift(-decimals) + " / " + contract.totalSupplyLocked2Y().shift(-decimals)); 244 | console.log("RESULT: token.totalSupplyLocked=" + contract.totalSupplyLocked().shift(-decimals)); 245 | console.log("RESULT: token.totalSupplyUnlocked=" + contract.totalSupplyUnlocked().shift(-decimals)); 246 | console.log("RESULT: token.balanceOfLocked(earlyBackersAccount)(1Y/2Y)=" + contract.balanceOfLocked1Y(earlyBackersAccount).shift(-decimals) + " / " + 247 | contract.balanceOfLocked2Y(earlyBackersAccount).shift(-decimals)); 248 | console.log("RESULT: token.balanceOfLocked(developersAccount)(1Y/2Y)=" + contract.balanceOfLocked1Y(developersAccount).shift(-decimals) + " / " + 249 | contract.balanceOfLocked2Y(developersAccount).shift(-decimals)); 250 | var locked1YDate = contract.LOCKED_1Y_DATE(); 251 | console.log("RESULT: token.LOCKED_1Y_DATE=" + locked1YDate + " " + new Date(locked1YDate * 1000).toUTCString() + 252 | " / " + new Date(locked1YDate * 1000).toGMTString()); 253 | var locked2YDate = contract.LOCKED_2Y_DATE(); 254 | console.log("RESULT: token.LOCKED_2Y_DATE=" + locked2YDate + " " + new Date(locked2YDate * 1000).toUTCString() + 255 | " / " + new Date(locked2YDate * 1000).toGMTString()); 256 | console.log("RESULT: lockedToken.TOKENS_LOCKED_1Y_TOTAL=" + lockedTokenContract.TOKENS_LOCKED_1Y_TOTAL().shift(-decimals)); 257 | console.log("RESULT: lockedToken.TOKENS_LOCKED_2Y_TOTAL=" + lockedTokenContract.TOKENS_LOCKED_2Y_TOTAL().shift(-decimals)); 258 | console.log("RESULT: lockedToken.totalSupplyLocked1Y=" + lockedTokenContract.totalSupplyLocked1Y().shift(-decimals)); 259 | console.log("RESULT: lockedToken.totalSupplyLocked2Y=" + lockedTokenContract.totalSupplyLocked2Y().shift(-decimals)); 260 | console.log("RESULT: lockedToken.totalSupplyLocked=" + lockedTokenContract.totalSupplyLocked().shift(-decimals)); 261 | console.log("RESULT: token.owner=" + contract.owner()); 262 | console.log("RESULT: token.newOwner=" + contract.newOwner()); 263 | 264 | var latestBlock = eth.blockNumber; 265 | var i; 266 | 267 | var ownershipTransferredEvent = contract.OwnershipTransferred({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 268 | i = 0; 269 | ownershipTransferredEvent.watch(function (error, result) { 270 | console.log("RESULT: OwnershipTransferred Event " + i++ + ": from=" + result.args._from + " to=" + result.args._to + " " + 271 | result.blockNumber); 272 | }); 273 | ownershipTransferredEvent.stopWatching(); 274 | 275 | var tokensPerKEtherUpdatedEvent = contract.TokensPerKEtherUpdated({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 276 | i = 0; 277 | tokensPerKEtherUpdatedEvent.watch(function (error, result) { 278 | console.log("RESULT: TokensPerKEtherUpdated Event " + i++ + ": tokensPerKEther=" + result.args.tokensPerKEther + " block=" + result.blockNumber); 279 | }); 280 | tokensPerKEtherUpdatedEvent.stopWatching(); 281 | 282 | var walletUpdatedEvent = contract.WalletUpdated({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 283 | i = 0; 284 | walletUpdatedEvent.watch(function (error, result) { 285 | console.log("RESULT: WalletUpdated Event " + i++ + ": from=" + result.args.newWallet + " block=" + result.blockNumber); 286 | }); 287 | walletUpdatedEvent.stopWatching(); 288 | 289 | var precommitmentAddedEvent = contract.PrecommitmentAdded({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 290 | i = 0; 291 | precommitmentAddedEvent.watch(function (error, result) { 292 | console.log("RESULT: PrecommitmentAdded Event " + i++ + ": participant=" + result.args.participant + 293 | " balance=" + result.args.balance.shift(-decimals) + 294 | " block=" + result.blockNumber); 295 | }); 296 | precommitmentAddedEvent.stopWatching(); 297 | 298 | var tokensBoughtEvent = contract.TokensBought({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 299 | i = 0; 300 | tokensBoughtEvent.watch(function (error, result) { 301 | console.log("RESULT: TokensBought Event " + i++ + ": buyer=" + result.args.buyer + 302 | " ethers=" + web3.fromWei(result.args.ethers, "ether") + 303 | " newEtherBalance=" + web3.fromWei(result.args.newEtherBalance, "ether") + 304 | " tokens=" + result.args.tokens.shift(-decimals) + 305 | " newTotalSupply=" + result.args.newTotalSupply.shift(-decimals) + 306 | " tokensPerKEther=" + result.args.tokensPerKEther + 307 | " block=" + result.blockNumber); 308 | }); 309 | tokensBoughtEvent.stopWatching(); 310 | 311 | var kycVerifiedEvent = contract.KycVerified({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 312 | i = 0; 313 | kycVerifiedEvent.watch(function (error, result) { 314 | console.log("RESULT: KycVerified Event " + i++ + ": participant=" + result.args.participant + " block=" + result.blockNumber); 315 | }); 316 | kycVerifiedEvent.stopWatching(); 317 | 318 | var approvalEvent = contract.Approval({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 319 | i = 0; 320 | approvalEvent.watch(function (error, result) { 321 | console.log("RESULT: Approval Event " + i++ + ": owner=" + result.args._owner + " spender=" + result.args._spender + " " + 322 | result.args._value.shift(-decimals) + " block=" + result.blockNumber); 323 | }); 324 | approvalEvent.stopWatching(); 325 | 326 | var transferEvent = contract.Transfer({}, { fromBlock: dynamicDetailsFromBlock, toBlock: latestBlock }); 327 | i = 0; 328 | transferEvent.watch(function (error, result) { 329 | console.log("RESULT: Transfer Event " + i++ + ": from=" + result.args._from + " to=" + result.args._to + 330 | " value=" + result.args._value.shift(-decimals) + " block=" + result.blockNumber); 331 | }); 332 | transferEvent.stopWatching(); 333 | dynamicDetailsFromBlock = latestBlock + 1; 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /scripts/getOpenANXTokenDetails.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ---------------------------------------------------------------------------------------------- 3 | # Get Details For The openANX token contract 4 | # 5 | # Enjoy. (c) openANX & BokkyPooBah / Bok Consulting Pty Ltd 2017. The MIT Licence. 6 | # ---------------------------------------------------------------------------------------------- 7 | 8 | # Set below if you don't want to use IPC 9 | # GETHATTACHPOINT=rpc:http://localhost:8545 10 | # GETHATTACHPOINT=rpc:http://localhost:8646 11 | 12 | DATE=`date "+%Y%m%d_%H%M%S"` 13 | # DATE=`date "+%Y%m%d"` 14 | echo "Date: $DATE" 15 | 16 | TEMPFILE=Temp_$DATE.txt 17 | MAINFILE=Main_$DATE.txt 18 | TOKENSBOUGHTFILE=TokensBought_$DATE.tsv 19 | TRANSFERFILE=Transfers_$DATE.tsv 20 | 21 | geth --verbosity 3 attach $GETHATTACHPOINT << EOF | tee $TEMPFILE 22 | 23 | var tokenAddress="0x701c244b988a513c945973defa05de933b23fe1d"; 24 | var tokenDeploymentBlock=3908947; 25 | // DEV var tokenAddress="0x90d8927407c79c4a28ee879b821c76fc9bcc2688"; 26 | // DEV var tokenDeploymentBlock=0; 27 | 28 | var tokenAbi=[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}, \ 29 | {"constant":false,"inputs":[{"name":"_tokensPerKEther","type":"uint256"}],"name":"setTokensPerKEther","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"lockedTokens","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"}],"name":"kycVerify","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked1Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"finalised","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MIN","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALS","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"START_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"},{"name":"balance","type":"uint256"}],"name":"addPrecommitment","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_SOFT_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"wallet","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"END_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked2Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_amount","type":"uint256"}],"name":"burnFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_HARD_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALSFACTOR","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MAX","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_2Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"NAME","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"finalise","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokensPerKEther","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"kycRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked2Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked1Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyUnlocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferAnyERC20Token","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_wallet","type":"address"}],"name":"setWallet","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_1Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"participant","type":"address"}],"name":"proxyPayment","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"SYMBOL","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_wallet","type":"address"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newWallet","type":"address"}],"name":"WalletUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"tokensPerKEther","type":"uint256"}],"name":"TokensPerKEtherUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"ethers","type":"uint256"},{"indexed":false,"name":"newEtherBalance","type":"uint256"},{"indexed":false,"name":"tokens","type":"uint256"},{"indexed":false,"name":"newTotalSupply","type":"uint256"},{"indexed":false,"name":"tokensPerKEther","type":"uint256"}],"name":"TokensBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"},{"indexed":false,"name":"balance","type":"uint256"}],"name":"PrecommitmentAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"participant","type":"address"}],"name":"KycVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]; 30 | var lockedTokensAbi=[{"constant":true,"inputs":[],"name":"TOKENS_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balancesLocked2Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked1Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MIN","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALS","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"START_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unlock1Y","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_SOFT_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_LOCKED_1Y_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"END_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked2Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balancesLocked1Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TRANCHE2_ACCOUNT","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_HARD_CAP","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"DECIMALSFACTOR","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"CONTRIBUTIONS_MAX","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_2Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"NAME","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked2Y","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"totalSupplyLocked1Y","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"unlock2Y","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"balanceOfLocked","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"addRemainingTokens","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"LOCKED_1Y_DATE","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"SYMBOL","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"TOKENS_LOCKED_2Y_TOTAL","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"_tokenContract","type":"address"}],"payable":false,"type":"constructor"}]; 31 | 32 | var contract = eth.contract(tokenAbi).at(tokenAddress); 33 | var lockedTokenAddress = contract.lockedTokens(); 34 | var lockedTokenContract = eth.contract(lockedTokensAbi).at(lockedTokenAddress); 35 | var decimals = contract.decimals(); 36 | console.log("MAIN: token.address=" + tokenAddress); 37 | console.log("MAIN: token.lockedTokensAddress=" + lockedTokenAddress); 38 | var currentTime = new Date(); 39 | console.log("MAIN: currentTime=" + currentTime / 1000 + " " + currentTime.toUTCString() + " / " + currentTime.toGMTString()); 40 | var startDate = contract.START_DATE(); 41 | console.log("MAIN: token.START_DATE=" + startDate + " " + new Date(startDate * 1000).toUTCString() + " / " + new Date(startDate * 1000).toGMTString()); 42 | var endDate = contract.END_DATE(); 43 | console.log("MAIN: token.END_DATE=" + endDate + " " + new Date(endDate * 1000).toUTCString() + " / " + new Date(endDate * 1000).toGMTString()); 44 | console.log("MAIN: token.symbol=" + contract.symbol()); 45 | console.log("MAIN: token.name=" + contract.name()); 46 | console.log("MAIN: token.decimals=" + decimals); 47 | console.log("MAIN: token.DECIMALSFACTOR=" + contract.DECIMALSFACTOR()); 48 | console.log("MAIN: token.TOKENS_SOFT_CAP=" + contract.TOKENS_SOFT_CAP().shift(-decimals)); 49 | console.log("MAIN: token.TOKENS_HARD_CAP=" + contract.TOKENS_HARD_CAP().shift(-decimals)); 50 | console.log("MAIN: token.TOKENS_TOTAL=" + contract.TOKENS_TOTAL().shift(-decimals)); 51 | console.log("MAIN: token.finalised=" + contract.finalised()); 52 | console.log("MAIN: token.tokensPerKEther=" + contract.tokensPerKEther()); 53 | console.log("MAIN: token.totalSupply=" + contract.totalSupply().shift(-decimals)); 54 | console.log("MAIN: token.totalSupplyLocked(1Y/2Y)=" + contract.totalSupplyLocked1Y().shift(-decimals) + " / " + contract.totalSupplyLocked2Y().shift(-decimals)); 55 | console.log("MAIN: token.totalSupplyLocked=" + contract.totalSupplyLocked().shift(-decimals)); 56 | console.log("MAIN: token.totalSupplyUnlocked=" + contract.totalSupplyUnlocked().shift(-decimals)); 57 | var locked1YDate = contract.LOCKED_1Y_DATE(); 58 | console.log("MAIN: token.LOCKED_1Y_DATE=" + locked1YDate + " " + new Date(locked1YDate * 1000).toUTCString() + " / " + new Date(locked1YDate * 1000).toGMTString()); 59 | var locked2YDate = contract.LOCKED_2Y_DATE(); 60 | console.log("MAIN: token.LOCKED_2Y_DATE=" + locked2YDate + " " + new Date(locked2YDate * 1000).toUTCString() + " / " + new Date(locked2YDate * 1000).toGMTString()); 61 | console.log("MAIN: lockedToken.TOKENS_LOCKED_1Y_TOTAL=" + lockedTokenContract.TOKENS_LOCKED_1Y_TOTAL().shift(-decimals)); 62 | console.log("MAIN: lockedToken.TOKENS_LOCKED_2Y_TOTAL=" + lockedTokenContract.TOKENS_LOCKED_2Y_TOTAL().shift(-decimals)); 63 | console.log("MAIN: lockedToken.totalSupplyLocked1Y=" + lockedTokenContract.totalSupplyLocked1Y().shift(-decimals)); 64 | console.log("MAIN: lockedToken.totalSupplyLocked2Y=" + lockedTokenContract.totalSupplyLocked2Y().shift(-decimals)); 65 | console.log("MAIN: lockedToken.totalSupplyLocked=" + lockedTokenContract.totalSupplyLocked().shift(-decimals)); 66 | console.log("MAIN: token.owner=" + contract.owner()); 67 | console.log("MAIN: token.newOwner=" + contract.newOwner()); 68 | 69 | var latestBlock = eth.blockNumber; 70 | var i; 71 | 72 | var ownershipTransferredEvent = contract.OwnershipTransferred({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 73 | i = 0; 74 | ownershipTransferredEvent.watch(function (error, result) { 75 | console.log("MAIN: OwnershipTransferred Event " + i++ + ": from=" + result.args._from + " to=" + result.args._to + " " + result.blockNumber); 76 | }); 77 | 78 | var tokensPerKEtherUpdatedEvent = contract.TokensPerKEtherUpdated({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 79 | i = 0; 80 | tokensPerKEtherUpdatedEvent.watch(function (error, result) { 81 | console.log("MAIN: TokensPerKEtherUpdated Event " + i++ + ": tokensPerKEther=" + result.args.tokensPerKEther + " block=" + result.blockNumber); 82 | }); 83 | tokensPerKEtherUpdatedEvent.stopWatching(); 84 | 85 | var walletUpdatedEvent = contract.WalletUpdated({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 86 | i = 0; 87 | walletUpdatedEvent.watch(function (error, result) { 88 | console.log("MAIN: WalletUpdated Event " + i++ + ": from=" + result.args.newWallet + " block=" + result.blockNumber); 89 | }); 90 | walletUpdatedEvent.stopWatching(); 91 | 92 | var tokensBoughtEvent = contract.TokensBought({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 93 | i = 0; 94 | var totalEthers = new BigNumber(0); 95 | console.log("TOKENSBOUGHT: No\tBuyer\tEthers\tTokens\tTokenBalance\tTokensPerKEther\tTxIndex\tTxHash\tBlock\tEtherBalance"); 96 | tokensBoughtEvent.watch(function (error, result) { 97 | totalEthers = totalEthers.add(result.args.ethers); 98 | console.log("TOKENSBOUGHT: " + i++ + "\t" + result.args.buyer + "\t" + web3.fromWei(result.args.ethers, "ether") + 99 | "\t" + result.args.tokens.shift(-decimals) + "\t" + result.args.newTotalSupply.shift(-decimals) + "\t" + result.args.tokensPerKEther + "\t" + 100 | result.transactionIndex + "\t" + result.transactionHash + "\t" + result.blockNumber + "\t" + web3.fromWei(totalEthers, "ether")); 101 | }); 102 | tokensBoughtEvent.stopWatching(); 103 | 104 | var kycVerifiedEvent = contract.KycVerified({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 105 | i = 0; 106 | kycVerifiedEvent.watch(function (error, result) { 107 | console.log("MAIN: KycVerified Event " + i++ + ": participant=" + result.args.participant + " block=" + result.blockNumber); 108 | }); 109 | kycVerifiedEvent.stopWatching(); 110 | 111 | var approvalEvent = contract.Approval({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 112 | i = 0; 113 | approvalEvent.watch(function (error, result) { 114 | console.log("MAIN: Approval Event " + i++ + ": owner=" + result.args._owner + " spender=" + result.args._spender + " " + 115 | result.args._value.shift(-decimals) + " block=" + result.blockNumber); 116 | }); 117 | approvalEvent.stopWatching(); 118 | 119 | var transferEvent = contract.Transfer({}, { fromBlock: tokenDeploymentBlock, toBlock: latestBlock }); 120 | i = 0; 121 | var totalTokens = new BigNumber(0); 122 | console.log("TRANSFER: No\tFrom\tTo\tTokens\tTokenBalance\tBlock\tTxIndex\tTxHash"); 123 | transferEvent.watch(function (error, result) { 124 | // console.log("TRANSFER: " + JSON.stringify(result)); 125 | totalTokens = totalTokens.add(result.args._value); 126 | console.log("TRANSFER: " + i++ + "\t" + result.args._from + "\t" + result.args._to + "\t" + result.args._value.shift(-decimals) + 127 | "\t" + totalTokens.shift(-decimals) + "\t" + result.blockNumber + "\t" + result.transactionIndex + "\t" + result.transactionHash); 128 | }); 129 | transferEvent.stopWatching(); 130 | 131 | EOF 132 | 133 | grep "MAIN: " $TEMPFILE | sed "s/MAIN: //" > $MAINFILE 134 | grep "TOKENSBOUGHT: " $TEMPFILE | sed "s/TOKENSBOUGHT: //" > $TOKENSBOUGHTFILE 135 | grep "TRANSFER: " $TEMPFILE | sed "s/TRANSFER: //" > $TRANSFERFILE 136 | -------------------------------------------------------------------------------- /test/OpenANXToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | import "./ERC20Interface.sol"; 13 | import "./Owned.sol"; 14 | import "./SafeMath.sol"; 15 | import "./OpenANXTokenConfig.sol"; 16 | import "./LockedTokens.sol"; 17 | 18 | 19 | // ---------------------------------------------------------------------------- 20 | // ERC20 Token, with the addition of symbol, name and decimals 21 | // ---------------------------------------------------------------------------- 22 | contract ERC20Token is ERC20Interface, Owned { 23 | using SafeMath for uint; 24 | 25 | // ------------------------------------------------------------------------ 26 | // symbol(), name() and decimals() 27 | // ------------------------------------------------------------------------ 28 | string public symbol; 29 | string public name; 30 | uint8 public decimals; 31 | 32 | // ------------------------------------------------------------------------ 33 | // Balances for each account 34 | // ------------------------------------------------------------------------ 35 | mapping(address => uint) balances; 36 | 37 | // ------------------------------------------------------------------------ 38 | // Owner of account approves the transfer of an amount to another account 39 | // ------------------------------------------------------------------------ 40 | mapping(address => mapping (address => uint)) allowed; 41 | 42 | 43 | // ------------------------------------------------------------------------ 44 | // Constructor 45 | // ------------------------------------------------------------------------ 46 | function ERC20Token( 47 | string _symbol, 48 | string _name, 49 | uint8 _decimals, 50 | uint _totalSupply 51 | ) Owned() { 52 | symbol = _symbol; 53 | name = _name; 54 | decimals = _decimals; 55 | totalSupply = _totalSupply; 56 | balances[owner] = _totalSupply; 57 | } 58 | 59 | 60 | // ------------------------------------------------------------------------ 61 | // Get the account balance of another account with address _owner 62 | // ------------------------------------------------------------------------ 63 | function balanceOf(address _owner) constant returns (uint balance) { 64 | return balances[_owner]; 65 | } 66 | 67 | 68 | // ------------------------------------------------------------------------ 69 | // Transfer the balance from owner's account to another account 70 | // ------------------------------------------------------------------------ 71 | function transfer(address _to, uint _amount) returns (bool success) { 72 | if (balances[msg.sender] >= _amount // User has balance 73 | && _amount > 0 // Non-zero transfer 74 | && balances[_to] + _amount > balances[_to] // Overflow check 75 | ) { 76 | balances[msg.sender] = balances[msg.sender].sub(_amount); 77 | balances[_to] = balances[_to].add(_amount); 78 | Transfer(msg.sender, _to, _amount); 79 | return true; 80 | } else { 81 | return false; 82 | } 83 | } 84 | 85 | 86 | // ------------------------------------------------------------------------ 87 | // Allow _spender to withdraw from your account, multiple times, up to the 88 | // _value amount. If this function is called again it overwrites the 89 | // current allowance with _value. 90 | // ------------------------------------------------------------------------ 91 | function approve( 92 | address _spender, 93 | uint _amount 94 | ) returns (bool success) { 95 | allowed[msg.sender][_spender] = _amount; 96 | Approval(msg.sender, _spender, _amount); 97 | return true; 98 | } 99 | 100 | 101 | // ------------------------------------------------------------------------ 102 | // Spender of tokens transfer an amount of tokens from the token owner's 103 | // balance to another account. The owner of the tokens must already 104 | // have approve(...)-d this transfer 105 | // ------------------------------------------------------------------------ 106 | function transferFrom( 107 | address _from, 108 | address _to, 109 | uint _amount 110 | ) returns (bool success) { 111 | if (balances[_from] >= _amount // From a/c has balance 112 | && allowed[_from][msg.sender] >= _amount // Transfer approved 113 | && _amount > 0 // Non-zero transfer 114 | && balances[_to] + _amount > balances[_to] // Overflow check 115 | ) { 116 | balances[_from] = balances[_from].sub(_amount); 117 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); 118 | balances[_to] = balances[_to].add(_amount); 119 | Transfer(_from, _to, _amount); 120 | return true; 121 | } else { 122 | return false; 123 | } 124 | } 125 | 126 | 127 | // ------------------------------------------------------------------------ 128 | // Returns the amount of tokens approved by the owner that can be 129 | // transferred to the spender's account 130 | // ------------------------------------------------------------------------ 131 | function allowance( 132 | address _owner, 133 | address _spender 134 | ) constant returns (uint remaining) { 135 | return allowed[_owner][_spender]; 136 | } 137 | } 138 | 139 | 140 | // ---------------------------------------------------------------------------- 141 | // openANX crowdsale token smart contract 142 | // ---------------------------------------------------------------------------- 143 | contract OpenANXToken is ERC20Token, OpenANXTokenConfig { 144 | 145 | // ------------------------------------------------------------------------ 146 | // Has the crowdsale been finalised? 147 | // ------------------------------------------------------------------------ 148 | bool public finalised = false; 149 | 150 | // ------------------------------------------------------------------------ 151 | // Number of tokens per 1,000 ETH 152 | // This can be adjusted as the ETH/USD rate changes 153 | // 154 | // Indicative rate of ETH per token of 0.00290923 at 8 June 2017 155 | // 156 | // This is the same as 1 / 0.00290923 = 343.733565238912015 OAX per ETH 157 | // 158 | // tokensPerEther = 343.733565238912015 159 | // tokensPerKEther = 343,733.565238912015 160 | // tokensPerKEther = 343,734 rounded to an uint, six significant figures 161 | // ------------------------------------------------------------------------ 162 | uint public tokensPerKEther = 343734; 163 | 164 | // ------------------------------------------------------------------------ 165 | // Locked Tokens - holds the 1y and 2y locked tokens information 166 | // ------------------------------------------------------------------------ 167 | LockedTokens public lockedTokens; 168 | 169 | // ------------------------------------------------------------------------ 170 | // Wallet receiving the raised funds 171 | // ------------------------------------------------------------------------ 172 | address public wallet; 173 | 174 | // ------------------------------------------------------------------------ 175 | // Crowdsale participant's accounts need to be KYC verified KYC before 176 | // the participant can move their tokens 177 | // ------------------------------------------------------------------------ 178 | mapping(address => bool) public kycRequired; 179 | 180 | 181 | // ------------------------------------------------------------------------ 182 | // Constructor 183 | // ------------------------------------------------------------------------ 184 | function OpenANXToken(address _wallet) 185 | ERC20Token(SYMBOL, NAME, DECIMALS, 0) 186 | { 187 | wallet = _wallet; 188 | lockedTokens = new LockedTokens(this); 189 | require(address(lockedTokens) != 0x0); 190 | } 191 | 192 | // ------------------------------------------------------------------------ 193 | // openANX can change the crowdsale wallet address 194 | // Can be set at any time before or during the crowdsale 195 | // Not relevant after the crowdsale is finalised as no more contributions 196 | // are accepted 197 | // ------------------------------------------------------------------------ 198 | function setWallet(address _wallet) onlyOwner { 199 | wallet = _wallet; 200 | WalletUpdated(wallet); 201 | } 202 | event WalletUpdated(address newWallet); 203 | 204 | 205 | // ------------------------------------------------------------------------ 206 | // openANX can set number of tokens per 1,000 ETH 207 | // Can only be set before the start of the crowdsale 208 | // ------------------------------------------------------------------------ 209 | function setTokensPerKEther(uint _tokensPerKEther) onlyOwner { 210 | require(now < START_DATE); 211 | require(_tokensPerKEther > 0); 212 | tokensPerKEther = _tokensPerKEther; 213 | TokensPerKEtherUpdated(tokensPerKEther); 214 | } 215 | event TokensPerKEtherUpdated(uint tokensPerKEther); 216 | 217 | 218 | // ------------------------------------------------------------------------ 219 | // Accept ethers to buy tokens during the crowdsale 220 | // ------------------------------------------------------------------------ 221 | function () payable { 222 | proxyPayment(msg.sender); 223 | } 224 | 225 | 226 | // ------------------------------------------------------------------------ 227 | // Accept ethers from one account for tokens to be created for another 228 | // account. Can be used by exchanges to purchase tokens on behalf of 229 | // it's user 230 | // ------------------------------------------------------------------------ 231 | function proxyPayment(address participant) payable { 232 | // No contributions after the crowdsale is finalised 233 | require(!finalised); 234 | 235 | // No contributions before the start of the crowdsale 236 | require(now >= START_DATE); 237 | // No contributions after the end of the crowdsale 238 | require(now <= END_DATE); 239 | 240 | // No contributions below the minimum (can be 0 ETH) 241 | require(msg.value >= CONTRIBUTIONS_MIN); 242 | // No contributions above a maximum (if maximum is set to non-0) 243 | require(CONTRIBUTIONS_MAX == 0 || msg.value < CONTRIBUTIONS_MAX); 244 | 245 | // Calculate number of tokens for contributed ETH 246 | // `18` is the ETH decimals 247 | // `- decimals` is the token decimals 248 | // `+ 3` for the tokens per 1,000 ETH factor 249 | uint tokens = msg.value * tokensPerKEther / 10**uint(18 - decimals + 3); 250 | 251 | // Check if the hard cap will be exceeded 252 | require(totalSupply + tokens <= TOKENS_HARD_CAP); 253 | 254 | // Add tokens purchased to account's balance and total supply 255 | balances[participant] = balances[participant].add(tokens); 256 | totalSupply = totalSupply.add(tokens); 257 | 258 | // Log the tokens purchased 259 | Transfer(0x0, participant, tokens); 260 | TokensBought(participant, msg.value, this.balance, tokens, 261 | totalSupply, tokensPerKEther); 262 | 263 | // KYC verification required before participant can transfer the tokens 264 | kycRequired[participant] = true; 265 | 266 | // Transfer the contributed ethers to the crowdsale wallet 267 | if (!wallet.send(msg.value)) throw; 268 | } 269 | event TokensBought(address indexed buyer, uint ethers, 270 | uint newEtherBalance, uint tokens, uint newTotalSupply, 271 | uint tokensPerKEther); 272 | 273 | 274 | // ------------------------------------------------------------------------ 275 | // openANX to finalise the crowdsale - to adding the locked tokens to 276 | // this contract and the total supply 277 | // ------------------------------------------------------------------------ 278 | function finalise() onlyOwner { 279 | // Can only finalise if raised > soft cap or after the end date 280 | require(totalSupply >= TOKENS_SOFT_CAP || now > END_DATE); 281 | 282 | // Can only finalise once 283 | require(!finalised); 284 | 285 | // Calculate and add remaining tokens to locked balances 286 | lockedTokens.addRemainingTokens(); 287 | 288 | // Allocate locked and premined tokens 289 | balances[address(lockedTokens)] = balances[address(lockedTokens)]. 290 | add(lockedTokens.totalSupplyLocked()); 291 | totalSupply = totalSupply.add(lockedTokens.totalSupplyLocked()); 292 | 293 | // Can only finalise once 294 | finalised = true; 295 | } 296 | 297 | 298 | // ------------------------------------------------------------------------ 299 | // openANX to add precommitment funding token balance before the crowdsale 300 | // commences 301 | // ------------------------------------------------------------------------ 302 | function addPrecommitment(address participant, uint balance) onlyOwner { 303 | require(now < START_DATE); 304 | require(balance > 0); 305 | balances[participant] = balances[participant].add(balance); 306 | totalSupply = totalSupply.add(balance); 307 | Transfer(0x0, participant, balance); 308 | } 309 | event PrecommitmentAdded(address indexed participant, uint balance); 310 | 311 | 312 | // ------------------------------------------------------------------------ 313 | // Transfer the balance from owner's account to another account, with KYC 314 | // verification check for the crowdsale participant's first transfer 315 | // ------------------------------------------------------------------------ 316 | function transfer(address _to, uint _amount) returns (bool success) { 317 | // Cannot transfer before crowdsale ends 318 | require(finalised); 319 | // Cannot transfer if KYC verification is required 320 | require(!kycRequired[msg.sender]); 321 | // Standard transfer 322 | return super.transfer(_to, _amount); 323 | } 324 | 325 | 326 | // ------------------------------------------------------------------------ 327 | // Spender of tokens transfer an amount of tokens from the token owner's 328 | // balance to another account, with KYC verification check for the 329 | // crowdsale participant's first transfer 330 | // ------------------------------------------------------------------------ 331 | function transferFrom(address _from, address _to, uint _amount) 332 | returns (bool success) 333 | { 334 | // Cannot transfer before crowdsale ends 335 | require(finalised); 336 | // Cannot transfer if KYC verification is required 337 | require(!kycRequired[_from]); 338 | // Standard transferFrom 339 | return super.transferFrom(_from, _to, _amount); 340 | } 341 | 342 | 343 | // ------------------------------------------------------------------------ 344 | // openANX to KYC verify the participant's account 345 | // ------------------------------------------------------------------------ 346 | function kycVerify(address participant) onlyOwner { 347 | kycRequired[participant] = false; 348 | KycVerified(participant); 349 | } 350 | event KycVerified(address indexed participant); 351 | 352 | 353 | // ------------------------------------------------------------------------ 354 | // Any account can burn _from's tokens as long as the _from account has 355 | // approved the _amount to be burnt using 356 | // approve(0x0, _amount) 357 | // ------------------------------------------------------------------------ 358 | function burnFrom( 359 | address _from, 360 | uint _amount 361 | ) returns (bool success) { 362 | if (balances[_from] >= _amount // From a/c has balance 363 | && allowed[_from][0x0] >= _amount // Transfer approved 364 | && _amount > 0 // Non-zero transfer 365 | && balances[0x0] + _amount > balances[0x0] // Overflow check 366 | ) { 367 | balances[_from] = balances[_from].sub(_amount); 368 | allowed[_from][0x0] = allowed[_from][0x0].sub(_amount); 369 | balances[0x0] = balances[0x0].add(_amount); 370 | totalSupply = totalSupply.sub(_amount); 371 | Transfer(_from, 0x0, _amount); 372 | return true; 373 | } else { 374 | return false; 375 | } 376 | } 377 | 378 | 379 | // ------------------------------------------------------------------------ 380 | // 1y locked balances for an account 381 | // ------------------------------------------------------------------------ 382 | function balanceOfLocked1Y(address account) constant returns (uint balance) { 383 | return lockedTokens.balanceOfLocked1Y(account); 384 | } 385 | 386 | 387 | // ------------------------------------------------------------------------ 388 | // 2y locked balances for an account 389 | // ------------------------------------------------------------------------ 390 | function balanceOfLocked2Y(address account) constant returns (uint balance) { 391 | return lockedTokens.balanceOfLocked2Y(account); 392 | } 393 | 394 | 395 | // ------------------------------------------------------------------------ 396 | // 1y and 2y locked balances for an account 397 | // ------------------------------------------------------------------------ 398 | function balanceOfLocked(address account) constant returns (uint balance) { 399 | return lockedTokens.balanceOfLocked(account); 400 | } 401 | 402 | 403 | // ------------------------------------------------------------------------ 404 | // 1y locked total supply 405 | // ------------------------------------------------------------------------ 406 | function totalSupplyLocked1Y() constant returns (uint) { 407 | if (finalised) { 408 | return lockedTokens.totalSupplyLocked1Y(); 409 | } else { 410 | return 0; 411 | } 412 | } 413 | 414 | 415 | // ------------------------------------------------------------------------ 416 | // 2y locked total supply 417 | // ------------------------------------------------------------------------ 418 | function totalSupplyLocked2Y() constant returns (uint) { 419 | if (finalised) { 420 | return lockedTokens.totalSupplyLocked2Y(); 421 | } else { 422 | return 0; 423 | } 424 | } 425 | 426 | 427 | // ------------------------------------------------------------------------ 428 | // 1y and 2y locked total supply 429 | // ------------------------------------------------------------------------ 430 | function totalSupplyLocked() constant returns (uint) { 431 | if (finalised) { 432 | return lockedTokens.totalSupplyLocked(); 433 | } else { 434 | return 0; 435 | } 436 | } 437 | 438 | 439 | // ------------------------------------------------------------------------ 440 | // Unlocked total supply 441 | // ------------------------------------------------------------------------ 442 | function totalSupplyUnlocked() constant returns (uint) { 443 | if (finalised && totalSupply >= lockedTokens.totalSupplyLocked()) { 444 | return totalSupply.sub(lockedTokens.totalSupplyLocked()); 445 | } else { 446 | return 0; 447 | } 448 | } 449 | 450 | 451 | // ------------------------------------------------------------------------ 452 | // openANX can transfer out any accidentally sent ERC20 tokens 453 | // ------------------------------------------------------------------------ 454 | function transferAnyERC20Token(address tokenAddress, uint amount) 455 | onlyOwner returns (bool success) 456 | { 457 | return ERC20Interface(tokenAddress).transfer(owner, amount); 458 | } 459 | } -------------------------------------------------------------------------------- /contracts/OpenANXToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | // ---------------------------------------------------------------------------- 4 | // OAX 'openANX Token' crowdfunding contract 5 | // 6 | // Refer to http://openanx.org/ for further information. 7 | // 8 | // Enjoy. (c) openANX and BokkyPooBah / Bok Consulting Pty Ltd 2017. 9 | // The MIT Licence. 10 | // ---------------------------------------------------------------------------- 11 | 12 | import "./ERC20Interface.sol"; 13 | import "./Owned.sol"; 14 | import "./SafeMath.sol"; 15 | import "./OpenANXTokenConfig.sol"; 16 | import "./LockedTokens.sol"; 17 | 18 | 19 | // ---------------------------------------------------------------------------- 20 | // ERC20 Token, with the addition of symbol, name and decimals 21 | // ---------------------------------------------------------------------------- 22 | contract ERC20Token is ERC20Interface, Owned { 23 | using SafeMath for uint; 24 | 25 | // ------------------------------------------------------------------------ 26 | // symbol(), name() and decimals() 27 | // ------------------------------------------------------------------------ 28 | string public symbol; 29 | string public name; 30 | uint8 public decimals; 31 | 32 | // ------------------------------------------------------------------------ 33 | // Balances for each account 34 | // ------------------------------------------------------------------------ 35 | mapping(address => uint) balances; 36 | 37 | // ------------------------------------------------------------------------ 38 | // Owner of account approves the transfer of an amount to another account 39 | // ------------------------------------------------------------------------ 40 | mapping(address => mapping (address => uint)) allowed; 41 | 42 | 43 | // ------------------------------------------------------------------------ 44 | // Constructor 45 | // ------------------------------------------------------------------------ 46 | function ERC20Token( 47 | string _symbol, 48 | string _name, 49 | uint8 _decimals, 50 | uint _totalSupply 51 | ) Owned() { 52 | symbol = _symbol; 53 | name = _name; 54 | decimals = _decimals; 55 | totalSupply = _totalSupply; 56 | balances[owner] = _totalSupply; 57 | } 58 | 59 | 60 | // ------------------------------------------------------------------------ 61 | // Get the account balance of another account with address _owner 62 | // ------------------------------------------------------------------------ 63 | function balanceOf(address _owner) constant returns (uint balance) { 64 | return balances[_owner]; 65 | } 66 | 67 | 68 | // ------------------------------------------------------------------------ 69 | // Transfer the balance from owner's account to another account 70 | // ------------------------------------------------------------------------ 71 | function transfer(address _to, uint _amount) returns (bool success) { 72 | if (balances[msg.sender] >= _amount // User has balance 73 | && _amount > 0 // Non-zero transfer 74 | && balances[_to] + _amount > balances[_to] // Overflow check 75 | ) { 76 | balances[msg.sender] = balances[msg.sender].sub(_amount); 77 | balances[_to] = balances[_to].add(_amount); 78 | Transfer(msg.sender, _to, _amount); 79 | return true; 80 | } else { 81 | return false; 82 | } 83 | } 84 | 85 | 86 | // ------------------------------------------------------------------------ 87 | // Allow _spender to withdraw from your account, multiple times, up to the 88 | // _value amount. If this function is called again it overwrites the 89 | // current allowance with _value. 90 | // ------------------------------------------------------------------------ 91 | function approve( 92 | address _spender, 93 | uint _amount 94 | ) returns (bool success) { 95 | allowed[msg.sender][_spender] = _amount; 96 | Approval(msg.sender, _spender, _amount); 97 | return true; 98 | } 99 | 100 | 101 | // ------------------------------------------------------------------------ 102 | // Spender of tokens transfer an amount of tokens from the token owner's 103 | // balance to another account. The owner of the tokens must already 104 | // have approve(...)-d this transfer 105 | // ------------------------------------------------------------------------ 106 | function transferFrom( 107 | address _from, 108 | address _to, 109 | uint _amount 110 | ) returns (bool success) { 111 | if (balances[_from] >= _amount // From a/c has balance 112 | && allowed[_from][msg.sender] >= _amount // Transfer approved 113 | && _amount > 0 // Non-zero transfer 114 | && balances[_to] + _amount > balances[_to] // Overflow check 115 | ) { 116 | balances[_from] = balances[_from].sub(_amount); 117 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); 118 | balances[_to] = balances[_to].add(_amount); 119 | Transfer(_from, _to, _amount); 120 | return true; 121 | } else { 122 | return false; 123 | } 124 | } 125 | 126 | 127 | // ------------------------------------------------------------------------ 128 | // Returns the amount of tokens approved by the owner that can be 129 | // transferred to the spender's account 130 | // ------------------------------------------------------------------------ 131 | function allowance( 132 | address _owner, 133 | address _spender 134 | ) constant returns (uint remaining) { 135 | return allowed[_owner][_spender]; 136 | } 137 | } 138 | 139 | 140 | // ---------------------------------------------------------------------------- 141 | // openANX crowdsale token smart contract 142 | // ---------------------------------------------------------------------------- 143 | contract OpenANXToken is ERC20Token, OpenANXTokenConfig { 144 | 145 | // ------------------------------------------------------------------------ 146 | // Has the crowdsale been finalised? 147 | // ------------------------------------------------------------------------ 148 | bool public finalised = false; 149 | 150 | // ------------------------------------------------------------------------ 151 | // Number of tokens per 1,000 ETH 152 | // This can be adjusted as the ETH/USD rate changes 153 | // 154 | // Indicative rate of ETH per token of 0.00290923 at 8 June 2017 155 | // 156 | // This is the same as 1 / 0.00290923 = 343.733565238912015 OAX per ETH 157 | // 158 | // tokensPerEther = 343.733565238912015 159 | // tokensPerKEther = 343,733.565238912015 160 | // tokensPerKEther = 343,734 rounded to an uint, six significant figures 161 | // ------------------------------------------------------------------------ 162 | uint public tokensPerKEther = 343734; 163 | 164 | // ------------------------------------------------------------------------ 165 | // Locked Tokens - holds the 1y and 2y locked tokens information 166 | // ------------------------------------------------------------------------ 167 | LockedTokens public lockedTokens; 168 | 169 | // ------------------------------------------------------------------------ 170 | // Wallet receiving the raised funds 171 | // ------------------------------------------------------------------------ 172 | address public wallet; 173 | 174 | // ------------------------------------------------------------------------ 175 | // Crowdsale participant's accounts need to be KYC verified KYC before 176 | // the participant can move their tokens 177 | // ------------------------------------------------------------------------ 178 | mapping(address => bool) public kycRequired; 179 | 180 | 181 | // ------------------------------------------------------------------------ 182 | // Constructor 183 | // ------------------------------------------------------------------------ 184 | function OpenANXToken(address _wallet) 185 | ERC20Token(SYMBOL, NAME, DECIMALS, 0) 186 | { 187 | wallet = _wallet; 188 | lockedTokens = new LockedTokens(this); 189 | require(address(lockedTokens) != 0x0); 190 | } 191 | 192 | // ------------------------------------------------------------------------ 193 | // openANX can change the crowdsale wallet address 194 | // Can be set at any time before or during the crowdsale 195 | // Not relevant after the crowdsale is finalised as no more contributions 196 | // are accepted 197 | // ------------------------------------------------------------------------ 198 | function setWallet(address _wallet) onlyOwner { 199 | wallet = _wallet; 200 | WalletUpdated(wallet); 201 | } 202 | event WalletUpdated(address newWallet); 203 | 204 | 205 | // ------------------------------------------------------------------------ 206 | // openANX can set number of tokens per 1,000 ETH 207 | // Can only be set before the start of the crowdsale 208 | // ------------------------------------------------------------------------ 209 | function setTokensPerKEther(uint _tokensPerKEther) onlyOwner { 210 | require(now < START_DATE); 211 | require(_tokensPerKEther > 0); 212 | tokensPerKEther = _tokensPerKEther; 213 | TokensPerKEtherUpdated(tokensPerKEther); 214 | } 215 | event TokensPerKEtherUpdated(uint tokensPerKEther); 216 | 217 | 218 | // ------------------------------------------------------------------------ 219 | // Accept ethers to buy tokens during the crowdsale 220 | // ------------------------------------------------------------------------ 221 | function () payable { 222 | proxyPayment(msg.sender); 223 | } 224 | 225 | 226 | // ------------------------------------------------------------------------ 227 | // Accept ethers from one account for tokens to be created for another 228 | // account. Can be used by exchanges to purchase tokens on behalf of 229 | // it's user 230 | // ------------------------------------------------------------------------ 231 | function proxyPayment(address participant) payable { 232 | // No contributions after the crowdsale is finalised 233 | require(!finalised); 234 | 235 | // No contributions before the start of the crowdsale 236 | require(now >= START_DATE); 237 | // No contributions after the end of the crowdsale 238 | require(now <= END_DATE); 239 | 240 | // No contributions below the minimum (can be 0 ETH) 241 | require(msg.value >= CONTRIBUTIONS_MIN); 242 | // No contributions above a maximum (if maximum is set to non-0) 243 | require(CONTRIBUTIONS_MAX == 0 || msg.value < CONTRIBUTIONS_MAX); 244 | 245 | // Calculate number of tokens for contributed ETH 246 | // `18` is the ETH decimals 247 | // `- decimals` is the token decimals 248 | // `+ 3` for the tokens per 1,000 ETH factor 249 | uint tokens = msg.value * tokensPerKEther / 10**uint(18 - decimals + 3); 250 | 251 | // Check if the hard cap will be exceeded 252 | require(totalSupply + tokens <= TOKENS_HARD_CAP); 253 | 254 | // Add tokens purchased to account's balance and total supply 255 | balances[participant] = balances[participant].add(tokens); 256 | totalSupply = totalSupply.add(tokens); 257 | 258 | // Log the tokens purchased 259 | Transfer(0x0, participant, tokens); 260 | TokensBought(participant, msg.value, this.balance, tokens, 261 | totalSupply, tokensPerKEther); 262 | 263 | // KYC verification required before participant can transfer the tokens 264 | kycRequired[participant] = true; 265 | 266 | // Transfer the contributed ethers to the crowdsale wallet 267 | if (!wallet.send(msg.value)) throw; 268 | } 269 | event TokensBought(address indexed buyer, uint ethers, 270 | uint newEtherBalance, uint tokens, uint newTotalSupply, 271 | uint tokensPerKEther); 272 | 273 | 274 | // ------------------------------------------------------------------------ 275 | // openANX to finalise the crowdsale - to adding the locked tokens to 276 | // this contract and the total supply 277 | // ------------------------------------------------------------------------ 278 | function finalise() onlyOwner { 279 | // Can only finalise if raised > soft cap or after the end date 280 | require(totalSupply >= TOKENS_SOFT_CAP || now > END_DATE); 281 | 282 | // Can only finalise once 283 | require(!finalised); 284 | 285 | // Calculate and add remaining tokens to locked balances 286 | lockedTokens.addRemainingTokens(); 287 | 288 | // Allocate locked and premined tokens 289 | balances[address(lockedTokens)] = balances[address(lockedTokens)]. 290 | add(lockedTokens.totalSupplyLocked()); 291 | totalSupply = totalSupply.add(lockedTokens.totalSupplyLocked()); 292 | 293 | // Can only finalise once 294 | finalised = true; 295 | } 296 | 297 | 298 | // ------------------------------------------------------------------------ 299 | // openANX to add precommitment funding token balance before the crowdsale 300 | // commences 301 | // ------------------------------------------------------------------------ 302 | function addPrecommitment(address participant, uint balance) onlyOwner { 303 | require(now < START_DATE); 304 | require(balance > 0); 305 | balances[participant] = balances[participant].add(balance); 306 | totalSupply = totalSupply.add(balance); 307 | Transfer(0x0, participant, balance); 308 | } 309 | event PrecommitmentAdded(address indexed participant, uint balance); 310 | 311 | 312 | // ------------------------------------------------------------------------ 313 | // Transfer the balance from owner's account to another account, with KYC 314 | // verification check for the crowdsale participant's first transfer 315 | // ------------------------------------------------------------------------ 316 | function transfer(address _to, uint _amount) returns (bool success) { 317 | // Cannot transfer before crowdsale ends 318 | require(finalised); 319 | // Cannot transfer if KYC verification is required 320 | require(!kycRequired[msg.sender]); 321 | // Standard transfer 322 | return super.transfer(_to, _amount); 323 | } 324 | 325 | 326 | // ------------------------------------------------------------------------ 327 | // Spender of tokens transfer an amount of tokens from the token owner's 328 | // balance to another account, with KYC verification check for the 329 | // crowdsale participant's first transfer 330 | // ------------------------------------------------------------------------ 331 | function transferFrom(address _from, address _to, uint _amount) 332 | returns (bool success) 333 | { 334 | // Cannot transfer before crowdsale ends 335 | require(finalised); 336 | // Cannot transfer if KYC verification is required 337 | require(!kycRequired[_from]); 338 | // Standard transferFrom 339 | return super.transferFrom(_from, _to, _amount); 340 | } 341 | 342 | 343 | // ------------------------------------------------------------------------ 344 | // openANX to KYC verify the participant's account 345 | // ------------------------------------------------------------------------ 346 | function kycVerify(address participant) onlyOwner { 347 | kycRequired[participant] = false; 348 | KycVerified(participant); 349 | } 350 | event KycVerified(address indexed participant); 351 | 352 | 353 | // ------------------------------------------------------------------------ 354 | // Any account can burn _from's tokens as long as the _from account has 355 | // approved the _amount to be burnt using 356 | // approve(0x0, _amount) 357 | // ------------------------------------------------------------------------ 358 | function burnFrom( 359 | address _from, 360 | uint _amount 361 | ) returns (bool success) { 362 | if (balances[_from] >= _amount // From a/c has balance 363 | && allowed[_from][0x0] >= _amount // Transfer approved 364 | && _amount > 0 // Non-zero transfer 365 | && balances[0x0] + _amount > balances[0x0] // Overflow check 366 | ) { 367 | balances[_from] = balances[_from].sub(_amount); 368 | allowed[_from][0x0] = allowed[_from][0x0].sub(_amount); 369 | balances[0x0] = balances[0x0].add(_amount); 370 | totalSupply = totalSupply.sub(_amount); 371 | Transfer(_from, 0x0, _amount); 372 | return true; 373 | } else { 374 | return false; 375 | } 376 | } 377 | 378 | 379 | // ------------------------------------------------------------------------ 380 | // 1y locked balances for an account 381 | // ------------------------------------------------------------------------ 382 | function balanceOfLocked1Y(address account) constant returns (uint balance) { 383 | return lockedTokens.balanceOfLocked1Y(account); 384 | } 385 | 386 | 387 | // ------------------------------------------------------------------------ 388 | // 2y locked balances for an account 389 | // ------------------------------------------------------------------------ 390 | function balanceOfLocked2Y(address account) constant returns (uint balance) { 391 | return lockedTokens.balanceOfLocked2Y(account); 392 | } 393 | 394 | 395 | // ------------------------------------------------------------------------ 396 | // 1y and 2y locked balances for an account 397 | // ------------------------------------------------------------------------ 398 | function balanceOfLocked(address account) constant returns (uint balance) { 399 | return lockedTokens.balanceOfLocked(account); 400 | } 401 | 402 | 403 | // ------------------------------------------------------------------------ 404 | // 1y locked total supply 405 | // ------------------------------------------------------------------------ 406 | function totalSupplyLocked1Y() constant returns (uint) { 407 | if (finalised) { 408 | return lockedTokens.totalSupplyLocked1Y(); 409 | } else { 410 | return 0; 411 | } 412 | } 413 | 414 | 415 | // ------------------------------------------------------------------------ 416 | // 2y locked total supply 417 | // ------------------------------------------------------------------------ 418 | function totalSupplyLocked2Y() constant returns (uint) { 419 | if (finalised) { 420 | return lockedTokens.totalSupplyLocked2Y(); 421 | } else { 422 | return 0; 423 | } 424 | } 425 | 426 | 427 | // ------------------------------------------------------------------------ 428 | // 1y and 2y locked total supply 429 | // ------------------------------------------------------------------------ 430 | function totalSupplyLocked() constant returns (uint) { 431 | if (finalised) { 432 | return lockedTokens.totalSupplyLocked(); 433 | } else { 434 | return 0; 435 | } 436 | } 437 | 438 | 439 | // ------------------------------------------------------------------------ 440 | // Unlocked total supply 441 | // ------------------------------------------------------------------------ 442 | function totalSupplyUnlocked() constant returns (uint) { 443 | if (finalised && totalSupply >= lockedTokens.totalSupplyLocked()) { 444 | return totalSupply.sub(lockedTokens.totalSupplyLocked()); 445 | } else { 446 | return 0; 447 | } 448 | } 449 | 450 | 451 | // ------------------------------------------------------------------------ 452 | // openANX can transfer out any accidentally sent ERC20 tokens 453 | // ------------------------------------------------------------------------ 454 | function transferAnyERC20Token(address tokenAddress, uint amount) 455 | onlyOwner returns (bool success) 456 | { 457 | return ERC20Interface(tokenAddress).transfer(owner, amount); 458 | } 459 | } -------------------------------------------------------------------------------- /test/01_test1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ---------------------------------------------------------------------------------------------- 3 | # Testing the smart contract 4 | # 5 | # Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2017. The MIT Licence. 6 | # ---------------------------------------------------------------------------------------------- 7 | 8 | MODE=${1:-test} 9 | 10 | GETHATTACHPOINT=`grep ^IPCFILE= settings.txt | sed "s/^.*=//"` 11 | PASSWORD=`grep ^PASSWORD= settings.txt | sed "s/^.*=//"` 12 | ERC20INTERFACESOL=`grep ^ERC20INTERFACESOL= settings.txt | sed "s/^.*=//"` 13 | ERC20INTERFACETEMPSOL=`grep ^ERC20INTERFACETEMPSOL= settings.txt | sed "s/^.*=//"` 14 | OWNEDSOL=`grep ^OWNEDSOL= settings.txt | sed "s/^.*=//"` 15 | OWNEDTEMPSOL=`grep ^OWNEDTEMPSOL= settings.txt | sed "s/^.*=//"` 16 | SAFEMATHSOL=`grep ^SAFEMATHSOL= settings.txt | sed "s/^.*=//"` 17 | SAFEMATHTEMPSOL=`grep ^SAFEMATHTEMPSOL= settings.txt | sed "s/^.*=//"` 18 | LOCKEDTOKENSSOL=`grep ^LOCKEDTOKENSSOL= settings.txt | sed "s/^.*=//"` 19 | LOCKEDTOKENSTEMPSOL=`grep ^LOCKEDTOKENSTEMPSOL= settings.txt | sed "s/^.*=//"` 20 | TOKENCONFIGSOL=`grep ^TOKENCONFIGSOL= settings.txt | sed "s/^.*=//"` 21 | TOKENCONFIGTEMPSOL=`grep ^TOKENCONFIGTEMPSOL= settings.txt | sed "s/^.*=//"` 22 | TOKENSOL=`grep ^TOKENSOL= settings.txt | sed "s/^.*=//"` 23 | TOKENTEMPSOL=`grep ^TOKENTEMPSOL= settings.txt | sed "s/^.*=//"` 24 | TOKENJS=`grep ^TOKENJS= settings.txt | sed "s/^.*=//"` 25 | DEPLOYMENTDATA=`grep ^DEPLOYMENTDATA= settings.txt | sed "s/^.*=//"` 26 | 27 | INCLUDEJS=`grep ^INCLUDEJS= settings.txt | sed "s/^.*=//"` 28 | TEST1OUTPUT=`grep ^TEST1OUTPUT= settings.txt | sed "s/^.*=//"` 29 | TEST1RESULTS=`grep ^TEST1RESULTS= settings.txt | sed "s/^.*=//"` 30 | 31 | CURRENTTIME=`date +%s` 32 | CURRENTTIMES=`date -r $CURRENTTIME -u` 33 | 34 | if [ "$MODE" == "dev" ]; then 35 | # Start time now 36 | STARTTIME=`echo "$CURRENTTIME" | bc` 37 | else 38 | # Start time 1m 10s in the future 39 | STARTTIME=`echo "$CURRENTTIME+70" | bc` 40 | fi 41 | STARTTIME_S=`date -r $STARTTIME -u` 42 | ENDTIME=`echo "$CURRENTTIME+60*3" | bc` 43 | ENDTIME_S=`date -r $ENDTIME -u` 44 | 45 | printf "MODE = '$MODE'\n" 46 | printf "GETHATTACHPOINT = '$GETHATTACHPOINT'\n" 47 | printf "PASSWORD = '$PASSWORD'\n" 48 | printf "ERC20INTERFACESOL = '$ERC20INTERFACESOL'\n" 49 | printf "ERC20INTERFACETEMPSOL = '$ERC20INTERFACETEMPSOL'\n" 50 | printf "OWNEDSOL = '$OWNEDSOL'\n" 51 | printf "OWNEDTEMPSOL = '$OWNEDTEMPSOL'\n" 52 | printf "SAFEMATHSOL = '$SAFEMATHSOL'\n" 53 | printf "SAFEMATHTEMPSOL = '$SAFEMATHTEMPSOL'\n" 54 | printf "LOCKEDTOKENSSOL = '$LOCKEDTOKENSSOL'\n" 55 | printf "LOCKEDTOKENSTEMPSOL = '$LOCKEDTOKENSTEMPSOL'\n" 56 | printf "TOKENCONFIGSOL = '$TOKENCONFIGSOL'\n" 57 | printf "TOKENCONFIGTEMPSOL = '$TOKENCONFIGTEMPSOL'\n" 58 | printf "TOKENSOL = '$TOKENSOL'\n" 59 | printf "TOKENTEMPSOL = '$TOKENTEMPSOL'\n" 60 | printf "TOKENJS = '$TOKENJS'\n" 61 | printf "DEPLOYMENTDATA = '$DEPLOYMENTDATA'\n" 62 | printf "INCLUDEJS = '$INCLUDEJS'\n" 63 | printf "TEST1OUTPUT = '$TEST1OUTPUT'\n" 64 | printf "TEST1RESULTS = '$TEST1RESULTS'\n" 65 | printf "CURRENTTIME = '$CURRENTTIME' '$CURRENTTIMES'\n" 66 | printf "STARTTIME = '$STARTTIME' '$STARTTIME_S'\n" 67 | printf "ENDTIME = '$ENDTIME' '$ENDTIME_S'\n" 68 | 69 | # Make copy of SOL file and modify start and end times --- 70 | `cp $ERC20INTERFACESOL $ERC20INTERFACETEMPSOL` 71 | `cp $OWNEDSOL $OWNEDTEMPSOL` 72 | `cp $SAFEMATHSOL $SAFEMATHTEMPSOL` 73 | `cp $LOCKEDTOKENSSOL $LOCKEDTOKENSTEMPSOL` 74 | `cp $TOKENCONFIGSOL $TOKENCONFIGTEMPSOL` 75 | `cp $TOKENSOL $TOKENTEMPSOL` 76 | 77 | # --- Modify dates --- 78 | # PRESALE_START_DATE = +1m 79 | `perl -pi -e "s/START_DATE = 1498136400;/START_DATE = $STARTTIME; \/\/ $STARTTIME_S/" $TOKENCONFIGTEMPSOL` 80 | `perl -pi -e "s/END_DATE = 1500728400;/END_DATE = $ENDTIME; \/\/ $ENDTIME_S/" $TOKENCONFIGTEMPSOL` 81 | `perl -pi -e "s/LOCKED_1Y_DATE \= START_DATE \+ 365 days;/LOCKED_1Y_DATE \= START_DATE \+ 3 minutes;/" $TOKENCONFIGTEMPSOL` 82 | `perl -pi -e "s/LOCKED_2Y_DATE \= START_DATE \+ 2 \* 365 days;/LOCKED_2Y_DATE \= START_DATE \+ 4 minutes;/" $TOKENCONFIGTEMPSOL` 83 | 84 | # --- Un-internal safeMaths --- 85 | #`perl -pi -e "s/internal/constant/" $TOKENTEMPSOL` 86 | 87 | DIFFS=`diff $TOKENCONFIGSOL $TOKENCONFIGTEMPSOL` 88 | echo "--- Differences ---" 89 | echo "$DIFFS" 90 | 91 | echo "var tokenOutput=`solc --optimize --combined-json abi,bin,interface $TOKENTEMPSOL`;" > $TOKENJS 92 | 93 | geth --verbosity 3 attach $GETHATTACHPOINT << EOF | tee $TEST1OUTPUT 94 | loadScript("$TOKENJS"); 95 | loadScript("functions.js"); 96 | 97 | var tokenAbi = JSON.parse(tokenOutput.contracts["$TOKENTEMPSOL:OpenANXToken"].abi); 98 | var tokenBin = "0x" + tokenOutput.contracts["$TOKENTEMPSOL:OpenANXToken"].bin; 99 | var lockedTokensAbi = JSON.parse(tokenOutput.contracts["$LOCKEDTOKENSTEMPSOL:LockedTokens"].abi); 100 | 101 | console.log("DATA: tokenABI=" + JSON.stringify(tokenAbi)); 102 | console.log("DATA: lockedTokensAbi=" + JSON.stringify(lockedTokensAbi)); 103 | 104 | unlockAccounts("$PASSWORD"); 105 | printBalances(); 106 | console.log("RESULT: "); 107 | 108 | var skipKycContract = "$MODE" == "dev" ? true : false; 109 | var skipSafeMath = "$MODE" == "dev" ? true : false; 110 | 111 | // ----------------------------------------------------------------------------- 112 | var testMessage = "Test 1.1 Deploy Token Contract"; 113 | console.log("RESULT: " + testMessage); 114 | var tokenContract = web3.eth.contract(tokenAbi); 115 | console.log(JSON.stringify(tokenContract)); 116 | var tokenTx = null; 117 | var tokenAddress = null; 118 | var token = tokenContract.new(tokenOwnerAccount, {from: tokenOwnerAccount, data: tokenBin, gas: 6000000}, 119 | function(e, contract) { 120 | if (!e) { 121 | if (!contract.address) { 122 | tokenTx = contract.transactionHash; 123 | } else { 124 | tokenAddress = contract.address; 125 | addAccount(tokenAddress, token.symbol() + " '" + token.name() + "' *"); 126 | addAccount(token.lockedTokens(), "Locked Tokens"); 127 | addTokenContractAddressAndAbi(tokenAddress, tokenAbi, lockedTokensAbi); 128 | console.log("DATA: tokenAddress=" + tokenAddress); 129 | } 130 | } 131 | } 132 | ); 133 | while (txpool.status.pending > 0) { 134 | } 135 | printTxData("tokenAddress=" + tokenAddress, tokenTx); 136 | printBalances(); 137 | failIfGasEqualsGasUsed(tokenTx, testMessage); 138 | printTokenContractStaticDetails(); 139 | printTokenContractDynamicDetails(); 140 | console.log("RESULT: "); 141 | console.log(JSON.stringify(token)); 142 | 143 | 144 | // ----------------------------------------------------------------------------- 145 | var testMessage = "Test 1.2 Precommitments, TokensPerKEther, Wallet"; 146 | console.log("RESULT: " + testMessage); 147 | var tx1_2_1 = token.addPrecommitment(precommitmentsAccount, "10000000000000000000000000", {from: tokenOwnerAccount, gas: 4000000}); 148 | var tx1_2_2 = token.setTokensPerKEther("1000000", {from: tokenOwnerAccount, gas: 4000000}); 149 | var tx1_2_3 = token.setWallet(crowdfundWallet, {from: tokenOwnerAccount, gas: 4000000}); 150 | while (txpool.status.pending > 0) { 151 | } 152 | printTxData("tx1_2_1", tx1_2_1); 153 | printTxData("tx1_2_2", tx1_2_2); 154 | printTxData("tx1_2_3", tx1_2_3); 155 | printBalances(); 156 | failIfGasEqualsGasUsed(tx1_2_1, testMessage + " - precommitments"); 157 | failIfGasEqualsGasUsed(tx1_2_2, testMessage + " - tokensPerKEther Rate From 343,734 To 1,000,000"); 158 | failIfGasEqualsGasUsed(tx1_2_3, testMessage + " - change crowdsale wallet"); 159 | printTokenContractDynamicDetails(); 160 | console.log("RESULT: "); 161 | 162 | 163 | // ----------------------------------------------------------------------------- 164 | // Wait for crowdsale start 165 | // ----------------------------------------------------------------------------- 166 | var startDateTime = token.START_DATE(); 167 | var startDateTimeDate = new Date(startDateTime * 1000); 168 | console.log("RESULT: Waiting until start date at " + startDateTime + " " + startDateTimeDate + 169 | " currentDate=" + new Date()); 170 | while ((new Date()).getTime() <= startDateTimeDate.getTime()) { 171 | } 172 | console.log("RESULT: Waited until start date at " + startDateTime + " " + startDateTimeDate + 173 | " currentDate=" + new Date()); 174 | 175 | 176 | // ----------------------------------------------------------------------------- 177 | var testMessage = "Test 2.1 Buy tokens"; 178 | console.log("RESULT: " + testMessage); 179 | var tx2_1_1 = eth.sendTransaction({from: account2, to: tokenAddress, gas: 400000, value: web3.toWei("100", "ether")}); 180 | var tx2_1_2 = eth.sendTransaction({from: account3, to: tokenAddress, gas: 400000, value: web3.toWei("1000", "ether")}); 181 | var tx2_1_3 = eth.sendTransaction({from: account4, to: tokenAddress, gas: 400000, value: web3.toWei("10000", "ether")}); 182 | var tx2_1_4 = eth.sendTransaction({from: directorsAccount, to: tokenAddress, gas: 400000, value: web3.toWei("1000", "ether")}); 183 | var tx2_1_5 = token.proxyPayment(account6, {from: account5, to: tokenAddress, gas: 400000, value: web3.toWei("0.5", "ether")}); 184 | while (txpool.status.pending > 0) { 185 | } 186 | printTxData("tx2_1_1", tx2_1_1); 187 | printTxData("tx2_1_2", tx2_1_2); 188 | printTxData("tx2_1_3", tx2_1_3); 189 | printTxData("tx2_1_4", tx2_1_4); 190 | printTxData("tx2_1_5", tx2_1_5); 191 | printBalances(); 192 | failIfGasEqualsGasUsed(tx2_1_1, testMessage + " - account2 buys 100,000 OAX for 100 ETH"); 193 | failIfGasEqualsGasUsed(tx2_1_2, testMessage + " - account3 buys 1,000,000 OAX for 1,000 ETH"); 194 | failIfGasEqualsGasUsed(tx2_1_3, testMessage + " - account4 buys 10,000,000 OAX for 10,000 ETH"); 195 | failIfGasEqualsGasUsed(tx2_1_4, testMessage + " - directorsAccount buys 1,000,000 OAX for 1,000 ETH"); 196 | failIfGasEqualsGasUsed(tx2_1_5, testMessage + " - account5 buys 500 OAX for 0.5 ETH on behalf of account6"); 197 | printTokenContractDynamicDetails(); 198 | console.log("RESULT: "); 199 | 200 | 201 | // ----------------------------------------------------------------------------- 202 | var testMessage = "Test 3.1 Cannot Move Tokens Without Finalisation And KYC Verification"; 203 | console.log("RESULT: " + testMessage); 204 | var tx3_1_1 = token.transfer(account5, "1000000000000", {from: account2, gas: 100000}); 205 | var tx3_1_2 = token.transfer(account6, "200000000000000", {from: account4, gas: 100000}); 206 | var tx3_1_3 = token.approve(account7, "30000000000000000", {from: account3, gas: 100000}); 207 | var tx3_1_4 = token.approve(account8, "4000000000000000000", {from: account4, gas: 100000}); 208 | while (txpool.status.pending > 0) { 209 | } 210 | var tx3_1_5 = token.transferFrom(account3, account7, "30000000000000000", {from: account7, gas: 100000}); 211 | var tx3_1_6 = token.transferFrom(account4, account8, "4000000000000000000", {from: account8, gas: 100000}); 212 | while (txpool.status.pending > 0) { 213 | } 214 | printTxData("tx3_1_1", tx3_1_1); 215 | printTxData("tx3_1_2", tx3_1_2); 216 | printTxData("tx3_1_3", tx3_1_3); 217 | printTxData("tx3_1_4", tx3_1_4); 218 | printTxData("tx3_1_5", tx3_1_5); 219 | printTxData("tx3_1_6", tx3_1_6); 220 | printBalances(); 221 | passIfGasEqualsGasUsed(tx3_1_1, testMessage + " - transfer 0.000001 OAX ac2 -> ac5. CHECK no movement"); 222 | passIfGasEqualsGasUsed(tx3_1_2, testMessage + " - transfer 0.0002 OAX ac4 -> ac6. CHECK no movement"); 223 | failIfGasEqualsGasUsed(tx3_1_3, testMessage + " - approve 0.03 OAX ac3 -> ac7"); 224 | failIfGasEqualsGasUsed(tx3_1_4, testMessage + " - approve 4 OAX ac4 -> ac8"); 225 | passIfGasEqualsGasUsed(tx3_1_5, testMessage + " - transferFrom 0.03 OAX ac3 -> ac5. CHECK no movement"); 226 | passIfGasEqualsGasUsed(tx3_1_6, testMessage + " - transferFrom 4 OAX ac4 -> ac6. CHECK no movement"); 227 | printTokenContractDynamicDetails(); 228 | console.log("RESULT: "); 229 | 230 | 231 | // ----------------------------------------------------------------------------- 232 | var testMessage = "Test 4.1 Finalise crowdsale"; 233 | console.log("RESULT: " + testMessage); 234 | var tx4_1 = token.finalise({from: tokenOwnerAccount, gas: 4000000}); 235 | while (txpool.status.pending > 0) { 236 | } 237 | printTxData("tx4_1", tx4_1); 238 | printBalances(); 239 | failIfGasEqualsGasUsed(tx4_1, testMessage); 240 | printTokenContractDynamicDetails(); 241 | console.log("RESULT: "); 242 | 243 | 244 | // ----------------------------------------------------------------------------- 245 | var testMessage = "Test 5.1 KYC Verify"; 246 | console.log("RESULT: " + testMessage); 247 | var tx5_1_1 = token.kycVerify(account2, {from: tokenOwnerAccount, gas: 4000000}); 248 | var tx5_1_2 = token.kycVerify(account3, {from: tokenOwnerAccount, gas: 4000000}); 249 | while (txpool.status.pending > 0) { 250 | } 251 | printTxData("tx5_1_1", tx5_1_1); 252 | printTxData("tx5_1_2", tx5_1_2); 253 | printBalances(); 254 | failIfGasEqualsGasUsed(tx5_1_1, testMessage + " - account2"); 255 | failIfGasEqualsGasUsed(tx5_1_2, testMessage + " - account3"); 256 | printTokenContractDynamicDetails(); 257 | console.log("RESULT: "); 258 | 259 | 260 | // ----------------------------------------------------------------------------- 261 | var testMessage = "Test 6.1 Move Tokens After Finalising"; 262 | console.log("RESULT: " + testMessage); 263 | console.log("RESULT: kyc(account3)=" + token.kycRequired(account3)); 264 | console.log("RESULT: kyc(account4)=" + token.kycRequired(account4)); 265 | var tx6_1_1 = token.transfer(account5, "1000000000000", {from: account2, gas: 100000}); 266 | var tx6_1_2 = token.transfer(account6, "200000000000000", {from: account4, gas: 100000}); 267 | var tx6_1_3 = token.approve(account7, "30000000000000000", {from: account3, gas: 100000}); 268 | var tx6_1_4 = token.approve(account8, "4000000000000000000", {from: account4, gas: 100000}); 269 | while (txpool.status.pending > 0) { 270 | } 271 | var tx6_1_5 = token.transferFrom(account3, account7, "30000000000000000", {from: account7, gas: 100000}); 272 | var tx6_1_6 = token.transferFrom(account4, account8, "4000000000000000000", {from: account8, gas: 100000}); 273 | while (txpool.status.pending > 0) { 274 | } 275 | printTxData("tx6_1_1", tx6_1_1); 276 | printTxData("tx6_1_2", tx6_1_2); 277 | printTxData("tx6_1_3", tx6_1_3); 278 | printTxData("tx6_1_4", tx6_1_4); 279 | printTxData("tx6_1_5", tx6_1_5); 280 | printTxData("tx6_1_6", tx6_1_6); 281 | printBalances(); 282 | failIfGasEqualsGasUsed(tx6_1_1, testMessage + " - transfer 0.000001 OAX ac2 -> ac5. CHECK for movement"); 283 | passIfGasEqualsGasUsed(tx6_1_2, testMessage + " - transfer 0.0002 OAX ac4 -> ac5. CHECK no movement"); 284 | failIfGasEqualsGasUsed(tx6_1_3, testMessage + " - approve 0.03 OAX ac3 -> ac5"); 285 | failIfGasEqualsGasUsed(tx6_1_4, testMessage + " - approve 4 OAX ac4 -> ac5"); 286 | failIfGasEqualsGasUsed(tx6_1_5, testMessage + " - transferFrom 0.03 OAX ac3 -> ac5. CHECK for movement"); 287 | passIfGasEqualsGasUsed(tx6_1_6, testMessage + " - transferFrom 4 OAX ac4 -> ac6. CHECK no movement"); 288 | printTokenContractDynamicDetails(); 289 | console.log("RESULT: "); 290 | 291 | 292 | // ----------------------------------------------------------------------------- 293 | // Wait for 1Y unlocked date 294 | // ----------------------------------------------------------------------------- 295 | var locked1YDateTime = token.LOCKED_1Y_DATE(); 296 | var locked1YDateTimeDate = new Date(locked1YDateTime * 1000); 297 | console.log("RESULT: Waiting until locked 1Y date at " + locked1YDateTime + " " + locked1YDateTimeDate + 298 | " currentDate=" + new Date()); 299 | while ((new Date()).getTime() <= locked1YDateTimeDate.getTime()) { 300 | } 301 | console.log("RESULT: Waited until locked 1Y date at " + locked1YDateTime + " " + locked1YDateTimeDate + 302 | " currentDate=" + new Date()); 303 | 304 | 305 | var lockedTokens = eth.contract(lockedTokensAbi).at(token.lockedTokens()); 306 | 307 | 308 | // ----------------------------------------------------------------------------- 309 | var testMessage = "Test 7.1 Unlock 1Y Locked Token"; 310 | console.log("RESULT: " + testMessage); 311 | var tx7_1_1 = lockedTokens.unlock1Y({from: earlyBackersAccount, gas: 4000000}); 312 | while (txpool.status.pending > 0) { 313 | } 314 | printTxData("tx7_1_1", tx7_1_1); 315 | printBalances(); 316 | failIfGasEqualsGasUsed(tx7_1_1, testMessage); 317 | printTokenContractDynamicDetails(); 318 | console.log("RESULT: "); 319 | 320 | 321 | // ----------------------------------------------------------------------------- 322 | var testMessage = "Test 7.2 Unsuccessfully Unlock 2Y Locked Token"; 323 | console.log("RESULT: " + testMessage); 324 | var tx7_2_1 = lockedTokens.unlock2Y({from: earlyBackersAccount, gas: 4000000}); 325 | while (txpool.status.pending > 0) { 326 | } 327 | printTxData("tx7_2_1", tx7_2_1); 328 | printBalances(); 329 | passIfGasEqualsGasUsed(tx7_2_1, testMessage); 330 | printTokenContractDynamicDetails(); 331 | console.log("RESULT: "); 332 | 333 | 334 | // ----------------------------------------------------------------------------- 335 | // Wait for 2Y unlocked date 336 | // ----------------------------------------------------------------------------- 337 | var locked2YDateTime = token.LOCKED_2Y_DATE(); 338 | var locked2YDateTimeDate = new Date(locked2YDateTime * 1000); 339 | console.log("RESULT: Waiting until locked 2Y date at " + locked2YDateTime + " " + locked2YDateTimeDate + 340 | " currentDate=" + new Date()); 341 | while ((new Date()).getTime() <= locked2YDateTimeDate.getTime()) { 342 | } 343 | console.log("RESULT: Waited until locked 2Y date at " + locked2YDateTime + " " + locked2YDateTimeDate + 344 | " currentDate=" + new Date()); 345 | 346 | 347 | // ----------------------------------------------------------------------------- 348 | var testMessage = "Test 8.1 Successfully Unlock 2Y Locked Token"; 349 | console.log("RESULT: " + testMessage); 350 | var tx8_1_1 = lockedTokens.unlock2Y({from: earlyBackersAccount, gas: 4000000}); 351 | while (txpool.status.pending > 0) { 352 | } 353 | printTxData("tx8_1_1", tx8_1_1); 354 | printBalances(); 355 | failIfGasEqualsGasUsed(tx8_1_1, testMessage); 356 | printTokenContractDynamicDetails(); 357 | console.log("RESULT: "); 358 | 359 | 360 | // ----------------------------------------------------------------------------- 361 | var testMessage = "Test 8.2 Successfully Unlock All Tokens including Tranche 1 remaining + Tranche 2 30M"; 362 | console.log("RESULT: " + testMessage); 363 | var tx8_2_1 = lockedTokens.unlock2Y({from: foundationAccount, gas: 4000000}); 364 | var tx8_2_2 = lockedTokens.unlock1Y({from: advisorsAccount, gas: 4000000}); 365 | var tx8_2_3 = lockedTokens.unlock2Y({from: advisorsAccount, gas: 4000000}); 366 | var tx8_2_4 = lockedTokens.unlock1Y({from: directorsAccount, gas: 4000000}); 367 | var tx8_2_5 = lockedTokens.unlock2Y({from: directorsAccount, gas: 4000000}); 368 | var tx8_2_6 = lockedTokens.unlock1Y({from: developersAccount, gas: 4000000}); 369 | var tx8_2_7 = lockedTokens.unlock1Y({from: tranche2Account, gas: 4000000}); 370 | while (txpool.status.pending > 0) { 371 | } 372 | printTxData("tx8_2_1", tx8_2_1); 373 | printTxData("tx8_2_2", tx8_2_2); 374 | printTxData("tx8_2_3", tx8_2_3); 375 | printTxData("tx8_2_4", tx8_2_4); 376 | printTxData("tx8_2_5", tx8_2_5); 377 | printTxData("tx8_2_6", tx8_2_6); 378 | printTxData("tx8_2_7", tx8_2_7); 379 | printBalances(); 380 | failIfGasEqualsGasUsed(tx8_2_1, testMessage); 381 | failIfGasEqualsGasUsed(tx8_2_2, testMessage); 382 | failIfGasEqualsGasUsed(tx8_2_3, testMessage); 383 | failIfGasEqualsGasUsed(tx8_2_4, testMessage); 384 | failIfGasEqualsGasUsed(tx8_2_5, testMessage); 385 | failIfGasEqualsGasUsed(tx8_2_6, testMessage); 386 | failIfGasEqualsGasUsed(tx8_2_7, testMessage); 387 | printTokenContractDynamicDetails(); 388 | console.log("RESULT: "); 389 | 390 | 391 | // ----------------------------------------------------------------------------- 392 | var testMessage = "Test 9.1 Burn Tokens"; 393 | console.log("RESULT: " + testMessage); 394 | var tx9_1_1 = token.burnFrom(account5, "100000000000000", {from: account2, gas: 100000}); 395 | var tx9_1_2 = token.transfer(account6, "20000000000000000", {from: account6, gas: 100000}); 396 | var tx9_1_3 = token.approve("0x0", "3000000000000000000", {from: account3, gas: 100000}); 397 | var tx9_1_4 = token.approve("0x0", "400000000000000000000", {from: account4, gas: 100000}); 398 | while (txpool.status.pending > 0) { 399 | } 400 | var tx9_1_5 = token.burnFrom(account3, "3000000000000000000", {from: account3, gas: 100000}); 401 | var tx9_1_6 = token.burnFrom(account4, "400000000000000000000", {from: account8, gas: 100000}); 402 | while (txpool.status.pending > 0) { 403 | } 404 | printTxData("tx9_1_1", tx9_1_1); 405 | printTxData("tx9_1_2", tx9_1_2); 406 | printTxData("tx9_1_3", tx9_1_3); 407 | printTxData("tx9_1_4", tx9_1_4); 408 | printTxData("tx9_1_5", tx9_1_5); 409 | printTxData("tx9_1_6", tx9_1_6); 410 | printBalances(); 411 | failIfGasEqualsGasUsed(tx9_1_1, testMessage + " - burn 0.0001 OAX ac2. CHECK no movement"); 412 | passIfGasEqualsGasUsed(tx9_1_2, testMessage + " - burn 0.02 OAX ac6. CHECK no movement"); 413 | failIfGasEqualsGasUsed(tx9_1_3, testMessage + " - approve burn 3 OAX ac3"); 414 | failIfGasEqualsGasUsed(tx9_1_4, testMessage + " - approve burn 400 OAX ac4"); 415 | failIfGasEqualsGasUsed(tx9_1_5, testMessage + " - burn 3 OAX ac3 from ac3. CHECK for movement"); 416 | failIfGasEqualsGasUsed(tx9_1_6, testMessage + " - burn 400 OAX ac4 from ac8. CHECK for movement"); 417 | printTokenContractDynamicDetails(); 418 | console.log("RESULT: "); 419 | 420 | 421 | // ----------------------------------------------------------------------------- 422 | var testMessage = "Test 10.1 Change Ownership"; 423 | console.log("RESULT: " + testMessage); 424 | var tx10_1_1 = token.transferOwnership(minerAccount, {from: tokenOwnerAccount, gas: 100000}); 425 | while (txpool.status.pending > 0) { 426 | } 427 | var tx10_1_2 = token.acceptOwnership({from: minerAccount, gas: 100000}); 428 | while (txpool.status.pending > 0) { 429 | } 430 | printTxData("tx10_1_1", tx10_1_1); 431 | printTxData("tx10_1_2", tx10_1_2); 432 | printBalances(); 433 | failIfGasEqualsGasUsed(tx10_1_1, testMessage + " - Change owner"); 434 | failIfGasEqualsGasUsed(tx10_1_2, testMessage + " - Accept ownership"); 435 | printTokenContractDynamicDetails(); 436 | console.log("RESULT: "); 437 | 438 | exit; 439 | 440 | 441 | // TODO: Update test for this 442 | if (!skipSafeMath && false) { 443 | // ----------------------------------------------------------------------------- 444 | // Notes: 445 | // = To simulate failure, comment out the throw lines in safeAdd() and safeSub() 446 | // 447 | var testMessage = "Test 2.0 Safe Maths"; 448 | console.log("RESULT: " + testMessage); 449 | console.log(JSON.stringify(token)); 450 | var result = token.safeAdd("1", "2"); 451 | if (result == 3) { 452 | console.log("RESULT: PASS safeAdd(1, 2) = 3"); 453 | } else { 454 | console.log("RESULT: FAIL safeAdd(1, 2) <> 3"); 455 | } 456 | 457 | var minusOneInt = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; 458 | result = token.safeAdd(minusOneInt, "124"); 459 | if (result == 0) { 460 | console.log("RESULT: PASS safeAdd(" + minusOneInt + ", 124) = 0. Result=" + result); 461 | } else { 462 | console.log("RESULT: FAIL safeAdd(" + minusOneInt + ", 124) = 123. Result=" + result); 463 | } 464 | 465 | result = token.safeAdd("124", minusOneInt); 466 | if (result == 0) { 467 | console.log("RESULT: PASS safeAdd(124, " + minusOneInt + ") = 0. Result=" + result); 468 | } else { 469 | console.log("RESULT: FAIL safeAdd(124, " + minusOneInt + ") = 123. Result=" + result); 470 | } 471 | 472 | result = token.safeSub("124", 1); 473 | if (result == 123) { 474 | console.log("RESULT: PASS safeSub(124, 1) = 123. Result=" + result); 475 | } else { 476 | console.log("RESULT: FAIL safeSub(124, 1) <> 123. Result=" + result); 477 | } 478 | 479 | result = token.safeSub("122", minusOneInt); 480 | if (result == 0) { 481 | console.log("RESULT: PASS safeSub(122, " + minusOneInt + ") = 0. Result=" + result); 482 | } else { 483 | console.log("RESULT: FAIL safeSub(122, " + minusOneInt + ") = 123. Result=" + result); 484 | } 485 | 486 | } 487 | 488 | EOF 489 | grep "DATA: " $TEST1OUTPUT | sed "s/DATA: //" > $DEPLOYMENTDATA 490 | cat $DEPLOYMENTDATA 491 | grep "RESULT: " $TEST1OUTPUT | sed "s/RESULT: //" > $TEST1RESULTS 492 | cat $TEST1RESULTS 493 | --------------------------------------------------------------------------------