From d9ce878b70465fb17deec146ed26e51288ccdd21 Mon Sep 17 00:00:00 2001 From: masaun Date: Fri, 31 Jan 2020 20:03:16 +0100 Subject: [PATCH] Initial commit --- .gitignore | 46 + CHANGELOG.md | 255 + LICENSE | 45 + README.md | 76 + contracts/Migrations.sol | 23 + contracts/bancor/.gitignore | 46 + contracts/bancor/.node-version | 1 + contracts/bancor/.snyk | 17 + contracts/bancor/BancorNetwork.sol | 675 ++ contracts/bancor/BancorNetworkPathFinder.sol | 165 + contracts/bancor/FeatureIds.sol | 11 + contracts/bancor/IBancorNetwork.sol | 120 + contracts/bancor/bancorx/BancorX.sol | 472 + .../bancor/bancorx/XTransferRerouter.sol | 56 + .../bancor/bancorx/interfaces/IBancorX.sol | 6 + .../bancorx/interfaces/IBancorXUpgrader.sol | 8 + .../bancor/converter/BancorConverter.sol | 909 ++ .../converter/BancorConverterFactory.sol | 58 + .../converter/BancorConverterRegistry.sol | 468 + .../converter/BancorConverterRegistryData.sol | 302 + .../converter/BancorConverterUpgrader.sol | 242 + contracts/bancor/converter/BancorFormula.sol | 613 ++ .../converter/interfaces/IBancorConverter.sol | 26 + .../interfaces/IBancorConverterFactory.sol | 18 + .../interfaces/IBancorConverterRegistry.sol | 23 + .../IBancorConverterRegistryData.sol | 26 + .../interfaces/IBancorConverterUpgrader.sol | 10 + .../converter/interfaces/IBancorFormula.sol | 14 + .../bancor/crowdsale/CrowdsaleController.sol | 199 + contracts/bancor/helpers/Migrations.sol | 23 + .../bancor/helpers/NonStandardERC20Token.sol | 101 + .../bancor/helpers/NonStandardSmartToken.sol | 118 + .../bancor/helpers/TestBancorFormula.sol | 38 + .../bancor/helpers/TestBancorNetwork.sol | 52 + .../helpers/TestCrowdsaleController.sol | 31 + contracts/bancor/helpers/TestFeatures.sol | 17 + .../helpers/TestNonStandardERC20Token.sol | 15 + contracts/bancor/helpers/TestSafeMath.sol | 25 + .../bancor/legacy/BancorGasPriceLimit.sol | 52 + contracts/bancor/legacy/BancorPriceFloor.sol | 65 + contracts/bancor/solidity/.solcover.js | 12 + contracts/bancor/solidity/truffle-config.js | 38 + contracts/bancor/token/ERC20Token.sol | 124 + contracts/bancor/token/EtherToken.sol | 135 + contracts/bancor/token/SmartToken.sol | 141 + .../bancor/token/SmartTokenController.sol | 104 + .../bancor/token/interfaces/IERC20Token.sol | 18 + .../bancor/token/interfaces/IEtherToken.sol | 12 + .../token/interfaces/INonStandardERC20.sol | 18 + .../bancor/token/interfaces/ISmartToken.sol | 12 + .../interfaces/ISmartTokenController.sol | 10 + contracts/bancor/utility/ContractFeatures.sol | 79 + contracts/bancor/utility/ContractRegistry.sol | 149 + .../bancor/utility/ContractRegistryClient.sol | 110 + contracts/bancor/utility/Managed.sol | 60 + contracts/bancor/utility/Owned.sol | 53 + contracts/bancor/utility/SafeMath.sol | 66 + contracts/bancor/utility/TokenHolder.sol | 43 + contracts/bancor/utility/Utils.sol | 31 + contracts/bancor/utility/Whitelist.sol | 94 + .../utility/interfaces/IAddressList.sol | 8 + .../utility/interfaces/IContractFeatures.sol | 9 + .../utility/interfaces/IContractRegistry.sol | 11 + .../bancor/utility/interfaces/IOwned.sol | 12 + .../utility/interfaces/ITokenHolder.sol | 10 + .../bancor/utility/interfaces/IWhitelist.sol | 8 + contracts/storage/BnConstants.sol | 21 + contracts/storage/BnEvents.sol | 11 + contracts/storage/BnObjects.sol | 11 + contracts/storage/BnStorage.sol | 13 + migrations/1_initial_migration.js | 4 + migrations/2_deploy_contracts.js | 11 + package-lock.json | 8790 +++++++++++++++++ package.json | 32 + scripts/compile.sh | 21 + scripts/deploy-one.js | 92 + scripts/fix-modules.js | 39 + scripts/flatten.js | 33 + scripts/rebuild-all.js | 22 + scripts/run-tests.js | 67 + scripts/verify-all.js | 111 + test/BancorConverter.js | 1427 +++ test/BancorConverterRegistry.js | 210 + test/BancorConverterRegistryData.js | 210 + test/BancorConverterUpgrader.js | 274 + test/BancorFormula.js | 211 + test/BancorNetwork.js | 991 ++ test/BancorNetworkPathFinder.js | 243 + test/BancorNetworkWithOldConverter.js | 100 + test/BancorX.js | 249 + test/ContractFeatures.js | 79 + test/ContractRegistry.js | 122 + test/CrowdsaleController.js | 289 + test/ERC20Token.js | 117 + test/EtherToken.js | 100 + test/Managed.js | 67 + test/Owned.js | 57 + test/SafeMath.js | 55 + test/SmartToken.js | 171 + test/SmartTokenController.js | 138 + test/TokenHolder.js | 60 + test/Whitelist.js | 119 + test/XConversions.js | 552 ++ test/XTransferRerouter.js | 69 + test/bin/bancor_converter_v10.abi | 1 + test/bin/bancor_converter_v10.bin | 1 + test/bin/bancor_converter_v11.abi | 1 + test/bin/bancor_converter_v11.bin | 1 + test/bin/bancor_converter_v4.abi | 1 + test/bin/bancor_converter_v4.bin | 1 + test/bin/bancor_converter_v9.abi | 1 + test/bin/bancor_converter_v9.bin | 1 + test/helpers/BancorConverter.js | 25 + test/helpers/ContractRegistryClient.js | 11 + test/helpers/FormulaConstants.js | 262 + test/helpers/Utils.js | 24 + truffle-config.js | 53 + utils/README.md | 129 + utils/deploy_network_emulation.js | 198 + utils/migrate_converter_registry_1.js | 147 + utils/migrate_converter_registry_2.js | 146 + utils/retrieve_contract_version.js | 51 + utils/verify_network_path_finder.js | 140 + 123 files changed, 23216 insertions(+) create mode 100644 .gitignore create mode 100755 CHANGELOG.md create mode 100755 LICENSE create mode 100755 README.md create mode 100755 contracts/Migrations.sol create mode 100755 contracts/bancor/.gitignore create mode 100755 contracts/bancor/.node-version create mode 100755 contracts/bancor/.snyk create mode 100755 contracts/bancor/BancorNetwork.sol create mode 100755 contracts/bancor/BancorNetworkPathFinder.sol create mode 100755 contracts/bancor/FeatureIds.sol create mode 100755 contracts/bancor/IBancorNetwork.sol create mode 100755 contracts/bancor/bancorx/BancorX.sol create mode 100755 contracts/bancor/bancorx/XTransferRerouter.sol create mode 100755 contracts/bancor/bancorx/interfaces/IBancorX.sol create mode 100755 contracts/bancor/bancorx/interfaces/IBancorXUpgrader.sol create mode 100755 contracts/bancor/converter/BancorConverter.sol create mode 100755 contracts/bancor/converter/BancorConverterFactory.sol create mode 100755 contracts/bancor/converter/BancorConverterRegistry.sol create mode 100755 contracts/bancor/converter/BancorConverterRegistryData.sol create mode 100755 contracts/bancor/converter/BancorConverterUpgrader.sol create mode 100755 contracts/bancor/converter/BancorFormula.sol create mode 100755 contracts/bancor/converter/interfaces/IBancorConverter.sol create mode 100755 contracts/bancor/converter/interfaces/IBancorConverterFactory.sol create mode 100755 contracts/bancor/converter/interfaces/IBancorConverterRegistry.sol create mode 100755 contracts/bancor/converter/interfaces/IBancorConverterRegistryData.sol create mode 100755 contracts/bancor/converter/interfaces/IBancorConverterUpgrader.sol create mode 100755 contracts/bancor/converter/interfaces/IBancorFormula.sol create mode 100755 contracts/bancor/crowdsale/CrowdsaleController.sol create mode 100755 contracts/bancor/helpers/Migrations.sol create mode 100755 contracts/bancor/helpers/NonStandardERC20Token.sol create mode 100755 contracts/bancor/helpers/NonStandardSmartToken.sol create mode 100755 contracts/bancor/helpers/TestBancorFormula.sol create mode 100755 contracts/bancor/helpers/TestBancorNetwork.sol create mode 100755 contracts/bancor/helpers/TestCrowdsaleController.sol create mode 100755 contracts/bancor/helpers/TestFeatures.sol create mode 100755 contracts/bancor/helpers/TestNonStandardERC20Token.sol create mode 100755 contracts/bancor/helpers/TestSafeMath.sol create mode 100755 contracts/bancor/legacy/BancorGasPriceLimit.sol create mode 100755 contracts/bancor/legacy/BancorPriceFloor.sol create mode 100755 contracts/bancor/solidity/.solcover.js create mode 100755 contracts/bancor/solidity/truffle-config.js create mode 100755 contracts/bancor/token/ERC20Token.sol create mode 100755 contracts/bancor/token/EtherToken.sol create mode 100755 contracts/bancor/token/SmartToken.sol create mode 100755 contracts/bancor/token/SmartTokenController.sol create mode 100755 contracts/bancor/token/interfaces/IERC20Token.sol create mode 100755 contracts/bancor/token/interfaces/IEtherToken.sol create mode 100755 contracts/bancor/token/interfaces/INonStandardERC20.sol create mode 100755 contracts/bancor/token/interfaces/ISmartToken.sol create mode 100755 contracts/bancor/token/interfaces/ISmartTokenController.sol create mode 100755 contracts/bancor/utility/ContractFeatures.sol create mode 100755 contracts/bancor/utility/ContractRegistry.sol create mode 100755 contracts/bancor/utility/ContractRegistryClient.sol create mode 100755 contracts/bancor/utility/Managed.sol create mode 100755 contracts/bancor/utility/Owned.sol create mode 100755 contracts/bancor/utility/SafeMath.sol create mode 100755 contracts/bancor/utility/TokenHolder.sol create mode 100755 contracts/bancor/utility/Utils.sol create mode 100755 contracts/bancor/utility/Whitelist.sol create mode 100755 contracts/bancor/utility/interfaces/IAddressList.sol create mode 100755 contracts/bancor/utility/interfaces/IContractFeatures.sol create mode 100755 contracts/bancor/utility/interfaces/IContractRegistry.sol create mode 100755 contracts/bancor/utility/interfaces/IOwned.sol create mode 100755 contracts/bancor/utility/interfaces/ITokenHolder.sol create mode 100755 contracts/bancor/utility/interfaces/IWhitelist.sol create mode 100755 contracts/storage/BnConstants.sol create mode 100755 contracts/storage/BnEvents.sol create mode 100755 contracts/storage/BnObjects.sol create mode 100755 contracts/storage/BnStorage.sol create mode 100755 migrations/1_initial_migration.js create mode 100755 migrations/2_deploy_contracts.js create mode 100755 package-lock.json create mode 100755 package.json create mode 100755 scripts/compile.sh create mode 100755 scripts/deploy-one.js create mode 100755 scripts/fix-modules.js create mode 100755 scripts/flatten.js create mode 100755 scripts/rebuild-all.js create mode 100755 scripts/run-tests.js create mode 100755 scripts/verify-all.js create mode 100755 test/BancorConverter.js create mode 100755 test/BancorConverterRegistry.js create mode 100755 test/BancorConverterRegistryData.js create mode 100755 test/BancorConverterUpgrader.js create mode 100755 test/BancorFormula.js create mode 100755 test/BancorNetwork.js create mode 100755 test/BancorNetworkPathFinder.js create mode 100755 test/BancorNetworkWithOldConverter.js create mode 100755 test/BancorX.js create mode 100755 test/ContractFeatures.js create mode 100755 test/ContractRegistry.js create mode 100755 test/CrowdsaleController.js create mode 100755 test/ERC20Token.js create mode 100755 test/EtherToken.js create mode 100755 test/Managed.js create mode 100755 test/Owned.js create mode 100755 test/SafeMath.js create mode 100755 test/SmartToken.js create mode 100755 test/SmartTokenController.js create mode 100755 test/TokenHolder.js create mode 100755 test/Whitelist.js create mode 100755 test/XConversions.js create mode 100755 test/XTransferRerouter.js create mode 100755 test/bin/bancor_converter_v10.abi create mode 100755 test/bin/bancor_converter_v10.bin create mode 100755 test/bin/bancor_converter_v11.abi create mode 100755 test/bin/bancor_converter_v11.bin create mode 100755 test/bin/bancor_converter_v4.abi create mode 100755 test/bin/bancor_converter_v4.bin create mode 100755 test/bin/bancor_converter_v9.abi create mode 100755 test/bin/bancor_converter_v9.bin create mode 100755 test/helpers/BancorConverter.js create mode 100755 test/helpers/ContractRegistryClient.js create mode 100755 test/helpers/FormulaConstants.js create mode 100755 test/helpers/Utils.js create mode 100755 truffle-config.js create mode 100755 utils/README.md create mode 100755 utils/deploy_network_emulation.js create mode 100755 utils/migrate_converter_registry_1.js create mode 100755 utils/migrate_converter_registry_2.js create mode 100755 utils/retrieve_contract_version.js create mode 100755 utils/verify_network_path_finder.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c774fed --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +.DS_STORE +.vscode + +# node +node_modules +npm-debug.log + +# python +*.pyc + +# npm test 1 +solidity/build/contracts + +# npm test 2 +solidity/allFiredEvents +solidity/coverageEnv +solidity/coverage +solidity/coverage.json +solidity/scTopics + +# npm run build +solidity/build/INonStandardSmartToken.bin +solidity/build/Migrations.abi +solidity/build/Migrations.bin +solidity/build/NewBancorConverter.abi +solidity/build/NewBancorConverter.bin +solidity/build/NonStandardERC20Token.abi +solidity/build/NonStandardERC20Token.bin +solidity/build/NonStandardSmartToken.abi +solidity/build/NonStandardSmartToken.bin +solidity/build/OldBancorConverter.abi +solidity/build/OldBancorConverter.bin +solidity/build/TestBancorFormula.abi +solidity/build/TestBancorFormula.bin +solidity/build/TestBancorNetwork.abi +solidity/build/TestBancorNetwork.bin +solidity/build/TestCrowdsaleController.abi +solidity/build/TestCrowdsaleController.bin +solidity/build/TestERC20Token.abi +solidity/build/TestERC20Token.bin +solidity/build/TestFeatures.abi +solidity/build/TestFeatures.bin +solidity/build/TestNonStandardERC20Token.abi +solidity/build/TestNonStandardERC20Token.bin +solidity/build/TestSafeMath.abi +solidity/build/TestSafeMath.bin diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100755 index 0000000..b312fff --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,255 @@ +### 0.5.15 (2020-01-23) +EtherToken +* Name & symbol are now constructor args + + +### 0.5.14 (2020-01-21) +BancorNetwork +* Removed signature/gas price limit logic + +BancorConverter +* Removed the `converterType` variable + +BancorConverterUpgrader +* Removed legacy converter (0.4) support + +BancorFormula +* Increased liquidation cost precision + +EtherToken +* Added `depositTo` function for direct depositing to another account for gas optimization + + +### 0.5.13 (2020-01-09) +BancorNetwork +* Added a new Conversion event that gets emitted for any conversion in the network + +BancorConverter +* Owners cannot disable conversions anymore +* Owners cannot disable conversions from specific reserves anymore +* Removed the virtual balance mechanism + + +### 0.5.12 (2019-12-19) +General +* Better handling for non standard ERC20 tokens's transfer function (removed NonStandardTokenRegistry contract, gas optimization) + +BancorConverterRegistry +* Fixed an issue that allowed adding duplicate pools to the registry in certain situations + + +### 0.5.11 (2019-12-17) +General +* Added the BancorNetworkPathFinder contract, now compatible with the new converter registry contract + +BancorConverter +* Added a dedicated getReserveRatio function + +BancorConverterRegistry +* Now enforces only a single liquidity pool for each reserve configuration +* Disabled converters are now considered invalid (can be removed by anyone) +* Added a utility function that returns a list of converters for a given list of smart tokens + + +### 0.5.8-10 (2019-12-12) +General +* Minor cleanups + + +### 0.5.7 (2019-12-12) +General: +* Added ContractRegistryClient contract for common contract registry behavior and cleaner access, and updated all registry clients + +BancorConverterRegistry: +* Full redesign - it now allows iterating over different primitives in the network and does not +require re-adding converters after a converter upgrade + +BancorConverter: +* Added support for fund/liquidate in non 50%/50% reserves converters + +BancorFormula: +* Added calculations for fund/liquidate in non 50%/50% reserves + + +### 0.5.6 (2019-11-18) +BancorConverter: +* Updated the virtual balances mechanism - it now scales all reserve balances by the same factor +and is only relevant to cross reserve conversions + + +### 0.5.5 (2019-11-05) +BancorNetwork: +* Added affiliate fee support in xConvert & xConvertPrioritized + +SmartTokenController: +* Removed the disableTokenTransfers function + + +### 0.5.4 (2019-11-03) +General: +* Added a testnet/private chain deployment script/migration +* Updated the readme file with more tutorials on the various scripts + +ERC20Token: +* Cleaned up construction, added the total supply as a constructor arg + + +### 0.5.3 (2019-10-22) +General: +* Cleaned up all compilation warnings + +BancorConverter: +* Added a protection against activation with no token supply + + +### 0.5.2 (2019-10-07) +BancorConverterRegistry +* Added events when adding/removing tokens +* Removing the last converter of a token will now also remove the token from the list of tokens + + +### 0.5.1 (2019-10-07) +General: +* Added the BancorNetworkPathFinder contract + +BancorConverterFactory +* Added utility function `latestConverterAddress` to return the latest converter for a given token +* Removing the last converter for a token will now also remove the token from the list of tokens + + +### 0.5.0 (2019-09-25) +General: +* Terminology changes (Connector -> Reserve, Weight -> Ratio) +* Compiler upgraded to 0.4.26 +* Truffle upgraded to 4.1.16 + + +### 0.4.12 (2019-09-01) +General: +* Fixed line breaks in documentation + + +### 0.4.11 (2019-08-29) +BancorNetwork: +* Added support for affilate fee + +BancorConverter: +* Minor cleanups / bug fixes + + +### 0.4.10 (2019-08-21) +BancorX: +* Added support for any ERC20/Smart token (was previously BNT specific) + + +### 0.4.9 (2019-08-18) +Converters: +* Fixed a rounding error in the `fund` function + + +### 0.4.8 (2019-07-20) +General: +* Added support for auto generation of documentation +* Updated contract documentation +* Cleaned up tests + +Network: +* Fixed an issue that caused getReturnByPath to fail if the path contained old converters + + +### 0.4.7 (2019-05-11) +* added the ConverterRegistry contract + + +### 0.4.6 (2019-01-29) +General: +* Added support for non standard ERC-20 tokens +* Added NonStandardTokenRegistry contract to support non standard ERC-20 tokens + +Network: +* Removed `convertForMultiple` + + +### 0.4.5 (2019-01-23) +General: +* Minor cleanups / bug fixes +* Moved to SafeMath + +Converters: +* Added `completeXConversion` function to convert from BNT to another token by providing an id rather than amount +* Changed the version from bytes32 to uint16 +* `quickConvert` and `quickConvertPrioritized` now call `convertForPrioritized3` in the BancorNetwork contract +* Renamed `isPurchaseEnabled` to `isSaleEnabled` in connector token struct, and validated that sales are enabled for the `fromConnector` rather than the `toConnector` in the conversion functions + +Network: +* `verifyTrustedSender` function argument `amount` renamed to `customVal` +* Added `xConvert` and `xConvertPrioritized` functions which converts any token to BNT and transfers the result to BancorX +* Added `validateXConversion` function to get around the 16 variable function limit in the `xConvert` function +* Added `convertForPrioritized3` with backwards compatibility to now receive a custom value along with the amount for verifying trusted senders + + +### 0.4.4 (2018-06-23) +General: +* Minor cleanups / bug fixes +* Upgraded compiler version to 0.4.24 + +Converters: +* Replaced the `setRegistry` function with `updateRegistry` function +* Removed quickBuyPath from converter +* getReturn now returns the fee as a separate return value +* Converter owner can no longer withdraw connector tokens while the converter is active +* Converter owner can no longer transfer the token ownership once the converter is active +* Added a dedicated `upgrade` function for easier upgrades +* Added a `fund` function for increasing liquidity atomically +* Added a `liquidate` function for decreasing liquidity atomically even when conversions are disabled + +Registry: +* Fixed item removal + + +### 0.4.3 (2018-06-23) +General: +* Upgraded compiler version to 0.4.23 +* Updated all contracts to make use of the new `constructor` keyword +* Removed more local contract dependencies and replaced them with querying the registry + +Utilities: +* ContractRegistry - added support for querying the number of items/contract names in the registry + + +### 0.4.2 (2018-06-10) +General: +* Added more predefined contract ids + +Bug fixes: +* Fixed a crash in BancorConverterUpgrader when trying to upgrade converters with virtual connector balance + + +### 0.4.1 (2018-06-07) +Bug fixes: +* Fixed BancorNetwork contract backward compatibility with older converters + + +### 0.4.0 (2018-06-06) +General: + * Restructured contract folders + * Upgraded compiler version to 0.4.21 + * Replaced from testrpc with ganache + * Other minor cleanups + +Converters: + * Gas cost optimizations when converting between 2 connectors, now uses an optimized dedicated + formula calculation + * Conversions now trigger 2 separate events - Conversion & PriceDataUpdate + * Added support for multiple conversions in a single atomic transaction + * Added support for conversion whitelist + * Removed the BancorConverterExtensions contract and replaced it with the new ContractRegistry contract + * Added the ability for the owner to also set the manager + * Renamed BancorQuickConverter to BancorNetwork to more accurately reflect its place as the entry point + for bancor related functionality + * Removed EIP228 related functionality and deprecated the EIP + +Utilities: + * Added support for contract registry to minimize dependencies between contracts + * Added support for a "feature flags" contract that allows contracts to dynamically query + other contracts for supported features diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..e08d00a --- /dev/null +++ b/LICENSE @@ -0,0 +1,45 @@ + Bprotocol Foundation (Bancor) LICENSE + +1. SUBJECT TO THE PROVISIONS SET FORTH HEREIN, INCLUDING “EFFECTIVE DATE”, YOU CAN + USE THIS CODE, FILE AND/OR SOFTWARE (“SOFTWARE”) ONLY IN CONNECTION WITH THE + BANCOR LIQUIDITY NETWORK AND/OR THE USE OF BNT ("PERMITTED USE"). ANY OTHER USE IS + PROHIBITED UNLESS THE USER SHALL RECEIVE AN EXPLICIT PRIOR WRITTEN APPROVAL FROM + BPROTOCOL FOUNDATION (BANCOR) TO DO SO (PLEASE CONTACT license@bancor.network IN + THIS REGARD), WHICH APPROVAL, IF GIVEN, MAY REQUIRE THE OBTAINMENT OF SEPARATE + LICENSE UNDER A DIFFERENT LICENSING MODEL. USING THIS SOFTWARE NOT IN THE FRAME OF + SUCH PERMITTED USE MAY, AMONG OTHERS, ALSO BREACH PATENT RIGHTS CONCERNING PATENTS + WHICH ARE EMBODIED/INCORPORATED/USED IN THIS SOFTWARE. + +2. ANY SUCH PERMITTED USE SHOULD ALSO COMPLY WITH THE TERMS BELOW. + +3. Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: +A. Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. +B. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. +B. Neither the name of the copyright holder nor the names of its contributors may be + used to endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +EFFECTIVE DATE: THIS LICENSE SHALL APPLY ONLY TO SOFTWARE (OR ANY VERSION THEREOF), +THAT HAS BEEN PUBLISHED AFTER THE DATE AND TIME THIS LICENSE HAS BEEN FIRST PUBLISHED +(“EFFECTIVE DATE”); Any previous versions published prior to the effective date (“Older Versions”) +shall remain licensed under the Apache License, Version 2.0 (the "Older Versions License"); +You may obtain a copy of the Older Version License at http://www.apache.org/licenses/LICENSE-2.0 +you may not use this file except in compliance with the Older Version License. Unless +required by applicable law or agreed to in writing, Older Versions distributed under the +Older Version License are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, either express or implied. See the Older Version License for the specific +language governing permissions and limitations under the Older Version License. diff --git a/README.md b/README.md new file mode 100755 index 0000000..51f6c83 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ + +# Bancor Protocol Contracts v0.5 (beta) + +## Overview + +Bancor is a decentralized liquidity protocol that provides tokens with constant liquidity. The protocol is made up of a series of smart contracts which are designed to pool liquidity and perform non-custodial token-to-token conversions in a single transaction. More than 150 tokens are integrated with the Bancor Protocol, including ETH, EOS, DAI, IQ, PEOS & more. + +* Join the [Bancor Developers Telegram group](https://t.me/BancorDevelopers) or the [Bancor Protocol Telegram group](https://t.me/bancor) +* Check out the [Bancor Blog](https://blog.bancor.network/) +* Read the Bancor Protocol [Whitepaper](https://storage.googleapis.com/website-bancor/2018/04/01ba8253-bancor_protocol_whitepaper_en.pdf) +* Visit the [Bancor Web App](https://www.bancor.network/communities/5a780b3a287443a5cdea2477?utm_source=social&utm_medium=github&utm_content=readme) + +## How Bancor Works + +Token conversions via the Bancor Protocol are executed against on-chain liquidity pools known as “Bancor Relays”. Each Relay holds reserves of both BNT (Bancor’s Network Token) and a base token (which could be any ERC20 or EOS-based token, with more blockchains to come). For instance, the base token for the ‘DAIBNT’ Relay is DAI. + +A Relay’s reserves receive and dispense tokens in order to fulfill trades and are autonomously rebalanced to determine prices. Selling BNT for the base token increases the size of the BNT reserve and decreases the size of the base token’s reserve. This shifts the reserve ratio, increasing the base token's price relative to BNT for subsequent transactions. The larger a trade relative to the total size of the reserves, the more price slippage will occur. + +Since BNT is a common pair for all Relays, it can be used as an intermediary allowing direct token-token trades in a single transaction, including across different blockchains. Notably, traders never need to hold BNT to perform conversions via Bancor Protocol. + +## Providing Liquidity on Bancor + +Anyone can become a liquidity provider to a Relay and contribute to its reserves. This is different than buying tokens on Bancor. It requires staking tokens in a Relay. Users can stake their tokens in a Relay by buying “Relay Tokens” on bancor.network, or through any third-party liquidity portal built atop the Bancor Protocol. Relay Tokens can be sold at any time to withdraw a proportional share of the Relay’s liquidity. + +Each time a Relay processes a conversion, a small liquidity provider fee (usually 0.1-0.3%) is taken out of each trade and deposited into the Relay’s reserves. These fees function as an incentive for liquidity providers who can withdraw their proportional share of the reserves including the accumulated fees. The larger a Relay’s reserves, the lower the slippage costs incurred by traders transacting with the Relay, driving more conversion volume and, in turn, more fees for liquidity providers. + +Currently, whoever initiates the Relay determines its fees, while in the future, liquidity providers will be able to vote on the Relay’s fee. Bancor takes no platform fee from trades. + +## Upgradeability + +All smart contract functions are public and all upgrades are opt-in. If significant improvements are made to the system a new version will be released. Token owners can choose between moving to the new system or staying in the old one. If possible, new versions will be backwards compatible and able to trade with the old versions. + +## Language + +A “Smart Token” refers to tokens which utilize reserves to automate trading, including “Liquid Tokens” (one reserve), “Relay Tokens” (two reserves) and “Array Tokens” (three or more reserves). See Section 6 of the [Bancor Whitepaper](https://storage.googleapis.com/website-bancor/2018/04/01ba8253-bancor_protocol_whitepaper_en.pdf) (“Smart Token Configurations”) for more details. + +The terms “reserves” and “connectors” have the same meaning throughout Bancor’s smart contract code and documentation. “Reserve ratio” and “connector weight” are also used interchangeably. “Connector balance” refers to the token inventories held in a Smart Token’s reserve. + +## Warning + +Bancor is a work in progress. Make sure you understand the risks before using it. + +## Testing + +### Prerequisites + +* node 10.16.0 +* npm 6.9.0 +* python 3.7.3 +* web3.py 4.9.2 + +### Installation + +* `npm install` + +### Verification + +* Verifying all the contracts: + * `npm test 1` (quick testing) + * `npm test 2` (full coverage) +* [Verifying the BancorFormula contract](solidity/python/README.md) + +### [Utilities](solidity/utils/README.md) + +## Collaborators + +* **[Yudi Levi](https://github.com/yudilevi)** +* **[Barak Manos](https://github.com/barakman)** +* **[Ilana Pinhas](https://github.com/ilanapi)** +* **[David Benchimol](https://github.com/davidbancor)** +* **[Or Dadosh](https://github.com/ordd)** +* **[Martin Holst Swende](https://github.com/holiman)** + +## License + +Bancor Protocol is open source and distributed under the Apache License v2.0 diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol new file mode 100755 index 0000000..c378ffb --- /dev/null +++ b/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/contracts/bancor/.gitignore b/contracts/bancor/.gitignore new file mode 100755 index 0000000..c774fed --- /dev/null +++ b/contracts/bancor/.gitignore @@ -0,0 +1,46 @@ +.DS_STORE +.vscode + +# node +node_modules +npm-debug.log + +# python +*.pyc + +# npm test 1 +solidity/build/contracts + +# npm test 2 +solidity/allFiredEvents +solidity/coverageEnv +solidity/coverage +solidity/coverage.json +solidity/scTopics + +# npm run build +solidity/build/INonStandardSmartToken.bin +solidity/build/Migrations.abi +solidity/build/Migrations.bin +solidity/build/NewBancorConverter.abi +solidity/build/NewBancorConverter.bin +solidity/build/NonStandardERC20Token.abi +solidity/build/NonStandardERC20Token.bin +solidity/build/NonStandardSmartToken.abi +solidity/build/NonStandardSmartToken.bin +solidity/build/OldBancorConverter.abi +solidity/build/OldBancorConverter.bin +solidity/build/TestBancorFormula.abi +solidity/build/TestBancorFormula.bin +solidity/build/TestBancorNetwork.abi +solidity/build/TestBancorNetwork.bin +solidity/build/TestCrowdsaleController.abi +solidity/build/TestCrowdsaleController.bin +solidity/build/TestERC20Token.abi +solidity/build/TestERC20Token.bin +solidity/build/TestFeatures.abi +solidity/build/TestFeatures.bin +solidity/build/TestNonStandardERC20Token.abi +solidity/build/TestNonStandardERC20Token.bin +solidity/build/TestSafeMath.abi +solidity/build/TestSafeMath.bin diff --git a/contracts/bancor/.node-version b/contracts/bancor/.node-version new file mode 100755 index 0000000..10ff020 --- /dev/null +++ b/contracts/bancor/.node-version @@ -0,0 +1 @@ +v10.16.0 \ No newline at end of file diff --git a/contracts/bancor/.snyk b/contracts/bancor/.snyk new file mode 100755 index 0000000..ab78861 --- /dev/null +++ b/contracts/bancor/.snyk @@ -0,0 +1,17 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.14.0 +ignore: {} +# patches apply the minimum changes required to fix a vulnerability +patch: + SNYK-JS-TREEKILL-536781: + - solidity-coverage > tree-kill: + patched: '2019-12-12T13:55:11.672Z' + SNYK-JS-LODASH-450202: + - snyk > @snyk/dep-graph > lodash: + patched: '2019-12-12T13:56:19.599Z' + - snyk > @snyk/snyk-cocoapods-plugin > @snyk/dep-graph > lodash: + patched: '2019-12-12T13:56:19.599Z' + - snyk > snyk-nuget-plugin > dotnet-deps-parser > lodash: + patched: '2019-12-12T13:56:19.599Z' + - snyk > @snyk/snyk-cocoapods-plugin > @snyk/cocoapods-lockfile-parser > @snyk/dep-graph > lodash: + patched: '2019-12-12T13:56:19.599Z' diff --git a/contracts/bancor/BancorNetwork.sol b/contracts/bancor/BancorNetwork.sol new file mode 100755 index 0000000..a772a0d --- /dev/null +++ b/contracts/bancor/BancorNetwork.sol @@ -0,0 +1,675 @@ +pragma solidity 0.4.26; +import './IBancorNetwork.sol'; +import './FeatureIds.sol'; +import './converter/interfaces/IBancorConverter.sol'; +import './converter/interfaces/IBancorFormula.sol'; +import './utility/TokenHolder.sol'; +import './utility/SafeMath.sol'; +import './utility/ContractRegistryClient.sol'; +import './utility/interfaces/IContractFeatures.sol'; +import './utility/interfaces/IWhitelist.sol'; +import './utility/interfaces/IAddressList.sol'; +import './token/interfaces/IEtherToken.sol'; +import './token/interfaces/ISmartToken.sol'; +import './token/interfaces/INonStandardERC20.sol'; +import './bancorx/interfaces/IBancorX.sol'; + +/** + * @dev The BancorNetwork contract is the main entry point for Bancor token conversions. + * It also allows for the conversion of any token in the Bancor Network to any other token in a single transaction by providing a conversion path. + * + * A note on Conversion Path: Conversion path is a data structure that is used when converting a token to another token in the Bancor Network, + * when the conversion cannot necessarily be done by a single converter and might require multiple 'hops'. + * The path defines which converters should be used and what kind of conversion should be done in each step. + * + * The path format doesn't include complex structure; instead, it is represented by a single array in which each 'hop' is represented by a 2-tuple - smart token & to token. + * In addition, the first element is always the source token. + * The smart token is only used as a pointer to a converter (since converter addresses are more likely to change as opposed to smart token addresses). + * + * Format: + * [source token, smart token, to token, smart token, to token...] +*/ +contract BancorNetwork is IBancorNetwork, TokenHolder, ContractRegistryClient, FeatureIds { + using SafeMath for uint256; + + uint256 private constant CONVERSION_FEE_RESOLUTION = 1000000; + uint256 private constant AFFILIATE_FEE_RESOLUTION = 1000000; + + uint256 public maxAffiliateFee = 30000; // maximum affiliate-fee + + mapping (address => bool) public etherTokens; // list of all supported ether tokens + mapping (bytes32 => bool) public conversionHashes; // list of conversion hashes, to prevent re-use of the same hash + + /** + * @dev triggered when a conversion between two tokens occurs + * + * @param _smartToken smart token governed by the converter + * @param _fromToken ERC20 token converted from + * @param _toToken ERC20 token converted to + * @param _fromAmount amount converted, in fromToken + * @param _toAmount amount returned, minus conversion fee + * @param _trader wallet that initiated the trade + */ + event Conversion( + address indexed _smartToken, + address indexed _fromToken, + address indexed _toToken, + uint256 _fromAmount, + uint256 _toAmount, + address _trader + ); + + /** + * @dev initializes a new BancorNetwork instance + * + * @param _registry address of a contract registry contract + */ + constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public { + } + + /** + * @dev allows the owner to update the maximum affiliate-fee + * + * @param _maxAffiliateFee maximum affiliate-fee + */ + function setMaxAffiliateFee(uint256 _maxAffiliateFee) + public + ownerOnly + { + require(_maxAffiliateFee <= AFFILIATE_FEE_RESOLUTION); + maxAffiliateFee = _maxAffiliateFee; + } + + /** + * @dev allows the owner to register/unregister ether tokens + * + * @param _token ether token contract address + * @param _register true to register, false to unregister + */ + function registerEtherToken(IEtherToken _token, bool _register) + public + ownerOnly + validAddress(_token) + notThis(_token) + { + etherTokens[_token] = _register; + } + + /** + * @dev converts the token to any other token in the bancor network by following + * a predefined conversion path and transfers the result tokens to a target account + * note that the network should already own the source tokens + * + * @param _path conversion path, see conversion path format above + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _for account that will receive the conversion result + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return tokens issued in return + */ + function convertFor2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for, address _affiliateAccount, uint256 _affiliateFee) public payable returns (uint256) { + // verify that the number of elements is odd and that maximum number of 'hops' is 10 + require(_path.length > 2 && _path.length <= (1 + 2 * 10) && _path.length % 2 == 1); + + // verify that the account which should receive the conversion result is whitelisted + require(isWhitelisted(_path, _for)); + + // handle msg.value + handleValue(_path[0], _amount, false); + + // convert and get the resulting amount + uint256 amount = convertByPath(_path, _amount, _minReturn, _affiliateAccount, _affiliateFee); + + // finished the conversion, transfer the funds to the target account + // if the target token is an ether token, withdraw the tokens and send them as ETH + // otherwise, transfer the tokens as is + IERC20Token toToken = _path[_path.length - 1]; + if (etherTokens[toToken]) + IEtherToken(toToken).withdrawTo(_for, amount); + else + ensureTransferFrom(toToken, this, _for, amount); + + return amount; + } + + /** + * @dev converts any other token to BNT in the bancor network + * by following a predefined conversion path and transfers the resulting + * tokens to BancorX. + * note that the network should already have been given allowance of the source token (if not ETH) + * + * @param _path conversion path, see conversion path format above + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _toBlockchain blockchain BNT will be issued on + * @param _to address/account on _toBlockchain to send the BNT to + * @param _conversionId pre-determined unique (if non zero) id which refers to this transaction + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return the amount of BNT received from this conversion + */ + function xConvert2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + bytes32 _toBlockchain, + bytes32 _to, + uint256 _conversionId, + address _affiliateAccount, + uint256 _affiliateFee + ) + public + payable + returns (uint256) + { + // verify that the number of elements is odd and that maximum number of 'hops' is 10 + require(_path.length > 2 && _path.length <= (1 + 2 * 10) && _path.length % 2 == 1); + + // verify that the destination token is BNT + require(_path[_path.length - 1] == addressOf(BNT_TOKEN)); + + // handle msg.value + handleValue(_path[0], _amount, true); + + // convert and get the resulting amount + uint256 amount = convertByPath(_path, _amount, _minReturn, _affiliateAccount, _affiliateFee); + + // transfer the resulting amount to BancorX + IBancorX(addressOf(BANCOR_X)).xTransfer(_toBlockchain, _to, amount, _conversionId); + + return amount; + } + + /** + * @dev executes the actual conversion by following the conversion path + * + * @param _path conversion path, see conversion path format above + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return amount of tokens issued + */ + function convertByPath( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _affiliateAccount, + uint256 _affiliateFee + ) private returns (uint256) { + uint256 toAmount; + uint256 fromAmount = _amount; + uint256 lastIndex = _path.length - 1; + + address bntToken; + if (address(_affiliateAccount) == 0) { + require(_affiliateFee == 0); + bntToken = address(0); + } + else { + require(0 < _affiliateFee && _affiliateFee <= maxAffiliateFee); + bntToken = addressOf(BNT_TOKEN); + } + + // iterate over the conversion path + for (uint256 i = 2; i <= lastIndex; i += 2) { + IBancorConverter converter = IBancorConverter(ISmartToken(_path[i - 1]).owner()); + + // if the smart token isn't the source (from token), the converter doesn't have control over it and thus we need to approve the request + if (_path[i - 1] != _path[i - 2]) + ensureAllowance(_path[i - 2], converter, fromAmount); + + // make the conversion - if it's the last one, also provide the minimum return value + toAmount = converter.change(_path[i - 2], _path[i], fromAmount, i == lastIndex ? _minReturn : 1); + + // pay affiliate-fee if needed + if (address(_path[i]) == bntToken) { + uint256 affiliateAmount = toAmount.mul(_affiliateFee).div(AFFILIATE_FEE_RESOLUTION); + require(_path[i].transfer(_affiliateAccount, affiliateAmount)); + toAmount -= affiliateAmount; + bntToken = address(0); + } + + emit Conversion(_path[i - 1], _path[i - 2], _path[i], fromAmount, toAmount, msg.sender); + fromAmount = toAmount; + } + + return toAmount; + } + + bytes4 private constant GET_RETURN_FUNC_SELECTOR = bytes4(uint256(keccak256("getReturn(address,address,uint256)") >> (256 - 4 * 8))); + + function getReturn(address _dest, address _fromToken, address _toToken, uint256 _amount) internal view returns (uint256, uint256) { + uint256[2] memory ret; + bytes memory data = abi.encodeWithSelector(GET_RETURN_FUNC_SELECTOR, _fromToken, _toToken, _amount); + + assembly { + let success := staticcall( + gas, // gas remaining + _dest, // destination address + add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array) + mload(data), // input length (loaded from the first 32 bytes in the `data` array) + ret, // output buffer + 64 // output length + ) + if iszero(success) { + revert(0, 0) + } + } + + return (ret[0], ret[1]); + } + + /** + * @dev calculates the expected return of converting a given amount on a given path + * note that there is no support for circular paths + * + * @param _path conversion path (see conversion path format above) + * @param _amount amount of _path[0] tokens received from the user + * + * @return amount of _path[_path.length - 1] tokens that the user will receive + * @return amount of _path[_path.length - 1] tokens that the user will pay as fee + */ + function getReturnByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256, uint256) { + uint256 amount; + uint256 fee; + uint256 supply; + uint256 balance; + uint32 ratio; + IBancorConverter converter; + IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); + + amount = _amount; + + // verify that the number of elements is larger than 2 and odd + require(_path.length > 2 && _path.length % 2 == 1); + + // iterate over the conversion path + for (uint256 i = 2; i < _path.length; i += 2) { + IERC20Token fromToken = _path[i - 2]; + IERC20Token smartToken = _path[i - 1]; + IERC20Token toToken = _path[i]; + + if (toToken == smartToken) { // buy the smart token + // check if the current smart token has changed + if (i < 3 || smartToken != _path[i - 3]) { + supply = smartToken.totalSupply(); + converter = IBancorConverter(ISmartToken(smartToken).owner()); + } + + // calculate the amount & the conversion fee + balance = converter.getConnectorBalance(fromToken); + (, ratio, , , ) = converter.connectors(fromToken); + amount = formula.calculatePurchaseReturn(supply, balance, ratio, amount); + fee = amount.mul(converter.conversionFee()).div(CONVERSION_FEE_RESOLUTION); + amount -= fee; + + // update the smart token supply for the next iteration + supply += amount; + } + else if (fromToken == smartToken) { // sell the smart token + // check if the current smart token has changed + if (i < 3 || smartToken != _path[i - 3]) { + supply = smartToken.totalSupply(); + converter = IBancorConverter(ISmartToken(smartToken).owner()); + } + + // calculate the amount & the conversion fee + balance = converter.getConnectorBalance(toToken); + (, ratio, , , ) = converter.connectors(toToken); + amount = formula.calculateSaleReturn(supply, balance, ratio, amount); + fee = amount.mul(converter.conversionFee()).div(CONVERSION_FEE_RESOLUTION); + amount -= fee; + + // update the smart token supply for the next iteration + supply -= amount; + } + else { // cross reserve conversion + // check if the current smart token has changed + if (i < 3 || smartToken != _path[i - 3]) { + converter = IBancorConverter(ISmartToken(smartToken).owner()); + } + + (amount, fee) = getReturn(converter, fromToken, toToken, amount); + } + } + + return (amount, fee); + } + + /** + * @dev claims the caller's tokens, converts them to any other token in the bancor network + * by following a predefined conversion path and transfers the result tokens to a target account + * note that allowance must be set beforehand + * + * @param _path conversion path, see conversion path format above + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _for account that will receive the conversion result + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return tokens issued in return + */ + function claimAndConvertFor2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for, address _affiliateAccount, uint256 _affiliateFee) public returns (uint256) { + // we need to transfer the tokens from the caller to the network before we follow + // the conversion path, to allow it to execute the conversion on behalf of the caller + // note: we assume we already have allowance + IERC20Token fromToken = _path[0]; + ensureTransferFrom(fromToken, msg.sender, this, _amount); + return convertFor2(_path, _amount, _minReturn, _for, _affiliateAccount, _affiliateFee); + } + + /** + * @dev converts the token to any other token in the bancor network by following + * a predefined conversion path and transfers the result tokens back to the sender + * note that the network should already own the source tokens + * + * @param _path conversion path, see conversion path format above + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return tokens issued in return + */ + function convert2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) public payable returns (uint256) { + return convertFor2(_path, _amount, _minReturn, msg.sender, _affiliateAccount, _affiliateFee); + } + + /** + * @dev claims the caller's tokens, converts them to any other token in the bancor network + * by following a predefined conversion path and transfers the result tokens back to the sender + * note that allowance must be set beforehand + * + * @param _path conversion path, see conversion path format above + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return tokens issued in return + */ + function claimAndConvert2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) public returns (uint256) { + return claimAndConvertFor2(_path, _amount, _minReturn, msg.sender, _affiliateAccount, _affiliateFee); + } + + /** + * @dev ensures transfer of tokens, taking into account that some ERC-20 implementations don't return + * true on success but revert on failure instead + * + * @param _token the token to transfer + * @param _from the address to transfer the tokens from + * @param _to the address to transfer the tokens to + * @param _amount the amount to transfer + */ + function ensureTransferFrom(IERC20Token _token, address _from, address _to, uint256 _amount) private { + // We must assume that functions `transfer` and `transferFrom` do not return anything, + // because not all tokens abide the requirement of the ERC20 standard to return success or failure. + // This is because in the current compiler version, the calling contract can handle more returned data than expected but not less. + // This may change in the future, so that the calling contract will revert if the size of the data is not exactly what it expects. + uint256 prevBalance = _token.balanceOf(_to); + if (_from == address(this)) + INonStandardERC20(_token).transfer(_to, _amount); + else + INonStandardERC20(_token).transferFrom(_from, _to, _amount); + uint256 postBalance = _token.balanceOf(_to); + require(postBalance > prevBalance); + } + + /** + * @dev utility, checks whether allowance for the given spender exists and approves one if it doesn't. + * Note that we use the non standard erc-20 interface in which `approve` has no return value so that + * this function will work for both standard and non standard tokens + * + * @param _token token to check the allowance in + * @param _spender approved address + * @param _value allowance amount + */ + function ensureAllowance(IERC20Token _token, address _spender, uint256 _value) private { + uint256 allowance = _token.allowance(this, _spender); + if (allowance < _value) { + if (allowance > 0) + INonStandardERC20(_token).approve(_spender, 0); + INonStandardERC20(_token).approve(_spender, _value); + } + } + + function isWhitelisted(IERC20Token[] _path, address _receiver) private view returns (bool) { + IContractFeatures features = IContractFeatures(addressOf(CONTRACT_FEATURES)); + for (uint256 i = 1; i < _path.length; i += 2) { + IBancorConverter converter = IBancorConverter(ISmartToken(_path[i]).owner()); + if (features.isSupported(converter, FeatureIds.CONVERTER_CONVERSION_WHITELIST)) { + IWhitelist whitelist = converter.conversionWhitelist(); + if (whitelist != address(0) && !whitelist.isWhitelisted(_receiver)) + return false; + } + } + return true; + } + + function handleValue(IERC20Token _token, uint256 _amount, bool _claim) private { + // if ETH is provided, ensure that the amount is identical to _amount, verify that the source token is an ether token and deposit the ETH in it + if (msg.value > 0) { + require(_amount == msg.value && etherTokens[_token]); + IEtherToken(_token).deposit.value(msg.value)(); + } + // Otherwise, claim the tokens from the sender if needed + else if (_claim) { + ensureTransferFrom(_token, msg.sender, this, _amount); + } + } + + /** + * @dev deprecated, backward compatibility + */ + function convert( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn + ) public payable returns (uint256) + { + return convert2(_path, _amount, _minReturn, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function claimAndConvert( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn + ) public returns (uint256) + { + return claimAndConvert2(_path, _amount, _minReturn, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function convertFor( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for + ) public payable returns (uint256) + { + return convertFor2(_path, _amount, _minReturn, _for, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function claimAndConvertFor( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for + ) public returns (uint256) + { + return claimAndConvertFor2(_path, _amount, _minReturn, _for, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function xConvert( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + bytes32 _toBlockchain, + bytes32 _to, + uint256 _conversionId + ) + public + payable + returns (uint256) + { + return xConvert2(_path, _amount, _minReturn, _toBlockchain, _to, _conversionId, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function xConvertPrioritized3( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + bytes32 _toBlockchain, + bytes32 _to, + uint256 _conversionId, + uint256[] memory, + address _affiliateAccount, + uint256 _affiliateFee + ) + public + payable + returns (uint256) + { + return xConvert2(_path, _amount, _minReturn, _toBlockchain, _to, _conversionId, _affiliateAccount, _affiliateFee); + } + + /** + * @dev deprecated, backward compatibility + */ + function xConvertPrioritized2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + bytes32 _toBlockchain, + bytes32 _to, + uint256 _conversionId, + uint256[] memory + ) + public + payable + returns (uint256) + { + return xConvert2(_path, _amount, _minReturn, _toBlockchain, _to, _conversionId, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function xConvertPrioritized( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + bytes32 _toBlockchain, + bytes32 _to, + uint256 _conversionId, + uint256, + uint8, + bytes32, + bytes32 + ) + public + payable + returns (uint256) + { + return xConvert2(_path, _amount, _minReturn, _toBlockchain, _to, _conversionId, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function convertForPrioritized4( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256[] memory, + address _affiliateAccount, + uint256 _affiliateFee + ) + public + payable + returns (uint256) + { + return convertFor2(_path, _amount, _minReturn, _for, _affiliateAccount, _affiliateFee); + } + + /** + * @dev deprecated, backward compatibility + */ + function convertForPrioritized3( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256, + uint256, + uint8, + bytes32, + bytes32 + ) + public + payable + returns (uint256) + { + return convertFor2(_path, _amount, _minReturn, _for, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function convertForPrioritized2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256, + uint8, + bytes32, + bytes32 + ) + public + payable + returns (uint256) + { + return convertFor2(_path, _amount, _minReturn, _for, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function convertForPrioritized( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256, + uint256, + uint8, + bytes32, + bytes32 + ) + public payable returns (uint256) + { + return convertFor2(_path, _amount, _minReturn, _for, address(0), 0); + } +} diff --git a/contracts/bancor/BancorNetworkPathFinder.sol b/contracts/bancor/BancorNetworkPathFinder.sol new file mode 100755 index 0000000..4a15ded --- /dev/null +++ b/contracts/bancor/BancorNetworkPathFinder.sol @@ -0,0 +1,165 @@ +pragma solidity 0.4.26; +import './utility/ContractRegistryClient.sol'; +import './converter/interfaces/IBancorConverterRegistry.sol'; +import './converter/interfaces/IBancorConverter.sol'; +import './token/interfaces/ISmartToken.sol'; + +/** + * @dev The BancorNetworkPathFinder contract allows generating a conversion path between any token pair in the Bancor Network. + * The path can then be used in various functions in the BancorNetwork contract. + * + * See the BancorNetwork contract for conversion path format. +*/ +contract BancorNetworkPathFinder is ContractRegistryClient { + address public anchorToken; + + /** + * @dev initializes a new BancorNetworkPathFinder instance + * + * @param _registry address of a contract registry contract + */ + constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public { + } + + /** + * @dev updates the anchor token + * + * @param _anchorToken address of the anchor token + */ + function setAnchorToken(address _anchorToken) public ownerOnly { + anchorToken = _anchorToken; + } + + /** + * @dev generates and returns the conversion path between a given token pair in the Bancor Network + * + * @param _sourceToken address of the source token + * @param _targetToken address of the target token + * + * @return path from the source token to the target token + */ + function generatePath(address _sourceToken, address _targetToken) public view returns (address[] memory) { + IBancorConverterRegistry converterRegistry = IBancorConverterRegistry(addressOf(BANCOR_CONVERTER_REGISTRY)); + address[] memory sourcePath = getPath(_sourceToken, converterRegistry); + address[] memory targetPath = getPath(_targetToken, converterRegistry); + return getShortestPath(sourcePath, targetPath); + } + + /** + * @dev generates and returns the conversion path between a given token and the anchor token + * + * @param _token address of the token + * @param _converterRegistry address of the converter registry + * + * @return path from the input token to the anchor token + */ + function getPath(address _token, IBancorConverterRegistry _converterRegistry) private view returns (address[] memory) { + if (_token == anchorToken) + return getInitialArray(_token); + + address[] memory smartTokens; + if (_converterRegistry.isSmartToken(_token)) + smartTokens = getInitialArray(_token); + else + smartTokens = _converterRegistry.getConvertibleTokenSmartTokens(_token); + + for (uint256 n = 0; n < smartTokens.length; n++) { + IBancorConverter converter = IBancorConverter(ISmartToken(smartTokens[n]).owner()); + uint256 connectorTokenCount = converter.connectorTokenCount(); + for (uint256 i = 0; i < connectorTokenCount; i++) { + address connectorToken = converter.connectorTokens(i); + if (connectorToken != _token) { + address[] memory path = getPath(connectorToken, _converterRegistry); + if (path.length > 0) + return getExtendedArray(_token, smartTokens[n], path); + } + } + } + + return new address[](0); + } + + /** + * @dev merges two paths with a common suffix into one + * + * @param _sourcePath address of the source path + * @param _targetPath address of the target path + * + * @return merged path + */ + function getShortestPath(address[] memory _sourcePath, address[] memory _targetPath) private pure returns (address[] memory) { + if (_sourcePath.length > 0 && _targetPath.length > 0) { + uint256 i = _sourcePath.length; + uint256 j = _targetPath.length; + while (i > 0 && j > 0 && _sourcePath[i - 1] == _targetPath[j - 1]) { + i--; + j--; + } + + address[] memory path = new address[](i + j + 1); + for (uint256 m = 0; m <= i; m++) + path[m] = _sourcePath[m]; + for (uint256 n = j; n > 0; n--) + path[path.length - n] = _targetPath[n - 1]; + + uint256 length = 0; + for (uint256 p = 0; p < path.length; p += 1) { + for (uint256 q = p + 2; q < path.length - p % 2; q += 2) { + if (path[p] == path[q]) + p = q; + } + path[length++] = path[p]; + } + + return getPartialArray(path, length); + } + + return new address[](0); + } + + /** + * @dev creates a new array containing a single item + * + * @param _item item + * + * @return initial array + */ + function getInitialArray(address _item) private pure returns (address[] memory) { + address[] memory array = new address[](1); + array[0] = _item; + return array; + } + + /** + * @dev prepends two items to the beginning of an array + * + * @param _item0 first item + * @param _item1 second item + * @param _array initial array + * + * @return extended array + */ + function getExtendedArray(address _item0, address _item1, address[] memory _array) private pure returns (address[] memory) { + address[] memory array = new address[](2 + _array.length); + array[0] = _item0; + array[1] = _item1; + for (uint256 i = 0; i < _array.length; i++) + array[2 + i] = _array[i]; + return array; + } + + /** + * @dev extracts the prefix of a given array + * + * @param _array given array + * @param _length prefix length + * + * @return partial array + */ + function getPartialArray(address[] memory _array, uint256 _length) private pure returns (address[] memory) { + address[] memory array = new address[](_length); + for (uint256 i = 0; i < _length; i++) + array[i] = _array[i]; + return array; + } +} diff --git a/contracts/bancor/FeatureIds.sol b/contracts/bancor/FeatureIds.sol new file mode 100755 index 0000000..5d9a43a --- /dev/null +++ b/contracts/bancor/FeatureIds.sol @@ -0,0 +1,11 @@ +pragma solidity 0.4.26; + +/** + * @dev Id definitions for bancor contract features + * + * Can be used to query the ContractFeatures contract to check whether a certain feature is supported by a contract +*/ +contract FeatureIds { + // converter features + uint256 public constant CONVERTER_CONVERSION_WHITELIST = 1 << 0; +} diff --git a/contracts/bancor/IBancorNetwork.sol b/contracts/bancor/IBancorNetwork.sol new file mode 100755 index 0000000..c74f66f --- /dev/null +++ b/contracts/bancor/IBancorNetwork.sol @@ -0,0 +1,120 @@ +pragma solidity 0.4.26; +import './token/interfaces/IERC20Token.sol'; + +/* + Bancor Network interface +*/ +contract IBancorNetwork { + function convert2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _affiliateAccount, + uint256 _affiliateFee + ) public payable returns (uint256); + + function claimAndConvert2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _affiliateAccount, + uint256 _affiliateFee + ) public returns (uint256); + + function convertFor2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + address _affiliateAccount, + uint256 _affiliateFee + ) public payable returns (uint256); + + function claimAndConvertFor2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + address _affiliateAccount, + uint256 _affiliateFee + ) public returns (uint256); + + // deprecated, backward compatibility + function convert( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn + ) public payable returns (uint256); + + // deprecated, backward compatibility + function claimAndConvert( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn + ) public returns (uint256); + + // deprecated, backward compatibility + function convertFor( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for + ) public payable returns (uint256); + + // deprecated, backward compatibility + function claimAndConvertFor( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for + ) public returns (uint256); + + // deprecated, backward compatibility + function convertForPrioritized4( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256[] memory _signature, + address _affiliateAccount, + uint256 _affiliateFee + ) public payable returns (uint256); + + // deprecated, backward compatibility + function convertForPrioritized3( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256 _customVal, + uint256 _block, + uint8 _v, + bytes32 _r, + bytes32 _s + ) public payable returns (uint256); + + // deprecated, backward compatibility + function convertForPrioritized2( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256 _block, + uint8 _v, + bytes32 _r, + bytes32 _s + ) public payable returns (uint256); + + // deprecated, backward compatibility + function convertForPrioritized( + IERC20Token[] _path, + uint256 _amount, + uint256 _minReturn, + address _for, + uint256 _block, + uint256 _nonce, + uint8 _v, + bytes32 _r, + bytes32 _s + ) public payable returns (uint256); +} diff --git a/contracts/bancor/bancorx/BancorX.sol b/contracts/bancor/bancorx/BancorX.sol new file mode 100755 index 0000000..7abacb7 --- /dev/null +++ b/contracts/bancor/bancorx/BancorX.sol @@ -0,0 +1,472 @@ +pragma solidity 0.4.26; + +import './interfaces/IBancorXUpgrader.sol'; +import './interfaces/IBancorX.sol'; +import '../token/interfaces/ISmartTokenController.sol'; +import '../utility/ContractRegistryClient.sol'; +import '../utility/SafeMath.sol'; +import '../utility/TokenHolder.sol'; +import '../token/interfaces/ISmartToken.sol'; + +/** + * @dev The BancorX contract allows cross chain token transfers. + * + * There are two processes that take place in the contract - + * - Initiate a cross chain transfer to a target blockchain (locks tokens from the caller account on Ethereum) + * - Report a cross chain transfer initiated on a source blockchain (releases tokens to an account on Ethereum) + * + * Reporting cross chain transfers works similar to standard multisig contracts, meaning that multiple + * callers are required to report a transfer before tokens are released to the target account. +*/ +contract BancorX is IBancorX, TokenHolder, ContractRegistryClient { + using SafeMath for uint256; + + // represents a transaction on another blockchain where tokens were destroyed/locked + struct Transaction { + uint256 amount; + bytes32 fromBlockchain; + address to; + uint8 numOfReports; + bool completed; + } + + uint16 public version = 3; + + uint256 public maxLockLimit; // the maximum amount of tokens that can be locked in one transaction + uint256 public maxReleaseLimit; // the maximum amount of tokens that can be released in one transaction + uint256 public minLimit; // the minimum amount of tokens that can be transferred in one transaction + uint256 public prevLockLimit; // the lock limit *after* the last transaction + uint256 public prevReleaseLimit; // the release limit *after* the last transaction + uint256 public limitIncPerBlock; // how much the limit increases per block + uint256 public prevLockBlockNumber; // the block number of the last lock transaction + uint256 public prevReleaseBlockNumber; // the block number of the last release transaction + uint256 public minRequiredReports; // minimum number of required reports to release tokens + + IERC20Token public token; // erc20 token or smart token + bool public isSmartToken; // false - erc20 token; true - smart token + + bool public xTransfersEnabled = true; // true if x transfers are enabled, false if not + bool public reportingEnabled = true; // true if reporting is enabled, false if not + + // txId -> Transaction + mapping (uint256 => Transaction) public transactions; + + // xTransferId -> txId + mapping (uint256 => uint256) public transactionIds; + + // txId -> reporter -> true if reporter already reported txId + mapping (uint256 => mapping (address => bool)) public reportedTxs; + + // address -> true if address is reporter + mapping (address => bool) public reporters; + + /** + * @dev triggered when tokens are locked in smart contract + * + * @param _from wallet address that the tokens are locked from + * @param _amount amount locked + */ + event TokensLock( + address indexed _from, + uint256 _amount + ); + + /** + * @dev triggered when tokens are released by the smart contract + * + * @param _to wallet address that the tokens are released to + * @param _amount amount released + */ + event TokensRelease( + address indexed _to, + uint256 _amount + ); + + /** + * @dev triggered when xTransfer is successfully called + * + * @param _from wallet address that initiated the xtransfer + * @param _toBlockchain target blockchain + * @param _to target wallet + * @param _amount transfer amount + * @param _id xtransfer id + */ + event XTransfer( + address indexed _from, + bytes32 _toBlockchain, + bytes32 indexed _to, + uint256 _amount, + uint256 _id + ); + + /** + * @dev triggered when report is successfully submitted + * + * @param _reporter reporter wallet + * @param _fromBlockchain source blockchain + * @param _txId tx id on the source blockchain + * @param _to target wallet + * @param _amount transfer amount + * @param _xTransferId xtransfer id + */ + event TxReport( + address indexed _reporter, + bytes32 _fromBlockchain, + uint256 _txId, + address _to, + uint256 _amount, + uint256 _xTransferId + ); + + /** + * @dev triggered when final report is successfully submitted + * + * @param _to target wallet + * @param _id xtransfer id + */ + event XTransferComplete( + address _to, + uint256 _id + ); + + /** + * @dev initializes a new BancorX instance + * + * @param _maxLockLimit maximum amount of tokens that can be locked in one transaction + * @param _maxReleaseLimit maximum amount of tokens that can be released in one transaction + * @param _minLimit minimum amount of tokens that can be transferred in one transaction + * @param _limitIncPerBlock how much the limit increases per block + * @param _minRequiredReports minimum number of reporters to report transaction before tokens can be released + * @param _registry address of contract registry + * @param _token erc20 token or smart token + * @param _isSmartToken false - erc20 token; true - smart token + */ + constructor( + uint256 _maxLockLimit, + uint256 _maxReleaseLimit, + uint256 _minLimit, + uint256 _limitIncPerBlock, + uint256 _minRequiredReports, + IContractRegistry _registry, + IERC20Token _token, + bool _isSmartToken + ) ContractRegistryClient(_registry) + public + { + // the maximum limits, minimum limit, and limit increase per block + maxLockLimit = _maxLockLimit; + maxReleaseLimit = _maxReleaseLimit; + minLimit = _minLimit; + limitIncPerBlock = _limitIncPerBlock; + minRequiredReports = _minRequiredReports; + + // previous limit is _maxLimit, and previous block number is current block number + prevLockLimit = _maxLockLimit; + prevReleaseLimit = _maxReleaseLimit; + prevLockBlockNumber = block.number; + prevReleaseBlockNumber = block.number; + + token = _token; + isSmartToken = _isSmartToken; + } + + // validates that the caller is a reporter + modifier isReporter { + require(reporters[msg.sender]); + _; + } + + // allows execution only when x transfers are enabled + modifier whenXTransfersEnabled { + require(xTransfersEnabled); + _; + } + + // allows execution only when reporting is enabled + modifier whenReportingEnabled { + require(reportingEnabled); + _; + } + + /** + * @dev setter + * + * @param _maxLockLimit new maxLockLimit + */ + function setMaxLockLimit(uint256 _maxLockLimit) public ownerOnly { + maxLockLimit = _maxLockLimit; + } + + /** + * @dev setter + * + * @param _maxReleaseLimit new maxReleaseLimit + */ + function setMaxReleaseLimit(uint256 _maxReleaseLimit) public ownerOnly { + maxReleaseLimit = _maxReleaseLimit; + } + + /** + * @dev setter + * + * @param _minLimit new minLimit + */ + function setMinLimit(uint256 _minLimit) public ownerOnly { + minLimit = _minLimit; + } + + /** + * @dev setter + * + * @param _limitIncPerBlock new limitIncPerBlock + */ + function setLimitIncPerBlock(uint256 _limitIncPerBlock) public ownerOnly { + limitIncPerBlock = _limitIncPerBlock; + } + + /** + * @dev setter + * + * @param _minRequiredReports new minRequiredReports + */ + function setMinRequiredReports(uint256 _minRequiredReports) public ownerOnly { + minRequiredReports = _minRequiredReports; + } + + /** + * @dev allows the owner to set/remove reporters + * + * @param _reporter reporter whos status is to be set + * @param _active true if the reporter is approved, false otherwise + */ + function setReporter(address _reporter, bool _active) public ownerOnly { + reporters[_reporter] = _active; + } + + /** + * @dev allows the owner enable/disable the xTransfer method + * + * @param _enable true to enable, false to disable + */ + function enableXTransfers(bool _enable) public ownerOnly { + xTransfersEnabled = _enable; + } + + /** + * @dev allows the owner enable/disable the reportTransaction method + * + * @param _enable true to enable, false to disable + */ + function enableReporting(bool _enable) public ownerOnly { + reportingEnabled = _enable; + } + + /** + * @dev upgrades the contract to the latest version + * can only be called by the owner + * note that the owner needs to call acceptOwnership on the new contract after the upgrade + * + * @param _reporters new list of reporters + */ + function upgrade(address[] _reporters) public ownerOnly { + IBancorXUpgrader bancorXUpgrader = IBancorXUpgrader(addressOf(BANCOR_X_UPGRADER)); + + transferOwnership(bancorXUpgrader); + bancorXUpgrader.upgrade(version, _reporters); + acceptOwnership(); + } + + /** + * @dev claims tokens from msg.sender to be converted to tokens on another blockchain + * + * @param _toBlockchain blockchain on which tokens will be issued + * @param _to address to send the tokens to + * @param _amount the amount of tokens to transfer + */ + function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount) public whenXTransfersEnabled { + // get the current lock limit + uint256 currentLockLimit = getCurrentLockLimit(); + + // require that; minLimit <= _amount <= currentLockLimit + require(_amount >= minLimit && _amount <= currentLockLimit); + + lockTokens(_amount); + + // set the previous lock limit and block number + prevLockLimit = currentLockLimit.sub(_amount); + prevLockBlockNumber = block.number; + + // emit XTransfer event with id of 0 + emit XTransfer(msg.sender, _toBlockchain, _to, _amount, 0); + } + + /** + * @dev claims tokens from msg.sender to be converted to tokens on another blockchain + * + * @param _toBlockchain blockchain on which tokens will be issued + * @param _to address to send the tokens to + * @param _amount the amount of tokens to transfer + * @param _id pre-determined unique (if non zero) id which refers to this transaction + */ + function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) public whenXTransfersEnabled { + // get the current lock limit + uint256 currentLockLimit = getCurrentLockLimit(); + + // require that; minLimit <= _amount <= currentLockLimit + require(_amount >= minLimit && _amount <= currentLockLimit); + + lockTokens(_amount); + + // set the previous lock limit and block number + prevLockLimit = currentLockLimit.sub(_amount); + prevLockBlockNumber = block.number; + + // emit XTransfer event + emit XTransfer(msg.sender, _toBlockchain, _to, _amount, _id); + } + + /** + * @dev allows reporter to report transaction which occured on another blockchain + * + * @param _fromBlockchain blockchain in which tokens were destroyed + * @param _txId transactionId of transaction thats being reported + * @param _to address to receive tokens + * @param _amount amount of tokens destroyed on another blockchain + * @param _xTransferId unique (if non zero) pre-determined id (unlike _txId which is determined after the transactions been mined) + */ + function reportTx( + bytes32 _fromBlockchain, + uint256 _txId, + address _to, + uint256 _amount, + uint256 _xTransferId + ) + public + isReporter + whenReportingEnabled + { + // require that the transaction has not been reported yet by the reporter + require(!reportedTxs[_txId][msg.sender]); + + // set reported as true + reportedTxs[_txId][msg.sender] = true; + + Transaction storage txn = transactions[_txId]; + + // If the caller is the first reporter, set the transaction details + if (txn.numOfReports == 0) { + txn.to = _to; + txn.amount = _amount; + txn.fromBlockchain = _fromBlockchain; + + if (_xTransferId != 0) { + // verify uniqueness of xTransfer id to prevent overwriting + require(transactionIds[_xTransferId] == 0); + transactionIds[_xTransferId] = _txId; + } + } else { + // otherwise, verify transaction details + require(txn.to == _to && txn.amount == _amount && txn.fromBlockchain == _fromBlockchain); + + if (_xTransferId != 0) { + require(transactionIds[_xTransferId] == _txId); + } + } + + // increment the number of reports + txn.numOfReports++; + + emit TxReport(msg.sender, _fromBlockchain, _txId, _to, _amount, _xTransferId); + + // if theres enough reports, try to release tokens + if (txn.numOfReports >= minRequiredReports) { + require(!transactions[_txId].completed); + + // set the transaction as completed + transactions[_txId].completed = true; + + emit XTransferComplete(_to, _xTransferId); + + releaseTokens(_to, _amount); + } + } + + /** + * @dev gets x transfer amount by xTransferId (not txId) + * + * @param _xTransferId unique (if non zero) pre-determined id (unlike _txId which is determined after the transactions been broadcasted) + * @param _for address corresponding to xTransferId + * + * @return amount that was sent in xTransfer corresponding to _xTransferId + */ + function getXTransferAmount(uint256 _xTransferId, address _for) public view returns (uint256) { + // xTransferId -> txId -> Transaction + Transaction storage transaction = transactions[transactionIds[_xTransferId]]; + + // verify that the xTransferId is for _for + require(transaction.to == _for); + + return transaction.amount; + } + + /** + * @dev method for calculating current lock limit + * + * @return the current maximum limit of tokens that can be locked + */ + function getCurrentLockLimit() public view returns (uint256) { + // prevLockLimit + ((currBlockNumber - prevLockBlockNumber) * limitIncPerBlock) + uint256 currentLockLimit = prevLockLimit.add(((block.number).sub(prevLockBlockNumber)).mul(limitIncPerBlock)); + if (currentLockLimit > maxLockLimit) + return maxLockLimit; + return currentLockLimit; + } + + /** + * @dev method for calculating current release limit + * + * @return the current maximum limit of tokens that can be released + */ + function getCurrentReleaseLimit() public view returns (uint256) { + // prevReleaseLimit + ((currBlockNumber - prevReleaseBlockNumber) * limitIncPerBlock) + uint256 currentReleaseLimit = prevReleaseLimit.add(((block.number).sub(prevReleaseBlockNumber)).mul(limitIncPerBlock)); + if (currentReleaseLimit > maxReleaseLimit) + return maxReleaseLimit; + return currentReleaseLimit; + } + + /** + * @dev claims and locks tokens from msg.sender to be converted to tokens on another blockchain + * + * @param _amount the amount of tokens to lock + */ + function lockTokens(uint256 _amount) private { + if (isSmartToken) + ISmartTokenController(ISmartToken(token).owner()).claimTokens(msg.sender, _amount); + else + token.transferFrom(msg.sender, address(this), _amount); + emit TokensLock(msg.sender, _amount); + } + + /** + * @dev private method to release tokens held by the contract + * + * @param _to the address to release tokens to + * @param _amount the amount of tokens to release + */ + function releaseTokens(address _to, uint256 _amount) private { + // get the current release limit + uint256 currentReleaseLimit = getCurrentReleaseLimit(); + + require(_amount >= minLimit && _amount <= currentReleaseLimit); + + // update the previous release limit and block number + prevReleaseLimit = currentReleaseLimit.sub(_amount); + prevReleaseBlockNumber = block.number; + + // no need to require, reverts on failure + token.transfer(_to, _amount); + + emit TokensRelease(_to, _amount); + } +} \ No newline at end of file diff --git a/contracts/bancor/bancorx/XTransferRerouter.sol b/contracts/bancor/bancorx/XTransferRerouter.sol new file mode 100755 index 0000000..c592914 --- /dev/null +++ b/contracts/bancor/bancorx/XTransferRerouter.sol @@ -0,0 +1,56 @@ +pragma solidity 0.4.26; + +import "../utility/Owned.sol"; + +contract XTransferRerouter is Owned { + bool public reroutingEnabled; + + // triggered when a rerouteTx is called + event TxReroute( + uint256 indexed _txId, + bytes32 _toBlockchain, + bytes32 _to + ); + + /** + * @dev initializes a new XTransferRerouter instance + * + * @param _reroutingEnabled intializes transactions routing to enabled/disabled + */ + constructor(bool _reroutingEnabled) public { + reroutingEnabled = _reroutingEnabled; + } + /** + * @dev allows the owner to disable/enable rerouting + * + * @param _enable true to enable, false to disable + */ + function enableRerouting(bool _enable) public ownerOnly { + reroutingEnabled = _enable; + } + + // allows execution only when rerouting enabled + modifier whenReroutingEnabled { + require(reroutingEnabled); + _; + } + + /** + * @dev allows a user to reroute a transaction to a new blockchain/target address + * + * @param _txId the original transaction id + * @param _blockchain the new blockchain name + * @param _to the new target address/account + */ + function rerouteTx( + uint256 _txId, + bytes32 _blockchain, + bytes32 _to + ) + public + whenReroutingEnabled + { + emit TxReroute(_txId, _blockchain, _to); + } + +} \ No newline at end of file diff --git a/contracts/bancor/bancorx/interfaces/IBancorX.sol b/contracts/bancor/bancorx/interfaces/IBancorX.sol new file mode 100755 index 0000000..3908811 --- /dev/null +++ b/contracts/bancor/bancorx/interfaces/IBancorX.sol @@ -0,0 +1,6 @@ +pragma solidity 0.4.26; + +contract IBancorX { + function xTransfer(bytes32 _toBlockchain, bytes32 _to, uint256 _amount, uint256 _id) public; + function getXTransferAmount(uint256 _xTransferId, address _for) public view returns (uint256); +} diff --git a/contracts/bancor/bancorx/interfaces/IBancorXUpgrader.sol b/contracts/bancor/bancorx/interfaces/IBancorXUpgrader.sol new file mode 100755 index 0000000..ced2963 --- /dev/null +++ b/contracts/bancor/bancorx/interfaces/IBancorXUpgrader.sol @@ -0,0 +1,8 @@ +pragma solidity 0.4.26; + +/* + Bancor X Upgrader interface +*/ +contract IBancorXUpgrader { + function upgrade(uint16 _version, address[] _reporters) public; +} diff --git a/contracts/bancor/converter/BancorConverter.sol b/contracts/bancor/converter/BancorConverter.sol new file mode 100755 index 0000000..12ec4f9 --- /dev/null +++ b/contracts/bancor/converter/BancorConverter.sol @@ -0,0 +1,909 @@ +pragma solidity 0.4.26; +import './interfaces/IBancorConverter.sol'; +import './interfaces/IBancorConverterUpgrader.sol'; +import './interfaces/IBancorFormula.sol'; +import '../IBancorNetwork.sol'; +import '../FeatureIds.sol'; +import '../utility/Managed.sol'; +import '../utility/SafeMath.sol'; +import '../utility/ContractRegistryClient.sol'; +import '../utility/interfaces/IContractFeatures.sol'; +import '../utility/interfaces/IAddressList.sol'; +import '../token/SmartTokenController.sol'; +import '../token/interfaces/ISmartToken.sol'; +import '../token/interfaces/INonStandardERC20.sol'; +import '../token/interfaces/IEtherToken.sol'; +import '../bancorx/interfaces/IBancorX.sol'; + +/** + * @dev Bancor Converter + * + * The Bancor converter allows for conversions between a Smart Token and other ERC20 tokens and between different ERC20 tokens and themselves. + * + * This mechanism opens the possibility to create different financial tools (for example, lower slippage in conversions). + * + * The converter is upgradable (just like any SmartTokenController) and all upgrades are opt-in. + * + * WARNING: It is NOT RECOMMENDED to use the converter with Smart Tokens that have less than 8 decimal digits or with very small numbers because of precision loss + * + * Open issues: + * - Front-running attacks are currently mitigated by the following mechanisms: + * - minimum return argument for each conversion provides a way to define a minimum/maximum price for the transaction + * - gas price limit prevents users from having control over the order of execution + * - gas price limit check can be skipped if the transaction comes from a trusted, whitelisted signer + * + * Other potential solutions might include a commit/reveal based schemes + * - Possibly add getters for the reserve fields so that the client won't need to rely on the order in the struct +*/ +contract BancorConverter is IBancorConverter, SmartTokenController, Managed, ContractRegistryClient, FeatureIds { + using SafeMath for uint256; + + uint32 private constant RATIO_RESOLUTION = 1000000; + uint64 private constant CONVERSION_FEE_RESOLUTION = 1000000; + + struct Reserve { + uint256 virtualBalance; // reserve virtual balance + uint32 ratio; // reserve ratio, represented in ppm, 1-1000000 + bool isVirtualBalanceEnabled; // true if virtual balance is enabled, false if not + bool isSaleEnabled; // is sale of the reserve token enabled, can be set by the owner + bool isSet; // used to tell if the mapping element is defined + } + + /** + * @dev version number + */ + uint16 public version = 25; + + IWhitelist public conversionWhitelist; // whitelist contract with list of addresses that are allowed to use the converter + IERC20Token[] public reserveTokens; // ERC20 standard token addresses (prior version 17, use 'connectorTokens' instead) + mapping (address => Reserve) public reserves; // reserve token addresses -> reserve data (prior version 17, use 'connectors' instead) + uint32 private totalReserveRatio = 0; // used to efficiently prevent increasing the total reserve ratio above 100% + uint32 public maxConversionFee = 0; // maximum conversion fee for the lifetime of the contract, + // represented in ppm, 0...1000000 (0 = no fee, 100 = 0.01%, 1000000 = 100%) + uint32 public conversionFee = 0; // current conversion fee, represented in ppm, 0...maxConversionFee + bool public conversionsEnabled = true; // deprecated, backward compatibility + + /** + * @dev triggered when a conversion between two tokens occurs + * + * @param _fromToken ERC20 token converted from + * @param _toToken ERC20 token converted to + * @param _trader wallet that initiated the trade + * @param _amount amount converted, in fromToken + * @param _return amount returned, minus conversion fee + * @param _conversionFee conversion fee + */ + event Conversion( + address indexed _fromToken, + address indexed _toToken, + address indexed _trader, + uint256 _amount, + uint256 _return, + int256 _conversionFee + ); + + /** + * @dev triggered after a conversion with new price data + * + * @param _connectorToken reserve token + * @param _tokenSupply smart token supply + * @param _connectorBalance reserve balance + * @param _connectorWeight reserve ratio + */ + event PriceDataUpdate( + address indexed _connectorToken, + uint256 _tokenSupply, + uint256 _connectorBalance, + uint32 _connectorWeight + ); + + /** + * @dev triggered when the conversion fee is updated + * + * @param _prevFee previous fee percentage, represented in ppm + * @param _newFee new fee percentage, represented in ppm + */ + event ConversionFeeUpdate(uint32 _prevFee, uint32 _newFee); + + /** + * @dev initializes a new BancorConverter instance + * + * @param _token smart token governed by the converter + * @param _registry address of a contract registry contract + * @param _maxConversionFee maximum conversion fee, represented in ppm + * @param _reserveToken optional, initial reserve, allows defining the first reserve at deployment time + * @param _reserveRatio optional, ratio for the initial reserve + */ + constructor( + ISmartToken _token, + IContractRegistry _registry, + uint32 _maxConversionFee, + IERC20Token _reserveToken, + uint32 _reserveRatio + ) ContractRegistryClient(_registry) + public + SmartTokenController(_token) + validConversionFee(_maxConversionFee) + { + IContractFeatures features = IContractFeatures(addressOf(CONTRACT_FEATURES)); + + // initialize supported features + if (features != address(0)) + features.enableFeatures(FeatureIds.CONVERTER_CONVERSION_WHITELIST, true); + + maxConversionFee = _maxConversionFee; + + if (_reserveToken != address(0)) + addReserve(_reserveToken, _reserveRatio); + } + + // validates a reserve token address - verifies that the address belongs to one of the reserve tokens + modifier validReserve(IERC20Token _address) { + require(reserves[_address].isSet); + _; + } + + // validates conversion fee + modifier validConversionFee(uint32 _conversionFee) { + require(_conversionFee >= 0 && _conversionFee <= CONVERSION_FEE_RESOLUTION); + _; + } + + // validates reserve ratio + modifier validReserveRatio(uint32 _ratio) { + require(_ratio > 0 && _ratio <= RATIO_RESOLUTION); + _; + } + + // allows execution only if the total-supply of the token is greater than zero + modifier totalSupplyGreaterThanZeroOnly { + require(token.totalSupply() > 0); + _; + } + + // allows execution only on a multiple-reserve converter + modifier multipleReservesOnly { + require(reserveTokens.length > 1); + _; + } + + /** + * @dev returns the number of reserve tokens defined + * note that prior to version 17, you should use 'connectorTokenCount' instead + * + * @return number of reserve tokens + */ + function reserveTokenCount() public view returns (uint16) { + return uint16(reserveTokens.length); + } + + /** + * @dev allows the owner to update & enable the conversion whitelist contract address + * when set, only addresses that are whitelisted are actually allowed to use the converter + * note that the whitelist check is actually done by the BancorNetwork contract + * + * @param _whitelist address of a whitelist contract + */ + function setConversionWhitelist(IWhitelist _whitelist) + public + ownerOnly + notThis(_whitelist) + { + conversionWhitelist = _whitelist; + } + + /** + * @dev allows transferring the token ownership + * the new owner needs to accept the transfer + * can only be called by the contract owner + * note that token ownership can only be transferred while the owner is the converter upgrader contract + * + * @param _newOwner new token owner + */ + function transferTokenOwnership(address _newOwner) + public + ownerOnly + only(BANCOR_CONVERTER_UPGRADER) + { + super.transferTokenOwnership(_newOwner); + } + + /** + * @dev used by a new owner to accept a token ownership transfer + * can only be called by the contract owner + * note that token ownership can only be accepted if its total-supply is greater than zero + */ + function acceptTokenOwnership() + public + ownerOnly + totalSupplyGreaterThanZeroOnly + { + super.acceptTokenOwnership(); + } + + /** + * @dev updates the current conversion fee + * can only be called by the manager + * + * @param _conversionFee new conversion fee, represented in ppm + */ + function setConversionFee(uint32 _conversionFee) + public + ownerOrManagerOnly + { + require(_conversionFee >= 0 && _conversionFee <= maxConversionFee); + emit ConversionFeeUpdate(conversionFee, _conversionFee); + conversionFee = _conversionFee; + } + + /** + * @dev given a return amount, returns the amount minus the conversion fee + * + * @param _amount return amount + * @param _magnitude 1 for standard conversion, 2 for cross reserve conversion + * + * @return return amount minus conversion fee + */ + function getFinalAmount(uint256 _amount, uint8 _magnitude) public view returns (uint256) { + return _amount.mul((CONVERSION_FEE_RESOLUTION - conversionFee) ** _magnitude).div(CONVERSION_FEE_RESOLUTION ** _magnitude); + } + + /** + * @dev withdraws tokens held by the converter and sends them to an account + * can only be called by the owner + * note that reserve tokens can only be withdrawn by the owner while the converter is inactive + * unless the owner is the converter upgrader contract + * + * @param _token ERC20 token contract address + * @param _to account to receive the new amount + * @param _amount amount to withdraw + */ + function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public { + address converterUpgrader = addressOf(BANCOR_CONVERTER_UPGRADER); + + // if the token is not a reserve token, allow withdrawal + // otherwise verify that the converter is inactive or that the owner is the upgrader contract + require(!reserves[_token].isSet || token.owner() != address(this) || owner == converterUpgrader); + super.withdrawTokens(_token, _to, _amount); + } + + /** + * @dev upgrades the converter to the latest version + * can only be called by the owner + * note that the owner needs to call acceptOwnership/acceptManagement on the new converter after the upgrade + */ + function upgrade() public ownerOnly { + IBancorConverterUpgrader converterUpgrader = IBancorConverterUpgrader(addressOf(BANCOR_CONVERTER_UPGRADER)); + + transferOwnership(converterUpgrader); + converterUpgrader.upgrade(version); + acceptOwnership(); + } + + /** + * @dev defines a new reserve for the token + * can only be called by the owner while the converter is inactive + * note that prior to version 17, you should use 'addConnector' instead + * + * @param _token address of the reserve token + * @param _ratio constant reserve ratio, represented in ppm, 1-1000000 + */ + function addReserve(IERC20Token _token, uint32 _ratio) + public + ownerOnly + inactive + validAddress(_token) + notThis(_token) + validReserveRatio(_ratio) + { + require(_token != token && !reserves[_token].isSet && totalReserveRatio + _ratio <= RATIO_RESOLUTION); // validate input + + reserves[_token].ratio = _ratio; + reserves[_token].isVirtualBalanceEnabled = false; + reserves[_token].virtualBalance = 0; + reserves[_token].isSaleEnabled = true; + reserves[_token].isSet = true; + reserveTokens.push(_token); + totalReserveRatio += _ratio; + } + + /** + * @dev updates a reserve's virtual balance + * only used during an upgrade process + * can only be called by the contract owner while the owner is the converter upgrader contract + * note that prior to version 17, you should use 'updateConnector' instead + * + * @param _reserveToken address of the reserve token + * @param _virtualBalance new reserve virtual balance, or 0 to disable virtual balance + */ + function updateReserveVirtualBalance(IERC20Token _reserveToken, uint256 _virtualBalance) + public + ownerOnly + only(BANCOR_CONVERTER_UPGRADER) + validReserve(_reserveToken) + { + Reserve storage reserve = reserves[_reserveToken]; + reserve.isVirtualBalanceEnabled = _virtualBalance != 0; + reserve.virtualBalance = _virtualBalance; + } + + /** + * @dev returns the reserve's ratio + * added in version 22 + * + * @param _reserveToken reserve token contract address + * + * @return reserve ratio + */ + function getReserveRatio(IERC20Token _reserveToken) + public + view + validReserve(_reserveToken) + returns (uint256) + { + return reserves[_reserveToken].ratio; + } + + /** + * @dev returns the reserve's balance + * note that prior to version 17, you should use 'getConnectorBalance' instead + * + * @param _reserveToken reserve token contract address + * + * @return reserve balance + */ + function getReserveBalance(IERC20Token _reserveToken) + public + view + validReserve(_reserveToken) + returns (uint256) + { + return _reserveToken.balanceOf(this); + } + + /** + * @dev calculates the expected return of converting a given amount of tokens + * + * @param _fromToken contract address of the token to convert from + * @param _toToken contract address of the token to convert to + * @param _amount amount of tokens received from the user + * + * @return amount of tokens that the user will receive + * @return amount of tokens that the user will pay as fee + */ + function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256, uint256) { + require(_fromToken != _toToken); // validate input + + // conversion between the token and one of its reserves + if (_toToken == token) + return getPurchaseReturn(_fromToken, _amount); + else if (_fromToken == token) + return getSaleReturn(_toToken, _amount); + + // conversion between 2 reserves + return getCrossReserveReturn(_fromToken, _toToken, _amount); + } + + /** + * @dev calculates the expected return of buying with a given amount of tokens + * + * @param _reserveToken contract address of the reserve token + * @param _depositAmount amount of reserve-tokens received from the user + * + * @return amount of supply-tokens that the user will receive + * @return amount of supply-tokens that the user will pay as fee + */ + function getPurchaseReturn(IERC20Token _reserveToken, uint256 _depositAmount) + public + view + active + validReserve(_reserveToken) + returns (uint256, uint256) + { + Reserve storage reserve = reserves[_reserveToken]; + + uint256 tokenSupply = token.totalSupply(); + uint256 reserveBalance = _reserveToken.balanceOf(this); + IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); + uint256 amount = formula.calculatePurchaseReturn(tokenSupply, reserveBalance, reserve.ratio, _depositAmount); + uint256 finalAmount = getFinalAmount(amount, 1); + + // return the amount minus the conversion fee and the conversion fee + return (finalAmount, amount - finalAmount); + } + + /** + * @dev calculates the expected return of selling a given amount of tokens + * + * @param _reserveToken contract address of the reserve token + * @param _sellAmount amount of supply-tokens received from the user + * + * @return amount of reserve-tokens that the user will receive + * @return amount of reserve-tokens that the user will pay as fee + */ + function getSaleReturn(IERC20Token _reserveToken, uint256 _sellAmount) + public + view + active + validReserve(_reserveToken) + returns (uint256, uint256) + { + Reserve storage reserve = reserves[_reserveToken]; + uint256 tokenSupply = token.totalSupply(); + uint256 reserveBalance = _reserveToken.balanceOf(this); + IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); + uint256 amount = formula.calculateSaleReturn(tokenSupply, reserveBalance, reserve.ratio, _sellAmount); + uint256 finalAmount = getFinalAmount(amount, 1); + + // return the amount minus the conversion fee and the conversion fee + return (finalAmount, amount - finalAmount); + } + + /** + * @dev calculates the expected return of converting a given amount from one reserve to another + * note that prior to version 17, you should use 'getCrossConnectorReturn' instead + * + * @param _fromReserveToken contract address of the reserve token to convert from + * @param _toReserveToken contract address of the reserve token to convert to + * @param _amount amount of tokens received from the user + * + * @return amount of tokens that the user will receive + * @return amount of tokens that the user will pay as fee + */ + function getCrossReserveReturn(IERC20Token _fromReserveToken, IERC20Token _toReserveToken, uint256 _amount) + public + view + active + validReserve(_fromReserveToken) + validReserve(_toReserveToken) + returns (uint256, uint256) + { + Reserve storage fromReserve = reserves[_fromReserveToken]; + Reserve storage toReserve = reserves[_toReserveToken]; + + IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); + uint256 amount = formula.calculateCrossReserveReturn( + getReserveBalance(_fromReserveToken), + fromReserve.ratio, + getReserveBalance(_toReserveToken), + toReserve.ratio, + _amount); + uint256 finalAmount = getFinalAmount(amount, 2); + + // return the amount minus the conversion fee and the conversion fee + // the fee is higher (magnitude = 2) since cross reserve conversion equals 2 conversions (from / to the smart token) + return (finalAmount, amount - finalAmount); + } + + /** + * @dev converts a specific amount of _fromToken to _toToken + * can only be called by the bancor network contract + * + * @param _fromToken ERC20 token to convert from + * @param _toToken ERC20 token to convert to + * @param _amount amount to convert, in fromToken + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * + * @return conversion return amount + */ + function convertInternal(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) + public + only(BANCOR_NETWORK) + greaterThanZero(_minReturn) + returns (uint256) + { + require(_fromToken != _toToken); // validate input + + // conversion between the token and one of its reserves + if (_toToken == token) + return buy(_fromToken, _amount, _minReturn); + else if (_fromToken == token) + return sell(_toToken, _amount, _minReturn); + + uint256 amount; + uint256 feeAmount; + + // conversion between 2 reserves + (amount, feeAmount) = getCrossReserveReturn(_fromToken, _toToken, _amount); + // ensure the trade gives something in return and meets the minimum requested amount + require(amount != 0 && amount >= _minReturn); + + Reserve storage fromReserve = reserves[_fromToken]; + Reserve storage toReserve = reserves[_toToken]; + + // ensure that the trade won't deplete the reserve balance + uint256 toReserveBalance = getReserveBalance(_toToken); + assert(amount < toReserveBalance); + + // transfer funds from the caller in the from reserve token + ensureTransferFrom(_fromToken, msg.sender, this, _amount); + // transfer funds to the caller in the to reserve token + ensureTransferFrom(_toToken, this, msg.sender, amount); + + // dispatch the conversion event + // the fee is higher (magnitude = 2) since cross reserve conversion equals 2 conversions (from / to the smart token) + dispatchConversionEvent(_fromToken, _toToken, _amount, amount, feeAmount); + + // dispatch price data updates for the smart token / both reserves + emit PriceDataUpdate(_fromToken, token.totalSupply(), _fromToken.balanceOf(this), fromReserve.ratio); + emit PriceDataUpdate(_toToken, token.totalSupply(), _toToken.balanceOf(this), toReserve.ratio); + return amount; + } + + /** + * @dev buys the token by depositing one of its reserve tokens + * + * @param _reserveToken reserve token contract address + * @param _depositAmount amount to deposit (in the reserve token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * + * @return buy return amount + */ + function buy(IERC20Token _reserveToken, uint256 _depositAmount, uint256 _minReturn) internal returns (uint256) { + uint256 amount; + uint256 feeAmount; + (amount, feeAmount) = getPurchaseReturn(_reserveToken, _depositAmount); + // ensure the trade gives something in return and meets the minimum requested amount + require(amount != 0 && amount >= _minReturn); + + Reserve storage reserve = reserves[_reserveToken]; + + // transfer funds from the caller in the reserve token + ensureTransferFrom(_reserveToken, msg.sender, this, _depositAmount); + // issue new funds to the caller in the smart token + token.issue(msg.sender, amount); + + // dispatch the conversion event + dispatchConversionEvent(_reserveToken, token, _depositAmount, amount, feeAmount); + + // dispatch price data update for the smart token/reserve + emit PriceDataUpdate(_reserveToken, token.totalSupply(), _reserveToken.balanceOf(this), reserve.ratio); + return amount; + } + + /** + * @dev sells the token by withdrawing from one of its reserve tokens + * + * @param _reserveToken reserve token contract address + * @param _sellAmount amount to sell (in the smart token) + * @param _minReturn if the conversion results in an amount smaller the minimum return - it is cancelled, must be nonzero + * + * @return sell return amount + */ + function sell(IERC20Token _reserveToken, uint256 _sellAmount, uint256 _minReturn) internal returns (uint256) { + require(_sellAmount <= token.balanceOf(msg.sender)); // validate input + uint256 amount; + uint256 feeAmount; + (amount, feeAmount) = getSaleReturn(_reserveToken, _sellAmount); + // ensure the trade gives something in return and meets the minimum requested amount + require(amount != 0 && amount >= _minReturn); + + // ensure that the trade will only deplete the reserve balance if the total supply is depleted as well + uint256 tokenSupply = token.totalSupply(); + uint256 reserveBalance = _reserveToken.balanceOf(this); + assert(amount < reserveBalance || (amount == reserveBalance && _sellAmount == tokenSupply)); + + Reserve storage reserve = reserves[_reserveToken]; + + // destroy _sellAmount from the caller's balance in the smart token + token.destroy(msg.sender, _sellAmount); + // transfer funds to the caller in the reserve token + ensureTransferFrom(_reserveToken, this, msg.sender, amount); + + // dispatch the conversion event + dispatchConversionEvent(token, _reserveToken, _sellAmount, amount, feeAmount); + + // dispatch price data update for the smart token/reserve + emit PriceDataUpdate(_reserveToken, token.totalSupply(), _reserveToken.balanceOf(this), reserve.ratio); + return amount; + } + + /** + * @dev converts a specific amount of _fromToken to _toToken + * note that prior to version 16, you should use 'convert' instead + * + * @param _fromToken ERC20 token to convert from + * @param _toToken ERC20 token to convert to + * @param _amount amount to convert, in fromToken + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return conversion return amount + */ + function convert2(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) public returns (uint256) { + IERC20Token[] memory path = new IERC20Token[](3); + (path[0], path[1], path[2]) = (_fromToken, token, _toToken); + return quickConvert2(path, _amount, _minReturn, _affiliateAccount, _affiliateFee); + } + + /** + * @dev converts the token to any other token in the bancor network by following a predefined conversion path + * note that when converting from an ERC20 token (as opposed to a smart token), allowance must be set beforehand + * note that prior to version 16, you should use 'quickConvert' instead + * + * @param _path conversion path, see conversion path format in the BancorNetwork contract + * @param _amount amount to convert from (in the initial source token) + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _affiliateAccount affiliate account + * @param _affiliateFee affiliate fee in PPM + * + * @return tokens issued in return + */ + function quickConvert2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) + public + payable + returns (uint256) + { + IBancorNetwork bancorNetwork = IBancorNetwork(addressOf(BANCOR_NETWORK)); + + // we need to transfer the source tokens from the caller to the BancorNetwork contract, + // so it can execute the conversion on behalf of the caller + if (msg.value == 0) { + // not ETH, send the source tokens to the BancorNetwork contract + // if the token is the smart token, no allowance is required - destroy the tokens + // from the caller and issue them to the BancorNetwork contract + if (_path[0] == token) { + token.destroy(msg.sender, _amount); // destroy _amount tokens from the caller's balance in the smart token + token.issue(bancorNetwork, _amount); // issue _amount new tokens to the BancorNetwork contract + } else { + // otherwise, we assume we already have allowance, transfer the tokens directly to the BancorNetwork contract + ensureTransferFrom(_path[0], msg.sender, bancorNetwork, _amount); + } + } + + // execute the conversion and pass on the ETH with the call + return bancorNetwork.convertFor2.value(msg.value)(_path, _amount, _minReturn, msg.sender, _affiliateAccount, _affiliateFee); + } + + /** + * @dev allows a user to convert BNT that was sent from another blockchain into any other + * token on the BancorNetwork without specifying the amount of BNT to be converted, but + * rather by providing the xTransferId which allows us to get the amount from BancorX. + * note that prior to version 16, you should use 'completeXConversion' instead + * + * @param _path conversion path, see conversion path format in the BancorNetwork contract + * @param _minReturn if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero + * @param _conversionId pre-determined unique (if non zero) id which refers to this transaction + * + * @return tokens issued in return + */ + function completeXConversion2( + IERC20Token[] _path, + uint256 _minReturn, + uint256 _conversionId + ) + public + returns (uint256) + { + IBancorX bancorX = IBancorX(addressOf(BANCOR_X)); + IBancorNetwork bancorNetwork = IBancorNetwork(addressOf(BANCOR_NETWORK)); + + // verify that the first token in the path is BNT + require(_path[0] == addressOf(BNT_TOKEN)); + + // get conversion amount from BancorX contract + uint256 amount = bancorX.getXTransferAmount(_conversionId, msg.sender); + + // send BNT from msg.sender to the BancorNetwork contract + token.destroy(msg.sender, amount); + token.issue(bancorNetwork, amount); + + return bancorNetwork.convertFor2(_path, amount, _minReturn, msg.sender, address(0), 0); + } + + /** + * @dev returns whether or not the caller is an administrator + */ + function isAdmin() internal view returns (bool) { + return msg.sender == owner || msg.sender == manager; + } + + /** + * @dev ensures transfer of tokens, taking into account that some ERC-20 implementations don't return + * true on success but revert on failure instead + * + * @param _token the token to transfer + * @param _from the address to transfer the tokens from + * @param _to the address to transfer the tokens to + * @param _amount the amount to transfer + */ + function ensureTransferFrom(IERC20Token _token, address _from, address _to, uint256 _amount) private { + // We must assume that functions `transfer` and `transferFrom` do not return anything, + // because not all tokens abide the requirement of the ERC20 standard to return success or failure. + // This is because in the current compiler version, the calling contract can handle more returned data than expected but not less. + // This may change in the future, so that the calling contract will revert if the size of the data is not exactly what it expects. + uint256 prevBalance = _token.balanceOf(_to); + if (_from == address(this)) + INonStandardERC20(_token).transfer(_to, _amount); + else + INonStandardERC20(_token).transferFrom(_from, _to, _amount); + uint256 postBalance = _token.balanceOf(_to); + require(postBalance > prevBalance); + } + + /** + * @dev buys the token with all reserve tokens using the same percentage + * for example, if the caller increases the supply by 10%, + * then it will cost an amount equal to 10% of each reserve token balance + * note that the function can be called only when conversions are enabled + * + * @param _amount amount to increase the supply by (in the smart token) + */ + function fund(uint256 _amount) + public + multipleReservesOnly + { + uint256 supply = token.totalSupply(); + IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); + + // iterate through the reserve tokens and transfer a percentage equal to the ratio between _amount + // and the total supply in each reserve from the caller to the converter + IERC20Token reserveToken; + uint256 reserveBalance; + uint256 reserveAmount; + for (uint16 i = 0; i < reserveTokens.length; i++) { + reserveToken = reserveTokens[i]; + reserveBalance = reserveToken.balanceOf(this); + reserveAmount = formula.calculateFundCost(supply, reserveBalance, totalReserveRatio, _amount); + + Reserve storage reserve = reserves[reserveToken]; + + // transfer funds from the caller in the reserve token + ensureTransferFrom(reserveToken, msg.sender, this, reserveAmount); + + // dispatch price data update for the smart token/reserve + emit PriceDataUpdate(reserveToken, supply + _amount, reserveBalance + reserveAmount, reserve.ratio); + } + + // issue new funds to the caller in the smart token + token.issue(msg.sender, _amount); + } + + /** + * @dev sells the token for all reserve tokens using the same percentage + * for example, if the holder sells 10% of the supply, + * then they will receive 10% of each reserve token balance in return + * note that the function can be called also when conversions are disabled + * + * @param _amount amount to liquidate (in the smart token) + */ + function liquidate(uint256 _amount) + public + multipleReservesOnly + { + uint256 supply = token.totalSupply(); + IBancorFormula formula = IBancorFormula(addressOf(BANCOR_FORMULA)); + + // destroy _amount from the caller's balance in the smart token + token.destroy(msg.sender, _amount); + + // iterate through the reserve tokens and send a percentage equal to the ratio between _amount + // and the total supply from each reserve balance to the caller + IERC20Token reserveToken; + uint256 reserveBalance; + uint256 reserveAmount; + for (uint16 i = 0; i < reserveTokens.length; i++) { + reserveToken = reserveTokens[i]; + reserveBalance = reserveToken.balanceOf(this); + reserveAmount = formula.calculateLiquidateReturn(supply, reserveBalance, totalReserveRatio, _amount); + + Reserve storage reserve = reserves[reserveToken]; + + // transfer funds to the caller in the reserve token + ensureTransferFrom(reserveToken, this, msg.sender, reserveAmount); + + // dispatch price data update for the smart token/reserve + emit PriceDataUpdate(reserveToken, supply - _amount, reserveBalance - reserveAmount, reserve.ratio); + } + } + + /** + * @dev helper, dispatches the Conversion event + * + * @param _fromToken ERC20 token to convert from + * @param _toToken ERC20 token to convert to + * @param _amount amount purchased/sold (in the source token) + * @param _returnAmount amount returned (in the target token) + */ + function dispatchConversionEvent(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _returnAmount, uint256 _feeAmount) private { + // fee amount is converted to 255 bits - + // negative amount means the fee is taken from the source token, positive amount means its taken from the target token + // currently the fee is always taken from the target token + // since we convert it to a signed number, we first ensure that it's capped at 255 bits to prevent overflow + assert(_feeAmount < 2 ** 255); + emit Conversion(_fromToken, _toToken, msg.sender, _amount, _returnAmount, int256(_feeAmount)); + } + + /** + * @dev deprecated, backward compatibility + */ + function change(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) { + return convertInternal(_fromToken, _toToken, _amount, _minReturn); + } + + /** + * @dev deprecated, backward compatibility + */ + function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) { + return convert2(_fromToken, _toToken, _amount, _minReturn, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public payable returns (uint256) { + return quickConvert2(_path, _amount, _minReturn, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function quickConvertPrioritized2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256[] memory, address _affiliateAccount, uint256 _affiliateFee) public payable returns (uint256) { + return quickConvert2(_path, _amount, _minReturn, _affiliateAccount, _affiliateFee); + } + + /** + * @dev deprecated, backward compatibility + */ + function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256, uint8, bytes32, bytes32) public payable returns (uint256) { + return quickConvert2(_path, _amount, _minReturn, address(0), 0); + } + + /** + * @dev deprecated, backward compatibility + */ + function completeXConversion(IERC20Token[] _path, uint256 _minReturn, uint256 _conversionId, uint256, uint8, bytes32, bytes32) public returns (uint256) { + return completeXConversion2(_path, _minReturn, _conversionId); + } + + /** + * @dev deprecated, backward compatibility + */ + function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool) { + Reserve storage reserve = reserves[_address]; + return(reserve.virtualBalance, reserve.ratio, reserve.isVirtualBalanceEnabled, reserve.isSaleEnabled, reserve.isSet); + } + + /** + * @dev deprecated, backward compatibility + */ + function connectorTokens(uint256 _index) public view returns (IERC20Token) { + return BancorConverter.reserveTokens[_index]; + } + + /** + * @dev deprecated, backward compatibility + */ + function connectorTokenCount() public view returns (uint16) { + return reserveTokenCount(); + } + + /** + * @dev deprecated, backward compatibility + */ + function addConnector(IERC20Token _token, uint32 _weight, bool /*_enableVirtualBalance*/) public { + addReserve(_token, _weight); + } + + /** + * @dev deprecated, backward compatibility + */ + function updateConnector(IERC20Token _connectorToken, uint32 /*_weight*/, bool /*_enableVirtualBalance*/, uint256 _virtualBalance) public { + updateReserveVirtualBalance(_connectorToken, _virtualBalance); + } + + /** + * @dev deprecated, backward compatibility + */ + function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256) { + return getReserveBalance(_connectorToken); + } + + /** + * @dev deprecated, backward compatibility + */ + function getCrossConnectorReturn(IERC20Token _fromConnectorToken, IERC20Token _toConnectorToken, uint256 _amount) public view returns (uint256, uint256) { + return getCrossReserveReturn(_fromConnectorToken, _toConnectorToken, _amount); + } +} diff --git a/contracts/bancor/converter/BancorConverterFactory.sol b/contracts/bancor/converter/BancorConverterFactory.sol new file mode 100755 index 0000000..ac7df11 --- /dev/null +++ b/contracts/bancor/converter/BancorConverterFactory.sol @@ -0,0 +1,58 @@ +pragma solidity 0.4.26; +import './BancorConverter.sol'; +import './interfaces/IBancorConverterFactory.sol'; +import '../utility/interfaces/IContractRegistry.sol'; + +/* + Bancor Converter Factory +*/ +contract BancorConverterFactory is IBancorConverterFactory { + /** + * @dev triggered when a new converter is created + * + * @param _converter new converter address + * @param _owner converter owner address + */ + event NewConverter(address indexed _converter, address indexed _owner); + + /** + * @dev initializes a new BancorConverterFactory instance + */ + constructor() public { + } + + /** + * @dev creates a new converter with the given arguments and transfers + * the ownership and management to the sender. + * + * @param _token smart token governed by the converter + * @param _registry address of a contract registry contract + * @param _maxConversionFee maximum conversion fee, represented in ppm + * @param _reserveToken optional, initial reserve, allows defining the first reserve at deployment time + * @param _reserveRatio optional, ratio for the initial reserve + * + * @return a new converter + */ + function createConverter( + ISmartToken _token, + IContractRegistry _registry, + uint32 _maxConversionFee, + IERC20Token _reserveToken, + uint32 _reserveRatio + ) public returns(address converterAddress) { + BancorConverter converter = new BancorConverter( + _token, + _registry, + _maxConversionFee, + _reserveToken, + _reserveRatio + ); + + converter.transferOwnership(msg.sender); + converter.transferManagement(msg.sender); + + address _converterAddress = address(converter); + emit NewConverter(_converterAddress, msg.sender); + return _converterAddress; + } +} diff --git a/contracts/bancor/converter/BancorConverterRegistry.sol b/contracts/bancor/converter/BancorConverterRegistry.sol new file mode 100755 index 0000000..49f07dd --- /dev/null +++ b/contracts/bancor/converter/BancorConverterRegistry.sol @@ -0,0 +1,468 @@ +pragma solidity 0.4.26; +import '../utility/ContractRegistryClient.sol'; +import './interfaces/IBancorConverterRegistry.sol'; +import './interfaces/IBancorConverterRegistryData.sol'; +import '../token/interfaces/ISmartToken.sol'; +import '../token/interfaces/ISmartTokenController.sol'; + +/** + * @dev The BancorConverterRegistry maintains a list of all active converters in the Bancor Network. + * + * Since converters can be upgraded and thus their address can change, the registry actually keeps smart tokens internally and not the converters themselves. + * The active converter for each smart token can be easily accessed by querying the smart token owner. + * + * The registry exposes 3 differnet lists that can be accessed and iterated, based on the use-case of the caller: + * - Smart tokens - can be used to get all the latest / historical data in the network + * - Liquidity pools - can be used to get all liquidity pools for funding, liquidation etc. + * - Convertible tokens - can be used to get all tokens that can be converted in the network (excluding pool + * tokens), and for each one - all smart tokens that hold it in their reserves + * + * + * The contract fires events whenever one of the primitives is added to or removed from the registry + * + * The contract is upgradable. +*/ +contract BancorConverterRegistry is IBancorConverterRegistry, ContractRegistryClient { + /** + * @dev triggered when a smart token is added to the registry + * + * @param _smartToken smart token + */ + event SmartTokenAdded(address indexed _smartToken); + + /** + * @dev triggered when a smart token is removed from the registry + * + * @param _smartToken smart token + */ + event SmartTokenRemoved(address indexed _smartToken); + + /** + * @dev triggered when a liquidity pool is added to the registry + * + * @param _liquidityPool liquidity pool + */ + event LiquidityPoolAdded(address indexed _liquidityPool); + + /** + * @dev triggered when a liquidity pool is removed from the registry + * + * @param _liquidityPool liquidity pool + */ + event LiquidityPoolRemoved(address indexed _liquidityPool); + + /** + * @dev triggered when a convertible token is added to the registry + * + * @param _convertibleToken convertible token + * @param _smartToken associated smart token + */ + event ConvertibleTokenAdded(address indexed _convertibleToken, address indexed _smartToken); + + /** + * @dev triggered when a convertible token is removed from the registry + * + * @param _convertibleToken convertible token + * @param _smartToken associated smart token + */ + event ConvertibleTokenRemoved(address indexed _convertibleToken, address indexed _smartToken); + + /** + * @dev initializes a new BancorConverterRegistry instance + * + * @param _registry address of a contract registry contract + */ + constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public { + } + + /** + * @dev adds a converter to the registry + * anyone can add a converter to the registry, as long as the converter is active and valid + * note that a liquidity pool converter can only be added if there's no existing pool with the same reserve configuration + * + * @param _converter converter + */ + function addConverter(IBancorConverter _converter) external { + // validate input + require(isConverterValid(_converter)); + + IBancorConverterRegistryData converterRegistryData = IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)); + ISmartToken token = ISmartTokenController(_converter).token(); + uint reserveTokenCount = _converter.connectorTokenCount(); + + // add the smart token + addSmartToken(converterRegistryData, token); + if (reserveTokenCount > 1) + addLiquidityPool(converterRegistryData, token); + else + addConvertibleToken(converterRegistryData, token, token); + + // add all reserve tokens + for (uint i = 0; i < reserveTokenCount; i++) + addConvertibleToken(converterRegistryData, _converter.connectorTokens(i), token); + } + + /** + * @dev removes a converter from the registry + * anyone can remove invalid or inactive converters from the registry + * note that the owner can also remove valid converters + * + * @param _converter converter + */ + function removeConverter(IBancorConverter _converter) external { + // validate input + require(msg.sender == owner || !isConverterValid(_converter)); + + IBancorConverterRegistryData converterRegistryData = IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)); + ISmartToken token = ISmartTokenController(_converter).token(); + uint reserveTokenCount = _converter.connectorTokenCount(); + + // remove the smart token + removeSmartToken(converterRegistryData, token); + if (reserveTokenCount > 1) + removeLiquidityPool(converterRegistryData, token); + else + removeConvertibleToken(converterRegistryData, token, token); + + // remove all reserve tokens + for (uint i = 0; i < reserveTokenCount; i++) + removeConvertibleToken(converterRegistryData, _converter.connectorTokens(i), token); + } + + /** + * @dev returns the number of smart tokens in the registry + * + * @return number of smart tokens + */ + function getSmartTokenCount() external view returns (uint) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getSmartTokenCount(); + } + + /** + * @dev returns the list of smart tokens in the registry + * + * @return list of smart tokens + */ + function getSmartTokens() external view returns (address[]) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getSmartTokens(); + } + + /** + * @dev returns the smart token at a given index + * + * @param _index index + * @return smart token at the given index + */ + function getSmartToken(uint _index) external view returns (address) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getSmartToken(_index); + } + + /** + * @dev checks whether or not a given value is a smart token + * + * @param _value value + * @return true if the given value is a smart token, false if not + */ + function isSmartToken(address _value) external view returns (bool) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).isSmartToken(_value); + } + + /** + * @dev returns the number of liquidity pools in the registry + * + * @return number of liquidity pools + */ + function getLiquidityPoolCount() external view returns (uint) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getLiquidityPoolCount(); + } + + /** + * @dev returns the list of liquidity pools in the registry + * + * @return list of liquidity pools + */ + function getLiquidityPools() external view returns (address[]) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getLiquidityPools(); + } + + /** + * @dev returns the liquidity pool at a given index + * + * @param _index index + * @return liquidity pool at the given index + */ + function getLiquidityPool(uint _index) external view returns (address) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getLiquidityPool(_index); + } + + /** + * @dev checks whether or not a given value is a liquidity pool + * + * @param _value value + * @return true if the given value is a liquidity pool, false if not + */ + function isLiquidityPool(address _value) external view returns (bool) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).isLiquidityPool(_value); + } + + /** + * @dev returns the number of convertible tokens in the registry + * + * @return number of convertible tokens + */ + function getConvertibleTokenCount() external view returns (uint) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getConvertibleTokenCount(); + } + + /** + * @dev returns the list of convertible tokens in the registry + * + * @return list of convertible tokens + */ + function getConvertibleTokens() external view returns (address[]) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getConvertibleTokens(); + } + + /** + * @dev returns the convertible token at a given index + * + * @param _index index + * @return convertible token at the given index + */ + function getConvertibleToken(uint _index) external view returns (address) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getConvertibleToken(_index); + } + + /** + * @dev checks whether or not a given value is a convertible token + * + * @param _value value + * @return true if the given value is a convertible token, false if not + */ + function isConvertibleToken(address _value) external view returns (bool) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).isConvertibleToken(_value); + } + + /** + * @dev returns the number of smart tokens associated with a given convertible token + * + * @param _convertibleToken convertible token + * @return number of smart tokens associated with the given convertible token + */ + function getConvertibleTokenSmartTokenCount(address _convertibleToken) external view returns (uint) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getConvertibleTokenSmartTokenCount(_convertibleToken); + } + + /** + * @dev returns the list of smart tokens associated with a given convertible token + * + * @param _convertibleToken convertible token + * @return list of smart tokens associated with the given convertible token + */ + function getConvertibleTokenSmartTokens(address _convertibleToken) external view returns (address[]) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getConvertibleTokenSmartTokens(_convertibleToken); + } + + /** + * @dev returns the smart token associated with a given convertible token at a given index + * + * @param _index index + * @return smart token associated with the given convertible token at the given index + */ + function getConvertibleTokenSmartToken(address _convertibleToken, uint _index) external view returns (address) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).getConvertibleTokenSmartToken(_convertibleToken, _index); + } + + /** + * @dev checks whether or not a given value is a smart token of a given convertible token + * + * @param _convertibleToken convertible token + * @param _value value + * @return true if the given value is a smart token of the given convertible token, false if not + */ + function isConvertibleTokenSmartToken(address _convertibleToken, address _value) external view returns (bool) { + return IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)).isConvertibleTokenSmartToken(_convertibleToken, _value); + } + + /** + * @dev returns a list of converters for a given list of smart tokens + * this is a utility function that can be used to reduce the number of calls to the contract + * + * @param _smartTokens list of smart tokens + * @return list of converters + */ + function getConvertersBySmartTokens(address[] _smartTokens) external view returns (address[]) { + address[] memory converters = new address[](_smartTokens.length); + + for (uint i = 0; i < _smartTokens.length; i++) + converters[i] = ISmartToken(_smartTokens[i]).owner(); + + return converters; + } + + /** + * @dev checks whether or not a given converter is valid + * + * @param _converter converter + * @return true if the given converter is valid, false if not + */ + function isConverterValid(IBancorConverter _converter) public view returns (bool) { + ISmartToken token = ISmartTokenController(_converter).token(); + + // verifies the the smart token has a supply and that the converter is active + if (token.totalSupply() == 0 || token.owner() != address(_converter)) + return false; + + uint reserveTokenCount = _converter.connectorTokenCount(); + address[] memory reserveTokens = new address[](reserveTokenCount); + uint[] memory reserveRatios = new uint[](reserveTokenCount); + + // verifies that the converter holds balance in each of its reserves + for (uint i = 0; i < reserveTokenCount; i++) { + IERC20Token reserveToken = _converter.connectorTokens(i); + if (reserveToken.balanceOf(_converter) == 0) + return false; + reserveTokens[i] = reserveToken; + reserveRatios[i] = getReserveRatio(_converter, reserveToken); + } + + return getLiquidityPoolByReserveConfig(reserveTokens, reserveRatios) == ISmartToken(0); + } + + /** + * @dev searches for a liquidity pool with specific reserve tokens / ratios + * + * @param _reserveTokens reserve tokens + * @param _reserveRatios reserve ratios + * @return the liquidity pool, or zero if no such liquidity pool exists + */ + function getLiquidityPoolByReserveConfig(address[] memory _reserveTokens, uint[] memory _reserveRatios) public view returns (ISmartToken) { + // validate input - ensure that the number of reserve tokens match the number of reserve ratio + if (_reserveTokens.length == _reserveRatios.length && _reserveTokens.length > 1) { + // get the smart tokens of the least frequent token (optimization) + address[] memory convertibleTokenSmartTokens = getLeastFrequentTokenSmartTokens(_reserveTokens); + // search for a converter with an identical reserve-configuration + for (uint i = 0; i < convertibleTokenSmartTokens.length; i++) { + ISmartToken smartToken = ISmartToken(convertibleTokenSmartTokens[i]); + IBancorConverter converter = IBancorConverter(smartToken.owner()); + if (isConverterReserveConfigEqual(converter, _reserveTokens, _reserveRatios)) + return smartToken; + } + } + + return ISmartToken(0); + } + + /** + * @dev adds a smart token to the registry + * + * @param _smartToken smart token + */ + function addSmartToken(IBancorConverterRegistryData _converterRegistryData, address _smartToken) internal { + _converterRegistryData.addSmartToken(_smartToken); + emit SmartTokenAdded(_smartToken); + } + + /** + * @dev removes a smart token from the registry + * + * @param _smartToken smart token + */ + function removeSmartToken(IBancorConverterRegistryData _converterRegistryData, address _smartToken) internal { + _converterRegistryData.removeSmartToken(_smartToken); + emit SmartTokenRemoved(_smartToken); + } + + /** + * @dev adds a liquidity pool to the registry + * + * @param _liquidityPool liquidity pool + */ + function addLiquidityPool(IBancorConverterRegistryData _converterRegistryData, address _liquidityPool) internal { + _converterRegistryData.addLiquidityPool(_liquidityPool); + emit LiquidityPoolAdded(_liquidityPool); + } + + /** + * @dev removes a liquidity pool from the registry + * + * @param _liquidityPool liquidity pool + */ + function removeLiquidityPool(IBancorConverterRegistryData _converterRegistryData, address _liquidityPool) internal { + _converterRegistryData.removeLiquidityPool(_liquidityPool); + emit LiquidityPoolRemoved(_liquidityPool); + } + + /** + * @dev adds a convertible token to the registry + * + * @param _convertibleToken convertible token + * @param _smartToken associated smart token + */ + function addConvertibleToken(IBancorConverterRegistryData _converterRegistryData, address _convertibleToken, address _smartToken) internal { + _converterRegistryData.addConvertibleToken(_convertibleToken, _smartToken); + emit ConvertibleTokenAdded(_convertibleToken, _smartToken); + } + + /** + * @dev removes a convertible token from the registry + * + * @param _convertibleToken convertible token + * @param _smartToken associated smart token + */ + function removeConvertibleToken(IBancorConverterRegistryData _converterRegistryData, address _convertibleToken, address _smartToken) internal { + _converterRegistryData.removeConvertibleToken(_convertibleToken, _smartToken); + emit ConvertibleTokenRemoved(_convertibleToken, _smartToken); + } + + function getLeastFrequentTokenSmartTokens(address[] memory _tokens) private view returns (address[] memory) { + IBancorConverterRegistryData bancorConverterRegistryData = IBancorConverterRegistryData(addressOf(BANCOR_CONVERTER_REGISTRY_DATA)); + + // find the token that has the smallest number of smart tokens + uint minSmartTokenCount = bancorConverterRegistryData.getConvertibleTokenSmartTokenCount(_tokens[0]); + address[] memory smartTokens = bancorConverterRegistryData.getConvertibleTokenSmartTokens(_tokens[0]); + for (uint i = 1; i < _tokens.length; i++) { + uint convertibleTokenSmartTokenCount = bancorConverterRegistryData.getConvertibleTokenSmartTokenCount(_tokens[i]); + if (minSmartTokenCount > convertibleTokenSmartTokenCount) { + minSmartTokenCount = convertibleTokenSmartTokenCount; + smartTokens = bancorConverterRegistryData.getConvertibleTokenSmartTokens(_tokens[i]); + } + } + return smartTokens; + } + + function isConverterReserveConfigEqual(IBancorConverter _converter, address[] memory _reserveTokens, uint[] memory _reserveRatios) private view returns (bool) { + if (_reserveTokens.length != _converter.connectorTokenCount()) + return false; + + for (uint i = 0; i < _reserveTokens.length; i++) { + if (_reserveRatios[i] != getReserveRatio(_converter, _reserveTokens[i])) + return false; + } + + return true; + } + + bytes4 private constant CONNECTORS_FUNC_SELECTOR = bytes4(uint256(keccak256("connectors(address)") >> (256 - 4 * 8))); + + function getReserveRatio(address _converter, address _reserveToken) private view returns (uint256) { + uint256[2] memory ret; + bytes memory data = abi.encodeWithSelector(CONNECTORS_FUNC_SELECTOR, _reserveToken); + + assembly { + let success := staticcall( + gas, // gas remaining + _converter, // destination address + add(data, 32), // input buffer (starts after the first 32 bytes in the `data` array) + mload(data), // input length (loaded from the first 32 bytes in the `data` array) + ret, // output buffer + 64 // output length + ) + if iszero(success) { + revert(0, 0) + } + } + + return ret[1]; + } +} diff --git a/contracts/bancor/converter/BancorConverterRegistryData.sol b/contracts/bancor/converter/BancorConverterRegistryData.sol new file mode 100755 index 0000000..36ff07b --- /dev/null +++ b/contracts/bancor/converter/BancorConverterRegistryData.sol @@ -0,0 +1,302 @@ +pragma solidity 0.4.26; +import '../utility/ContractRegistryClient.sol'; +import './interfaces/IBancorConverterRegistryData.sol'; + +/** + * @dev The BancorConverterRegistryData contract is an integral part of the Bancor converter registry + * as it serves as the database contract that holds all registry data. + * + * The registry is separated into two different contracts for upgradability - the data contract + * is harder to upgrade as it requires migrating all registry data into a new contract, while + * the registry contract itself can be easily upgraded. + * + * For that same reason, the data contract is simple and contains no logic beyond the basic data + * access utilities that it exposes. +*/ +contract BancorConverterRegistryData is IBancorConverterRegistryData, ContractRegistryClient { + struct Item { + bool valid; + uint index; + } + + struct Items { + address[] array; + mapping(address => Item) table; + } + + struct List { + uint index; + Items items; + } + + struct Lists { + address[] array; + mapping(address => List) table; + } + + Items smartTokens; + Items liquidityPools; + Lists convertibleTokens; + + /** + * @dev initializes a new BancorConverterRegistryData instance + * + * @param _registry address of a contract registry contract + */ + constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public { + } + + /** + * @dev adds a smart token + * + * @param _smartToken smart token + */ + function addSmartToken(address _smartToken) external only(BANCOR_CONVERTER_REGISTRY) { + addItem(smartTokens, _smartToken); + } + + /** + * @dev removes a smart token + * + * @param _smartToken smart token + */ + function removeSmartToken(address _smartToken) external only(BANCOR_CONVERTER_REGISTRY) { + removeItem(smartTokens, _smartToken); + } + + /** + * @dev adds a liquidity pool + * + * @param _liquidityPool liquidity pool + */ + function addLiquidityPool(address _liquidityPool) external only(BANCOR_CONVERTER_REGISTRY) { + addItem(liquidityPools, _liquidityPool); + } + + /** + * @dev removes a liquidity pool + * + * @param _liquidityPool liquidity pool + */ + function removeLiquidityPool(address _liquidityPool) external only(BANCOR_CONVERTER_REGISTRY) { + removeItem(liquidityPools, _liquidityPool); + } + + /** + * @dev adds a convertible token + * + * @param _convertibleToken convertible token + * @param _smartToken associated smart token + */ + function addConvertibleToken(address _convertibleToken, address _smartToken) external only(BANCOR_CONVERTER_REGISTRY) { + List storage list = convertibleTokens.table[_convertibleToken]; + if (list.items.array.length == 0) { + list.index = convertibleTokens.array.push(_convertibleToken) - 1; + } + addItem(list.items, _smartToken); + } + + /** + * @dev removes a convertible token + * + * @param _convertibleToken convertible token + * @param _smartToken associated smart token + */ + function removeConvertibleToken(address _convertibleToken, address _smartToken) external only(BANCOR_CONVERTER_REGISTRY) { + List storage list = convertibleTokens.table[_convertibleToken]; + removeItem(list.items, _smartToken); + if (list.items.array.length == 0) { + address lastConvertibleToken = convertibleTokens.array[convertibleTokens.array.length - 1]; + convertibleTokens.table[lastConvertibleToken].index = list.index; + convertibleTokens.array[list.index] = lastConvertibleToken; + convertibleTokens.array.length--; + delete convertibleTokens.table[_convertibleToken]; + } + } + + /** + * @dev returns the number of smart tokens + * + * @return number of smart tokens + */ + function getSmartTokenCount() external view returns (uint) { + return smartTokens.array.length; + } + + /** + * @dev returns the list of smart tokens + * + * @return list of smart tokens + */ + function getSmartTokens() external view returns (address[]) { + return smartTokens.array; + } + + /** + * @dev returns the smart token at a given index + * + * @param _index index + * @return smart token at the given index + */ + function getSmartToken(uint _index) external view returns (address) { + return smartTokens.array[_index]; + } + + /** + * @dev checks whether or not a given value is a smart token + * + * @param _value value + * @return true if the given value is a smart token, false if not + */ + function isSmartToken(address _value) external view returns (bool) { + return smartTokens.table[_value].valid; + } + + /** + * @dev returns the number of liquidity pools + * + * @return number of liquidity pools + */ + function getLiquidityPoolCount() external view returns (uint) { + return liquidityPools.array.length; + } + + /** + * @dev returns the list of liquidity pools + * + * @return list of liquidity pools + */ + function getLiquidityPools() external view returns (address[]) { + return liquidityPools.array; + } + + /** + * @dev returns the liquidity pool at a given index + * + * @param _index index + * @return liquidity pool at the given index + */ + function getLiquidityPool(uint _index) external view returns (address) { + return liquidityPools.array[_index]; + } + + /** + * @dev checks whether or not a given value is a liquidity pool + * + * @param _value value + * @return true if the given value is a liquidity pool, false if not + */ + function isLiquidityPool(address _value) external view returns (bool) { + return liquidityPools.table[_value].valid; + } + + /** + * @dev returns the number of convertible tokens + * + * @return number of convertible tokens + */ + function getConvertibleTokenCount() external view returns (uint) { + return convertibleTokens.array.length; + } + + /** + * @dev returns the list of convertible tokens + * + * @return list of convertible tokens + */ + function getConvertibleTokens() external view returns (address[]) { + return convertibleTokens.array; + } + + /** + * @dev returns the convertible token at a given index + * + * @param _index index + * @return convertible token at the given index + */ + function getConvertibleToken(uint _index) external view returns (address) { + return convertibleTokens.array[_index]; + } + + /** + * @dev checks whether or not a given value is a convertible token + * + * @param _value value + * @return true if the given value is a convertible token, false if not + */ + function isConvertibleToken(address _value) external view returns (bool) { + return convertibleTokens.table[_value].items.array.length > 0; + } + + /** + * @dev returns the number of smart tokens associated with a given convertible token + * + * @param _convertibleToken convertible token + * @return number of smart tokens associated with the given convertible token + */ + function getConvertibleTokenSmartTokenCount(address _convertibleToken) external view returns (uint) { + return convertibleTokens.table[_convertibleToken].items.array.length; + } + + /** + * @dev returns the list of smart tokens associated with a given convertible token + * + * @param _convertibleToken convertible token + * @return list of smart tokens associated with the given convertible token + */ + function getConvertibleTokenSmartTokens(address _convertibleToken) external view returns (address[]) { + return convertibleTokens.table[_convertibleToken].items.array; + } + + /** + * @dev returns the smart token associated with a given convertible token at a given index + * + * @param _index index + * @return smart token associated with the given convertible token at the given index + */ + function getConvertibleTokenSmartToken(address _convertibleToken, uint _index) external view returns (address) { + return convertibleTokens.table[_convertibleToken].items.array[_index]; + } + + /** + * @dev checks whether or not a given value is a smart token of a given convertible token + * + * @param _convertibleToken convertible token + * @param _value value + * @return true if the given value is a smart token of the given convertible token, false it not + */ + function isConvertibleTokenSmartToken(address _convertibleToken, address _value) external view returns (bool) { + return convertibleTokens.table[_convertibleToken].items.table[_value].valid; + } + + /** + * @dev adds an item to a list of items + * + * @param _items list of items + * @param _value item's value + */ + function addItem(Items storage _items, address _value) internal { + Item storage item = _items.table[_value]; + require(item.valid == false); + + item.index = _items.array.push(_value) - 1; + item.valid = true; + } + + /** + * @dev removes an item from a list of items + * + * @param _items list of items + * @param _value item's value + */ + function removeItem(Items storage _items, address _value) internal { + Item storage item = _items.table[_value]; + require(item.valid == true); + + address lastValue = _items.array[_items.array.length - 1]; + _items.table[lastValue].index = item.index; + _items.array[item.index] = lastValue; + _items.array.length--; + delete _items.table[_value]; + } +} diff --git a/contracts/bancor/converter/BancorConverterUpgrader.sol b/contracts/bancor/converter/BancorConverterUpgrader.sol new file mode 100755 index 0000000..5a4edb9 --- /dev/null +++ b/contracts/bancor/converter/BancorConverterUpgrader.sol @@ -0,0 +1,242 @@ +pragma solidity 0.4.26; +import './interfaces/IBancorConverter.sol'; +import './interfaces/IBancorConverterUpgrader.sol'; +import './interfaces/IBancorConverterFactory.sol'; +import '../utility/ContractRegistryClient.sol'; +import '../utility/interfaces/IContractFeatures.sol'; +import '../utility/interfaces/IWhitelist.sol'; +import '../FeatureIds.sol'; + +/* + Bancor converter dedicated interface +*/ +contract IBancorConverterExtended is IBancorConverter, IOwned { + function token() public view returns (ISmartToken) {this;} + function maxConversionFee() public view returns (uint32) {this;} + function conversionFee() public view returns (uint32) {this;} + function connectorTokenCount() public view returns (uint16); + function reserveTokenCount() public view returns (uint16); + function connectorTokens(uint256 _index) public view returns (IERC20Token) {_index; this;} + function reserveTokens(uint256 _index) public view returns (IERC20Token) {_index; this;} + function setConversionWhitelist(IWhitelist _whitelist) public; + function transferTokenOwnership(address _newOwner) public; + function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public; + function acceptTokenOwnership() public; + function transferManagement(address _newManager) public; + function acceptManagement() public; + function setConversionFee(uint32 _conversionFee) public; + function addConnector(IERC20Token _token, uint32 _weight, bool _enableVirtualBalance) public; + function updateConnector(IERC20Token _connectorToken, uint32 _weight, bool _enableVirtualBalance, uint256 _virtualBalance) public; +} + +/** + * @dev Bancor Converter Upgrader + * + * The Bancor converter upgrader contract allows upgrading an older Bancor converter contract (0.4 and up) + * to the latest version. + * To begin the upgrade process, simply execute the 'upgrade' function. + * At the end of the process, the ownership of the newly upgraded converter will be transferred + * back to the original owner and the original owner will need to execute the 'acceptOwnership' function. + * + * The address of the new converter is available in the ConverterUpgrade event. + * + * Note that for older converters that don't yet have the 'upgrade' function, ownership should first + * be transferred manually to the ConverterUpgrader contract using the 'transferOwnership' function + * and then the upgrader 'upgrade' function should be executed directly. +*/ +contract BancorConverterUpgrader is IBancorConverterUpgrader, ContractRegistryClient, FeatureIds { + string public version = '0.3'; + + /** + * @dev triggered when the contract accept a converter ownership + * + * @param _converter converter address + * @param _owner new owner - local upgrader address + */ + event ConverterOwned(address indexed _converter, address indexed _owner); + + /** + * @dev triggered when the upgrading process is done + * + * @param _oldConverter old converter address + * @param _newConverter new converter address + */ + event ConverterUpgrade(address indexed _oldConverter, address indexed _newConverter); + + /** + * @dev initializes a new BancorConverterUpgrader instance + * + * @param _registry address of a contract registry contract + */ + constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public { + } + + /** + * @dev upgrades an old converter to the latest version + * will throw if ownership wasn't transferred to the upgrader before calling this function. + * ownership of the new converter will be transferred back to the original owner. + * fires the ConverterUpgrade event upon success. + * can only be called by a converter + * + * @param _version old converter version + */ + function upgrade(bytes32 _version) public { + upgradeOld(IBancorConverter(msg.sender), _version); + } + + /** + * @dev upgrades an old converter to the latest version + * will throw if ownership wasn't transferred to the upgrader before calling this function. + * ownership of the new converter will be transferred back to the original owner. + * fires the ConverterUpgrade event upon success. + * can only be called by a converter + * + * @param _version old converter version + */ + function upgrade(uint16 _version) public { + upgradeOld(IBancorConverter(msg.sender), bytes32(_version)); + } + + /** + * @dev upgrades an old converter to the latest version + * will throw if ownership wasn't transferred to the upgrader before calling this function. + * ownership of the new converter will be transferred back to the original owner. + * fires the ConverterUpgrade event upon success. + * + * @param _converter old converter contract address + * @param _version old converter version + */ + function upgradeOld(IBancorConverter _converter, bytes32 _version) public { + _version; + IBancorConverterExtended converter = IBancorConverterExtended(_converter); + address prevOwner = converter.owner(); + acceptConverterOwnership(converter); + IBancorConverterExtended newConverter = createConverter(converter); + copyConnectors(converter, newConverter); + copyConversionFee(converter, newConverter); + transferConnectorsBalances(converter, newConverter); + ISmartToken token = converter.token(); + + if (token.owner() == address(converter)) { + converter.transferTokenOwnership(newConverter); + newConverter.acceptTokenOwnership(); + } + + converter.transferOwnership(prevOwner); + newConverter.transferOwnership(prevOwner); + newConverter.transferManagement(prevOwner); + + emit ConverterUpgrade(address(converter), address(newConverter)); + } + + /** + * @dev the first step when upgrading a converter is to transfer the ownership to the local contract. + * the upgrader contract then needs to accept the ownership transfer before initiating + * the upgrade process. + * fires the ConverterOwned event upon success + * + * @param _oldConverter converter to accept ownership of + */ + function acceptConverterOwnership(IBancorConverterExtended _oldConverter) private { + _oldConverter.acceptOwnership(); + emit ConverterOwned(_oldConverter, this); + } + + /** + * @dev creates a new converter with same basic data as the original old converter + * the newly created converter will have no connectors at this step. + * + * @param _oldConverter old converter contract address + * + * @return the new converter new converter contract address + */ + function createConverter(IBancorConverterExtended _oldConverter) private returns(IBancorConverterExtended) { + IWhitelist whitelist; + ISmartToken token = _oldConverter.token(); + uint32 maxConversionFee = _oldConverter.maxConversionFee(); + + IBancorConverterFactory converterFactory = IBancorConverterFactory(addressOf(BANCOR_CONVERTER_FACTORY)); + address converterAddress = converterFactory.createConverter( + token, + registry, + maxConversionFee, + IERC20Token(address(0)), + 0 + ); + + IBancorConverterExtended converter = IBancorConverterExtended(converterAddress); + converter.acceptOwnership(); + converter.acceptManagement(); + + // get the contract features address from the registry + IContractFeatures features = IContractFeatures(addressOf(CONTRACT_FEATURES)); + + if (features.isSupported(_oldConverter, FeatureIds.CONVERTER_CONVERSION_WHITELIST)) { + whitelist = _oldConverter.conversionWhitelist(); + if (whitelist != address(0)) + converter.setConversionWhitelist(whitelist); + } + + return converter; + } + + /** + * @dev copies the connectors from the old converter to the new one. + * note that this will not work for an unlimited number of connectors due to block gas limit constraints. + * + * @param _oldConverter old converter contract address + * @param _newConverter new converter contract address + */ + function copyConnectors(IBancorConverterExtended _oldConverter, IBancorConverterExtended _newConverter) + private + { + uint256 virtualBalance; + uint32 weight; + bool isVirtualBalanceEnabled; + uint16 connectorTokenCount = _oldConverter.connectorTokenCount(); + + for (uint16 i = 0; i < connectorTokenCount; i++) { + address connectorAddress = _oldConverter.connectorTokens(i); + (virtualBalance, weight, isVirtualBalanceEnabled, , ) = _oldConverter.connectors(connectorAddress); + + IERC20Token connectorToken = IERC20Token(connectorAddress); + _newConverter.addConnector(connectorToken, weight, isVirtualBalanceEnabled); + + if (isVirtualBalanceEnabled) + _newConverter.updateConnector(connectorToken, weight, isVirtualBalanceEnabled, virtualBalance); + } + } + + /** + * @dev copies the conversion fee from the old converter to the new one + * + * @param _oldConverter old converter contract address + * @param _newConverter new converter contract address + */ + function copyConversionFee(IBancorConverterExtended _oldConverter, IBancorConverterExtended _newConverter) private { + uint32 conversionFee = _oldConverter.conversionFee(); + _newConverter.setConversionFee(conversionFee); + } + + /** + * @dev transfers the balance of each connector in the old converter to the new one. + * note that the function assumes that the new converter already has the exact same number of + * also, this will not work for an unlimited number of connectors due to block gas limit constraints. + * + * @param _oldConverter old converter contract address + * @param _newConverter new converter contract address + */ + function transferConnectorsBalances(IBancorConverterExtended _oldConverter, IBancorConverterExtended _newConverter) + private + { + uint256 connectorBalance; + uint16 connectorTokenCount = _oldConverter.connectorTokenCount(); + + for (uint16 i = 0; i < connectorTokenCount; i++) { + address connectorAddress = _oldConverter.connectorTokens(i); + IERC20Token connector = IERC20Token(connectorAddress); + connectorBalance = connector.balanceOf(_oldConverter); + _oldConverter.withdrawTokens(connector, address(_newConverter), connectorBalance); + } + } +} diff --git a/contracts/bancor/converter/BancorFormula.sol b/contracts/bancor/converter/BancorFormula.sol new file mode 100755 index 0000000..7d4b18f --- /dev/null +++ b/contracts/bancor/converter/BancorFormula.sol @@ -0,0 +1,613 @@ +pragma solidity 0.4.26; +import './interfaces/IBancorFormula.sol'; +import '../utility/SafeMath.sol'; +import '../utility/Utils.sol'; + +contract BancorFormula is IBancorFormula, Utils { + using SafeMath for uint256; + + uint16 public version = 6; + + uint256 private constant ONE = 1; + uint32 private constant MAX_RATIO = 1000000; + uint8 private constant MIN_PRECISION = 32; + uint8 private constant MAX_PRECISION = 127; + + /** + * Auto-generated via 'PrintIntScalingFactors.py' + */ + uint256 private constant FIXED_1 = 0x080000000000000000000000000000000; + uint256 private constant FIXED_2 = 0x100000000000000000000000000000000; + uint256 private constant MAX_NUM = 0x200000000000000000000000000000000; + + /** + * Auto-generated via 'PrintLn2ScalingFactors.py' + */ + uint256 private constant LN2_NUMERATOR = 0x3f80fe03f80fe03f80fe03f80fe03f8; + uint256 private constant LN2_DENOMINATOR = 0x5b9de1d10bf4103d647b0955897ba80; + + /** + * Auto-generated via 'PrintFunctionOptimalLog.py' and 'PrintFunctionOptimalExp.py' + */ + uint256 private constant OPT_LOG_MAX_VAL = 0x15bf0a8b1457695355fb8ac404e7a79e3; + uint256 private constant OPT_EXP_MAX_VAL = 0x800000000000000000000000000000000; + + /** + * Auto-generated via 'PrintFunctionConstructor.py' + */ + uint256[128] private maxExpArray; + constructor() public { + // maxExpArray[ 0] = 0x6bffffffffffffffffffffffffffffffff; + // maxExpArray[ 1] = 0x67ffffffffffffffffffffffffffffffff; + // maxExpArray[ 2] = 0x637fffffffffffffffffffffffffffffff; + // maxExpArray[ 3] = 0x5f6fffffffffffffffffffffffffffffff; + // maxExpArray[ 4] = 0x5b77ffffffffffffffffffffffffffffff; + // maxExpArray[ 5] = 0x57b3ffffffffffffffffffffffffffffff; + // maxExpArray[ 6] = 0x5419ffffffffffffffffffffffffffffff; + // maxExpArray[ 7] = 0x50a2ffffffffffffffffffffffffffffff; + // maxExpArray[ 8] = 0x4d517fffffffffffffffffffffffffffff; + // maxExpArray[ 9] = 0x4a233fffffffffffffffffffffffffffff; + // maxExpArray[ 10] = 0x47165fffffffffffffffffffffffffffff; + // maxExpArray[ 11] = 0x4429afffffffffffffffffffffffffffff; + // maxExpArray[ 12] = 0x415bc7ffffffffffffffffffffffffffff; + // maxExpArray[ 13] = 0x3eab73ffffffffffffffffffffffffffff; + // maxExpArray[ 14] = 0x3c1771ffffffffffffffffffffffffffff; + // maxExpArray[ 15] = 0x399e96ffffffffffffffffffffffffffff; + // maxExpArray[ 16] = 0x373fc47fffffffffffffffffffffffffff; + // maxExpArray[ 17] = 0x34f9e8ffffffffffffffffffffffffffff; + // maxExpArray[ 18] = 0x32cbfd5fffffffffffffffffffffffffff; + // maxExpArray[ 19] = 0x30b5057fffffffffffffffffffffffffff; + // maxExpArray[ 20] = 0x2eb40f9fffffffffffffffffffffffffff; + // maxExpArray[ 21] = 0x2cc8340fffffffffffffffffffffffffff; + // maxExpArray[ 22] = 0x2af09481ffffffffffffffffffffffffff; + // maxExpArray[ 23] = 0x292c5bddffffffffffffffffffffffffff; + // maxExpArray[ 24] = 0x277abdcdffffffffffffffffffffffffff; + // maxExpArray[ 25] = 0x25daf6657fffffffffffffffffffffffff; + // maxExpArray[ 26] = 0x244c49c65fffffffffffffffffffffffff; + // maxExpArray[ 27] = 0x22ce03cd5fffffffffffffffffffffffff; + // maxExpArray[ 28] = 0x215f77c047ffffffffffffffffffffffff; + // maxExpArray[ 29] = 0x1fffffffffffffffffffffffffffffffff; + // maxExpArray[ 30] = 0x1eaefdbdabffffffffffffffffffffffff; + // maxExpArray[ 31] = 0x1d6bd8b2ebffffffffffffffffffffffff; + maxExpArray[ 32] = 0x1c35fedd14ffffffffffffffffffffffff; + maxExpArray[ 33] = 0x1b0ce43b323fffffffffffffffffffffff; + maxExpArray[ 34] = 0x19f0028ec1ffffffffffffffffffffffff; + maxExpArray[ 35] = 0x18ded91f0e7fffffffffffffffffffffff; + maxExpArray[ 36] = 0x17d8ec7f0417ffffffffffffffffffffff; + maxExpArray[ 37] = 0x16ddc6556cdbffffffffffffffffffffff; + maxExpArray[ 38] = 0x15ecf52776a1ffffffffffffffffffffff; + maxExpArray[ 39] = 0x15060c256cb2ffffffffffffffffffffff; + maxExpArray[ 40] = 0x1428a2f98d72ffffffffffffffffffffff; + maxExpArray[ 41] = 0x13545598e5c23fffffffffffffffffffff; + maxExpArray[ 42] = 0x1288c4161ce1dfffffffffffffffffffff; + maxExpArray[ 43] = 0x11c592761c666fffffffffffffffffffff; + maxExpArray[ 44] = 0x110a688680a757ffffffffffffffffffff; + maxExpArray[ 45] = 0x1056f1b5bedf77ffffffffffffffffffff; + maxExpArray[ 46] = 0x0faadceceeff8bffffffffffffffffffff; + maxExpArray[ 47] = 0x0f05dc6b27edadffffffffffffffffffff; + maxExpArray[ 48] = 0x0e67a5a25da4107fffffffffffffffffff; + maxExpArray[ 49] = 0x0dcff115b14eedffffffffffffffffffff; + maxExpArray[ 50] = 0x0d3e7a392431239fffffffffffffffffff; + maxExpArray[ 51] = 0x0cb2ff529eb71e4fffffffffffffffffff; + maxExpArray[ 52] = 0x0c2d415c3db974afffffffffffffffffff; + maxExpArray[ 53] = 0x0bad03e7d883f69bffffffffffffffffff; + maxExpArray[ 54] = 0x0b320d03b2c343d5ffffffffffffffffff; + maxExpArray[ 55] = 0x0abc25204e02828dffffffffffffffffff; + maxExpArray[ 56] = 0x0a4b16f74ee4bb207fffffffffffffffff; + maxExpArray[ 57] = 0x09deaf736ac1f569ffffffffffffffffff; + maxExpArray[ 58] = 0x0976bd9952c7aa957fffffffffffffffff; + maxExpArray[ 59] = 0x09131271922eaa606fffffffffffffffff; + maxExpArray[ 60] = 0x08b380f3558668c46fffffffffffffffff; + maxExpArray[ 61] = 0x0857ddf0117efa215bffffffffffffffff; + maxExpArray[ 62] = 0x07ffffffffffffffffffffffffffffffff; + maxExpArray[ 63] = 0x07abbf6f6abb9d087fffffffffffffffff; + maxExpArray[ 64] = 0x075af62cbac95f7dfa7fffffffffffffff; + maxExpArray[ 65] = 0x070d7fb7452e187ac13fffffffffffffff; + maxExpArray[ 66] = 0x06c3390ecc8af379295fffffffffffffff; + maxExpArray[ 67] = 0x067c00a3b07ffc01fd6fffffffffffffff; + maxExpArray[ 68] = 0x0637b647c39cbb9d3d27ffffffffffffff; + maxExpArray[ 69] = 0x05f63b1fc104dbd39587ffffffffffffff; + maxExpArray[ 70] = 0x05b771955b36e12f7235ffffffffffffff; + maxExpArray[ 71] = 0x057b3d49dda84556d6f6ffffffffffffff; + maxExpArray[ 72] = 0x054183095b2c8ececf30ffffffffffffff; + maxExpArray[ 73] = 0x050a28be635ca2b888f77fffffffffffff; + maxExpArray[ 74] = 0x04d5156639708c9db33c3fffffffffffff; + maxExpArray[ 75] = 0x04a23105873875bd52dfdfffffffffffff; + maxExpArray[ 76] = 0x0471649d87199aa990756fffffffffffff; + maxExpArray[ 77] = 0x04429a21a029d4c1457cfbffffffffffff; + maxExpArray[ 78] = 0x0415bc6d6fb7dd71af2cb3ffffffffffff; + maxExpArray[ 79] = 0x03eab73b3bbfe282243ce1ffffffffffff; + maxExpArray[ 80] = 0x03c1771ac9fb6b4c18e229ffffffffffff; + maxExpArray[ 81] = 0x0399e96897690418f785257fffffffffff; + maxExpArray[ 82] = 0x0373fc456c53bb779bf0ea9fffffffffff; + maxExpArray[ 83] = 0x034f9e8e490c48e67e6ab8bfffffffffff; + maxExpArray[ 84] = 0x032cbfd4a7adc790560b3337ffffffffff; + maxExpArray[ 85] = 0x030b50570f6e5d2acca94613ffffffffff; + maxExpArray[ 86] = 0x02eb40f9f620fda6b56c2861ffffffffff; + maxExpArray[ 87] = 0x02cc8340ecb0d0f520a6af58ffffffffff; + maxExpArray[ 88] = 0x02af09481380a0a35cf1ba02ffffffffff; + maxExpArray[ 89] = 0x0292c5bdd3b92ec810287b1b3fffffffff; + maxExpArray[ 90] = 0x0277abdcdab07d5a77ac6d6b9fffffffff; + maxExpArray[ 91] = 0x025daf6654b1eaa55fd64df5efffffffff; + maxExpArray[ 92] = 0x0244c49c648baa98192dce88b7ffffffff; + maxExpArray[ 93] = 0x022ce03cd5619a311b2471268bffffffff; + maxExpArray[ 94] = 0x0215f77c045fbe885654a44a0fffffffff; + maxExpArray[ 95] = 0x01ffffffffffffffffffffffffffffffff; + maxExpArray[ 96] = 0x01eaefdbdaaee7421fc4d3ede5ffffffff; + maxExpArray[ 97] = 0x01d6bd8b2eb257df7e8ca57b09bfffffff; + maxExpArray[ 98] = 0x01c35fedd14b861eb0443f7f133fffffff; + maxExpArray[ 99] = 0x01b0ce43b322bcde4a56e8ada5afffffff; + maxExpArray[100] = 0x019f0028ec1fff007f5a195a39dfffffff; + maxExpArray[101] = 0x018ded91f0e72ee74f49b15ba527ffffff; + maxExpArray[102] = 0x017d8ec7f04136f4e5615fd41a63ffffff; + maxExpArray[103] = 0x016ddc6556cdb84bdc8d12d22e6fffffff; + maxExpArray[104] = 0x015ecf52776a1155b5bd8395814f7fffff; + maxExpArray[105] = 0x015060c256cb23b3b3cc3754cf40ffffff; + maxExpArray[106] = 0x01428a2f98d728ae223ddab715be3fffff; + maxExpArray[107] = 0x013545598e5c23276ccf0ede68034fffff; + maxExpArray[108] = 0x01288c4161ce1d6f54b7f61081194fffff; + maxExpArray[109] = 0x011c592761c666aa641d5a01a40f17ffff; + maxExpArray[110] = 0x0110a688680a7530515f3e6e6cfdcdffff; + maxExpArray[111] = 0x01056f1b5bedf75c6bcb2ce8aed428ffff; + maxExpArray[112] = 0x00faadceceeff8a0890f3875f008277fff; + maxExpArray[113] = 0x00f05dc6b27edad306388a600f6ba0bfff; + maxExpArray[114] = 0x00e67a5a25da41063de1495d5b18cdbfff; + maxExpArray[115] = 0x00dcff115b14eedde6fc3aa5353f2e4fff; + maxExpArray[116] = 0x00d3e7a3924312399f9aae2e0f868f8fff; + maxExpArray[117] = 0x00cb2ff529eb71e41582cccd5a1ee26fff; + maxExpArray[118] = 0x00c2d415c3db974ab32a51840c0b67edff; + maxExpArray[119] = 0x00bad03e7d883f69ad5b0a186184e06bff; + maxExpArray[120] = 0x00b320d03b2c343d4829abd6075f0cc5ff; + maxExpArray[121] = 0x00abc25204e02828d73c6e80bcdb1a95bf; + maxExpArray[122] = 0x00a4b16f74ee4bb2040a1ec6c15fbbf2df; + maxExpArray[123] = 0x009deaf736ac1f569deb1b5ae3f36c130f; + maxExpArray[124] = 0x00976bd9952c7aa957f5937d790ef65037; + maxExpArray[125] = 0x009131271922eaa6064b73a22d0bd4f2bf; + maxExpArray[126] = 0x008b380f3558668c46c91c49a2f8e967b9; + maxExpArray[127] = 0x00857ddf0117efa215952912839f6473e6; + } + + /** + * @dev given a token supply, reserve balance, ratio and a deposit amount (in the reserve token), + * calculates the return for a given conversion (in the main token) + * + * Formula: + * Return = _supply * ((1 + _depositAmount / _reserveBalance) ^ (_reserveRatio / 1000000) - 1) + * + * @param _supply token total supply + * @param _reserveBalance total reserve balance + * @param _reserveRatio reserve ratio, represented in ppm, 1-1000000 + * @param _depositAmount deposit amount, in reserve token + * + * @return purchase return amount + */ + function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _depositAmount) public view returns (uint256) { + // validate input + require(_supply > 0 && _reserveBalance > 0 && _reserveRatio > 0 && _reserveRatio <= MAX_RATIO); + + // special case for 0 deposit amount + if (_depositAmount == 0) + return 0; + + // special case if the ratio = 100% + if (_reserveRatio == MAX_RATIO) + return _supply.mul(_depositAmount) / _reserveBalance; + + uint256 result; + uint8 precision; + uint256 baseN = _depositAmount.add(_reserveBalance); + (result, precision) = power(baseN, _reserveBalance, _reserveRatio, MAX_RATIO); + uint256 temp = _supply.mul(result) >> precision; + return temp - _supply; + } + + /** + * @dev given a token supply, reserve balance, ratio and a sell amount (in the main token), + * calculates the return for a given conversion (in the reserve token) + * + * Formula: + * Return = _reserveBalance * (1 - (1 - _sellAmount / _supply) ^ (1000000 / _reserveRatio)) + * + * @param _supply token total supply + * @param _reserveBalance total reserve + * @param _reserveRatio constant reserve Ratio, represented in ppm, 1-1000000 + * @param _sellAmount sell amount, in the token itself + * + * @return sale return amount + */ + function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _sellAmount) public view returns (uint256) { + // validate input + require(_supply > 0 && _reserveBalance > 0 && _reserveRatio > 0 && _reserveRatio <= MAX_RATIO && _sellAmount <= _supply); + + // special case for 0 sell amount + if (_sellAmount == 0) + return 0; + + // special case for selling the entire supply + if (_sellAmount == _supply) + return _reserveBalance; + + // special case if the ratio = 100% + if (_reserveRatio == MAX_RATIO) + return _reserveBalance.mul(_sellAmount) / _supply; + + uint256 result; + uint8 precision; + uint256 baseD = _supply - _sellAmount; + (result, precision) = power(_supply, baseD, MAX_RATIO, _reserveRatio); + uint256 temp1 = _reserveBalance.mul(result); + uint256 temp2 = _reserveBalance << precision; + return (temp1 - temp2) / result; + } + + /** + * @dev given two reserve balances/ratios and a sell amount (in the first reserve token), + * calculates the return for a conversion from the first reserve token to the second reserve token (in the second reserve token) + * note that prior to version 4, you should use 'calculateCrossConnectorReturn' instead + * + * Formula: + * Return = _toReserveBalance * (1 - (_fromReserveBalance / (_fromReserveBalance + _amount)) ^ (_fromReserveRatio / _toReserveRatio)) + * + * @param _fromReserveBalance input reserve balance + * @param _fromReserveRatio input reserve ratio, represented in ppm, 1-1000000 + * @param _toReserveBalance output reserve balance + * @param _toReserveRatio output reserve ratio, represented in ppm, 1-1000000 + * @param _amount input reserve amount + * + * @return second reserve amount + */ + function calculateCrossReserveReturn(uint256 _fromReserveBalance, uint32 _fromReserveRatio, uint256 _toReserveBalance, uint32 _toReserveRatio, uint256 _amount) public view returns (uint256) { + // validate input + require(_fromReserveBalance > 0 && _fromReserveRatio > 0 && _fromReserveRatio <= MAX_RATIO && _toReserveBalance > 0 && _toReserveRatio > 0 && _toReserveRatio <= MAX_RATIO); + + // special case for equal ratios + if (_fromReserveRatio == _toReserveRatio) + return _toReserveBalance.mul(_amount) / _fromReserveBalance.add(_amount); + + uint256 result; + uint8 precision; + uint256 baseN = _fromReserveBalance.add(_amount); + (result, precision) = power(baseN, _fromReserveBalance, _fromReserveRatio, _toReserveRatio); + uint256 temp1 = _toReserveBalance.mul(result); + uint256 temp2 = _toReserveBalance << precision; + return (temp1 - temp2) / result; + } + + /** + * @dev given a smart token supply, reserve balance, total ratio and an amount of requested smart tokens, + * calculates the amount of reserve tokens required for purchasing the given amount of smart tokens + * + * Formula: + * Return = _reserveBalance * (((_supply + _amount) / _supply) ^ (MAX_RATIO / _totalRatio) - 1) + * + * @param _supply smart token supply + * @param _reserveBalance reserve token balance + * @param _totalRatio total ratio, represented in ppm, 2-2000000 + * @param _amount requested amount of smart tokens + * + * @return amount of reserve tokens + */ + function calculateFundCost(uint256 _supply, uint256 _reserveBalance, uint32 _totalRatio, uint256 _amount) public view returns (uint256) { + // validate input + require(_supply > 0 && _reserveBalance > 0 && _totalRatio > 1 && _totalRatio <= MAX_RATIO * 2); + + // special case for 0 amount + if (_amount == 0) + return 0; + + // special case if the total ratio = 100% + if (_totalRatio == MAX_RATIO) + return (_amount.mul(_reserveBalance) - 1) / _supply + 1; + + uint256 result; + uint8 precision; + uint256 baseN = _supply.add(_amount); + (result, precision) = power(baseN, _supply, MAX_RATIO, _totalRatio); + uint256 temp = ((_reserveBalance.mul(result) - 1) >> precision) + 1; + return temp - _reserveBalance; + } + + /** + * @dev given a smart token supply, reserve balance, total ratio and an amount of smart tokens to liquidate, + * calculates the amount of reserve tokens received for selling the given amount of smart tokens + * + * Formula: + * Return = _reserveBalance * (1 - ((_supply - _amount) / _supply) ^ (MAX_RATIO / _totalRatio)) + * + * @param _supply smart token supply + * @param _reserveBalance reserve token balance + * @param _totalRatio total ratio, represented in ppm, 2-2000000 + * @param _amount amount of smart tokens to liquidate + * + * @return amount of reserve tokens + */ + function calculateLiquidateReturn(uint256 _supply, uint256 _reserveBalance, uint32 _totalRatio, uint256 _amount) public view returns (uint256) { + // validate input + require(_supply > 0 && _reserveBalance > 0 && _totalRatio > 1 && _totalRatio <= MAX_RATIO * 2 && _amount <= _supply); + + // special case for 0 amount + if (_amount == 0) + return 0; + + // special case for liquidating the entire supply + if (_amount == _supply) + return _reserveBalance; + + // special case if the total ratio = 100% + if (_totalRatio == MAX_RATIO) + return _amount.mul(_reserveBalance) / _supply; + + uint256 result; + uint8 precision; + uint256 baseD = _supply - _amount; + (result, precision) = power(_supply, baseD, MAX_RATIO, _totalRatio); + uint256 temp1 = _reserveBalance.mul(result); + uint256 temp2 = _reserveBalance << precision; + return (temp1 - temp2) / result; + } + + /** + * @dev General Description: + * Determine a value of precision. + * Calculate an integer approximation of (_baseN / _baseD) ^ (_expN / _expD) * 2 ^ precision. + * Return the result along with the precision used. + * + * Detailed Description: + * Instead of calculating "base ^ exp", we calculate "e ^ (log(base) * exp)". + * The value of "log(base)" is represented with an integer slightly smaller than "log(base) * 2 ^ precision". + * The larger "precision" is, the more accurately this value represents the real value. + * However, the larger "precision" is, the more bits are required in order to store this value. + * And the exponentiation function, which takes "x" and calculates "e ^ x", is limited to a maximum exponent (maximum value of "x"). + * This maximum exponent depends on the "precision" used, and it is given by "maxExpArray[precision] >> (MAX_PRECISION - precision)". + * Hence we need to determine the highest precision which can be used for the given input, before calling the exponentiation function. + * This allows us to compute "base ^ exp" with maximum accuracy and without exceeding 256 bits in any of the intermediate computations. + * This functions assumes that "_expN < 2 ^ 256 / log(MAX_NUM - 1)", otherwise the multiplication should be replaced with a "safeMul". + * Since we rely on unsigned-integer arithmetic and "base < 1" ==> "log(base) < 0", this function does not support "_baseN < _baseD". + */ + function power(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) internal view returns (uint256, uint8) { + require(_baseN < MAX_NUM); + + uint256 baseLog; + uint256 base = _baseN * FIXED_1 / _baseD; + if (base < OPT_LOG_MAX_VAL) { + baseLog = optimalLog(base); + } + else { + baseLog = generalLog(base); + } + + uint256 baseLogTimesExp = baseLog * _expN / _expD; + if (baseLogTimesExp < OPT_EXP_MAX_VAL) { + return (optimalExp(baseLogTimesExp), MAX_PRECISION); + } + else { + uint8 precision = findPositionInMaxExpArray(baseLogTimesExp); + return (generalExp(baseLogTimesExp >> (MAX_PRECISION - precision), precision), precision); + } + } + + /** + * @dev computes log(x / FIXED_1) * FIXED_1. + * This functions assumes that "x >= FIXED_1", because the output would be negative otherwise. + */ + function generalLog(uint256 x) internal pure returns (uint256) { + uint256 res = 0; + + // If x >= 2, then we compute the integer part of log2(x), which is larger than 0. + if (x >= FIXED_2) { + uint8 count = floorLog2(x / FIXED_1); + x >>= count; // now x < 2 + res = count * FIXED_1; + } + + // If x > 1, then we compute the fraction part of log2(x), which is larger than 0. + if (x > FIXED_1) { + for (uint8 i = MAX_PRECISION; i > 0; --i) { + x = (x * x) / FIXED_1; // now 1 < x < 4 + if (x >= FIXED_2) { + x >>= 1; // now 1 < x < 2 + res += ONE << (i - 1); + } + } + } + + return res * LN2_NUMERATOR / LN2_DENOMINATOR; + } + + /** + * @dev computes the largest integer smaller than or equal to the binary logarithm of the input. + */ + function floorLog2(uint256 _n) internal pure returns (uint8) { + uint8 res = 0; + + if (_n < 256) { + // At most 8 iterations + while (_n > 1) { + _n >>= 1; + res += 1; + } + } + else { + // Exactly 8 iterations + for (uint8 s = 128; s > 0; s >>= 1) { + if (_n >= (ONE << s)) { + _n >>= s; + res |= s; + } + } + } + + return res; + } + + /** + * @dev the global "maxExpArray" is sorted in descending order, and therefore the following statements are equivalent: + * - This function finds the position of [the smallest value in "maxExpArray" larger than or equal to "x"] + * - This function finds the highest position of [a value in "maxExpArray" larger than or equal to "x"] + */ + function findPositionInMaxExpArray(uint256 _x) internal view returns (uint8) { + uint8 lo = MIN_PRECISION; + uint8 hi = MAX_PRECISION; + + while (lo + 1 < hi) { + uint8 mid = (lo + hi) / 2; + if (maxExpArray[mid] >= _x) + lo = mid; + else + hi = mid; + } + + if (maxExpArray[hi] >= _x) + return hi; + if (maxExpArray[lo] >= _x) + return lo; + + require(false); + return 0; + } + + /** + * @dev this function can be auto-generated by the script 'PrintFunctionGeneralExp.py'. + * it approximates "e ^ x" via maclaurin summation: "(x^0)/0! + (x^1)/1! + ... + (x^n)/n!". + * it returns "e ^ (x / 2 ^ precision) * 2 ^ precision", that is, the result is upshifted for accuracy. + * the global "maxExpArray" maps each "precision" to "((maximumExponent + 1) << (MAX_PRECISION - precision)) - 1". + * the maximum permitted value for "x" is therefore given by "maxExpArray[precision] >> (MAX_PRECISION - precision)". + */ + function generalExp(uint256 _x, uint8 _precision) internal pure returns (uint256) { + uint256 xi = _x; + uint256 res = 0; + + xi = (xi * _x) >> _precision; res += xi * 0x3442c4e6074a82f1797f72ac0000000; // add x^02 * (33! / 02!) + xi = (xi * _x) >> _precision; res += xi * 0x116b96f757c380fb287fd0e40000000; // add x^03 * (33! / 03!) + xi = (xi * _x) >> _precision; res += xi * 0x045ae5bdd5f0e03eca1ff4390000000; // add x^04 * (33! / 04!) + xi = (xi * _x) >> _precision; res += xi * 0x00defabf91302cd95b9ffda50000000; // add x^05 * (33! / 05!) + xi = (xi * _x) >> _precision; res += xi * 0x002529ca9832b22439efff9b8000000; // add x^06 * (33! / 06!) + xi = (xi * _x) >> _precision; res += xi * 0x00054f1cf12bd04e516b6da88000000; // add x^07 * (33! / 07!) + xi = (xi * _x) >> _precision; res += xi * 0x0000a9e39e257a09ca2d6db51000000; // add x^08 * (33! / 08!) + xi = (xi * _x) >> _precision; res += xi * 0x000012e066e7b839fa050c309000000; // add x^09 * (33! / 09!) + xi = (xi * _x) >> _precision; res += xi * 0x000001e33d7d926c329a1ad1a800000; // add x^10 * (33! / 10!) + xi = (xi * _x) >> _precision; res += xi * 0x0000002bee513bdb4a6b19b5f800000; // add x^11 * (33! / 11!) + xi = (xi * _x) >> _precision; res += xi * 0x00000003a9316fa79b88eccf2a00000; // add x^12 * (33! / 12!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000048177ebe1fa812375200000; // add x^13 * (33! / 13!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000005263fe90242dcbacf00000; // add x^14 * (33! / 14!) + xi = (xi * _x) >> _precision; res += xi * 0x000000000057e22099c030d94100000; // add x^15 * (33! / 15!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000057e22099c030d9410000; // add x^16 * (33! / 16!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000052b6b54569976310000; // add x^17 * (33! / 17!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000004985f67696bf748000; // add x^18 * (33! / 18!) + xi = (xi * _x) >> _precision; res += xi * 0x000000000000003dea12ea99e498000; // add x^19 * (33! / 19!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000000031880f2214b6e000; // add x^20 * (33! / 20!) + xi = (xi * _x) >> _precision; res += xi * 0x000000000000000025bcff56eb36000; // add x^21 * (33! / 21!) + xi = (xi * _x) >> _precision; res += xi * 0x000000000000000001b722e10ab1000; // add x^22 * (33! / 22!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000001317c70077000; // add x^23 * (33! / 23!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000cba84aafa00; // add x^24 * (33! / 24!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000082573a0a00; // add x^25 * (33! / 25!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000005035ad900; // add x^26 * (33! / 26!) + xi = (xi * _x) >> _precision; res += xi * 0x000000000000000000000002f881b00; // add x^27 * (33! / 27!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000001b29340; // add x^28 * (33! / 28!) + xi = (xi * _x) >> _precision; res += xi * 0x00000000000000000000000000efc40; // add x^29 * (33! / 29!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000007fe0; // add x^30 * (33! / 30!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000420; // add x^31 * (33! / 31!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000021; // add x^32 * (33! / 32!) + xi = (xi * _x) >> _precision; res += xi * 0x0000000000000000000000000000001; // add x^33 * (33! / 33!) + + return res / 0x688589cc0e9505e2f2fee5580000000 + _x + (ONE << _precision); // divide by 33! and then add x^1 / 1! + x^0 / 0! + } + + /** + * @dev computes log(x / FIXED_1) * FIXED_1 + * Input range: FIXED_1 <= x <= LOG_EXP_MAX_VAL - 1 + * Auto-generated via 'PrintFunctionOptimalLog.py' + * Detailed description: + * - Rewrite the input as a product of natural exponents and a single residual r, such that 1 < r < 2 + * - The natural logarithm of each (pre-calculated) exponent is the degree of the exponent + * - The natural logarithm of r is calculated via Taylor series for log(1 + x), where x = r - 1 + * - The natural logarithm of the input is calculated by summing up the intermediate results above + * - For example: log(250) = log(e^4 * e^1 * e^0.5 * 1.021692859) = 4 + 1 + 0.5 + log(1 + 0.021692859) + */ + function optimalLog(uint256 x) internal pure returns (uint256) { + uint256 res = 0; + + uint256 y; + uint256 z; + uint256 w; + + if (x >= 0xd3094c70f034de4b96ff7d5b6f99fcd8) {res += 0x40000000000000000000000000000000; x = x * FIXED_1 / 0xd3094c70f034de4b96ff7d5b6f99fcd8;} // add 1 / 2^1 + if (x >= 0xa45af1e1f40c333b3de1db4dd55f29a7) {res += 0x20000000000000000000000000000000; x = x * FIXED_1 / 0xa45af1e1f40c333b3de1db4dd55f29a7;} // add 1 / 2^2 + if (x >= 0x910b022db7ae67ce76b441c27035c6a1) {res += 0x10000000000000000000000000000000; x = x * FIXED_1 / 0x910b022db7ae67ce76b441c27035c6a1;} // add 1 / 2^3 + if (x >= 0x88415abbe9a76bead8d00cf112e4d4a8) {res += 0x08000000000000000000000000000000; x = x * FIXED_1 / 0x88415abbe9a76bead8d00cf112e4d4a8;} // add 1 / 2^4 + if (x >= 0x84102b00893f64c705e841d5d4064bd3) {res += 0x04000000000000000000000000000000; x = x * FIXED_1 / 0x84102b00893f64c705e841d5d4064bd3;} // add 1 / 2^5 + if (x >= 0x8204055aaef1c8bd5c3259f4822735a2) {res += 0x02000000000000000000000000000000; x = x * FIXED_1 / 0x8204055aaef1c8bd5c3259f4822735a2;} // add 1 / 2^6 + if (x >= 0x810100ab00222d861931c15e39b44e99) {res += 0x01000000000000000000000000000000; x = x * FIXED_1 / 0x810100ab00222d861931c15e39b44e99;} // add 1 / 2^7 + if (x >= 0x808040155aabbbe9451521693554f733) {res += 0x00800000000000000000000000000000; x = x * FIXED_1 / 0x808040155aabbbe9451521693554f733;} // add 1 / 2^8 + + z = y = x - FIXED_1; + w = y * y / FIXED_1; + res += z * (0x100000000000000000000000000000000 - y) / 0x100000000000000000000000000000000; z = z * w / FIXED_1; // add y^01 / 01 - y^02 / 02 + res += z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y) / 0x200000000000000000000000000000000; z = z * w / FIXED_1; // add y^03 / 03 - y^04 / 04 + res += z * (0x099999999999999999999999999999999 - y) / 0x300000000000000000000000000000000; z = z * w / FIXED_1; // add y^05 / 05 - y^06 / 06 + res += z * (0x092492492492492492492492492492492 - y) / 0x400000000000000000000000000000000; z = z * w / FIXED_1; // add y^07 / 07 - y^08 / 08 + res += z * (0x08e38e38e38e38e38e38e38e38e38e38e - y) / 0x500000000000000000000000000000000; z = z * w / FIXED_1; // add y^09 / 09 - y^10 / 10 + res += z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y) / 0x600000000000000000000000000000000; z = z * w / FIXED_1; // add y^11 / 11 - y^12 / 12 + res += z * (0x089d89d89d89d89d89d89d89d89d89d89 - y) / 0x700000000000000000000000000000000; z = z * w / FIXED_1; // add y^13 / 13 - y^14 / 14 + res += z * (0x088888888888888888888888888888888 - y) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16 + + return res; + } + + /** + * @dev computes e ^ (x / FIXED_1) * FIXED_1 + * input range: 0 <= x <= OPT_EXP_MAX_VAL - 1 + * auto-generated via 'PrintFunctionOptimalExp.py' + * Detailed description: + * - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible + * - The exponentiation of each binary exponent is given (pre-calculated) + * - The exponentiation of r is calculated via Taylor series for e^x, where x = r + * - The exponentiation of the input is calculated by multiplying the intermediate results above + * - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859 + */ + function optimalExp(uint256 x) internal pure returns (uint256) { + uint256 res = 0; + + uint256 y; + uint256 z; + + z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3) + z = z * y / FIXED_1; res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!) + z = z * y / FIXED_1; res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!) + z = z * y / FIXED_1; res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!) + z = z * y / FIXED_1; res += z * 0x004807432bc18000; // add y^05 * (20! / 05!) + z = z * y / FIXED_1; res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!) + z = z * y / FIXED_1; res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!) + z = z * y / FIXED_1; res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!) + z = z * y / FIXED_1; res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!) + z = z * y / FIXED_1; res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!) + z = z * y / FIXED_1; res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!) + z = z * y / FIXED_1; res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!) + z = z * y / FIXED_1; res += z * 0x0000000017499f00; // add y^13 * (20! / 13!) + z = z * y / FIXED_1; res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!) + z = z * y / FIXED_1; res += z * 0x00000000001c6380; // add y^15 * (20! / 15!) + z = z * y / FIXED_1; res += z * 0x000000000001c638; // add y^16 * (20! / 16!) + z = z * y / FIXED_1; res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!) + z = z * y / FIXED_1; res += z * 0x000000000000017c; // add y^18 * (20! / 18!) + z = z * y / FIXED_1; res += z * 0x0000000000000014; // add y^19 * (20! / 19!) + z = z * y / FIXED_1; res += z * 0x0000000000000001; // add y^20 * (20! / 20!) + res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0! + + if ((x & 0x010000000000000000000000000000000) != 0) res = res * 0x1c3d6a24ed82218787d624d3e5eba95f9 / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3) + if ((x & 0x020000000000000000000000000000000) != 0) res = res * 0x18ebef9eac820ae8682b9793ac6d1e778 / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2) + if ((x & 0x040000000000000000000000000000000) != 0) res = res * 0x1368b2fc6f9609fe7aceb46aa619baed5 / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1) + if ((x & 0x080000000000000000000000000000000) != 0) res = res * 0x0bc5ab1b16779be3575bd8f0520a9f21e / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0) + if ((x & 0x100000000000000000000000000000000) != 0) res = res * 0x0454aaa8efe072e7f6ddbab84b40a55c5 / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1) + if ((x & 0x200000000000000000000000000000000) != 0) res = res * 0x00960aadc109e7a3bf4578099615711d7 / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2) + if ((x & 0x400000000000000000000000000000000) != 0) res = res * 0x0002bf84208204f5977f9a8cf01fdc307 / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3) + + return res; + } + + /** + * @dev deprecated, backward compatibility + */ + function calculateCrossConnectorReturn(uint256 _fromConnectorBalance, uint32 _fromConnectorWeight, uint256 _toConnectorBalance, uint32 _toConnectorWeight, uint256 _amount) public view returns (uint256) { + return calculateCrossReserveReturn(_fromConnectorBalance, _fromConnectorWeight, _toConnectorBalance, _toConnectorWeight, _amount); + } +} diff --git a/contracts/bancor/converter/interfaces/IBancorConverter.sol b/contracts/bancor/converter/interfaces/IBancorConverter.sol new file mode 100755 index 0000000..394db99 --- /dev/null +++ b/contracts/bancor/converter/interfaces/IBancorConverter.sol @@ -0,0 +1,26 @@ +pragma solidity 0.4.26; +import '../../token/interfaces/IERC20Token.sol'; +import '../../utility/interfaces/IWhitelist.sol'; + +/* + Bancor Converter interface +*/ +contract IBancorConverter { + function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256, uint256); + function convert2(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) public returns (uint256); + function quickConvert2(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) public payable returns (uint256); + function conversionWhitelist() public view returns (IWhitelist) {this;} + function conversionFee() public view returns (uint32) {this;} + function reserves(address _address) public view returns (uint256, uint32, bool, bool, bool) {_address; this;} + function getReserveBalance(IERC20Token _reserveToken) public view returns (uint256); + function reserveTokens(uint256 _index) public view returns (IERC20Token) {_index; this;} + + // deprecated, backward compatibility + function change(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256); + function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256); + function quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public payable returns (uint256); + function connectors(address _address) public view returns (uint256, uint32, bool, bool, bool); + function getConnectorBalance(IERC20Token _connectorToken) public view returns (uint256); + function connectorTokens(uint256 _index) public view returns (IERC20Token); + function connectorTokenCount() public view returns (uint16); +} diff --git a/contracts/bancor/converter/interfaces/IBancorConverterFactory.sol b/contracts/bancor/converter/interfaces/IBancorConverterFactory.sol new file mode 100755 index 0000000..acd3b73 --- /dev/null +++ b/contracts/bancor/converter/interfaces/IBancorConverterFactory.sol @@ -0,0 +1,18 @@ +pragma solidity 0.4.26; +import '../../token/interfaces/IERC20Token.sol'; +import '../../token/interfaces/ISmartToken.sol'; +import '../../utility/interfaces/IContractRegistry.sol'; + +/* + Bancor Converter Factory interface +*/ +contract IBancorConverterFactory { + function createConverter( + ISmartToken _token, + IContractRegistry _registry, + uint32 _maxConversionFee, + IERC20Token _reserveToken, + uint32 _reserveRatio + ) + public returns (address); +} diff --git a/contracts/bancor/converter/interfaces/IBancorConverterRegistry.sol b/contracts/bancor/converter/interfaces/IBancorConverterRegistry.sol new file mode 100755 index 0000000..1fdde9b --- /dev/null +++ b/contracts/bancor/converter/interfaces/IBancorConverterRegistry.sol @@ -0,0 +1,23 @@ +pragma solidity 0.4.26; +import './IBancorConverter.sol'; + +interface IBancorConverterRegistry { + function addConverter(IBancorConverter _converter) external; + function removeConverter(IBancorConverter _converter) external; + function getSmartTokenCount() external view returns (uint); + function getSmartTokens() external view returns (address[]); + function getSmartToken(uint _index) external view returns (address); + function isSmartToken(address _value) external view returns (bool); + function getLiquidityPoolCount() external view returns (uint); + function getLiquidityPools() external view returns (address[]); + function getLiquidityPool(uint _index) external view returns (address); + function isLiquidityPool(address _value) external view returns (bool); + function getConvertibleTokenCount() external view returns (uint); + function getConvertibleTokens() external view returns (address[]); + function getConvertibleToken(uint _index) external view returns (address); + function isConvertibleToken(address _value) external view returns (bool); + function getConvertibleTokenSmartTokenCount(address _convertibleToken) external view returns (uint); + function getConvertibleTokenSmartTokens(address _convertibleToken) external view returns (address[]); + function getConvertibleTokenSmartToken(address _convertibleToken, uint _index) external view returns (address); + function isConvertibleTokenSmartToken(address _convertibleToken, address _value) external view returns (bool); +} diff --git a/contracts/bancor/converter/interfaces/IBancorConverterRegistryData.sol b/contracts/bancor/converter/interfaces/IBancorConverterRegistryData.sol new file mode 100755 index 0000000..7e5f2c1 --- /dev/null +++ b/contracts/bancor/converter/interfaces/IBancorConverterRegistryData.sol @@ -0,0 +1,26 @@ +pragma solidity 0.4.26; + +interface IBancorConverterRegistryData { + function addSmartToken(address _smartToken) external; + function removeSmartToken(address _smartToken) external; + function addLiquidityPool(address _liquidityPool) external; + function removeLiquidityPool(address _liquidityPool) external; + function addConvertibleToken(address _convertibleToken, address _smartToken) external; + function removeConvertibleToken(address _convertibleToken, address _smartToken) external; + function getSmartTokenCount() external view returns (uint); + function getSmartTokens() external view returns (address[]); + function getSmartToken(uint _index) external view returns (address); + function isSmartToken(address _value) external view returns (bool); + function getLiquidityPoolCount() external view returns (uint); + function getLiquidityPools() external view returns (address[]); + function getLiquidityPool(uint _index) external view returns (address); + function isLiquidityPool(address _value) external view returns (bool); + function getConvertibleTokenCount() external view returns (uint); + function getConvertibleTokens() external view returns (address[]); + function getConvertibleToken(uint _index) external view returns (address); + function isConvertibleToken(address _value) external view returns (bool); + function getConvertibleTokenSmartTokenCount(address _convertibleToken) external view returns (uint); + function getConvertibleTokenSmartTokens(address _convertibleToken) external view returns (address[]); + function getConvertibleTokenSmartToken(address _convertibleToken, uint _index) external view returns (address); + function isConvertibleTokenSmartToken(address _convertibleToken, address _value) external view returns (bool); +} diff --git a/contracts/bancor/converter/interfaces/IBancorConverterUpgrader.sol b/contracts/bancor/converter/interfaces/IBancorConverterUpgrader.sol new file mode 100755 index 0000000..50d308b --- /dev/null +++ b/contracts/bancor/converter/interfaces/IBancorConverterUpgrader.sol @@ -0,0 +1,10 @@ +pragma solidity 0.4.26; +import './IBancorConverter.sol'; + +/* + Bancor Converter Upgrader interface +*/ +contract IBancorConverterUpgrader { + function upgrade(bytes32 _version) public; + function upgrade(uint16 _version) public; +} diff --git a/contracts/bancor/converter/interfaces/IBancorFormula.sol b/contracts/bancor/converter/interfaces/IBancorFormula.sol new file mode 100755 index 0000000..140ddf0 --- /dev/null +++ b/contracts/bancor/converter/interfaces/IBancorFormula.sol @@ -0,0 +1,14 @@ +pragma solidity 0.4.26; + +/* + Bancor Formula interface +*/ +contract IBancorFormula { + function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _depositAmount) public view returns (uint256); + function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint32 _reserveRatio, uint256 _sellAmount) public view returns (uint256); + function calculateCrossReserveReturn(uint256 _fromReserveBalance, uint32 _fromReserveRatio, uint256 _toReserveBalance, uint32 _toReserveRatio, uint256 _amount) public view returns (uint256); + function calculateFundCost(uint256 _supply, uint256 _reserveBalance, uint32 _totalRatio, uint256 _amount) public view returns (uint256); + function calculateLiquidateReturn(uint256 _supply, uint256 _reserveBalance, uint32 _totalRatio, uint256 _amount) public view returns (uint256); + // deprecated, backward compatibility + function calculateCrossConnectorReturn(uint256 _fromConnectorBalance, uint32 _fromConnectorWeight, uint256 _toConnectorBalance, uint32 _toConnectorWeight, uint256 _amount) public view returns (uint256); +} diff --git a/contracts/bancor/crowdsale/CrowdsaleController.sol b/contracts/bancor/crowdsale/CrowdsaleController.sol new file mode 100755 index 0000000..bc55787 --- /dev/null +++ b/contracts/bancor/crowdsale/CrowdsaleController.sol @@ -0,0 +1,199 @@ +pragma solidity 0.4.26; +import '../token/SmartTokenController.sol'; +import '../token/interfaces/ISmartToken.sol'; +import '../utility/SafeMath.sol'; + +/** + * @dev Crowdsale + * + * The crowdsale version of the smart token controller, allows contributing ether in exchange for Bancor tokens + * The price remains fixed for the entire duration of the crowdsale + * Note that 20% of the contributions are the BNT token's ETH reserve balance +*/ +contract CrowdsaleController is SmartTokenController { + using SafeMath for uint256; + + + uint256 public constant DURATION = 14 days; // crowdsale duration + uint256 public constant TOKEN_PRICE_N = 1; // initial price in wei (numerator) + uint256 public constant TOKEN_PRICE_D = 100; // initial price in wei (denominator) + uint256 public constant BTCS_ETHER_CAP = 50000 ether; // maximum bitcoin suisse ether contribution + uint256 public constant MAX_GAS_PRICE = 50000000000 wei; // maximum gas price for contribution transactions + + string public version = '0.1'; + + uint256 public startTime = 0; // crowdsale start time (in seconds) + uint256 public endTime = 0; // crowdsale end time (in seconds) + uint256 public totalEtherCap = 1000000 ether; // current ether contribution cap, initialized with a temp value as a safety mechanism until the real cap is revealed + uint256 public totalEtherContributed = 0; // ether contributed so far + bytes32 public realEtherCapHash; // ensures that the real cap is predefined on deployment and cannot be changed later + address public beneficiary = address(0); // address to receive all ether contributions + address public btcs = address(0); // bitcoin suisse address + + // triggered on each contribution + event Contribution(address indexed _contributor, uint256 _amount, uint256 _return); + + /** + * @dev initializes a new CrowdsaleController instance + * + * @param _token smart token the crowdsale is for + * @param _startTime crowdsale start time + * @param _beneficiary address to receive all ether contributions + * @param _btcs bitcoin suisse address + */ + constructor(ISmartToken _token, uint256 _startTime, address _beneficiary, address _btcs, bytes32 _realEtherCapHash) + public + SmartTokenController(_token) + validAddress(_beneficiary) + validAddress(_btcs) + earlierThan(_startTime) + greaterThanZero(uint256(_realEtherCapHash)) + { + startTime = _startTime; + endTime = startTime + DURATION; + beneficiary = _beneficiary; + btcs = _btcs; + realEtherCapHash = _realEtherCapHash; + } + + // verifies that the gas price is lower than 50 gwei + modifier validGasPrice() { + assert(tx.gasprice <= MAX_GAS_PRICE); + _; + } + + // verifies that the ether cap is valid based on the key provided + modifier validEtherCap(uint256 _cap, uint256 _key) { + require(computeRealCap(_cap, _key) == realEtherCapHash); + _; + } + + // ensures that it's earlier than the given time + modifier earlierThan(uint256 _time) { + assert(now < _time); + _; + } + + // ensures that the current time is between _startTime (inclusive) and _endTime (exclusive) + modifier between(uint256 _startTime, uint256 _endTime) { + assert(now >= _startTime && now < _endTime); + _; + } + + // ensures that the sender is bitcoin suisse + modifier btcsOnly() { + assert(msg.sender == btcs); + _; + } + + // ensures that we didn't reach the ether cap + modifier etherCapNotReached(uint256 _contribution) { + assert(totalEtherContributed.add(_contribution) <= totalEtherCap); + _; + } + + // ensures that we didn't reach the bitcoin suisse ether cap + modifier btcsEtherCapNotReached(uint256 _ethContribution) { + assert(totalEtherContributed.add(_ethContribution) <= BTCS_ETHER_CAP); + _; + } + + /** + * @dev computes the real cap based on the given cap & key + * + * @param _cap cap + * @param _key key used to compute the cap hash + * + * @return computed real cap hash + */ + function computeRealCap(uint256 _cap, uint256 _key) public pure returns (bytes32) { + return keccak256(abi.encodePacked(_cap, _key)); + } + + /** + * @dev enables the real cap defined on deployment + * + * @param _cap predefined cap + * @param _key key used to compute the cap hash + */ + function enableRealCap(uint256 _cap, uint256 _key) + public + ownerOnly + active + between(startTime, endTime) + validEtherCap(_cap, _key) + { + require(_cap < totalEtherCap); // validate input + totalEtherCap = _cap; + } + + /** + * @dev computes the number of tokens that should be issued for a given contribution + * + * @param _contribution contribution amount + * + * @return computed number of tokens + */ + function computeReturn(uint256 _contribution) public pure returns (uint256) { + return _contribution.mul(TOKEN_PRICE_D).div(TOKEN_PRICE_N); + } + + /** + * @dev ETH contribution + * can only be called during the crowdsale + * + * @return tokens issued in return + */ + function contributeETH() + public + payable + between(startTime, endTime) + returns (uint256 amount) + { + return processContribution(); + } + + /** + * @dev Contribution through BTCs (Bitcoin Suisse only) + * can only be called before the crowdsale started + * + * @return tokens issued in return + */ + function contributeBTCs() + public + payable + btcsOnly + btcsEtherCapNotReached(msg.value) + earlierThan(startTime) + returns (uint256 amount) + { + return processContribution(); + } + + /** + * @dev handles contribution logic + * note that the Contribution event is triggered using the sender as the contributor, regardless of the actual contributor + * + * @return tokens issued in return + */ + function processContribution() private + active + etherCapNotReached(msg.value) + validGasPrice + returns (uint256 amount) + { + uint256 tokenAmount = computeReturn(msg.value); + beneficiary.transfer(msg.value); // transfer the ether to the beneficiary account + totalEtherContributed = totalEtherContributed.add(msg.value); // update the total contribution amount + token.issue(msg.sender, tokenAmount); // issue new funds to the contributor in the smart token + token.issue(beneficiary, tokenAmount); // issue tokens to the beneficiary + + emit Contribution(msg.sender, msg.value, tokenAmount); + return tokenAmount; + } + + // fallback + function() payable public { + contributeETH(); + } +} diff --git a/contracts/bancor/helpers/Migrations.sol b/contracts/bancor/helpers/Migrations.sol new file mode 100755 index 0000000..7fa4901 --- /dev/null +++ b/contracts/bancor/helpers/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity 0.4.26; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + constructor() public { + owner = msg.sender; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/contracts/bancor/helpers/NonStandardERC20Token.sol b/contracts/bancor/helpers/NonStandardERC20Token.sol new file mode 100755 index 0000000..98e7066 --- /dev/null +++ b/contracts/bancor/helpers/NonStandardERC20Token.sol @@ -0,0 +1,101 @@ +pragma solidity 0.4.26; +import '../token/interfaces/INonStandardERC20.sol'; +import '../utility/Utils.sol'; +import '../utility/SafeMath.sol'; + +/** + * ERC20 Non-Standard Token implementation +*/ +contract NonStandardERC20Token is INonStandardERC20, Utils { + using SafeMath for uint256; + + + string public standard = 'Token 0.1'; + string public name = ''; + string public symbol = ''; + uint8 public decimals = 0; + uint256 public totalSupply = 0; + mapping (address => uint256) public balanceOf; + mapping (address => mapping (address => uint256)) public allowance; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + /** + * @dev initializes a new NonStandardERC20Token instance + * + * @param _name token name + * @param _symbol token symbol + * @param _decimals decimal points, for display purposes + */ + constructor(string _name, string _symbol, uint8 _decimals) public { + require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input + + name = _name; + symbol = _symbol; + decimals = _decimals; + } + + /** + * @dev send coins + * throws on any error rather then return a false flag to minimize user errors + * + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transfer(address _to, uint256 _value) + public + validAddress(_to) + { + balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value); + balanceOf[_to] = balanceOf[_to].add(_value); + emit Transfer(msg.sender, _to, _value); + } + + /** + * @dev an account/contract attempts to get the coins + * throws on any error rather then return a false flag to minimize user errors + * + * @param _from source address + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transferFrom(address _from, address _to, uint256 _value) + public + validAddress(_from) + validAddress(_to) + { + allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value); + balanceOf[_from] = balanceOf[_from].sub(_value); + balanceOf[_to] = balanceOf[_to].add(_value); + emit Transfer(_from, _to, _value); + } + + /** + * @dev allow another account/contract to spend some tokens on your behalf + * throws on any error rather then return a false flag to minimize user errors + * + * also, to minimize the risk of the approve/transferFrom attack vector + * (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice + * in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value + * + * @param _spender approved address + * @param _value allowance amount + * + * @return true if the approval was successful, false if it wasn't + */ + function approve(address _spender, uint256 _value) + public + validAddress(_spender) + { + // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal + require(_value == 0 || allowance[msg.sender][_spender] == 0); + + allowance[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + } +} diff --git a/contracts/bancor/helpers/NonStandardSmartToken.sol b/contracts/bancor/helpers/NonStandardSmartToken.sol new file mode 100755 index 0000000..5cfd3c4 --- /dev/null +++ b/contracts/bancor/helpers/NonStandardSmartToken.sol @@ -0,0 +1,118 @@ +pragma solidity 0.4.26; +import './NonStandardERC20Token.sol'; +import '../utility/Owned.sol'; + +/* + Smart Token v0.3 + + 'Owned' is specified here for readability reasons +*/ +contract NonStandardSmartToken is Owned, NonStandardERC20Token { + using SafeMath for uint256; + + + string public version = '0.3'; + + bool public transfersEnabled = true; // true if transfer/transferFrom are enabled, false if not + + // triggered when a smart token is deployed - the _token address is defined for forward compatibility, in case we want to trigger the event from a factory + event NewSmartToken(address _token); + // triggered when the total supply is increased + event Issuance(uint256 _amount); + // triggered when the total supply is decreased + event Destruction(uint256 _amount); + + /** + * @dev initializes a new NonStandardSmartToken instance + * + * @param _name token name + * @param _symbol token short symbol, minimum 1 character + * @param _decimals for display purposes only + */ + constructor(string _name, string _symbol, uint8 _decimals) + public + NonStandardERC20Token(_name, _symbol, _decimals) + { + emit NewSmartToken(address(this)); + } + + // allows execution only when transfers aren't disabled + modifier transfersAllowed { + assert(transfersEnabled); + _; + } + + /** + * @dev disables/enables transfers + * can only be called by the contract owner + * + * @param _disable true to disable transfers, false to enable them + */ + function disableTransfers(bool _disable) public ownerOnly { + transfersEnabled = !_disable; + } + + /** + * @dev increases the token supply and sends the new tokens to an account + * can only be called by the contract owner + * + * @param _to account to receive the new amount + * @param _amount amount to increase the supply by + */ + function issue(address _to, uint256 _amount) + public + ownerOnly + validAddress(_to) + notThis(_to) + { + totalSupply = totalSupply.add(_amount); + balanceOf[_to] = balanceOf[_to].add(_amount); + + emit Issuance(_amount); + emit Transfer(this, _to, _amount); + } + + /** + * @dev removes tokens from an account and decreases the token supply + * can be called by the contract owner to destroy tokens from any account or by any holder to destroy tokens from his/her own account + * + * @param _from account to remove the amount from + * @param _amount amount to decrease the supply by + */ + function destroy(address _from, uint256 _amount) public { + require(msg.sender == _from || msg.sender == owner); // validate input + + balanceOf[_from] = balanceOf[_from].sub(_amount); + totalSupply = totalSupply.sub(_amount); + + emit Transfer(_from, this, _amount); + emit Destruction(_amount); + } + + // ERC20 standard method overrides with some extra functionality + + /** + * @dev send coins + * throws on any error rather then return a false flag to minimize user errors + * in addition to the standard checks, the function throws if transfers are disabled + * + * @param _to target address + * @param _value transfer amount + */ + function transfer(address _to, uint256 _value) public transfersAllowed { + super.transfer(_to, _value); + } + + /** + * @dev an account/contract attempts to get the coins + * throws on any error rather then return a false flag to minimize user errors + * in addition to the standard checks, the function throws if transfers are disabled + * + * @param _from source address + * @param _to target address + * @param _value transfer amount + */ + function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed { + super.transferFrom(_from, _to, _value); + } +} diff --git a/contracts/bancor/helpers/TestBancorFormula.sol b/contracts/bancor/helpers/TestBancorFormula.sol new file mode 100755 index 0000000..0ac9ac0 --- /dev/null +++ b/contracts/bancor/helpers/TestBancorFormula.sol @@ -0,0 +1,38 @@ +pragma solidity 0.4.26; +import '../converter/BancorFormula.sol'; + +/* + BancorFormula test helper that exposes some BancorFormula functions +*/ +contract TestBancorFormula is BancorFormula { + constructor() public { + } + + function powerTest(uint256 _baseN, uint256 _baseD, uint32 _expN, uint32 _expD) external view returns (uint256, uint8) { + return super.power(_baseN, _baseD, _expN, _expD); + } + + function generalLogTest(uint256 x) external pure returns (uint256) { + return super.generalLog(x); + } + + function floorLog2Test(uint256 _n) external pure returns (uint8) { + return super.floorLog2(_n); + } + + function findPositionInMaxExpArrayTest(uint256 _x) external view returns (uint8) { + return super.findPositionInMaxExpArray(_x); + } + + function generalExpTest(uint256 _x, uint8 _precision) external pure returns (uint256) { + return super.generalExp(_x, _precision); + } + + function optimalLogTest(uint256 x) external pure returns (uint256) { + return super.optimalLog(x); + } + + function optimalExpTest(uint256 x) external pure returns (uint256) { + return super.optimalExp(x); + } +} diff --git a/contracts/bancor/helpers/TestBancorNetwork.sol b/contracts/bancor/helpers/TestBancorNetwork.sol new file mode 100755 index 0000000..c2627de --- /dev/null +++ b/contracts/bancor/helpers/TestBancorNetwork.sol @@ -0,0 +1,52 @@ +pragma solidity 0.4.26; +import '../BancorNetwork.sol'; + +contract OldBancorConverter { + uint256 private amount; + + constructor(uint256 _amount) public { + amount = _amount; + } + + function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) external view returns (uint256) { + _fromToken; + _toToken; + _amount; + return (amount); + } +} + +contract NewBancorConverter { + uint256 private amount; + uint256 private fee; + + constructor(uint256 _amount, uint256 _fee) public { + amount = _amount; + fee = _fee; + } + + function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) external view returns (uint256, uint256) { + _fromToken; + _toToken; + _amount; + return (amount, fee); + } +} + +contract TestBancorNetwork is BancorNetwork { + OldBancorConverter private oldBancorConverter; + NewBancorConverter private newBancorConverter; + + constructor(uint256 _amount, uint256 _fee) public BancorNetwork(IContractRegistry(address(1))) { + oldBancorConverter = new OldBancorConverter(_amount); + newBancorConverter = new NewBancorConverter(_amount, _fee); + } + + function getReturnOld() external view returns (uint256, uint256) { + return getReturn(address(oldBancorConverter), IERC20Token(0), IERC20Token(0), uint256(0)); + } + + function getReturnNew() external view returns (uint256, uint256) { + return getReturn(address(newBancorConverter), IERC20Token(0), IERC20Token(0), uint256(0)); + } +} diff --git a/contracts/bancor/helpers/TestCrowdsaleController.sol b/contracts/bancor/helpers/TestCrowdsaleController.sol new file mode 100755 index 0000000..8f56bc1 --- /dev/null +++ b/contracts/bancor/helpers/TestCrowdsaleController.sol @@ -0,0 +1,31 @@ +pragma solidity 0.4.26; +import '../crowdsale/CrowdsaleController.sol'; + +/* + Test crowdsale controller with start time < now < end time +*/ +contract TestCrowdsaleController is CrowdsaleController { + using SafeMath for uint256; + + + uint256 public constant BTCS_ETHER_CAP_SMALL = 2 ether; // maximum bitcoin suisse ether contribution + + constructor( + ISmartToken _token, + uint256 _startTime, + address _beneficiary, + address _btcs, + bytes32 _realEtherCapHash, + uint256 _startTimeOverride) + public + CrowdsaleController(_token, _startTime, _beneficiary, _btcs, _realEtherCapHash) + { + startTime = _startTimeOverride; + endTime = startTime + DURATION; + } + + modifier btcsEtherCapNotReached(uint256 _ethContribution) { + assert(totalEtherContributed.add(_ethContribution) <= BTCS_ETHER_CAP_SMALL); + _; + } +} diff --git a/contracts/bancor/helpers/TestFeatures.sol b/contracts/bancor/helpers/TestFeatures.sol new file mode 100755 index 0000000..1da462e --- /dev/null +++ b/contracts/bancor/helpers/TestFeatures.sol @@ -0,0 +1,17 @@ +pragma solidity 0.4.26; +import '../utility/interfaces/IContractFeatures.sol'; + +/* + Test helper that uses the ContractFeatures contract +*/ +contract TestFeatures { + IContractFeatures public features; + + constructor(IContractFeatures _features) public { + features = _features; + } + + function enableFeatures(uint256 _features, bool _enable) public { + features.enableFeatures(_features, _enable); + } +} diff --git a/contracts/bancor/helpers/TestNonStandardERC20Token.sol b/contracts/bancor/helpers/TestNonStandardERC20Token.sol new file mode 100755 index 0000000..baa1bf5 --- /dev/null +++ b/contracts/bancor/helpers/TestNonStandardERC20Token.sol @@ -0,0 +1,15 @@ +pragma solidity 0.4.26; +import './NonStandardERC20Token.sol'; + +/* + Test token with predefined supply +*/ +contract TestNonStandardERC20Token is NonStandardERC20Token { + constructor(string _name, string _symbol, uint256 _supply) + public + NonStandardERC20Token(_name, _symbol, 0) + { + totalSupply = _supply; + balanceOf[msg.sender] = _supply; + } +} diff --git a/contracts/bancor/helpers/TestSafeMath.sol b/contracts/bancor/helpers/TestSafeMath.sol new file mode 100755 index 0000000..41b06c0 --- /dev/null +++ b/contracts/bancor/helpers/TestSafeMath.sol @@ -0,0 +1,25 @@ +pragma solidity 0.4.26; +import '../utility/SafeMath.sol'; + +/* + Utils test helper that exposes the safe math functions +*/ +contract TestSafeMath { + using SafeMath for uint256; + + + constructor() public { + } + + function testSafeAdd(uint256 _x, uint256 _y) public pure returns (uint256) { + return _x.add(_y); + } + + function testSafeSub(uint256 _x, uint256 _y) public pure returns (uint256) { + return _x.sub(_y); + } + + function testSafeMul(uint256 _x, uint256 _y) public pure returns (uint256) { + return _x.mul(_y); + } +} diff --git a/contracts/bancor/legacy/BancorGasPriceLimit.sol b/contracts/bancor/legacy/BancorGasPriceLimit.sol new file mode 100755 index 0000000..9cc69cb --- /dev/null +++ b/contracts/bancor/legacy/BancorGasPriceLimit.sol @@ -0,0 +1,52 @@ +pragma solidity 0.4.26; +import '../utility/Owned.sol'; +import '../utility/Utils.sol'; + +/** + * @dev The BancorGasPriceLimit contract serves as an extra front-running attack mitigation mechanism. + * It sets a maximum gas price on all bancor conversions, which prevents users from "cutting in line" + * in order to front-run other transactions. + * The gas price limit is universal to all converters and it can be updated by the owner to be in line + * with the network's current gas price. +*/ +contract BancorGasPriceLimit is Owned, Utils { + uint256 public gasPrice = 0 wei; // maximum gas price for bancor transactions + + /** + * @dev initializes a new BancorGasPriceLimit instance + * + * @param _gasPrice gas price limit + */ + constructor(uint256 _gasPrice) + public + greaterThanZero(_gasPrice) + { + gasPrice = _gasPrice; + } + + /** + * @dev allows the owner to update the gas price limit + * + * @param _gasPrice new gas price limit + */ + function setGasPrice(uint256 _gasPrice) + public + ownerOnly + greaterThanZero(_gasPrice) + { + gasPrice = _gasPrice; + } + + /** + * @dev validate that the given gas price is equal to the current network gas price + * + * @param _gasPrice tested gas price + */ + function validateGasPrice(uint256 _gasPrice) + public + view + greaterThanZero(_gasPrice) + { + require(_gasPrice <= gasPrice); + } +} diff --git a/contracts/bancor/legacy/BancorPriceFloor.sol b/contracts/bancor/legacy/BancorPriceFloor.sol new file mode 100755 index 0000000..e30e4f9 --- /dev/null +++ b/contracts/bancor/legacy/BancorPriceFloor.sol @@ -0,0 +1,65 @@ +pragma solidity 0.4.26; +import '../utility/TokenHolder.sol'; +import '../utility/Owned.sol'; +import '../utility/Utils.sol'; +import '../utility/SafeMath.sol'; +import '../token/interfaces/ISmartToken.sol'; + +/** + * @dev BancorPriceFloor + * + * The bancor price floor contract is a simple contract that allows selling smart tokens for a constant ETH price + * + * 'Owned' is specified here for readability reasons +*/ +contract BancorPriceFloor is Owned, TokenHolder { + using SafeMath for uint256; + + + uint256 public constant TOKEN_PRICE_N = 1; // crowdsale price in wei (numerator) + uint256 public constant TOKEN_PRICE_D = 100; // crowdsale price in wei (denominator) + + string public version = '0.1'; + ISmartToken public token; // smart token the contract allows selling + + /** + * @dev initializes a new BancorPriceFloor instance + * + * @param _token smart token the contract allows selling + */ + constructor(ISmartToken _token) + public + validAddress(_token) + { + token = _token; + } + + /** + * @dev sells the smart token for ETH + * note that the function will sell the full allowance amount + * + * @return ETH sent in return + */ + function sell() public returns (uint256 amount) { + uint256 allowance = token.allowance(msg.sender, this); // get the full allowance amount + assert(token.transferFrom(msg.sender, this, allowance)); // transfer all tokens from the sender to the contract + uint256 etherValue = allowance.mul(TOKEN_PRICE_N).div(TOKEN_PRICE_D); // calculate ETH value of the tokens + msg.sender.transfer(etherValue); // send the ETH amount to the seller + return etherValue; + } + + /** + * @dev withdraws ETH from the contract + * + * @param _amount amount of ETH to withdraw + */ + function withdraw(uint256 _amount) public ownerOnly { + msg.sender.transfer(_amount); // send the amount + } + + /** + * @dev deposits ETH in the contract + */ + function() public payable { + } +} diff --git a/contracts/bancor/solidity/.solcover.js b/contracts/bancor/solidity/.solcover.js new file mode 100755 index 0000000..cfa188d --- /dev/null +++ b/contracts/bancor/solidity/.solcover.js @@ -0,0 +1,12 @@ +// See +module.exports = { + port: 7555, + norpc: true, + testCommand: "node ../../node_modules/truffle/build/cli.bundled.js test --network=coverage", + compileCommand: "node ../../node_modules/truffle/build/cli.bundled.js compile --network=coverage", + skipFiles: [ + "helpers/Migrations.sol", + "legacy/BancorGasPriceLimit.sol", + "legacy/BancorPriceFloor.sol" + ] +}; diff --git a/contracts/bancor/solidity/truffle-config.js b/contracts/bancor/solidity/truffle-config.js new file mode 100755 index 0000000..6c4d7e3 --- /dev/null +++ b/contracts/bancor/solidity/truffle-config.js @@ -0,0 +1,38 @@ +// See +module.exports = { + networks: { + development: { + host: "localhost", + port: 7545, + network_id: "*", // Match any network id + gasPrice: 20000000000, // Gas price used for deploys + gas: 6721975 // Gas limit used for deploys + }, + production: { + host: "localhost", + port: 7545, + network_id: "*", // Match any network id + gasPrice: 20000000000, // Gas price used for deploys + gas: 6721975 // Gas limit used for deploys + }, + coverage: { // See + host: "localhost", + port: 7555, // Also in .solcover.js + network_id: "*", // Match any network id + gasPrice: 0x1, // Gas price used for deploys + gas: 0x1fffffffffffff // Gas limit used for deploys + } + }, + mocha: { + enableTimeouts: false, + useColors: true, + bail: true, + reporter: "list" // See + }, + solc: { + optimizer: { + enabled: true, + runs: 200 + } + } +}; diff --git a/contracts/bancor/token/ERC20Token.sol b/contracts/bancor/token/ERC20Token.sol new file mode 100755 index 0000000..f8b1682 --- /dev/null +++ b/contracts/bancor/token/ERC20Token.sol @@ -0,0 +1,124 @@ +pragma solidity 0.4.26; +import './interfaces/IERC20Token.sol'; +import '../utility/Utils.sol'; +import '../utility/SafeMath.sol'; + +/** + * @dev ERC20 Standard Token implementation +*/ +contract ERC20Token is IERC20Token, Utils { + using SafeMath for uint256; + + + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + mapping (address => uint256) public balanceOf; + mapping (address => mapping (address => uint256)) public allowance; + + /** + * @dev triggered when tokens are transferred between wallets + * + * @param _from source address + * @param _to target address + * @param _value transfer amount + */ + event Transfer(address indexed _from, address indexed _to, uint256 _value); + + /** + * @dev triggered when a wallet allows another wallet to transfer tokens from on its behalf + * + * @param _owner wallet that approves the allowance + * @param _spender wallet that receives the allowance + * @param _value allowance amount + */ + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + /** + * @dev initializes a new ERC20Token instance + * + * @param _name token name + * @param _symbol token symbol + * @param _decimals decimal points, for display purposes + * @param _totalSupply total supply of token units + */ + constructor(string _name, string _symbol, uint8 _decimals, uint256 _totalSupply) public { + require(bytes(_name).length > 0 && bytes(_symbol).length > 0); // validate input + + name = _name; + symbol = _symbol; + decimals = _decimals; + totalSupply = _totalSupply; + balanceOf[msg.sender] = _totalSupply; + } + + /** + * @dev send coins + * throws on any error rather then return a false flag to minimize user errors + * + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transfer(address _to, uint256 _value) + public + validAddress(_to) + returns (bool success) + { + balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value); + balanceOf[_to] = balanceOf[_to].add(_value); + emit Transfer(msg.sender, _to, _value); + return true; + } + + /** + * @dev an account/contract attempts to get the coins + * throws on any error rather then return a false flag to minimize user errors + * + * @param _from source address + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transferFrom(address _from, address _to, uint256 _value) + public + validAddress(_from) + validAddress(_to) + returns (bool success) + { + allowance[_from][msg.sender] = allowance[_from][msg.sender].sub(_value); + balanceOf[_from] = balanceOf[_from].sub(_value); + balanceOf[_to] = balanceOf[_to].add(_value); + emit Transfer(_from, _to, _value); + return true; + } + + /** + * @dev allow another account/contract to spend some tokens on your behalf + * throws on any error rather then return a false flag to minimize user errors + * + * also, to minimize the risk of the approve/transferFrom attack vector + * (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice + * in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value + * + * @param _spender approved address + * @param _value allowance amount + * + * @return true if the approval was successful, false if it wasn't + */ + function approve(address _spender, uint256 _value) + public + validAddress(_spender) + returns (bool success) + { + // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal + require(_value == 0 || allowance[msg.sender][_spender] == 0); + + allowance[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + return true; + } +} diff --git a/contracts/bancor/token/EtherToken.sol b/contracts/bancor/token/EtherToken.sol new file mode 100755 index 0000000..d08fc23 --- /dev/null +++ b/contracts/bancor/token/EtherToken.sol @@ -0,0 +1,135 @@ +pragma solidity 0.4.26; +import './ERC20Token.sol'; +import './interfaces/IEtherToken.sol'; +import '../utility/SafeMath.sol'; + +/** + * @dev Ether tokenization contract + * + * 'Owned' is specified here for readability reasons +*/ +contract EtherToken is IEtherToken, ERC20Token { + using SafeMath for uint256; + + /** + * @dev triggered when the total supply is increased + * + * @param _amount amount that gets added to the supply + */ + event Issuance(uint256 _amount); + + /** + * @dev triggered when the total supply is decreased + * + * @param _amount amount that gets removed from the supply + */ + event Destruction(uint256 _amount); + + /** + * @dev initializes a new EtherToken instance + * + * @param _name token name + * @param _symbol token symbol + */ + constructor(string _name, string _symbol) + public + ERC20Token(_name, _symbol, 18, 0) { + } + + /** + * @dev deposit ether on behalf of the sender + */ + function deposit() public payable { + depositTo(msg.sender); + } + + /** + * @dev withdraw ether to the sender's account + * + * @param _amount amount of ether to withdraw + */ + function withdraw(uint256 _amount) public { + withdrawTo(msg.sender, _amount); + } + + /** + * @dev deposit ether to be entitled for a given account + * + * @param _to account to be entitled for the ether + */ + function depositTo(address _to) + public + payable + notThis(_to) + { + balanceOf[_to] = balanceOf[_to].add(msg.value); // add the value to the account balance + totalSupply = totalSupply.add(msg.value); // increase the total supply + + emit Issuance(msg.value); + emit Transfer(this, _to, msg.value); + } + + /** + * @dev withdraw ether entitled by the sender to a given account + * + * @param _to account to receive the ether + * @param _amount amount of ether to withdraw + */ + function withdrawTo(address _to, uint256 _amount) + public + notThis(_to) + { + balanceOf[msg.sender] = balanceOf[msg.sender].sub(_amount); // deduct the amount from the account balance + totalSupply = totalSupply.sub(_amount); // decrease the total supply + _to.transfer(_amount); // send the amount to the target account + + emit Transfer(msg.sender, this, _amount); + emit Destruction(_amount); + } + + // ERC20 standard method overrides with some extra protection + + /** + * @dev send coins + * throws on any error rather then return a false flag to minimize user errors + * + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transfer(address _to, uint256 _value) + public + notThis(_to) + returns (bool success) + { + assert(super.transfer(_to, _value)); + return true; + } + + /** + * @dev an account/contract attempts to get the coins + * throws on any error rather then return a false flag to minimize user errors + * + * @param _from source address + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transferFrom(address _from, address _to, uint256 _value) + public + notThis(_to) + returns (bool success) + { + assert(super.transferFrom(_from, _to, _value)); + return true; + } + + /** + * @dev deposit ether in the account + */ + function() public payable { + deposit(); + } +} diff --git a/contracts/bancor/token/SmartToken.sol b/contracts/bancor/token/SmartToken.sol new file mode 100755 index 0000000..b11a4e3 --- /dev/null +++ b/contracts/bancor/token/SmartToken.sol @@ -0,0 +1,141 @@ +pragma solidity 0.4.26; +import './ERC20Token.sol'; +import './interfaces/ISmartToken.sol'; +import '../utility/Owned.sol'; +import '../utility/TokenHolder.sol'; + +/** + * @dev Smart Token + * + * 'Owned' is specified here for readability reasons +*/ +contract SmartToken is ISmartToken, Owned, ERC20Token, TokenHolder { + using SafeMath for uint256; + + + string public version = '0.3'; + + bool public transfersEnabled = true; // true if transfer/transferFrom are enabled, false if not + + /** + * @dev triggered when a smart token is deployed + * the _token address is defined for forward compatibility, in case the event is trigger by a factory + * + * @param _token new smart token address + */ + event NewSmartToken(address _token); + + /** + * @dev triggered when the total supply is increased + * + * @param _amount amount that gets added to the supply + */ + event Issuance(uint256 _amount); + + /** + * @dev triggered when the total supply is decreased + * + * @param _amount amount that gets removed from the supply + */ + event Destruction(uint256 _amount); + + /** + * @dev initializes a new SmartToken instance + * + * @param _name token name + * @param _symbol token short symbol, minimum 1 character + * @param _decimals for display purposes only + */ + constructor(string _name, string _symbol, uint8 _decimals) + public + ERC20Token(_name, _symbol, _decimals, 0) + { + emit NewSmartToken(address(this)); + } + + // allows execution only when transfers aren't disabled + modifier transfersAllowed { + assert(transfersEnabled); + _; + } + + /** + * @dev disables/enables transfers + * can only be called by the contract owner + * + * @param _disable true to disable transfers, false to enable them + */ + function disableTransfers(bool _disable) public ownerOnly { + transfersEnabled = !_disable; + } + + /** + * @dev increases the token supply and sends the new tokens to an account + * can only be called by the contract owner + * + * @param _to account to receive the new amount + * @param _amount amount to increase the supply by + */ + function issue(address _to, uint256 _amount) + public + ownerOnly + validAddress(_to) + notThis(_to) + { + totalSupply = totalSupply.add(_amount); + balanceOf[_to] = balanceOf[_to].add(_amount); + + emit Issuance(_amount); + emit Transfer(this, _to, _amount); + } + + /** + * @dev removes tokens from an account and decreases the token supply + * can be called by the contract owner to destroy tokens from any account or by any holder to destroy tokens from his/her own account + * + * @param _from account to remove the amount from + * @param _amount amount to decrease the supply by + */ + function destroy(address _from, uint256 _amount) public { + require(msg.sender == _from || msg.sender == owner); // validate input + + balanceOf[_from] = balanceOf[_from].sub(_amount); + totalSupply = totalSupply.sub(_amount); + + emit Transfer(_from, this, _amount); + emit Destruction(_amount); + } + + // ERC20 standard method overrides with some extra functionality + + /** + * @dev send coins + * throws on any error rather then return a false flag to minimize user errors + * in addition to the standard checks, the function throws if transfers are disabled + * + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transfer(address _to, uint256 _value) public transfersAllowed returns (bool success) { + assert(super.transfer(_to, _value)); + return true; + } + + /** + * @dev an account/contract attempts to get the coins + * throws on any error rather then return a false flag to minimize user errors + * in addition to the standard checks, the function throws if transfers are disabled + * + * @param _from source address + * @param _to target address + * @param _value transfer amount + * + * @return true if the transfer was successful, false if it wasn't + */ + function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed returns (bool success) { + assert(super.transferFrom(_from, _to, _value)); + return true; + } +} diff --git a/contracts/bancor/token/SmartTokenController.sol b/contracts/bancor/token/SmartTokenController.sol new file mode 100755 index 0000000..78aa39c --- /dev/null +++ b/contracts/bancor/token/SmartTokenController.sol @@ -0,0 +1,104 @@ +pragma solidity 0.4.26; +import './interfaces/ISmartTokenController.sol'; +import './interfaces/ISmartToken.sol'; +import '../utility/TokenHolder.sol'; + +/** + * @dev The smart token controller is an upgradable part of the smart token that allows + * more functionality as well as fixes for bugs/exploits. + * Once it accepts ownership of the token, it becomes the token's sole controller + * that can execute any of its functions. + * + * To upgrade the controller, ownership must be transferred to a new controller, along with + * any relevant data. + * + * The smart token must be set on construction and cannot be changed afterwards. + * Wrappers are provided (as opposed to a single 'execute' function) for each of the token's functions, for easier access. + * + * Note that the controller can transfer token ownership to a new controller that + * doesn't allow executing any function on the token, for a trustless solution. + * Doing that will also remove the owner's ability to upgrade the controller. +*/ +contract SmartTokenController is ISmartTokenController, TokenHolder { + ISmartToken public token; // Smart Token contract + address public bancorX; // BancorX contract + + /** + * @dev initializes a new SmartTokenController instance + * + * @param _token smart token governed by the controller + */ + constructor(ISmartToken _token) + public + validAddress(_token) + { + token = _token; + } + + // ensures that the controller is the token's owner + modifier active() { + require(token.owner() == address(this)); + _; + } + + // ensures that the controller is not the token's owner + modifier inactive() { + require(token.owner() != address(this)); + _; + } + + /** + * @dev allows transferring the token ownership + * the new owner needs to accept the transfer + * can only be called by the contract owner + * + * @param _newOwner new token owner + */ + function transferTokenOwnership(address _newOwner) public ownerOnly { + token.transferOwnership(_newOwner); + } + + /** + * @dev used by a new owner to accept a token ownership transfer + * can only be called by the contract owner + */ + function acceptTokenOwnership() public ownerOnly { + token.acceptOwnership(); + } + + /** + * @dev withdraws tokens held by the controller and sends them to an account + * can only be called by the owner + * + * @param _token ERC20 token contract address + * @param _to account to receive the new amount + * @param _amount amount to withdraw + */ + function withdrawFromToken(IERC20Token _token, address _to, uint256 _amount) public ownerOnly { + ITokenHolder(token).withdrawTokens(_token, _to, _amount); + } + + /** + * @dev allows the associated BancorX contract to claim tokens from any address (so that users + * dont have to first give allowance when calling BancorX) + * + * @param _from address to claim the tokens from + * @param _amount the amount of tokens to claim + */ + function claimTokens(address _from, uint256 _amount) public { + // only the associated BancorX contract may call this method + require(msg.sender == bancorX); + + // destroy the tokens belonging to _from, and issue the same amount to bancorX + token.destroy(_from, _amount); + token.issue(msg.sender, _amount); + } + + /** + * @dev allows the owner to set the associated BancorX contract + * @param _bancorX BancorX contract + */ + function setBancorX(address _bancorX) public ownerOnly { + bancorX = _bancorX; + } +} diff --git a/contracts/bancor/token/interfaces/IERC20Token.sol b/contracts/bancor/token/interfaces/IERC20Token.sol new file mode 100755 index 0000000..03dcf8e --- /dev/null +++ b/contracts/bancor/token/interfaces/IERC20Token.sol @@ -0,0 +1,18 @@ +pragma solidity 0.4.26; + +/* + ERC20 Standard Token interface +*/ +contract IERC20Token { + // these functions aren't abstract since the compiler emits automatically generated getter functions as external + function name() public view returns (string) {this;} + function symbol() public view returns (string) {this;} + function decimals() public view returns (uint8) {this;} + function totalSupply() public view returns (uint256) {this;} + function balanceOf(address _owner) public view returns (uint256) {_owner; this;} + function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;} + + function transfer(address _to, uint256 _value) public returns (bool success); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + function approve(address _spender, uint256 _value) public returns (bool success); +} diff --git a/contracts/bancor/token/interfaces/IEtherToken.sol b/contracts/bancor/token/interfaces/IEtherToken.sol new file mode 100755 index 0000000..8e6a92e --- /dev/null +++ b/contracts/bancor/token/interfaces/IEtherToken.sol @@ -0,0 +1,12 @@ +pragma solidity 0.4.26; +import './IERC20Token.sol'; + +/* + Ether Token interface +*/ +contract IEtherToken is IERC20Token { + function deposit() public payable; + function withdraw(uint256 _amount) public; + function depositTo(address _to) public payable; + function withdrawTo(address _to, uint256 _amount) public; +} diff --git a/contracts/bancor/token/interfaces/INonStandardERC20.sol b/contracts/bancor/token/interfaces/INonStandardERC20.sol new file mode 100755 index 0000000..eea9cea --- /dev/null +++ b/contracts/bancor/token/interfaces/INonStandardERC20.sol @@ -0,0 +1,18 @@ +pragma solidity 0.4.26; + +/* + ERC20 Standard Token interface which doesn't return true/false for transfer, transferFrom and approve +*/ +contract INonStandardERC20 { + // these functions aren't abstract since the compiler emits automatically generated getter functions as external + function name() public view returns (string) {this;} + function symbol() public view returns (string) {this;} + function decimals() public view returns (uint8) {this;} + function totalSupply() public view returns (uint256) {this;} + function balanceOf(address _owner) public view returns (uint256) {_owner; this;} + function allowance(address _owner, address _spender) public view returns (uint256) {_owner; _spender; this;} + + function transfer(address _to, uint256 _value) public; + function transferFrom(address _from, address _to, uint256 _value) public; + function approve(address _spender, uint256 _value) public; +} diff --git a/contracts/bancor/token/interfaces/ISmartToken.sol b/contracts/bancor/token/interfaces/ISmartToken.sol new file mode 100755 index 0000000..c9a0741 --- /dev/null +++ b/contracts/bancor/token/interfaces/ISmartToken.sol @@ -0,0 +1,12 @@ +pragma solidity 0.4.26; +import './IERC20Token.sol'; +import '../../utility/interfaces/IOwned.sol'; + +/* + Smart Token interface +*/ +contract ISmartToken is IOwned, IERC20Token { + function disableTransfers(bool _disable) public; + function issue(address _to, uint256 _amount) public; + function destroy(address _from, uint256 _amount) public; +} diff --git a/contracts/bancor/token/interfaces/ISmartTokenController.sol b/contracts/bancor/token/interfaces/ISmartTokenController.sol new file mode 100755 index 0000000..b8123c1 --- /dev/null +++ b/contracts/bancor/token/interfaces/ISmartTokenController.sol @@ -0,0 +1,10 @@ +pragma solidity 0.4.26; +import './ISmartToken.sol'; + +/* + Smart Token Controller interface +*/ +contract ISmartTokenController { + function claimTokens(address _from, uint256 _amount) public; + function token() public view returns (ISmartToken) {this;} +} diff --git a/contracts/bancor/utility/ContractFeatures.sol b/contracts/bancor/utility/ContractFeatures.sol new file mode 100755 index 0000000..5cf1b06 --- /dev/null +++ b/contracts/bancor/utility/ContractFeatures.sol @@ -0,0 +1,79 @@ +pragma solidity 0.4.26; +import './interfaces/IContractFeatures.sol'; + +/** + * @dev Contract Features + * + * Generic contract that allows every contract on the blockchain to define which features it supports. + * Other contracts can query this contract to find out whether a given contract on the + * blockchain supports a certain feature. + * Each contract type can define its own list of feature flags. + * Features can be only enabled/disabled by the contract they are defined for. + * + * Features should be defined by each contract type as bit flags, e.g. - + * uint256 public constant FEATURE1 = 1 << 0; + * uint256 public constant FEATURE2 = 1 << 1; + * uint256 public constant FEATURE3 = 1 << 2; + * ... +*/ +contract ContractFeatures is IContractFeatures { + mapping (address => uint256) private featureFlags; + + /** + * @dev triggered when a contract notifies of features it supports + * + * @param _address contract address + * @param _features features supported + */ + event FeaturesAddition(address indexed _address, uint256 _features); + + /** + * @dev triggered when a contract notifies of features it no longer supports + * + * @param _address contract address + * @param _features features no longer supported + */ + event FeaturesRemoval(address indexed _address, uint256 _features); + + /** + * @dev initializes a new ContractFeatures instance + */ + constructor() public { + } + + /** + * @dev returns true if a given contract supports the given feature(s), false if not + * + * @param _contract contract address to check support for + * @param _features feature(s) to check for + * + * @return true if the contract supports the feature(s), false if not + */ + function isSupported(address _contract, uint256 _features) public view returns (bool) { + return (featureFlags[_contract] & _features) == _features; + } + + /** + * @dev allows a contract to enable/disable certain feature(s) + * + * @param _features feature(s) to enable/disable + * @param _enable true to enable the feature(s), false to disabled them + */ + function enableFeatures(uint256 _features, bool _enable) public { + if (_enable) { + if (isSupported(msg.sender, _features)) + return; + + featureFlags[msg.sender] |= _features; + + emit FeaturesAddition(msg.sender, _features); + } else { + if (!isSupported(msg.sender, _features)) + return; + + featureFlags[msg.sender] &= ~_features; + + emit FeaturesRemoval(msg.sender, _features); + } + } +} diff --git a/contracts/bancor/utility/ContractRegistry.sol b/contracts/bancor/utility/ContractRegistry.sol new file mode 100755 index 0000000..ba36f85 --- /dev/null +++ b/contracts/bancor/utility/ContractRegistry.sol @@ -0,0 +1,149 @@ +pragma solidity 0.4.26; +import './Owned.sol'; +import './Utils.sol'; +import './interfaces/IContractRegistry.sol'; + +/** + * @dev Contract Registry + * + * The contract registry keeps contract addresses by name. + * The owner can update contract addresses so that a contract name always points to the latest version + * of the given contract. + * Other contracts can query the registry to get updated addresses instead of depending on specific + * addresses. + * + * Note that contract names are limited to 32 bytes UTF8 encoded ASCII strings to optimize gas costs +*/ +contract ContractRegistry is IContractRegistry, Owned, Utils { + struct RegistryItem { + address contractAddress; // contract address + uint256 nameIndex; // index of the item in the list of contract names + } + + mapping (bytes32 => RegistryItem) private items; // name -> RegistryItem mapping + string[] public contractNames; // list of all registered contract names + + /** + * @dev triggered when an address pointed to by a contract name is modified + * + * @param _contractName contract name + * @param _contractAddress new contract address + */ + event AddressUpdate(bytes32 indexed _contractName, address _contractAddress); + + /** + * @dev returns the number of items in the registry + * + * @return number of items + */ + function itemCount() public view returns (uint256) { + return contractNames.length; + } + + /** + * @dev returns the address associated with the given contract name + * + * @param _contractName contract name + * + * @return contract address + */ + function addressOf(bytes32 _contractName) public view returns (address) { + return items[_contractName].contractAddress; + } + + /** + * @dev registers a new address for the contract name in the registry + * + * @param _contractName contract name + * @param _contractAddress contract address + */ + function registerAddress(bytes32 _contractName, address _contractAddress) + public + ownerOnly + validAddress(_contractAddress) + { + require(_contractName.length > 0); // validate input + + if (items[_contractName].contractAddress == address(0)) { + // add the contract name to the name list + uint256 i = contractNames.push(bytes32ToString(_contractName)); + // update the item's index in the list + items[_contractName].nameIndex = i - 1; + } + + // update the address in the registry + items[_contractName].contractAddress = _contractAddress; + + // dispatch the address update event + emit AddressUpdate(_contractName, _contractAddress); + } + + /** + * @dev removes an existing contract address from the registry + * + * @param _contractName contract name + */ + function unregisterAddress(bytes32 _contractName) public ownerOnly { + require(_contractName.length > 0); // validate input + require(items[_contractName].contractAddress != address(0)); + + // remove the address from the registry + items[_contractName].contractAddress = address(0); + + // if there are multiple items in the registry, move the last element to the deleted element's position + // and modify last element's registryItem.nameIndex in the items collection to point to the right position in contractNames + if (contractNames.length > 1) { + string memory lastContractNameString = contractNames[contractNames.length - 1]; + uint256 unregisterIndex = items[_contractName].nameIndex; + + contractNames[unregisterIndex] = lastContractNameString; + bytes32 lastContractName = stringToBytes32(lastContractNameString); + RegistryItem storage registryItem = items[lastContractName]; + registryItem.nameIndex = unregisterIndex; + } + + // remove the last element from the name list + contractNames.length--; + // zero the deleted element's index + items[_contractName].nameIndex = 0; + + // dispatch the address update event + emit AddressUpdate(_contractName, address(0)); + } + + /** + * @dev utility, converts bytes32 to a string + * note that the bytes32 argument is assumed to be UTF8 encoded ASCII string + * + * @return string representation of the given bytes32 argument + */ + function bytes32ToString(bytes32 _bytes) private pure returns (string) { + bytes memory byteArray = new bytes(32); + for (uint256 i; i < 32; i++) { + byteArray[i] = _bytes[i]; + } + + return string(byteArray); + } + + /** + * @dev utility, converts string to bytes32 + * note that the bytes32 argument is assumed to be UTF8 encoded ASCII string + * + * @return string representation of the given bytes32 argument + */ + function stringToBytes32(string memory _string) private pure returns (bytes32) { + bytes32 result; + assembly { + result := mload(add(_string,32)) + } + return result; + } + + /** + * @dev deprecated, backward compatibility + */ + function getAddress(bytes32 _contractName) public view returns (address) { + return addressOf(_contractName); + } +} diff --git a/contracts/bancor/utility/ContractRegistryClient.sol b/contracts/bancor/utility/ContractRegistryClient.sol new file mode 100755 index 0000000..0387806 --- /dev/null +++ b/contracts/bancor/utility/ContractRegistryClient.sol @@ -0,0 +1,110 @@ +pragma solidity 0.4.26; +import './Owned.sol'; +import './Utils.sol'; +import './interfaces/IContractRegistry.sol'; + +/** + * @dev Base contract for ContractRegistry clients +*/ +contract ContractRegistryClient is Owned, Utils { + bytes32 internal constant CONTRACT_FEATURES = "ContractFeatures"; + bytes32 internal constant CONTRACT_REGISTRY = "ContractRegistry"; + bytes32 internal constant BANCOR_NETWORK = "BancorNetwork"; + bytes32 internal constant BANCOR_FORMULA = "BancorFormula"; + bytes32 internal constant BANCOR_CONVERTER_FACTORY = "BancorConverterFactory"; + bytes32 internal constant BANCOR_CONVERTER_UPGRADER = "BancorConverterUpgrader"; + bytes32 internal constant BANCOR_CONVERTER_REGISTRY = "BancorConverterRegistry"; + bytes32 internal constant BANCOR_CONVERTER_REGISTRY_DATA = "BancorConverterRegistryData"; + bytes32 internal constant BNT_TOKEN = "BNTToken"; + bytes32 internal constant BANCOR_X = "BancorX"; + bytes32 internal constant BANCOR_X_UPGRADER = "BancorXUpgrader"; + + IContractRegistry public registry; // address of the current contract-registry + IContractRegistry public prevRegistry; // address of the previous contract-registry + bool public adminOnly; // only an administrator can update the contract-registry + + /** + * @dev verifies that the caller is mapped to the given contract name + * + * @param _contractName contract name + */ + modifier only(bytes32 _contractName) { + require(msg.sender == addressOf(_contractName)); + _; + } + + /** + * @dev initializes a new ContractRegistryClient instance + * + * @param _registry address of a contract-registry contract + */ + constructor(IContractRegistry _registry) internal validAddress(_registry) { + registry = IContractRegistry(_registry); + prevRegistry = IContractRegistry(_registry); + } + + /** + * @dev updates to the new contract-registry + */ + function updateRegistry() public { + // verify that this function is permitted + require(!adminOnly || isAdmin()); + + // get the new contract-registry + address newRegistry = addressOf(CONTRACT_REGISTRY); + + // verify that the new contract-registry is different and not zero + require(newRegistry != address(registry) && newRegistry != address(0)); + + // verify that the new contract-registry is pointing to a non-zero contract-registry + require(IContractRegistry(newRegistry).addressOf(CONTRACT_REGISTRY) != address(0)); + + // save a backup of the current contract-registry before replacing it + prevRegistry = registry; + + // replace the current contract-registry with the new contract-registry + registry = IContractRegistry(newRegistry); + } + + /** + * @dev restores the previous contract-registry + */ + function restoreRegistry() public { + // verify that this function is permitted + require(isAdmin()); + + // restore the previous contract-registry + registry = prevRegistry; + } + + /** + * @dev restricts the permission to update the contract-registry + * + * @param _adminOnly indicates whether or not permission is restricted to administrator only + */ + function restrictRegistryUpdate(bool _adminOnly) public { + // verify that this function is permitted + require(adminOnly != _adminOnly && isAdmin()); + + // change the permission to update the contract-registry + adminOnly = _adminOnly; + } + + /** + * @dev returns whether or not the caller is an administrator + */ + function isAdmin() internal view returns (bool) { + return msg.sender == owner; + } + + /** + * @dev returns the address associated with the given contract name + * + * @param _contractName contract name + * + * @return contract address + */ + function addressOf(bytes32 _contractName) internal view returns (address) { + return registry.addressOf(_contractName); + } +} diff --git a/contracts/bancor/utility/Managed.sol b/contracts/bancor/utility/Managed.sol new file mode 100755 index 0000000..1defb25 --- /dev/null +++ b/contracts/bancor/utility/Managed.sol @@ -0,0 +1,60 @@ +pragma solidity 0.4.26; +import './Owned.sol'; + +/** + * @dev Provides support and utilities for contract management + * Note that a managed contract must also have an owner +*/ +contract Managed is Owned { + address public manager; + address public newManager; + + /** + * @dev triggered when the manager is updated + * + * @param _prevManager previous manager + * @param _newManager new manager + */ + event ManagerUpdate(address indexed _prevManager, address indexed _newManager); + + /** + * @dev initializes a new Managed instance + */ + constructor() public { + manager = msg.sender; + } + + // allows execution by the manager only + modifier managerOnly { + assert(msg.sender == manager); + _; + } + + // allows execution by either the owner or the manager only + modifier ownerOrManagerOnly { + require(msg.sender == owner || msg.sender == manager); + _; + } + + /** + * @dev allows transferring the contract management + * the new manager still needs to accept the transfer + * can only be called by the contract manager + * + * @param _newManager new contract manager + */ + function transferManagement(address _newManager) public ownerOrManagerOnly { + require(_newManager != manager); + newManager = _newManager; + } + + /** + * @dev used by a new manager to accept a management transfer + */ + function acceptManagement() public { + require(msg.sender == newManager); + emit ManagerUpdate(manager, newManager); + manager = newManager; + newManager = address(0); + } +} diff --git a/contracts/bancor/utility/Owned.sol b/contracts/bancor/utility/Owned.sol new file mode 100755 index 0000000..5454499 --- /dev/null +++ b/contracts/bancor/utility/Owned.sol @@ -0,0 +1,53 @@ +pragma solidity 0.4.26; +import './interfaces/IOwned.sol'; + +/** + * @dev Provides support and utilities for contract ownership +*/ +contract Owned is IOwned { + address public owner; + address public newOwner; + + /** + * @dev triggered when the owner is updated + * + * @param _prevOwner previous owner + * @param _newOwner new owner + */ + event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner); + + /** + * @dev initializes a new Owned instance + */ + constructor() public { + owner = msg.sender; + } + + // allows execution by the owner only + modifier ownerOnly { + require(msg.sender == owner); + _; + } + + /** + * @dev allows transferring the contract ownership + * the new owner still needs to accept the transfer + * can only be called by the contract owner + * + * @param _newOwner new contract owner + */ + function transferOwnership(address _newOwner) public ownerOnly { + require(_newOwner != owner); + newOwner = _newOwner; + } + + /** + * @dev used by a new owner to accept an ownership transfer + */ + function acceptOwnership() public { + require(msg.sender == newOwner); + emit OwnerUpdate(owner, newOwner); + owner = newOwner; + newOwner = address(0); + } +} diff --git a/contracts/bancor/utility/SafeMath.sol b/contracts/bancor/utility/SafeMath.sol new file mode 100755 index 0000000..1595a92 --- /dev/null +++ b/contracts/bancor/utility/SafeMath.sol @@ -0,0 +1,66 @@ +pragma solidity 0.4.26; + +/** + * @dev Library for basic math operations with overflow/underflow protection +*/ +library SafeMath { + /** + * @dev returns the sum of _x and _y, reverts if the calculation overflows + * + * @param _x value 1 + * @param _y value 2 + * + * @return sum + */ + function add(uint256 _x, uint256 _y) internal pure returns (uint256) { + uint256 z = _x + _y; + require(z >= _x); + return z; + } + + /** + * @dev returns the difference of _x minus _y, reverts if the calculation underflows + * + * @param _x minuend + * @param _y subtrahend + * + * @return difference + */ + function sub(uint256 _x, uint256 _y) internal pure returns (uint256) { + require(_x >= _y); + return _x - _y; + } + + /** + * @dev returns the product of multiplying _x by _y, reverts if the calculation overflows + * + * @param _x factor 1 + * @param _y factor 2 + * + * @return product + */ + function mul(uint256 _x, uint256 _y) internal pure returns (uint256) { + // gas optimization + if (_x == 0) + return 0; + + uint256 z = _x * _y; + require(z / _x == _y); + return z; + } + + /** + * ev Integer division of two numbers truncating the quotient, reverts on division by zero. + * + * aram _x dividend + * aram _y divisor + * + * eturn quotient + */ + function div(uint256 _x, uint256 _y) internal pure returns (uint256) { + require(_y > 0); + uint256 c = _x / _y; + + return c; + } +} diff --git a/contracts/bancor/utility/TokenHolder.sol b/contracts/bancor/utility/TokenHolder.sol new file mode 100755 index 0000000..cd1797d --- /dev/null +++ b/contracts/bancor/utility/TokenHolder.sol @@ -0,0 +1,43 @@ +pragma solidity 0.4.26; +import './Owned.sol'; +import './Utils.sol'; +import './interfaces/ITokenHolder.sol'; +import '../token/interfaces/INonStandardERC20.sol'; +import '../token/interfaces/IERC20Token.sol'; + +/** + * @dev We consider every contract to be a 'token holder' since it's currently not possible + * for a contract to deny receiving tokens. + * + * The TokenHolder's contract sole purpose is to provide a safety mechanism that allows + * the owner to send tokens that were sent to the contract by mistake back to their sender. + * + * Note that we use the non standard ERC-20 interface which has no return value for transfer + * in order to support both non standard as well as standard token contracts. + * see https://github.com/ethereum/solidity/issues/4116 +*/ +contract TokenHolder is ITokenHolder, Owned, Utils { + /** + * @dev initializes a new TokenHolder instance + */ + constructor() public { + } + + /** + * @dev withdraws tokens held by the contract and sends them to an account + * can only be called by the owner + * + * @param _token ERC20 token contract address + * @param _to account to receive the new amount + * @param _amount amount to withdraw + */ + function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) + public + ownerOnly + validAddress(_token) + validAddress(_to) + notThis(_to) + { + INonStandardERC20(_token).transfer(_to, _amount); + } +} diff --git a/contracts/bancor/utility/Utils.sol b/contracts/bancor/utility/Utils.sol new file mode 100755 index 0000000..2c5ac91 --- /dev/null +++ b/contracts/bancor/utility/Utils.sol @@ -0,0 +1,31 @@ +pragma solidity 0.4.26; + +/** + * @dev Utilities & Common Modifiers +*/ +contract Utils { + /** + * constructor + */ + constructor() public { + } + + // verifies that an amount is greater than zero + modifier greaterThanZero(uint256 _amount) { + require(_amount > 0); + _; + } + + // validates an address - currently only checks that it isn't null + modifier validAddress(address _address) { + require(_address != address(0)); + _; + } + + // verifies that the address is different than this contract address + modifier notThis(address _address) { + require(_address != address(this)); + _; + } + +} diff --git a/contracts/bancor/utility/Whitelist.sol b/contracts/bancor/utility/Whitelist.sol new file mode 100755 index 0000000..8364278 --- /dev/null +++ b/contracts/bancor/utility/Whitelist.sol @@ -0,0 +1,94 @@ +pragma solidity 0.4.26; +import './Owned.sol'; +import './Utils.sol'; +import './interfaces/IWhitelist.sol'; + +/** + * @dev The contract manages a list of whitelisted addresses +*/ +contract Whitelist is IWhitelist, Owned, Utils { + mapping (address => bool) private whitelist; + + /** + * @dev triggered when an address is added to the whitelist + * + * @param _address address that's added from the whitelist + */ + event AddressAddition(address _address); + + /** + * @dev triggered when an address is removed from the whitelist + * + * @param _address address that's removed from the whitelist + */ + event AddressRemoval(address _address); + + /** + * @dev initializes a new Whitelist instance + */ + constructor() public { + } + + /** + * @dev returns true if a given address is whitelisted, false if not + * + * @param _address address to check + * + * @return true if the address is whitelisted, false if not + */ + function isWhitelisted(address _address) public view returns (bool) { + return whitelist[_address]; + } + + /** + * @dev adds a given address to the whitelist + * + * @param _address address to add + */ + function addAddress(address _address) + ownerOnly + validAddress(_address) + public + { + if (whitelist[_address]) // checks if the address is already whitelisted + return; + + whitelist[_address] = true; + emit AddressAddition(_address); + } + + /** + * @dev adds a list of addresses to the whitelist + * + * @param _addresses addresses to add + */ + function addAddresses(address[] _addresses) public { + for (uint256 i = 0; i < _addresses.length; i++) { + addAddress(_addresses[i]); + } + } + + /** + * @dev removes a given address from the whitelist + * + * @param _address address to remove + */ + function removeAddress(address _address) ownerOnly public { + if (!whitelist[_address]) // checks if the address is actually whitelisted + return; + + whitelist[_address] = false; + emit AddressRemoval(_address); + } + + /** + * @dev removes a list of addresses from the whitelist + * + * @param _addresses addresses to remove + */ + function removeAddresses(address[] _addresses) public { + for (uint256 i = 0; i < _addresses.length; i++) { + removeAddress(_addresses[i]); + } + } +} diff --git a/contracts/bancor/utility/interfaces/IAddressList.sol b/contracts/bancor/utility/interfaces/IAddressList.sol new file mode 100755 index 0000000..5ca494a --- /dev/null +++ b/contracts/bancor/utility/interfaces/IAddressList.sol @@ -0,0 +1,8 @@ +pragma solidity 0.4.26; + +/* + Address list interface +*/ +contract IAddressList { + mapping (address => bool) public listedAddresses; +} diff --git a/contracts/bancor/utility/interfaces/IContractFeatures.sol b/contracts/bancor/utility/interfaces/IContractFeatures.sol new file mode 100755 index 0000000..9aed327 --- /dev/null +++ b/contracts/bancor/utility/interfaces/IContractFeatures.sol @@ -0,0 +1,9 @@ +pragma solidity 0.4.26; + +/* + Contract Features interface +*/ +contract IContractFeatures { + function isSupported(address _contract, uint256 _features) public view returns (bool); + function enableFeatures(uint256 _features, bool _enable) public; +} diff --git a/contracts/bancor/utility/interfaces/IContractRegistry.sol b/contracts/bancor/utility/interfaces/IContractRegistry.sol new file mode 100755 index 0000000..ce8df03 --- /dev/null +++ b/contracts/bancor/utility/interfaces/IContractRegistry.sol @@ -0,0 +1,11 @@ +pragma solidity 0.4.26; + +/* + Contract Registry interface +*/ +contract IContractRegistry { + function addressOf(bytes32 _contractName) public view returns (address); + + // deprecated, backward compatibility + function getAddress(bytes32 _contractName) public view returns (address); +} diff --git a/contracts/bancor/utility/interfaces/IOwned.sol b/contracts/bancor/utility/interfaces/IOwned.sol new file mode 100755 index 0000000..54a9e85 --- /dev/null +++ b/contracts/bancor/utility/interfaces/IOwned.sol @@ -0,0 +1,12 @@ +pragma solidity 0.4.26; + +/* + Owned contract interface +*/ +contract IOwned { + // this function isn't abstract since the compiler emits automatically generated getter functions as external + function owner() public view returns (address) {this;} + + function transferOwnership(address _newOwner) public; + function acceptOwnership() public; +} diff --git a/contracts/bancor/utility/interfaces/ITokenHolder.sol b/contracts/bancor/utility/interfaces/ITokenHolder.sol new file mode 100755 index 0000000..0c7c152 --- /dev/null +++ b/contracts/bancor/utility/interfaces/ITokenHolder.sol @@ -0,0 +1,10 @@ +pragma solidity 0.4.26; +import './IOwned.sol'; +import '../../token/interfaces/IERC20Token.sol'; + +/* + Token Holder interface +*/ +contract ITokenHolder is IOwned { + function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public; +} diff --git a/contracts/bancor/utility/interfaces/IWhitelist.sol b/contracts/bancor/utility/interfaces/IWhitelist.sol new file mode 100755 index 0000000..cde1970 --- /dev/null +++ b/contracts/bancor/utility/interfaces/IWhitelist.sol @@ -0,0 +1,8 @@ +pragma solidity 0.4.26; + +/* + Whitelist interface +*/ +contract IWhitelist { + function isWhitelisted(address _address) public view returns (bool); +} diff --git a/contracts/storage/BnConstants.sol b/contracts/storage/BnConstants.sol new file mode 100755 index 0000000..b04fa87 --- /dev/null +++ b/contracts/storage/BnConstants.sol @@ -0,0 +1,21 @@ +pragma solidity 0.4.26; + + +/// @title Shared constants +contract BnConstants { + + /** + * @notice In Exp terms, 1e18 is 1, or 100% + */ + uint256 constant hundredPercent = 1e18; + + /** + * @notice In Exp terms, 1e16 is 0.01, or 1% + */ + uint256 constant onePercent = 1e16; + + bool constant CONFIRMED = true; + + uint8 constant EXAMPLE_VALUE = 1; + +} diff --git a/contracts/storage/BnEvents.sol b/contracts/storage/BnEvents.sol new file mode 100755 index 0000000..e671595 --- /dev/null +++ b/contracts/storage/BnEvents.sol @@ -0,0 +1,11 @@ +pragma solidity 0.4.26; + + +contract BnEvents { + + event Example( + uint256 indexed Id, + uint256 exchangeRateCurrent + ); + +} diff --git a/contracts/storage/BnObjects.sol b/contracts/storage/BnObjects.sol new file mode 100755 index 0000000..fba97cd --- /dev/null +++ b/contracts/storage/BnObjects.sol @@ -0,0 +1,11 @@ +pragma solidity 0.4.26; + + +contract BnObjects { + + struct ExampleObject { + address addr; + uint amount; + } + +} diff --git a/contracts/storage/BnStorage.sol b/contracts/storage/BnStorage.sol new file mode 100755 index 0000000..117e96d --- /dev/null +++ b/contracts/storage/BnStorage.sol @@ -0,0 +1,13 @@ +pragma solidity 0.4.26; + +import "./BnObjects.sol"; +import "./BnEvents.sol"; + + +// shared storage +contract BnStorage is BnObjects, BnEvents { + + mapping (uint => ExampleObject) examples; + +} + diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js new file mode 100755 index 0000000..d0272af --- /dev/null +++ b/migrations/1_initial_migration.js @@ -0,0 +1,4 @@ +module.exports = function(deployer, network, accounts) { + if (network == "production") + deployer.deploy(artifacts.require("Migrations")); +}; diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js new file mode 100755 index 0000000..daa887c --- /dev/null +++ b/migrations/2_deploy_contracts.js @@ -0,0 +1,11 @@ +module.exports = function(deployer, network, accounts) { + if (network == "production") { + const CFG_FILE_NAME = process.argv[4]; + const NODE_ADDRESS = process.argv[5]; + const PRIVATE_KEY = process.argv[6]; + const child_process = require("child_process"); + const result = child_process.spawnSync("node", [__dirname + "/../utils/deploy_network_emulation.js", CFG_FILE_NAME, NODE_ADDRESS, PRIVATE_KEY], {stdio: ["inherit", "inherit", "pipe"]}); + if (result.stderr.length > 0) + throw new Error(result.stderr); + } +}; diff --git a/package-lock.json b/package-lock.json new file mode 100755 index 0000000..2399e7d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,8790 @@ +{ + "name": "bancor-contracts", + "version": "0.5.15", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.2.tgz", + "integrity": "sha512-EXxN64agfUqqIGeEjI5dL5z0Sw0ZwWo1mLTi4mQowCZ42O59b7DRpZAnTC6OqdF28wMBMFKNb/4uFGrVaigSpg==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "@resolver-engine/core": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@resolver-engine/core/-/core-0.2.1.tgz", + "integrity": "sha512-nsLQHmPJ77QuifqsIvqjaF5B9aHnDzJjp73Q1z6apY3e9nqYrx4Dtowhpsf7Jwftg/XzVDEMQC+OzUBNTS+S1A==", + "requires": { + "debug": "^3.1.0", + "request": "^2.85.0" + } + }, + "@resolver-engine/fs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@resolver-engine/fs/-/fs-0.2.1.tgz", + "integrity": "sha512-7kJInM1Qo2LJcKyDhuYzh9ZWd+mal/fynfL9BNjWOiTcOpX+jNfqb/UmGUqros5pceBITlWGqS4lU709yHFUbg==", + "requires": { + "@resolver-engine/core": "^0.2.1", + "debug": "^3.1.0" + } + }, + "@resolver-engine/imports": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@resolver-engine/imports/-/imports-0.2.2.tgz", + "integrity": "sha512-u5/HUkvo8q34AA+hnxxqqXGfby5swnH0Myw91o3Sm2TETJlNKXibFGSKBavAH+wvWdBi4Z5gS2Odu0PowgVOUg==", + "requires": { + "@resolver-engine/core": "^0.2.1", + "debug": "^3.1.0", + "hosted-git-info": "^2.6.0" + } + }, + "@resolver-engine/imports-fs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@resolver-engine/imports-fs/-/imports-fs-0.2.2.tgz", + "integrity": "sha512-gFCgMvCwyppjwq0UzIjde/WI+yDs3oatJhozG9xdjJdewwtd7LiF0T5i9lrHAUtqrQbqoFE4E+ZMRVHWpWHpKQ==", + "requires": { + "@resolver-engine/fs": "^0.2.1", + "@resolver-engine/imports": "^0.2.2", + "debug": "^3.1.0" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@snyk/cli-interface": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.3.0.tgz", + "integrity": "sha512-ecbylK5Ol2ySb/WbfPj0s0GuLQR+KWKFzUgVaoNHaSoN6371qRWwf2uVr+hPUP4gXqCai21Ug/RDArfOhlPwrQ==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@snyk/cocoapods-lockfile-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@snyk/cocoapods-lockfile-parser/-/cocoapods-lockfile-parser-3.0.0.tgz", + "integrity": "sha512-AebCc+v9vtOL9tFkU4/tommgVsXxqdx6t45kCkBW+FC4PaYvfYEg9Eg/9GqlY9+nFrLFo/uTr+E/aR0AF/KqYA==", + "requires": { + "@snyk/dep-graph": "^1.11.0", + "@snyk/ruby-semver": "^2.0.4", + "@types/js-yaml": "^3.12.1", + "core-js": "^3.2.0", + "js-yaml": "^3.13.1", + "source-map-support": "^0.5.7", + "tslib": "^1.9.3" + } + }, + "@snyk/composer-lockfile-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@snyk/composer-lockfile-parser/-/composer-lockfile-parser-1.2.0.tgz", + "integrity": "sha512-kZT+HTqgNcQMeoE5NM9M3jj463M8zI7ZxqZXLw9WoyVs5JTt9g0qFWxIG1cNwZdGVI+y7tzZbNWw9BlMD1vCCQ==", + "requires": { + "lodash": "^4.17.13" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "@snyk/dep-graph": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@snyk/dep-graph/-/dep-graph-1.13.1.tgz", + "integrity": "sha512-Ww2xvm5UQgrq9eV0SdTBCh+w/4oI2rCx5vn1IOSeypaR0CO4p+do1vm3IDZ2ugg4jLSfHP8+LiD6ORESZMkQ2w==", + "requires": { + "graphlib": "^2.1.5", + "lodash": "^4.7.14", + "object-hash": "^1.3.1", + "semver": "^6.0.0", + "source-map-support": "^0.5.11", + "tslib": "^1.9.3" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@snyk/gemfile": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@snyk/gemfile/-/gemfile-1.2.0.tgz", + "integrity": "sha512-nI7ELxukf7pT4/VraL4iabtNNMz8mUo7EXlqCFld8O5z6mIMLX9llps24iPpaIZOwArkY3FWA+4t+ixyvtTSIA==" + }, + "@snyk/ruby-semver": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@snyk/ruby-semver/-/ruby-semver-2.0.4.tgz", + "integrity": "sha512-ceMD4CBS3qtAg+O0BUvkKdsheUNCqi+/+Rju243Ul8PsUgZnXmGiqfk/2z7DCprRQnxUTra4+IyeDQT7wAheCQ==", + "requires": { + "lodash": "^4.17.14" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "@snyk/snyk-cocoapods-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@snyk/snyk-cocoapods-plugin/-/snyk-cocoapods-plugin-2.0.1.tgz", + "integrity": "sha512-XVkvaMvMzQ3miJi/YZmsRJSAUfDloYhfg6pXPgzAeAugB4p+cNi01Z68pT62ypB8U/Ugh1Xx2pb9aoOFqBbSjA==", + "requires": { + "@snyk/cli-interface": "1.5.0", + "@snyk/cocoapods-lockfile-parser": "3.0.0", + "@snyk/dep-graph": "^1.13.1", + "source-map-support": "^0.5.7", + "tslib": "^1.9.3" + }, + "dependencies": { + "@snyk/cli-interface": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-1.5.0.tgz", + "integrity": "sha512-+Qo+IO3YOXWgazlo+CKxOuWFLQQdaNCJ9cSfhFQd687/FuesaIxWdInaAdfpsLScq0c6M1ieZslXgiZELSzxbg==", + "requires": { + "tslib": "^1.9.3" + } + } + } + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/agent-base": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/agent-base/-/agent-base-4.2.0.tgz", + "integrity": "sha512-8mrhPstU+ZX0Ugya8tl5DsDZ1I5ZwQzbL/8PA0z8Gj0k9nql7nkaMzmPVLj+l/nixWaliXi+EBiLA8bptw3z7Q==", + "requires": { + "@types/events": "*", + "@types/node": "*" + } + }, + "@types/bn.js": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.5.tgz", + "integrity": "sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng==", + "requires": { + "@types/node": "*" + } + }, + "@types/bunyan": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.6.tgz", + "integrity": "sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" + }, + "@types/js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA==" + }, + "@types/node": { + "version": "10.12.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.26.tgz", + "integrity": "sha512-nMRqS+mL1TOnIJrL6LKJcNZPB8V3eTfRo9FQA2b5gDvrHurC8XbSA86KNe0dShlEL7ReWJv/OU9NL7Z0dnqWTg==" + }, + "@types/restify": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/restify/-/restify-4.3.6.tgz", + "integrity": "sha512-4l4f0EXnleXQttlhRCXtTuJ8UelsKiAKIK2AAEd2epBHu41aEbM0U2z6E5tUrNwlbxz7qaNBISduGMeg+G3PaA==", + "requires": { + "@types/bunyan": "*", + "@types/node": "*" + } + }, + "@types/semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" + }, + "@types/xml2js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.3.tgz", + "integrity": "sha512-Pv2HGRE4gWLs31In7nsyXEH4uVVsd0HNV9i2dyASvtDIlOtSTr1eczPLDpdEuyv5LWH5LT20GIXwPjkshKWI1g==", + "requires": { + "@types/events": "*", + "@types/node": "*" + } + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=" + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "optional": true + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "requires": { + "string-width": "^3.0.0" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "ast-types": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", + "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934", + "from": "git+https://github.com/frozeman/bignumber.js-nolookahead.git" + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "boxen": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz", + "integrity": "sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^2.4.2", + "cli-boxes": "^2.2.0", + "string-width": "^3.0.0", + "term-size": "^1.2.0", + "type-fest": "^0.3.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sha3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.4.tgz", + "integrity": "sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY=", + "requires": { + "js-sha3": "^0.6.1", + "safe-buffer": "^5.1.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "chownr": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", + "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==" + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "cint": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/cint/-/cint-8.2.1.tgz", + "integrity": "sha1-cDhrG0jidz0NYxZqVa/5TvRFahI=" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-spinner": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/cli-spinner/-/cli-spinner-0.2.10.tgz", + "integrity": "sha512-U0sSQ+JJvSLi1pAYuJykwiA8Dsr15uHEy85iCJ6A+0DjVxivr3d+N2Wjvodeg89uP5K6TswFkKBfAD7B3YSn/Q==" + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "requires": { + "colors": "1.0.3" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + } + } + }, + "clone-deep": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz", + "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=", + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.1", + "kind-of": "^3.2.2", + "shallow-clone": "^0.1.2" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "configstore": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz", + "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==", + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "core-js": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz", + "integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-js": { + "version": "3.1.9-1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", + "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-uri-to-buffer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", + "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==" + }, + "death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decimal.js": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", + "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "defer-to-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "degenerator": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", + "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", + "requires": { + "ast-types": "0.x.x", + "escodegen": "1.x.x", + "esprima": "3.x.x" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dockerfile-ast": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/dockerfile-ast/-/dockerfile-ast-0.0.16.tgz", + "integrity": "sha512-+HZToHjjiLPl46TqBrok5dMrg5oCkZFPSROMQjRmvin0zG4FxK0DJXTpV/CUPYY2zpmEvVza55XLwSHFx/xZMw==", + "requires": { + "vscode-languageserver-types": "^3.5.0" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotnet-deps-parser": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/dotnet-deps-parser/-/dotnet-deps-parser-4.7.0.tgz", + "integrity": "sha512-dmXi1UzukjjgjMuov65Ibv4fGJyVXPj2mVyEkLZob4Q2aQ5WJYAn6R2MWckq0N9X1CE6rrdvROAyXl/v/aGLpw==", + "requires": { + "@types/xml2js": "0.4.3", + "lodash": "^4.17.11", + "source-map-support": "^0.5.7", + "tslib": "^1.10.0", + "xml2js": "0.4.19" + }, + "dependencies": { + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + } + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "email-validator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/email-validator/-/email-validator-2.0.4.tgz", + "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz", + "integrity": "sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.51", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz", + "integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } + } + }, + "eth-lib": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", + "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "keccakjs": "^0.2.1", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethereumjs-testrpc-sc": { + "version": "6.5.1-sc.1", + "resolved": "https://registry.npmjs.org/ethereumjs-testrpc-sc/-/ethereumjs-testrpc-sc-6.5.1-sc.1.tgz", + "integrity": "sha512-DPBwpMFFH5DTXicYjLLdTq6VLnD8p4oo5QfC9EmRKLzDsWRNXDhA9cgcdUZhhXuPM+mVAem7Q+o3Y3CQ5f+FKA==", + "requires": { + "ethereumjs-util": "6.1.0", + "source-map-support": "0.5.12", + "yargs": "13.2.4" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "bindings": { + "version": "1.5.0", + "bundled": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip66": { + "version": "1.1.5", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bn.js": { + "version": "4.11.8", + "bundled": true + }, + "brorand": { + "version": "1.1.0", + "bundled": true + }, + "browserify-aes": { + "version": "1.2.0", + "bundled": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "bundled": true + }, + "buffer-xor": { + "version": "1.0.3", + "bundled": true + }, + "cipher-base": { + "version": "1.0.4", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "5.0.0", + "bundled": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.3", + "bundled": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "create-hash": { + "version": "1.2.0", + "bundled": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "bundled": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "bundled": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "drbg.js": { + "version": "1.0.1", + "bundled": true, + "requires": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + } + }, + "elliptic": { + "version": "6.5.1", + "bundled": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "bundled": true + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "ethereumjs-util": { + "version": "6.1.0", + "bundled": true, + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + }, + "ethjs-util": { + "version": "0.1.6", + "bundled": true, + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "bundled": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "bundled": true + }, + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "^3.0.0" + } + }, + "hash-base": { + "version": "3.0.4", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "invert-kv": { + "version": "2.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "is-hex-prefixed": { + "version": "1.0.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "keccak": { + "version": "1.4.0", + "bundled": true, + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "lcid": { + "version": "2.0.0", + "bundled": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "bundled": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "bundled": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "bundled": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "bundled": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "bundled": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "bundled": true + }, + "nan": { + "version": "2.14.0", + "bundled": true + }, + "nice-try": { + "version": "1.0.5", + "bundled": true + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "bundled": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "bundled": true + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-is-promise": { + "version": "2.1.0", + "bundled": true + }, + "p-limit": { + "version": "2.2.1", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "ripemd160": { + "version": "2.0.2", + "bundled": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.3", + "bundled": true, + "requires": { + "bn.js": "^4.11.1", + "safe-buffer": "^5.1.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "bundled": true + }, + "secp256k1": { + "version": "3.7.1", + "bundled": true, + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.4.1", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + } + }, + "semver": { + "version": "5.7.1", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha.js": { + "version": "2.4.11", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "source-map": { + "version": "0.6.1", + "bundled": true + }, + "source-map-support": { + "version": "0.5.12", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "bundled": true, + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yargs": { + "version": "13.2.4", + "bundled": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "get-caller-file": { + "version": "2.0.5", + "bundled": true + }, + "require-main-filename": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "bundled": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "bundled": true + } + } + } + } + }, + "ethjs-abi": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.1.8.tgz", + "integrity": "sha1-zSiFg+1ijN+tr4re+juh28vKbBg=", + "requires": { + "bn.js": "4.11.6", + "js-sha3": "0.5.5", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=" + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "dependencies": { + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + } + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "dependencies": { + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "fs-minipass": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.6.tgz", + "integrity": "sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "ganache-cli": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ganache-cli/-/ganache-cli-6.7.0.tgz", + "integrity": "sha512-9CZsClo9hl5MxGL7hkk14mie89Q94P0idh92jcV7LmppTYTCG7SHatuwcfqN7emFHArMt3fneN4QbH2do2N6Ow==", + "requires": { + "ethereumjs-util": "6.1.0", + "source-map-support": "0.5.12", + "yargs": "13.2.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "bindings": { + "version": "1.5.0", + "bundled": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip66": { + "version": "1.1.5", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bn.js": { + "version": "4.11.8", + "bundled": true + }, + "brorand": { + "version": "1.1.0", + "bundled": true + }, + "browserify-aes": { + "version": "1.2.0", + "bundled": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "bundled": true + }, + "buffer-xor": { + "version": "1.0.3", + "bundled": true + }, + "camelcase": { + "version": "5.3.1", + "bundled": true + }, + "cipher-base": { + "version": "1.0.4", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "5.0.0", + "bundled": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "bundled": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "create-hash": { + "version": "1.2.0", + "bundled": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "bundled": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "bundled": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "drbg.js": { + "version": "1.0.1", + "bundled": true, + "requires": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + } + }, + "elliptic": { + "version": "6.5.0", + "bundled": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "bundled": true + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "ethereumjs-util": { + "version": "6.1.0", + "bundled": true, + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + }, + "ethjs-util": { + "version": "0.1.6", + "bundled": true, + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "bundled": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "bundled": true + }, + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "bundled": true + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "^3.0.0" + } + }, + "hash-base": { + "version": "3.0.4", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "invert-kv": { + "version": "2.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "is-hex-prefixed": { + "version": "1.0.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "keccak": { + "version": "1.4.0", + "bundled": true, + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "lcid": { + "version": "2.0.0", + "bundled": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "bundled": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "bundled": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "bundled": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "bundled": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "bundled": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "bundled": true + }, + "nan": { + "version": "2.14.0", + "bundled": true + }, + "nice-try": { + "version": "1.0.5", + "bundled": true + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "bundled": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "bundled": true + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-is-promise": { + "version": "2.1.0", + "bundled": true + }, + "p-limit": { + "version": "2.2.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "2.0.0", + "bundled": true + }, + "ripemd160": { + "version": "2.0.2", + "bundled": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.3", + "bundled": true, + "requires": { + "bn.js": "^4.11.1", + "safe-buffer": "^5.1.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "bundled": true + }, + "secp256k1": { + "version": "3.7.1", + "bundled": true, + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.4.1", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + } + }, + "semver": { + "version": "5.7.0", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha.js": { + "version": "2.4.11", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "source-map": { + "version": "0.6.1", + "bundled": true + }, + "source-map-support": { + "version": "0.5.12", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "bundled": true, + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wrap-ansi": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yargs": { + "version": "13.2.4", + "bundled": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.1", + "bundled": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==" + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==" + }, + "get-stream": { + "version": "3.0.0", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "get-uri": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.4.tgz", + "integrity": "sha512-v7LT/s8kVjs+Tx0ykk1I+H/rbpzkHvuIq87LmeXptcf5sNWm9uQiwjNAt94SJPA1zOlCntmnOlJvVWKmzsxG8Q==", + "requires": { + "data-uri-to-buffer": "1", + "debug": "2", + "extend": "~3.0.2", + "file-uri-to-path": "1", + "ftp": "~0.3.10", + "readable-stream": "2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-up": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-4.0.1.tgz", + "integrity": "sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw==", + "requires": { + "is-ssh": "^1.3.0", + "parse-url": "^5.0.0" + } + }, + "git-url-parse": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.2.tgz", + "integrity": "sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ==", + "requires": { + "git-up": "^4.0.0" + } + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "requires": { + "lodash": "^4.17.15" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" + }, + "handlebars": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.3.1.tgz", + "integrity": "sha512-c0HoNHzDiHpBt4Kqe99N8tdLPKAnGCQ73gYMPWtAYM4PwGnf7xl8PBUHJqh9ijlzt2uQKaSRxbXRt+rZ7M2/kA==", + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==" + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", + "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + } + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore-walk": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.2.tgz", + "integrity": "sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, + "is-npm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-3.0.0.tgz", + "integrity": "sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==" + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + }, + "is-ssh": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.3.1.tgz", + "integrity": "sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg==", + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + } + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=" + }, + "js-sha3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.6.1.tgz", + "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-helpfulerror": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", + "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=", + "requires": { + "jju": "^1.1.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.2.tgz", + "integrity": "sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, + "keccakjs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", + "integrity": "sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg==", + "requires": { + "browserify-sha3": "^0.0.4", + "sha3": "^1.2.2" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", + "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "make-fetch-happen": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz", + "integrity": "sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA==", + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^12.0.0", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=" + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "mock-fs": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.7.0.tgz", + "integrity": "sha512-WlQNtUlzMRpvLHf8dqeUmNqfdPjGY29KrJF50Ldb4AcL+vQeR8QH3wQcFMgrhTwb1gHjZn9xggho+84tBskLgA==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "nan": { + "version": "2.10.0", + "resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "nconf": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.10.0.tgz", + "integrity": "sha512-fKiXMQrpP7CYWJQzKkPPx9hPgmq+YLDyxcG9N8RpiE9FoCkCbzD0NyW0YhE3xn3Aupe7nnDeIx4PFzYehpHT9Q==", + "requires": { + "async": "^1.4.0", + "ini": "^1.3.0", + "secure-keys": "^1.0.0", + "yargs": "^3.19.0" + } + }, + "needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + }, + "nested-error-stacks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", + "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==" + }, + "netmask": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", + "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-alias": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-alias/-/node-alias-1.0.4.tgz", + "integrity": "sha1-HxuRa1a56iQcATX5fO1pQPVW8pI=", + "requires": { + "chalk": "^1.1.1", + "lodash": "^4.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "normalize-url": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.3.0.tgz", + "integrity": "sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==" + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==" + }, + "npm-check-updates": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-3.1.23.tgz", + "integrity": "sha512-Z2dkMdNgue6OPkQDPcAK62Qrwv+G1PaEmKrDrrSAiSP7pRD3u30xOVy1nLukS1XrJ2/zF8XTVxFe9/ubcvlcPQ==", + "requires": { + "chalk": "^2.4.2", + "cint": "^8.2.1", + "cli-table": "^0.3.1", + "commander": "^3.0.1", + "fast-diff": "^1.2.0", + "find-up": "4.1.0", + "get-stdin": "^7.0.0", + "json-parse-helpfulerror": "^1.0.3", + "libnpmconfig": "^1.2.1", + "lodash": "^4.17.15", + "node-alias": "^1.0.4", + "pacote": "^9.5.8", + "progress": "^2.0.3", + "prompts": "^2.2.1", + "rc-config-loader": "^2.0.4", + "requireg": "^0.2.2", + "semver": "^6.3.0", + "semver-utils": "^1.1.4", + "spawn-please": "^0.3.0", + "update-notifier": "^3.0.1" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "commander": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.1.tgz", + "integrity": "sha512-UNgvDd+csKdc9GD4zjtkHKQbT8Aspt2jCBqNSPp53vAS0L1tS9sXB2TCEOPHJ7kt9bN/niWkYj8T3RQSoMXdSQ==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "requires": { + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", + "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-fetch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.1.tgz", + "integrity": "sha512-1ZQ+yjnxc698R5h9Yje9CASapzAZr7aYDkJDdERg9xg2hOEY0vRJwskOaJAXq8N/eLavzvW4g564YAfq6zMn/A==", + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original-require": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/original-require/-/original-require-1.0.1.tgz", + "integrity": "sha1-DxMEcVhM0zURxew4yNWSE/msXiA=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "pac-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-3.0.1.tgz", + "integrity": "sha512-44DUg21G/liUZ48dJpUSjZnFfZro/0K5JTyFYLBcmh9+T6Ooi4/i4efwUiEy0+4oQusCBqWdhv16XohIj1GqnQ==", + "requires": { + "agent-base": "^4.2.0", + "debug": "^4.1.1", + "get-uri": "^2.0.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^3.0.0", + "pac-resolver": "^3.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "https-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "pac-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", + "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", + "requires": { + "co": "^4.6.0", + "degenerator": "^1.0.4", + "ip": "^1.1.5", + "netmask": "^1.0.6", + "thunkify": "^2.1.2" + } + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + } + } + }, + "pacote": { + "version": "9.5.8", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.8.tgz", + "integrity": "sha512-0Tl8Oi/K0Lo4MZmH0/6IsT3gpGf9eEAznLXEQPKgPq7FscnbUOyopnVpwXlnQdIbCUaojWy1Wd7VMyqfVsRrIw==", + "requires": { + "bluebird": "^3.5.3", + "cacache": "^12.0.2", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^3.0.0", + "npm-registry-fetch": "^4.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.1", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" + } + }, + "parse-headers": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz", + "integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=", + "requires": { + "for-each": "^0.3.2", + "trim": "0.0.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-4.0.1.tgz", + "integrity": "sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA==", + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0" + } + }, + "parse-url": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz", + "integrity": "sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg==", + "requires": { + "is-ssh": "^1.3.0", + "normalize-url": "^3.3.0", + "parse-path": "^4.0.0", + "protocols": "^1.4.0" + }, + "dependencies": { + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" + } + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "prompts": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz", + "integrity": "sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.3" + } + }, + "protocols": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz", + "integrity": "sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg==" + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "requires": { + "genfun": "^5.0.0" + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "proxy-agent": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.1.1.tgz", + "integrity": "sha512-WudaR0eTsDx33O3EJE16PjBRZWcX8GqCEeERw1W3hZJgH/F2a46g7jty6UGty6NeJ4CKQy8ds2CJPMiyeqaTvw==", + "requires": { + "agent-base": "^4.2.0", + "debug": "4", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^3.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^3.0.1", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "https-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "rc-config-loader": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-2.0.4.tgz", + "integrity": "sha512-k06UzRbYDWgF4Mc/YrsZsmzSpDLuHoThJxep+vq5H09hiX8rbA5Ue/Ra0dwWm5MQvWYW4YBXgA186inNxuxidQ==", + "requires": { + "debug": "^4.1.1", + "js-yaml": "^3.12.0", + "json5": "^2.1.0", + "object-assign": "^4.1.0", + "object-keys": "^1.0.12", + "path-exists": "^3.0.0", + "require-from-string": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + }, + "registry-auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.0.0.tgz", + "integrity": "sha512-lpQkHxd9UL6tb3k/aHAVfnVtn+Bcs9ob5InuFLLEDqSqeq+AljB8GZW9xY0x7F+xYwEcjKe07nyoxzEYz6yvkw==", + "requires": { + "rc": "^1.2.8", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, + "req-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-1.0.1.tgz", + "integrity": "sha1-DXOurpJm5penj3l2AZZ352rPD/8=", + "requires": { + "req-from": "^1.0.1" + } + }, + "req-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-1.0.1.tgz", + "integrity": "sha1-v4HaUUeUfTLRO5R9wSpYrUWHNQ4=", + "requires": { + "resolve-from": "^2.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "requireg": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", + "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", + "requires": { + "nested-error-stacks": "~2.0.1", + "rc": "~1.2.7", + "resolve": "~1.7.1" + }, + "dependencies": { + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "requires": { + "path-parse": "^1.0.5" + } + } + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "secure-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz", + "integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o=" + }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "requires": { + "commander": "~2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "requires": { + "semver": "^5.0.3" + } + }, + "semver-utils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/semver-utils/-/semver-utils-1.1.4.tgz", + "integrity": "sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "sha3": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.2.tgz", + "integrity": "sha1-pmxQmN5MJbyIM27ItIF9AFvKe6k=", + "requires": { + "nan": "2.10.0" + } + }, + "shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "requires": { + "is-buffer": "^1.0.2" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "sisteransi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.3.tgz", + "integrity": "sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg==" + }, + "smart-buffer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==" + }, + "snyk": { + "version": "1.259.0", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.259.0.tgz", + "integrity": "sha512-miPuJtJeJ7rX3620ZeoDum5z9tNbpbwogqlOlccYY6UiAUFWnmEhqFxpSeGMq3aguj+rYmyGwClhP2LooGgUaA==", + "requires": { + "@snyk/cli-interface": "2.3.0", + "@snyk/dep-graph": "1.13.1", + "@snyk/gemfile": "1.2.0", + "@snyk/snyk-cocoapods-plugin": "2.0.1", + "@types/agent-base": "^4.2.0", + "@types/restify": "^4.3.6", + "abbrev": "^1.1.1", + "ansi-escapes": "3.2.0", + "chalk": "^2.4.2", + "cli-spinner": "0.2.10", + "configstore": "^3.1.2", + "debug": "^3.1.0", + "diff": "^4.0.1", + "git-url-parse": "11.1.2", + "glob": "^7.1.3", + "inquirer": "^6.2.2", + "lodash": "^4.17.14", + "needle": "^2.2.4", + "opn": "^5.5.0", + "os-name": "^3.0.0", + "proxy-agent": "^3.1.1", + "proxy-from-env": "^1.0.0", + "semver": "^6.0.0", + "snyk-config": "^2.2.1", + "snyk-docker-plugin": "1.33.1", + "snyk-go-plugin": "1.11.1", + "snyk-gradle-plugin": "3.2.2", + "snyk-module": "1.9.1", + "snyk-mvn-plugin": "2.7.0", + "snyk-nodejs-lockfile-parser": "1.16.1", + "snyk-nuget-plugin": "1.14.0", + "snyk-php-plugin": "1.7.0", + "snyk-policy": "1.13.5", + "snyk-python-plugin": "^1.14.0", + "snyk-resolve": "1.0.1", + "snyk-resolve-deps": "4.4.0", + "snyk-sbt-plugin": "2.9.1", + "snyk-tree": "^1.0.0", + "snyk-try-require": "1.3.1", + "source-map-support": "^0.5.11", + "strip-ansi": "^5.2.0", + "tempfile": "^2.0.0", + "then-fs": "^2.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.2", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" + }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "requires": { + "package-json": "^4.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "requires": { + "rc": "^1.0.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + } + } + }, + "snyk-config": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/snyk-config/-/snyk-config-2.2.3.tgz", + "integrity": "sha512-9NjxHVMd1U1LFw66Lya4LXgrsFUiuRiL4opxfTFo0LmMNzUoU5Bk/p0zDdg3FE5Wg61r4fP2D8w+QTl6M8CGiw==", + "requires": { + "debug": "^3.1.0", + "lodash": "^4.17.15", + "nconf": "^0.10.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "snyk-docker-plugin": { + "version": "1.33.1", + "resolved": "https://registry.npmjs.org/snyk-docker-plugin/-/snyk-docker-plugin-1.33.1.tgz", + "integrity": "sha512-xfs3DN1tPMTh6J8x2341wGK4HRr+pI5+i/YRuRmsslnBnwk/DkKYcbt8zOIWk6kzMoW8vo+9LqqXBQO/24szKg==", + "requires": { + "debug": "^4.1.1", + "dockerfile-ast": "0.0.16", + "semver": "^6.1.0", + "tar-stream": "^2.1.0", + "tslib": "^1" + }, + "dependencies": { + "bl": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", + "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", + "requires": { + "readable-stream": "^3.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "tar-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", + "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==", + "requires": { + "bl": "^3.0.0", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + } + } + }, + "snyk-go-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/snyk-go-parser/-/snyk-go-parser-1.3.1.tgz", + "integrity": "sha512-jrFRfIk6yGHFeipGD66WV9ei/A/w/lIiGqI80w1ndMbg6D6M5pVNbK7ngDTmo4GdHrZDYqx/VBGBsUm2bol3Rg==", + "requires": { + "toml": "^3.0.0", + "tslib": "^1.9.3" + } + }, + "snyk-go-plugin": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/snyk-go-plugin/-/snyk-go-plugin-1.11.1.tgz", + "integrity": "sha512-IsNi7TmpHoRHzONOWJTT8+VYozQJnaJpKgnYNQjzNm2JlV8bDGbdGQ1a8LcEoChxnJ8v8aMZy7GTiQyGGABtEQ==", + "requires": { + "debug": "^4.1.1", + "graphlib": "^2.1.1", + "snyk-go-parser": "1.3.1", + "tmp": "0.0.33", + "tslib": "^1.10.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "snyk-gradle-plugin": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-3.2.2.tgz", + "integrity": "sha512-ijIWsypbtpdTuRcYTFsnEWbaBnhCc7q0iIg0A4OcOW/xLyInPwyfBMnip4ubNfHAS/PrvzgfwwwJhttcQD0ZaQ==", + "requires": { + "@snyk/cli-interface": "2.2.0", + "@types/debug": "^4.1.4", + "chalk": "^2.4.2", + "clone-deep": "^0.3.0", + "debug": "^4.1.1", + "tmp": "0.0.33", + "tslib": "^1.9.3" + }, + "dependencies": { + "@snyk/cli-interface": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.2.0.tgz", + "integrity": "sha512-sA7V2JhgqJB9z5uYotgQc5iNDv//y+Mdm39rANxmFjtZMSYJZHkP80arzPjw1mB5ni/sWec7ieYUUFeySZBfVg==", + "requires": { + "tslib": "^1.9.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "snyk-module": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-1.9.1.tgz", + "integrity": "sha512-A+CCyBSa4IKok5uEhqT+hV/35RO6APFNLqk9DRRHg7xW2/j//nPX8wTSZUPF8QeRNEk/sX+6df7M1y6PBHGSHA==", + "requires": { + "debug": "^3.1.0", + "hosted-git-info": "^2.7.1" + } + }, + "snyk-mvn-plugin": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-2.7.0.tgz", + "integrity": "sha512-DLBt+6ZvtoleXE7Si3wAa6gdPSWsXdIQEY6m2zW2InN9WiaRwIEKMCY822eFmRPZVNNmZNRUIeQsoHZwv/slqQ==", + "requires": { + "@snyk/cli-interface": "2.2.0", + "debug": "^4.1.1", + "lodash": "^4.17.15", + "needle": "^2.4.0", + "tmp": "^0.1.0", + "tslib": "1.9.3" + }, + "dependencies": { + "@snyk/cli-interface": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.2.0.tgz", + "integrity": "sha512-sA7V2JhgqJB9z5uYotgQc5iNDv//y+Mdm39rANxmFjtZMSYJZHkP80arzPjw1mB5ni/sWec7ieYUUFeySZBfVg==", + "requires": { + "tslib": "^1.9.3" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "requires": { + "rimraf": "^2.6.3" + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + } + } + }, + "snyk-nodejs-lockfile-parser": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.16.1.tgz", + "integrity": "sha512-MEQImB2XU35D66wYve6g1RcDuD9vyoxGvYtM+ngSd5ItujzjIpyF26W7niqHwBRGLamqjsKF5cOlbmHs+wsx/Q==", + "requires": { + "@yarnpkg/lockfile": "^1.0.2", + "graphlib": "^2.1.5", + "lodash": "^4.17.14", + "source-map-support": "^0.5.7", + "tslib": "^1.9.3", + "uuid": "^3.3.2" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "snyk-nuget-plugin": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/snyk-nuget-plugin/-/snyk-nuget-plugin-1.14.0.tgz", + "integrity": "sha512-RqXZQEA1s9LIA3euvLtx17UQQ1/q/sHkUf6p0sgTxG2D1xQI8vMtrbDxYgfpPwaHK22AjGaeK3J0TgPND5qZdw==", + "requires": { + "debug": "^3.1.0", + "dotnet-deps-parser": "4.7.0", + "jszip": "^3.1.5", + "lodash": "^4.17.14", + "snyk-paket-parser": "1.5.0", + "tslib": "^1.9.3", + "xml2js": "^0.4.17" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "snyk-paket-parser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/snyk-paket-parser/-/snyk-paket-parser-1.5.0.tgz", + "integrity": "sha512-1CYMPChJ9D9LBy3NLqHyv8TY7pR/LMISSr08LhfFw/FpfRZ+gTH8W6bbxCmybAYrOFNCqZkRprqOYDqZQFHipA==", + "requires": { + "tslib": "^1.9.3" + } + }, + "snyk-php-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/snyk-php-plugin/-/snyk-php-plugin-1.7.0.tgz", + "integrity": "sha512-mDe90xkqSEVrpx1ZC7ItqCOc6fZCySbE+pHVI+dAPUmf1C1LSWZrZVmAVeo/Dw9sJzJfzmcdAFQl+jZP8/uV0A==", + "requires": { + "@snyk/cli-interface": "2.2.0", + "@snyk/composer-lockfile-parser": "1.2.0", + "tslib": "1.9.3" + }, + "dependencies": { + "@snyk/cli-interface": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@snyk/cli-interface/-/cli-interface-2.2.0.tgz", + "integrity": "sha512-sA7V2JhgqJB9z5uYotgQc5iNDv//y+Mdm39rANxmFjtZMSYJZHkP80arzPjw1mB5ni/sWec7ieYUUFeySZBfVg==", + "requires": { + "tslib": "^1.9.3" + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + } + } + }, + "snyk-policy": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/snyk-policy/-/snyk-policy-1.13.5.tgz", + "integrity": "sha512-KI6GHt+Oj4fYKiCp7duhseUj5YhyL/zJOrrJg0u6r59Ux9w8gmkUYT92FHW27ihwuT6IPzdGNEuy06Yv2C9WaQ==", + "requires": { + "debug": "^3.1.0", + "email-validator": "^2.0.4", + "js-yaml": "^3.13.1", + "lodash.clonedeep": "^4.5.0", + "semver": "^6.0.0", + "snyk-module": "^1.9.1", + "snyk-resolve": "^1.0.1", + "snyk-try-require": "^1.3.1", + "then-fs": "^2.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "snyk-python-plugin": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/snyk-python-plugin/-/snyk-python-plugin-1.14.1.tgz", + "integrity": "sha512-76u10VrYJp0tz7eD7DC5/Q3fBMPlLieOqoUbN67u0OqF1nF7BLnFBnakZ9VbOqYeJyBoloL9+HIMJ5Nma9qLCQ==", + "requires": { + "@snyk/cli-interface": "^2.0.3", + "tmp": "0.0.33" + } + }, + "snyk-resolve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/snyk-resolve/-/snyk-resolve-1.0.1.tgz", + "integrity": "sha512-7+i+LLhtBo1Pkth01xv+RYJU8a67zmJ8WFFPvSxyCjdlKIcsps4hPQFebhz+0gC5rMemlaeIV6cqwqUf9PEDpw==", + "requires": { + "debug": "^3.1.0", + "then-fs": "^2.0.0" + } + }, + "snyk-resolve-deps": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/snyk-resolve-deps/-/snyk-resolve-deps-4.4.0.tgz", + "integrity": "sha512-aFPtN8WLqIk4E1ulMyzvV5reY1Iksz+3oPnUVib1jKdyTHymmOIYF7z8QZ4UUr52UsgmrD9EA/dq7jpytwFoOQ==", + "requires": { + "@types/node": "^6.14.4", + "@types/semver": "^5.5.0", + "ansicolors": "^0.3.2", + "debug": "^3.2.5", + "lodash.assign": "^4.2.0", + "lodash.assignin": "^4.2.0", + "lodash.clone": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lru-cache": "^4.0.0", + "semver": "^5.5.1", + "snyk-module": "^1.6.0", + "snyk-resolve": "^1.0.0", + "snyk-tree": "^1.0.0", + "snyk-try-require": "^1.1.1", + "then-fs": "^2.0.0" + }, + "dependencies": { + "@types/node": { + "version": "6.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.9.tgz", + "integrity": "sha512-leP/gxHunuazPdZaCvsCefPQxinqUDsCxCR5xaDUrY2MkYxQRFZZwU5e7GojyYsGB7QVtCi7iVEl/hoFXQYc+w==" + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "snyk-sbt-plugin": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/snyk-sbt-plugin/-/snyk-sbt-plugin-2.9.1.tgz", + "integrity": "sha512-+cRFH4uAaoW7NeVLaWmpU236uhe4JRBakNGe+M9UhAswEqDAyFmyzWVU57EAjlzJKLIdh9JPFUvzjntGNs1I1A==", + "requires": { + "debug": "^4.1.1", + "semver": "^6.1.2", + "tmp": "^0.1.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "requires": { + "rimraf": "^2.6.3" + } + } + } + }, + "snyk-tree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/snyk-tree/-/snyk-tree-1.0.0.tgz", + "integrity": "sha1-D7cxdtvzLngvGRAClBYESPkRHMg=", + "requires": { + "archy": "^1.0.0" + } + }, + "snyk-try-require": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/snyk-try-require/-/snyk-try-require-1.3.1.tgz", + "integrity": "sha1-bgJvkuZK9/zM6h7lPVJIQeQYohI=", + "requires": { + "debug": "^3.1.0", + "lodash.clonedeep": "^4.3.0", + "lru-cache": "^4.0.0", + "then-fs": "^2.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } + }, + "socks": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", + "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + }, + "socks-proxy-agent": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", + "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", + "requires": { + "agent-base": "~4.2.1", + "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "requires": { + "es6-promisify": "^5.0.0" + } + } + } + }, + "sol-explore": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/sol-explore/-/sol-explore-1.6.2.tgz", + "integrity": "sha1-Q66MQZ/TrAVqBfip0fsQIs1B7MI=" + }, + "solidity-coverage": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.6.7.tgz", + "integrity": "sha512-Ga0Er5b25sFhy1uaYrQ+jQisP9819nj3zP1pfyAfE8Skg+/OBQ+Ev3sOmYQy9qZMjDuFJ7PCjuFVXdqdBO3cZw==", + "requires": { + "death": "^1.1.0", + "ethereumjs-testrpc-sc": "6.5.1-sc.1", + "istanbul": "^0.4.5", + "keccakjs": "^0.2.1", + "req-cwd": "^1.0.1", + "shelljs": "^0.8.3", + "sol-explore": "^1.6.2", + "solidity-parser-antlr": "0.4.7", + "tree-kill": "^1.2.0", + "web3": "1.2.1", + "web3-eth-abi": "1.0.0-beta.55" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + } + } + }, + "ethers": { + "version": "4.0.37", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.37.tgz", + "integrity": "sha512-B7bDdyQ45A5lPr6k2HOkEKMtYOuqlfy+nNf8glnRvWidkDQnToKw1bv7UyrwlbsIgY2mE03UxTVtouXcT6Vvcw==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "solidity-parser-antlr": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/solidity-parser-antlr/-/solidity-parser-antlr-0.4.7.tgz", + "integrity": "sha512-iVjNhgqkXw+o+0E+xoLcji+3KuXLe8Rm8omUuVGhsV14+ZsTwQ70nhBRXg8O3t9xwdS0iST1q9NJ5IqHnqpWkA==", + "requires": { + "npm-check-updates": "^3.1.11" + } + }, + "utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", + "integrity": "sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "web3-eth-abi": { + "version": "1.0.0-beta.55", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.55.tgz", + "integrity": "sha512-3h1xnm/vYmKUXTOYAOP0OsB5uijQV76pNNRGKOB6Dq6GR1pbcbD3WrB/4I643YA8l91t5FRzFzUiA3S77R2iqw==", + "requires": { + "@babel/runtime": "^7.3.1", + "ethers": "^4.0.27", + "lodash": "^4.17.11", + "web3-utils": "1.0.0-beta.55" + } + }, + "web3-utils": { + "version": "1.0.0-beta.55", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.55.tgz", + "integrity": "sha512-ASWqUi8gtWK02Tp8ZtcoAbHenMpQXNvHrakgzvqTNNZn26wgpv+Q4mdPi0KOR6ZgHFL8R/9b5BBoUTglS1WPpg==", + "requires": { + "@babel/runtime": "^7.3.1", + "@types/bn.js": "^4.11.4", + "@types/node": "^10.12.18", + "bn.js": "4.11.8", + "eth-lib": "0.2.8", + "ethjs-unit": "^0.1.6", + "lodash": "^4.17.11", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "2.1.1" + } + } + } + }, + "solidity-parser-antlr": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/solidity-parser-antlr/-/solidity-parser-antlr-0.4.11.tgz", + "integrity": "sha512-4jtxasNGmyC0midtjH/lTFPZYvTTUMy6agYcF+HoMnzW8+cqo3piFrINb4ZCzpPW+7tTVFCGa5ubP34zOzeuMg==" + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "spawn-please": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-0.3.0.tgz", + "integrity": "sha1-2zOOxM/2Orxp8dDgjO6euL69nRE=" + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", + "integrity": "sha512-Zhev35/y7hRMcID/upReIvRse+I9SVhyVre/KTJSJQWMz3C3+G+HpO7m1wK/yckEtujKZ7dS4hkVxAnmHaIGVQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + }, + "tar": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", + "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.5", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, + "temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=" + }, + "tempfile": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", + "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", + "requires": { + "temp-dir": "^1.0.0", + "uuid": "^3.0.1" + } + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "requires": { + "execa": "^0.7.0" + } + }, + "then-fs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/then-fs/-/then-fs-2.0.0.tgz", + "integrity": "sha1-cveS3Z0xcFqRrhnr/Piz+WjIHaI=", + "requires": { + "promise": ">=3.2 <8" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunkify": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", + "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=" + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==" + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + }, + "truffle": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/truffle/-/truffle-4.1.16.tgz", + "integrity": "sha512-Ew0YSyy2802wqqe1Ko4kxo1FMffM+l9uEUXJ+GScwAAXFXwKWRC++eEa9pIIQdHcEj0rJo0jXhp65vS/LOcNwA==", + "requires": { + "mocha": "^4.1.0", + "original-require": "1.0.1", + "solc": "0.4.26" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "solc": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz", + "integrity": "sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA==", + "requires": { + "fs-extra": "^0.30.0", + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } + }, + "truffle-blockchain-utils": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/truffle-blockchain-utils/-/truffle-blockchain-utils-0.0.5.tgz", + "integrity": "sha1-pOXAZNrdafeCoTfz0nbSEJXaekc=" + }, + "truffle-contract": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/truffle-contract/-/truffle-contract-3.0.8.tgz", + "integrity": "sha512-uhXb/G4dORU4RjFlwZZbFT0n5BS8akify+MaRsnWWs4SA/bo6x4/bQs1xtdO3b5Cl9nXiOX88wdQzRj3xtPVUg==", + "requires": { + "ethjs-abi": "0.1.8", + "truffle-blockchain-utils": "^0.0.5", + "truffle-contract-schema": "^2.0.3", + "truffle-error": "^0.0.3", + "web3": "0.20.6" + }, + "dependencies": { + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" + }, + "web3": { + "version": "0.20.6", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.20.6.tgz", + "integrity": "sha1-PpcwauAk+yThCj11yIQwJWIhUSA=", + "requires": { + "bignumber.js": "git+https://github.com/frozeman/bignumber.js-nolookahead.git", + "crypto-js": "^3.1.4", + "utf8": "^2.1.1", + "xhr2": "*", + "xmlhttprequest": "*" + } + } + } + }, + "truffle-contract-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/truffle-contract-schema/-/truffle-contract-schema-2.0.3.tgz", + "integrity": "sha512-eI5cFifbB3zpcO4RsXSnjN9JMSlJ4M50GQPdrfbrIXRTXHsyQ433SkgFjIATUwfq++TXWkCRfKMjN8eA7YQ3+Q==", + "requires": { + "ajv": "^5.1.1", + "crypto-js": "^3.1.9-1", + "debug": "^3.1.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + } + } + }, + "truffle-error": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/truffle-error/-/truffle-error-0.0.3.tgz", + "integrity": "sha1-S/VSQuFN7uHHGUkycJGC3v8sl8o=" + }, + "truffle-flattener": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/truffle-flattener/-/truffle-flattener-1.4.2.tgz", + "integrity": "sha512-7qUIzaW8a4vI4nui14wsytht2oaqvqnZ1Iet2wRq2T0bCJ0wb6HByMKQhZKpU46R+n5BMTY4K5n+0ITyeNlmuQ==", + "requires": { + "@resolver-engine/imports-fs": "^0.2.2", + "find-up": "^2.1.0", + "mkdirp": "^0.5.1", + "solidity-parser-antlr": "^0.4.11", + "tsort": "0.0.1" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.3.tgz", + "integrity": "sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==" + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbzip2-stream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.1.tgz", + "integrity": "sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==", + "requires": { + "buffer": "^3.0.1", + "through": "^2.3.6" + }, + "dependencies": { + "base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + }, + "buffer": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-3.6.0.tgz", + "integrity": "sha1-pyyTb3e5a/UvX357RnGAYoVR3vs=", + "requires": { + "base64-js": "0.0.8", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" + }, + "update-notifier": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-3.0.1.tgz", + "integrity": "sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==", + "requires": { + "boxen": "^3.0.0", + "chalk": "^2.0.1", + "configstore": "^4.0.0", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.1.0", + "is-npm": "^3.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vscode-languageserver-types": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", + "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + } + } + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "oboe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", + "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", + "requires": { + "http-https": "^1.0.0" + } + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" + }, + "swarm-js": { + "version": "0.1.39", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.39.tgz", + "integrity": "sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "decompress": "^4.0.0", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-promievent": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz", + "integrity": "sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw==", + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "3.1.2" + } + }, + "web3-core-requestmanager": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz", + "integrity": "sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-providers-http": "1.2.1", + "web3-providers-ipc": "1.2.1", + "web3-providers-ws": "1.2.1" + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "elliptic": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", + "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-providers-http": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.1.tgz", + "integrity": "sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ==", + "requires": { + "web3-core-helpers": "1.2.1", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz", + "integrity": "sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA==", + "requires": { + "oboe": "2.1.4", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-providers-ws": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz", + "integrity": "sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "websocket": { + "version": "github:web3-js/WebSocket-Node#905deb4812572b344f5801f8c9ce8bb02799d82e", + "from": "github:web3-js/WebSocket-Node#polyfill/globalThis", + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + } + } + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "requires": { + "string-width": "^2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "windows-release": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", + "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "requires": { + "execa": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", + "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", + "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", + "requires": { + "xhr-request": "^1.0.1" + } + }, + "xhr2": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.0.tgz", + "integrity": "sha512-BDtiD0i2iKPK/S8OAZfpk6tyzEDnKKSjxWHcMBVmh+LuqJ8A32qXTyOx+TVOg2dKvq6zGBq2sgKPkEeRs1qTRA==" + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xml2js": { + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", + "requires": { + "sax": ">=0.6.0", + "util.promisify": "~1.0.0", + "xmlbuilder": "~11.0.0" + }, + "dependencies": { + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + } + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..e66b108 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "bancor-contracts", + "version": "0.5.15", + "repository": { + "type": "git", + "url": "https://github.com/bancorprotocol/contracts" + }, + "engines": { + "npm": "^3.0.0" + }, + "scripts": { + "install": "node scripts/fix-modules.js", + "build": "node scripts/rebuild-all.js", + "test": "node scripts/run-tests.js", + "deploy": "node scripts/deploy-one.js", + "verify": "node scripts/verify-all.js", + "flatten": "node scripts/flatten.js", + "snyk-protect": "snyk protect", + "prepare": "npm run snyk-protect" + }, + "dependencies": { + "decimal.js": "10.2.0", + "ganache-cli": "6.7.0", + "solidity-coverage": "0.6.7", + "truffle": "4.1.16", + "truffle-contract": "3.0.8", + "truffle-flattener": "1.4.2", + "web3": "1.2.1", + "snyk": "^1.259.0" + }, + "snyk": true +} diff --git a/scripts/compile.sh b/scripts/compile.sh new file mode 100755 index 0000000..90137fa --- /dev/null +++ b/scripts/compile.sh @@ -0,0 +1,21 @@ +#!/bin/bash +solc="docker run -v $HOME/git/contracts/solidity:/solidity ethereum/solc:0.4.26" +CONTRACTS_PATH=../solidity/contracts +OUTPUT_PATH=../solidity/build +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/BancorNetwork.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/BancorNetworkPathFinder.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/bancorx/BancorX.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/bancorx/XTransferRerouter.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/converter/BancorConverter.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/converter/BancorConverterFactory.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/converter/BancorConverterRegistry.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/converter/BancorConverterRegistryData.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/converter/BancorConverterUpgrader.sol +$solc --optimize --optimize-runs 20000 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/converter/BancorFormula.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/legacy/BancorPriceFloor.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/crowdsale/CrowdsaleController.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/token/EtherToken.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/token/SmartToken.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/utility/ContractRegistry.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/utility/ContractFeatures.sol +$solc --optimize --optimize-runs 200 --abi --bin --allow-paths $CONTRACTS_PATH, -o $OUTPUT_PATH --overwrite $CONTRACTS_PATH/utility/Whitelist.sol diff --git a/scripts/deploy-one.js b/scripts/deploy-one.js new file mode 100755 index 0000000..b98451d --- /dev/null +++ b/scripts/deploy-one.js @@ -0,0 +1,92 @@ +const fs = require("fs"); +const Web3 = require("web3"); + +const NODE_ADDRESS = process.argv[2]; +const PRIVATE_KEY = process.argv[3]; +const CONTRACT_NAME = process.argv[4]; +const CONTRACT_ARGS = process.argv.slice(5); + +async function scan(message) { + process.stdout.write(message); + return await new Promise(function(resolve, reject) { + process.stdin.resume(); + process.stdin.once("data", function(data) { + process.stdin.pause(); + resolve(data.toString().trim()); + }); + }); +} + +async function getGasPrice(web3) { + while (true) { + const nodeGasPrice = await web3.eth.getGasPrice(); + const userGasPrice = await scan(`Enter gas-price or leave empty to use ${nodeGasPrice}: `); + if (/^\d+$/.test(userGasPrice)) + return userGasPrice; + if (userGasPrice == "") + return nodeGasPrice; + console.log("Illegal gas-price"); + } +} + +async function getTransactionReceipt(web3) { + while (true) { + const hash = await scan("Enter transaction-hash or leave empty to retry: "); + if (/^0x([0-9A-Fa-f]{64})$/.test(hash)) { + const receipt = await web3.eth.getTransactionReceipt(hash); + if (receipt) + return receipt; + console.log("Invalid transaction-hash"); + } + else if (hash) { + console.log("Illegal transaction-hash"); + } + else { + return null; + } + } +} + +async function send(web3, account, transaction) { + while (true) { + try { + const options = { + data : transaction.encodeABI(), + gas : await transaction.estimateGas({from: account.address}), + gasPrice: await getGasPrice(web3), + }; + const signed = await web3.eth.accounts.signTransaction(options, account.privateKey); + const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction); + return receipt; + } + catch (error) { + console.log(error.message); + const receipt = await getTransactionReceipt(web3); + if (receipt) + return receipt; + } + } +} + +async function run() { + const web3 = new Web3(NODE_ADDRESS); + const account = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY); + const path = __dirname + "/../solidity/build/" + CONTRACT_NAME; + const abi = fs.readFileSync(path + ".abi", {encoding: "utf8"}); + const bin = fs.readFileSync(path + ".bin", {encoding: "utf8"}); + const contract = new web3.eth.Contract(JSON.parse(abi)); + const options = {data: "0x" + bin, arguments: CONTRACT_ARGS}; + const transaction = contract.deploy(options); + const receipt = await send(web3, account, transaction); + console.log(JSON.stringify({ + [CONTRACT_NAME]: { + name: CONTRACT_NAME, + addr: receipt.contractAddress, + args: transaction.encodeABI().slice(options.data.length) + } + })); + if (web3.currentProvider.constructor.name == "WebsocketProvider") + web3.currentProvider.connection.close(); +} + +run(); \ No newline at end of file diff --git a/scripts/fix-modules.js b/scripts/fix-modules.js new file mode 100755 index 0000000..a791a0a --- /dev/null +++ b/scripts/fix-modules.js @@ -0,0 +1,39 @@ +const fs = require("fs"); + +try { + fs.closeSync(fs.openSync("./node_modules/run-once", "wx")); +} +catch (error) { + console.error("This script should not run more than once"); + process.exit(); +} + +function fix(fileName, tokens) { + console.log("Fixing " + fileName); + try { + let data = fs.readFileSync(fileName, {encoding: "utf8"}); + for (const token of tokens) + data = data.split(token.prev).join(token.next); + fs.writeFileSync(fileName, data, {encoding: "utf8"}); + } + catch (error) { + console.log(error.message); + } +} + +fix("./node_modules/truffle/build/cli.bundled.js", [ + {prev: "request = new XHR2", next: "request = new XMLHttpRequest"}, + {prev: "error = errors.InvalidResponse", next: "error = payload.method === 'evm_revert' || payload.method === 'evm_snapshot' ? null : errors.InvalidResponse"}, + {prev: "display_path = \".\" + path.sep + path.relative(options.working_directory, import_path);", next: "if (options.fix_paths) {display_path = \".\" + path.sep + path.relative(options.working_directory, import_path); result[display_path] = result[import_path]; delete result[import_path];}"}] +); + +fix("./node_modules/solidity-coverage/lib/app.js", [ + {prev: "events.push", next: "coverage.processEvent"}] +); + +fix("./node_modules/solidity-coverage/lib/coverageMap.js", [ + {prev: " generate(events, pathPrefix) {", next: " processEvent(line) {"}, + {prev: " for (let idx = 0; idx < events.length; idx++) {", next: ""}, + {prev: " const event = JSON.parse(events[idx]);", next: " const event = JSON.parse(line);"}, + {prev: " // Finally, interpret the assert pre/post events", next: " generate(events, pathPrefix) {"}] +); diff --git a/scripts/flatten.js b/scripts/flatten.js new file mode 100755 index 0000000..8a971af --- /dev/null +++ b/scripts/flatten.js @@ -0,0 +1,33 @@ +const WORK_DIR = "./solidity"; +const NODE_DIR = "../node_modules"; +const CONTRACT_NAME = process.argv[2]; + +const fs = require("fs"); +const path = require("path"); +const spawnSync = require("child_process").spawnSync; + +function run() { + for (const pathName of getPathNames("contracts")) { + const contractName = path.basename(pathName, ".sol"); + if (CONTRACT_NAME == contractName) + console.log(getSourceCode(pathName)) + } +} + +function getPathNames(dirName) { + let pathNames = []; + for (const fileName of fs.readdirSync(WORK_DIR + "/" + dirName)) { + if (fs.statSync(WORK_DIR + "/" + dirName + "/" + fileName).isDirectory()) + pathNames = pathNames.concat(getPathNames(dirName + "/" + fileName)); + else if (fileName.endsWith(".sol")) + pathNames.push(dirName + "/" + fileName); + } + return pathNames; +} + +function getSourceCode(pathName) { + const result = spawnSync("node", [NODE_DIR + "/truffle-flattener/index.js", pathName], {cwd: WORK_DIR}); + return result.output.toString().slice(1, -1); +} + +run(); diff --git a/scripts/rebuild-all.js b/scripts/rebuild-all.js new file mode 100755 index 0000000..af15323 --- /dev/null +++ b/scripts/rebuild-all.js @@ -0,0 +1,22 @@ +const WORK_DIR = "./solidity"; +const NODE_DIR = "../node_modules"; +const INPUT_DIR = "./solidity/build/contracts/"; +const OUTPUT_DIR = "./solidity/build/"; + +const fs = require("fs"); +const spawn = require("child_process").spawn; + +function extractBinaries() { + for (const fileName of fs.readdirSync(INPUT_DIR)) { + const data = JSON.parse(fs.readFileSync(INPUT_DIR + fileName, {encoding: "utf8"})); + fs.writeFileSync(OUTPUT_DIR + fileName.replace(".json", ".abi"), JSON.stringify(data.abi) , {encoding: "utf8"}); + fs.writeFileSync(OUTPUT_DIR + fileName.replace(".json", ".bin"), data.bytecode.substring(2), {encoding: "utf8"}); + } +} + +const cp = spawn("node", [NODE_DIR + "/truffle/build/cli.bundled.js", "compile", "--all", "--fix_paths"], {cwd: WORK_DIR}); + +cp.stdout.on("data", function(data) {process.stdout.write(data.toString());}); +cp.stderr.on("data", function(data) {process.stderr.write(data.toString());}); +cp.on("error", function(error) {process.stderr.write(error.toString());}); +cp.on("exit", function(code, signal) {extractBinaries();}); diff --git a/scripts/run-tests.js b/scripts/run-tests.js new file mode 100755 index 0000000..15f445f --- /dev/null +++ b/scripts/run-tests.js @@ -0,0 +1,67 @@ +const TRUFFLE_TEST = "1"; +const SOL_COVERAGE = "2"; + +const WORK_DIR = "./solidity"; +const NODE_DIR = "../node_modules"; + +const spawn = require("child_process").spawn; + +function getArgs(id, args) { + switch (id) { + case TRUFFLE_TEST: return args.TRUFFLE_TEST; + case SOL_COVERAGE: return args.SOL_COVERAGE; + } + throw `invalid input = '${id}'`; +} + +function server(id) { + const args = getArgs(id, { + TRUFFLE_TEST: [NODE_DIR + "/ganache-cli/cli.js" , "--port=7545", "--gasPrice=20000000000", "--gasLimit=6721975" ], + SOL_COVERAGE: [NODE_DIR + "/ethereumjs-testrpc-sc/cli.js", "--port=7555", "--gasPrice=0x1" , "--gasLimit=0x1fffffffffffff"], + }); + const cp = spawn("node", [...args, + "--account=0x0000000000000000000000000000000000000000000000000000000000000001,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000002,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000003,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000004,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000005,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000006,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000007,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000008,1000000000000000000000000000000000000000", + "--account=0x0000000000000000000000000000000000000000000000000000000000000009,1000000000000000000000000000000000000000", + "--account=0x000000000000000000000000000000000000000000000000000000000000000a,1000000000000000000000000000000000000000"], {cwd: WORK_DIR}); + cp.stdout.on("data", function(data) {/*process.stdout.write(data.toString());*/}); + cp.stderr.on("data", function(data) {process.stderr.write(data.toString());}); + cp.on("error", function(error) {process.stderr.write(error.toString());}); + return cp; +} + +function client(id) { + const args = getArgs(id, { + TRUFFLE_TEST: [NODE_DIR + "/truffle/build/cli.bundled.js", "test"], + SOL_COVERAGE: [NODE_DIR + "/solidity-coverage/bin/exec.js" ], + }); + const cp = spawn("node", args, {cwd: WORK_DIR}); + cp.stdout.on("data", function(data) {process.stdout.write(data.toString());}); + cp.stderr.on("data", function(data) {process.stderr.write(data.toString());}); + cp.on("error", function(error) {process.stderr.write(error.toString());}); + return cp; +} + +function execute(id) { + const server_cp = server(id); + const client_cp = client(id); + client_cp.on("exit", function(code, signal) {server_cp.kill();}); +} + +if (process.argv.length > 2) { + execute(process.argv[2]); +} +else { + process.stdout.write(`Enter '${TRUFFLE_TEST}' for truffle-test or '${SOL_COVERAGE}' for solidity-coverage: `); + process.stdin.resume(); + process.stdin.once("data", function(data) { + process.stdin.pause(); + execute(data.toString().trim()); + }); +} diff --git a/scripts/verify-all.js b/scripts/verify-all.js new file mode 100755 index 0000000..bad5d50 --- /dev/null +++ b/scripts/verify-all.js @@ -0,0 +1,111 @@ +const WORK_DIR = "./solidity"; +const NODE_DIR = "../node_modules"; +const INPUT_FILE = process.argv[2]; + +const fs = require("fs"); +const path = require("path"); +const request = require("request"); +const spawnSync = require("child_process").spawnSync; + +const input = JSON.parse(fs.readFileSync(INPUT_FILE, {encoding: "utf8"})); +// input example: +// { +// "network" : "api", // use "api" for mainnet or "api-" for testnet +// "apiKey" : "", // generate this value at https://etherscan.io/myapikey +// "compilerVersion": "v0.4.26+commit.4563c3fc", +// "optimization" : {"used": 1, "runs": 200}, +// "contracts" : { +// "ContractA1": {"name": "ContractA", "addr": "0x0000000000000000000000000000000000000001", "args": ""}, +// "ContractA2": {"name": "ContractA", "addr": "0x0000000000000000000000000000000000000002", "args": ""}, +// "ContractB1": {"name": "ContractB", "addr": "0x0000000000000000000000000000000000000003", "args": ""}, +// "ContractC1": {"name": "ContractC", "addr": "0x0000000000000000000000000000000000000004", "args": ""}, +// } +// } + +function run() { + for (const pathName of getPathNames("contracts")) { + const contractName = path.basename(pathName, ".sol"); + for (const contractId of Object.keys(input.contracts)) { + if (input.contracts[contractId].name == contractName) + post(contractId, getSourceCode(pathName)); + } + } +} + +function getPathNames(dirName) { + let pathNames = []; + for (const fileName of fs.readdirSync(WORK_DIR + "/" + dirName)) { + if (fs.statSync(WORK_DIR + "/" + dirName + "/" + fileName).isDirectory()) + pathNames = pathNames.concat(getPathNames(dirName + "/" + fileName)); + else if (fileName.endsWith(".sol")) + pathNames.push(dirName + "/" + fileName); + } + return pathNames; +} + +function getSourceCode(pathName) { + const result = spawnSync("node", [NODE_DIR + "/truffle-flattener/index.js", pathName], {cwd: WORK_DIR}); + return result.output.toString().slice(1, -1); +} + +function post(contractId, sourceCode) { + console.log(contractId + ": sending verification request..."); + request.post({ + url: "https://" + input.network + ".etherscan.io/api", + form: { + module : "contract", + action : "verifysourcecode", + sourceCode : sourceCode, + apikey : input.apiKey, + compilerversion : input.compilerVersion, + optimizationUsed : input.optimization.used, + runs : input.optimization.runs, + contractname : input.contracts[contractId].name, + contractaddress : input.contracts[contractId].addr, + constructorArguements: input.contracts[contractId].args, + } + }, + function(error, response, body) { + if (error) { + console.log(contractId + ": " + error); + } + else { + body = parse(body); + if (body.status == "1") + get(contractId, body.result); + else + console.log(contractId + ": " + body.result); + } + } + ); +} + +function get(contractId, guid) { + console.log(contractId + ": checking verification status..."); + request.get( + "https://" + input.network + ".etherscan.io/api?module=contract&action=checkverifystatus&guid=" + guid, + function(error, response, body) { + if (error) { + console.log(contractId + ": " + error); + } + else { + body = parse(body); + if (body.result == "Pending in queue") + get(contractId, guid); + else + console.log(contractId + ": " + body.result); + } + } + ); +} + +function parse(str) { + try { + return JSON.parse(str); + } + catch (error) { + return {}; + } +} + +run(); \ No newline at end of file diff --git a/test/BancorConverter.js b/test/BancorConverter.js new file mode 100755 index 0000000..0df2f91 --- /dev/null +++ b/test/BancorConverter.js @@ -0,0 +1,1427 @@ +/* global artifacts, contract, before, it, assert */ +/* eslint-disable prefer-reflect */ + +const fs = require('fs'); + +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const BancorNetwork = artifacts.require('BancorNetwork'); +const BancorConverter = artifacts.require('BancorConverter'); +const SmartToken = artifacts.require('SmartToken'); +const BancorFormula = artifacts.require('BancorFormula'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const ContractFeatures = artifacts.require('ContractFeatures'); +const ERC20Token = artifacts.require('ERC20Token'); +const TestNonStandardERC20Token = artifacts.require('TestNonStandardERC20Token'); +const BancorConverterFactory = artifacts.require('BancorConverterFactory'); +const BancorConverterUpgrader = artifacts.require('BancorConverterUpgrader'); + +const ratio10Percent = 100000; + +let token; +let tokenAddress; +let contractRegistry; +let contractFeatures; +let reserveToken; +let reserveToken2; +let reserveToken3; +let upgrader; + +// used by purchase/sale tests +async function initConverter(accounts, activate, maxConversionFee = 0) { + token = await SmartToken.new('Token1', 'TKN1', 2); + tokenAddress = token.address; + + let converter = await BancorConverter.new( + tokenAddress, + contractRegistry.address, + maxConversionFee, + reserveToken.address, + 250000 + ); + let converterAddress = converter.address; + await converter.addReserve(reserveToken2.address, 150000); + + await token.issue(accounts[0], 20000); + await reserveToken.transfer(converterAddress, 5000); + await reserveToken2.transfer(converterAddress, 8000); + + if (activate) { + await token.transferOwnership(converterAddress); + await converter.acceptTokenOwnership(); + } + + return converter; +} + +function verifyReserve(reserve, isSet, isEnabled, ratio, isVirtualBalanceEnabled, virtualBalance) { + assert.equal(reserve[0], virtualBalance); + assert.equal(reserve[1], ratio); + assert.equal(reserve[2], isVirtualBalanceEnabled); + assert.equal(reserve[3], isEnabled); + assert.equal(reserve[4], isSet); +} + +function getConversionAmount(transaction, logIndex = 0) { + return transaction.logs[logIndex].args._return.toNumber(); +} + +contract('BancorConverter', accounts => { + before(async () => { + contractRegistry = await ContractRegistry.new(); + + contractFeatures = await ContractFeatures.new(); + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_FEATURES, contractFeatures.address); + + let bancorFormula = await BancorFormula.new(); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_FORMULA, bancorFormula.address); + + let bancorNetwork = await BancorNetwork.new(contractRegistry.address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_NETWORK, bancorNetwork.address); + + let factory = await BancorConverterFactory.new(); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_FACTORY, factory.address); + + upgrader = await BancorConverterUpgrader.new(contractRegistry.address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, upgrader.address); + + let token = await SmartToken.new('Token1', 'TKN1', 2); + tokenAddress = token.address; + + reserveToken = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 1000000000); + reserveToken2 = await TestNonStandardERC20Token.new('ERC Token 2', 'ERC2', 2000000000); + reserveToken3 = await ERC20Token.new('ERC Token 3', 'ERC2', 0, 1500000000); + }); + + it('verifies the converter data after construction', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + let token = await converter.token.call(); + assert.equal(token, tokenAddress); + let registry = await converter.registry.call(); + assert.equal(registry, contractRegistry.address); + + let featureWhitelist = await converter.CONVERTER_CONVERSION_WHITELIST.call(); + let isSupported = await contractFeatures.isSupported.call(converter.address, featureWhitelist); + assert(isSupported); + + let maxConversionFee = await converter.maxConversionFee.call(); + assert.equal(maxConversionFee, 0); + }); + + it('should allow to claim tokens if caller is set as BancorX in the converter', async () => { + let bancorX = accounts[2]; + let converter = await initConverter(accounts, true); + await converter.setBancorX(bancorX); + await token.transfer(accounts[1], 100); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 100); + await converter.claimTokens(accounts[1], 100, {from: bancorX}); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 0); + }); + + it('should not allow to claim tokens if caller is not set as BancorX in the converter', async () => { + let bancorX = accounts[2]; + let converter = await initConverter(accounts, true); + await token.transfer(accounts[1], 100); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 100); + await utils.catchRevert(converter.claimTokens(accounts[1], 100, {from: bancorX})); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 100); + }); + + it('should throw when attempting to construct a converter with no token', async () => { + await utils.catchRevert(BancorConverter.new('0x0', contractRegistry.address, 0, '0x0', 0)); + }); + + it('should throw when attempting to construct a converter with no contract registry', async () => { + await utils.catchRevert(BancorConverter.new(tokenAddress, '0x0', 0, '0x0', 0)); + }); + + it('should throw when attempting to construct a converter with invalid conversion fee', async () => { + await utils.catchRevert(BancorConverter.new(tokenAddress, contractRegistry.address, 1000001, '0x0', 0)); + }); + + it('verifies the first reserve when provided at construction time', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, reserveToken.address, 200000); + let reserveTokenAddress = await converter.reserveTokens.call(0); + assert.equal(reserveTokenAddress, reserveToken.address); + let reserve = await converter.reserves.call(reserveTokenAddress); + verifyReserve(reserve, true, true, 200000, false, 0); + }); + + it('should throw when attempting to construct a converter with a reserve with invalid ratio', async () => { + await utils.catchRevert(BancorConverter.new(tokenAddress, contractRegistry.address, 0, reserveToken.address, 1000001)); + }); + + it('verifies the reserve token count before / after adding a reserve', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + let reserveTokenCount = await converter.reserveTokenCount.call(); + assert.equal(reserveTokenCount, 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + reserveTokenCount = await converter.reserveTokenCount.call(); + assert.equal(reserveTokenCount, 1); + }); + + it('verifies the owner can update the conversion whitelist contract address', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + let prevWhitelist = await converter.conversionWhitelist.call(); + await converter.setConversionWhitelist(accounts[3]); + let newWhitelist = await converter.conversionWhitelist.call(); + assert.notEqual(prevWhitelist, newWhitelist); + }); + + it('should throw when a non owner attempts update the conversion whitelist contract address', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.setConversionWhitelist(accounts[3], { from: accounts[1] })); + }); + + it('verifies the owner can remove the conversion whitelist contract address', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.setConversionWhitelist(accounts[3]); + let whitelist = await converter.conversionWhitelist.call(); + assert.equal(whitelist, accounts[3]); + await converter.setConversionWhitelist(utils.zeroAddress); + whitelist = await converter.conversionWhitelist.call(); + assert.equal(whitelist, utils.zeroAddress); + }); + + it('should throw when the owner attempts update the conversion whitelist contract address with the converter address', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.setConversionWhitelist(converter.address)); + }); + + it('verifies the owner can update the fee', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + await converter.setConversionFee(30000); + let conversionFee = await converter.conversionFee.call(); + assert.equal(conversionFee, 30000); + }); + + it('verifies the manager can update the fee', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + await converter.transferManagement(accounts[4]); + await converter.acceptManagement({ from: accounts[4] }); + + await converter.setConversionFee(30000, { from: accounts[4] }); + let conversionFee = await converter.conversionFee.call(); + assert.equal(conversionFee, 30000); + }); + + it('should throw when attempting to update the fee to an invalid value', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + + await utils.catchRevert(converter.setConversionFee(200001)); + }); + + it('should throw when a non owner and non manager attempts to update the fee', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + + await utils.catchRevert(converter.setConversionFee(30000, { from: accounts[1] })); + }); + + it('verifies that getFinalAmount returns the correct amount', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + await converter.setConversionFee(10000); + let finalAmount = await converter.getFinalAmount.call(500000, 1); + assert.equal(finalAmount, 495000); + finalAmount = await converter.getFinalAmount.call(500000, 2); + assert.equal(finalAmount, 490050); + }); + + it('verifies that an event is fired when the owner updates the fee', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + let watcher = converter.ConversionFeeUpdate(); + await converter.setConversionFee(30000); + let events = await watcher.get(); + assert.equal(events[0].args._prevFee.valueOf(), 0); + assert.equal(events[0].args._newFee.valueOf(), 30000); + }); + + it('verifies that an event is fired when the owner updates the fee multiple times', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + let watcher = converter.ConversionFeeUpdate(); + let events; + for (let i = 1; i <= 10; ++i) { + await converter.setConversionFee(10000 * i); + events = await watcher.get(); + assert.equal(events[0].args._prevFee.valueOf(), 10000 * (i - 1)); + assert.equal(events[0].args._newFee.valueOf(), 10000 * i); + } + }); + + it('should not fire an event when attempting to update the fee to an invalid value', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + let watcher = converter.ConversionFeeUpdate(); + + await utils.catchRevert(converter.setConversionFee(200001)); + let events = await watcher.get(); + assert.equal(events.length, 0); + }); + + it('should not fire an event when a non owner attempts to update the fee', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 200000, '0x0', 0); + let watcher = converter.ConversionFeeUpdate(); + + await utils.catchRevert(converter.setConversionFee(30000, { from: accounts[1] })); + let events = await watcher.get(); + assert.equal(events.length, 0); + }); + + it('verifies that 2 reserves are added correctly', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + let reserve = await converter.reserves.call(reserveToken.address); + verifyReserve(reserve, true, true, ratio10Percent, false, 0); + await converter.addReserve(reserveToken2.address, 200000); + reserve = await converter.reserves.call(reserveToken2.address); + verifyReserve(reserve, true, true, 200000, false, 0); + }); + + it('should throw when a non owner attempts to add a reserve', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.addReserve(reserveToken.address, ratio10Percent, { from: accounts[1] })); + }); + + it('should throw when attempting to accept token ownership when its total supply is zero', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let converter = await BancorConverter.new(token.address, contractRegistry.address, 0, '0x0', 0); + await token.transferOwnership(converter.address); + + await utils.catchRevert(converter.acceptTokenOwnership()); + }); + + it('should throw when attempting to add a reserve when the converter is active', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let converter = await BancorConverter.new(token.address, contractRegistry.address, 0, '0x0', 0); + await token.issue(accounts[0], 20000); + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await utils.catchRevert(converter.addReserve(reserveToken.address, ratio10Percent)); + }); + + it('should throw when attempting to add a reserve with invalid address', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.addReserve('0x0', ratio10Percent)); + }); + + it('should throw when attempting to add a reserve with ratio = 0', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.addReserve(reserveToken.address, 0)); + }); + + it('should throw when attempting to add a reserve with ratio greater than 100%', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.addReserve(reserveToken.address, 1000001)); + }); + + it('should throw when attempting to add the token as a reserve', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.addReserve(tokenAddress, ratio10Percent)); + }); + + it('should throw when attempting to add the converter as a reserve', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + + await utils.catchRevert(converter.addReserve(converter.address, ratio10Percent)); + }); + + it('should throw when attempting to add a reserve that already exists', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + + await utils.catchRevert(converter.addReserve(reserveToken.address, 200000)); + }); + + it('should throw when attempting to add multiple reserves with total ratio greater than 100%', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, 500000); + + await utils.catchRevert(converter.addReserve(reserveToken2.address, 500001)); + }); + + it('verifies that the owner can update a reserve virtual balance if the owner is the upgrader contract', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + let reserve = await converter.reserves.call(reserveToken.address); + verifyReserve(reserve, true, true, ratio10Percent, false, 0); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, accounts[0]); + + await converter.updateReserveVirtualBalance(reserveToken.address, 50); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, upgrader.address); + + reserve = await converter.reserves.call(reserveToken.address); + verifyReserve(reserve, true, true, ratio10Percent, true, 50); + }); + + it('should throw when the owner attempts to update a reserve virtual balance', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + + await utils.catchRevert(converter.updateReserveVirtualBalance(reserveToken.address, 0)); + }); + + it('should throw when a non owner attempts to update a reserve virtual balance', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + + await utils.catchRevert(converter.updateReserveVirtualBalance(reserveToken.address, 0, { from: accounts[1] })); + }); + + it('should throw when attempting to update a reserve virtual balance for a reserve that does not exist', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + let reserve = await converter.reserves.call(reserveToken.address); + verifyReserve(reserve, true, true, ratio10Percent, false, 0); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, accounts[0]); + + await utils.catchRevert(converter.updateReserveVirtualBalance(reserveToken2.address, 0)); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, upgrader.address); + }); + + it('verifies that the correct reserve ratio is returned', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, reserveToken.address, ratio10Percent); + let reserveRatio = await converter.getReserveRatio(reserveToken.address); + assert.equal(reserveRatio, ratio10Percent); + }); + + it('should throw when attempting to retrieve the balance for a reserve that does not exist', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, '0x0', 0); + await converter.addReserve(reserveToken.address, ratio10Percent); + + await utils.catchRevert(converter.getReserveBalance.call(reserveToken2.address)); + }); + + it('verifies that the owner can transfer the token ownership if the owner is the upgrader contract', async () => { + let converter = await initConverter(accounts, true); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, accounts[0]); + + await converter.transferTokenOwnership(accounts[1]); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, upgrader.address); + let tokenAddress = await converter.token.call(); + let contract = await web3.eth.contract(JSON.parse(fs.readFileSync(__dirname + '/../build/SmartToken.abi'))); + let token = await contract.at(tokenAddress); + let newOwner = await token.newOwner.call(); + assert.equal(newOwner, accounts[1]); + }); + + it('should throw when the owner attempts to transfer the token ownership', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.transferTokenOwnership(accounts[1])); + }); + + it('should throw when a non owner attempts to transfer the token ownership', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.transferTokenOwnership(accounts[1], { from: accounts[2] })); + }); + + it('should throw when a the upgrader contract attempts to transfer the token ownership while the upgrader is not the owner', async () => { + let converter = await initConverter(accounts, true); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, accounts[2]); + + await utils.catchRevert(converter.transferTokenOwnership(accounts[1], { from: accounts[2] })); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, upgrader.address); + }); + + it('verifies that the owner can withdraw a non reserve token from the converter while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + let token = await ERC20Token.new('ERC Token 3', 'ERC3', 0, 100000); + await token.transfer(converter.address, 100); + let balance = await token.balanceOf.call(converter.address); + assert.equal(balance, 100); + + await converter.withdrawTokens(token.address, accounts[1], 50); + balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 50); + }); + + it('verifies that the owner can withdraw a reserve token from the converter while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await converter.withdrawTokens(reserveToken.address, accounts[1], 50); + balance = await reserveToken.balanceOf.call(accounts[1]); + assert.equal(balance, 50); + }); + + it('verifies that the owner can withdraw a non reserve token from the converter while the converter is active', async () => { + let converter = await initConverter(accounts, true); + + let token = await ERC20Token.new('ERC Token 3', 'ERC3', 0, 100000); + await token.transfer(converter.address, 100); + let balance = await token.balanceOf.call(converter.address); + assert.equal(balance, 100); + + await converter.withdrawTokens(token.address, accounts[1], 50); + balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 50); + }); + + it('should throw when the owner attempts to withdraw a reserve token while the converter is active', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.withdrawTokens(reserveToken.address, accounts[1], 50)); + }); + + it('should throw when a non owner attempts to withdraw a non reserve token while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + let token = await ERC20Token.new('ERC Token 3', 'ERC3', 0, 100000); + await token.transfer(converter.address, 100); + let balance = await token.balanceOf.call(converter.address); + assert.equal(balance, 100); + + await utils.catchRevert(converter.withdrawTokens(token.address, accounts[1], 50, { from: accounts[2] })); + }); + + it('should throw when a non owner attempts to withdraw a reserve token while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.withdrawTokens(reserveToken.address, accounts[1], 50, { from: accounts[2] })); + }); + + it('should throw when a non owner attempts to withdraw a reserve token while the converter is active', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.withdrawTokens(reserveToken.address, accounts[1], 50, { from: accounts[2] })); + }); + + it('verifies that the owner can upgrade the converter while the converter is active', async () => { + let converter = await initConverter(accounts, true); + await converter.upgrade(); + }); + + it('verifies that the owner can upgrade the converter while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + await converter.upgrade(); + }); + + it('verifies that the owner can upgrade the converter while the converter using the legacy upgrade function', async () => { + let converter = await initConverter(accounts, true); + await converter.transferOwnership(upgrader.address); + await upgrader.upgradeOld(converter.address, web3.fromUtf8("0.9")); + }); + + it('should throw when a non owner attempts to upgrade the converter', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.upgrade({ from: accounts[1] })); + }); + + it('verifies that getReturn returns a valid amount', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(reserveToken.address, tokenAddress, 500))[0]; + assert.isNumber(returnAmount.toNumber()); + assert.notEqual(returnAmount.toNumber(), 0); + }); + + it('verifies that getReturn returns the same amount as getPurchaseReturn when converting from a reserve to the token', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(reserveToken.address, tokenAddress, 500))[0]; + let purchaseReturnAmount = (await converter.getPurchaseReturn.call(reserveToken.address, 500))[0]; + assert.equal(returnAmount.toNumber(), purchaseReturnAmount.toNumber()); + }); + + it('verifies that getReturn returns the same amount as getSaleReturn when converting from the token to a reserve', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(tokenAddress, reserveToken.address, 500))[0]; + let saleReturnAmount = (await converter.getSaleReturn.call(reserveToken.address, 500))[0]; + assert.isNumber(returnAmount.toNumber()); + assert.notEqual(returnAmount.toNumber(), 0); + assert.equal(returnAmount.toNumber(), saleReturnAmount.toNumber()); + }); + + it('verifies that getReturn returns the same amount as buy -> sell when converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert(reserveToken.address, tokenAddress, 500, 1); + let purchaseAmount = getConversionAmount(purchaseRes); + let saleRes = await converter.convert(tokenAddress, reserveToken2.address, purchaseAmount, 1); + let saleAmount = getConversionAmount(saleRes); + + // converting directly between 2 tokens is more efficient than buying and then selling + // which might result in a very small rounding difference + assert(returnAmount.minus(saleAmount).absoluteValue().toNumber() < 2); + }); + + it('verifies that Conversion event returns conversion fee after buying', async () => { + let converter = await initConverter(accounts, true, 5000); + await converter.setConversionFee(3000); + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert(reserveToken.address, tokenAddress, 500, 1); + assert(purchaseRes.logs.length > 0 && purchaseRes.logs[0].event == 'Conversion'); + assert('_conversionFee' in purchaseRes.logs[0].args); + }); + + it('verifies that Conversion event returns conversion fee after selling', async () => { + let converter = await initConverter(accounts, true, 5000); + await converter.setConversionFee(3000); + await reserveToken.approve(converter.address, 500); + let saleRes = await converter.convert(tokenAddress, reserveToken.address, 500, 1); + assert(saleRes.logs.length > 0 && saleRes.logs[0].event == 'Conversion'); + assert('_conversionFee' in saleRes.logs[0].args); + }); + + it('should throw when attempting to get the return with an invalid from token adress', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getReturn.call('0x0', reserveToken2.address, 500)); + }); + + it('should throw when attempting to get the return with an invalid to token address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getReturn.call(reserveToken.address, '0x0', 500)); + }); + + it('should throw when attempting to get the return with identical from/to addresses', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getReturn.call(reserveToken.address, reserveToken.address, 500)); + }); + + it('should throw when attempting to get the purchase return while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.getPurchaseReturn.call(reserveToken.address, 500)); + }); + + it('should throw when attempting to get the purchase return with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getPurchaseReturn.call(tokenAddress, 500)); + }); + + it('should throw when attempting to get the sale return while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.getSaleReturn.call(reserveToken.address, 500)); + }); + + it('should throw when attempting to get the sale return with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getSaleReturn.call(tokenAddress, 500)); + }); + + it('verifies that convert returns a valid amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + let res = await converter.convert(reserveToken.address, tokenAddress, 500, 1); + let conversionAmount = getConversionAmount(res); + assert.isNumber(conversionAmount); + assert.notEqual(conversionAmount, 0); + }); + + it('verifies that selling right after buying does not result in an amount greater than the original purchase amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert(reserveToken.address, tokenAddress, 500, 1); + let purchaseAmount = getConversionAmount(purchaseRes); + let saleRes = await converter.convert(tokenAddress, reserveToken.address, purchaseAmount, 1); + let saleAmount = getConversionAmount(saleRes); + assert(saleAmount <= 500); + }); + + it('verifies that buying right after selling does not result in an amount greater than the original sale amount', async () => { + let converter = await initConverter(accounts, true); + let saleRes = await converter.convert(tokenAddress, reserveToken.address, 500, 1); + let saleAmount = getConversionAmount(saleRes); + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert(reserveToken.address, tokenAddress, saleAmount, 1); + let purchaseAmount = getConversionAmount(purchaseRes); + + assert(purchaseAmount <= 500); + }); + + it('should throw when attempting to convert with an invalid from token adress', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert('0x0', reserveToken2.address, 500, 1)); + }); + + it('should throw when attempting to convert with an invalid to token address', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, '0x0', 500, 1)); + }); + + it('should throw when attempting to convert with identical from/to addresses', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, reserveToken.address, 500, 0)); + }); + + it('should throw when attempting to convert with 0 minimum requested amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, reserveToken2.address, 500, 2000)); + }); + + it('should throw when attempting to convert when the return is smaller than the minimum requested amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, reserveToken2.address, 500, 2000)); + }); + + it('verifies balances after buy', async () => { + let converter = await initConverter(accounts, true); + + let tokenPrevBalance = await token.balanceOf.call(accounts[0]); + let reserveTokenPrevBalance = await reserveToken.balanceOf.call(accounts[0]); + + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert(reserveToken.address, tokenAddress, 500, 1); + let purchaseAmount = getConversionAmount(purchaseRes); + + let reserveTokenNewBalance = await reserveToken.balanceOf.call(accounts[0]); + assert.equal(reserveTokenNewBalance.toNumber(), reserveTokenPrevBalance.minus(500).toNumber()); + + let tokenNewBalance = await token.balanceOf.call(accounts[0]); + assert.equal(tokenNewBalance.toNumber(), tokenPrevBalance.plus(purchaseAmount).toNumber()); + }); + + it('should throw when attempting to buy while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, tokenAddress, 500, 1)); + }); + + it('should throw when attempting to buy with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(tokenAddress, tokenAddress, 500, 1)); + }); + + it('should throw when attempting to buy while the purchase yields 0 return', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, tokenAddress, 0, 1)); + }); + + it('should throw when attempting to buy with 0 minimum requested amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert(reserveToken.address, tokenAddress, 500, 0)); + }); + + it('should throw when attempting to buy without first approving the converter to transfer from the buyer account in the reserve contract', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert(reserveToken.address, tokenAddress, 500, 1)); + }); + + it('verifies balances after sell', async () => { + let converter = await initConverter(accounts, true); + + let tokenPrevBalance = await token.balanceOf.call(accounts[0]); + let reserveTokenPrevBalance = await reserveToken.balanceOf.call(accounts[0]); + + let saleRes = await converter.convert(tokenAddress, reserveToken.address, 500, 1); + let saleAmount = getConversionAmount(saleRes); + + let reserveTokenNewBalance = await reserveToken.balanceOf.call(accounts[0]); + assert.equal(reserveTokenNewBalance.toNumber(), reserveTokenPrevBalance.plus(saleAmount).toNumber()); + + let tokenNewBalance = await token.balanceOf.call(accounts[0]); + assert.equal(tokenNewBalance.toNumber(), tokenPrevBalance.minus(500).toNumber()); + }); + + it('should throw when attempting to sell while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.convert(tokenAddress, reserveToken.address, 500, 1)); + }); + + it('should throw when attempting to sell with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert(tokenAddress, tokenAddress, 500, 1)); + }); + + it('should throw when attempting to sell while the sale yields 0 return', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert(tokenAddress, reserveToken.address, 0, 1)); + }); + + it('should throw when attempting to sell with amount greater then the seller balance', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert(tokenAddress, reserveToken.address, 30000, 1)); + }); + + it('should throw when attempting to execute fund on a single-reserve converter', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, reserveToken.address, 1000000); + + await utils.catchRevert(converter.fund(1)); + }); + + it('should throw when attempting to execute liquidate on a single-reserve converter', async () => { + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, 0, reserveToken.address, 1000000); + + await utils.catchRevert(converter.liquidate(1)); + }); + + it('verifies that getReturn returns the same amount as getCrossReserveReturn when converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + let returnAmount2 = (await converter.getCrossReserveReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + assert.equal(returnAmount.toNumber(), returnAmount2.toNumber()); + }); + + it('verifies that getCrossReserveReturn returns the same amount as converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getCrossReserveReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + + await reserveToken.approve(converter.address, 500); + let convertRes = await converter.convert(reserveToken.address, reserveToken2.address, 500, 1); + let returnAmount2 = getConversionAmount(convertRes); + + assert.equal(returnAmount.toNumber(), returnAmount2); + }); + + it('verifies that getCrossReserveReturn returns the same amount as converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getCrossReserveReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + + await reserveToken.approve(converter.address, 500); + let convertRes = await converter.convert(reserveToken.address, reserveToken2.address, 500, 1); + let returnAmount2 = getConversionAmount(convertRes); + + assert.equal(returnAmount.toNumber(), returnAmount2); + }); + + for (const percent of [50, 75, 100]) { + it(`verifies that fund executes when the total reserve ratio equals ${percent}%`, async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, (percent - 40) * 10000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + let prevBalance = await token.balanceOf.call(accounts[0]); + await reserveToken.approve(converter.address, 100000); + await reserveToken2.approve(converter.address, 100000); + await reserveToken3.approve(converter.address, 100000); + await converter.fund(100); + let balance = await token.balanceOf.call(accounts[0]); + + assert.equal(balance.toNumber(), prevBalance.toNumber() + 100); + }); + } + + it('verifies that fund gets the correct reserve balance amounts from the caller', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await reserveToken.transfer(accounts[9], 5000); + await reserveToken2.transfer(accounts[9], 5000); + await reserveToken3.transfer(accounts[9], 5000); + + let supply = await token.totalSupply.call(); + let percentage = 100 / (supply / 19); + let prevReserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let prevReserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let prevReserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + let token1Amount = prevReserve1Balance * percentage / 100; + let token2Amount = prevReserve2Balance * percentage / 100; + let token3Amount = prevReserve3Balance * percentage / 100; + + await reserveToken.approve(converter.address, 100000, { from: accounts[9] }); + await reserveToken2.approve(converter.address, 100000, { from: accounts[9] }); + await reserveToken3.approve(converter.address, 100000, { from: accounts[9] }); + await converter.fund(19, { from: accounts[9] }); + + let reserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let reserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let reserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + + assert.equal(reserve1Balance.toNumber(), prevReserve1Balance.plus(Math.ceil(token1Amount))); + assert.equal(reserve2Balance.toNumber(), prevReserve2Balance.plus(Math.ceil(token2Amount))); + assert.equal(reserve3Balance.toNumber(), prevReserve3Balance.plus(Math.ceil(token3Amount))); + + let token1Balance = await reserveToken.balanceOf.call(accounts[9]); + let token2Balance = await reserveToken2.balanceOf.call(accounts[9]); + let token3Balance = await reserveToken3.balanceOf.call(accounts[9]); + + await reserveToken.transfer(accounts[0], token1Balance, { from: accounts[9] }); + await reserveToken2.transfer(accounts[0], token2Balance, { from: accounts[9] }); + await reserveToken3.transfer(accounts[0], token3Balance, { from: accounts[9] }); + }); + + it('verifies that increasing the liquidity by a large amount gets the correct reserve balance amounts from the caller', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await reserveToken.transfer(accounts[9], 500000); + await reserveToken2.transfer(accounts[9], 500000); + await reserveToken3.transfer(accounts[9], 500000); + + let supply = await token.totalSupply.call(); + let percentage = 100 / (supply / 140854); + let prevReserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let prevReserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let prevReserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + let token1Amount = prevReserve1Balance * percentage / 100; + let token2Amount = prevReserve2Balance * percentage / 100; + let token3Amount = prevReserve3Balance * percentage / 100; + + await reserveToken.approve(converter.address, 100000, { from: accounts[9] }); + await reserveToken2.approve(converter.address, 100000, { from: accounts[9] }); + await reserveToken3.approve(converter.address, 100000, { from: accounts[9] }); + await converter.fund(140854, { from: accounts[9] }); + + let reserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let reserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let reserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + + assert.equal(reserve1Balance.toNumber(), prevReserve1Balance.plus(Math.ceil(token1Amount))); + assert.equal(reserve2Balance.toNumber(), prevReserve2Balance.plus(Math.ceil(token2Amount))); + assert.equal(reserve3Balance.toNumber(), prevReserve3Balance.plus(Math.ceil(token3Amount))); + + let token1Balance = await reserveToken.balanceOf.call(accounts[9]); + let token2Balance = await reserveToken2.balanceOf.call(accounts[9]); + let token3Balance = await reserveToken3.balanceOf.call(accounts[9]); + + await reserveToken.transfer(accounts[0], token1Balance, { from: accounts[9] }); + await reserveToken2.transfer(accounts[0], token2Balance, { from: accounts[9] }); + await reserveToken3.transfer(accounts[0], token3Balance, { from: accounts[9] }); + }); + + it('should throw when attempting to fund the converter with insufficient funds', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await reserveToken.transfer(accounts[9], 100); + await reserveToken2.transfer(accounts[9], 100); + await reserveToken3.transfer(accounts[9], 100); + + await reserveToken.approve(converter.address, 100000, { from: accounts[9] }); + await reserveToken2.approve(converter.address, 100000, { from: accounts[9] }); + await reserveToken3.approve(converter.address, 100000, { from: accounts[9] }); + await converter.fund(5, { from: accounts[9] }); + + await utils.catchRevert(converter.fund(600, { from: accounts[9] })); + let token1Balance = await reserveToken.balanceOf.call(accounts[9]); + let token2Balance = await reserveToken2.balanceOf.call(accounts[9]); + let token3Balance = await reserveToken3.balanceOf.call(accounts[9]); + + await reserveToken.transfer(accounts[0], token1Balance, { from: accounts[9] }); + await reserveToken2.transfer(accounts[0], token2Balance, { from: accounts[9] }); + await reserveToken3.transfer(accounts[0], token3Balance, { from: accounts[9] }); + + }); + + for (const percent of [50, 75, 100]) { + it(`verifies that liquidate executes when the total reserve ratio equals ${percent}%`, async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, (percent - 40) * 10000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + let prevSupply = await token.totalSupply.call(); + await converter.liquidate(100); + let supply = await token.totalSupply(); + + assert.equal(prevSupply - 100, supply); + }); + } + + it('verifies that liquidate sends the correct reserve balance amounts to the caller', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await token.transfer(accounts[9], 100); + + let supply = await token.totalSupply.call(); + let percentage = 100 / (supply / 19); + let reserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let reserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let reserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + let token1Amount = reserve1Balance * percentage / 100; + let token2Amount = reserve2Balance * percentage / 100; + let token3Amount = reserve3Balance * percentage / 100; + + await converter.liquidate(19, { from: accounts[9] }); + + let token1Balance = await reserveToken.balanceOf.call(accounts[9]); + let token2Balance = await reserveToken2.balanceOf.call(accounts[9]); + let token3Balance = await reserveToken3.balanceOf.call(accounts[9]); + + assert.equal(token1Balance.toNumber(), Math.floor(token1Amount)); + assert.equal(token2Balance.toNumber(), Math.floor(token2Amount)); + assert.equal(token3Balance.toNumber(), Math.floor(token3Amount)); + + await reserveToken.transfer(accounts[0], token1Balance, { from: accounts[9] }); + await reserveToken2.transfer(accounts[0], token2Balance, { from: accounts[9] }); + await reserveToken3.transfer(accounts[0], token3Balance, { from: accounts[9] }); + }); + + it('verifies that liquidating a large amount sends the correct reserve balance amounts to the caller', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await token.transfer(accounts[9], 15000); + + let supply = await token.totalSupply.call(); + let percentage = 100 / (supply / 14854); + let reserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let reserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let reserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + let token1Amount = reserve1Balance * percentage / 100; + let token2Amount = reserve2Balance * percentage / 100; + let token3Amount = reserve3Balance * percentage / 100; + + await converter.liquidate(14854, { from: accounts[9] }); + + supply = await token.totalSupply.call(); + let token1Balance = await reserveToken.balanceOf.call(accounts[9]); + let token2Balance = await reserveToken2.balanceOf.call(accounts[9]); + let token3Balance = await reserveToken3.balanceOf.call(accounts[9]); + + assert.equal(token1Balance.toNumber(), Math.floor(token1Amount)); + assert.equal(token2Balance.toNumber(), Math.floor(token2Amount)); + assert.equal(token3Balance.toNumber(), Math.floor(token3Amount)); + + await reserveToken.transfer(accounts[0], token1Balance, { from: accounts[9] }); + await reserveToken2.transfer(accounts[0], token2Balance, { from: accounts[9] }); + await reserveToken3.transfer(accounts[0], token3Balance, { from: accounts[9] }); + }); + + it('verifies that liquidating the entire supply sends the full reserve balances to the caller', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await token.transfer(accounts[9], 20000); + + let reserve1Balance = await converter.getReserveBalance.call(reserveToken.address); + let reserve2Balance = await converter.getReserveBalance.call(reserveToken2.address); + let reserve3Balance = await converter.getReserveBalance.call(reserveToken3.address); + + await converter.liquidate(20000, { from: accounts[9] }); + + let supply = await token.totalSupply.call(); + let token1Balance = await reserveToken.balanceOf.call(accounts[9]); + let token2Balance = await reserveToken2.balanceOf.call(accounts[9]); + let token3Balance = await reserveToken3.balanceOf.call(accounts[9]); + + assert.equal(supply, 0); + assert.equal(token1Balance.toNumber(), reserve1Balance.toNumber()); + assert.equal(token2Balance.toNumber(), reserve2Balance.toNumber()); + assert.equal(token3Balance.toNumber(), reserve3Balance.toNumber()); + + await reserveToken.transfer(accounts[0], token1Balance, { from: accounts[9] }); + await reserveToken2.transfer(accounts[0], token2Balance, { from: accounts[9] }); + await reserveToken3.transfer(accounts[0], token3Balance, { from: accounts[9] }); + }); + + it('should throw when attempting to liquidate with insufficient funds', async () => { + let converter = await initConverter(accounts, false); + await converter.addReserve(reserveToken3.address, 600000); + + await reserveToken3.transfer(converter.address, 6000); + + await token.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + + await token.transfer(accounts[9], 100); + + await converter.liquidate(5, { from: accounts[9] }); + + await utils.catchRevert(converter.liquidate(600, { from: accounts[9] })); + }); + + it('should throw when attempting to register the registry to the zero address', async () => { + await utils.catchRevert(contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, utils.zeroAddress)); + }); + + it('should throw when attempting to update the registry when it points to the zero address', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.updateRegistry()); + assert.equal(await converter.registry.call(), contractRegistry.address); + assert.equal(await converter.prevRegistry.call(), contractRegistry.address); + }); + + it('should throw when attempting to update the registry when it points to the current registry', async () => { + let converter = await initConverter(accounts, false); + + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, contractRegistry.address); + await utils.catchRevert(converter.updateRegistry()); + assert.equal(await converter.registry.call(), contractRegistry.address); + assert.equal(await converter.prevRegistry.call(), contractRegistry.address); + }); + + it('should throw when attempting to update the registry when it points to a new registry which points to the zero address', async () => { + let converter = await initConverter(accounts, false); + + let newRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, newRegistry.address); + await utils.catchRevert(converter.updateRegistry()); + assert.equal(await converter.registry.call(), contractRegistry.address); + assert.equal(await converter.prevRegistry.call(), contractRegistry.address); + + // set the original registry back + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, contractRegistry.address); + }); + + it('should allow anyone to update the registry address', async () => { + let converter = await initConverter(accounts, false); + let newRegistry = await ContractRegistry.new(); + + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, newRegistry.address); + await newRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, newRegistry.address); + await converter.updateRegistry({ from: accounts[1] }); + + assert.equal(await converter.registry.call(), newRegistry.address); + assert.equal(await converter.prevRegistry.call(), contractRegistry.address); + + // set the original registry back + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, contractRegistry.address); + }); + + it('should allow the owner to restore the previous registry and disable updates', async () => { + let converter = await initConverter(accounts, false); + let newRegistry = await ContractRegistry.new(); + + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, newRegistry.address); + await newRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, newRegistry.address); + await converter.updateRegistry({ from: accounts[1] }); + + await converter.restoreRegistry({ from: accounts[0] }); + + assert.equal(await converter.registry.call(), contractRegistry.address); + assert.equal(await converter.prevRegistry.call(), contractRegistry.address); + + await converter.restrictRegistryUpdate(true, { from: accounts[0] }); + await utils.catchRevert(converter.updateRegistry({ from: accounts[1] })); + + await converter.updateRegistry({ from: accounts[0] }); + assert.equal(await converter.registry.call(), newRegistry.address); + assert.equal(await converter.prevRegistry.call(), contractRegistry.address); + + // re register address + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_REGISTRY, contractRegistry.address); + }); + + it('verifies that getReturn returns the same amount as buy -> sell when converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0); + let purchaseAmount = getConversionAmount(purchaseRes); + let saleRes = await converter.convert2(tokenAddress, reserveToken2.address, purchaseAmount, 1, utils.zeroAddress, 0); + let saleAmount = getConversionAmount(saleRes); + + // converting directly between 2 tokens is more efficient than buying and then selling + // which might result in a very small rounding difference + assert(returnAmount.minus(saleAmount).absoluteValue().toNumber() < 2); + }); + + it('verifies that Conversion event returns conversion fee after buying', async () => { + let converter = await initConverter(accounts, true, 5000); + await converter.setConversionFee(3000); + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0); + assert(purchaseRes.logs.length > 0 && purchaseRes.logs[0].event == 'Conversion'); + assert('_conversionFee' in purchaseRes.logs[0].args); + }); + + it('verifies that Conversion event returns conversion fee after selling', async () => { + let converter = await initConverter(accounts, true, 5000); + await converter.setConversionFee(3000); + await reserveToken.approve(converter.address, 500); + let saleRes = await converter.convert2(tokenAddress, reserveToken.address, 500, 1, utils.zeroAddress, 0); + assert(saleRes.logs.length > 0 && saleRes.logs[0].event == 'Conversion'); + assert('_conversionFee' in saleRes.logs[0].args); + }); + + it('should throw when attempting to get the return with an invalid from token adress', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getReturn.call('0x0', reserveToken2.address, 500)); + }); + + it('should throw when attempting to get the return with an invalid to token address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getReturn.call(reserveToken.address, '0x0', 500)); + }); + + it('should throw when attempting to get the return with identical from/to addresses', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getReturn.call(reserveToken.address, reserveToken.address, 500)); + }); + + it('should throw when attempting to get the purchase return while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.getPurchaseReturn.call(reserveToken.address, 500)); + }); + + it('should throw when attempting to get the purchase return with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getPurchaseReturn.call(tokenAddress, 500)); + }); + + it('should throw when attempting to get the sale return while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.getSaleReturn.call(reserveToken.address, 500)); + }); + + it('should throw when attempting to get the sale return with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.getSaleReturn.call(tokenAddress, 500)); + }); + + it('verifies that convert2 returns a valid amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + let res = await converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0); + let conversionAmount = getConversionAmount(res); + assert.isNumber(conversionAmount); + assert.notEqual(conversionAmount, 0); + }); + + it('verifies that selling right after buying does not result in an amount greater than the original purchase amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0); + let purchaseAmount = getConversionAmount(purchaseRes); + let saleRes = await converter.convert2(tokenAddress, reserveToken.address, purchaseAmount, 1, utils.zeroAddress, 0); + let saleAmount = getConversionAmount(saleRes); + assert(saleAmount <= 500); + }); + + it('verifies that buying right after selling does not result in an amount greater than the original sale amount', async () => { + let converter = await initConverter(accounts, true); + let saleRes = await converter.convert2(tokenAddress, reserveToken.address, 500, 1, utils.zeroAddress, 0); + let saleAmount = getConversionAmount(saleRes); + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert2(reserveToken.address, tokenAddress, saleAmount, 1, utils.zeroAddress, 0); + let purchaseAmount = getConversionAmount(purchaseRes); + + assert(purchaseAmount <= 500); + }); + + it('should throw when attempting to convert2 with an invalid from token adress', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2('0x0', reserveToken2.address, 500, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to convert2 with an invalid to token address', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, '0x0', 500, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to convert2 with identical from/to addresses', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, reserveToken.address, 500, 0, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to convert2 with 0 minimum requested amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, reserveToken2.address, 500, 2000, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to convert2 when the return is smaller than the minimum requested amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, reserveToken2.address, 500, 2000, utils.zeroAddress, 0)); + }); + + it('verifies balances after buy', async () => { + let converter = await initConverter(accounts, true); + + let tokenPrevBalance = await token.balanceOf.call(accounts[0]); + let reserveTokenPrevBalance = await reserveToken.balanceOf.call(accounts[0]); + + await reserveToken.approve(converter.address, 500); + let purchaseRes = await converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0); + let purchaseAmount = getConversionAmount(purchaseRes); + + let reserveTokenNewBalance = await reserveToken.balanceOf.call(accounts[0]); + assert.equal(reserveTokenNewBalance.toNumber(), reserveTokenPrevBalance.minus(500).toNumber()); + + let tokenNewBalance = await token.balanceOf.call(accounts[0]); + assert.equal(tokenNewBalance.toNumber(), tokenPrevBalance.plus(purchaseAmount).toNumber()); + }); + + it('should throw when attempting to buy while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to buy with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(tokenAddress, tokenAddress, 500, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to buy while the purchase yields 0 return', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, tokenAddress, 0, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to buy with 0 minimum requested amount', async () => { + let converter = await initConverter(accounts, true); + await reserveToken.approve(converter.address, 500); + + await utils.catchRevert(converter.convert2(reserveToken.address, tokenAddress, 500, 0, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to buy without first approving the converter to transfer from the buyer account in the reserve contract', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert2(reserveToken.address, tokenAddress, 500, 1, utils.zeroAddress, 0)); + }); + + it('verifies balances after sell', async () => { + let converter = await initConverter(accounts, true); + + let tokenPrevBalance = await token.balanceOf.call(accounts[0]); + let reserveTokenPrevBalance = await reserveToken.balanceOf.call(accounts[0]); + + let saleRes = await converter.convert2(tokenAddress, reserveToken.address, 500, 1, utils.zeroAddress, 0); + let saleAmount = getConversionAmount(saleRes); + + let reserveTokenNewBalance = await reserveToken.balanceOf.call(accounts[0]); + assert.equal(reserveTokenNewBalance.toNumber(), reserveTokenPrevBalance.plus(saleAmount).toNumber()); + + let tokenNewBalance = await token.balanceOf.call(accounts[0]); + assert.equal(tokenNewBalance.toNumber(), tokenPrevBalance.minus(500).toNumber()); + }); + + it('should throw when attempting to sell while the converter is not active', async () => { + let converter = await initConverter(accounts, false); + + await utils.catchRevert(converter.convert2(tokenAddress, reserveToken.address, 500, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to sell with a non reserve address', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert2(tokenAddress, tokenAddress, 500, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to sell while the sale yields 0 return', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert2(tokenAddress, reserveToken.address, 0, 1, utils.zeroAddress, 0)); + }); + + it('should throw when attempting to sell with amount greater then the seller balance', async () => { + let converter = await initConverter(accounts, true); + + await utils.catchRevert(converter.convert2(tokenAddress, reserveToken.address, 30000, 1, utils.zeroAddress, 0)); + }); + + it('verifies that getReturn returns the same amount as getCrossReserveReturn when converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + let returnAmount2 = (await converter.getCrossReserveReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + assert.equal(returnAmount.toNumber(), returnAmount2.toNumber()); + }); + + it('verifies that getCrossReserveReturn returns the same amount as converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getCrossReserveReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + + await reserveToken.approve(converter.address, 500); + let convertRes = await converter.convert2(reserveToken.address, reserveToken2.address, 500, 1, utils.zeroAddress, 0); + let returnAmount2 = getConversionAmount(convertRes); + + assert.equal(returnAmount.toNumber(), returnAmount2); + }); + + it('verifies that getCrossReserveReturn returns the same amount as converting between 2 reserves', async () => { + let converter = await initConverter(accounts, true); + let returnAmount = (await converter.getCrossReserveReturn.call(reserveToken.address, reserveToken2.address, 500))[0]; + + await reserveToken.approve(converter.address, 500); + let convertRes = await converter.convert2(reserveToken.address, reserveToken2.address, 500, 1, utils.zeroAddress, 0); + let returnAmount2 = getConversionAmount(convertRes); + + assert.equal(returnAmount.toNumber(), returnAmount2); + }); +}); \ No newline at end of file diff --git a/test/BancorConverterRegistry.js b/test/BancorConverterRegistry.js new file mode 100755 index 0000000..49ce30f --- /dev/null +++ b/test/BancorConverterRegistry.js @@ -0,0 +1,210 @@ +/* global artifacts, contract, before, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const EtherToken = artifacts.require('EtherToken'); +const SmartToken = artifacts.require('SmartToken'); +const BancorConverter = artifacts.require('BancorConverter'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const BancorConverterRegistry = artifacts.require('BancorConverterRegistry'); +const BancorConverterRegistryData = artifacts.require('BancorConverterRegistryData'); + +contract('BancorConverterRegistry', function(accounts) { + let converter1; + let converter2; + let converter3; + let converter4; + let converter5; + let converter6; + let converter7; + let etherToken; + let smartToken1; + let smartToken2; + let smartToken3; + let smartToken4; + let smartToken5; + let smartToken6; + let smartToken7; + let smartToken8; + let smartToken9; + let smartTokenA; + let smartTokenB; + let smartTokenC; + let smartTokenD; + let smartTokenE; + let contractRegistry + let converterRegistry; + let converterRegistryData; + + before(async function() { + etherToken = await EtherToken.new('Token0', 'TKN0'); + smartToken1 = await SmartToken.new('Token1', 'TKN1', 18); + smartToken2 = await SmartToken.new('Token2', 'TKN2', 18); + smartToken3 = await SmartToken.new('Token3', 'TKN3', 18); + smartToken4 = await SmartToken.new('Token4', 'TKN4', 18); + smartToken5 = await SmartToken.new('Token5', 'TKN5', 18); + smartToken6 = await SmartToken.new('Token6', 'TKN6', 18); + smartToken7 = await SmartToken.new('Token7', 'TKN7', 18); + smartToken8 = await SmartToken.new('Token8', 'TKN8', 18); + smartToken9 = await SmartToken.new('Token9', 'TKN9', 18); + smartTokenA = await SmartToken.new('TokenA', 'TKNA', 18); + smartTokenB = await SmartToken.new('TokenB', 'TKNB', 18); + smartTokenC = await SmartToken.new('TokenC', 'TKNC', 18); + smartTokenD = await SmartToken.new('TokenD', 'TKND', 18); + smartTokenE = await SmartToken.new('TokenE', 'TKNE', 18); + + contractRegistry = await ContractRegistry.new(); + + converterRegistry = await BancorConverterRegistry .new(contractRegistry.address); + converterRegistryData = await BancorConverterRegistryData.new(contractRegistry.address); + + converter1 = await BancorConverter.new(smartToken1.address, contractRegistry.address, 0, etherToken .address, 0x1000); + converter2 = await BancorConverter.new(smartToken2.address, contractRegistry.address, 0, smartToken4.address, 0x2400); + converter3 = await BancorConverter.new(smartToken3.address, contractRegistry.address, 0, smartToken6.address, 0x3600); + converter4 = await BancorConverter.new(smartToken4.address, contractRegistry.address, 0, smartToken8.address, 0x4800); + converter5 = await BancorConverter.new(smartToken5.address, contractRegistry.address, 0, smartTokenA.address, 0x5A00); + converter6 = await BancorConverter.new(smartToken6.address, contractRegistry.address, 0, smartTokenC.address, 0x6C00); + converter7 = await BancorConverter.new(smartToken7.address, contractRegistry.address, 0, smartTokenE.address, 0x7E00); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_REGISTRY , converterRegistry .address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_REGISTRY_DATA, converterRegistryData.address); + + await converter2.addReserve(smartToken1.address, 0x2100); + await converter3.addReserve(smartToken1.address, 0x3100); + await converter4.addReserve(smartToken1.address, 0x4100); + await converter5.addReserve(smartToken1.address, 0x5100); + await converter6.addReserve(smartToken1.address, 0x6100); + await converter7.addReserve(smartToken2.address, 0x7200); + + await etherToken.deposit({value: 1000000}); + await smartToken1.issue(accounts[0], 1000000); + await smartToken2.issue(accounts[0], 1000000); + await smartToken3.issue(accounts[0], 1000000); + await smartToken4.issue(accounts[0], 1000000); + await smartToken5.issue(accounts[0], 1000000); + await smartToken6.issue(accounts[0], 1000000); + await smartToken7.issue(accounts[0], 1000000); + await smartToken8.issue(accounts[0], 1000000); + await smartToken9.issue(accounts[0], 1000000); + await smartTokenA.issue(accounts[0], 1000000); + await smartTokenB.issue(accounts[0], 1000000); + await smartTokenC.issue(accounts[0], 1000000); + await smartTokenD.issue(accounts[0], 1000000); + await smartTokenE.issue(accounts[0], 1000000); + + await etherToken .transfer(converter1.address, 1000); + await smartToken4.transfer(converter2.address, 1000); + await smartToken6.transfer(converter3.address, 1000); + await smartToken8.transfer(converter4.address, 1000); + await smartTokenA.transfer(converter5.address, 1000); + await smartTokenC.transfer(converter6.address, 1000); + await smartTokenE.transfer(converter7.address, 1000); + await smartToken1.transfer(converter2.address, 1000); + await smartToken1.transfer(converter3.address, 1000); + await smartToken1.transfer(converter4.address, 1000); + await smartToken1.transfer(converter5.address, 1000); + await smartToken1.transfer(converter6.address, 1000); + await smartToken2.transfer(converter7.address, 1000); + + await smartToken1.transferOwnership(converter1.address); + await smartToken2.transferOwnership(converter2.address); + await smartToken3.transferOwnership(converter3.address); + await smartToken4.transferOwnership(converter4.address); + await smartToken5.transferOwnership(converter5.address); + await smartToken6.transferOwnership(converter6.address); + await smartToken7.transferOwnership(converter7.address); + await converter1.acceptTokenOwnership(); + await converter2.acceptTokenOwnership(); + await converter3.acceptTokenOwnership(); + await converter4.acceptTokenOwnership(); + await converter5.acceptTokenOwnership(); + await converter6.acceptTokenOwnership(); + await converter7.acceptTokenOwnership(); + }); + + it('function addConverter', async function() { + await test(converterRegistry.addConverter, converter1, 'Added'); + await test(converterRegistry.addConverter, converter2, 'Added'); + await test(converterRegistry.addConverter, converter3, 'Added'); + await test(converterRegistry.addConverter, converter4, 'Added'); + await test(converterRegistry.addConverter, converter5, 'Added'); + await test(converterRegistry.addConverter, converter6, 'Added'); + await test(converterRegistry.addConverter, converter7, 'Added'); + }); + + it('function getLiquidityPoolByReserveConfig', async function() { + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([etherToken .address ], [0x1000 ]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartToken4.address], [0x2400, 0x2100]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartToken6.address], [0x3600, 0x3100]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartToken8.address], [0x4800, 0x4100]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartTokenA.address], [0x5A00, 0x5100]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartTokenC.address], [0x6C00, 0x6100]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken2.address, smartTokenE.address], [0x7E00, 0x7200]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken4.address, smartToken1.address], [0x2100, 0x2400]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken6.address, smartToken1.address], [0x3100, 0x3600]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken8.address, smartToken1.address], [0x4100, 0x4800]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartTokenA.address, smartToken1.address], [0x5100, 0x5A00]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartTokenC.address, smartToken1.address], [0x6100, 0x6C00]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartTokenE.address, smartToken2.address], [0x7200, 0x7E00]), utils.zeroAddress ); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartToken4.address], [0x2100, 0x2400]), smartToken2.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartToken6.address], [0x3100, 0x3600]), smartToken3.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartToken8.address], [0x4100, 0x4800]), smartToken4.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartTokenA.address], [0x5100, 0x5A00]), smartToken5.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken1.address, smartTokenC.address], [0x6100, 0x6C00]), smartToken6.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken2.address, smartTokenE.address], [0x7200, 0x7E00]), smartToken7.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken4.address, smartToken1.address], [0x2400, 0x2100]), smartToken2.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken6.address, smartToken1.address], [0x3600, 0x3100]), smartToken3.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartToken8.address, smartToken1.address], [0x4800, 0x4100]), smartToken4.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartTokenA.address, smartToken1.address], [0x5A00, 0x5100]), smartToken5.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartTokenC.address, smartToken1.address], [0x6C00, 0x6100]), smartToken6.address); + assert.equal(await converterRegistry.getLiquidityPoolByReserveConfig([smartTokenE.address, smartToken2.address], [0x7E00, 0x7200]), smartToken7.address); + }); + + it('function removeConverter', async function() { + await test(converterRegistry.removeConverter, converter1, 'Removed'); + await test(converterRegistry.removeConverter, converter2, 'Removed'); + await test(converterRegistry.removeConverter, converter3, 'Removed'); + await test(converterRegistry.removeConverter, converter4, 'Removed'); + await test(converterRegistry.removeConverter, converter5, 'Removed'); + await test(converterRegistry.removeConverter, converter6, 'Removed'); + await test(converterRegistry.removeConverter, converter7, 'Removed'); + }); + + it('should return a list of converters for a list of smart tokens', async () => { + const tokens = [smartToken1.address, smartToken2.address, smartToken3.address]; + const expected = [converter1.address, converter2.address, converter3.address]; + const actual = await converterRegistry.getConvertersBySmartTokens(tokens); + assert.deepEqual(actual, expected); + }); +}); + +async function test(func, converter, suffix) { + const response = await func(converter.address); + const token = await converter.token(); + const count = await converter.connectorTokenCount(); + const log = response.logs[0]; + const expected = `SmartToken${suffix}(${token})`; + const actual = `${log.event}(${log.args._smartToken})`; + assert.equal(actual, expected); + if (count.greaterThan(1)) { + const log = response.logs[1]; + const expected = `LiquidityPool${suffix}(${token})`; + const actual = `${log.event}(${log.args._liquidityPool})`; + assert.equal(actual, expected); + } + else { + const log = response.logs[1]; + const expected = `ConvertibleToken${suffix}(${token},${token})`; + const actual = `${log.event}(${log.args._convertibleToken},${log.args._smartToken})`; + assert.equal(actual, expected); + } + for (let i = 0; count.greaterThan(i); i++) { + const connectorToken = await converter.connectorTokens(i); + const log = response.logs[2 + i]; + const expected = `ConvertibleToken${suffix}(${connectorToken},${token})`; + const actual = `${log.event}(${log.args._convertibleToken},${log.args._smartToken})`; + assert.equal(actual, expected); + } +} diff --git a/test/BancorConverterRegistryData.js b/test/BancorConverterRegistryData.js new file mode 100755 index 0000000..76c8829 --- /dev/null +++ b/test/BancorConverterRegistryData.js @@ -0,0 +1,210 @@ +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +contract('BancorConverterRegistryData', function(accounts) { + let contractRegistry + let converterRegistry; + + const keyAccounts = accounts.slice(0, 4); + const valAccounts = accounts.slice(4, 8); + const currentState = {convertibleTokens: [], smartTokens: []}; + + before(async function() { + contractRegistry = await artifacts.require('ContractRegistry').new(); + converterRegistry = await artifacts.require('BancorConverterRegistryData').new(contractRegistry.address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_REGISTRY, accounts[0]); + }); + + describe('security assertion:', function() { + it('function addSmartToken should abort with an error if called without permission', async function() { + await utils.catchRevert(converterRegistry.addSmartToken(accounts[0], {from: accounts[1]})); + }); + + it('function removeSmartToken should abort with an error if called without permission', async function() { + await utils.catchRevert(converterRegistry.removeSmartToken(accounts[0], {from: accounts[1]})); + }); + + it('function addLiquidityPool should abort with an error if called without permission', async function() { + await utils.catchRevert(converterRegistry.addLiquidityPool(accounts[0], {from: accounts[1]})); + }); + + it('function removeLiquidityPool should abort with an error if called without permission', async function() { + await utils.catchRevert(converterRegistry.removeLiquidityPool(accounts[0], {from: accounts[1]})); + }); + + it('function addConvertibleToken should abort with an error if called without permission', async function() { + await utils.catchRevert(converterRegistry.addConvertibleToken(accounts[0], accounts[0], {from: accounts[1]})); + }); + + it('function removeConvertibleToken should abort with an error if called without permission', async function() { + await utils.catchRevert(converterRegistry.removeConvertibleToken(accounts[0], accounts[0], {from: accounts[1]})); + }); + }); + + describe('smart tokens basic verification:', function() { + it('function addSmartToken should complete successfully if item does not exists', async function() { + await converterRegistry.addSmartToken(accounts[0]); + }); + + it('function addSmartToken should abort with an error if item already exists', async function() { + await utils.catchRevert(converterRegistry.addSmartToken(accounts[0])); + }); + + it('function removeSmartToken should complete successfully if item already exists', async function() { + await converterRegistry.removeSmartToken(accounts[0]); + }); + + it('function removeSmartToken should abort with an error if item does not exist', async function() { + await utils.catchRevert(converterRegistry.removeSmartToken(accounts[0])); + }); + }); + + describe('liquidity pools basic verification:', function() { + it('function addLiquidityPool should complete successfully if item does not exists', async function() { + await converterRegistry.addLiquidityPool(accounts[0]); + }); + + it('function addLiquidityPool should abort with an error if item already exists', async function() { + await utils.catchRevert(converterRegistry.addLiquidityPool(accounts[0])); + }); + + it('function removeLiquidityPool should complete successfully if item already exists', async function() { + await converterRegistry.removeLiquidityPool(accounts[0]); + }); + + it('function removeLiquidityPool should abort with an error if item does not exist', async function() { + await utils.catchRevert(converterRegistry.removeLiquidityPool(accounts[0])); + }); + }); + + describe('convertible tokens basic verification:', function() { + it('function addConvertibleToken should complete successfully if item does not exists', async function() { + await converterRegistry.addConvertibleToken(keyAccounts[0], valAccounts[0]); + }); + + it('function addConvertibleToken should abort with an error if item already exists', async function() { + await utils.catchRevert(converterRegistry.addConvertibleToken(keyAccounts[0], valAccounts[0])); + }); + + it('function removeConvertibleToken should complete successfully if item already exists', async function() { + await converterRegistry.removeConvertibleToken(keyAccounts[0], valAccounts[0]); + }); + + it('function removeConvertibleToken should abort with an error if item does not exist', async function() { + await utils.catchRevert(converterRegistry.removeConvertibleToken(keyAccounts[0], valAccounts[0])); + }); + }); + + describe('smart tokens advanced verification:', function() { + it('remove first item until all items removed', async function() { + await removeAllOneByOne(+1); + }); + + it('remove last item until all items removed', async function() { + await removeAllOneByOne(-1); + }); + + async function removeAllOneByOne(direction) { + console.log(`adding ${accounts.length} items...`); + for (const account of accounts) + await converterRegistry.addSmartToken(account); + for (let items = accounts.slice(); items.length > 0; items.length--) { + const bgnIndex = (items.length - 1) * (1 - direction) / 2; + const endIndex = (items.length - 1) * (1 + direction) / 2; + const item = await converterRegistry.getSmartToken(bgnIndex); + await converterRegistry.removeSmartToken(item); + assert.equal(item, items[bgnIndex]); + items[bgnIndex] = items[endIndex]; + console.log(`item ${bgnIndex} removed`); + } + }; + }); + + describe('liquidity pools advanced verification:', function() { + it('remove first item until all items removed', async function() { + await removeAllOneByOne(+1); + }); + + it('remove last item until all items removed', async function() { + await removeAllOneByOne(-1); + }); + + async function removeAllOneByOne(direction) { + console.log(`adding ${accounts.length} items...`); + for (const account of accounts) + await converterRegistry.addLiquidityPool(account); + for (let items = accounts.slice(); items.length > 0; items.length--) { + const bgnIndex = (items.length - 1) * (1 - direction) / 2; + const endIndex = (items.length - 1) * (1 + direction) / 2; + const item = await converterRegistry.getLiquidityPool(bgnIndex); + await converterRegistry.removeLiquidityPool(item); + assert.equal(item, items[bgnIndex]); + items[bgnIndex] = items[endIndex]; + console.log(`item ${bgnIndex} removed`); + } + }; + }); + + describe('convertible tokens advanced verification:', function() { + for (const reverseKeys of [false, true]) { + for (const reverseVals of [false, true]) { + for (const addTuples of [rows, cols]) { + for (const removeTuples of [rows, cols]) { + for (const [convertibleToken, smartToken] of addTuples(false, false)) { + it(title(convertibleToken, smartToken, add), async function() { + await test(convertibleToken, smartToken, add); + }); + } + for (const [convertibleToken, smartToken] of removeTuples(reverseKeys, reverseVals)) { + it(title(convertibleToken, smartToken, remove), async function() { + await test(convertibleToken, smartToken, remove); + }); + } + } + } + } + } + + function reorder(tokens, reverse) {return reverse ? tokens.slice().reverse() : tokens;} + function stringify(state) {return accounts.reduce((result, account, index) => result.split(account).join(`${index}`), JSON.stringify(state));} + function title(convertibleToken, smartToken, func) {return `${func.name}(${accounts.indexOf(convertibleToken)} --> ${accounts.indexOf(smartToken)})`;} + function rows(reverseKeys, reverseVals) {return [].concat.apply([], reorder(keyAccounts, reverseKeys).map(x => reorder(valAccounts, reverseVals).map(y => [x, y])));} + function cols(reverseKeys, reverseVals) {return [].concat.apply([], reorder(valAccounts, reverseVals).map(x => reorder(keyAccounts, reverseKeys).map(y => [y, x])));} + + async function test(convertibleToken, smartToken, func) { + const response = await func(convertibleToken, smartToken); + const convertibleTokens = await converterRegistry.getConvertibleTokens(); + const smartTokens = await Promise.all(convertibleTokens.map(convertibleToken => converterRegistry.getConvertibleTokenSmartTokens(convertibleToken))); + assert.equal(stringify({convertibleTokens: convertibleTokens, smartTokens: smartTokens}), stringify(currentState)); + } + + async function add(convertibleToken, smartToken) { + const index = currentState.convertibleTokens.indexOf(convertibleToken); + if (index == -1) { + currentState.convertibleTokens.push(convertibleToken); + currentState.smartTokens.push([smartToken]); + } + else { + currentState.smartTokens[index].push(smartToken); + } + return await converterRegistry.addConvertibleToken(convertibleToken, smartToken); + } + + async function remove(convertibleToken, smartToken) { + const index = currentState.convertibleTokens.indexOf(convertibleToken); + if (currentState.smartTokens[index].length == 1) { + currentState.smartTokens.splice(index, 1); + swapLast(currentState.convertibleTokens, convertibleToken); + } + else { + swapLast(currentState.smartTokens[index], smartToken); + } + return await converterRegistry.removeConvertibleToken(convertibleToken, smartToken); + } + + function swapLast(array, item) { + array[array.indexOf(item)] = array[array.length - 1]; + array.length -= 1; + } + }); +}); diff --git a/test/BancorConverterUpgrader.js b/test/BancorConverterUpgrader.js new file mode 100755 index 0000000..f580c12 --- /dev/null +++ b/test/BancorConverterUpgrader.js @@ -0,0 +1,274 @@ +/* global artifacts, contract, before, it, assert */ +/* eslint-disable prefer-reflect */ + +const utils = require('./helpers/Utils'); +const BancorConverter = require('./helpers/BancorConverter'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const SmartToken = artifacts.require('SmartToken'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const ContractFeatures = artifacts.require('ContractFeatures'); +const ERC20Token = artifacts.require('ERC20Token'); +const Whitelist = artifacts.require('Whitelist'); +const BancorConverterFactory = artifacts.require('BancorConverterFactory'); +const BancorConverterUpgrader = artifacts.require('BancorConverterUpgrader'); + +const versions = [9, 10, 11]; + +let token; +let contractRegistry; +let contractFeatures; +let converterUpgrader; + +async function initConverter(accounts, activate, version = null, maxConversionFee = 30000) { + token = await SmartToken.new('Token1', 'TKN1', 18); + let tokenAddress = token.address; + + let connectorToken = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 100000); + let connectorTokenAddress = connectorToken.address; + + let connectorToken2 = await ERC20Token.new('ERC Token 2', 'ERC2', 0, 200000); + let connectorTokenAddress2 = connectorToken2.address; + + let converter = await BancorConverter.new(tokenAddress, contractRegistry.address, maxConversionFee, connectorTokenAddress, 500000, version); + let converterAddress = converter.address; + await converter.addConnector(connectorTokenAddress2, 150000, false); + + await token.issue(accounts[0], 20000); + await connectorToken.transfer(converterAddress, 5000); + await connectorToken2.transfer(converterAddress, 8000); + await converter.setConversionFee(1000); + + if (activate) { + await token.transferOwnership(converterAddress); + await converter.acceptTokenOwnership(); + } + + return converter; +} + +async function upgradeConverter(converter, version = null) { + let newConverter; + + // for the latest version, we just call upgrade on the converter + if (!version) { + await converter.upgrade(); + newConverter = await getNewConverter(); + } + else { + // for previous versions we transfer ownership to the upgrader, then call upgradeOld on the upgrader, + // then accept ownership of the new and old converter. The end results should be the same. + await converter.transferOwnership(converterUpgrader.address); + await converterUpgrader.upgradeOld(converter.address, web3.fromAscii(`0.${version}`)); + newConverter = await getNewConverter(); + await converter.acceptOwnership(); + } + + return newConverter; +} + +async function getNewConverter() { + let converterUpgrade = converterUpgrader.ConverterUpgrade({fromBlock: 'latest', toBlock: 'latest'}); + newConverterAddress = await new Promise((resolve, reject) => { + converterUpgrade.get((error, logs) => { + assert(logs.length == 1); + resolve(logs[0].args._newConverter); + }); + }); + + let converter = await BancorConverter.at(newConverterAddress); + return converter; +} + +contract('BancorConverterUpgrader', accounts => { + before(async () => { + contractRegistry = await ContractRegistry.new(); + + contractFeatures = await ContractFeatures.new(); + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_FEATURES, contractFeatures.address); + + let converterFactory = await BancorConverterFactory.new(); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_FACTORY, converterFactory.address); + + converterUpgrader = await BancorConverterUpgrader.new(contractRegistry.address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_UPGRADER, converterUpgrader.address); + }); + + it('verifies that the ownership of the converter is returned to the original owner after upgrade', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let initialOwner = await converter.owner.call(); + await upgradeConverter(converter, versions[i]) + let currentOwner = await converter.owner.call(); + assert.equal(initialOwner, currentOwner); + } + }); + + it('verifies that the ownership of the new converter is transfered to the old converter owner', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let initialOwner = await converter.owner.call(); + let newConverter = await upgradeConverter(converter, versions[i]) + + let newOwner = await newConverter.newOwner.call(); + assert.equal(initialOwner, newOwner); + } + }); + + it('verifies that the token ownership held by the converter is transfered to the new converter', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let tokenAddress = await converter.token.call(); + let token1 = await SmartToken.at(tokenAddress) + let initialTokenOwner = await token1.owner.call(); + assert.equal(initialTokenOwner, converter.address); + let newConverter = await upgradeConverter(converter, versions[i]); + let currentTokenOwner = await token1.owner.call(); + assert.equal(currentTokenOwner, newConverter.address); + } + }); + + it('verifies that the management of the new converter is transfered to the old converter owner', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let initialManager = await converter.manager.call(); + let newConverter = await upgradeConverter(converter, versions[i]); + let newManager = await newConverter.newManager.call(); + assert.equal(initialManager, newManager); + } + }); + + it('verifies that the whitelist feature is enabled in the new converter', async () => { + let converter = await initConverter(accounts, true); + await converter.upgrade(); + let newConverter = await getNewConverter(); + + let featureWhitelist = await newConverter.CONVERTER_CONVERSION_WHITELIST.call(); + let isSupported = await contractFeatures.isSupported.call(newConverter.address, featureWhitelist); + assert(isSupported); + }); + + it('verifies that the whitelist from the converter is copied to the new converter', async () => { + let converter = await initConverter(accounts, true); + let whitelist = await Whitelist.new(); + await converter.setConversionWhitelist(whitelist.address); + await converter.upgrade(); + let newConverter = await getNewConverter(); + let conversionWhitelist = await newConverter.conversionWhitelist.call(); + assert.equal(whitelist.address, conversionWhitelist); + }); + + it('should throw if the upgrader did not receive the converter ownership before calling the upgrade function', async () => { + let converter = await initConverter(accounts, true); + await utils.catchRevert(converterUpgrader.upgradeOld(converter.address, web3.fromUtf8("0.7"))); + }); + + it('verifies that the max conversion fee after upgrade is the same', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i], 20000); + let newConverter = await upgradeConverter(converter, versions[i]); + let newVal = await newConverter.maxConversionFee.call(); + assert.equal(newVal.toFixed(), "20000"); + } + + }); + + it('verifies that the conversion fee after upgrade is the same', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let initialConversionFee = await converter.conversionFee.call(); + let newConverter = await upgradeConverter(converter, versions[i]); + let currentConversionFee = await newConverter.conversionFee.call(); + assert.equal(initialConversionFee.toFixed(), currentConversionFee.toFixed()); + } + }); + + it('verifies that the ownership did not change if the process stopped due to gas limitation', async () => { + let converter = await initConverter(accounts, true); + let initialOwner = await converter.owner.call(); + + await utils.catchRevert(converter.upgrade({ gas: 2000000 })); + let currentOwner = await converter.owner.call(); + assert.equal(initialOwner, currentOwner); + }); + + it('verifies upgrade of converter without connectors', async () => { + let token1 = await SmartToken.new('Token1', 'TKN1', 18); + let converter1 = await BancorConverter.new(token1.address, contractRegistry.address, 30000, '0x0', 0); + await token1.issue(accounts[0], 20000); + await token1.transferOwnership(converter1.address); + await converter1.acceptTokenOwnership(); + let currentMaxConversionFee = await converter1.maxConversionFee.call(); + await converter1.upgrade(); + let newConverter = await getNewConverter(); + let newConverterConnectorTokenCount = await newConverter.connectorTokenCount.call(); + assert.equal(newConverterConnectorTokenCount.toFixed(), 0); + let newTokenOwner = await token1.owner.call(); + assert.equal(newTokenOwner, newConverter.address); + let newMaxConversionFee = await newConverter.maxConversionFee.call(); + assert.equal(currentMaxConversionFee.toFixed(), newMaxConversionFee.toFixed()); + }); + + it('verifies that the connectors count after an upgrade is the same', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let currentConverterConnectorTokenCount = await converter.connectorTokenCount.call(); + let newConverter = await upgradeConverter(converter, versions[i]); + let newConverterConnectorTokenCount = await newConverter.connectorTokenCount.call(); + assert.equal(currentConverterConnectorTokenCount.toFixed(), newConverterConnectorTokenCount.toFixed()); + } + }); + + it('verifies that the connector balances after upgrade is equal', async () => { + for (let i = 0; i < versions.length; i++) { + let converter = await initConverter(accounts, true, versions[i]); + let connector1 = await converter.connectorTokens.call(0); + let connector2 = await converter.connectorTokens.call(1); + let initialConnectorBalance1 = await converter.getConnectorBalance.call(connector1); + let initialConnectorBalance2 = await converter.getConnectorBalance.call(connector2); + let newConverter = await upgradeConverter(converter, versions[i]); + let currentConnectorBalance1 = await newConverter.getConnectorBalance.call(connector1); + let currentConnectorBalance2 = await newConverter.getConnectorBalance.call(connector2); + assert.equal(initialConnectorBalance1.toFixed(), currentConnectorBalance1.toFixed()); + assert.equal(initialConnectorBalance2.toFixed(), currentConnectorBalance2.toFixed()); + } + }); + + it('verifies that balances did not change if the process stopped due to gas limitation', async () => { + let converter = await initConverter(accounts, true); + let connector1 = await converter.connectorTokens.call(0); + let connector2 = await converter.connectorTokens.call(1); + let initialConnectorBalance1 = await converter.getConnectorBalance.call(connector1); + let initialConnectorBalance2 = await converter.getConnectorBalance.call(connector2); + + await utils.catchRevert(converter.upgrade({ gas: 2000000 })); + let currentConnectorBalance1 = await converter.getConnectorBalance.call(connector1); + let currentConnectorBalance2 = await converter.getConnectorBalance.call(connector2); + assert.equal(initialConnectorBalance1.toFixed(), currentConnectorBalance1.toFixed()); + assert.equal(initialConnectorBalance2.toFixed(), currentConnectorBalance2.toFixed()); + }); + + it('verifies upgrade of a non active converter', async () => { + let token1 = await SmartToken.new('Token1', 'TKN1', 18); + let connectorToken = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 100000); + let connectorTokenAddress = connectorToken.address; + let connectorToken2 = await ERC20Token.new('ERC Token 2', 'ERC2', 0, 200000); + let connectorTokenAddress2 = connectorToken2.address; + let converter1 = await BancorConverter.new(token1.address, contractRegistry.address, 30000, connectorTokenAddress, 500000); + await converter1.addConnector(connectorTokenAddress2, 500000, false); + await connectorToken.transfer(converter1.address, 5000); + await connectorToken2.transfer(converter1.address, 8000); + await token1.issue(accounts[0], 20000); + let currentMaxConversionFee = await converter1.maxConversionFee.call(); + await converter1.upgrade(); + let newConverter = await getNewConverter(); + let newConverterConnectorTokenCount = await newConverter.connectorTokenCount.call(); + assert.equal(newConverterConnectorTokenCount.toFixed(), 2); + let newMaxConversionFee = await newConverter.maxConversionFee.call(); + assert.equal(currentMaxConversionFee.toFixed(), newMaxConversionFee.toFixed()); + let currentConnectorBalance1 = await newConverter.getConnectorBalance.call(connectorToken.address); + let currentConnectorBalance2 = await newConverter.getConnectorBalance.call(connectorToken2.address); + assert.equal(currentConnectorBalance1.toFixed(), 5000); + assert.equal(currentConnectorBalance2.toFixed(), 8000); + }); +}); diff --git a/test/BancorFormula.js b/test/BancorFormula.js new file mode 100755 index 0000000..6dca9ec --- /dev/null +++ b/test/BancorFormula.js @@ -0,0 +1,211 @@ +/* global artifacts, contract, it, before, assert, web3 */ +/* eslint-disable prefer-reflect, no-loop-func */ + +let constants = require('./helpers/FormulaConstants'); +let catchRevert = require('./helpers/Utils').catchRevert; +let TestBancorFormula = artifacts.require('./helpers/TestBancorFormula'); + +contract('BancorFormula', () => { + let formula; + before(async () => { + formula = await TestBancorFormula.new(); + }); + + let ILLEGAL_VAL = web3.toBigNumber(2).toPower(256); + let MAX_BASE_N = web3.toBigNumber(2).toPower(256 - constants.MAX_PRECISION).minus(1); + let MIN_BASE_D = web3.toBigNumber(1); + let MAX_EXPONENT = 1000000; + + for (let percent = 1; percent <= 100; percent++) { + let baseN = MAX_BASE_N; + let baseD = MAX_BASE_N.minus(1); + let expN = MAX_EXPONENT * percent / 100; + let expD = MAX_EXPONENT; + let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`; + + it(`${test}`, async () => { + await formula.powerTest(baseN, baseD, expN, expD); + }); + } + + for (let percent = 1; percent <= 100; percent++) { + let baseN = MAX_BASE_N; + let baseD = MAX_BASE_N.minus(1); + let expN = MAX_EXPONENT; + let expD = MAX_EXPONENT * percent / 100; + let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`; + + it(`${test}`, async () => { + await formula.powerTest(baseN, baseD, expN, expD); + }); + } + + for (let percent = 1; percent <= 100; percent++) { + let baseN = MAX_BASE_N; + let baseD = MIN_BASE_D; + let expN = MAX_EXPONENT * percent / 100; + let expD = MAX_EXPONENT; + let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`; + + it(`${test}`, async () => { + if (percent < 64) + await formula.powerTest(baseN, baseD, expN, expD); + else + await catchRevert(formula.powerTest(baseN, baseD, expN, expD)); + }); + } + + for (let percent = 1; percent <= 100; percent++) { + let baseN = MAX_BASE_N; + let baseD = MIN_BASE_D; + let expN = MAX_EXPONENT; + let expD = MAX_EXPONENT * percent / 100; + let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`; + + it(`${test}`, async () => { + await catchRevert(formula.powerTest(baseN, baseD, expN, expD)); + }); + } + + let values = [ + MAX_BASE_N.dividedToIntegerBy(MIN_BASE_D), + MAX_BASE_N.dividedToIntegerBy(MAX_BASE_N.minus(1)), + MIN_BASE_D.plus(1).dividedToIntegerBy(MIN_BASE_D), + ]; + + for (let index = 0; index < values.length; index++) { + let test = `Function generalLog(0x${values[index].toString(16)})`; + + it(`${test}`, async () => { + let retVal = await formula.generalLogTest(values[index]); + assert(retVal.times(MAX_EXPONENT).lessThan(ILLEGAL_VAL), `output is too large`); + }); + } + + for (let precision = constants.MIN_PRECISION; precision <= constants.MAX_PRECISION; precision++) { + let maxExp = web3.toBigNumber(constants.maxExpArray[precision]); + let shlVal = web3.toBigNumber(2).toPower(constants.MAX_PRECISION - precision); + let tuples = [ + {'input' : maxExp.plus(0).times(shlVal).minus(1), 'output' : web3.toBigNumber(precision-0)}, + {'input' : maxExp.plus(0).times(shlVal).minus(0), 'output' : web3.toBigNumber(precision-0)}, + {'input' : maxExp.plus(1).times(shlVal).minus(1), 'output' : web3.toBigNumber(precision-0)}, + {'input' : maxExp.plus(1).times(shlVal).minus(0), 'output' : web3.toBigNumber(precision-1)}, + ]; + + for (let index = 0; index < tuples.length; index++) { + let input = tuples[index]['input' ]; + let output = tuples[index]['output']; + let test = `Function findPositionInMaxExpArray(0x${input.toString(16)})`; + + it(`${test}`, async () => { + if (precision == constants.MIN_PRECISION && output.lessThan(web3.toBigNumber(precision))) { + await catchRevert(formula.findPositionInMaxExpArrayTest(input)); + } + else { + let retVal = await formula.findPositionInMaxExpArrayTest(input); + assert(retVal.equals(output), `output should be ${output.toString(10)} but it is ${retVal.toString(10)}`); + } + }); + } + } + + for (let precision = constants.MIN_PRECISION; precision <= constants.MAX_PRECISION; precision++) { + let maxExp = web3.toBigNumber(constants.maxExpArray[precision]); + let maxVal = web3.toBigNumber(constants.maxValArray[precision]); + let errExp = maxExp.plus(1); + let test1 = `Function generalExp(0x${maxExp.toString(16)}, ${precision})`; + let test2 = `Function generalExp(0x${errExp.toString(16)}, ${precision})`; + + it(`${test1}`, async () => { + let retVal = await formula.generalExpTest(maxExp, precision); + assert(retVal.equals(maxVal), `output is wrong`); + }); + + it(`${test2}`, async () => { + let retVal = await formula.generalExpTest(errExp, precision); + assert(retVal.lessThan(maxVal), `output indicates that maxExpArray[${precision}] is wrong`); + }); + } + + for (let precision = constants.MIN_PRECISION; precision <= constants.MAX_PRECISION; precision++) { + let minExp = web3.toBigNumber(constants.maxExpArray[precision-1]).plus(1); + let minVal = web3.toBigNumber(2).toPower(precision); + let test = `Function generalExp(0x${minExp.toString(16)}, ${precision})`; + + it(`${test}`, async () => { + let retVal = await formula.generalExpTest(minExp, precision); + assert(retVal.greaterThanOrEqualTo(minVal), `output is too small`); + }); + } + + for (let n = 1; n <= 255; n++) { + let tuples = [ + {'input' : web3.toBigNumber(2).toPower(n) , 'output' : web3.toBigNumber(n)}, + {'input' : web3.toBigNumber(2).toPower(n).plus(1) , 'output' : web3.toBigNumber(n)}, + {'input' : web3.toBigNumber(2).toPower(n+1).minus(1), 'output' : web3.toBigNumber(n)}, + ]; + + for (let index = 0; index < tuples.length; index++) { + let input = tuples[index]['input' ]; + let output = tuples[index]['output']; + let test = `Function floorLog2(0x${input.toString(16)})`; + + it(`${test}`, async () => { + let retVal = await formula.floorLog2Test(input); + assert(retVal.equals(output), `output should be ${output.toString(10)} but it is ${retVal.toString(10)}`); + }); + } + } + + let Decimal = require("decimal.js"); + Decimal.set({precision: 100, rounding: Decimal.ROUND_DOWN}); + web3.BigNumber.config({DECIMAL_PLACES: 100, ROUNDING_MODE: web3.BigNumber.ROUND_DOWN}); + + let LOG_MIN = 1; + let EXP_MIN = 0; + let LOG_MAX = web3.toBigNumber(Decimal.exp(1).toFixed()); + let EXP_MAX = web3.toBigNumber(Decimal.pow(2,4).toFixed()); + let FIXED_1 = web3.toBigNumber(2).toPower(constants.MAX_PRECISION); + + for (let percent = 0; percent < 100; percent++) { + let x = web3.toBigNumber(percent).dividedBy(100).times(LOG_MAX.minus(LOG_MIN)).plus(LOG_MIN); + + it(`Function optimalLog(${x.toFixed()})`, async () => { + let fixedPoint = await formula.optimalLogTest(FIXED_1.times(x).truncated()); + let floatPoint = web3.toBigNumber(Decimal(x.toFixed()).ln().times(FIXED_1.toFixed()).toFixed()); + let ratio = fixedPoint.equals(floatPoint) ? web3.toBigNumber(1) : fixedPoint.dividedBy(floatPoint); + assert(ratio.greaterThanOrEqualTo("0.99999999999999999999999999999999999") && ratio.lessThanOrEqualTo("1"), `ratio = ${ratio.toFixed()}`); + }); + } + + for (let percent = 0; percent < 100; percent++) { + let x = web3.toBigNumber(percent).dividedBy(100).times(EXP_MAX.minus(EXP_MIN)).plus(EXP_MIN); + + it(`Function optimalExp(${x.toFixed()})`, async () => { + let fixedPoint = await formula.optimalExpTest(FIXED_1.times(x).truncated()); + let floatPoint = web3.toBigNumber(Decimal(x.toFixed()).exp().times(FIXED_1.toFixed()).toFixed()); + let ratio = fixedPoint.equals(floatPoint) ? web3.toBigNumber(1) : fixedPoint.dividedBy(floatPoint); + assert(ratio.greaterThanOrEqualTo("0.99999999999999999999999999999999999") && ratio.lessThanOrEqualTo("1"), `ratio = ${ratio.toFixed()}`); + }); + } + + for (let n = 0; n < 256 - constants.MAX_PRECISION; n++) { + let values = [ + web3.toBigNumber(2).toPower(n), + web3.toBigNumber(2).toPower(n).plus(1), + web3.toBigNumber(2).toPower(n).times(1.5), + web3.toBigNumber(2).toPower(n+1).minus(1), + ]; + + for (let index = 0; index < values.length; index++) { + let x = values[index]; + + it(`Function generalLog(${x.toFixed()})`, async () => { + let fixedPoint = await formula.generalLogTest(FIXED_1.times(x).truncated()); + let floatPoint = web3.toBigNumber(Decimal(x.toFixed()).ln().times(FIXED_1.toFixed()).toFixed()); + let ratio = fixedPoint.equals(floatPoint) ? web3.toBigNumber(1) : fixedPoint.dividedBy(floatPoint); + assert(ratio.greaterThanOrEqualTo("0.99999999999999999999999999999999999") && ratio.lessThanOrEqualTo("1"), `ratio = ${ratio.toFixed()}`); + }); + } + } +}); diff --git a/test/BancorNetwork.js b/test/BancorNetwork.js new file mode 100755 index 0000000..5070be9 --- /dev/null +++ b/test/BancorNetwork.js @@ -0,0 +1,991 @@ +/* global artifacts, contract, before, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const Whitelist = artifacts.require('Whitelist'); +const BancorNetwork = artifacts.require('BancorNetwork'); +const BancorConverter = artifacts.require('BancorConverter'); +const SmartToken = artifacts.require('SmartToken'); +const NonStandardSmartToken = artifacts.require('NonStandardSmartToken'); +const BancorFormula = artifacts.require('BancorFormula'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const ContractFeatures = artifacts.require('ContractFeatures'); +const EtherToken = artifacts.require('EtherToken'); +const TestNonStandardERC20Token = artifacts.require('TestNonStandardERC20Token'); + +let etherToken; +let smartToken1; +let smartToken2; +let smartToken3; +let smartToken4; +let erc20Token; +let contractRegistry; +let converter1; +let converter2; +let converter3; +let converter4; +let bancorNetwork; +let smartToken1BuyPath; +let smartToken2BuyPath; +let smartToken1SellPath; +let smartToken2SellPath; + +/* +Token network structure: + + SmartToken2 + / \ + SmartToken1 SmartToken3 + \ \ + \ SmartToken4 + \ / \ + EtherToken ERC20Token + +*/ + +contract('BancorNetwork', accounts => { + const trustedAddress = accounts[3]; + const untrustedAddress = accounts[1]; + + before(async () => { + contractRegistry = await ContractRegistry.new(); + + let contractFeatures = await ContractFeatures.new(); + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_FEATURES, contractFeatures.address); + + let bancorFormula = await BancorFormula.new(); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_FORMULA, bancorFormula.address); + + bancorNetwork = await BancorNetwork.new(contractRegistry.address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_NETWORK, bancorNetwork.address); + + etherToken = await EtherToken.new('Token0', 'TKN0'); + await etherToken.deposit({ value: 10000000 }); + + await bancorNetwork.registerEtherToken(etherToken.address, true); + + smartToken1 = await SmartToken.new('Token1', 'TKN1', 2); + await smartToken1.issue(accounts[0], 1000000); + + smartToken2 = await NonStandardSmartToken.new('Token2', 'TKN2', 2); + await smartToken2.issue(accounts[0], 2000000); + + smartToken3 = await SmartToken.new('Token3', 'TKN3', 2); + await smartToken3.issue(accounts[0], 3000000); + + smartToken4 = await SmartToken.new('Token4', 'TKN4', 2); + await smartToken4.issue(accounts[0], 2500000); + + await contractRegistry.registerAddress(ContractRegistryClient.BNT_TOKEN, smartToken1.address); + + erc20Token = await TestNonStandardERC20Token.new('ERC20Token', 'ERC5', 1000000); + + converter1 = await BancorConverter.new(smartToken1.address, contractRegistry.address, 0, etherToken.address, 250000); + + converter2 = await BancorConverter.new(smartToken2.address, contractRegistry.address, 0, smartToken1.address, 300000); + await converter2.addReserve(smartToken3.address, 150000); + + converter3 = await BancorConverter.new(smartToken3.address, contractRegistry.address, 0, smartToken4.address, 350000); + + converter4 = await BancorConverter.new(smartToken4.address, contractRegistry.address, 0, etherToken.address, 150000); + await converter4.addReserve(erc20Token.address, 220000); + + await etherToken.transfer(converter1.address, 50000); + await smartToken1.transfer(converter2.address, 40000); + await smartToken3.transfer(converter2.address, 25000); + await smartToken4.transfer(converter3.address, 30000); + await etherToken.transfer(converter4.address, 20000); + await erc20Token.transfer(converter4.address, 35000); + + await smartToken1.transferOwnership(converter1.address); + await converter1.acceptTokenOwnership(); + + await smartToken2.transferOwnership(converter2.address); + await converter2.acceptTokenOwnership(); + + await smartToken3.transferOwnership(converter3.address); + await converter3.acceptTokenOwnership(); + + await smartToken4.transferOwnership(converter4.address); + await converter4.acceptTokenOwnership(); + + smartToken1BuyPath = [etherToken.address, smartToken1.address, smartToken1.address]; + smartToken2BuyPath = [etherToken.address, smartToken1.address, smartToken1.address, smartToken2.address, smartToken2.address]; + + smartToken1SellPath = [smartToken1.address, smartToken1.address, etherToken.address]; + smartToken2SellPath = [smartToken2.address, smartToken2.address, smartToken1.address, smartToken1.address, etherToken.address]; + }); + + it('verifies that sending ether to the converter fails', async () => { + await utils.catchRevert(converter2.send(100)); + }); + + it('should be able to quickConvert from a non compliant erc-20 to another token', async () => { + await erc20Token.approve(converter4.address, 1000); + let path = [erc20Token.address, smartToken4.address, smartToken4.address]; + let prevBalance = await smartToken4.balanceOf.call(accounts[0]); + await converter4.quickConvert(path, 1000, 1); + let postBalance = await smartToken4.balanceOf.call(accounts[0]); + + assert.isAbove(postBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should be able to quickConvert from a smart token to a non compliant erc-20', async () => { + let path = [smartToken4.address, smartToken4.address, erc20Token.address]; + let prevBalance = await erc20Token.balanceOf.call(accounts[0]); + await converter4.quickConvert(path, 1000, 1); + let postBalance = await erc20Token.balanceOf.call(accounts[0]); + + assert.isAbove(postBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('verifies that quick buy with a single converter results in increased balance for the buyer', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + let res = await converter1.quickConvert(smartToken1BuyPath, 100, 1, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + // console.log(`gas used for converting eth -> 1: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that quick buy with multiple converters results in increased balance for the buyer', async () => { + let prevBalance = await smartToken2.balanceOf.call(accounts[1]); + + let res = await converter2.quickConvert(smartToken2BuyPath, 100, 1, { from: accounts[1], value: 100 }); + let newBalance = await smartToken2.balanceOf.call(accounts[1]); + + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + // console.log(`gas used for converting eth -> 1 -> 2: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that quick buy with minimum return equal to the full expected return amount results in the exact increase in balance for the buyer', async () => { + let prevBalance = await smartToken2.balanceOf.call(accounts[0]); + + let token1Return = (await converter1.getPurchaseReturn(etherToken.address, 100000))[0]; + let token2Return = (await converter2.getPurchaseReturn(smartToken1.address, token1Return))[0]; + + await converter2.quickConvert(smartToken2BuyPath, 100000, token2Return, { value: 100000 }); + let newBalance = await smartToken2.balanceOf.call(accounts[0]); + + assert.equal(token2Return.toNumber(), newBalance.toNumber() - prevBalance.toNumber(), "new balance isn't equal to the expected purchase return"); + }); + + it('should throw when attempting to quick buy and the return amount is lower than the given minimum', async () => { + await utils.catchRevert(converter2.quickConvert(smartToken2BuyPath, 100, 1000000, { from: accounts[1], value: 100 })); + }); + + it('should throw when attempting to quick buy and passing an amount higher than the ETH amount sent with the request', async () => { + await utils.catchRevert(converter2.quickConvert(smartToken2BuyPath, 100001, 1, { from: accounts[1], value: 100000 })); + }); + + it('verifies the caller balances after selling directly for ether with a single converter', async () => { + let prevETHBalance = web3.eth.getBalance(accounts[0]); + let prevTokenBalance = await smartToken1.balanceOf.call(accounts[0]); + + let res = await converter1.quickConvert(smartToken1SellPath, 10000, 1); + let newETHBalance = web3.eth.getBalance(accounts[0]); + let newTokenBalance = await smartToken1.balanceOf.call(accounts[0]); + + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + assert(newETHBalance.greaterThan(prevETHBalance.minus(transactionCost)), "new ETH balance isn't higher than previous balance"); + assert(newTokenBalance.lessThan(prevTokenBalance), "new token balance isn't lower than previous balance"); + }); + + it('verifies the caller balances after selling directly for ether with multiple converters', async () => { + let prevETHBalance = web3.eth.getBalance(accounts[0]); + let prevTokenBalance = await smartToken2.balanceOf.call(accounts[0]); + + let res = await converter2.quickConvert(smartToken2SellPath, 10000, 1); + let newETHBalance = web3.eth.getBalance(accounts[0]); + let newTokenBalance = await smartToken2.balanceOf.call(accounts[0]); + + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + assert(newETHBalance.greaterThan(prevETHBalance.minus(transactionCost)), "new ETH balance isn't higher than previous balance"); + assert(newTokenBalance.lessThan(prevTokenBalance), "new token balance isn't lower than previous balance"); + }); + + it('should throw when attempting to sell directly for ether and the return amount is lower than the given minimum', async () => { + await utils.catchRevert(converter2.quickConvert(smartToken2SellPath, 10000, 20000)); + }); + + it('verifies the caller balances after converting from one token to another with multiple converters', async () => { + + let path = [smartToken1.address, + smartToken2.address, smartToken2.address, + smartToken2.address, smartToken3.address, + smartToken3.address, smartToken4.address]; + + let prevToken1Balance = await smartToken1.balanceOf.call(accounts[0]); + let prevToken4Balance = await smartToken4.balanceOf.call(accounts[0]); + + await converter1.quickConvert(path, 1000, 1); + let newToken1Balance = await smartToken1.balanceOf.call(accounts[0]); + let newToken4Balance = await smartToken4.balanceOf.call(accounts[0]); + + assert(newToken4Balance.greaterThan(prevToken4Balance), "bought token balance isn't higher than previous balance"); + assert(newToken1Balance.lessThan(prevToken1Balance), "sold token balance isn't lower than previous balance"); + }); + + it('verifies valid ether token registration', async () => { + let etherToken1 = await EtherToken.new('Token0', 'TKN0'); + await etherToken1.deposit({ value: 10000000 }); + let bancorNetwork1 = await BancorNetwork.new(contractRegistry.address); + await bancorNetwork1.registerEtherToken(etherToken1.address, true); + let validEtherToken = await bancorNetwork1.etherTokens.call(etherToken1.address); + assert.isTrue(validEtherToken, 'registered etherToken address verification'); + }); + + it('should throw when attempting register ether token with invalid address', async () => { + let bancorNetwork1 = await BancorNetwork.new(contractRegistry.address); + await utils.catchRevert(bancorNetwork1.registerEtherToken('0x0', true)); + }); + + it('should throw when non owner attempting register ether token', async () => { + let etherToken1 = await EtherToken.new('Token0', 'TKN0'); + await etherToken1.deposit({ value: 10000000 }); + let bancorNetwork1 = await BancorNetwork.new(contractRegistry.address); + await utils.catchRevert(bancorNetwork1.registerEtherToken(etherToken1.address, true, { from: accounts[1] })); + }); + + it('verifies valid ether token unregistration', async () => { + let etherToken1 = await EtherToken.new('Token0', 'TKN0'); + await etherToken1.deposit({ value: 10000000 }); + let bancorNetwork1 = await BancorNetwork.new(contractRegistry.address); + await bancorNetwork1.registerEtherToken(etherToken1.address, true); + let validEtherToken = await bancorNetwork1.etherTokens.call(etherToken1.address); + assert.isTrue(validEtherToken, 'registered etherToken address verification'); + await bancorNetwork1.registerEtherToken(etherToken1.address, false); + let validEtherToken2 = await bancorNetwork1.etherTokens.call(etherToken1.address); + assert.isNotTrue(validEtherToken2, 'unregistered etherToken address verification'); + }); + + it('should throw when non owner attempting to unregister ether token', async () => { + let etherToken1 = await EtherToken.new('Token0', 'TKN0'); + await etherToken1.deposit({ value: 10000000 }); + let bancorNetwork1 = await BancorNetwork.new(contractRegistry.address); + await bancorNetwork1.registerEtherToken(etherToken1.address, true); + let validEtherToken = await bancorNetwork1.etherTokens.call(etherToken1.address); + assert.isTrue(validEtherToken, 'registered etherToken address verification'); + await utils.catchRevert(bancorNetwork1.registerEtherToken(etherToken1.address, false, { from: accounts[1] })); + }); + + it('verifies that convertFor transfers the converted amount correctly', async () => { + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor(smartToken1BuyPath, 10000, 1, accounts[1], { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that convert transfers the converted amount correctly', async () => { + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convert(smartToken1BuyPath, 10000, 1, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies claimAndConvertFor with a path that starts with a smart token and ends with another smart token', async () => { + await smartToken4.approve(bancorNetwork.address, 10000); + let path = [smartToken4.address, smartToken3.address, smartToken3.address, smartToken2.address, smartToken2.address]; + let balanceBeforeTransfer = await smartToken2.balanceOf.call(accounts[1]); + await bancorNetwork.claimAndConvertFor(path, 10000, 1, accounts[1]); + let balanceAfterTransfer = await smartToken2.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that convertFor returns the valid converted amount', async () => { + let amount = await bancorNetwork.convertFor.call(smartToken1BuyPath, 10000, 1, accounts[1], { value: 10000 }); + assert.isAbove(amount.toNumber(), 1, 'amount converted'); + }); + + it('verifies that convert returns the valid converted amount', async () => { + let amount = await bancorNetwork.convert.call(smartToken1BuyPath, 10000, 1, { from: accounts[1], value: 10000 }); + assert.isAbove(amount.toNumber(), 1, 'amount converted'); + }); + + it('should throw when calling convertFor with ether token but without sending ether', async () => { + await utils.catchRevert(bancorNetwork.convertFor(smartToken1BuyPath, 10000, 1, accounts[1])); + }); + + it('should throw when calling convertFor with ether amount different than the amount sent', async () => { + await utils.catchRevert(bancorNetwork.convertFor.call(smartToken1BuyPath, 20000, 1, accounts[1], { value: 10000 })); + }); + + it('should throw when calling convertFor with invalid path', async () => { + let invalidPath = [etherToken.address, smartToken1.address]; + await utils.catchRevert(bancorNetwork.convertFor(invalidPath, 10000, 1, accounts[1], { value: 10000 })); + }); + + it('should throw when calling convertFor with invalid long path', async () => { + let longBuyPath = []; + for (let i = 0; i < 100; ++i) + longBuyPath.push(etherToken.address); + + await utils.catchRevert(bancorNetwork.convertFor(longBuyPath, 10000, 1, accounts[1], { value: 10000 })); + }); + + it('should throw when calling convert with ether token but without sending ether', async () => { + await utils.catchRevert(bancorNetwork.convert(smartToken1BuyPath, 10000, 1, { from: accounts[1] })); + }); + + it('should throw when calling convert with ether amount different than the amount sent', async () => { + await utils.catchRevert(bancorNetwork.convert.call(smartToken1BuyPath, 20000, 1, { from: accounts[1], value: 10000 })); + }); + + it('should throw when calling convert with invalid path', async () => { + let invalidPath = [etherToken.address, smartToken1.address]; + await utils.catchRevert(bancorNetwork.convert(invalidPath, 10000, 1, { from: accounts[1], value: 10000 })); + }); + + it('should throw when calling convert with invalid long path', async () => { + let longBuyPath = []; + for (let i = 0; i < 100; ++i) + longBuyPath.push(etherToken.address); + + await utils.catchRevert(bancorNetwork.convert(longBuyPath, 10000, 1, { from: accounts[1], value: 10000 })); + }); + + it('verifies that claimAndConvertFor transfers the converted amount correctly', async () => { + await etherToken.approve(bancorNetwork.address, 10000); + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.claimAndConvertFor(smartToken1BuyPath, 10000, 1, accounts[1]); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('should throw when calling claimAndConvertFor without approval', async () => { + await utils.catchRevert(bancorNetwork.claimAndConvertFor(smartToken1BuyPath, 10000, 1, accounts[1])); + }); + + it('verifies that claimAndConvert transfers the converted amount correctly', async () => { + await etherToken.approve(bancorNetwork.address, 10000); + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[0]); + await bancorNetwork.claimAndConvert(smartToken1BuyPath, 10000, 1); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[0]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('should throw when calling claimAndConvert without approval', async () => { + await utils.catchRevert(bancorNetwork.claimAndConvert(smartToken1BuyPath, 10000, 1)); + }); + + it('verifies that convertFor is allowed for a whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + await converter1.setConversionWhitelist(whitelist.address); + + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor(smartToken1BuyPath, 10000, 1, accounts[1], { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('should throw when calling convertFor with a non whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await converter1.setConversionWhitelist(whitelist.address); + + await utils.catchRevert(bancorNetwork.convertFor(smartToken1BuyPath, 10000, 1, accounts[1], { value: 10000 })); + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken1BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor(smartToken1BuyPath, 10000, 1, accounts[1], { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token through multiple converters', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken2BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken2.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor(smartToken2BuyPath, 10000, 1, accounts[1], { value: 10000 }); + let balanceAfterTransfer = await smartToken2.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that convert is allowed for a whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + await converter1.setConversionWhitelist(whitelist.address); + + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convert(smartToken1BuyPath, 10000, 1, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('should throw when calling convert with a non whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await converter1.setConversionWhitelist(whitelist.address); + + await utils.catchRevert(bancorNetwork.convert(smartToken1BuyPath, 10000, 1, { from: accounts[1], value: 10000 })); + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken1BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convert(smartToken1BuyPath, 10000, 1, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token through multiple converters', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken2BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken2.balanceOf.call(accounts[1]); + await bancorNetwork.convert(smartToken2BuyPath, 10000, 1, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken2.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for cross reserve conversion', async () => { + await converter2.quickConvert([etherToken.address, smartToken1.address, smartToken1.address], 1000, 1, { from: accounts[1], value: 1000 }); + await smartToken1.approve(converter2.address, 100, { from: accounts[1] }); + let path = [smartToken1.address, smartToken2.address, smartToken3.address]; + let returnByPath = (await bancorNetwork.getReturnByPath.call(path, 100))[0]; + let balanceBeforeTransfer = await smartToken3.balanceOf.call(accounts[1]); + await converter2.quickConvert(path, 100, 1, { from: accounts[1] }); + let balanceAfterTransfer = await smartToken3.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for selling the smart token', async () => { + await converter1.quickConvert(smartToken1BuyPath, 100, 1, { from: accounts[1], value: 100 }); + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken1SellPath, 100))[0]; + let balanceBeforeTransfer = web3.eth.getBalance(accounts[1]); + let res = await converter1.quickConvert(smartToken1SellPath, 100, 1, { from: accounts[1] }); + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + let balanceAfterTransfer = web3.eth.getBalance(accounts[1]); + assert.equal(returnByPath.toNumber(), balanceAfterTransfer.minus(balanceBeforeTransfer).plus(transactionCost).toNumber()); + }); + + it('verifies that getReturnByPath returns the correct amount for selling the smart token through multiple converters', async () => { + await converter2.quickConvert(smartToken2BuyPath, 100, 1, { from: accounts[1], value: 100 }); + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken2SellPath, 100))[0]; + let balanceBeforeTransfer = web3.eth.getBalance(accounts[1]); + let res = await converter2.quickConvert(smartToken2SellPath, 100, 1, { from: accounts[1] }); + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + let balanceAfterTransfer = web3.eth.getBalance(accounts[1]); + assert.equal(returnByPath.toNumber(), balanceAfterTransfer.minus(balanceBeforeTransfer).plus(transactionCost).toNumber()); + // console.log(`gas used for converting 2 -> 1 -> eth: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that getReturnByPath returns the correct amount for selling the smart token with a long conversion path', async () => { + await converter4.quickConvert([etherToken.address, smartToken1.address, smartToken1.address, smartToken2.address, smartToken3.address], 1000, 1, { from: accounts[1], value: 1000 }); + let path = [smartToken3.address, smartToken2.address, smartToken2.address, smartToken2.address, smartToken1.address, smartToken1.address, etherToken.address]; + let returnByPath = (await bancorNetwork.getReturnByPath.call(path, 100))[0]; + let balanceBeforeTransfer = web3.eth.getBalance(accounts[1]); + let res = await converter3.quickConvert(path, 100, 1, { from: accounts[1] }); + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + let balanceAfterTransfer = web3.eth.getBalance(accounts[1]); + assert.equal(returnByPath.toNumber(), balanceAfterTransfer.minus(balanceBeforeTransfer).plus(transactionCost).toNumber()); + // console.log(`gas used for converting 3 -> 2 -> 1 -> eth: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that getReturnByPath returns the same amount as getReturn when converting a reserve to the smart token', async () => { + let getReturn = (await converter2.getReturn.call(smartToken1.address, smartToken2.address, 100))[0]; + let returnByPath = (await bancorNetwork.getReturnByPath.call([smartToken1.address, smartToken2.address, smartToken2.address], 100))[0]; + assert.equal(getReturn.toNumber(), returnByPath.toNumber()); + }); + + it('verifies that getReturnByPath returns the same amount as getReturn when converting from a token to a reserve', async () => { + let getReturn = (await converter2.getReturn.call(smartToken2.address, smartToken1.address, 100))[0]; + let returnByPath = (await bancorNetwork.getReturnByPath.call([smartToken2.address, smartToken2.address, smartToken1.address], 100))[0]; + assert.equal(getReturn.toNumber(), returnByPath.toNumber()); + }); + + it('should throw when attempting to call getReturnByPath on a path with fewer than 3 elements', async () => { + let invalidPath = [etherToken.address, smartToken1.address]; + await utils.catchRevert(bancorNetwork.getReturnByPath.call(invalidPath, 1000)); + }); + + it('should throw when attempting to call getReturnByPath on a path with an odd number of elements', async () => { + let invalidPath = [etherToken.address, smartToken1.address, smartToken2.address, smartToken3.address]; + await utils.catchRevert(bancorNetwork.getReturnByPath.call(invalidPath, 1000)); + }); + + it('should throw when attempting to get the return by path with invalid long path', async () => { + let longBuyPath = []; + for (let i = 0; i < 103; ++i) + longBuyPath.push(etherToken.address); + + await utils.catchRevert(bancorNetwork.getReturnByPath.call(longBuyPath, 1000)); + }); + + it('verifies quickConvertPrioritized', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + await converter1.quickConvertPrioritized(smartToken1BuyPath, 100, 1, 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should throw when calling quickConvertPrioritized with wrong path', async () => { + let wrongPath = [etherToken.address, smartToken1.address, smartToken1.address, smartToken1.address, smartToken1.address]; + + await utils.catchRevert(converter1.quickConvertPrioritized(wrongPath, 100, 1, 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 })); + }); + + it('should throw when calling quickConvertPrioritized with wrong amount', async () => { + await utils.catchRevert(converter1.quickConvertPrioritized(smartToken1BuyPath, 200, 1, 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 })); + }); + + it('verifies convertForPrioritized2', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + await bancorNetwork.convertForPrioritized2(smartToken1BuyPath, 100, 1, accounts[1], 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should throw when calling convertForPrioritized2 with wrong path', async () => { + let wrongPath = [etherToken.address, smartToken1.address, smartToken1.address, smartToken1.address, smartToken1.address]; + + await utils.catchRevert(bancorNetwork.convertForPrioritized2(wrongPath, 100, 1, accounts[1], 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 })); + }); + + it('should throw when calling convertForPrioritized2 with wrong amount', async () => { + await utils.catchRevert(bancorNetwork.convertForPrioritized2(smartToken1BuyPath, 200, 1, accounts[1], 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 })); + }); + + it('verifies convertForPrioritized3', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + await bancorNetwork.convertForPrioritized3(smartToken1BuyPath, 100, 1, accounts[1], 0, 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should throw when calling convertForPrioritized3 with wrong path', async () => { + let wrongPath = [etherToken.address, smartToken1.address, smartToken1.address, smartToken1.address, smartToken1.address]; + + await utils.catchRevert(bancorNetwork.convertForPrioritized3(wrongPath, 100, 1, accounts[1], 100, 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 })); + }); + + it('should throw when calling convertForPrioritized3 with wrong amount', async () => { + await utils.catchRevert(bancorNetwork.convertForPrioritized3(smartToken1BuyPath, 200, 1, accounts[1], 200, 0, 0, utils.zeroBytes32, utils.zeroBytes32, { from: accounts[1], value: 100 })); + }); + + it('verifies that convertFor2 transfers the converted amount correctly', async () => { + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that convert2 transfers the converted amount correctly', async () => { + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies claimAndConvertFor2 with a path that starts with a smart token and ends with another smart token', async () => { + await smartToken4.approve(bancorNetwork.address, 10000); + let path = [smartToken4.address, smartToken3.address, smartToken3.address, smartToken2.address, smartToken2.address]; + let balanceBeforeTransfer = await smartToken2.balanceOf.call(accounts[1]); + await bancorNetwork.claimAndConvertFor2(path, 10000, 1, accounts[1], utils.zeroAddress, 0); + let balanceAfterTransfer = await smartToken2.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that convertFor2 returns the valid converted amount', async () => { + let amount = await bancorNetwork.convertFor2.call(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 }); + assert.isAbove(amount.toNumber(), 1, 'amount converted'); + }); + + it('verifies that convert2 returns the valid converted amount', async () => { + let amount = await bancorNetwork.convert2.call(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 }); + assert.isAbove(amount.toNumber(), 1, 'amount converted'); + }); + + it('should throw when calling convertFor2 with ether token but without sending ether', async () => { + await utils.catchRevert(bancorNetwork.convertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0)); + }); + + it('should throw when calling convertFor2 with ether amount different than the amount sent', async () => { + await utils.catchRevert(bancorNetwork.convertFor2.call(smartToken1BuyPath, 20000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 })); + }); + + it('should throw when calling convertFor2 with invalid path', async () => { + let invalidPath = [etherToken.address, smartToken1.address]; + await utils.catchRevert(bancorNetwork.convertFor2(invalidPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 })); + }); + + it('should throw when calling convertFor2 with invalid long path', async () => { + let longBuyPath = []; + for (let i = 0; i < 100; ++i) + longBuyPath.push(etherToken.address); + + await utils.catchRevert(bancorNetwork.convertFor2(longBuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 })); + }); + + it('should throw when calling convert2 with ether token but without sending ether', async () => { + await utils.catchRevert(bancorNetwork.convert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1] })); + }); + + it('should throw when calling convert2 with ether amount different than the amount sent', async () => { + await utils.catchRevert(bancorNetwork.convert2.call(smartToken1BuyPath, 20000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 })); + }); + + it('should throw when calling convert2 with invalid path', async () => { + let invalidPath = [etherToken.address, smartToken1.address]; + await utils.catchRevert(bancorNetwork.convert2(invalidPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 })); + }); + + it('should throw when calling convert2 with invalid long path', async () => { + let longBuyPath = []; + for (let i = 0; i < 100; ++i) + longBuyPath.push(etherToken.address); + + await utils.catchRevert(bancorNetwork.convert2(longBuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 })); + }); + + it('verifies that claimAndConvertFor2 transfers the converted amount correctly', async () => { + await etherToken.approve(bancorNetwork.address, 10000); + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.claimAndConvertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('should throw when calling claimAndConvertFor2 without approval', async () => { + await utils.catchRevert(bancorNetwork.claimAndConvertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0)); + }); + + it('verifies that claimAndConvert2 transfers the converted amount correctly', async () => { + await etherToken.approve(bancorNetwork.address, 10000); + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[0]); + await bancorNetwork.claimAndConvert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[0]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('should throw when calling claimAndConvert2 without approval', async () => { + await utils.catchRevert(bancorNetwork.claimAndConvert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0)); + }); + + it('verifies that convertFor2 is allowed for a whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + await converter1.setConversionWhitelist(whitelist.address); + + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('should throw when calling convertFor2 with a non whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await converter1.setConversionWhitelist(whitelist.address); + + await utils.catchRevert(bancorNetwork.convertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 })); + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken1BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor2(smartToken1BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token through multiple converters', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken2BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken2.balanceOf.call(accounts[1]); + await bancorNetwork.convertFor2(smartToken2BuyPath, 10000, 1, accounts[1], utils.zeroAddress, 0, { value: 10000 }); + let balanceAfterTransfer = await smartToken2.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that convert2 is allowed for a whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + await converter1.setConversionWhitelist(whitelist.address); + + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('should throw when calling convert2 with a non whitelisted account', async () => { + let whitelist = await Whitelist.new(); + await converter1.setConversionWhitelist(whitelist.address); + + await utils.catchRevert(bancorNetwork.convert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 })); + await converter1.setConversionWhitelist(utils.zeroAddress); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken1BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[1]); + await bancorNetwork.convert2(smartToken1BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for buying the smart token through multiple converters', async () => { + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken2BuyPath, 10000))[0]; + let balanceBeforeTransfer = await smartToken2.balanceOf.call(accounts[1]); + await bancorNetwork.convert2(smartToken2BuyPath, 10000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken2.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('should be able to quickConvert2 from a non compliant erc-20 to another token', async () => { + await erc20Token.approve(converter4.address, 1000); + let path = [erc20Token.address, smartToken4.address, smartToken4.address]; + let prevBalance = await smartToken4.balanceOf.call(accounts[0]); + await converter4.quickConvert2(path, 1000, 1, utils.zeroAddress, 0); + let postBalance = await smartToken4.balanceOf.call(accounts[0]); + + assert.isAbove(postBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should be able to quickConvert2 from a smart token to a non compliant erc-20', async () => { + let path = [smartToken4.address, smartToken4.address, erc20Token.address]; + let prevBalance = await erc20Token.balanceOf.call(accounts[0]); + await converter4.quickConvert2(path, 1000, 1, utils.zeroAddress, 0); + let postBalance = await erc20Token.balanceOf.call(accounts[0]); + + assert.isAbove(postBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('verifies that quick buy with a single converter results in increased balance for the buyer', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + let res = await converter1.quickConvert2(smartToken1BuyPath, 100, 1, utils.zeroAddress, 0, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + // console.log(`gas used for converting eth -> 1: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that quick buy with multiple converters results in increased balance for the buyer', async () => { + let prevBalance = await smartToken2.balanceOf.call(accounts[1]); + + let res = await converter2.quickConvert2(smartToken2BuyPath, 100, 1, utils.zeroAddress, 0, { from: accounts[1], value: 100 }); + let newBalance = await smartToken2.balanceOf.call(accounts[1]); + + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + // console.log(`gas used for converting eth -> 1 -> 2: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that quick buy with minimum return equal to the full expected return amount results in the exact increase in balance for the buyer', async () => { + let prevBalance = await smartToken2.balanceOf.call(accounts[0]); + + let token1Return = (await converter1.getPurchaseReturn(etherToken.address, 100000))[0]; + let token2Return = (await converter2.getPurchaseReturn(smartToken1.address, token1Return))[0]; + + await converter2.quickConvert2(smartToken2BuyPath, 100000, token2Return, utils.zeroAddress, 0, { value: 100000 }); + let newBalance = await smartToken2.balanceOf.call(accounts[0]); + + assert.equal(token2Return.toNumber(), newBalance.toNumber() - prevBalance.toNumber(), "new balance isn't equal to the expected purchase return"); + }); + + it('should throw when attempting to quick buy and the return amount is lower than the given minimum', async () => { + await utils.catchRevert(converter2.quickConvert2(smartToken2BuyPath, 100, 1000000, utils.zeroAddress, 0, { from: accounts[1], value: 100 })); + }); + + it('should throw when attempting to quick buy and passing an amount higher than the ETH amount sent with the request', async () => { + await utils.catchRevert(converter2.quickConvert2(smartToken2BuyPath, 100001, 1, utils.zeroAddress, 0, { from: accounts[1], value: 100000 })); + }); + + it('verifies the caller balances after selling directly for ether with a single converter', async () => { + let prevETHBalance = web3.eth.getBalance(accounts[0]); + let prevTokenBalance = await smartToken1.balanceOf.call(accounts[0]); + + let res = await converter1.quickConvert2(smartToken1SellPath, 10000, 1, utils.zeroAddress, 0); + let newETHBalance = web3.eth.getBalance(accounts[0]); + let newTokenBalance = await smartToken1.balanceOf.call(accounts[0]); + + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + assert(newETHBalance.greaterThan(prevETHBalance.minus(transactionCost)), "new ETH balance isn't higher than previous balance"); + assert(newTokenBalance.lessThan(prevTokenBalance), "new token balance isn't lower than previous balance"); + }); + + it('verifies the caller balances after selling directly for ether with multiple converters', async () => { + let prevETHBalance = web3.eth.getBalance(accounts[0]); + let prevTokenBalance = await smartToken2.balanceOf.call(accounts[0]); + + let res = await converter2.quickConvert2(smartToken2SellPath, 10000, 1, utils.zeroAddress, 0); + let newETHBalance = web3.eth.getBalance(accounts[0]); + let newTokenBalance = await smartToken2.balanceOf.call(accounts[0]); + + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + assert(newETHBalance.greaterThan(prevETHBalance.minus(transactionCost)), "new ETH balance isn't higher than previous balance"); + assert(newTokenBalance.lessThan(prevTokenBalance), "new token balance isn't lower than previous balance"); + }); + + it('should throw when attempting to sell directly for ether and the return amount is lower than the given minimum', async () => { + await utils.catchRevert(converter2.quickConvert2(smartToken2SellPath, 10000, 20000, utils.zeroAddress, 0)); + }); + + it('verifies the caller balances after converting from one token to another with multiple converters', async () => { + + let path = [smartToken1.address, + smartToken2.address, smartToken2.address, + smartToken2.address, smartToken3.address, + smartToken3.address, smartToken4.address]; + + let prevToken1Balance = await smartToken1.balanceOf.call(accounts[0]); + let prevToken4Balance = await smartToken4.balanceOf.call(accounts[0]); + + await converter1.quickConvert2(path, 1000, 1, utils.zeroAddress, 0); + let newToken1Balance = await smartToken1.balanceOf.call(accounts[0]); + let newToken4Balance = await smartToken4.balanceOf.call(accounts[0]); + + assert(newToken4Balance.greaterThan(prevToken4Balance), "bought token balance isn't higher than previous balance"); + assert(newToken1Balance.lessThan(prevToken1Balance), "sold token balance isn't lower than previous balance"); + }); + + it('verifies that getReturnByPath returns the correct amount for cross reserve conversion', async () => { + await converter2.quickConvert2([etherToken.address, smartToken1.address, smartToken1.address], 1000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 1000 }); + await smartToken1.approve(converter2.address, 100, { from: accounts[1] }); + let path = [smartToken1.address, smartToken2.address, smartToken3.address]; + let returnByPath = (await bancorNetwork.getReturnByPath.call(path, 100))[0]; + let balanceBeforeTransfer = await smartToken3.balanceOf.call(accounts[1]); + await converter2.quickConvert2(path, 100, 1, utils.zeroAddress, 0, { from: accounts[1] }); + let balanceAfterTransfer = await smartToken3.balanceOf.call(accounts[1]); + assert.equal(returnByPath, balanceAfterTransfer - balanceBeforeTransfer); + }); + + it('verifies that getReturnByPath returns the correct amount for selling the smart token', async () => { + await converter1.quickConvert2(smartToken1BuyPath, 100, 1, utils.zeroAddress, 0, { from: accounts[1], value: 100 }); + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken1SellPath, 100))[0]; + let balanceBeforeTransfer = web3.eth.getBalance(accounts[1]); + let res = await converter1.quickConvert2(smartToken1SellPath, 100, 1, utils.zeroAddress, 0, { from: accounts[1] }); + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + let balanceAfterTransfer = web3.eth.getBalance(accounts[1]); + assert.equal(returnByPath.toNumber(), balanceAfterTransfer.minus(balanceBeforeTransfer).plus(transactionCost).toNumber()); + }); + + it('verifies that getReturnByPath returns the correct amount for selling the smart token through multiple converters', async () => { + await converter2.quickConvert2(smartToken2BuyPath, 100, 1, utils.zeroAddress, 0, { from: accounts[1], value: 100 }); + let returnByPath = (await bancorNetwork.getReturnByPath.call(smartToken2SellPath, 100))[0]; + let balanceBeforeTransfer = web3.eth.getBalance(accounts[1]); + let res = await converter2.quickConvert2(smartToken2SellPath, 100, 1, utils.zeroAddress, 0, { from: accounts[1] }); + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + let balanceAfterTransfer = web3.eth.getBalance(accounts[1]); + assert.equal(returnByPath.toNumber(), balanceAfterTransfer.minus(balanceBeforeTransfer).plus(transactionCost).toNumber()); + // console.log(`gas used for converting 2 -> 1 -> eth: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies that getReturnByPath returns the correct amount for selling the smart token with a long conversion path', async () => { + await converter4.quickConvert2([etherToken.address, smartToken1.address, smartToken1.address, smartToken2.address, smartToken3.address], 1000, 1, utils.zeroAddress, 0, { from: accounts[1], value: 1000 }); + let path = [smartToken3.address, smartToken2.address, smartToken2.address, smartToken2.address, smartToken1.address, smartToken1.address, etherToken.address]; + let returnByPath = (await bancorNetwork.getReturnByPath.call(path, 100))[0]; + let balanceBeforeTransfer = web3.eth.getBalance(accounts[1]); + let res = await converter3.quickConvert2(path, 100, 1, utils.zeroAddress, 0, { from: accounts[1] }); + let transaction = web3.eth.getTransaction(res.tx); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + let balanceAfterTransfer = web3.eth.getBalance(accounts[1]); + assert.equal(returnByPath.toNumber(), balanceAfterTransfer.minus(balanceBeforeTransfer).plus(transactionCost).toNumber()); + // console.log(`gas used for converting 3 -> 2 -> 1 -> eth: ${res.receipt.cumulativeGasUsed}`); + }); + + it('verifies quickConvertPrioritized2', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + await converter1.quickConvertPrioritized2(smartToken1BuyPath, 100, 1, [], utils.zeroAddress, 0, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should throw when calling quickConvertPrioritized2 with wrong path', async () => { + let wrongPath = [etherToken.address, smartToken1.address, smartToken1.address, smartToken1.address, smartToken1.address]; + + await utils.catchRevert(converter1.quickConvertPrioritized2(wrongPath, 100, 1, [], utils.zeroAddress, 0, { from: accounts[1], value: 100 })); + }); + + it('should throw when calling quickConvertPrioritized2 with wrong amount', async () => { + await utils.catchRevert(converter1.quickConvertPrioritized2(smartToken1BuyPath, 200, 1, [], utils.zeroAddress, 0, { from: accounts[1], value: 100 })); + }); + + it('verifies convertForPrioritized4', async () => { + let prevBalance = await smartToken1.balanceOf.call(accounts[1]); + + await bancorNetwork.convertForPrioritized4(smartToken1BuyPath, 100, 1, accounts[1], [], utils.zeroAddress, 0, { from: accounts[1], value: 100 }); + let newBalance = await smartToken1.balanceOf.call(accounts[1]); + assert.isAbove(newBalance.toNumber(), prevBalance.toNumber(), "new balance isn't higher than previous balance"); + }); + + it('should throw when calling convertForPrioritized4 with wrong path', async () => { + let wrongPath = [etherToken.address, smartToken1.address, smartToken1.address, smartToken1.address, smartToken1.address]; + + await utils.catchRevert(bancorNetwork.convertForPrioritized4(wrongPath, 100, 1, accounts[1], [], utils.zeroAddress, 0, { from: accounts[1], value: 100 })); + }); + + it('should throw when calling convertForPrioritized4 with wrong amount', async () => { + await utils.catchRevert(bancorNetwork.convertForPrioritized4(smartToken1BuyPath, 200, 1, accounts[1], [], utils.zeroAddress, 0, { from: accounts[1], value: 100 })); + }); + + it('verifies that convertFor2 transfers the affiliate fee correctly', async () => { + let path = smartToken1BuyPath; + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[2]); + await bancorNetwork.convertFor2(path, 10000, 1, accounts[1], accounts[2], 10000, { value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[2]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that convert2 transfers the affiliate fee correctly', async () => { + let path = smartToken1BuyPath; + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[2]); + await bancorNetwork.convert2(path, 10000, 1, accounts[2], 10000, { from: accounts[1], value: 10000 }); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[2]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that claimAndConvert2 transfers the affiliate fee correctly', async () => { + let path = smartToken1BuyPath; + await etherToken.approve(bancorNetwork.address, 10000); + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[2]); + await bancorNetwork.claimAndConvert2(path, 10000, 1, accounts[2], 10000); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[2]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that claimAndConvertFor2 transfers the affiliate fee correctly', async () => { + await etherToken.approve(bancorNetwork.address, 10000); + let balanceBeforeTransfer = await smartToken1.balanceOf.call(accounts[2]); + await bancorNetwork.claimAndConvertFor2(smartToken1BuyPath, 10000, 1, accounts[1], accounts[2], 10000); + let balanceAfterTransfer = await smartToken1.balanceOf.call(accounts[2]); + assert.isAbove(balanceAfterTransfer.toNumber(), balanceBeforeTransfer.toNumber(), 'amount transfered'); + }); + + it('verifies that setMaxAffiliateFee can set the maximum affiliate-fee', async () => { + let oldMaxAffiliateFee = await bancorNetwork.maxAffiliateFee.call(); + await bancorNetwork.setMaxAffiliateFee(oldMaxAffiliateFee.plus(1)); + let newMaxAffiliateFee = await bancorNetwork.maxAffiliateFee.call(); + await bancorNetwork.setMaxAffiliateFee(oldMaxAffiliateFee); + assert.equal(newMaxAffiliateFee.toString(), oldMaxAffiliateFee.plus(1)); + }); + + it('should throw when calling setMaxAffiliateFee with a non-owner or an illegal value', async () => { + await utils.catchRevert(bancorNetwork.setMaxAffiliateFee("1000000", { from: accounts[1] })); + await utils.catchRevert(bancorNetwork.setMaxAffiliateFee("1000001", { from: accounts[0] })); + }); +}); diff --git a/test/BancorNetworkPathFinder.js b/test/BancorNetworkPathFinder.js new file mode 100755 index 0000000..eb363a5 --- /dev/null +++ b/test/BancorNetworkPathFinder.js @@ -0,0 +1,243 @@ +/* global artifacts, contract, before, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const EtherToken = artifacts.require('EtherToken'); +const SmartToken = artifacts.require('SmartToken'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const BancorConverter = artifacts.require('BancorConverter'); +const BancorConverterRegistry = artifacts.require('BancorConverterRegistry'); +const BancorConverterRegistryData = artifacts.require('BancorConverterRegistryData'); +const BancorNetworkPathFinder = artifacts.require('BancorNetworkPathFinder'); + +async function print(sourceToken, targetToken, path) { + const sourceSymbol = await SmartToken.at(sourceToken).symbol(); + const targetSymbol = await SmartToken.at(targetToken).symbol(); + const symbols = await Promise.all(path.map(token => SmartToken.at(token).symbol())); + console.log(`path from ${sourceSymbol} to ${targetSymbol} = [${symbols}]`); +} + +async function generatePath(sourceToken, targetToken, anchorToken, converterRegistry) { + const sourcePath = await getPath(sourceToken, anchorToken, converterRegistry); + const targetPath = await getPath(targetToken, anchorToken, converterRegistry); + return getShortestPath(sourcePath, targetPath); +} + +async function getPath(token, anchorToken, converterRegistry) { + if (token == anchorToken) + return [token]; + + const isSmartToken = await converterRegistry.isSmartToken(token); + const smartTokens = isSmartToken ? [token] : await converterRegistry.getConvertibleTokenSmartTokens(token); + for (const smartToken of smartTokens) { + const converter = BancorConverter.at(await SmartToken.at(smartToken).owner()); + const connectorTokenCount = await converter.connectorTokenCount(); + for (let i = 0; i < connectorTokenCount; i++) { + const connectorToken = await converter.connectorTokens(i); + if (connectorToken != token) { + const path = await getPath(connectorToken, anchorToken, converterRegistry); + if (path.length > 0) + return [token, smartToken, ...path]; + } + } + } + + return []; +} + +function getShortestPath(sourcePath, targetPath) { + if (sourcePath.length > 0 && targetPath.length > 0) { + let i = sourcePath.length - 1; + let j = targetPath.length - 1; + while (i >= 0 && j >= 0 && sourcePath[i] == targetPath[j]) { + i--; + j--; + } + + const path = []; + for (let m = 0; m <= i + 1; m++) + path.push(sourcePath[m]); + for (let n = j; n >= 0; n--) + path.push(targetPath[n]); + + let length = 0; + for (let p = 0; p < path.length; p += 1) { + for (let q = p + 2; q < path.length - p % 2; q += 2) { + if (path[p] == path[q]) + p = q; + } + path[length++] = path[p]; + } + + return path.slice(0, length); + } + + return []; +} + +contract('BancorNetworkPathFinder', accounts => { + let pathFinder; + let converter1; + let converter2; + let converter3; + let converter4; + let converter5; + let converter6; + let converter7; + let etherToken; + let smartToken1; + let smartToken2; + let smartToken3; + let smartToken4; + let smartToken5; + let smartToken6; + let smartToken7; + let smartToken8; + let smartToken9; + let smartTokenA; + let smartTokenB; + let smartTokenC; + let smartTokenD; + let smartTokenE; + let anchorToken; + let contractRegistry + let converterRegistry; + let converterRegistryData; + + before(async function() { + etherToken = await EtherToken.new('Token0', 'TKN0'); + smartToken1 = await SmartToken.new('Token1', 'TKN1', 18); + smartToken2 = await SmartToken.new('Token2', 'TKN2', 18); + smartToken3 = await SmartToken.new('Token3', 'TKN3', 18); + smartToken4 = await SmartToken.new('Token4', 'TKN4', 18); + smartToken5 = await SmartToken.new('Token5', 'TKN5', 18); + smartToken6 = await SmartToken.new('Token6', 'TKN6', 18); + smartToken7 = await SmartToken.new('Token7', 'TKN7', 18); + smartToken8 = await SmartToken.new('Token8', 'TKN8', 18); + smartToken9 = await SmartToken.new('Token9', 'TKN9', 18); + smartTokenA = await SmartToken.new('TokenA', 'TKNA', 18); + smartTokenB = await SmartToken.new('TokenB', 'TKNB', 18); + smartTokenC = await SmartToken.new('TokenC', 'TKNC', 18); + smartTokenD = await SmartToken.new('TokenD', 'TKND', 18); + smartTokenE = await SmartToken.new('TokenE', 'TKNE', 18); + anchorToken = etherToken.address; + + contractRegistry = await ContractRegistry.new(); + + pathFinder = await BancorNetworkPathFinder .new(contractRegistry.address); + converterRegistry = await BancorConverterRegistry .new(contractRegistry.address); + converterRegistryData = await BancorConverterRegistryData.new(contractRegistry.address); + + converter1 = await BancorConverter.new(smartToken1.address, contractRegistry.address, 0, etherToken .address, 500000); + converter2 = await BancorConverter.new(smartToken2.address, contractRegistry.address, 0, smartToken4.address, 500000); + converter3 = await BancorConverter.new(smartToken3.address, contractRegistry.address, 0, smartToken6.address, 500000); + converter4 = await BancorConverter.new(smartToken4.address, contractRegistry.address, 0, smartToken8.address, 500000); + converter5 = await BancorConverter.new(smartToken5.address, contractRegistry.address, 0, smartTokenA.address, 500000); + converter6 = await BancorConverter.new(smartToken6.address, contractRegistry.address, 0, smartTokenC.address, 500000); + converter7 = await BancorConverter.new(smartToken7.address, contractRegistry.address, 0, smartTokenE.address, 500000); + + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_REGISTRY , converterRegistry .address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_CONVERTER_REGISTRY_DATA, converterRegistryData.address); + + await converter2.addReserve(smartToken1.address, 500000); + await converter3.addReserve(smartToken1.address, 500000); + await converter4.addReserve(smartToken1.address, 500000); + await converter5.addReserve(smartToken1.address, 500000); + await converter6.addReserve(smartToken1.address, 500000); + await converter7.addReserve(smartToken2.address, 500000); + + await etherToken.deposit({value: 1000000}); + await smartToken1.issue(accounts[0], 1000000); + await smartToken2.issue(accounts[0], 1000000); + await smartToken3.issue(accounts[0], 1000000); + await smartToken4.issue(accounts[0], 1000000); + await smartToken5.issue(accounts[0], 1000000); + await smartToken6.issue(accounts[0], 1000000); + await smartToken7.issue(accounts[0], 1000000); + await smartToken8.issue(accounts[0], 1000000); + await smartToken9.issue(accounts[0], 1000000); + await smartTokenA.issue(accounts[0], 1000000); + await smartTokenB.issue(accounts[0], 1000000); + await smartTokenC.issue(accounts[0], 1000000); + await smartTokenD.issue(accounts[0], 1000000); + await smartTokenE.issue(accounts[0], 1000000); + + await etherToken .transfer(converter1.address, 1000); + await smartToken4.transfer(converter2.address, 1000); + await smartToken6.transfer(converter3.address, 1000); + await smartToken8.transfer(converter4.address, 1000); + await smartTokenA.transfer(converter5.address, 1000); + await smartTokenC.transfer(converter6.address, 1000); + await smartTokenE.transfer(converter7.address, 1000); + await smartToken1.transfer(converter2.address, 1000); + await smartToken1.transfer(converter3.address, 1000); + await smartToken1.transfer(converter4.address, 1000); + await smartToken1.transfer(converter5.address, 1000); + await smartToken1.transfer(converter6.address, 1000); + await smartToken2.transfer(converter7.address, 1000); + + await smartToken1.transferOwnership(converter1.address); + await smartToken2.transferOwnership(converter2.address); + await smartToken3.transferOwnership(converter3.address); + await smartToken4.transferOwnership(converter4.address); + await smartToken5.transferOwnership(converter5.address); + await smartToken6.transferOwnership(converter6.address); + await smartToken7.transferOwnership(converter7.address); + await converter1.acceptTokenOwnership(); + await converter2.acceptTokenOwnership(); + await converter3.acceptTokenOwnership(); + await converter4.acceptTokenOwnership(); + await converter5.acceptTokenOwnership(); + await converter6.acceptTokenOwnership(); + await converter7.acceptTokenOwnership(); + + await converterRegistry.addConverter(converter1.address); + await converterRegistry.addConverter(converter2.address); + await converterRegistry.addConverter(converter3.address); + await converterRegistry.addConverter(converter4.address); + await converterRegistry.addConverter(converter5.address); + await converterRegistry.addConverter(converter6.address); + await converterRegistry.addConverter(converter7.address); + }); + + it('verifies that the owner can update the anchor token', async () => { + await pathFinder.setAnchorToken(anchorToken, {from: accounts[0]}); + assert.equal(await pathFinder.anchorToken(), anchorToken); + }); + + it('should throw when a non owner tries to update the anchor token', async () => { + await utils.catchRevert(pathFinder.setAnchorToken(anchorToken, {from: accounts[1]})); + }); + + it('should return an empty path if the source-token has no path to the anchor-token', async () => { + const sourceToken = utils.zeroAddress; + const targetToken = smartToken1.address; + const expected = await generatePath(sourceToken, targetToken, anchorToken, converterRegistry); + const actual = await pathFinder.generatePath(sourceToken, targetToken); + assert.equal(actual + expected, []); + }); + + it('should return an empty path if the target-token has no path to the anchor-token', async () => { + const sourceToken = smartToken1.address; + const targetToken = utils.zeroAddress; + const expected = await generatePath(sourceToken, targetToken, anchorToken, converterRegistry); + const actual = await pathFinder.generatePath(sourceToken, targetToken); + assert.equal(actual + expected, []); + }); + + const variables = ["etherToken", ..."123456789ABCDE".split("").map(c => "smartToken" + c)]; + for (const sourceVariable of variables) { + for (const targetVariable of variables) { + it(`from ${sourceVariable} to ${targetVariable}`, async () => { + const sourceToken = eval(`${sourceVariable}.address`); + const targetToken = eval(`${targetVariable}.address`); + const expected = await generatePath(sourceToken, targetToken, anchorToken, converterRegistry); + const actual = await pathFinder.generatePath(sourceToken, targetToken); + assert.equal(`${actual}`, `${expected}`); + await print(sourceToken, targetToken, actual); + }); + } + } +}); diff --git a/test/BancorNetworkWithOldConverter.js b/test/BancorNetworkWithOldConverter.js new file mode 100755 index 0000000..2408dcf --- /dev/null +++ b/test/BancorNetworkWithOldConverter.js @@ -0,0 +1,100 @@ +/* global artifacts, contract, before, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const BancorConverter = require('./helpers/BancorConverter'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const BancorNetwork = artifacts.require('BancorNetwork'); +const TestBancorNetwork = artifacts.require('TestBancorNetwork'); +const SmartToken = artifacts.require('SmartToken'); +const NonStandardSmartToken = artifacts.require('NonStandardSmartToken'); +const BancorFormula = artifacts.require('BancorFormula'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const ContractFeatures = artifacts.require('ContractFeatures'); + +const OLD_CONVERTER_VERSION = 9; + +let smartToken1; +let smartToken2; +let smartToken3; +let contractRegistry; +let converter; +let bancorNetwork; + +/* +Token network structure: + + SmartToken2 + / \ + SmartToken1 SmartToken3 + +*/ + +contract('BancorNetworkWithOldConverter', accounts => { + before(async () => { + contractRegistry = await ContractRegistry.new(); + + let contractFeatures = await ContractFeatures.new(); + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_FEATURES, contractFeatures.address); + + let bancorFormula = await BancorFormula.new(); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_FORMULA, bancorFormula.address); + + bancorNetwork = await BancorNetwork.new(contractRegistry.address); + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_NETWORK, bancorNetwork.address); + + smartToken1 = await SmartToken.new('Token1', 'TKN1', 2); + await smartToken1.issue(accounts[0], 1000000); + + smartToken2 = await NonStandardSmartToken.new('Token2', 'TKN2', 2); + await smartToken2.issue(accounts[0], 2000000); + + smartToken3 = await SmartToken.new('Token3', 'TKN3', 2); + await smartToken3.issue(accounts[0], 3000000); + + converter = await BancorConverter.new(smartToken2.address, contractRegistry.address, 0, smartToken1.address, 300000, OLD_CONVERTER_VERSION); + await converter.addConnector(smartToken3.address, 150000, false); + + await smartToken1.transfer(converter.address, 40000); + await smartToken3.transfer(converter.address, 25000); + + await smartToken2.transferOwnership(converter.address); + await converter.acceptTokenOwnership(); + }); + + it('verifies that getReturnByPath returns the same amount as getReturn when converting a reserve to the smart token', async () => { + let getReturn = (await converter.getReturn.call(smartToken1.address, smartToken2.address, 100)); + let returnByPath = (await bancorNetwork.getReturnByPath.call([smartToken1.address, smartToken2.address, smartToken2.address], 100))[0]; + assert.equal(getReturn.toNumber(), returnByPath.toNumber()); + }); + + it('verifies that getReturnByPath returns the same amount as getReturn when converting from a token to a reserve', async () => { + let getReturn = (await converter.getReturn.call(smartToken2.address, smartToken1.address, 100)); + let returnByPath = (await bancorNetwork.getReturnByPath.call([smartToken2.address, smartToken2.address, smartToken1.address], 100))[0]; + assert.equal(getReturn.toNumber(), returnByPath.toNumber()); + }); + + for (let amount = 0; amount < 10; amount++) { + for (let fee = 0; fee < 10; fee++) { + it(`test old getReturn with amount = ${amount} and fee = ${fee}`, async () => { + const tester = await TestBancorNetwork.new(amount, fee); + const [_amount, _fee] = await tester.getReturnOld(); + const expected = `amount = ${amount} and fee = ${0}`; + const actual = `amount = ${_amount} and fee = ${_fee}`; + assert.equal(actual, expected); + }); + } + } + + for (let amount = 0; amount < 10; amount++) { + for (let fee = 0; fee < 10; fee++) { + it(`test new getReturn with amount = ${amount} and fee = ${fee}`, async () => { + const tester = await TestBancorNetwork.new(amount, fee); + const [_amount, _fee] = await tester.getReturnNew(); + const expected = `amount = ${amount} and fee = ${fee}`; + const actual = `amount = ${_amount} and fee = ${_fee}`; + assert.equal(actual, expected); + }); + } + } +}); diff --git a/test/BancorX.js b/test/BancorX.js new file mode 100755 index 0000000..3c9cd19 --- /dev/null +++ b/test/BancorX.js @@ -0,0 +1,249 @@ +/* global artifacts, contract, before, it, assert */ +/* eslint-disable prefer-reflect */ + +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const BancorConverter = artifacts.require('BancorConverter'); +const BancorX = artifacts.require('BancorX'); +const SmartToken = artifacts.require('SmartToken'); +const EtherToken = artifacts.require('EtherToken'); +const ContractRegistry = artifacts.require('ContractRegistry'); + +const MAX_LOCK_LIMIT = web3.toBigNumber('1000000000000000000000') // 1000 tokens +const MAX_RELEASE_LIMIT = web3.toBigNumber('1000000000000000000000') // 1000 tokens +const MIN_LIMIT = web3.toBigNumber('1000000000000000000') // 1 token +const LIM_INC_PER_BLOCK = web3.toBigNumber('1000000000000000000') // 1 token +const TEST_AMOUNT = web3.toBigNumber('10000000000000000000') // 10 tokens +const SUPPLY_AMOUNT = web3.toBigNumber('77492920201018469141404133') +const RESERVE_AMOUNT = web3.toBigNumber('45688650129186275318509') +const MIN_REQ_REPORTS = web3.toBigNumber('3') +const TRANSACTION_ID = web3.toBigNumber('12345678') +const X_TRANSFER_ID = web3.toBigNumber('87654321') + +const EOS_ADDRESS = web3.fromAscii('just a string 1') +const EOS_BLOCKCHAIN = web3.fromAscii('just a string 2') + +function assertEqual(x, y) { + assert.equal(x.toFixed(), y.toFixed()) +} + +async function initBancorX(accounts, isSmartToken) { + const etherToken = await EtherToken.new('Ether', 'ETH') + const contractRegistry = await ContractRegistry.new() + const smartToken = await SmartToken.new('Bancor', 'BNT', 18) + const bancorConverter = await BancorConverter.new( + smartToken.address, + contractRegistry.address, + '100000', + etherToken.address, + '100000' + ) + + const bancorX = await BancorX.new( + MAX_LOCK_LIMIT, + MAX_RELEASE_LIMIT, + MIN_LIMIT, + LIM_INC_PER_BLOCK, + MIN_REQ_REPORTS, + contractRegistry.address, + smartToken.address, + isSmartToken + ) + + // register BancorX address + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_X, bancorX.address) + + // issue bnt + await smartToken.issue(accounts[0], SUPPLY_AMOUNT) + + // set bancorx address for bnt converter + + if (isSmartToken) { + await smartToken.transferOwnership(bancorConverter.address) + await bancorConverter.acceptTokenOwnership() + await bancorConverter.setBancorX(bancorX.address) + } + else { + await smartToken.approve(bancorX.address, SUPPLY_AMOUNT) + } + + return bancorX; +} + +contract('BancorX', async accounts => { + for (const isSmartToken of [false, true]) { + describe(`with ${isSmartToken ? 'smart' : 'erc20'} token:`, () => { + it('should allow the owner to set reporters', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.setReporter(accounts[1], true) + await bancorX.setReporter(accounts[2], true) + await bancorX.setReporter(accounts[3], true) + + assert.equal(await bancorX.reporters.call(accounts[1]), true) + assert.equal(await bancorX.reporters.call(accounts[2]), true) + assert.equal(await bancorX.reporters.call(accounts[3]), true) + }) + + it('should not allow a non-owner to set reporters', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await utils.catchRevert(bancorX.setReporter(accounts[1], true, {from: accounts[1]})) + await utils.catchRevert(bancorX.setReporter(accounts[2], true, {from: accounts[1]})) + await utils.catchRevert(bancorX.setReporter(accounts[3], true, {from: accounts[1]})) + + assert.equal(await bancorX.reporters.call(accounts[1]), false) + assert.equal(await bancorX.reporters.call(accounts[2]), false) + assert.equal(await bancorX.reporters.call(accounts[3]), false) + }) + + it('should allow the owner to set limits', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.setMaxLockLimit(MAX_LOCK_LIMIT.plus(1)) + await bancorX.setMaxReleaseLimit(MAX_RELEASE_LIMIT.plus(1)) + await bancorX.setMinLimit(MIN_LIMIT.plus(1)) + await bancorX.setLimitIncPerBlock(LIM_INC_PER_BLOCK.plus(1)) + await bancorX.setMinRequiredReports(MIN_REQ_REPORTS.plus(1)) + + let maxLockLimit = await bancorX.maxLockLimit.call() + let maxReleaseLimit = await bancorX.maxReleaseLimit.call() + let minLimit = await bancorX.minLimit.call() + let limitIncPerBlock = await bancorX.limitIncPerBlock.call() + let minRequiredReports = await bancorX.minRequiredReports.call() + + assertEqual(maxLockLimit, MAX_LOCK_LIMIT.plus(1)) + assertEqual(maxReleaseLimit, MAX_RELEASE_LIMIT.plus(1)) + assertEqual(minLimit, MIN_LIMIT.plus(1)) + assertEqual(limitIncPerBlock, LIM_INC_PER_BLOCK.plus(1)) + assertEqual(minRequiredReports, MIN_REQ_REPORTS.plus(1)) + }) + + it('should not allow a non-owner to set limits', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await utils.catchRevert(bancorX.setMaxLockLimit(MAX_LOCK_LIMIT.plus(1), {from: accounts[1]})) + await utils.catchRevert(bancorX.setMaxReleaseLimit(MAX_RELEASE_LIMIT.plus(1), {from: accounts[1]})) + await utils.catchRevert(bancorX.setMinLimit(MIN_LIMIT.plus(1), {from: accounts[1]})) + await utils.catchRevert(bancorX.setLimitIncPerBlock(LIM_INC_PER_BLOCK.plus(1), {from: accounts[1]})) + await utils.catchRevert(bancorX.setMinRequiredReports(MIN_REQ_REPORTS.plus(1), {from: accounts[1]})) + + let maxLockLimit = await bancorX.maxLockLimit.call() + let maxReleaseLimit = await bancorX.maxReleaseLimit.call() + let minLimit = await bancorX.minLimit.call() + let limitIncPerBlock = await bancorX.limitIncPerBlock.call() + let minRequiredReports = await bancorX.minRequiredReports.call() + + assertEqual(maxLockLimit, MAX_LOCK_LIMIT) + assertEqual(maxReleaseLimit, MAX_RELEASE_LIMIT) + assertEqual(minLimit, MIN_LIMIT) + assertEqual(limitIncPerBlock, LIM_INC_PER_BLOCK) + assertEqual(minRequiredReports, MIN_REQ_REPORTS) + }) + + it('should not be able to lock below the min limit', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + let amount = MIN_LIMIT.minus(1) + await utils.catchRevert(bancorX.xTransfer(EOS_BLOCKCHAIN, EOS_ADDRESS, amount)) + }) + + it('should not be able to lock above the max limit', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + let amount = MAX_LOCK_LIMIT.plus(1) + await utils.catchRevert(bancorX.xTransfer(EOS_BLOCKCHAIN, EOS_ADDRESS, amount)) + }) + + it('should not be able to release below the min limit', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + let amount = MIN_LIMIT.minus(1) + await bancorX.setReporter(accounts[1], true) + await bancorX.setReporter(accounts[2], true) + await bancorX.setReporter(accounts[3], true) + await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], amount, X_TRANSFER_ID, {from: accounts[1]}) + await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], amount, X_TRANSFER_ID, {from: accounts[2]}) + await utils.catchRevert(bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], amount, X_TRANSFER_ID, {from: accounts[3]})) + }) + + it('should not be able to release above the max limit', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + let amount = MAX_RELEASE_LIMIT.plus(1) + await bancorX.setReporter(accounts[1], true) + await bancorX.setReporter(accounts[2], true) + await bancorX.setReporter(accounts[3], true) + await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], amount, X_TRANSFER_ID, {from: accounts[1]}) + await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], amount, X_TRANSFER_ID, {from: accounts[2]}) + await utils.catchRevert(bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], amount, X_TRANSFER_ID, {from: accounts[3]})) + }) + + it('should emit an event when successfuly locking tokens', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + let amount = TEST_AMOUNT + let result = await bancorX.xTransfer(EOS_BLOCKCHAIN, EOS_ADDRESS, amount) + assert.equal(result.logs[0].args._amount, amount.toFixed()) + assert.equal(result.logs[0].args._from, accounts[0]) + }) + + it('should properly calculate the current lock limit after a single transaction', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + let numOfTests = 10; + let amount = LIM_INC_PER_BLOCK.times(numOfTests) + await bancorX.xTransfer(EOS_BLOCKCHAIN, EOS_ADDRESS, amount) + + for (let i = 0; i <= numOfTests; i++) { + assertEqual(await bancorX.getCurrentLockLimit.call(), MAX_LOCK_LIMIT.minus(amount).plus(MIN_LIMIT.times(i))) + web3.currentProvider.send({method: 'evm_mine'}); + } + + for (let i = 0; i < 3; i++) { + assertEqual(await bancorX.getCurrentLockLimit.call(), MAX_LOCK_LIMIT) + web3.currentProvider.send({method: 'evm_mine'}); + } + }) + + it('should not allow a reporter to report the same transaction twice', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.setReporter(accounts[1], true) + await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[1]}) + await utils.catchRevert(bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[1]})) + }) + + it('should not allow two reporters to give conflicting transaction details', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.setReporter(accounts[1], true) + await bancorX.setReporter(accounts[2], true) + await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[1], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[1]}) + await utils.catchRevert(bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[2], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[2]})) + }) + + it('should not allow a non-reporter to report', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await utils.catchRevert(bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[1]})) + }) + + it('should not allow reports when disabled', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.setReporter(accounts[1], true) + await bancorX.enableReporting(false) + await utils.catchRevert(bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[1]})) + }) + + it('should not allow xTransfers when disabled', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.enableXTransfers(false) + await utils.catchRevert(bancorX.xTransfer(EOS_BLOCKCHAIN, EOS_ADDRESS, TEST_AMOUNT)) + }) + + it('Gas Test', async () => { + let bancorX = await initBancorX(accounts, isSmartToken) + await bancorX.setReporter(accounts[1], true) + await bancorX.setReporter(accounts[2], true) + await bancorX.setReporter(accounts[3], true) + let result0 = await bancorX.xTransfer(EOS_BLOCKCHAIN, EOS_ADDRESS, TEST_AMOUNT) + let result1 = await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[1]}) + let result2 = await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[2]}) + let result3 = await bancorX.reportTx(EOS_BLOCKCHAIN, TRANSACTION_ID, accounts[0], TEST_AMOUNT, X_TRANSFER_ID, {from: accounts[3]}) + console.log(`\nGasPrice for xTransfer: ${result0.receipt.gasUsed}`) + console.log(`GasPrice for reportTx (first reporter, no release): ${result1.receipt.gasUsed}`) + console.log(`GasPrice for reportTx (second reporter, no release): ${result2.receipt.gasUsed}`) + console.log(`GasPrice for reportTx (third reporter, yes release): ${result3.receipt.gasUsed}`) + }) + }) + } +}) diff --git a/test/ContractFeatures.js b/test/ContractFeatures.js new file mode 100755 index 0000000..0a081a6 --- /dev/null +++ b/test/ContractFeatures.js @@ -0,0 +1,79 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const ContractFeatures = artifacts.require('ContractFeatures'); +const TestFeatures = artifacts.require('TestFeatures'); + +let FEATURE1 = 1 << 0; +let FEATURE2 = 1 << 1; +let FEATURE3 = 1 << 2; + +contract('ContractFeatures', () => { + it('verifies that a given contract feature is not set after construction', async () => { + let contractFeatures = await ContractFeatures.new(); + let testFeatures = await TestFeatures.new(contractFeatures.address); + let isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE1); + assert(!isSupported); + }); + + it('verifies that a contract can enable a feature', async () => { + let contractFeatures = await ContractFeatures.new(); + let testFeatures = await TestFeatures.new(contractFeatures.address); + await testFeatures.enableFeatures(FEATURE1, true); + await testFeatures.enableFeatures(FEATURE3, true); + let isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE1); + assert(isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE2); + assert(!isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE3); + assert(isSupported); + }); + + it('verifies that a contract can enable multiple features with one call', async () => { + let contractFeatures = await ContractFeatures.new(); + let testFeatures = await TestFeatures.new(contractFeatures.address); + await testFeatures.enableFeatures(FEATURE1 | FEATURE3, true); + let isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE1); + assert(isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE2); + assert(!isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE3); + assert(isSupported); + }); + + it('verifies that a contract can attempt to enable a feature that is already enabled', async () => { + let contractFeatures = await ContractFeatures.new(); + let testFeatures = await TestFeatures.new(contractFeatures.address); + await testFeatures.enableFeatures(FEATURE1, true); + await testFeatures.enableFeatures(FEATURE1, true); + await testFeatures.enableFeatures(FEATURE3, true); + let isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE1); + assert(isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE2); + assert(!isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE3); + assert(isSupported); + }); + + it('verifies that a contract can disable a feature', async () => { + let contractFeatures = await ContractFeatures.new(); + let testFeatures = await TestFeatures.new(contractFeatures.address); + await testFeatures.enableFeatures(FEATURE1, true); + await testFeatures.enableFeatures(FEATURE2, true); + await testFeatures.enableFeatures(FEATURE3, true); + let isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE1); + assert(isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE2); + assert(isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE3); + assert(isSupported); + + await testFeatures.enableFeatures(FEATURE2, false); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE1); + assert(isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE2); + assert(!isSupported); + isSupported = await contractFeatures.isSupported.call(testFeatures.address, FEATURE3); + assert(isSupported); + }); +}); diff --git a/test/ContractRegistry.js b/test/ContractRegistry.js new file mode 100755 index 0000000..f4a9723 --- /dev/null +++ b/test/ContractRegistry.js @@ -0,0 +1,122 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const ContractRegistry = artifacts.require('ContractRegistry'); +const utils = require('./helpers/Utils'); + +let contractName1 = 'red'; +let contractName2 = 'blue'; +let contractName3 = 'black'; + +contract('ContractRegistry', accounts => { + it('verifies that a given contract address is not set after construction', async () => { + let contractRegistry = await ContractRegistry.new(); + let address = await contractRegistry.addressOf.call(contractName1); + assert.equal(address, utils.zeroAddress); + }); + + it('verifies that the owner can register a contract address', async () => { + let contractRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(contractName1, accounts[1]); + let address = await contractRegistry.addressOf.call(contractName1); + assert.equal(address, accounts[1]); + }); + + it('should throw when a non owner attempts to register a contract address', async () => { + let contractRegistry = await ContractRegistry.new(); + + await utils.catchRevert(contractRegistry.registerAddress(contractName1, accounts[1], { from: accounts[2] })); + }); + + it('verifies that the contract name list gets updated correctly when registering addresses', async () => { + let contractRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(contractName1, accounts[1]); + await contractRegistry.registerAddress(contractName2, accounts[2]); + + let itemCount = await contractRegistry.itemCount.call(); + assert.equal(itemCount.toString(), "2"); + let name = await contractRegistry.contractNames.call(0); + assert.equal(name, contractName1); + name = await contractRegistry.contractNames.call(1); + assert.equal(name, contractName2); + }); + + it('verifies that the owner can unregister a contract address', async () => { + let contractRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(contractName1, accounts[1]); + let address = await contractRegistry.addressOf.call(contractName1); + assert.equal(address, accounts[1]); + + await contractRegistry.unregisterAddress(contractName1); + address = await contractRegistry.addressOf.call(contractName1); + assert.equal(address, utils.zeroAddress); + }); + + it('should throw when a non owner attempts to unregister a contract address', async () => { + let contractRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(contractName1, accounts[1]); + let address = await contractRegistry.addressOf.call(contractName1); + assert.equal(address, accounts[1]); + + await utils.catchRevert(contractRegistry.unregisterAddress(contractName1, { from: accounts[2] })); + }); + + it('verifies that the contract name list gets updated correctly when unregistering addresses', async () => { + let contractRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(contractName1, accounts[1]); + await contractRegistry.registerAddress(contractName2, accounts[2]); + await contractRegistry.registerAddress(contractName3, accounts[3]); + + let itemCount = await contractRegistry.itemCount.call(); + assert.equal(itemCount.toString(), "3"); + let name = await contractRegistry.contractNames.call(0); + assert.equal(name, contractName1); + name = await contractRegistry.contractNames.call(1); + assert.equal(name, contractName2); + name = await contractRegistry.contractNames.call(2); + assert.equal(name, contractName3); + + await contractRegistry.unregisterAddress(contractName1); + itemCount = await contractRegistry.itemCount.call(); + assert.equal(itemCount.toString(), "2"); + name = await contractRegistry.contractNames.call(0); + assert.equal(name, contractName3); + name = await contractRegistry.contractNames.call(1); + assert.equal(name, contractName2); + }); + + it('verifies that a registry item can be unregistered and reregistered properly', async () => { + let contractRegistry = await ContractRegistry.new(); + + await contractRegistry.registerAddress(contractName1, accounts[1]); + await contractRegistry.registerAddress(contractName2, accounts[2]); + + await contractRegistry.unregisterAddress(contractName1); + await contractRegistry.registerAddress(contractName1, accounts[1]); + + // contractName2 is in first index after unregister and reregister + let cn2 = await contractRegistry.contractNames.call(0); + let cn1 = await contractRegistry.contractNames.call(1); + + assert.equal(cn1, contractName1); + assert.equal(cn2, contractName2); + }); + + it('should throw when unregistering non registered address', async () => { + let contractRegistry = await ContractRegistry.new(); + + await contractRegistry.registerAddress(contractName1, accounts[1]); + await utils.catchRevert(contractRegistry.unregisterAddress(contractName2)); + }); + + it('verifies that deprecated function getAddress works correctly', async () => { + let contractRegistry = await ContractRegistry.new(); + await contractRegistry.registerAddress(contractName1, accounts[1]); + await contractRegistry.registerAddress(contractName2, accounts[2]); + await contractRegistry.registerAddress(contractName3, accounts[3]); + + assert.equal(await contractRegistry.getAddress.call(contractName1), await contractRegistry.addressOf.call(contractName1)); + assert.equal(await contractRegistry.getAddress.call(contractName2), await contractRegistry.addressOf.call(contractName2)); + assert.equal(await contractRegistry.getAddress.call(contractName3), await contractRegistry.addressOf.call(contractName3)); + }); +}); \ No newline at end of file diff --git a/test/CrowdsaleController.js b/test/CrowdsaleController.js new file mode 100755 index 0000000..a37e5a0 --- /dev/null +++ b/test/CrowdsaleController.js @@ -0,0 +1,289 @@ +/* global artifacts, contract, before, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const CrowdsaleController = artifacts.require('CrowdsaleController'); +const SmartToken = artifacts.require('SmartToken'); +const TestCrowdsaleController = artifacts.require('TestCrowdsaleController'); +const utils = require('./helpers/Utils'); + +let token; +let tokenAddress; +let beneficiaryAddress = '0x69aa30b306805bd17488ce957d03e3c0213ee9e6'; +let btcsAddress; +let startTime = Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60; // crowdsale hasn't started +let startTimeInProgress = Math.floor(Date.now() / 1000) - 12 * 60 * 60; // ongoing crowdsale +let startTimeFinished = Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60; // ongoing crowdsale +let realCap = 1000; +let realCapSmall = 10; +let realCapLarge = 1000000000000000000000000000000000000; +let realCapKey = 234; + +// sha3(uint256(1000), uint256(234)) +let realEtherCapHash = '0xd3a40f1165164f13f237cc938419cc292e66b7bb3aa190f21087a3813c5ae1ca'; +// sha3(uint256(10), uint256(234)) +let realEtherCapHashSmall = '0x6abc5e51eb354f6e72ad5be4976fd8d867b70b09c5cb286a12375fd9a45644b6'; +// sha3(uint256(1000000000000000000000000000000000000), uint256(234)) +let realEtherCapHashLarge = '0xe8de42a704eab00275ed4cdc7e4e626633a0ce70bc986007a037e3ff699f4381'; + +let badContributionGasPrice = 50000000001; + +async function generateDefaultController() { + return await CrowdsaleController.new(tokenAddress, startTime, beneficiaryAddress, btcsAddress, realEtherCapHash); +} + +// used by contribution tests, creates a controller that's already in progress +async function initController(accounts, activate, startTimeOverride = startTimeInProgress, capHash = realEtherCapHash) { + token = await SmartToken.new('Token1', 'TKN1', 2); + tokenAddress = token.address; + + let controller = await TestCrowdsaleController.new(tokenAddress, startTime, beneficiaryAddress, btcsAddress, capHash, startTimeOverride); + let controllerAddress = controller.address; + + if (activate) { + await token.transferOwnership(controllerAddress); + await controller.acceptTokenOwnership(); + } + + return controller; +} + +function getContributionAmount(transaction, logIndex = 0) { + return transaction.logs[logIndex].args._return.toNumber(); +} + +contract('CrowdsaleController', accounts => { + before(async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + tokenAddress = token.address; + btcsAddress = accounts[4]; + }); + + it('verifies the base storage values after construction', async () => { + let controller = await generateDefaultController(); + let token = await controller.token.call(); + assert.equal(token, tokenAddress); + let start = await controller.startTime.call(); + assert.equal(start.toNumber(), startTime); + let endTime = await controller.endTime.call(); + let duration = await controller.DURATION.call(); + assert.equal(endTime.toNumber(), startTime + duration.toNumber()); + let beneficiary = await controller.beneficiary.call(); + assert.equal(beneficiary, beneficiaryAddress); + let btcs = await controller.btcs.call(); + assert.equal(btcs, btcsAddress); + let realCapHash = await controller.realEtherCapHash.call(); + assert.equal(realCapHash, realEtherCapHash); + }); + + it('should throw when attempting to construct a controller with no token', async () => { + await utils.catchRevert(CrowdsaleController.new('0x0', startTime, beneficiaryAddress, btcsAddress, realEtherCapHash)); + }); + + it('should throw when attempting to construct a controller with start time that has already passed', async () => { + await utils.catchInvalidOpcode(CrowdsaleController.new(tokenAddress, 10000000, beneficiaryAddress, btcsAddress, realEtherCapHash)); + }); + + it('should throw when attempting to construct a controller without beneficiary address', async () => { + await utils.catchRevert(CrowdsaleController.new(tokenAddress, startTime, '0x0', btcsAddress, realEtherCapHash)); + }); + + it('should throw when attempting to construct a controller without bitcoin suisse address', async () => { + await utils.catchRevert(CrowdsaleController.new(tokenAddress, startTime, beneficiaryAddress, '0x0', realEtherCapHash)); + }); + + it('should throw when attempting to construct a controller without ether cap hash', async () => { + await utils.catchRevert(CrowdsaleController.new(tokenAddress, startTime, beneficiaryAddress, btcsAddress, 0)); + }); + + it('verifies the real ether cap balance after enabled by the owner', async () => { + let controller = await initController(accounts, true); + await controller.enableRealCap(realCap, realCapKey); + let totalEtherCap = await controller.totalEtherCap.call(); + assert.equal(totalEtherCap, realCap); + }); + + it('should throw when a non owner attempts to enable the real ether cap', async () => { + let controller = await initController(accounts, true); + + await utils.catchRevert(controller.enableRealCap(realCap, realCapKey, { from: accounts[1] })); + }); + + it('should throw when the owner attempts to enable the real ether cap while the controller is not active', async () => { + let controller = await initController(accounts, false); + + await utils.catchRevert(controller.enableRealCap(realCap, realCapKey)); + }); + + it('should throw when the owner attempts to enable the real ether cap before the start time', async () => { + let controller = await initController(accounts, true, startTime); + + await utils.catchInvalidOpcode(controller.enableRealCap(realCap, realCapKey)); + }); + + it('should throw when the owner attempts to enable the real ether cap with an invalid cap', async () => { + let controller = await initController(accounts, true); + + await utils.catchRevert(controller.enableRealCap(0, realCapKey)); + }); + + it('should throw when the owner attempts to enable the real ether cap with the wrong real cap', async () => { + let controller = await initController(accounts, true); + + await utils.catchRevert(controller.enableRealCap(1001, realCapKey)); + }); + + it('should throw when the owner attempts to enable the real ether cap with the wrong cap key', async () => { + let controller = await initController(accounts, true); + + await utils.catchRevert(controller.enableRealCap(realCap, 235)); + }); + + it('should throw when the owner attempts to enable the real ether cap with a value larger than the initial cap', async () => { + let controller = await CrowdsaleController.new(tokenAddress, startTime, beneficiaryAddress, btcsAddress, realEtherCapHashLarge); + + await utils.catchRevert(controller.enableRealCap(realCapLarge, realCapKey)); + }); + + it('verifies that computeReturn returns a valid amount', async () => { + let controller = await initController(accounts, true); + let returnAmount = await controller.computeReturn.call(500); + assert.isNumber(returnAmount.toNumber()); + assert.notEqual(returnAmount.toNumber(), 0); + }); + + it('verifies that computeReturn returns the same amount as contributeETH', async () => { + let controller = await initController(accounts, true); + let returnAmount = await controller.computeReturn.call(500); + + let purchaseRes = await controller.contributeETH({ value: 500 }); + let purchaseAmount = getContributionAmount(purchaseRes); + + assert.equal(returnAmount, purchaseAmount); + }); + + it('verifies that computeReturn returns the same amount as contributeBTCs', async () => { + let controller = await initController(accounts, true, startTime); + let returnAmount = await controller.computeReturn.call(500); + + let purchaseRes = await controller.contributeBTCs({ value: 500, from: btcsAddress }); + let purchaseAmount = getContributionAmount(purchaseRes); + + assert.equal(returnAmount, purchaseAmount); + }); + + it('verifies balances and total eth contributed after contributing ether', async () => { + let controller = await initController(accounts, true); + + let prevEtherBalance = await web3.eth.getBalance(beneficiaryAddress); + + let res = await controller.contributeETH({ value: 200, from: accounts[1] }); + let purchaseAmount = getContributionAmount(res); + assert.isNumber(purchaseAmount); + assert.notEqual(purchaseAmount, 0); + + let contributorTokenBalance = await token.balanceOf.call(accounts[1]); + assert.equal(contributorTokenBalance, purchaseAmount); + + let beneficiaryTokenBalance = await token.balanceOf.call(beneficiaryAddress); + assert.equal(beneficiaryTokenBalance, purchaseAmount); + + let beneficiaryEtherBalance = await web3.eth.getBalance(beneficiaryAddress); + assert.equal(beneficiaryEtherBalance.toNumber(), prevEtherBalance.plus(200).toNumber()); + + let totalEtherContributed = await controller.totalEtherContributed.call(); + assert.equal(totalEtherContributed, 200); + }); + + it('should throw when attempting to contribute ether while the controller is not active', async () => { + let controller = await initController(accounts, false); + + await utils.catchRevert(controller.contributeETH({ value: 2000 })); + }); + + it('should throw when attempting to contribute ether before the crowdsale has started', async () => { + let controller = await initController(accounts, true, startTime); + + await utils.catchInvalidOpcode(controller.contributeETH({ value: 2000 })); + }); + + it('should throw when attempting to contribute ether after the crowdsale has finished', async () => { + let controller = await initController(accounts, true, startTimeFinished); + + await utils.catchInvalidOpcode(controller.contributeETH({ value: 2000 })); + }); + + it('should throw when attempting to contribute ether while hitting the real ether cap', async () => { + let controller = await initController(accounts, true, startTimeInProgress, realEtherCapHashSmall); + await controller.enableRealCap(realCapSmall, realCapKey); + + await utils.catchInvalidOpcode(controller.contributeETH({ value: realCap + 1 })); + }); + + it('should throw when attempting to contribute ether with a large gas price', async () => { + let controller = await initController(accounts, true); + + await utils.catchInvalidOpcode(controller.contributeETH({ value: 2000, gasPrice: badContributionGasPrice })); + }); + + it('verifies balances and total eth contributed after contributing through btcs', async () => { + let controller = await initController(accounts, true, startTime); + + let prevContributorTokenBalance = await token.balanceOf.call(btcsAddress); + let prevEtherBalance = await web3.eth.getBalance(beneficiaryAddress); + + let res = await controller.contributeBTCs({ value: 200, from: btcsAddress }); + let purchaseAmount = getContributionAmount(res); + assert.isNumber(purchaseAmount); + assert.notEqual(purchaseAmount, 0); + + let contributorTokenBalance = await token.balanceOf.call(btcsAddress); + assert.equal(contributorTokenBalance.toNumber(), prevContributorTokenBalance.plus(purchaseAmount).toNumber()); + + let beneficiaryTokenBalance = await token.balanceOf.call(beneficiaryAddress); + assert.equal(beneficiaryTokenBalance, purchaseAmount); + + let beneficiaryEtherBalance = await web3.eth.getBalance(beneficiaryAddress); + assert.equal(beneficiaryEtherBalance.toNumber(), prevEtherBalance.plus(200).toNumber()); + + let totalEtherContributed = await controller.totalEtherContributed.call(); + assert.equal(totalEtherContributed, 200); + }); + + it('should throw when attempting to contribute through btcs from a non btcs address', async () => { + let controller = await initController(accounts, true, startTime); + + await utils.catchInvalidOpcode(controller.contributeBTCs({ value: 2000 })); + }); + + it('should throw when attempting to contribute through btcs while the controller is not active', async () => { + let controller = await initController(accounts, false, startTime); + + await utils.catchRevert(controller.contributeBTCs({ value: 2000, from: btcsAddress })); + }); + + it('should throw when attempting to contribute through btcs after the crowdsale has started', async () => { + let controller = await initController(accounts, true); + + await utils.catchInvalidOpcode(controller.contributeBTCs({ value: 2000, from: btcsAddress })); + }); + + it('should throw when attempting to contribute through btcs after the crowdsale has finished', async () => { + let controller = await initController(accounts, true, startTimeFinished); + + await utils.catchInvalidOpcode(controller.contributeBTCs({ value: 2000, from: btcsAddress })); + }); + + it('should throw when attempting to contribute through btcs while hitting the btcs ether cap', async () => { + let controller = await initController(accounts, true, startTime); + let btcsEtherCap = await controller.BTCS_ETHER_CAP_SMALL.call(); + let largerThanCap = btcsEtherCap.plus(1); + + await utils.catchInvalidOpcode(controller.contributeBTCs({ value: largerThanCap.toString(), from: btcsAddress })); + }); + + it('should throw when attempting to contribute through btcs with large gas price', async () => { + let controller = await initController(accounts, true, startTime); + + await utils.catchInvalidOpcode(controller.contributeBTCs({ value: 200, from: btcsAddress, gasPrice: badContributionGasPrice })); + }); +}); diff --git a/test/ERC20Token.js b/test/ERC20Token.js new file mode 100755 index 0000000..8eba03a --- /dev/null +++ b/test/ERC20Token.js @@ -0,0 +1,117 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const ERC20Token = artifacts.require('ERC20Token'); +const utils = require('./helpers/Utils'); + +const invalidAccount = '0x0'; + +contract('ERC20Token', accounts => { + it('verifies the token name after construction', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 0); + let name = await token.name.call(); + assert.equal(name, 'Token1'); + }); + + it('verifies the token symbol after construction', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 0); + let symbol = await token.symbol.call(); + assert.equal(symbol, 'TKN1'); + }); + + it('verifies the balances after a transfer', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 10000); + await token.transfer(accounts[1], 500); + let balance; + balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 9500); + balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 500); + }); + + it('verifies that a transfer fires a Transfer event', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 10000); + let res = await token.transfer(accounts[1], 500); + assert(res.logs.length > 0 && res.logs[0].event == 'Transfer'); + }); + + it('should throw when attempting to transfer more than the balance', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 100); + + await utils.catchRevert(token.transfer(accounts[1], 500)); + }); + + it('should throw when attempting to transfer to an invalid address', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 100); + + await utils.catchRevert(token.transfer(invalidAccount, 10)); + }); + + it('verifies the allowance after an approval', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 10000); + await token.approve(accounts[1], 500); + let allowance = await token.allowance.call(accounts[0], accounts[1]); + assert.equal(allowance, 500); + }); + + it('verifies that an approval fires an Approval event', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 10000); + let res = await token.approve(accounts[1], 500); + assert(res.logs.length > 0 && res.logs[0].event == 'Approval'); + }); + + it('should throw when attempting to define allowance for an invalid address', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 100); + + await utils.catchRevert(token.approve(invalidAccount, 10)); + }); + + it('verifies the balances after transferring from another account', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 1000); + await token.approve(accounts[1], 500); + await token.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] }); + let balance; + balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 950); + balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 0); + balance = await token.balanceOf.call(accounts[2]); + assert.equal(balance, 50); + }); + + it('verifies that transferring from another account fires a Transfer event', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 1000); + await token.approve(accounts[1], 500); + let res = await token.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] }); + assert(res.logs.length > 0 && res.logs[0].event == 'Transfer'); + }); + + it('verifies the new allowance after transferring from another account', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 1000); + await token.approve(accounts[1], 500); + await token.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] }); + let allowance = await token.allowance.call(accounts[0], accounts[1]); + assert.equal(allowance, 450); + }); + + it('should throw when attempting to transfer from another account more than the allowance', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 1000); + await token.approve(accounts[1], 100); + + await utils.catchRevert(token.transferFrom(accounts[0], accounts[2], 200, { from: accounts[1] })); + }); + + it('should throw when attempting to transfer from an invalid account', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 1000); + await token.approve(accounts[1], 100); + + await utils.catchRevert(token.transferFrom(invalidAccount, accounts[2], 50, { from: accounts[1] })); + }); + + it('should throw when attempting to transfer from to an invalid account', async () => { + let token = await ERC20Token.new('Token1', 'TKN1', 0, 1000); + await token.approve(accounts[1], 100); + + await utils.catchRevert(token.transferFrom(accounts[0], invalidAccount, 50, { from: accounts[1] })); + }); +}); \ No newline at end of file diff --git a/test/EtherToken.js b/test/EtherToken.js new file mode 100755 index 0000000..a799a4f --- /dev/null +++ b/test/EtherToken.js @@ -0,0 +1,100 @@ +/* global artifacts, contract, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const EtherToken = artifacts.require('EtherToken'); +const utils = require('./helpers/Utils'); + +contract('EtherToken', accounts => { + let token; + beforeEach(async () => { + token = await EtherToken.new('Ether Token', 'ETH'); + }); + + it('verifies the token name after construction', async () => { + let name = await token.name.call(); + assert.equal(name, 'Ether Token'); + }); + + it('verifies the token symbol after construction', async () => { + let symbol = await token.symbol.call(); + assert.equal(symbol, 'ETH'); + }); + + it('verifies the balance and supply after a deposit through the deposit function', async () => { + await token.deposit({ value: 1000 }); + let balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 1000); + let supply = await token.totalSupply.call(); + assert.equal(supply, 1000); + }); + + it('verifies the balance and supply after a deposit through the fallback function', async () => { + await token.send(1000); + let balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 1000); + let supply = await token.totalSupply.call(); + assert.equal(supply, 1000); + }); + + it('verifies the balance and supply after a deposit through the depositTo function', async () => { + await token.depositTo(accounts[1], { value: 1000 }); + let balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 1000); + let supply = await token.totalSupply.call(); + assert.equal(supply, 1000); + }); + + it('verifies the balance and supply after a withdrawal', async () => { + await token.deposit({ value: 100 }); + await token.withdraw(20); + let tokenBalance = await token.balanceOf.call(accounts[0]); + assert.equal(tokenBalance, 80); + let supply = await token.totalSupply.call(); + assert.equal(supply, 80); + }); + + it('verifies the ether balance after a withdrawal', async () => { + await token.deposit({ value: 100 }); + let prevBalance = web3.eth.getBalance(accounts[0]); + let res = await token.withdraw(20); + let transaction = web3.eth.getTransaction(res.tx); + let newBalance = web3.eth.getBalance(accounts[0]); + prevBalance = web3.toBigNumber(prevBalance); + newBalance = web3.toBigNumber(newBalance); + let transactionCost = transaction.gasPrice.times(res.receipt.cumulativeGasUsed); + assert.equal(newBalance.toString(), prevBalance.minus(transactionCost).plus(20).toString()); + }); + + it('verifies the ether balance after a withdrawal to target account', async () => { + await token.deposit({ value: 100 }); + let prevBalance = web3.eth.getBalance(accounts[1]); + await token.withdrawTo(accounts[1], 20); + let newBalance = web3.eth.getBalance(accounts[1]); + prevBalance = web3.toBigNumber(prevBalance); + newBalance = web3.toBigNumber(newBalance); + assert.equal(newBalance.toString(), prevBalance.plus(20).toString()); + }); + + it('verifies the balances after a transfer', async () => { + await token.deposit({ value: 100 }); + await token.transfer(accounts[1], 10); + let balance; + balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 90); + balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 10); + }); + + it('should throw when attempting to transfer to the token address', async () => { + await token.deposit({ value: 100 }); + + await utils.catchRevert(token.transfer(token.address, 10)); + }); + + it('should throw when attempting to transferFrom to the token address', async () => { + await token.deposit({ value: 100 }); + await token.approve(accounts[1], 50); + + await utils.catchRevert(token.transferFrom(accounts[0], token.address, 10, { from: accounts[1] })); + }); +}); \ No newline at end of file diff --git a/test/Managed.js b/test/Managed.js new file mode 100755 index 0000000..7a97443 --- /dev/null +++ b/test/Managed.js @@ -0,0 +1,67 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const Managed = artifacts.require('Managed'); +const utils = require('./helpers/Utils'); + +contract('Managed', accounts => { + it('verifies the manager after construction', async () => { + let contract = await Managed.new(); + let manager = await contract.manager.call(); + assert.equal(manager, accounts[0]); + }); + + it('verifies the new manager after management transfer', async () => { + let contract = await Managed.new(); + await contract.transferManagement(accounts[1]); + await contract.acceptManagement({ from: accounts[1] }); + let manager = await contract.manager.call(); + assert.equal(manager, accounts[1]); + }); + + it('verifies that management transfer fires an ManagerUpdate event', async () => { + let contract = await Managed.new(); + await contract.transferManagement(accounts[1]); + let res = await contract.acceptManagement({ from: accounts[1] }); + assert(res.logs.length > 0 && res.logs[0].event == 'ManagerUpdate'); + }); + + it('verifies that newManager is cleared after management transfer', async () => { + let contract = await Managed.new(); + await contract.transferManagement(accounts[1]); + await contract.acceptManagement({ from: accounts[1] }); + let newManager = await contract.newManager.call(); + assert.equal(newManager, utils.zeroAddress); + }); + + it('verifies that no management transfer takes places before the new manager accepted it', async () => { + let contract = await Managed.new(); + await contract.transferManagement(accounts[1]); + let manager = await contract.manager.call(); + assert.equal(manager, accounts[0]); + }); + + it('verifies that only the manager can initiate management transfer', async () => { + let contract = await Managed.new(); + + await utils.catchRevert(contract.transferManagement(accounts[1], { from: accounts[2] })); + }); + + it('verifies that the manager can cancel management transfer before the new manager accepted it', async () => { + let contract = await Managed.new(); + await contract.transferManagement(accounts[1]); + await contract.transferManagement('0x0'); + let newManager = await contract.newManager.call(); + assert.equal(newManager, utils.zeroAddress); + }); + + it('verifies that the owner can transfer the management', async () => { + let contract = await Managed.new(); + await contract.transferOwnership(accounts[1]); + await contract.acceptOwnership({ from: accounts[1] }); + await contract.transferManagement(accounts[2], { from: accounts[1] }); + await contract.acceptManagement({ from: accounts[2] }); + let manager = await contract.manager.call(); + assert.equal(manager, accounts[2]); + }); +}); \ No newline at end of file diff --git a/test/Owned.js b/test/Owned.js new file mode 100755 index 0000000..b6b7588 --- /dev/null +++ b/test/Owned.js @@ -0,0 +1,57 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const Owned = artifacts.require('Owned'); +const utils = require('./helpers/Utils'); + +contract('Owned', accounts => { + it('verifies the owner after construction', async () => { + let contract = await Owned.new(); + let owner = await contract.owner.call(); + assert.equal(owner, accounts[0]); + }); + + it('verifies the new owner after ownership transfer', async () => { + let contract = await Owned.new(); + await contract.transferOwnership(accounts[1]); + await contract.acceptOwnership({ from: accounts[1] }); + let owner = await contract.owner.call(); + assert.equal(owner, accounts[1]); + }); + + it('verifies that ownership transfer fires an OwnerUpdate event', async () => { + let contract = await Owned.new(); + await contract.transferOwnership(accounts[1]); + let res = await contract.acceptOwnership({ from: accounts[1] }); + assert(res.logs.length > 0 && res.logs[0].event == 'OwnerUpdate'); + }); + + it('verifies that newOwner is cleared after ownership transfer', async () => { + let contract = await Owned.new(); + await contract.transferOwnership(accounts[1]); + await contract.acceptOwnership({ from: accounts[1] }); + let newOwner = await contract.newOwner.call(); + assert.equal(newOwner, utils.zeroAddress); + }); + + it('verifies that no ownership transfer takes places before the new owner accepted it', async () => { + let contract = await Owned.new(); + await contract.transferOwnership(accounts[1]); + let owner = await contract.owner.call(); + assert.equal(owner, accounts[0]); + }); + + it('verifies that only the owner can initiate ownership transfer', async () => { + let contract = await Owned.new(); + + await utils.catchRevert(contract.transferOwnership(accounts[1], { from: accounts[2] })); + }); + + it('verifies that the owner can cancel ownership transfer before the new owner accepted it', async () => { + let contract = await Owned.new(); + await contract.transferOwnership(accounts[1]); + await contract.transferOwnership('0x0'); + let newOwner = await contract.newOwner.call(); + assert.equal(newOwner, utils.zeroAddress); + }); +}); \ No newline at end of file diff --git a/test/SafeMath.js b/test/SafeMath.js new file mode 100755 index 0000000..b1b2a7c --- /dev/null +++ b/test/SafeMath.js @@ -0,0 +1,55 @@ +/* global artifacts, contract, it, assert, web3 */ +/* eslint-disable prefer-reflect */ + +const TestSafeMath = artifacts.require('TestSafeMath'); +const utils = require('./helpers/Utils'); + +contract('SafeMath', () => { + it('verifies successful addition', async () => { + let math = await TestSafeMath.new(); + let x = 2957; + let y = 1740; + let z = await math.testSafeAdd.call(x, y); + assert.equal(z, x + y); + }); + + it('should throw on addition overflow', async () => { + let math = await TestSafeMath.new(); + let x = web3.toBigNumber('115792089237316195423570985008687907853269984665640564039457584007913129639935'); + let y = 1; + + await utils.catchRevert(math.testSafeAdd.call(x, y)); + }); + + it('verifies successful subtraction', async () => { + let math = await TestSafeMath.new(); + let x = 2957; + let y = 1740; + let z = await math.testSafeSub.call(x, y); + assert.equal(z, x - y); + }); + + it('should throw on subtraction with negative result', async () => { + let math = await TestSafeMath.new(); + let x = 10; + let y = 11; + + await utils.catchRevert(math.testSafeSub.call(x, y)); + }); + + it('verifies successful multiplication', async () => { + let math = await TestSafeMath.new(); + let x = 2957; + let y = 1740; + let z = await math.testSafeMul.call(x, y); + assert.equal(z, x * y); + }); + + it('should throw on multiplication overflow', async () => { + let math = await TestSafeMath.new(); + let x = web3.toBigNumber('15792089237316195423570985008687907853269984665640564039457584007913129639935'); + let y = 2000; + + await utils.catchRevert(math.testSafeMul.call(x, y)); + }); +}); \ No newline at end of file diff --git a/test/SmartToken.js b/test/SmartToken.js new file mode 100755 index 0000000..ab6b979 --- /dev/null +++ b/test/SmartToken.js @@ -0,0 +1,171 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const SmartToken = artifacts.require('SmartToken'); +const utils = require('./helpers/Utils'); + +contract('SmartToken', accounts => { + it('verifies the token name, symbol and decimal units after construction', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let name = await token.name.call(); + assert.equal(name, 'Token1'); + let symbol = await token.symbol.call(); + assert.equal(symbol, 'TKN1'); + let decimals = await token.decimals.call(); + assert.equal(decimals, 2); + }); + + it('should throw when attempting to construct a token with no name', async () => { + await utils.catchRevert(SmartToken.new('', 'TKN1', 2)); + }); + + it('should throw when attempting to construct a token with no symbol', async () => { + await utils.catchRevert(SmartToken.new('Token1', '', 2)); + }); + + it('verifies that the owner can disable & re-enable transfers', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.disableTransfers(true); + let transfersEnabled = await token.transfersEnabled.call(); + assert.equal(transfersEnabled, false); + await token.disableTransfers(false); + transfersEnabled = await token.transfersEnabled.call(); + assert.equal(transfersEnabled, true); + }); + + it('should throw when a non owner attempts to disable transfers', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + + await utils.catchRevert(token.disableTransfers(true, { from: accounts[1] })); + }); + + it('verifies that issue tokens updates the target balance and the total supply', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[1], 100); + let totalSupply = await token.totalSupply.call(); + assert.equal(totalSupply, 100); + let balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 100); + }); + + it('verifies that the owner can issue tokens', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[1], 100); + let balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 100); + }); + + it('verifies that the owner can issue tokens to his/her own account', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 100); + let balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 100); + }); + + it('should throw when the owner attempts to issue tokens to an invalid address', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + + await utils.catchRevert(token.issue('0x0', 100)); + }); + + it('should throw when the owner attempts to issue tokens to the token address', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + + await utils.catchRevert(token.issue(token.address, 100)); + }); + + it('should throw when a non owner attempts to issue tokens', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + + await utils.catchRevert(token.issue(accounts[1], 100, { from: accounts[2] })); + }); + + it('verifies that destroy tokens updates the target balance and the total supply', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[1], 100); + await token.destroy(accounts[1], 20); + let totalSupply = await token.totalSupply.call(); + assert.equal(totalSupply, 80); + let balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 80); + }); + + it('verifies that the owner can destroy tokens', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[1], 100); + await token.destroy(accounts[1], 20); + let balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 80); + }); + + it('verifies that the owner can destroy tokens from his/her own account', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 100); + await token.destroy(accounts[0], 20); + let balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 80); + }); + + it('verifies that a holder can destroy tokens from his/her own account', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[1], 100); + await token.destroy(accounts[1], 20); + let balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 80); + }); + + it('should throw when a non owner attempts to destroy tokens', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[1], 100); + + await utils.catchRevert(token.destroy(accounts[1], 20, { from: accounts[2] })); + }); + + it('verifies the balances after a transfer', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 10000); + await token.transfer(accounts[1], 500); + let balance; + balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 9500); + balance = await token.balanceOf.call(accounts[1]); + assert.equal(balance, 500); + }); + + it('should throw when attempting to transfer while transfers are disabled', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 1000); + let balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 1000); + await token.transfer(accounts[1], 100); + await token.disableTransfers(true); + let transfersEnabled = await token.transfersEnabled.call(); + assert.equal(transfersEnabled, false); + + await utils.catchInvalidOpcode(token.transfer(accounts[1], 100)); + }); + + it('verifies the allowance after an approval', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 10000); + await token.approve(accounts[1], 500); + let allowance = await token.allowance.call(accounts[0], accounts[1]); + assert.equal(allowance, 500); + }); + + it('should throw when attempting to transfer from while transfers are disabled', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 1000); + let balance = await token.balanceOf.call(accounts[0]); + assert.equal(balance, 1000); + await token.approve(accounts[1], 500); + let allowance = await token.allowance.call(accounts[0], accounts[1]); + assert.equal(allowance, 500); + await token.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] }); + await token.disableTransfers(true); + let transfersEnabled = await token.transfersEnabled.call(); + assert.equal(transfersEnabled, false); + + await utils.catchInvalidOpcode(token.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] })); + }); +}); \ No newline at end of file diff --git a/test/SmartTokenController.js b/test/SmartTokenController.js new file mode 100755 index 0000000..8f62076 --- /dev/null +++ b/test/SmartTokenController.js @@ -0,0 +1,138 @@ +/* global artifacts, contract, before, it, assert */ +/* eslint-disable prefer-reflect */ + +const SmartToken = artifacts.require('SmartToken'); +const SmartTokenController = artifacts.require('SmartTokenController'); +const ERC20Token = artifacts.require('ERC20Token'); +const utils = require('./helpers/Utils'); + +let token; +let tokenAddress; + +// initializes a new controller with a new token and optionally transfers ownership from the token to the controller +async function initController(accounts, activate) { + token = await SmartToken.new('Token1', 'TKN1', 2); + await token.issue(accounts[0], 20000); + let controller = await SmartTokenController.new(token.address); + + if (activate) { + await token.transferOwnership(controller.address); + await controller.acceptTokenOwnership(); + } + + return controller; +} + +contract('SmartTokenController', accounts => { + before(async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + tokenAddress = token.address; + }); + + it('verifies the token address after construction', async () => { + let controller = await SmartTokenController.new(tokenAddress); + let token = await controller.token.call(); + assert.equal(token, tokenAddress); + }); + + it('should throw when attempting to construct a controller with no token', async () => { + await utils.catchRevert(SmartTokenController.new('0x0')); + }); + + it('verifies that the owner can accept token ownership', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let controller = await SmartTokenController.new(token.address); + await token.transferOwnership(controller.address); + await controller.acceptTokenOwnership(); + let tokenOwner = await token.owner.call(); + assert.equal(tokenOwner, controller.address); + }); + + it('should throw when a non owner attempts to accept token ownership', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let controller = await SmartTokenController.new(token.address); + await token.transferOwnership(controller.address); + + await utils.catchRevert(controller.acceptTokenOwnership({ from: accounts[1] })); + }); + + it('verifies that the owner can transfer token ownership', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let controller = await SmartTokenController.new(token.address); + await token.transferOwnership(controller.address); + await controller.acceptTokenOwnership(); + + let controller2 = await SmartTokenController.new(token.address); + await controller.transferTokenOwnership(controller2.address); + await controller2.acceptTokenOwnership(); + + let tokenOwner = await token.owner.call(); + assert.equal(tokenOwner, controller2.address); + }); + + it('should throw when the owner attempts to transfer token ownership while the controller is not active', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let controller = await SmartTokenController.new(token.address); + await token.transferOwnership(controller.address); + + let controller2 = await SmartTokenController.new(token.address); + + await utils.catchRevert(controller.transferTokenOwnership(controller2.address)); + }); + + it('should throw when a non owner attempts to transfer token ownership', async () => { + let token = await SmartToken.new('Token1', 'TKN1', 2); + let controller = await SmartTokenController.new(token.address); + await token.transferOwnership(controller.address); + await controller.acceptTokenOwnership(); + + let controller2 = await SmartTokenController.new(token.address); + + await utils.catchRevert(controller.transferTokenOwnership(controller2.address, { from: accounts[1] })); + }); + + it('verifies that the owner can withdraw other tokens from the token', async () => { + let controller = await initController(accounts, true); + let ercToken = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 100000); + const prevBalance = await ercToken.balanceOf.call(accounts[0]); + await ercToken.transfer(token.address, 100); + await controller.withdrawFromToken(ercToken.address, accounts[0], 100); + const balance = await ercToken.balanceOf.call(accounts[0]); + assert.equal(prevBalance.toNumber(), balance.toNumber()); + }); + + it('should throw when the owner attempts to withdraw other tokens from the token while the controller is not active', async () => { + let controller = await initController(accounts, false); + let ercToken = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 100000); + await ercToken.transfer(token.address, 100); + + await utils.catchRevert(controller.withdrawFromToken(ercToken.address, accounts[0], 100)); + }); + + it('should throw when a non owner attempts to withdraw other tokens from the token', async () => { + let controller = await initController(accounts, true); + let ercToken = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 100000); + await ercToken.transfer(token.address, 100); + + await utils.catchRevert(controller.withdrawFromToken(ercToken.address, accounts[0], 100, { from: accounts[1] })); + }); + + it('should allow to claim tokens if caller is set as BancorX in the controller', async () => { + let bancorX = accounts[2]; + let controller = await initController(accounts, true); + await controller.setBancorX(bancorX); + await token.transfer(accounts[1], 100); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 100); + await controller.claimTokens(accounts[1], 100, {from: bancorX}); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 0); + }); + + it('should not allow to claim tokens if caller is not set as BancorX in the controller', async () => { + let bancorX = accounts[2]; + let controller = await initController(accounts, true); + await token.transfer(accounts[1], 100); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 100); + await utils.catchRevert(controller.claimTokens(accounts[1], 100, {from: bancorX})); + assert.equal((await token.balanceOf(accounts[1])).toNumber(), 100); + }); +}); \ No newline at end of file diff --git a/test/TokenHolder.js b/test/TokenHolder.js new file mode 100755 index 0000000..f7678b8 --- /dev/null +++ b/test/TokenHolder.js @@ -0,0 +1,60 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const TokenHolder = artifacts.require('TokenHolder'); +const ERC20Token = artifacts.require('ERC20Token'); +const utils = require('./helpers/Utils'); + +let holderAddress; +let erc20Token; +let erc20TokenAddress; + +// initializes the holder with some ERC20 token balance +async function initHolder() { + let holder = await TokenHolder.new(); + holderAddress = holder.address; + erc20Token = await ERC20Token.new('ERC Token 1', 'ERC1', 0, 100000); + erc20TokenAddress = erc20Token.address; + await erc20Token.transfer(holderAddress, 1000); + return holder; +} + +contract('TokenHolder', accounts => { + it('verifies that the owner can withdraw tokens', async () => { + let holder = await initHolder(); + let prevBalance = await erc20Token.balanceOf.call(accounts[2]); + await holder.withdrawTokens(erc20TokenAddress, accounts[2], 100); + let balance = await erc20Token.balanceOf.call(accounts[2]); + assert.equal(balance.toNumber(), prevBalance.plus(100).toNumber()); + }); + + it('should throw when a non owner attempts to withdraw tokens', async () => { + let holder = await initHolder(); + + await utils.catchRevert(holder.withdrawTokens(erc20TokenAddress, accounts[2], 100, { from: accounts[3] })); + }); + + it('should throw when attempting to withdraw tokens from an invalid ERC20 token address', async () => { + let holder = await initHolder(); + + await utils.catchRevert(holder.withdrawTokens('0x0', accounts[2], 100)); + }); + + it('should throw when attempting to withdraw tokens to an invalid account address', async () => { + let holder = await initHolder(); + + await utils.catchRevert(holder.withdrawTokens(erc20TokenAddress, '0x0', 100)); + }); + + it('should throw when attempting to withdraw tokens to the holder address', async () => { + let holder = await initHolder(); + + await utils.catchRevert(holder.withdrawTokens(erc20TokenAddress, holderAddress, 100)); + }); + + it('should throw when attempting to withdraw an amount greater than the holder balance', async () => { + let holder = await initHolder(); + + await utils.catchRevert(holder.withdrawTokens(erc20TokenAddress, accounts[2], 5000)); + }); +}); \ No newline at end of file diff --git a/test/Whitelist.js b/test/Whitelist.js new file mode 100755 index 0000000..d47d5d9 --- /dev/null +++ b/test/Whitelist.js @@ -0,0 +1,119 @@ +/* global artifacts, contract, it, assert */ +/* eslint-disable prefer-reflect */ + +const Whitelist = artifacts.require('Whitelist'); +const utils = require('./helpers/Utils'); + +const invalidAccount = '0x0'; + +contract('Whitelist', accounts => { + it('verifies that a given address is not whitelisted after construction', async () => { + let whitelist = await Whitelist.new(); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(!isWhitelisted); + }); + + it('verifies that the owner can add an address to the whitelist', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(isWhitelisted); + }); + + it('should throw when a non owner tries to add an address to the whitelist', async () => { + let whitelist = await Whitelist.new(); + + await utils.catchRevert(whitelist.addAddress(accounts[1], { from: accounts[2] })); + }); + + it('should throw when the owner tries to add an invalid address to the whitelist', async () => { + let whitelist = await Whitelist.new(); + + await utils.catchRevert(whitelist.addAddress(invalidAccount)); + }); + + it('verifies that the owner can add multiple addresses to the whitelist', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddresses([accounts[1], accounts[2]]); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[2]); + assert(isWhitelisted); + }); + + it('should throw when a non owner tries to add multiple addresses to the whitelist', async () => { + let whitelist = await Whitelist.new(); + + await utils.catchRevert(whitelist.addAddresses([accounts[1], accounts[2]], { from: accounts[2] })); + }); + + it('verifies that the owner can remove an address from the whitelist', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(isWhitelisted); + + await whitelist.removeAddress(accounts[1]); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(!isWhitelisted); + }); + + it('should throw when a non owner tries to remove an address from the whitelist', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddress(accounts[1]); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(isWhitelisted); + + await utils.catchRevert(whitelist.removeAddress(accounts[1], { from: accounts[2] })); + }); + + it('verifies that the owner can remove multiple addresses from the whitelist', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddresses([accounts[1], accounts[2], accounts[3]]); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[2]); + assert(isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[3]); + assert(isWhitelisted); + + await whitelist.removeAddresses([accounts[1], accounts[3]]); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(!isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[2]); + assert(isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[3]); + assert(!isWhitelisted); + }); + + it('should throw when a non owner tries to remove multiple address from the whitelist', async () => { + let whitelist = await Whitelist.new(); + await whitelist.addAddresses([accounts[1], accounts[2], accounts[3]]); + let isWhitelisted = await whitelist.isWhitelisted.call(accounts[1]); + assert(isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[2]); + assert(isWhitelisted); + isWhitelisted = await whitelist.isWhitelisted.call(accounts[3]); + assert(isWhitelisted); + + await utils.catchRevert(whitelist.removeAddresses([accounts[1], accounts[3]], { from: accounts[2] })); + }); + + it('verifies that an address can be added unless it is already added and removed unless it is already removed', async () => { + let whitelist = await Whitelist.new(); + let status0 = await whitelist.isWhitelisted.call(accounts[1]); + let response1 = await whitelist.addAddress(accounts[1]); + let status1 = await whitelist.isWhitelisted.call(accounts[1]); + let response2 = await whitelist.addAddress(accounts[1]); + let status2 = await whitelist.isWhitelisted.call(accounts[1]); + let response3 = await whitelist.removeAddress(accounts[1]); + let status3 = await whitelist.isWhitelisted.call(accounts[1]); + let response4 = await whitelist.removeAddress(accounts[1]); + let status4 = await whitelist.isWhitelisted.call(accounts[1]); + assert(!status0 && status1 && status2 && !status3 && !status4); + assert(response1.logs.length == 1 && response1.logs[0].event == "AddressAddition" && response1.logs[0].args._address == accounts[1]); + assert(response2.logs.length == 0); + assert(response3.logs.length == 1 && response3.logs[0].event == "AddressRemoval" && response3.logs[0].args._address == accounts[1]); + assert(response4.logs.length == 0); + }); +}); \ No newline at end of file diff --git a/test/XConversions.js b/test/XConversions.js new file mode 100755 index 0000000..5bbffb3 --- /dev/null +++ b/test/XConversions.js @@ -0,0 +1,552 @@ +/* global artifacts, contract, before, it, assert */ +/* eslint-disable prefer-reflect */ + +const utils = require('./helpers/Utils'); +const ContractRegistryClient = require('./helpers/ContractRegistryClient'); + +const BancorConverter = artifacts.require('BancorConverter'); +const BancorX = artifacts.require('BancorX'); +const SmartToken = artifacts.require('SmartToken'); +const EtherToken = artifacts.require('EtherToken'); +const ContractRegistry = artifacts.require('ContractRegistry'); +const BancorNetwork = artifacts.require('BancorNetwork'); +const BancorFormula = artifacts.require('BancorFormula'); +const ContractFeatures = artifacts.require('ContractFeatures'); +const ERC20Token = artifacts.require('ERC20Token'); + +const MAX_LOCK_LIMIT = '1000000000000000000000' // 1000 bnt +const MAX_RELEASE_LIMIT = '1000000000000000000000' // 1000 bnt +const MIN_LIMIT = '1000000000000000000' // 1 bnt +const LIM_INC_PER_BLOCK = '1000000000000000000' // 1 bnt +const MIN_REQUIRED_REPORTS = '3' +const BNT_AMOUNT = '920201018469141404133' +const BNT_RESERVE_AMOUNT = '650129186275318509' + +// this is just gibberish bytes32 +const eosAddress = '0xd5e9a21dbc95b47e2750562a96d365aa5fb6a75c000000000000000000000000' +const EOS_BLOCKCHAIN = '0xd5e9a21dbc95b47e2750562a96d365aa5fb6a75c000000000000000000000000' + +// bancor network contracts +let bancorX, bancorNetwork, bntConverter, bntToken, etherToken, erc20Token, erc20TokenConverter +// paths +let ethBntPath, bntEthPath, erc20TokenBntPath, bntErc20Path + +let reporter1, reporter2, reporter3, affiliateAddress + +contract("XConversions", accounts => { + describe("basic testing:", () => { + before(async () => { + await initBancorNetwork(accounts) + }) + + it("should be able to xConvertPrioritized from eth", async () => { + const path = ethBntPath + const amount = web3.toWei(1) + + const retAmount = await bancorNetwork.xConvertPrioritized.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + 0, + 0, + utils.zeroBytes32, + utils.zeroBytes32, + { from: accounts[5], value: amount } + ) + + const prevBalance = await bntToken.balanceOf(bancorX.address) + + const res = await bancorNetwork.xConvertPrioritized( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + 0, + 0, + utils.zeroBytes32, + utils.zeroBytes32, + { from: accounts[5], value: amount } + ) + + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalance).toString(10), retAmount.toString(10)) + }) + + it("should be able to xConvertPrioritized2 from eth", async () => { + const path = ethBntPath + const amount = web3.toWei(1) + + const retAmount = await bancorNetwork.xConvertPrioritized2.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + [], + { from: accounts[5], value: amount } + ) + + const prevBalance = await bntToken.balanceOf(bancorX.address) + + const res = await bancorNetwork.xConvertPrioritized2( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + [], + { from: accounts[5], value: amount } + ) + + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalance).toString(10), retAmount.toString(10)) + }) + + it("should be able to xConvert from eth", async () => { + const path = ethBntPath + const amount = web3.toWei(1) + + const retAmount = await bancorNetwork.xConvert.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + { from: accounts[5], value: amount } + ) + + const prevBalance = await bntToken.balanceOf(bancorX.address) + + const res = await bancorNetwork.xConvert( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + { from: accounts[5], value: amount } + ) + + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalance).toString(10), retAmount.toString(10)) + }) + + it("should be able to xConvert from an ERC20", async () => { + const path = erc20TokenBntPath + const amount = web3.toWei(1) + + await erc20Token.approve(bancorNetwork.address, amount, { from: accounts[5] }) + + const retAmount = await bancorNetwork.xConvert.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + { from: accounts[5] } + ) + + const prevBalance = await bntToken.balanceOf(bancorX.address) + + const res = await bancorNetwork.xConvert( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + { from: accounts[5] } + ) + + // console.log(res.receipt.gasUsed) + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalance).toString(10), retAmount.toString(10)) + }) + + it("should be able to completeXConversion to eth", async () => { + const txId = getId() + const xTransferId = getId() + const amount = web3.toWei('10') // releasing 10 BNT + const path = bntEthPath + + await reportAndRelease(accounts[5], amount, txId, EOS_BLOCKCHAIN, xTransferId) + + const prevBalance = await web3.eth.getBalance(accounts[5]) + + const res = await bntConverter.completeXConversion( + path, + 1, + xTransferId, + 0, + 0, + utils.zeroBytes32, + utils.zeroBytes32, + { from: accounts[5] } + ) + + const currBalance = await web3.eth.getBalance(accounts[5]) + + assert(currBalance.greaterThan(prevBalance)) + }) + + it("should be able to completeXConversion to an ERC20", async () => { + const txId = getId() + const xTransferId = getId() + const amount = web3.toWei('10') // releasing 10 BNT + const path = bntErc20Path + + await reportAndRelease(accounts[5], amount, txId, EOS_BLOCKCHAIN, xTransferId) + + const prevBalance = await erc20Token.balanceOf(accounts[5]) + + const retAmount = await bntConverter.completeXConversion.call( + path, + 1, + xTransferId, + 0, + 0, + utils.zeroBytes32, + utils.zeroBytes32, + { from: accounts[5] } + ) + + const res = await bntConverter.completeXConversion( + path, + 1, + xTransferId, + 0, + 0, + utils.zeroBytes32, + utils.zeroBytes32, + { from: accounts[5] } + ) + + const currBalance = await erc20Token.balanceOf(accounts[5]) + + assert.equal(currBalance.minus(prevBalance).toString(10), retAmount.toString(10)) + }) + + it("shouldn't be able to completeXConversion to an ERC20 with a different xTransferId", async () => { + const txId1 = getId() + const xTransferId1 = getId() + const txId2 = getId() + const xTransferId2 = getId() + const amount = web3.toWei('10') // releasing 10 BNT + const path = bntErc20Path + + await reportAndRelease(accounts[5], amount, txId1, EOS_BLOCKCHAIN, xTransferId1) + await reportAndRelease(accounts[4], amount, txId2, EOS_BLOCKCHAIN, xTransferId2) + + await utils.catchRevert(bntConverter.completeXConversion( + path, + 1, + xTransferId2, + 0, + 0, + utils.zeroBytes32, + utils.zeroBytes32, + { from: accounts[5] } + )) + }) + + it("should be able to completeXConversion2 to eth", async () => { + const txId = getId() + const xTransferId = getId() + const amount = web3.toWei('10') // releasing 10 BNT + const path = bntEthPath + + await reportAndRelease(accounts[5], amount, txId, EOS_BLOCKCHAIN, xTransferId) + + const prevBalance = await web3.eth.getBalance(accounts[5]) + + const res = await bntConverter.completeXConversion2( + path, + 1, + xTransferId, + { from: accounts[5] } + ) + + const currBalance = await web3.eth.getBalance(accounts[5]) + + assert(currBalance.greaterThan(prevBalance)) + }) + + it("should be able to completeXConversion2 to an ERC20", async () => { + const txId = getId() + const xTransferId = getId() + const amount = web3.toWei('10') // releasing 10 BNT + const path = bntErc20Path + + await reportAndRelease(accounts[5], amount, txId, EOS_BLOCKCHAIN, xTransferId) + + const prevBalance = await erc20Token.balanceOf(accounts[5]) + + const retAmount = await bntConverter.completeXConversion2.call( + path, + 1, + xTransferId, + { from: accounts[5] } + ) + + const res = await bntConverter.completeXConversion2( + path, + 1, + xTransferId, + { from: accounts[5] } + ) + + const currBalance = await erc20Token.balanceOf(accounts[5]) + + assert.equal(currBalance.minus(prevBalance).toString(10), retAmount.toString(10)) + }) + + it("shouldn't be able to completeXConversion2 to an ERC20 with a different xTransferId", async () => { + const txId1 = getId() + const xTransferId1 = getId() + const txId2 = getId() + const xTransferId2 = getId() + const amount = web3.toWei('10') // releasing 10 BNT + const path = bntErc20Path + + await reportAndRelease(accounts[5], amount, txId1, EOS_BLOCKCHAIN, xTransferId1) + await reportAndRelease(accounts[4], amount, txId2, EOS_BLOCKCHAIN, xTransferId2) + + await utils.catchRevert(bntConverter.completeXConversion2( + path, + 1, + xTransferId2, + { from: accounts[5] } + )) + }) + }) + + for (const percent of ["0.5", "1.0", "1.5", "2.0", "3.0"]) { + const affiliateFee = web3.toBigNumber(1000000).mul(percent).div(100) + describe(`advanced testing with affiliate-fee = ${percent}%:`, () => { + before(async () => { + await initBancorNetwork(accounts) + }) + + it("should be able to xConvertPrioritized3 from eth", async () => { + const path = ethBntPath + const amount = web3.toWei(1) + + const retAmount = await bancorNetwork.xConvertPrioritized3.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + [], + affiliateAddress, affiliateFee, + { from: accounts[5], value: amount } + ) + + const prevBalanceOfBancorX = await bntToken.balanceOf(bancorX.address) + const prevBalanceAffiliate = await bntToken.balanceOf(affiliateAddress) + + const res = await bancorNetwork.xConvertPrioritized3( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + [], + affiliateAddress, affiliateFee, + { from: accounts[5], value: amount } + ) + + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalanceOfBancorX).toString(10), retAmount.toString(10)) + assert.equal((await bntToken.balanceOf(affiliateAddress)).minus(prevBalanceAffiliate).toString(10), expectedFee(retAmount, percent).toString(10)) + }) + + it("should be able to xConvert2 from eth", async () => { + const path = ethBntPath + const amount = web3.toWei(1) + + const retAmount = await bancorNetwork.xConvert2.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + affiliateAddress, affiliateFee, + { from: accounts[5], value: amount } + ) + + const prevBalanceOfBancorX = await bntToken.balanceOf(bancorX.address) + const prevBalanceAffiliate = await bntToken.balanceOf(affiliateAddress) + + const res = await bancorNetwork.xConvert2( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + affiliateAddress, affiliateFee, + { from: accounts[5], value: amount } + ) + + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalanceOfBancorX).toString(10), retAmount.toString(10)) + assert.equal((await bntToken.balanceOf(affiliateAddress)).minus(prevBalanceAffiliate).toString(10), expectedFee(retAmount, percent).toString(10)) + }) + + it("should be able to xConvert2 from an ERC20", async () => { + const path = erc20TokenBntPath + const amount = web3.toWei(1) + + await erc20Token.approve(bancorNetwork.address, amount, { from: accounts[5] }) + + const retAmount = await bancorNetwork.xConvert2.call( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + affiliateAddress, affiliateFee, + { from: accounts[5] } + ) + + const prevBalanceOfBancorX = await bntToken.balanceOf(bancorX.address) + const prevBalanceAffiliate = await bntToken.balanceOf(affiliateAddress) + + const res = await bancorNetwork.xConvert2( + path, + amount, + 1, + EOS_BLOCKCHAIN, + eosAddress, + 0, + affiliateAddress, affiliateFee, + { from: accounts[5] } + ) + + assert.equal((await bntToken.balanceOf(bancorX.address)).minus(prevBalanceOfBancorX).toString(10), retAmount.toString(10)) + assert.equal((await bntToken.balanceOf(affiliateAddress)).minus(prevBalanceAffiliate).toString(10), expectedFee(retAmount, percent).toString(10)) + }) + }) + } +}) + +async function reportAndRelease(to, amount, txId, blockchainType, xTransferId = 0) { + for (let i = 1; i <= 3; i++) { + await bancorX.reportTx( + blockchainType, + txId, + to, + amount, + xTransferId, + { from: eval(`reporter${i}`) } + ) + } +} + +const initBancorNetwork = async accounts => { + reporter1 = accounts[1] + reporter2 = accounts[2] + reporter3 = accounts[3] + affiliateAddress = accounts[4] + + const bancorFormula = await BancorFormula.new(); + const contractRegistry = await ContractRegistry.new() + const contractFeatures = await ContractFeatures.new() + + etherToken = await EtherToken.new('Ether', 'ETH') + bntToken = await SmartToken.new('Bancor', 'BNT', 18) + bntConverter = await BancorConverter.new( + bntToken.address, + contractRegistry.address, + '30000', + etherToken.address, + '100000' + ) + + bancorX = await BancorX.new( + MAX_LOCK_LIMIT, + MAX_RELEASE_LIMIT, + MIN_LIMIT, + LIM_INC_PER_BLOCK, + MIN_REQUIRED_REPORTS, + contractRegistry.address, + bntToken.address, + true + ) + + await bancorX.setReporter(reporter1, true) + await bancorX.setReporter(reporter2, true) + await bancorX.setReporter(reporter3, true) + + await etherToken.deposit({ value: BNT_RESERVE_AMOUNT }); + await etherToken.transfer(bntConverter.address, BNT_RESERVE_AMOUNT); + + bancorNetwork = await BancorNetwork.new(contractRegistry.address); + await bancorNetwork.registerEtherToken(etherToken.address, true); + + await contractRegistry.registerAddress(ContractRegistryClient.BNT_TOKEN, bntToken.address) + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_FORMULA, bancorFormula.address) + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_NETWORK, bancorNetwork.address) + await contractRegistry.registerAddress(ContractRegistryClient.CONTRACT_FEATURES, contractFeatures.address) + await contractRegistry.registerAddress(ContractRegistryClient.BANCOR_X, bancorX.address) + + // issue bnt and transfer ownership to converter + await bntToken.issue(accounts[0], BNT_AMOUNT) + await bntToken.transferOwnership(bntConverter.address) + + // set bancorx address for bnt converter, and accept token ownership + await bntConverter.acceptTokenOwnership() + await bntConverter.setBancorX(bancorX.address) + + // creating second converter + const relayToken = await SmartToken.new('Relay Token', 'RLY', 18) + + erc20Token = await ERC20Token.new('Test Token', 'TST', 0, web3.toWei('100')) + erc20TokenConverter = await BancorConverter.new( + relayToken.address, + contractRegistry.address, + '30000', + bntToken.address, + '500000' // 100% reserve ratio + ) + + await relayToken.issue(accounts[0], web3.toWei('200')) + await erc20Token.transfer(erc20TokenConverter.address, web3.toWei('50')) + await erc20Token.transfer(accounts[5], web3.toWei('50')) + await bntToken.transfer(erc20TokenConverter.address, web3.toWei('100')) + + await erc20TokenConverter.addReserve(erc20Token.address, '500000') + await relayToken.transferOwnership(erc20TokenConverter.address) + await erc20TokenConverter.acceptTokenOwnership() + + // settings paths for easy use + ethBntPath = [etherToken.address, bntToken.address, bntToken.address] + bntEthPath = [bntToken.address, bntToken.address, etherToken.address] + erc20TokenBntPath = [erc20Token.address, relayToken.address, bntToken.address] + bntErc20Path = [bntToken.address, relayToken.address, erc20Token.address] +} + +function getId() { + if (this.id == undefined) + this.id = 0 + return ++this.id +} + +const Decimal = require("decimal.js") +Decimal.set({precision: 100, rounding: Decimal.ROUND_DOWN}) + +function expectedFee(amount, percent) { + let fee = Decimal(amount.toFixed()) + const ratio = Decimal(percent).div(100) + for (let n = 0; n < 4; n++) + fee = fee.mul(ratio.pow(2 ** n).plus(1)) + return web3.toBigNumber(fee.mul(ratio).truncated()) +} diff --git a/test/XTransferRerouter.js b/test/XTransferRerouter.js new file mode 100755 index 0000000..a2c17ea --- /dev/null +++ b/test/XTransferRerouter.js @@ -0,0 +1,69 @@ +/* global artifacts, contract, before, it, assert */ +/* eslint-disable prefer-reflect */ + +const XTransferRerouter = artifacts.require('XTransferRerouter'); +const utils = require('./helpers/Utils'); + +const EOS_ADDRESS = web3.fromAscii('just a string 1') +const EOS_BLOCKCHAIN = web3.fromAscii('just a string 2') + +let txRouter +let accounts + +contract('XTransferRerouter', async () => { + // get contracts deployed during migration (truffle migrates contracts before tests) + before(async () => { + accounts = await web3.eth.accounts + + }) + + it('verify that a user can\'t call rerouteTx when rerouting is disabled', async () => { + txRouter = await XTransferRerouter.new(false, { + from: accounts[0] + }) + await utils.catchRevert(txRouter.rerouteTx( + 123, + EOS_ADDRESS, + EOS_BLOCKCHAIN, + { from: accounts[1] } + )) + + }) + it('verify that calling rerouteTx emits an event properly', async () => { + txRouter = await XTransferRerouter.new(true, { + from: accounts[0] + }) + const tx = await txRouter.rerouteTx( + 123, + EOS_ADDRESS, + EOS_BLOCKCHAIN, + { from: accounts[1] } + ) + let event = tx.logs.some(log => log.event == 'TxReroute') + assert(event, 'TxReroute event was not emitted') + }) + + it('verify that a non-owner can\'t call enableRerouting', async () => { + txRouter = await XTransferRerouter.new(false, { + from: accounts[0] + }) + await utils.catchRevert(txRouter.enableRerouting( + true, + { from: accounts[1] } + )) + }) + + it('verify that the owner can call enableRerouting', async () => { + txRouter = await XTransferRerouter.new(false, { + from: accounts[0] + }) + let reroutingEnabledBefore = await txRouter.reroutingEnabled.call() + await txRouter.enableRerouting( + true, + { from: accounts[0] } + ) + let reroutingEnabledAfter = await txRouter.reroutingEnabled.call() + assert(!reroutingEnabledBefore && reroutingEnabledAfter, 'reroutingEnabled didn\'t get updated properly!') + }) +}) + diff --git a/test/bin/bancor_converter_v10.abi b/test/bin/bancor_converter_v10.abi new file mode 100755 index 0000000..5055824 --- /dev/null +++ b/test/bin/bancor_converter_v10.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_weight","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"},{"name":"_virtualBalance","type":"uint256"}],"name":"updateConnector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"connectors","outputs":[{"name":"virtualBalance","type":"uint256"},{"name":"weight","type":"uint32"},{"name":"isVirtualBalanceEnabled","type":"bool"},{"name":"isPurchaseEnabled","type":"bool"},{"name":"isSet","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"connectorTokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"}],"name":"getReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"},{"name":"_block","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"name":"quickConvertPrioritized","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableConversions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convertInternal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"acceptTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_amount","type":"uint256"},{"name":"_magnitude","type":"uint8"}],"name":"getFinalAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"converterType","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_weight","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"}],"name":"addConnector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawFromToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newManager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_whitelist","type":"address"}],"name":"setConversionWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"clearQuickBuyPath","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_disable","type":"bool"}],"name":"disableConnectorPurchases","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_CONVERTER_FACTORY","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"change","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_FORMULA","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"connectorTokenCount","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getSaleReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convert","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CONTRACT_FEATURES","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableTokenTransfers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromConnectorToken","type":"address"},{"name":"_toConnectorToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getCrossConnectorReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_NETWORK","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_GAS_PRICE_LIMIT","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CONVERTER_CONVERSION_WHITELIST","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuickBuyPathLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxConversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_depositAmount","type":"uint256"}],"name":"getPurchaseReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_registry","type":"address"}],"name":"setRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"conversionsEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionWhitelist","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"}],"name":"setQuickBuyPath","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"}],"name":"getConnectorBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newManager","type":"address"}],"name":"transferManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"quickBuyPath","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_conversionFee","type":"uint32"}],"name":"setConversionFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"quickConvert","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_token","type":"address"},{"name":"_registry","type":"address"},{"name":"_maxConversionFee","type":"uint32"},{"name":"_connectorToken","type":"address"},{"name":"_connectorWeight","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_fromToken","type":"address"},{"indexed":true,"name":"_toToken","type":"address"},{"indexed":true,"name":"_trader","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"},{"indexed":false,"name":"_return","type":"uint256"},{"indexed":false,"name":"_conversionFee","type":"int256"}],"name":"Conversion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_connectorToken","type":"address"},{"indexed":false,"name":"_tokenSupply","type":"uint256"},{"indexed":false,"name":"_connectorBalance","type":"uint256"},{"indexed":false,"name":"_connectorWeight","type":"uint32"}],"name":"PriceDataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_prevFee","type":"uint32"},{"indexed":false,"name":"_newFee","type":"uint32"}],"name":"ConversionFeeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_prevManager","type":"address"},{"indexed":true,"name":"_newManager","type":"address"}],"name":"ManagerUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_prevOwner","type":"address"},{"indexed":true,"name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"}] \ No newline at end of file diff --git a/test/bin/bancor_converter_v10.bin b/test/bin/bancor_converter_v10.bin new file mode 100755 index 0000000..828a323 --- /dev/null +++ b/test/bin/bancor_converter_v10.bin @@ -0,0 +1 @@ +60c0604052600460808190527f302e31300000000000000000000000000000000000000000000000000000000060a090815262000040916005919062000594565b506040805180820190915260068082527f62616e636f720000000000000000000000000000000000000000000000000000602090920191825262000085918162000594565b50600c80546001606860020a0319166c01000000000000000000000000179055348015620000b257600080fd5b5060405160a08062003ce58339810160409081528151602083015191830151606084015160809094015160008054600160a060020a03191633600160a060020a039081169190911782559395929386908190811615156200011257600080fd5b5060028054600160a060020a0319908116600160a060020a0393841617909155600380549091163383161790558590811615156200014f57600080fd5b8460008163ffffffff1610158015620001715750620f424063ffffffff821611155b15156200017d57600080fd5b60078054600160a060020a031916600160a060020a038981169190911791829055604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f436f6e747261637446656174757265730000000000000000000000000000000060048201529051929091169163bb34534c916024808201926020929091908290030181600087803b1580156200021b57600080fd5b505af115801562000230573d6000803e3d6000fd5b505050506040513d60208110156200024757600080fd5b50519250600160a060020a03831615620002de57604080517f2c7077c000000000000000000000000000000000000000000000000000000000815260016004820181905260248201529051600160a060020a03851691632c7077c091604480830192600092919082900301818387803b158015620002c457600080fd5b505af1158015620002d9573d6000803e3d6000fd5b505050505b600c805467ffffffff00000000191664010000000063ffffffff891602179055600160a060020a038516156200032557620003258585600064010000000062000333810204565b505050505050505062000639565b60005433600160a060020a039081169116146200034c57fe5b600254604080517f8da5cb5b0000000000000000000000000000000000000000000000000000000081529051600160a060020a03308116931691638da5cb5b9160048083019260209291908290030181600087803b158015620003ae57600080fd5b505af1158015620003c3573d6000803e3d6000fd5b505050506040513d6020811015620003da57600080fd5b5051600160a060020a03161415620003ee57fe5b82600160a060020a03811615156200040557600080fd5b8330600160a060020a031681600160a060020a0316141515156200042857600080fd5b8360008163ffffffff16118015620004495750620f424063ffffffff821611155b15156200045557600080fd5b600254600160a060020a038781169116148015906200049a5750600160a060020a0386166000908152600b60205260409020600101546601000000000000900460ff16155b8015620004b95750600c54620f424063ffffffff918216870190911611155b1515620004c557600080fd5b505050600160a060020a039092166000818152600b602052604081208181556001908101805466ff0000000000001965ff0000000000199715156401000000000264ff000000001963ffffffff808a1663ffffffff19958616179190911691909117989098166501000000000017166601000000000000179091556009805492830181559092527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af018054600160a060020a031916909217909155600c80548085169093019093169116179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005d757805160ff191683800117855562000607565b8280016001018555821562000607579182015b8281111562000607578251825591602001919060010190620005ea565b506200061592915062000619565b5090565b6200063691905b8082111562000615576000815560010162000620565b90565b61369c80620006496000396000f3006080604052600436106102585763ffffffff60e060020a6000350416630ca7892381146102c15780630e53aae9146102f557806319b640151461034a5780631e1401f81461037e57806321e6b53d146103ba57806322742564146103db578063228d2820146104435780632a2e2f0c1461045d57806338a5e0161461048a5780633aa0145a1461049f5780633e8ff43f146104bd5780633f4d2fc21461054757806341a5b33d1461057657806342906029146105a0578063481c6a75146105b55780634af80f0e146105ca5780634e2280c4146105eb578063514385be1461060057806354fd4d5014610626578063579cd3ca1461063b5780635a46f06c146106695780635e35359e1461067e5780635e5144eb146106a85780636d7bd3fc146106d557806371f52bf3146106ea57806372b44b2c1461071657806375892cf11461073a57806379ba5097146107675780637b1039991461077c57806383315b6e1461079157806385d5e631146107a65780638da5cb5b146107c05780638e3047e0146107d55780639232494e146107ff5780639249993a1461081457806392d1abb7146108295780639396a7f01461083e57806394c275ad14610853578063a2c4c33614610868578063a91ee0dc1461088c578063bf754558146108ad578063c45d3d92146108d6578063c8c2fe6c146108eb578063d395ee0f14610900578063d4ee1d9014610955578063d89595121461096a578063e4edf8521461098b578063e7ee85a5146109ac578063ecbca55d146109c4578063f0843ba9146109e2578063f2fde38b14610a33578063fc0c546a14610a54575b6102be600a8054806020026020016040519081016040528092919081815260200182805480156102b157602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610293575b5050505050346001610a69565b50005b3480156102cd57600080fd5b506102f3600160a060020a036004351663ffffffff602435166044351515606435610ab4565b005b34801561030157600080fd5b50610316600160a060020a0360043516610bc8565b6040805195865263ffffffff9094166020860152911515848401521515606084015215156080830152519081900360a00190f35b34801561035657600080fd5b50610362600435610c0e565b60408051600160a060020a039092168252519081900360200190f35b34801561038a57600080fd5b506103a8600160a060020a0360043581169060243516604435610c36565b60408051918252519081900360200190f35b3480156103c657600080fd5b506102f3600160a060020a0360043516610cad565b604080516020600480358082013583810280860185019096528085526103a895369593946024949385019291829185019084908082843750949750508435955050506020830135926040810135925060ff606082013516915060808101359060a00135610d47565b34801561044f57600080fd5b506102f3600435151561114a565b34801561046957600080fd5b506103a8600160a060020a03600435811690602435166044356064356111ad565b34801561049657600080fd5b506102f36116f7565b3480156104ab57600080fd5b506103a860043560ff6024351661177c565b3480156104c957600080fd5b506104d26117d8565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561050c5781810151838201526020016104f4565b50505050905090810190601f1680156105395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561055357600080fd5b506102f3600160a060020a036004351663ffffffff602435166044351515611866565b34801561058257600080fd5b506102f3600160a060020a0360043581169060243516604435611ac8565b3480156105ac57600080fd5b50610362611b73565b3480156105c157600080fd5b50610362611b82565b3480156105d657600080fd5b506102f3600160a060020a0360043516611b91565b3480156105f757600080fd5b506102f3611bfb565b34801561060c57600080fd5b506102f3600160a060020a03600435166024351515611c23565b34801561063257600080fd5b506104d2611cac565b34801561064757600080fd5b50610650611d07565b6040805163ffffffff9092168252519081900360200190f35b34801561067557600080fd5b506103a8611d1f565b34801561068a57600080fd5b506102f3600160a060020a0360043581169060243516604435611d43565b3480156106b457600080fd5b506103a8600160a060020a0360043581169060243516604435606435611e48565b3480156106e157600080fd5b506103a8611e56565b3480156106f657600080fd5b506106ff611e7a565b6040805161ffff9092168252519081900360200190f35b34801561072257600080fd5b506103a8600160a060020a0360043516602435611e81565b34801561074657600080fd5b506103a8600160a060020a0360043581169060243516604435606435612180565b34801561077357600080fd5b506102f3612221565b34801561078857600080fd5b506103626122ad565b34801561079d57600080fd5b506103a86122bc565b3480156107b257600080fd5b506102f360043515156122e0565b3480156107cc57600080fd5b5061036261235f565b3480156107e157600080fd5b506103a8600160a060020a036004358116906024351660443561236e565b34801561080b57600080fd5b506103a8612665565b34801561082057600080fd5b506103a8612689565b34801561083557600080fd5b506103a86126ad565b34801561084a57600080fd5b506103a86126b2565b34801561085f57600080fd5b506106506126b8565b34801561087457600080fd5b506103a8600160a060020a03600435166024356126cc565b34801561089857600080fd5b506102f3600160a060020a036004351661299f565b3480156108b957600080fd5b506108c2612a20565b604080519115158252519081900360200190f35b3480156108e257600080fd5b50610362612a39565b3480156108f757600080fd5b506102f3612a48565b34801561090c57600080fd5b50604080516020600480358082013583810280860185019096528085526102f395369593946024949385019291829185019084908082843750949750612ad69650505050505050565b34801561096157600080fd5b50610362612b36565b34801561097657600080fd5b506103a8600160a060020a0360043516612b45565b34801561099757600080fd5b506102f3600160a060020a0360043516612c43565b3480156109b857600080fd5b50610362600435612cc3565b3480156109d057600080fd5b506102f363ffffffff60043516612cd1565b604080516020600480358082013583810280860185019096528085526103a895369593946024949385019291829185019084908082843750949750508435955050506020909201359150610a699050565b348015610a3f57600080fd5b506102f3600160a060020a0360043516612dc0565b348015610a6057600080fd5b50610362612e22565b60008360028151118015610a7f57506015815111155b8015610a9057508051600290066001145b1515610a9b57600080fd5b610aab8585856000808080610d47565b95945050505050565b6000805433600160a060020a03908116911614610acd57fe5b600160a060020a0385166000908152600b602052604090206001015485906601000000000000900460ff161515610b0357600080fd5b8460008163ffffffff16118015610b235750620f424063ffffffff821611155b1515610b2e57600080fd5b600160a060020a0387166000908152600b602052604090206001810154600c54919450620f424063ffffffff918216928216929092038801161115610b7257600080fd5b5050600181018054600c805463ffffffff928316818416038801831663ffffffff199182161790915582549515156401000000000264ff0000000019929097169516949094179390931693909317909155905550565b600b602052600090815260409020805460019091015463ffffffff81169060ff640100000000820481169165010000000000810482169166010000000000009091041685565b6009805482908110610c1c57fe5b600091825260209091200154600160a060020a0316905081565b6000600160a060020a038481169084161415610c5157600080fd5b600254600160a060020a0384811691161415610c7857610c7184836126cc565b9050610ca6565b600254600160a060020a0385811691161415610c9857610c718383611e81565b610ca384848461236e565b90505b9392505050565b60005433600160a060020a03908116911614610cc557fe5b600254604080517ff2fde38b000000000000000000000000000000000000000000000000000000008152600160a060020a0384811660048301529151919092169163f2fde38b91602480830192600092919082900301818387803b158015610d2c57600080fd5b505af1158015610d40573d6000803e3d6000fd5b5050505050565b60008060008960028151118015610d6057506015815111155b8015610d7157508051600290066001145b1515610d7c57600080fd5b8a6000815181101515610d8b57fe5b6020908102909101810151600754604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f42616e636f724e6574776f726b0000000000000000000000000000000000000060048201529051929650600160a060020a039091169263bb34534c926024808401938290030181600087803b158015610e1957600080fd5b505af1158015610e2d573d6000803e3d6000fd5b505050506040513d6020811015610e4357600080fd5b5051915034151561102757600254600160a060020a0384811691161415610f7f57600254604080517fa24835d1000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152602482018e90529151919092169163a24835d191604480830192600092919082900301818387803b158015610ed257600080fd5b505af1158015610ee6573d6000803e3d6000fd5b50505050600260009054906101000a9004600160a060020a0316600160a060020a031663867904b4838c6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b158015610f6257600080fd5b505af1158015610f76573d6000803e3d6000fd5b50505050611027565b604080517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0333811660048301528481166024830152604482018d90529151918516916323b872dd916064808201926020929091908290030181600087803b158015610ff357600080fd5b505af1158015611007573d6000803e3d6000fd5b505050506040513d602081101561101d57600080fd5b5051151561102757fe5b81600160a060020a0316636b08f2ef348d8d8d338e8e8e8e6040518a63ffffffff1660e060020a028152600401808060200189815260200188815260200187600160a060020a0316600160a060020a031681526020018681526020018560ff1660ff1681526020018460001916600019168152602001836000191660001916815260200182810382528a818151815260200191508051906020019060200280838360005b838110156110e35781810151838201526020016110cb565b5050505090500199505050505050505050506020604051808303818588803b15801561110e57600080fd5b505af1158015611122573d6000803e3d6000fd5b50505050506040513d602081101561113957600080fd5b50519b9a5050505050505050505050565b60005433600160a060020a0390811691161480611175575060035433600160a060020a039081169116145b151561118057600080fd5b600c80546cff000000000000000000000000191691156c0100000000000000000000000002919091179055565b600754604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f42616e636f724e6574776f726b0000000000000000000000000000000000000060048201529051600092839283928392839283928392600160a060020a03169163bb34534c91602480830192602092919082900301818787803b15801561123e57600080fd5b505af1158015611252573d6000803e3d6000fd5b505050506040513d602081101561126857600080fd5b5051905033600160a060020a039081169082161461128557600080fd5b600c546c01000000000000000000000000900460ff1615156112a357fe5b87600081116112b157600080fd5b600160a060020a038c8116908c1614156112ca57600080fd5b600254600160a060020a038c8116911614156112f2576112eb8c8b8b612e31565b97506116e8565b600254600160a060020a038d811691161415611313576112eb8b8b8b6130dc565b61131e8c8c8c61236e565b9650861580159061132f5750888710155b151561133a57600080fd5b600160a060020a038c166000908152600b602052604090206001810154909650640100000000900460ff1615611379578554611376908b6134c5565b86555b600160a060020a038b166000908152600b602052604090206001810154909550640100000000900460ff16156113b85784546113b590886134d4565b85555b6113c18b612b45565b93508387106113cc57fe5b604080517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0333811660048301523081166024830152604482018d90529151918e16916323b872dd916064808201926020929091908290030181600087803b15801561144057600080fd5b505af1158015611454573d6000803e3d6000fd5b505050506040513d602081101561146a57600080fd5b5051151561147457fe5b8a600160a060020a031663a9059cbb33896040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b1580156114d757600080fd5b505af11580156114eb573d6000803e3d6000fd5b505050506040513d602081101561150157600080fd5b5051151561150b57fe5b61151f8761151a89600261177c565b6134d4565b925061152e8c8c8c8a876134e6565b8b600160a060020a03167f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156115ac57600080fd5b505af11580156115c0573d6000803e3d6000fd5b505050506040513d60208110156115d657600080fd5b50516115e18f612b45565b60018a015460408051938452602084019290925263ffffffff1682820152519081900360600190a28a600160a060020a03167f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561168757600080fd5b505af115801561169b573d6000803e3d6000fd5b505050506040513d60208110156116b157600080fd5b50516116bc8e612b45565b600189015460408051938452602084019290925263ffffffff1682820152519081900360600190a28697505b50505050505050949350505050565b60005433600160a060020a0390811691161461170f57fe5b600260009054906101000a9004600160a060020a0316600160a060020a03166379ba50976040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561176257600080fd5b505af1158015611776573d6000803e3d6000fd5b50505050565b60008160ff16620f42400a67ffffffffffffffff166117c7848460ff16600c60089054906101000a900463ffffffff1663ffffffff16620f4240030a67ffffffffffffffff1661356b565b8115156117d057fe5b049392505050565b6006805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561185e5780601f106118335761010080835404028352916020019161185e565b820191906000526020600020905b81548152906001019060200180831161184157829003601f168201915b505050505081565b60005433600160a060020a0390811691161461187e57fe5b600254604080517f8da5cb5b0000000000000000000000000000000000000000000000000000000081529051600160a060020a03308116931691638da5cb5b9160048083019260209291908290030181600087803b1580156118df57600080fd5b505af11580156118f3573d6000803e3d6000fd5b505050506040513d602081101561190957600080fd5b5051600160a060020a0316141561191c57fe5b82600160a060020a038116151561193257600080fd5b8330600160a060020a031681600160a060020a03161415151561195457600080fd5b8360008163ffffffff161180156119745750620f424063ffffffff821611155b151561197f57600080fd5b600254600160a060020a038781169116148015906119c35750600160a060020a0386166000908152600b60205260409020600101546601000000000000900460ff16155b80156119e15750600c54620f424063ffffffff918216870190911611155b15156119ec57600080fd5b505050600160a060020a039092166000818152600b602052604081208181556001908101805466ff0000000000001965ff0000000000199715156401000000000264ff000000001963ffffffff808a1663ffffffff19958616179190911691909117989098166501000000000017166601000000000000179091556009805492830181559092527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01805473ffffffffffffffffffffffffffffffffffffffff1916909217909155600c80548085169093019093169116179055565b60005433600160a060020a03908116911614611ae057fe5b600254604080517f5e35359e000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015285811660248301526044820185905291519190921691635e35359e91606480830192600092919082900301818387803b158015611b5657600080fd5b505af1158015611b6a573d6000803e3d6000fd5b50505050505050565b600454600160a060020a031681565b600354600160a060020a031681565b60005433600160a060020a03908116911614611ba957fe5b8030600160a060020a031681600160a060020a031614151515611bcb57600080fd5b506008805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b60005433600160a060020a03908116911614611c1357fe5b6000611c20600a8261358f565b50565b60005433600160a060020a03908116911614611c3b57fe5b600160a060020a0382166000908152600b602052604090206001015482906601000000000000900460ff161515611c7157600080fd5b50600160a060020a03919091166000908152600b60205260409020600101805465ff0000000000191691156501000000000002919091179055565b6005805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561185e5780601f106118335761010080835404028352916020019161185e565b600c5468010000000000000000900463ffffffff1681565b7f42616e636f72436f6e766572746572466163746f72790000000000000000000081565b60005433600160a060020a03908116911614611d5b57fe5b82600160a060020a0381161515611d7157600080fd5b82600160a060020a0381161515611d8757600080fd5b8330600160a060020a031681600160a060020a031614151515611da957600080fd5b85600160a060020a031663a9059cbb86866040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b158015611e0c57600080fd5b505af1158015611e20573d6000803e3d6000fd5b505050506040513d6020811015611e3657600080fd5b50511515611e4057fe5b505050505050565b6000610aab858585856111ad565b7f42616e636f72466f726d756c610000000000000000000000000000000000000081565b6009545b90565b60008060008060008030600160a060020a0316600260009054906101000a9004600160a060020a0316600160a060020a0316638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611ee757600080fd5b505af1158015611efb573d6000803e3d6000fd5b505050506040513d6020811015611f1157600080fd5b5051600160a060020a031614611f2357fe5b600160a060020a0388166000908152600b602052604090206001015488906601000000000000900460ff161515611f5957600080fd5b600160a060020a03808a166000908152600b6020908152604080832060025482517f18160ddd0000000000000000000000000000000000000000000000000000000081529251919b50909416936318160ddd93600480840194938390030190829087803b158015611fc957600080fd5b505af1158015611fdd573d6000803e3d6000fd5b505050506040513d6020811015611ff357600080fd5b5051945061200089612b45565b600754604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f42616e636f72466f726d756c610000000000000000000000000000000000000060048201529051929650600160a060020a039091169163bb34534c916024808201926020929091908290030181600087803b15801561208957600080fd5b505af115801561209d573d6000803e3d6000fd5b505050506040513d60208110156120b357600080fd5b50516001870154604080517f49f9b0f7000000000000000000000000000000000000000000000000000000008152600481018990526024810188905263ffffffff9092166044830152606482018b905251919450600160a060020a038516916349f9b0f7916084808201926020929091908290030181600087803b15801561213a57600080fd5b505af115801561214e573d6000803e3d6000fd5b505050506040513d602081101561216457600080fd5b5051915061217382600161177c565b9998505050505050505050565b60408051606081018252600160a060020a038087168252600254811660208301528516918101919091526000906121bb90600d9060036135b3565b50610aab600d80548060200260200160405190810160405280929190818152602001828054801561221557602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116121f7575b50505050508484610a69565b60015433600160a060020a0390811691161461223c57600080fd5b60015460008054604051600160a060020a0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a3600180546000805473ffffffffffffffffffffffffffffffffffffffff19908116600160a060020a03841617909155169055565b600754600160a060020a031681565b7f436f6e747261637446656174757265730000000000000000000000000000000081565b60005433600160a060020a039081169116146122f857fe5b600254604080517f1608f18f00000000000000000000000000000000000000000000000000000000815283151560048201529051600160a060020a0390921691631608f18f9160248082019260009290919082900301818387803b158015610d2c57600080fd5b600054600160a060020a031681565b600080600080600080600030600160a060020a0316600260009054906101000a9004600160a060020a0316600160a060020a0316638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156123d657600080fd5b505af11580156123ea573d6000803e3d6000fd5b505050506040513d602081101561240057600080fd5b5051600160a060020a03161461241257fe5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561244857600080fd5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561247e57600080fd5b600160a060020a038c81166000908152600b6020526040808220928e16825290206001810154919950975065010000000000900460ff1615156124c057600080fd5b6124c98c612b45565b95506124d48b612b45565b600754604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f42616e636f72466f726d756c610000000000000000000000000000000000000060048201529051929750600160a060020a039091169163bb34534c916024808201926020929091908290030181600087803b15801561255d57600080fd5b505af1158015612571573d6000803e3d6000fd5b505050506040513d602081101561258757600080fd5b505160018981015490890154604080517f65098bb3000000000000000000000000000000000000000000000000000000008152600481018b905263ffffffff9384166024820152604481018a9052929091166064830152608482018d905251919550600160a060020a038616916365098bb39160a4808201926020929091908290030181600087803b15801561261c57600080fd5b505af1158015612630573d6000803e3d6000fd5b505050506040513d602081101561264657600080fd5b5051925061265583600261177c565b9c9b505050505050505050505050565b7f42616e636f724e6574776f726b0000000000000000000000000000000000000081565b7f42616e636f7247617350726963654c696d69740000000000000000000000000081565b600181565b600a5490565b600c54640100000000900463ffffffff1681565b60008060008060008030600160a060020a0316600260009054906101000a9004600160a060020a0316600160a060020a0316638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561273257600080fd5b505af1158015612746573d6000803e3d6000fd5b505050506040513d602081101561275c57600080fd5b5051600160a060020a03161461276e57fe5b600160a060020a0388166000908152600b602052604090206001015488906601000000000000900460ff1615156127a457600080fd5b600160a060020a0389166000908152600b60205260409020600181015490965065010000000000900460ff1615156127db57600080fd5b600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561282e57600080fd5b505af1158015612842573d6000803e3d6000fd5b505050506040513d602081101561285857600080fd5b5051945061286589612b45565b600754604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f42616e636f72466f726d756c610000000000000000000000000000000000000060048201529051929650600160a060020a039091169163bb34534c916024808201926020929091908290030181600087803b1580156128ee57600080fd5b505af1158015612902573d6000803e3d6000fd5b505050506040513d602081101561291857600080fd5b50516001870154604080517f29a00e7c000000000000000000000000000000000000000000000000000000008152600481018990526024810188905263ffffffff9092166044830152606482018b905251919450600160a060020a038516916329a00e7c916084808201926020929091908290030181600087803b15801561213a57600080fd5b60005433600160a060020a039081169116146129b757fe5b80600160a060020a03811615156129cd57600080fd5b8130600160a060020a031681600160a060020a0316141515156129ef57600080fd5b50506007805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600c546c01000000000000000000000000900460ff1681565b600854600160a060020a031681565b60045433600160a060020a03908116911614612a6357600080fd5b600454600354604051600160a060020a0392831692909116907fbe4cc281795971a471c980e842627a7f1ea3892ddfce8c5b6357cd2611c1973290600090a3600480546003805473ffffffffffffffffffffffffffffffffffffffff19908116600160a060020a03841617909155169055565b60005433600160a060020a03908116911614612aee57fe5b8060028151118015612b0257506015815111155b8015612b1357508051600290066001145b1515612b1e57600080fd5b8151612b3190600a9060208501906135b3565b505050565b600154600160a060020a031681565b600160a060020a0381166000908152600b6020526040812060010154819083906601000000000000900460ff161515612b7d57600080fd5b600160a060020a0384166000908152600b602052604090206001810154909250640100000000900460ff16612c385783600160a060020a03166370a08231306040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b158015612c0757600080fd5b505af1158015612c1b573d6000803e3d6000fd5b505050506040513d6020811015612c3157600080fd5b5051612c3b565b81545b949350505050565b60005433600160a060020a0390811691161480612c6e575060035433600160a060020a039081169116145b1515612c7957600080fd5b600354600160a060020a0382811691161415612c9457600080fd5b6004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600a805482908110610c1c57fe5b60005433600160a060020a0390811691161480612cfc575060035433600160a060020a039081169116145b1515612d0757600080fd5b8060008163ffffffff1610158015612d335750600c5463ffffffff640100000000909104811690821611155b1515612d3e57600080fd5b600c546040805163ffffffff6801000000000000000090930483168152918416602083015280517f81cd2ffb37dd237c0e4e2a3de5265fcf9deb43d3e7801e80db9f1ccfba7ee6009281900390910190a150600c805463ffffffff90921668010000000000000000026bffffffff000000000000000019909216919091179055565b60005433600160a060020a03908116911614612dd857fe5b600054600160a060020a0382811691161415612df357600080fd5b6001805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600254600160a060020a031681565b600080600080612e4187876126cc565b92508215801590612e525750848310155b1515612e5d57600080fd5b600160a060020a0387166000908152600b602052604090206001810154909250640100000000900460ff1615612e9c578154612e9990876134c5565b82555b604080517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0333811660048301523081166024830152604482018990529151918916916323b872dd916064808201926020929091908290030181600087803b158015612f1057600080fd5b505af1158015612f24573d6000803e3d6000fd5b505050506040513d6020811015612f3a57600080fd5b50511515612f4457fe5b600254604080517f867904b4000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152602482018790529151919092169163867904b491604480830192600092919082900301818387803b158015612fb257600080fd5b505af1158015612fc6573d6000803e3d6000fd5b50505050612fd98361151a85600161177c565b600254909150612ff6908890600160a060020a03168886856134e6565b86600160a060020a03167f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561307457600080fd5b505af1158015613088573d6000803e3d6000fd5b505050506040513d602081101561309e57600080fd5b50516130a98a612b45565b600186015460408051938452602084019290925263ffffffff1682820152519081900360600190a2509095945050505050565b600254604080517f70a08231000000000000000000000000000000000000000000000000000000008152600160a060020a03338116600483015291516000938493849384938493849316916370a0823191602480830192602092919082900301818787803b15801561314d57600080fd5b505af1158015613161573d6000803e3d6000fd5b505050506040513d602081101561317757600080fd5b505188111561318557600080fd5b61318f8989611e81565b945084158015906131a05750868510155b15156131ab57600080fd5b600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156131fe57600080fd5b505af1158015613212573d6000803e3d6000fd5b505050506040513d602081101561322857600080fd5b5051935061323589612b45565b92508285108061324e5750828514801561324e57508388145b151561325657fe5b600160a060020a0389166000908152600b602052604090206001810154909250640100000000900460ff161561329557815461329290866134d4565b82555b600254604080517fa24835d1000000000000000000000000000000000000000000000000000000008152600160a060020a033381166004830152602482018c90529151919092169163a24835d191604480830192600092919082900301818387803b15801561330357600080fd5b505af1158015613317573d6000803e3d6000fd5b5050505088600160a060020a031663a9059cbb33876040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b15801561337e57600080fd5b505af1158015613392573d6000803e3d6000fd5b505050506040513d60208110156133a857600080fd5b505115156133b257fe5b6133c18561151a87600161177c565b6002549091506133dd90600160a060020a03168a8a88856134e6565b88600160a060020a03167f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561345b57600080fd5b505af115801561346f573d6000803e3d6000fd5b505050506040513d602081101561348557600080fd5b50516134908c612b45565b600186015460408051938452602084019290925263ffffffff1682820152519081900360600190a25092979650505050505050565b600082820183811015610ca657fe5b6000818310156134e057fe5b50900390565b7f800000000000000000000000000000000000000000000000000000000000000081111561351057fe5b60408051848152602081018490528082018390529051600160a060020a033381169287821692918916917f276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb9181900360600190a45050505050565b6000828202831580613587575082848281151561358457fe5b04145b1515610ca657fe5b815481835581811115612b3157600083815260209020612b31918101908301613625565b828054828255906000526020600020908101928215613615579160200282015b82811115613615578251825473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039091161782556020909201916001909101906135d3565b5061362192915061363f565b5090565b611e7e91905b80821115613621576000815560010161362b565b611e7e91905b8082111561362157805473ffffffffffffffffffffffffffffffffffffffff191681556001016136455600a165627a7a723058207222a3b070f5f814d7a34fa79f50764677d4d3759aafdd49d616faf8b33d822c0029 \ No newline at end of file diff --git a/test/bin/bancor_converter_v11.abi b/test/bin/bancor_converter_v11.abi new file mode 100755 index 0000000..a896dc4 --- /dev/null +++ b/test/bin/bancor_converter_v11.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"BANCOR_CONVERTER_UPGRADER","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_weight","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"},{"name":"_virtualBalance","type":"uint256"}],"name":"updateConnector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"connectors","outputs":[{"name":"virtualBalance","type":"uint256"},{"name":"weight","type":"uint32"},{"name":"isVirtualBalanceEnabled","type":"bool"},{"name":"isPurchaseEnabled","type":"bool"},{"name":"isSet","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"connectorTokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BNT_TOKEN","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"}],"name":"getReturn","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allowRegistryUpdate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"},{"name":"_block","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"name":"quickConvertPrioritized","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableConversions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"CONTRACT_REGISTRY","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convertInternal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"acceptTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_amount","type":"uint256"},{"name":"_magnitude","type":"uint8"}],"name":"getFinalAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"converterType","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_weight","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"}],"name":"addConnector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"liquidate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawFromToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newManager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"updateRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_whitelist","type":"address"}],"name":"setConversionWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_disable","type":"bool"}],"name":"disableConnectorPurchases","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_CONVERTER_FACTORY","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"change","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"prevRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BNT_CONVERTER","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_FORMULA","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"connectorTokenCount","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getSaleReturn","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convert","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CONTRACT_FEATURES","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableTokenTransfers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"claimTokensEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromConnectorToken","type":"address"},{"name":"_toConnectorToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getCrossConnectorReturn","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_NETWORK","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_GAS_PRICE_LIMIT","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CONVERTER_CONVERSION_WHITELIST","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxConversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_depositAmount","type":"uint256"}],"name":"getPurchaseReturn","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_enable","type":"bool"}],"name":"enableClaimTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"restoreRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"conversionsEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionWhitelist","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_X","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"fund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"}],"name":"getConnectorBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newManager","type":"address"}],"name":"transferManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_conversionFee","type":"uint32"}],"name":"setConversionFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"quickConvert","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableRegistryUpdate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_amount","type":"uint256"}],"name":"claimTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_token","type":"address"},{"name":"_registry","type":"address"},{"name":"_maxConversionFee","type":"uint32"},{"name":"_connectorToken","type":"address"},{"name":"_connectorWeight","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_fromToken","type":"address"},{"indexed":true,"name":"_toToken","type":"address"},{"indexed":true,"name":"_trader","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"},{"indexed":false,"name":"_return","type":"uint256"},{"indexed":false,"name":"_conversionFee","type":"int256"}],"name":"Conversion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_connectorToken","type":"address"},{"indexed":false,"name":"_tokenSupply","type":"uint256"},{"indexed":false,"name":"_connectorBalance","type":"uint256"},{"indexed":false,"name":"_connectorWeight","type":"uint32"}],"name":"PriceDataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_prevFee","type":"uint32"},{"indexed":false,"name":"_newFee","type":"uint32"}],"name":"ConversionFeeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_conversionsEnabled","type":"bool"}],"name":"ConversionsEnable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_prevManager","type":"address"},{"indexed":true,"name":"_newManager","type":"address"}],"name":"ManagerUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_prevOwner","type":"address"},{"indexed":true,"name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"}] \ No newline at end of file diff --git a/test/bin/bancor_converter_v11.bin b/test/bin/bancor_converter_v11.bin new file mode 100755 index 0000000..4f265e5 --- /dev/null +++ b/test/bin/bancor_converter_v11.bin @@ -0,0 +1 @@ +7f302e31310000000000000000000000000000000000000000000000000000000060055560c0604052600660808190527f62616e636f72000000000000000000000000000000000000000000000000000060a0908152620000629190816200058d565b506007805461ff001960ff19909116600117169055600c80546001606860020a0319166c01000000000000000000000000179055348015620000a357600080fd5b5060405160a080620048008339810160409081528151602083015191830151606084015160809094015160008054600160a060020a03191633178155929491928580600160a060020a0381161515620000fb57600080fd5b5060028054600160a060020a03928316600160a060020a03199182161790915560038054909116331790558590811615156200013657600080fd5b8460008163ffffffff1610158015620001585750620f424063ffffffff821611155b15156200016457600080fd5b60088054600160a060020a031916600160a060020a0389811691821792839055600780546201000060b060020a0319166201000090930292909217909155604080517fbb34534c0000000000000000000000000000000000000000000000000000000081527f436f6e747261637446656174757265730000000000000000000000000000000060048201529051929091169163bb34534c916024808201926020929091908290030181600087803b1580156200021f57600080fd5b505af115801562000234573d6000803e3d6000fd5b505050506040513d60208110156200024b57600080fd5b50519250600160a060020a03831615620002e257604080517f2c7077c000000000000000000000000000000000000000000000000000000000815260016004820181905260248201529051600160a060020a03851691632c7077c091604480830192600092919082900301818387803b158015620002c857600080fd5b505af1158015620002dd573d6000803e3d6000fd5b505050505b600c805467ffffffff00000000191664010000000063ffffffff891602179055600160a060020a038516156200032957620003298585600064010000000062000337810204565b505050505050505062000632565b600054600160a060020a031633146200034f57600080fd5b600254604080517f8da5cb5b00000000000000000000000000000000000000000000000000000000815290513092600160a060020a031691638da5cb5b9160048083019260209291908290030181600087803b158015620003af57600080fd5b505af1158015620003c4573d6000803e3d6000fd5b505050506040513d6020811015620003db57600080fd5b5051600160a060020a03161415620003f257600080fd5b82600160a060020a03811615156200040957600080fd5b83600160a060020a0381163014156200042157600080fd5b8360008163ffffffff16118015620004425750620f424063ffffffff821611155b15156200044e57600080fd5b600254600160a060020a03878116911614801590620004935750600160a060020a0386166000908152600b60205260409020600101546601000000000000900460ff16155b8015620004b25750600c54620f424063ffffffff918216870190911611155b1515620004be57600080fd5b505050600160a060020a039092166000818152600b602052604081208181556001908101805466ff0000000000001965ff0000000000199715156401000000000264ff000000001963ffffffff808a1663ffffffff1995861617919091169190911798909816650100000000001716660100000000000017909155600a805492830181559092527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8018054600160a060020a031916909217909155600c80548085169093019093169116179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005d057805160ff191683800117855562000600565b8280016001018555821562000600579182015b8281111562000600578251825591602001919060010190620005e3565b506200060e92915062000612565b5090565b6200062f91905b808211156200060e576000815560010162000619565b90565b6141be80620006426000396000f3006080604052600436106102d15763ffffffff60e060020a6000350416630c87355e81146102d65780630ca78923146102fd5780630e53aae91461033157806319b64015146103865780631d000b61146103ba5780631e1401f8146103cf57806320d7d3671461041257806321e6b53d1461043b578063227425641461045c578063228d2820146104c457806325f9bfef146104de5780632a2e2f0c146104f357806338a5e016146105205780633aa0145a146105355780633e8ff43f146105535780633f4d2fc2146105dd578063415f12401461060c57806341a5b33d14610624578063429060291461064e578063481c6a751461066357806349d10b64146106785780634af80f0e1461068d578063514385be146106ae57806354fd4d50146106d4578063579cd3ca146106e95780635a46f06c146107175780635e35359e1461072c5780635e5144eb1461075657806361cd756e1461078357806362614ae6146107985780636d7bd3fc146107ad57806371f52bf3146107c257806372b44b2c146107ee57806375892cf11461081257806379ba50971461083f5780637b1039991461085457806383315b6e1461086957806385d5e6311461087e5780638b34839f146108985780638da5cb5b146108ad5780638e3047e0146108c25780639232494e146108ec5780639249993a1461090157806392d1abb71461091657806394c275ad1461092b578063a2c4c33614610940578063ad374f5b14610964578063b4a176d31461097e578063bf75455814610993578063c45d3d92146109a8578063c4a8598e146109bd578063c8c2fe6c146109d2578063ca1d209d146109e7578063d4ee1d90146109ff578063d55ec69714610a14578063d895951214610a29578063e4edf85214610a4a578063ecbca55d14610a6b578063f0843ba914610a89578063f2fde38b14610ada578063fa1c594e14610afb578063fc0c546a14610b15578063fe417fa514610b2a575b600080fd5b3480156102e257600080fd5b506102eb610b4e565b60408051918252519081900360200190f35b34801561030957600080fd5b5061032f600160a060020a036004351663ffffffff602435166044351515606435610b60565b005b34801561033d57600080fd5b50610352600160a060020a0360043516610c73565b6040805195865263ffffffff9094166020860152911515848401521515606084015215156080830152519081900360a00190f35b34801561039257600080fd5b5061039e600435610cb9565b60408051600160a060020a039092168252519081900360200190f35b3480156103c657600080fd5b506102eb610ce1565b3480156103db57600080fd5b506103f9600160a060020a0360043581169060243516604435610d05565b6040805192835260208301919091528051918290030190f35b34801561041e57600080fd5b50610427610d82565b604080519115158252519081900360200190f35b34801561044757600080fd5b5061032f600160a060020a0360043516610d8b565b604080516020600480358082013583810280860185019096528085526102eb95369593946024949385019291829185019084908082843750949750508435955050506020830135926040810135925060ff606082013516915060808101359060a00135610e55565b3480156104d057600080fd5b5061032f6004351515611212565b3480156104ea57600080fd5b506102eb6112d1565b3480156104ff57600080fd5b506102eb600160a060020a03600435811690602435166044356064356112f5565b34801561052c57600080fd5b5061032f6117d7565b34801561054157600080fd5b506102eb60043560ff6024351661185b565b34801561055f57600080fd5b506105686118b7565b6040805160208082528351818301528351919283929083019185019080838360005b838110156105a257818101518382015260200161058a565b50505050905090810190601f1680156105cf5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156105e957600080fd5b5061032f600160a060020a036004351663ffffffff602435166044351515611945565b34801561061857600080fd5b5061032f600435611b8f565b34801561063057600080fd5b5061032f600160a060020a0360043581169060243516604435611e39565b34801561065a57600080fd5b5061039e611eda565b34801561066f57600080fd5b5061039e611ee9565b34801561068457600080fd5b5061032f611ef8565b34801561069957600080fd5b5061032f600160a060020a036004351661203f565b3480156106ba57600080fd5b5061032f600160a060020a03600435166024351515612090565b3480156106e057600080fd5b506102eb612118565b3480156106f557600080fd5b506106fe61211e565b6040805163ffffffff9092168252519081900360200190f35b34801561072357600080fd5b506102eb612136565b34801561073857600080fd5b5061032f600160a060020a036004358116906024351660443561215a565b34801561076257600080fd5b506102eb600160a060020a03600435811690602435166044356064356122db565b34801561078f57600080fd5b5061039e6122f2565b3480156107a457600080fd5b506102eb612307565b3480156107b957600080fd5b506102eb61232b565b3480156107ce57600080fd5b506107d761233d565b6040805161ffff9092168252519081900360200190f35b3480156107fa57600080fd5b506103f9600160a060020a0360043516602435612344565b34801561081e57600080fd5b506102eb600160a060020a0360043581169060243516604435606435612628565b34801561084b57600080fd5b5061032f6126c9565b34801561086057600080fd5b5061039e612744565b34801561087557600080fd5b506102eb612753565b34801561088a57600080fd5b5061032f6004351515612777565b3480156108a457600080fd5b50610427612810565b3480156108b957600080fd5b5061039e61281e565b3480156108ce57600080fd5b506103f9600160a060020a036004358116906024351660443561282d565b3480156108f857600080fd5b506102eb612aec565b34801561090d57600080fd5b506102eb612b10565b34801561092257600080fd5b506102eb612b34565b34801561093757600080fd5b506106fe612b39565b34801561094c57600080fd5b506103f9600160a060020a0360043516602435612b4d565b34801561097057600080fd5b5061032f6004351515612dfe565b34801561098a57600080fd5b5061032f612e2f565b34801561099f57600080fd5b50610427612e89565b3480156109b457600080fd5b5061039e612ea2565b3480156109c957600080fd5b506102eb612eb1565b3480156109de57600080fd5b5061032f612ed5565b3480156109f357600080fd5b5061032f600435612f52565b348015610a0b57600080fd5b5061039e61321f565b348015610a2057600080fd5b5061032f61322e565b348015610a3557600080fd5b506102eb600160a060020a036004351661335f565b348015610a5657600080fd5b5061032f600160a060020a0360043516613463565b348015610a7757600080fd5b5061032f63ffffffff600435166134ce565b604080516020600480358082013583810280860185019096528085526102eb953695939460249493850192918291850190849080828437509497505084359550505060209092013591506135b59050565b348015610ae657600080fd5b5061032f600160a060020a03600435166135f7565b348015610b0757600080fd5b5061032f600435151561364b565b348015610b2157600080fd5b5061039e61368b565b348015610b3657600080fd5b5061032f600160a060020a036004351660243561369a565b60008051602061415383398151915281565b60008054600160a060020a03163314610b7857600080fd5b600160a060020a0385166000908152600b602052604090206001015485906601000000000000900460ff161515610bae57600080fd5b8460008163ffffffff16118015610bce5750620f424063ffffffff821611155b1515610bd957600080fd5b600160a060020a0387166000908152600b602052604090206001810154600c54919450620f424063ffffffff918216928216929092038801161115610c1d57600080fd5b5050600181018054600c805463ffffffff928316818416038801831663ffffffff199182161790915582549515156401000000000264ff0000000019929097169516949094179390931693909317909155905550565b600b602052600090815260409020805460019091015463ffffffff81169060ff640100000000820481169165010000000000810482169166010000000000009091041685565b600a805482908110610cc757fe5b600091825260209091200154600160a060020a0316905081565b7f424e54546f6b656e00000000000000000000000000000000000000000000000081565b600080600160a060020a038581169085161415610d2157600080fd5b600254600160a060020a0385811691161415610d4a57610d418584612b4d565b91509150610d7a565b600254600160a060020a0386811691161415610d6a57610d418484612344565b610d7585858561282d565b915091505b935093915050565b60075460ff1681565b600054600160a060020a03163314610da257600080fd5b6008546040805160e260020a632ecd14d302815260008051602061415383398151915260048201529051600092600160a060020a03169163bb34534c91602480830192602092919082900301818787803b158015610dff57600080fd5b505af1158015610e13573d6000803e3d6000fd5b505050506040513d6020811015610e2957600080fd5b5051600054909150600160a060020a03808316911614610e4857600080fd5b610e5182613844565b5050565b60008060008960028151118015610e6e57506015815111155b8015610e7f57508051600290066001145b1515610e8a57600080fd5b8a6000815181101515610e9957fe5b60209081029091018101516008546040805160e260020a632ecd14d30281527f42616e636f724e6574776f726b0000000000000000000000000000000000000060048201529051929650600160a060020a039091169263bb34534c926024808401938290030181600087803b158015610f1157600080fd5b505af1158015610f25573d6000803e3d6000fd5b505050506040513d6020811015610f3b57600080fd5b505191503415156110ef57600254600160a060020a038481169116141561105f576002546040805160e060020a63a24835d1028152336004820152602481018d90529051600160a060020a039092169163a24835d19160448082019260009290919082900301818387803b158015610fb257600080fd5b505af1158015610fc6573d6000803e3d6000fd5b50505050600260009054906101000a9004600160a060020a0316600160a060020a031663867904b4838c6040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b15801561104257600080fd5b505af1158015611056573d6000803e3d6000fd5b505050506110ef565b6040805160e060020a6323b872dd028152336004820152600160a060020a038481166024830152604482018d90529151918516916323b872dd916064808201926020929091908290030181600087803b1580156110bb57600080fd5b505af11580156110cf573d6000803e3d6000fd5b505050506040513d60208110156110e557600080fd5b505115156110ef57fe5b81600160a060020a0316636b08f2ef348d8d8d338e8e8e8e6040518a63ffffffff1660e060020a028152600401808060200189815260200188815260200187600160a060020a0316600160a060020a031681526020018681526020018560ff1660ff1681526020018460001916600019168152602001836000191660001916815260200182810382528a818151815260200191508051906020019060200280838360005b838110156111ab578181015183820152602001611193565b5050505090500199505050505050505050506020604051808303818588803b1580156111d657600080fd5b505af11580156111ea573d6000803e3d6000fd5b50505050506040513d602081101561120157600080fd5b50519b9a5050505050505050505050565b600054600160a060020a03163314806112355750600354600160a060020a031633145b151561124057600080fd5b600c5460ff6c0100000000000000000000000090910416151581151514156112ce57600c80546c01000000000000000000000000831581026cff000000000000000000000000199092169190911791829055604080519190920460ff161515815290517fb8e670608a57255ce4f35952b324cba70211a4200a91ce81d26e06d488c1f66b9181900360200190a15b50565b7f436f6e747261637452656769737472790000000000000000000000000000000081565b6008546040805160e260020a632ecd14d30281527f42616e636f724e6574776f726b0000000000000000000000000000000000000060048201529051600092839283928392839283928392600160a060020a03169163bb34534c91602480830192602092919082900301818787803b15801561137057600080fd5b505af1158015611384573d6000803e3d6000fd5b505050506040513d602081101561139a57600080fd5b5051905033600160a060020a038216146113b357600080fd5b600c546c01000000000000000000000000900460ff1615156113d157fe5b87600081116113df57600080fd5b600160a060020a038c8116908c1614156113f857600080fd5b600254600160a060020a038c811691161415611420576114198c8b8b6138c2565b97506117c8565b600254600160a060020a038d811691161415611441576114198b8b8b613b2c565b61144c8c8c8c61282d565b909750955086158015906114605750888710155b151561146b57600080fd5b600160a060020a038c166000908152600b602052604090206001810154909550640100000000900460ff16156114aa5784546114a7908b613ee0565b85555b600160a060020a038b166000908152600b602052604090206001810154909450640100000000900460ff16156114e95783546114e69088613ef6565b84555b6114f28b61335f565b92508287106114fd57fe5b6040805160e060020a6323b872dd028152336004820152306024820152604481018c90529051600160a060020a038e16916323b872dd9160648083019260209291908290030181600087803b15801561155557600080fd5b505af1158015611569573d6000803e3d6000fd5b505050506040513d602081101561157f57600080fd5b5051151561158957fe5b604080517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018990529051600160a060020a038d169163a9059cbb9160448083019260209291908290030181600087803b1580156115f157600080fd5b505af1158015611605573d6000803e3d6000fd5b505050506040513d602081101561161b57600080fd5b5051151561162557fe5b6116328c8c8c8a8a613f08565b8b600160a060020a0316600080516020614173833981519152600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561169e57600080fd5b505af11580156116b2573d6000803e3d6000fd5b505050506040513d60208110156116c857600080fd5b50516116d38f61335f565b600189015460408051938452602084019290925263ffffffff1682820152519081900360600190a28a600160a060020a0316600080516020614173833981519152600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561176757600080fd5b505af115801561177b573d6000803e3d6000fd5b505050506040513d602081101561179157600080fd5b505161179c8e61335f565b600188015460408051938452602084019290925263ffffffff1682820152519081900360600190a28697505b50505050505050949350505050565b600054600160a060020a031633146117ee57600080fd5b600260009054906101000a9004600160a060020a0316600160a060020a03166379ba50976040518163ffffffff1660e060020a028152600401600060405180830381600087803b15801561184157600080fd5b505af1158015611855573d6000803e3d6000fd5b50505050565b60008160ff16620f42400a67ffffffffffffffff166118a6848460ff16600c60089054906101000a900463ffffffff1663ffffffff16620f4240030a67ffffffffffffffff16613f8c565b8115156118af57fe5b049392505050565b6006805460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152929183018282801561193d5780601f106119125761010080835404028352916020019161193d565b820191906000526020600020905b81548152906001019060200180831161192057829003601f168201915b505050505081565b600054600160a060020a0316331461195c57600080fd5b600254604080517f8da5cb5b00000000000000000000000000000000000000000000000000000000815290513092600160a060020a031691638da5cb5b9160048083019260209291908290030181600087803b1580156119bb57600080fd5b505af11580156119cf573d6000803e3d6000fd5b505050506040513d60208110156119e557600080fd5b5051600160a060020a031614156119fb57600080fd5b82600160a060020a0381161515611a1157600080fd5b83600160a060020a038116301415611a2857600080fd5b8360008163ffffffff16118015611a485750620f424063ffffffff821611155b1515611a5357600080fd5b600254600160a060020a03878116911614801590611a975750600160a060020a0386166000908152600b60205260409020600101546601000000000000900460ff16155b8015611ab55750600c54620f424063ffffffff918216870190911611155b1515611ac057600080fd5b505050600160a060020a039092166000818152600b602052604081208181556001908101805466ff0000000000001965ff0000000000199715156401000000000264ff000000001963ffffffff808a1663ffffffff1995861617919091169190911798909816650100000000001716660100000000000017909155600a805492830181559092527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8018054600160a060020a031916909217909155600c80548085169093019093169116179055565b600c546000908190819081908190819063ffffffff16620f424014611bb357600080fd5b600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015611c0657600080fd5b505af1158015611c1a573d6000803e3d6000fd5b505050506040513d6020811015611c3057600080fd5b50516002546040805160e060020a63a24835d1028152336004820152602481018b90529051929850600160a060020a039091169163a24835d19160448082019260009290919082900301818387803b158015611c8b57600080fd5b505af1158015611c9f573d6000803e3d6000fd5b50505050600091505b600a5461ffff83161015611e3057600a805461ffff8416908110611cc857fe5b600091825260209091200154600160a060020a03169450611ce88561335f565b935085611cf58886613f8c565b811515611cfe57fe5b600160a060020a0387166000908152600b6020526040902060018101549290910494509150640100000000900460ff1615611d42578054611d3f9084613ef6565b81555b604080517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018590529051600160a060020a0387169163a9059cbb9160448083019260209291908290030181600087803b158015611daa57600080fd5b505af1158015611dbe573d6000803e3d6000fd5b505050506040513d6020811015611dd457600080fd5b50511515611dde57fe5b6001810154604080518989038152858703602082015263ffffffff9092168282015251600160a060020a03871691600080516020614173833981519152919081900360600190a2600190910190611ca8565b50505050505050565b600054600160a060020a03163314611e5057600080fd5b600254604080517f5e35359e000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015285811660248301526044820185905291519190921691635e35359e91606480830192600092919082900301818387803b158015611ec657600080fd5b505af1158015611e30573d6000803e3d6000fd5b600454600160a060020a031681565b600354600160a060020a031681565b60075460009060ff1680611f165750600054600160a060020a031633145b1515611f2157600080fd5b6008546040805160e260020a632ecd14d30281527f436f6e747261637452656769737472790000000000000000000000000000000060048201529051600160a060020a039092169163bb34534c916024808201926020929091908290030181600087803b158015611f9157600080fd5b505af1158015611fa5573d6000803e3d6000fd5b505050506040513d6020811015611fbb57600080fd5b5051600854909150600160a060020a03808316911614801590611fe65750600160a060020a03811615155b1515611ff157600080fd5b600880546007805475ffffffffffffffffffffffffffffffffffffffff00001916600160a060020a03808416620100000291909117909155600160a060020a03199091169216919091179055565b600054600160a060020a0316331461205657600080fd5b80600160a060020a03811630141561206d57600080fd5b5060098054600160a060020a031916600160a060020a0392909216919091179055565b600054600160a060020a031633146120a757600080fd5b600160a060020a0382166000908152600b602052604090206001015482906601000000000000900460ff1615156120dd57600080fd5b50600160a060020a03919091166000908152600b60205260409020600101805465ff0000000000191691156501000000000002919091179055565b60055481565b600c5468010000000000000000900463ffffffff1681565b7f42616e636f72436f6e766572746572466163746f72790000000000000000000081565b6008546040805160e260020a632ecd14d302815260008051602061415383398151915260048201529051600092600160a060020a03169163bb34534c91602480830192602092919082900301818787803b1580156121b757600080fd5b505af11580156121cb573d6000803e3d6000fd5b505050506040513d60208110156121e157600080fd5b5051600160a060020a0385166000908152600b60205260409020600101549091506601000000000000900460ff1615806122ad5750600254604080517f8da5cb5b00000000000000000000000000000000000000000000000000000000815290513092600160a060020a031691638da5cb5b9160048083019260209291908290030181600087803b15801561227557600080fd5b505af1158015612289573d6000803e3d6000fd5b505050506040513d602081101561229f57600080fd5b5051600160a060020a031614155b806122c55750600054600160a060020a038281169116145b15156122d057600080fd5b611855848484613fb0565b60006122e9858585856112f5565b95945050505050565b600754620100009004600160a060020a031681565b7f424e54436f6e766572746572000000000000000000000000000000000000000081565b60008051602061413383398151915281565b600a545b90565b60008060008060008060008030600160a060020a0316600260009054906101000a9004600160a060020a0316600160a060020a0316638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1580156123ad57600080fd5b505af11580156123c1573d6000803e3d6000fd5b505050506040513d60208110156123d757600080fd5b5051600160a060020a0316146123ec57600080fd5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561242257600080fd5b600160a060020a03808c166000908152600b6020908152604080832060025482517f18160ddd0000000000000000000000000000000000000000000000000000000081529251919c50909416936318160ddd93600480840194938390030190829087803b15801561249257600080fd5b505af11580156124a6573d6000803e3d6000fd5b505050506040513d60208110156124bc57600080fd5b505195506124c98b61335f565b6008546040805160e260020a632ecd14d302815260008051602061413383398151915260048201529051929750600160a060020a039091169163bb34534c916024808201926020929091908290030181600087803b15801561252a57600080fd5b505af115801561253e573d6000803e3d6000fd5b505050506040513d602081101561255457600080fd5b50516001880154604080517f49f9b0f7000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905263ffffffff9092166044830152606482018d905251919550600160a060020a038616916349f9b0f7916084808201926020929091908290030181600087803b1580156125db57600080fd5b505af11580156125ef573d6000803e3d6000fd5b505050506040513d602081101561260557600080fd5b5051925061261483600161185b565b9b928c90039a509198505050505050505050565b60408051606081018252600160a060020a0380871682526002548116602083015285169181019190915260009061266390600d9060036140a9565b506122e9600d8054806020026020016040519081016040528092919081815260200182805480156126bd57602002820191906000526020600020905b8154600160a060020a0316815260019091019060200180831161269f575b505050505084846135b5565b600154600160a060020a031633146126e057600080fd5b60015460008054604051600160a060020a0393841693909116917f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91a36001805460008054600160a060020a0319908116600160a060020a03841617909155169055565b600854600160a060020a031681565b7f436f6e747261637446656174757265730000000000000000000000000000000081565b600054600160a060020a0316331461278e57600080fd5b600254604080517f1608f18f00000000000000000000000000000000000000000000000000000000815283151560048201529051600160a060020a0390921691631608f18f9160248082019260009290919082900301818387803b1580156127f557600080fd5b505af1158015612809573d6000803e3d6000fd5b5050505050565b600754610100900460ff1681565b600054600160a060020a031681565b600080600080600080600030600160a060020a0316600260009054906101000a9004600160a060020a0316600160a060020a0316638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15801561289557600080fd5b505af11580156128a9573d6000803e3d6000fd5b505050506040513d60208110156128bf57600080fd5b5051600160a060020a0316146128d457600080fd5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561290a57600080fd5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561294057600080fd5b600160a060020a038c81166000908152600b6020526040808220928e16825290206001810154919850965065010000000000900460ff16151561298257600080fd5b6008546040805160e260020a632ecd14d302815260008051602061413383398151915260048201529051600160a060020a039092169163bb34534c916024808201926020929091908290030181600087803b1580156129e057600080fd5b505af11580156129f4573d6000803e3d6000fd5b505050506040513d6020811015612a0a57600080fd5b50519450600160a060020a0385166365098bb3612a268e61335f565b60018a015463ffffffff16612a3a8f61335f565b60018b01546040805163ffffffff87811660e060020a028252600482019690965293851660248501526044840192909252929092166064820152608481018e9052905160a48083019260209291908290030181600087803b158015612a9e57600080fd5b505af1158015612ab2573d6000803e3d6000fd5b505050506040513d6020811015612ac857600080fd5b50519350612ad784600261185b565b9c938d90039b50929950505050505050505050565b7f42616e636f724e6574776f726b0000000000000000000000000000000000000081565b7f42616e636f7247617350726963654c696d69740000000000000000000000000081565b600181565b600c54640100000000900463ffffffff1681565b60008060008060008060008030600160a060020a0316600260009054906101000a9004600160a060020a0316600160a060020a0316638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015612bb657600080fd5b505af1158015612bca573d6000803e3d6000fd5b505050506040513d6020811015612be057600080fd5b5051600160a060020a031614612bf557600080fd5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff161515612c2b57600080fd5b600160a060020a038b166000908152600b60205260409020600181015490975065010000000000900460ff161515612c6257600080fd5b600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015612cb557600080fd5b505af1158015612cc9573d6000803e3d6000fd5b505050506040513d6020811015612cdf57600080fd5b50519550612cec8b61335f565b6008546040805160e260020a632ecd14d302815260008051602061413383398151915260048201529051929750600160a060020a039091169163bb34534c916024808201926020929091908290030181600087803b158015612d4d57600080fd5b505af1158015612d61573d6000803e3d6000fd5b505050506040513d6020811015612d7757600080fd5b50516001880154604080517f29a00e7c000000000000000000000000000000000000000000000000000000008152600481018a90526024810189905263ffffffff9092166044830152606482018d905251919550600160a060020a038616916329a00e7c916084808201926020929091908290030181600087803b1580156125db57600080fd5b600054600160a060020a03163314612e1557600080fd5b600780549115156101000261ff0019909216919091179055565b600054600160a060020a0316331480612e525750600354600160a060020a031633145b1515612e5d57600080fd5b6007805460088054600160a060020a031916600160a060020a036201000084041617905560ff19169055565b600c546c01000000000000000000000000900460ff1681565b600954600160a060020a031681565b7f42616e636f72580000000000000000000000000000000000000000000000000081565b600454600160a060020a03163314612eec57600080fd5b600454600354604051600160a060020a0392831692909116907fbe4cc281795971a471c980e842627a7f1ea3892ddfce8c5b6357cd2611c1973290600090a36004805460038054600160a060020a0319908116600160a060020a03841617909155169055565b600c546000908190819081908190819063ffffffff16620f424014612f7657600080fd5b600c546c01000000000000000000000000900460ff161515612f9457fe5b600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015612fe757600080fd5b505af1158015612ffb573d6000803e3d6000fd5b505050506040513d602081101561301157600080fd5b50519550600091505b600a5461ffff8316101561319257600a805461ffff841690811061303a57fe5b600091825260209091200154600160a060020a0316945061305a8561335f565b9350856130678886613f8c565b81151561307057fe5b600160a060020a0387166000908152600b6020526040902060018101549290910494509150640100000000900460ff16156130b45780546130b19084613ee0565b81555b6040805160e060020a6323b872dd028152336004820152306024820152604481018590529051600160a060020a038716916323b872dd9160648083019260209291908290030181600087803b15801561310c57600080fd5b505af1158015613120573d6000803e3d6000fd5b505050506040513d602081101561313657600080fd5b5051151561314057fe5b600181015460408051888a018152868601602082015263ffffffff9092168282015251600160a060020a03871691600080516020614173833981519152919081900360600190a260019091019061301a565b600254604080517f867904b4000000000000000000000000000000000000000000000000000000008152336004820152602481018a90529051600160a060020a039092169163867904b49160448082019260009290919082900301818387803b1580156131fe57600080fd5b505af1158015613212573d6000803e3d6000fd5b5050505050505050505050565b600154600160a060020a031681565b60008054600160a060020a0316331461324657600080fd5b6008546040805160e260020a632ecd14d302815260008051602061415383398151915260048201529051600160a060020a039092169163bb34534c916024808201926020929091908290030181600087803b1580156132a457600080fd5b505af11580156132b8573d6000803e3d6000fd5b505050506040513d60208110156132ce57600080fd5b505190506132db816135f7565b600554604080517fbc444e13000000000000000000000000000000000000000000000000000000008152600481019290925251600160a060020a0383169163bc444e1391602480830192600092919082900301818387803b15801561333f57600080fd5b505af1158015613353573d6000803e3d6000fd5b505050506112ce6126c9565b600160a060020a0381166000908152600b6020526040812060010154819083906601000000000000900460ff16151561339757600080fd5b600160a060020a0384166000908152600b602052604090206001810154909250640100000000900460ff1661345857604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529051600160a060020a038616916370a082319160248083019260209291908290030181600087803b15801561342757600080fd5b505af115801561343b573d6000803e3d6000fd5b505050506040513d602081101561345157600080fd5b505161345b565b81545b949350505050565b600054600160a060020a03163314806134865750600354600160a060020a031633145b151561349157600080fd5b600354600160a060020a03828116911614156134ac57600080fd5b60048054600160a060020a031916600160a060020a0392909216919091179055565b600054600160a060020a03163314806134f15750600354600160a060020a031633145b15156134fc57600080fd5b8060008163ffffffff16101580156135285750600c5463ffffffff640100000000909104811690821611155b151561353357600080fd5b600c546040805163ffffffff6801000000000000000090930483168152918416602083015280517f81cd2ffb37dd237c0e4e2a3de5265fcf9deb43d3e7801e80db9f1ccfba7ee6009281900390910190a150600c805463ffffffff90921668010000000000000000026bffffffff000000000000000019909216919091179055565b600083600281511180156135cb57506015815111155b80156135dc57508051600290066001145b15156135e757600080fd5b6122e98585856000808080610e55565b600054600160a060020a0316331461360e57600080fd5b600054600160a060020a038281169116141561362957600080fd5b60018054600160a060020a031916600160a060020a0392909216919091179055565b600054600160a060020a031633148061366e5750600354600160a060020a031633145b151561367957600080fd5b6007805460ff19169115919091179055565b600254600160a060020a031681565b600754600090610100900460ff1615156136b357600080fd5b6008546040805160e260020a632ecd14d30281527f42616e636f72580000000000000000000000000000000000000000000000000060048201529051600160a060020a039092169163bb34534c916024808201926020929091908290030181600087803b15801561372357600080fd5b505af1158015613737573d6000803e3d6000fd5b505050506040513d602081101561374d57600080fd5b5051905033600160a060020a0382161461376657600080fd5b6002546040805160e060020a63a24835d1028152600160a060020a038681166004830152602482018690529151919092169163a24835d191604480830192600092919082900301818387803b1580156137be57600080fd5b505af11580156137d2573d6000803e3d6000fd5b5050600254604080517f867904b4000000000000000000000000000000000000000000000000000000008152600160a060020a03868116600483015260248201889052915191909216935063867904b49250604480830192600092919082900301818387803b158015611ec657600080fd5b600054600160a060020a0316331461385b57600080fd5b600254604080517ff2fde38b000000000000000000000000000000000000000000000000000000008152600160a060020a0384811660048301529151919092169163f2fde38b91602480830192600092919082900301818387803b1580156127f557600080fd5b6000806000806138d28787612b4d565b909350915082158015906138e65750848310155b15156138f157600080fd5b50600160a060020a0386166000908152600b602052604090206001810154640100000000900460ff161561392e57805461392b9087613ee0565b81555b6040805160e060020a6323b872dd028152336004820152306024820152604481018890529051600160a060020a038916916323b872dd9160648083019260209291908290030181600087803b15801561398657600080fd5b505af115801561399a573d6000803e3d6000fd5b505050506040513d60208110156139b057600080fd5b505115156139ba57fe5b600254604080517f867904b4000000000000000000000000000000000000000000000000000000008152336004820152602481018690529051600160a060020a039092169163867904b49160448082019260009290919082900301818387803b158015613a2657600080fd5b505af1158015613a3a573d6000803e3d6000fd5b5050600254613a589250899150600160a060020a0316888686613f08565b86600160a060020a0316600080516020614173833981519152600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613ac457600080fd5b505af1158015613ad8573d6000803e3d6000fd5b505050506040513d6020811015613aee57600080fd5b5051613af98a61335f565b600185015460408051938452602084019290925263ffffffff1682820152519081900360600190a2509095945050505050565b600254604080517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152905160009283928392839283928392600160a060020a03909216916370a082319160248082019260209290919082900301818787803b158015613b9e57600080fd5b505af1158015613bb2573d6000803e3d6000fd5b505050506040513d6020811015613bc857600080fd5b5051881115613bd657600080fd5b613be08989612344565b90955093508415801590613bf45750868510155b1515613bff57600080fd5b600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613c5257600080fd5b505af1158015613c66573d6000803e3d6000fd5b505050506040513d6020811015613c7c57600080fd5b50519250613c898961335f565b915081851080613ca257508185148015613ca257508288145b1515613caa57fe5b50600160a060020a0388166000908152600b602052604090206001810154640100000000900460ff1615613ce7578054613ce49086613ef6565b81555b6002546040805160e060020a63a24835d1028152336004820152602481018b90529051600160a060020a039092169163a24835d19160448082019260009290919082900301818387803b158015613d3d57600080fd5b505af1158015613d51573d6000803e3d6000fd5b5050604080517fa9059cbb000000000000000000000000000000000000000000000000000000008152336004820152602481018990529051600160a060020a038d16935063a9059cbb925060448083019260209291908290030181600087803b158015613dbd57600080fd5b505af1158015613dd1573d6000803e3d6000fd5b505050506040513d6020811015613de757600080fd5b50511515613df157fe5b600254613e0a90600160a060020a03168a8a8888613f08565b88600160a060020a0316600080516020614173833981519152600260009054906101000a9004600160a060020a0316600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b158015613e7657600080fd5b505af1158015613e8a573d6000803e3d6000fd5b505050506040513d6020811015613ea057600080fd5b5051613eab8c61335f565b600185015460408051938452602084019290925263ffffffff1682820152519081900360600190a25092979650505050505050565b600082820183811015613eef57fe5b9392505050565b600081831015613f0257fe5b50900390565b7f8000000000000000000000000000000000000000000000000000000000000000811115613f3257fe5b604080518481526020810184905280820183905290513391600160a060020a0387811692908916917f276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb919081900360600190a45050505050565b6000828202831580613fa85750828482811515613fa557fe5b04145b1515613eef57fe5b600054600160a060020a03163314613fc757600080fd5b82600160a060020a0381161515613fdd57600080fd5b82600160a060020a0381161515613ff357600080fd5b83600160a060020a03811630141561400a57600080fd5b85600160a060020a031663a9059cbb86866040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050602060405180830381600087803b15801561406d57600080fd5b505af1158015614081573d6000803e3d6000fd5b505050506040513d602081101561409757600080fd5b505115156140a157fe5b505050505050565b8280548282559060005260206000209081019282156140fe579160200282015b828111156140fe5782518254600160a060020a031916600160a060020a039091161782556020909201916001909101906140c9565b5061410a92915061410e565b5090565b61234191905b8082111561410a578054600160a060020a0319168155600101614114560042616e636f72466f726d756c610000000000000000000000000000000000000042616e636f72436f6e76657274657255706772616465720000000000000000008a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a788a165627a7a723058208adda2e9e204d9435c93c8e584b431ded1f8b39a256b071340f8827eb179f9c90029 \ No newline at end of file diff --git a/test/bin/bancor_converter_v4.abi b/test/bin/bancor_converter_v4.abi new file mode 100755 index 0000000..416f409 --- /dev/null +++ b/test/bin/bancor_converter_v4.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"_reserveToken","type":"address"}],"name":"getReserveBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"}],"name":"getReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableConversions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_extensions","type":"address"}],"name":"setExtensions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"extensions","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_amount","type":"uint256"}],"name":"getConversionFeeAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_ratio","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"}],"name":"addReserve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"acceptTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"converterType","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawFromToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newManager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"clearQuickBuyPath","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"change","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_reserveToken","type":"address"},{"name":"_sellAmount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"sell","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_reserveToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getSaleReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convert","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_minReturn","type":"uint256"}],"name":"quickBuy","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableTokenTransfers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuickBuyPathLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxConversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"reserveTokenCount","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_reserveToken","type":"address"},{"name":"_depositAmount","type":"uint256"}],"name":"getPurchaseReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_reserveToken","type":"address"},{"name":"_depositAmount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"buy","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"quickChange","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_reserveToken","type":"address"},{"name":"_ratio","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"},{"name":"_virtualBalance","type":"uint256"}],"name":"updateReserve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"hasQuickBuyEtherToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuickBuyEtherToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"convertibleTokenCount","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionsEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"reserveTokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"}],"name":"setQuickBuyPath","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"reserves","outputs":[{"name":"virtualBalance","type":"uint256"},{"name":"ratio","type":"uint32"},{"name":"isVirtualBalanceEnabled","type":"bool"},{"name":"isPurchaseEnabled","type":"bool"},{"name":"isSet","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newManager","type":"address"}],"name":"transferManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"quickBuyPath","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_reserveToken","type":"address"},{"name":"_disable","type":"bool"}],"name":"disableReservePurchases","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_conversionFee","type":"uint32"}],"name":"setConversionFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"quickConvert","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenIndex","type":"uint16"}],"name":"convertibleToken","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_token","type":"address"},{"name":"_extensions","type":"address"},{"name":"_maxConversionFee","type":"uint32"},{"name":"_reserveToken","type":"address"},{"name":"_reserveRatio","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_fromToken","type":"address"},{"indexed":true,"name":"_toToken","type":"address"},{"indexed":true,"name":"_trader","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"},{"indexed":false,"name":"_return","type":"uint256"},{"indexed":false,"name":"_currentPriceN","type":"uint256"},{"indexed":false,"name":"_currentPriceD","type":"uint256"}],"name":"Conversion","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_prevManager","type":"address"},{"indexed":false,"name":"_newManager","type":"address"}],"name":"ManagerUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_prevOwner","type":"address"},{"indexed":false,"name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"}] \ No newline at end of file diff --git a/test/bin/bancor_converter_v4.bin b/test/bin/bancor_converter_v4.bin new file mode 100755 index 0000000..954be9f --- /dev/null +++ b/test/bin/bancor_converter_v4.bin @@ -0,0 +1 @@ +606060405260408051908101604052600381527f302e340000000000000000000000000000000000000000000000000000000000602082015260059080516200004d92916020019062000485565b506040805190810160405260068082527f62616e636f72000000000000000000000000000000000000000000000000000060208301529080516200009692916020019062000485565b50600b80546001606860020a0319166c010000000000000000000000001790553415620000c257600080fd5b60405160a0806200344a83398101604052808051919060200180519190602001805191906020018051919060200180519150505b5b845b5b5b5b60008054600160a060020a03191633600160a060020a03161790555b5b5b80600160a060020a03811615156200013157600080fd5b60028054600160a060020a031916600160a060020a0384161790555b5b505060038054600160a060020a03191633600160a060020a03161790555b83600160a060020a03811615156200018357600080fd5b8360008163ffffffff1610158015620001a55750620f424063ffffffff821611155b1515620001b157600080fd5b60078054600160a060020a031916600160a060020a0388811691909117909155600b805467ffffffff00000000191664010000000063ffffffff89160217905584161562000215576200021584846000640100000000620012be6200022682021704565b5b5b5b505b5050505050506200055c565b60005433600160a060020a039081169116146200023f57fe5b600254600160a060020a033081169116638da5cb5b6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1515620002a557600080fd5b6102c65a03f11515620002b757600080fd5b50505060405180519050600160a060020a031614151515620002d557fe5b82600160a060020a0381161515620002ec57600080fd5b8330600160a060020a031681600160a060020a0316141515156200030f57600080fd5b8360008163ffffffff16118015620003305750620f424063ffffffff821611155b15156200033c57600080fd5b600254600160a060020a03878116911614801590620003815750600160a060020a0386166000908152600a60205260409020600101546601000000000000900460ff16155b8015620003a05750600b54620f424063ffffffff918216870190911611155b1515620003ac57600080fd5b600160a060020a0386166000908152600a602052604081209081556001908101805466010000000000006501000000000063ffffffff1990921663ffffffff8a161764ff000000001916640100000000891515021765ff000000000019169190911766ff000000000000191617905560088054909181016200042f83826200050b565b916000526020600020900160005b8154600160a060020a03808b166101009390930a9283029202191617905550600b805463ffffffff80821688011663ffffffff199091161790555b5b505b505b505b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620004c857805160ff1916838001178555620004f8565b82800160010185558215620004f8579182015b82811115620004f8578251825591602001919060010190620004db565b5b506200050792915062000538565b5090565b8154818355818115116200047f576000838152602090206200047f91810190830162000538565b5b505050565b6200055991905b808211156200050757600081556001016200053f565b5090565b90565b612ede806200056c6000396000f300606060405236156102355763ffffffff60e060020a60003504166315226b5481146102a05780631e1401f8146102d157806321e6b53d1461030b578063228d28201461032c5780632314aad61461034657806324f159c2146103675780632a3c2c56146103965780633291b39a146103be57806338a5e016146103ed5780633e8ff43f1461040257806341a5b33d1461048d57806342906029146104b7578063481c6a75146104e65780634e2280c41461051557806354fd4d501461052a578063579cd3ca146105b55780635e35359e146105e15780635e5144eb1461060b5780636a2724621461064857806372b44b2c1461067f57806375892cf1146106b35780637758c4f8146106f057806379ba50971461070d57806385d5e631146107225780638da5cb5b1461073c5780639396a7f01461076b57806394c275ad146107905780639b99a8e2146107bc578063a2c4c336146107e6578063a59ac6dd1461081a578063a93d7c7214610851578063ab5841f2146108b9578063abeb5f9f146108eb578063b3a9afb714610912578063ba9a8b3714610941578063bf7545581461096b578063c8c2fe6c14610992578063d031370b146109a7578063d395ee0f146109d9578063d4ee1d9014610a2a578063d66bd52414610a59578063e4edf85214610ab4578063e7ee85a514610ad5578063e8c7893414610b07578063ecbca55d14610b2d578063f0843ba914610b4b578063f2c8d24714610ba8578063f2fde38b14610bde578063fc0c546a14610bff575b5b61029c600980548060200260200160405190810160405280929190818152602001828054801561028f57602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610271575b5050505050346001610c2e565b505b005b34156102ab57600080fd5b6102bf600160a060020a0360043516610f52565b60405190815260200160405180910390f35b34156102dc57600080fd5b6102bf600160a060020a036004358116906024351660443561103e565b60405190815260200160405180910390f35b341561031657600080fd5b61029e600160a060020a036004351661113b565b005b341561033757600080fd5b61029e60043515156111bc565b005b341561035157600080fd5b61029e600160a060020a0360043516611201565b005b341561037257600080fd5b61037a611275565b604051600160a060020a03909116815260200160405180910390f35b34156103a157600080fd5b6102bf600435611284565b60405190815260200160405180910390f35b34156103c957600080fd5b61029e600160a060020a036004351663ffffffff6024351660443515156112be565b005b34156103f857600080fd5b61029e6114f7565b005b341561040d57600080fd5b610415611566565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156104525780820151818401525b602001610439565b50505050905090810190601f16801561047f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561049857600080fd5b61029e600160a060020a0360043581169060243516604435611604565b005b34156104c257600080fd5b61037a61169a565b604051600160a060020a03909116815260200160405180910390f35b34156104f157600080fd5b61037a6116a9565b604051600160a060020a03909116815260200160405180910390f35b341561052057600080fd5b61029e6116b8565b005b341561053557600080fd5b6104156116e2565b60405160208082528190810183818151815260200191508051906020019080838360005b838110156104525780820151818401525b602001610439565b50505050905090810190601f16801561047f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156105c057600080fd5b6105c8611780565b60405163ffffffff909116815260200160405180910390f35b34156105ec57600080fd5b61029e600160a060020a0360043581169060243516604435611798565b005b341561061657600080fd5b6102bf600160a060020a036004358116906024351660443560643561188b565b60405190815260200160405180910390f35b341561065357600080fd5b6102bf600160a060020a03600435166024356044356118a4565b60405190815260200160405180910390f35b341561068a57600080fd5b6102bf600160a060020a0360043516602435611d08565b60405190815260200160405180910390f35b34156106be57600080fd5b6102bf600160a060020a0360043581169060243516604435606435611d83565b60405190815260200160405180910390f35b6102bf600435611e15565b60405190815260200160405180910390f35b341561071857600080fd5b61029e611e84565b005b341561072d57600080fd5b61029e6004351515611f1f565b005b341561074757600080fd5b61037a611f98565b604051600160a060020a03909116815260200160405180910390f35b341561077657600080fd5b6102bf611fa7565b60405190815260200160405180910390f35b341561079b57600080fd5b6105c8611fae565b60405163ffffffff909116815260200160405180910390f35b34156107c757600080fd5b6107cf611fc2565b60405161ffff909116815260200160405180910390f35b34156107f157600080fd5b6102bf600160a060020a0360043516602435611fc9565b60405190815260200160405180910390f35b341561082557600080fd5b6102bf600160a060020a0360043516602435604435612255565b60405190815260200160405180910390f35b341561085c57600080fd5b6102bf6004602481358181019083013580602081810201604051908101604052809392919081815260200183836020028082843750949650508435946020013593506125b192505050565b60405190815260200160405180910390f35b34156108c457600080fd5b61029e600160a060020a036004351663ffffffff6024351660443515156064356125c8565b005b34156108f657600080fd5b6108fe6126df565b604051901515815260200160405180910390f35b341561091d57600080fd5b61037a6126ea565b604051600160a060020a03909116815260200160405180910390f35b341561094c57600080fd5b6107cf61272e565b60405161ffff909116815260200160405180910390f35b341561097657600080fd5b6108fe612741565b604051901515815260200160405180910390f35b341561099d57600080fd5b61029e61275a565b005b34156109b257600080fd5b61037a6004356127f5565b604051600160a060020a03909116815260200160405180910390f35b34156109e457600080fd5b61029e600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061282795505050505050565b005b3415610a3557600080fd5b61037a612892565b604051600160a060020a03909116815260200160405180910390f35b3415610a6457600080fd5b610a78600160a060020a03600435166128a1565b60405194855263ffffffff90931660208501529015156040808501919091529015156060840152901515608083015260a0909101905180910390f35b3415610abf57600080fd5b61029e600160a060020a03600435166128e7565b005b3415610ae057600080fd5b61037a60043561293a565b604051600160a060020a03909116815260200160405180910390f35b3415610b1257600080fd5b61029e600160a060020a0360043516602435151561296c565b005b3415610b3857600080fd5b61029e63ffffffff600435166129f5565b005b6102bf600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965050843594602001359350610c2e92505050565b60405190815260200160405180910390f35b3415610bb357600080fd5b61037a61ffff60043516612a73565b604051600160a060020a03909116815260200160405180910390f35b3415610be957600080fd5b61029e600160a060020a0360043516612ad0565b005b3415610c0a57600080fd5b61037a612b23565b604051600160a060020a03909116815260200160405180910390f35b60008060008560028151118015610c4757506015815111155b8015610c60575060028151811515610c5b57fe5b066001145b1515610c6b57600080fd5b86600081518110610c7857fe5b90602001906020020151600754909350600160a060020a031663c31e05476000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515610ccd57600080fd5b6102c65a03f11515610cde57600080fd5b5050506040518051925050341515610e6d57600254600160a060020a0384811691161415610de057600254600160a060020a031663a24835d1338860405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515610d5c57600080fd5b6102c65a03f11515610d6d57600080fd5b5050600254600160a060020a0316905063867904b4838860405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515610dc757600080fd5b6102c65a03f11515610dd857600080fd5b505050610e6d565b82600160a060020a03166323b872dd33848960006040516020015260405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b1515610e4a57600080fd5b6102c65a03f11515610e5b57600080fd5b505050604051805190501515610e6d57fe5b5b5b81600160a060020a031663c98fefed34898989336000604051602001526040518663ffffffff1660e060020a028152600401808060200185815260200184815260200183600160a060020a0316600160a060020a03168152602001828103825286818151815260200191508051906020019060200280838360005b83811015610f035780820151818401525b602001610eea565b50505050905001955050505050506020604051808303818588803b1515610f2957600080fd5b6125ee5a03f11515610f3a57600080fd5b5050505060405180519450505b5b5050509392505050565b600160a060020a0381166000908152600a6020526040812060010154819083906601000000000000900460ff161515610f8a57600080fd5b600160a060020a0384166000908152600a602052604090206001810154909250640100000000900460ff166110305783600160a060020a03166370a082313060006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b151561101057600080fd5b6102c65a03f1151561102157600080fd5b50505060405180519050611033565b81545b92505b5b5050919050565b600080600160a060020a03858116908516141561105a57600080fd5b600254600160a060020a03858116911614156110815761107a8584611fc9565b9150611133565b600254600160a060020a03868116911614156110a85761107a8484611d08565b9150611133565b5b6110b38584611fc9565b600254909150611130908590839061112b90600160a060020a03166318160ddd6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561110a57600080fd5b6102c65a03f1151561111b57600080fd5b5050506040518051905085612b32565b612b4c565b91505b509392505050565b60005433600160a060020a0390811691161461115357fe5b600254600160a060020a031663f2fde38b8260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b15156111a357600080fd5b6102c65a03f115156111b457600080fd5b5050505b5b50565b60035433600160a060020a039081169116146111d457fe5b600b80546cff000000000000000000000000191682156c01000000000000000000000000021790555b5b50565b60005433600160a060020a0390811691161461121957fe5b80600160a060020a038116151561122f57600080fd5b8130600160a060020a031681600160a060020a03161415151561125157600080fd5b60078054600160a060020a031916600160a060020a0385161790555b5b505b505b50565b600754600160a060020a031681565b600b54600090620f4240906112ac90849068010000000000000000900463ffffffff16612d64565b8115156112b557fe5b0490505b919050565b60005433600160a060020a039081169116146112d657fe5b600254600160a060020a033081169116638da5cb5b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561132257600080fd5b6102c65a03f1151561133357600080fd5b50505060405180519050600160a060020a03161415151561135057fe5b82600160a060020a038116151561136657600080fd5b8330600160a060020a031681600160a060020a03161415151561138857600080fd5b8360008163ffffffff161180156113a85750620f424063ffffffff821611155b15156113b357600080fd5b600254600160a060020a038781169116148015906113f75750600160a060020a0386166000908152600a60205260409020600101546601000000000000900460ff16155b80156114155750600b54620f424063ffffffff918216870190911611155b151561142057600080fd5b600160a060020a0386166000908152600a602052604081209081556001908101805466010000000000006501000000000063ffffffff1990921663ffffffff8a161764ff000000001916640100000000891515021765ff000000000019169190911766ff000000000000191617905560088054909181016114a18382612daa565b916000526020600020900160005b8154600160a060020a03808b166101009390930a9283029202191617905550600b805463ffffffff80821688011663ffffffff199091161790555b5b505b505b505b5b505050565b60005433600160a060020a0390811691161461150f57fe5b600254600160a060020a03166379ba50976040518163ffffffff1660e060020a028152600401600060405180830381600087803b151561154e57600080fd5b6102c65a03f1151561126d57600080fd5b5050505b5b565b60068054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156115fc5780601f106115d1576101008083540402835291602001916115fc565b820191906000526020600020905b8154815290600101906020018083116115df57829003601f168201915b505050505081565b60005433600160a060020a0390811691161461161c57fe5b600254600160a060020a0316635e35359e84848460405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401600060405180830381600087803b151561167f57600080fd5b6102c65a03f115156114ea57600080fd5b5050505b5b505050565b600454600160a060020a031681565b600354600160a060020a031681565b60005433600160a060020a039081169116146116d057fe5b60006111b8600982612daa565b505b5b565b60058054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156115fc5780601f106115d1576101008083540402835291602001916115fc565b820191906000526020600020905b8154815290600101906020018083116115df57829003601f168201915b505050505081565b600b5468010000000000000000900463ffffffff1681565b60005433600160a060020a039081169116146117b057fe5b82600160a060020a03811615156117c657600080fd5b82600160a060020a03811615156117dc57600080fd5b8330600160a060020a031681600160a060020a0316141515156117fe57600080fd5b85600160a060020a031663a9059cbb868660006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b151561185b57600080fd5b6102c65a03f1151561186c57600080fd5b5050506040518051905015156114ea57fe5b5b5b505b505b505b505050565b600061189985858585611d83565b90505b949350505050565b6000806000806000806000600b600c9054906101000a900460ff1615156118c757fe5b600754600160a060020a031663961a929c6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561190f57600080fd5b6102c65a03f1151561192057600080fd5b50505060405180519050600160a060020a031663fe173b976000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561196f57600080fd5b6102c65a03f1151561198057600080fd5b50505060405180513a1115905061199357fe5b87600081116119a157600080fd5b600254600160a060020a03166370a082313360006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b15156119fa57600080fd5b6102c65a03f11515611a0b57600080fd5b50505060405180518b11159050611a2157600080fd5b611a2b8b8b611d08565b96508615801590611a3c5750888710155b1515611a4457fe5b600254600160a060020a03166318160ddd6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611a8c57600080fd5b6102c65a03f11515611a9d57600080fd5b505050604051805190509550611ab28b610f52565b945084871080611acb57508487148015611acb5750858a145b5b1515611ad457fe5b600160a060020a038b166000908152600a602052604090206001810154909450640100000000900460ff1615611b13578354611b109088612d93565b84555b600254600160a060020a031663a24835d1338c60405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515611b6957600080fd5b6102c65a03f11515611b7a57600080fd5b5050508a600160a060020a031663a9059cbb338960006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515611bda57600080fd5b6102c65a03f11515611beb57600080fd5b505050604051805190501515611bfd57fe5b611c12611c098c610f52565b620f4240612d64565b600254909350611c8d90600160a060020a03166318160ddd6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611c6157600080fd5b6102c65a03f11515611c7257600080fd5b5050506040518051600187015490915063ffffffff16612d64565b600254909250600160a060020a03338116918d821691167f431d62569d69247969ee24b65452f881ddcc12e42b7b71c324403449f870c0d38d8b87896040518085815260200184815260200183815260200182815260200194505050505060405180910390a48697505b5b505b5b5050505050509392505050565b600254600090611d7a9084908490600160a060020a03166318160ddd85604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611d5a57600080fd5b6102c65a03f11515611d6b57600080fd5b50505060405180519050612b4c565b90505b92915050565b600080600160a060020a038681169086161415611d9f57600080fd5b600254600160a060020a0386811691161415611dc757611dc0868585612255565b9150611e0c565b600254600160a060020a0387811691161415611def57611dc08585856118a4565b9150611e0c565b5b611dfc86856001612255565b9050611e098582856118a4565b91505b50949350505050565b6000611e7c6009805480602002602001604051908101604052809291908181526020018280548015611e7057602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311611e52575b50505050503484610c2e565b90505b919050565b60015433600160a060020a03908116911614611e9f57600080fd5b6000546001547f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a91600160a060020a039081169116604051600160a060020a039283168152911660208201526040908101905180910390a16001805460008054600160a060020a0319908116600160a060020a038416179091551690555b565b60005433600160a060020a03908116911614611f3757fe5b600254600160a060020a0316631608f18f8260405160e060020a63ffffffff84160281529015156004820152602401600060405180830381600087803b15156111a357600080fd5b6102c65a03f115156111b457600080fd5b5050505b5b50565b600054600160a060020a031681565b6009545b90565b600b54640100000000900463ffffffff1681565b6008545b90565b60025460009081908190819081908190600160a060020a033081169116638da5cb5b83604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561202157600080fd5b6102c65a03f1151561203257600080fd5b50505060405180519050600160a060020a031614151561204e57fe5b600160a060020a0388166000908152600a602052604090206001015488906601000000000000900460ff16151561208457600080fd5b600160a060020a0389166000908152600a60205260409020600181015490965065010000000000900460ff1615156120bb57600080fd5b600254600160a060020a03166318160ddd6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561210357600080fd5b6102c65a03f1151561211457600080fd5b50505060405180519050945061212989610f52565b600754909450600160a060020a0316634b75f54f6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561217457600080fd5b6102c65a03f1151561218557600080fd5b50505060405180516001880154600160a060020a0390911691506329a00e7c908790879063ffffffff168c6000604051602001526040518563ffffffff1660e060020a028152600401808581526020018481526020018363ffffffff1663ffffffff168152602001828152602001945050505050602060405180830381600087803b151561221257600080fd5b6102c65a03f1151561222357600080fd5b50505060405180519050925061223883611284565b91506122448383612d93565b96505b5b505b505050505092915050565b6000806000806000600b600c9054906101000a900460ff16151561227557fe5b600754600160a060020a031663961a929c6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156122bd57600080fd5b6102c65a03f115156122ce57600080fd5b50505060405180519050600160a060020a031663fe173b976000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561231d57600080fd5b6102c65a03f1151561232e57600080fd5b50505060405180513a1115905061234157fe5b856000811161234f57600080fd5b6123598989611fc9565b9450841580159061236a5750868510155b151561237257fe5b600160a060020a0389166000908152600a602052604090206001810154909450640100000000900460ff16156123b15783546123ae9089612b32565b84555b88600160a060020a03166323b872dd33308b60006040516020015260405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b151561241b57600080fd5b6102c65a03f1151561242c57600080fd5b50505060405180519050151561243e57fe5b600254600160a060020a031663867904b4338760405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561249457600080fd5b6102c65a03f115156124a557600080fd5b5050506124bd611c098a610f52565b620f4240612d64565b60025490935061253890600160a060020a03166318160ddd6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611c6157600080fd5b6102c65a03f11515611c7257600080fd5b5050506040518051600187015490915063ffffffff16612d64565b600254909250600160a060020a03338116918116908b167f431d62569d69247969ee24b65452f881ddcc12e42b7b71c324403449f870c0d38b8988886040518085815260200184815260200183815260200182815260200194505050505060405180910390a48495505b5b505b5b505050509392505050565b60006125be848484610c2e565b90505b9392505050565b6000805433600160a060020a039081169116146125e157fe5b600160a060020a0385166000908152600a602052604090206001015485906601000000000000900460ff16151561261757600080fd5b8460008163ffffffff161180156126375750620f424063ffffffff821611155b151561264257600080fd5b600160a060020a0387166000908152600a602052604090206001810154600b54919450620f424063ffffffff91821692821692909203880116111561268657600080fd5b600183018054600b805463ffffffff928316818416038a01831663ffffffff199182161790915582548815156401000000000264ff0000000019938b169190921617919091161790558383555b5b505b505b5050505050565b600954600090115b90565b6009546000908190116126f957fe5b60098054600090811061270857fe5b906000526020600020900160005b9054906101000a9004600160a060020a031690505b90565b6000612738611fc2565b60010190505b90565b600b546c01000000000000000000000000900460ff1681565b60045433600160a060020a0390811691161461277557600080fd5b6003546004547fbe4cc281795971a471c980e842627a7f1ea3892ddfce8c5b6357cd2611c1973291600160a060020a039081169116604051600160a060020a039283168152911660208201526040908101905180910390a16004805460038054600160a060020a0319908116600160a060020a038416179091551690555b565b600880548290811061280357fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b60005433600160a060020a0390811691161461283f57fe5b806002815111801561285357506015815111155b801561286c57506002815181151561286757fe5b066001145b151561287757600080fd5b600982805161126d929160200190612dfe565b505b5b505b50565b600154600160a060020a031681565b600a602052600090815260409020805460019091015463ffffffff81169060ff640100000000820481169165010000000000810482169166010000000000009091041685565b60035433600160a060020a039081169116146128ff57fe5b600354600160a060020a038281169116141561291a57600080fd5b60048054600160a060020a031916600160a060020a0383161790555b5b50565b600980548290811061280357fe5b906000526020600020900160005b915054906101000a9004600160a060020a031681565b60005433600160a060020a0390811691161461298457fe5b600160a060020a0382166000908152600a602052604090206001015482906601000000000000900460ff1615156129ba57600080fd5b600160a060020a0383166000908152600a60205260409020600101805465ff00000000001916831565010000000000021790555b5b505b5050565b60035433600160a060020a03908116911614612a0d57fe5b8060008163ffffffff1610158015612a395750600b5463ffffffff640100000000909104811690821611155b1515612a4457600080fd5b600b80546bffffffff000000000000000019166801000000000000000063ffffffff8516021790555b5b505b50565b600061ffff82161515612a925750600254600160a060020a03166112b9565b6008805461ffff600019850116908110612aa857fe5b906000526020600020900160005b9054906101000a9004600160a060020a031690505b919050565b60005433600160a060020a03908116911614612ae857fe5b600054600160a060020a0382811691161415612b0357600080fd5b60018054600160a060020a031916600160a060020a0383161790555b5b50565b600254600160a060020a031681565b600082820183811015612b4157fe5b8091505b5092915050565b6002546000908190819081908190600160a060020a033081169116638da5cb5b83604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612ba257600080fd5b6102c65a03f11515612bb357600080fd5b50505060405180519050600160a060020a0316141515612bcf57fe5b600160a060020a0388166000908152600a602052604090206001015488906601000000000000900460ff161515612c0557600080fd5b8660008111612c1357600080fd5b600160a060020a038a166000908152600a602052604090209550612c368a610f52565b600754909550600160a060020a0316634b75f54f6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612c8157600080fd5b6102c65a03f11515612c9257600080fd5b50505060405180516001880154600160a060020a0390911691506349f9b0f7908a90889063ffffffff168d6000604051602001526040518563ffffffff1660e060020a028152600401808581526020018481526020018363ffffffff1663ffffffff168152602001828152602001945050505050602060405180830381600087803b1515612d1f57600080fd5b6102c65a03f11515612d3057600080fd5b505050604051805190509350612d4584611284565b9250612d518484612d93565b96505b5b505b505b505050509392505050565b6000828202831580612d805750828482811515612d7d57fe5b04145b1515612b4157fe5b8091505b5092915050565b600081831015612d9f57fe5b508082035b92915050565b81548183558181151161126d5760008381526020902061126d918101908301612e66565b5b505050565b81548183558181151161126d5760008381526020902061126d918101908301612e66565b5b505050565b828054828255906000526020600020908101928215612e55579160200282015b82811115612e555782518254600160a060020a031916600160a060020a039190911617825560209290920191600190910190612e1e565b5b50612e62929150612e87565b5090565b611fab91905b80821115612e625760008155600101612e6c565b5090565b90565b611fab91905b80821115612e62578054600160a060020a0319168155600101612e8d565b5090565b905600a165627a7a723058203bcef28c4ec11ed12402a171b2dd19dc92ac88874efc5b720e6fe0af1749c5bf0029 \ No newline at end of file diff --git a/test/bin/bancor_converter_v9.abi b/test/bin/bancor_converter_v9.abi new file mode 100755 index 0000000..7c471da --- /dev/null +++ b/test/bin/bancor_converter_v9.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_weight","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"},{"name":"_virtualBalance","type":"uint256"}],"name":"updateConnector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"connectors","outputs":[{"name":"virtualBalance","type":"uint256"},{"name":"weight","type":"uint32"},{"name":"isVirtualBalanceEnabled","type":"bool"},{"name":"isPurchaseEnabled","type":"bool"},{"name":"isSet","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"connectorTokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"}],"name":"getReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"},{"name":"_block","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"name":"quickConvertPrioritized","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableConversions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convertInternal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_amount","type":"uint256"}],"name":"getConversionFeeAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptTokenOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"converterType","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_weight","type":"uint32"},{"name":"_enableVirtualBalance","type":"bool"}],"name":"addConnector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawFromToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newManager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"manager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_whitelist","type":"address"}],"name":"setConversionWhitelist","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"clearQuickBuyPath","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_disable","type":"bool"}],"name":"disableConnectorPurchases","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"change","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_FORMULA","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"connectorTokenCount","outputs":[{"name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getSaleReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_fromToken","type":"address"},{"name":"_toToken","type":"address"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"convert","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CONTRACT_FEATURES","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_disable","type":"bool"}],"name":"disableTokenTransfers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_fromConnectorToken","type":"address"},{"name":"_toConnectorToken","type":"address"},{"name":"_sellAmount","type":"uint256"}],"name":"getCrossConnectorReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BANCOR_NETWORK","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"CONVERTER_CONVERSION_WHITELIST","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getQuickBuyPathLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxConversionFee","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"},{"name":"_depositAmount","type":"uint256"}],"name":"getPurchaseReturn","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_registry","type":"address"}],"name":"setRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"conversionsEnabled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"conversionWhitelist","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"}],"name":"setQuickBuyPath","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_connectorToken","type":"address"}],"name":"getConnectorBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newManager","type":"address"}],"name":"transferManagement","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"quickBuyPath","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_conversionFee","type":"uint32"}],"name":"setConversionFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_path","type":"address[]"},{"name":"_amount","type":"uint256"},{"name":"_minReturn","type":"uint256"}],"name":"quickConvert","outputs":[{"name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_token","type":"address"},{"name":"_registry","type":"address"},{"name":"_maxConversionFee","type":"uint32"},{"name":"_connectorToken","type":"address"},{"name":"_connectorWeight","type":"uint32"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_fromToken","type":"address"},{"indexed":true,"name":"_toToken","type":"address"},{"indexed":true,"name":"_trader","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"},{"indexed":false,"name":"_return","type":"uint256"},{"indexed":false,"name":"_conversionFee","type":"int256"}],"name":"Conversion","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_fromToken","type":"address"},{"indexed":true,"name":"_toToken","type":"address"},{"indexed":false,"name":"_priceN","type":"uint256"},{"indexed":false,"name":"_priceD","type":"uint256"}],"name":"PriceUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_prevFee","type":"uint32"},{"indexed":false,"name":"_newFee","type":"uint32"}],"name":"ConversionFeeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_prevManager","type":"address"},{"indexed":true,"name":"_newManager","type":"address"}],"name":"ManagerUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_prevOwner","type":"address"},{"indexed":true,"name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"}] \ No newline at end of file diff --git a/test/bin/bancor_converter_v9.bin b/test/bin/bancor_converter_v9.bin new file mode 100755 index 0000000..f83c9e1 --- /dev/null +++ b/test/bin/bancor_converter_v9.bin @@ -0,0 +1 @@ +606060405260408051908101604052600381527f302e390000000000000000000000000000000000000000000000000000000000602082015260059080516200004d92916020019062000577565b506040805190810160405260068082527f62616e636f72000000000000000000000000000000000000000000000000000060208301529080516200009692916020019062000577565b50600c80546001606860020a0319166c010000000000000000000000001790553415620000c257600080fd5b60405160a08062003808833981016040528080519190602001805191906020018051919060200180519190602001805160008054600160a060020a03191633600160a060020a03908116919091178255919350915086908190811615156200012957600080fd5b5060028054600160a060020a0319908116600160a060020a0393841617909155600380549091163383161790558590811615156200016657600080fd5b8460008163ffffffff1610158015620001885750620f424063ffffffff821611155b15156200019457600080fd5b60078054600160a060020a031916600160a060020a038981169190911791829055166321f8a7217f436f6e74726163744665617475726573000000000000000000000000000000006040517c010000000000000000000000000000000000000000000000000000000063ffffffff84160281526004810191909152602401602060405180830381600087803b15156200022c57600080fd5b5af115156200023a57600080fd5b5050506040518051935050600160a060020a03831615620002d05782600160a060020a0316632c7077c06001806040517c010000000000000000000000000000000000000000000000000000000063ffffffff8516028152600481019290925215156024820152604401600060405180830381600087803b1515620002be57600080fd5b5af11515620002cc57600080fd5b5050505b600c805467ffffffff00000000191664010000000063ffffffff891602179055600160a060020a038516156200031c576200031c85856000640100000000620016db6200032a82021704565b505050505050505062000648565b60005433600160a060020a039081169116146200034357fe5b600254600160a060020a033081169116638da5cb5b6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1515620003a057600080fd5b5af11515620003ae57600080fd5b50505060405180519050600160a060020a031614151515620003cc57fe5b82600160a060020a0381161515620003e357600080fd5b8330600160a060020a031681600160a060020a0316141515156200040657600080fd5b8360008163ffffffff16118015620004275750620f424063ffffffff821611155b15156200043357600080fd5b600254600160a060020a03878116911614801590620004785750600160a060020a0386166000908152600b60205260409020600101546601000000000000900460ff16155b8015620004975750600c54620f424063ffffffff918216870190911611155b1515620004a357600080fd5b600160a060020a0386166000908152600b602052604081209081556001908101805466010000000000006501000000000063ffffffff1990921663ffffffff8a161764ff000000001916640100000000891515021765ff000000000019169190911766ff00000000000019161790556009805490918101620005268382620005fc565b5060009182526020909120018054600160a060020a031916600160a060020a0397909716969096179095555050600c805463ffffffff19811663ffffffff9182169490940116929092179091555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620005ba57805160ff1916838001178555620005ea565b82800160010185558215620005ea579182015b82811115620005ea578251825591602001919060010190620005cd565b50620005f892915062000628565b5090565b81548183558181151162000623576000838152602090206200062391810190830162000628565b505050565b6200064591905b80821115620005f857600081556001016200062f565b90565b6131b080620006586000396000f30060606040526004361061026e5763ffffffff60e060020a6000350416630c87355e81146102d75780630ca78923146102fc5780630e53aae91461032e57806319b64015146103895780631d000b61146103bb5780631e1401f8146103ce57806321e6b53d146103f65780632274256414610415578063228d2820146104795780632a2e2f0c1461049157806338a5e016146104bc5780633aa0145a146104cf5780633e8ff43f146104eb5780633f4d2fc21461057557806341a5b33d146105a257806342906029146105ca578063481c6a75146105dd5780634af80f0e146105f05780634e2280c41461060f578063514385be1461062257806354fd4d5014610646578063579cd3ca146106595780635a46f06c146106855780635e35359e146106985780635e5144eb146106c05780636d7bd3fc146106eb57806371f52bf3146106fe57806372b44b2c1461072857806375892cf11461074a57806379ba5097146107755780637b1039991461078857806383315b6e1461079b57806385d5e631146107ae5780638da5cb5b146107c65780638e3047e0146107d95780639232494e146108015780639249993a1461081457806392d1abb7146108275780639396a7f01461083a57806394c275ad1461084d578063a2c4c33614610860578063a91ee0dc14610882578063bf754558146108a1578063c45d3d92146108c8578063c8c2fe6c146108db578063d395ee0f146108ee578063d4ee1d901461093d578063d895951214610950578063e4edf8521461096f578063e7ee85a51461098e578063ecbca55d146109a4578063f0843ba9146109c0578063f2fde38b14610a0b578063fc0c546a14610a2a575b6102d4600a8054806020026020016040519081016040528092919081815260200182805480156102c757602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116102a9575b5050505050346001610a3d565b50005b34156102e257600080fd5b6102ea610a90565b60405190815260200160405180910390f35b341561030757600080fd5b61032c600160a060020a036004351663ffffffff602435166044351515606435610ab4565b005b341561033957600080fd5b61034d600160a060020a0360043516610bc8565b60405194855263ffffffff90931660208501529015156040808501919091529015156060840152901515608083015260a0909101905180910390f35b341561039457600080fd5b61039f600435610c0e565b604051600160a060020a03909116815260200160405180910390f35b34156103c657600080fd5b6102ea610c36565b34156103d957600080fd5b6102ea600160a060020a0360043581169060243516604435610c5a565b341561040157600080fd5b61032c600160a060020a0360043516610cd1565b6102ea600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965050843594602081013594506040810135935060ff60608201351692506080810135915060a00135610d4c565b341561048457600080fd5b61032c600435151561108b565b341561049c57600080fd5b6102ea600160a060020a03600435811690602435166044356064356110ee565b34156104c757600080fd5b61032c611578565b34156104da57600080fd5b6102ea60043560ff602435166115e1565b34156104f657600080fd5b6104fe61163d565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561053a578082015183820152602001610522565b50505050905090810190601f1680156105675780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561058057600080fd5b61032c600160a060020a036004351663ffffffff6024351660443515156116db565b34156105ad57600080fd5b61032c600160a060020a036004358116906024351660443561190f565b34156105d557600080fd5b61039f61199f565b34156105e857600080fd5b61039f6119ae565b34156105fb57600080fd5b61032c600160a060020a03600435166119bd565b341561061a57600080fd5b61032c611a27565b341561062d57600080fd5b61032c600160a060020a03600435166024351515611a4f565b341561065157600080fd5b6104fe611ad8565b341561066457600080fd5b61066c611b43565b60405163ffffffff909116815260200160405180910390f35b341561069057600080fd5b6102ea611b5b565b34156106a357600080fd5b61032c600160a060020a0360043581169060243516604435611b7f565b34156106cb57600080fd5b6102ea600160a060020a0360043581169060243516604435606435611c58565b34156106f657600080fd5b6102ea611c66565b341561070957600080fd5b610711611c8a565b60405161ffff909116815260200160405180910390f35b341561073357600080fd5b6102ea600160a060020a0360043516602435611c91565b341561075557600080fd5b6102ea600160a060020a0360043581169060243516604435606435611ee8565b341561078057600080fd5b61032c611f88565b341561079357600080fd5b61039f612016565b34156107a657600080fd5b6102ea612025565b34156107b957600080fd5b61032c6004351515612049565b34156107d157600080fd5b61039f6120a9565b34156107e457600080fd5b6102ea600160a060020a03600435811690602435166044356120b8565b341561080c57600080fd5b6102ea61232e565b341561081f57600080fd5b6102ea612352565b341561083257600080fd5b6102ea612376565b341561084557600080fd5b6102ea61237b565b341561085857600080fd5b61066c612381565b341561086b57600080fd5b6102ea600160a060020a0360043516602435612395565b341561088d57600080fd5b61032c600160a060020a03600435166125da565b34156108ac57600080fd5b6108b461265b565b604051901515815260200160405180910390f35b34156108d357600080fd5b61039f612674565b34156108e657600080fd5b61032c612683565b34156108f957600080fd5b61032c600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965061271195505050505050565b341561094857600080fd5b61039f612774565b341561095b57600080fd5b6102ea600160a060020a0360043516612783565b341561097a57600080fd5b61032c600160a060020a036004351661285f565b341561099957600080fd5b61039f6004356128df565b34156109af57600080fd5b61032c63ffffffff600435166128ed565b6102ea600460248135818101908301358060208181020160405190810160405280939291908181526020018383602002808284375094965050843594602001359350610a3d92505050565b3415610a1657600080fd5b61032c600160a060020a03600435166129e3565b3415610a3557600080fd5b61039f612a45565b60008360028151118015610a5357506015815111155b8015610a6c575060028151811515610a6757fe5b066001145b1515610a7757600080fd5b610a878585856000808080610d4c565b95945050505050565b7f42616e636f72436f6e766572746572557067726164657200000000000000000081565b6000805433600160a060020a03908116911614610acd57fe5b600160a060020a0385166000908152600b602052604090206001015485906601000000000000900460ff161515610b0357600080fd5b8460008163ffffffff16118015610b235750620f424063ffffffff821611155b1515610b2e57600080fd5b600160a060020a0387166000908152600b602052604090206001810154600c54919450620f424063ffffffff918216928216929092038801161115610b7257600080fd5b5050600181018054600c805463ffffffff928316818416038801831663ffffffff199182161790915582549515156401000000000264ff0000000019929097169516949094179390931693909317909155905550565b600b602052600090815260409020805460019091015463ffffffff81169060ff640100000000820481169165010000000000810482169166010000000000009091041685565b6009805482908110610c1c57fe5b600091825260209091200154600160a060020a0316905081565b7f424e54546f6b656e00000000000000000000000000000000000000000000000081565b6000600160a060020a038481169084161415610c7557600080fd5b600254600160a060020a0384811691161415610c9c57610c958483612395565b9050610cca565b600254600160a060020a0385811691161415610cbc57610c958383611c91565b610cc78484846120b8565b90505b9392505050565b60005433600160a060020a03908116911614610ce957fe5b600254600160a060020a031663f2fde38b8260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401600060405180830381600087803b1515610d3957600080fd5b5af11515610d4657600080fd5b50505050565b60008060008960028151118015610d6557506015815111155b8015610d7e575060028151811515610d7957fe5b066001145b1515610d8957600080fd5b8a600081518110610d9657fe5b90602001906020020151600754909350600160a060020a03166321f8a7217f42616e636f724e6574776f726b0000000000000000000000000000000000000060405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610e0b57600080fd5b5af11515610e1857600080fd5b5050506040518051925050341515610f9257600254600160a060020a0384811691161415610f1257600254600160a060020a031663a24835d1338c60405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515610e9657600080fd5b5af11515610ea357600080fd5b5050600254600160a060020a0316905063867904b4838c60405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515610efd57600080fd5b5af11515610f0a57600080fd5b505050610f92565b82600160a060020a03166323b872dd33848d60405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b1515610f7357600080fd5b5af11515610f8057600080fd5b505050604051805190501515610f9257fe5b81600160a060020a0316636b08f2ef348d8d8d338e8e8e8e60405160e060020a63ffffffff8c160281526024810188905260448101879052600160a060020a03861660648201526084810185905260ff841660a482015260c4810183905260e4810182905261010060048201908152908190610104018a818151815260200191508051906020019060200280838360005b8381101561103b578082015183820152602001611023565b5050505090500199505050505050505050506020604051808303818588803b151561106557600080fd5b5af1151561107257600080fd5b5050505060405180519c9b505050505050505050505050565b60005433600160a060020a03908116911614806110b6575060035433600160a060020a039081169116145b15156110c157600080fd5b600c80546cff000000000000000000000000191691156c0100000000000000000000000002919091179055565b600754600090819081908190819081908190600160a060020a03166321f8a7217f42616e636f724e6574776f726b0000000000000000000000000000000000000060405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561116557600080fd5b5af1151561117257600080fd5b505050604051805191505033600160a060020a039081169082161461119657600080fd5b600c546c01000000000000000000000000900460ff1615156111b457fe5b87600081116111c257600080fd5b600160a060020a038c8116908c1614156111db57600080fd5b600254600160a060020a038c811691161415611203576111fc8c8b8b612a54565b9750611569565b600254600160a060020a038d811691161415611224576111fc8b8b8b612c95565b61122f8c8c8c6120b8565b965086158015906112405750888710155b151561124b57600080fd5b600160a060020a038c166000908152600b602052604090206001810154909650640100000000900460ff161561128a578554611287908b612fc6565b86555b600160a060020a038b166000908152600b602052604090206001810154909550640100000000900460ff16156112c95784546112c69088612fd5565b85555b6112d28b612783565b93508387106112dd57fe5b8b600160a060020a03166323b872dd33308d60405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b151561133e57600080fd5b5af1151561134b57600080fd5b50505060405180519050151561135d57fe5b8a600160a060020a031663a9059cbb338960405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b15156113b157600080fd5b5af115156113be57600080fd5b5050506040518051905015156113d057fe5b6113e4876113df8960026115e1565b612fd5565b92506113f38c8c8c8a87612fe7565b600254600160a060020a03808e16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a78891166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561145857600080fd5b5af1151561146557600080fd5b505050604051805190506114788f612783565b60018a015463ffffffff16604051928352602083019190915263ffffffff166040808301919091526060909101905180910390a2600254600160a060020a03808d16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a78891166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561151157600080fd5b5af1151561151e57600080fd5b505050604051805190506115318e612783565b600189015463ffffffff16604051928352602083019190915263ffffffff166040808301919091526060909101905180910390a28697505b50505050505050949350505050565b60005433600160a060020a0390811691161461159057fe5b600254600160a060020a03166379ba50976040518163ffffffff1660e060020a028152600401600060405180830381600087803b15156115cf57600080fd5b5af115156115dc57600080fd5b505050565b60008160ff16620f42400a67ffffffffffffffff1661162c848460ff16600c60089054906101000a900463ffffffff1663ffffffff16620f4240030a67ffffffffffffffff1661307d565b81151561163557fe5b049392505050565b60068054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156116d35780601f106116a8576101008083540402835291602001916116d3565b820191906000526020600020905b8154815290600101906020018083116116b657829003601f168201915b505050505081565b60005433600160a060020a039081169116146116f357fe5b600254600160a060020a033081169116638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561173657600080fd5b5af1151561174357600080fd5b50505060405180519050600160a060020a03161415151561176057fe5b82600160a060020a038116151561177657600080fd5b8330600160a060020a031681600160a060020a03161415151561179857600080fd5b8360008163ffffffff161180156117b85750620f424063ffffffff821611155b15156117c357600080fd5b600254600160a060020a038781169116148015906118075750600160a060020a0386166000908152600b60205260409020600101546601000000000000900460ff16155b80156118255750600c54620f424063ffffffff918216870190911611155b151561183057600080fd5b600160a060020a0386166000908152600b602052604081209081556001908101805466010000000000006501000000000063ffffffff1990921663ffffffff8a161764ff000000001916640100000000891515021765ff000000000019169190911766ff000000000000191617905560098054909181016118b183826130a1565b506000918252602090912001805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0397909716969096179095555050600c805463ffffffff19811663ffffffff9182169490940116929092179091555050565b60005433600160a060020a0390811691161461192757fe5b600254600160a060020a0316635e35359e84848460405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401600060405180830381600087803b151561198a57600080fd5b5af1151561199757600080fd5b505050505050565b600454600160a060020a031681565b600354600160a060020a031681565b60005433600160a060020a039081169116146119d557fe5b8030600160a060020a031681600160a060020a0316141515156119f757600080fd5b506008805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b60005433600160a060020a03908116911614611a3f57fe5b6000611a4c600a826130a1565b50565b60005433600160a060020a03908116911614611a6757fe5b600160a060020a0382166000908152600b602052604090206001015482906601000000000000900460ff161515611a9d57600080fd5b50600160a060020a03919091166000908152600b60205260409020600101805465ff0000000000191691156501000000000002919091179055565b60058054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156116d35780601f106116a8576101008083540402835291602001916116d3565b600c5468010000000000000000900463ffffffff1681565b7f42616e636f72436f6e766572746572466163746f72790000000000000000000081565b60005433600160a060020a03908116911614611b9757fe5b82600160a060020a0381161515611bad57600080fd5b82600160a060020a0381161515611bc357600080fd5b8330600160a060020a031681600160a060020a031614151515611be557600080fd5b85600160a060020a031663a9059cbb868660405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515611c3957600080fd5b5af11515611c4657600080fd5b50505060405180519050151561199757fe5b6000610a87858585856110ee565b7f42616e636f72466f726d756c610000000000000000000000000000000000000081565b6009545b90565b60025460009081908190819081908190600160a060020a033081169116638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611ce157600080fd5b5af11515611cee57600080fd5b50505060405180519050600160a060020a0316141515611d0a57fe5b600160a060020a0388166000908152600b602052604090206001015488906601000000000000900460ff161515611d4057600080fd5b600160a060020a03808a166000908152600b602052604090819020600254909850909116906318160ddd90518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611d9757600080fd5b5af11515611da457600080fd5b505050604051805190509450611db989612783565b600754909450600160a060020a03166321f8a7217f42616e636f72466f726d756c610000000000000000000000000000000000000060405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515611e2457600080fd5b5af11515611e3157600080fd5b50505060405180516001880154909450600160a060020a03851691506349f9b0f7908790879063ffffffff168c6040518563ffffffff1660e060020a028152600401808581526020018481526020018363ffffffff1663ffffffff168152602001828152602001945050505050602060405180830381600087803b1515611eb757600080fd5b5af11515611ec457600080fd5b505050604051805190509150611edb8260016115e1565b9998505050505050505050565b600060606040519081016040908152600160a060020a03808816835260025481166020840152861690820152611f2290600d9060036130c5565b50610a87600d805480602002602001604051908101604052809291908181526020018280548015611f7c57602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311611f5e575b50505050508484610a3d565b60015433600160a060020a03908116911614611fa357600080fd5b600154600054600160a060020a0391821691167f343765429aea5a34b3ff6a3785a98a5abb2597aca87bfbb58632c173d585373a60405160405180910390a3600180546000805473ffffffffffffffffffffffffffffffffffffffff19908116600160a060020a03841617909155169055565b600754600160a060020a031681565b7f436f6e747261637446656174757265730000000000000000000000000000000081565b60005433600160a060020a0390811691161461206157fe5b600254600160a060020a0316631608f18f8260405160e060020a63ffffffff84160281529015156004820152602401600060405180830381600087803b1515610d3957600080fd5b600054600160a060020a031681565b600254600090819081908190819081908190600160a060020a033081169116638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b151561210a57600080fd5b5af1151561211757600080fd5b50505060405180519050600160a060020a031614151561213357fe5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561216957600080fd5b600160a060020a038a166000908152600b60205260409020600101548a906601000000000000900460ff16151561219f57600080fd5b600160a060020a038c81166000908152600b6020526040808220928e16825290206001810154919950975065010000000000900460ff1615156121e157600080fd5b6121ea8c612783565b95506121f58b612783565b600754909550600160a060020a03166321f8a7217f42616e636f72466f726d756c610000000000000000000000000000000000000060405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561226057600080fd5b5af1151561226d57600080fd5b50505060405180516001808b0154908a0154919650600160a060020a03871692506365098bb391899163ffffffff908116918a91168f60405163ffffffff87811660e060020a0282526004820196909652938516602485015260448401929092529092166064820152608481019190915260a401602060405180830381600087803b15156122fa57600080fd5b5af1151561230757600080fd5b50505060405180519050925061231e8360026115e1565b9c9b505050505050505050505050565b7f42616e636f724e6574776f726b0000000000000000000000000000000000000081565b7f42616e636f7247617350726963654c696d69740000000000000000000000000081565b600181565b600a5490565b600c54640100000000900463ffffffff1681565b60025460009081908190819081908190600160a060020a033081169116638da5cb5b6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156123e557600080fd5b5af115156123f257600080fd5b50505060405180519050600160a060020a031614151561240e57fe5b600160a060020a0388166000908152600b602052604090206001015488906601000000000000900460ff16151561244457600080fd5b600160a060020a0389166000908152600b60205260409020600181015490965065010000000000900460ff16151561247b57600080fd5b600254600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156124ba57600080fd5b5af115156124c757600080fd5b5050506040518051905094506124dc89612783565b600754909450600160a060020a03166321f8a7217f42616e636f72466f726d756c610000000000000000000000000000000000000060405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561254757600080fd5b5af1151561255457600080fd5b50505060405180516001880154909450600160a060020a03851691506329a00e7c908790879063ffffffff168c6040518563ffffffff1660e060020a028152600401808581526020018481526020018363ffffffff1663ffffffff168152602001828152602001945050505050602060405180830381600087803b1515611eb757600080fd5b60005433600160a060020a039081169116146125f257fe5b80600160a060020a038116151561260857600080fd5b8130600160a060020a031681600160a060020a03161415151561262a57600080fd5b50506007805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600c546c01000000000000000000000000900460ff1681565b600854600160a060020a031681565b60045433600160a060020a0390811691161461269e57600080fd5b600454600354600160a060020a0391821691167fbe4cc281795971a471c980e842627a7f1ea3892ddfce8c5b6357cd2611c1973260405160405180910390a3600480546003805473ffffffffffffffffffffffffffffffffffffffff19908116600160a060020a03841617909155169055565b60005433600160a060020a0390811691161461272957fe5b806002815111801561273d57506015815111155b801561275657506002815181151561275157fe5b066001145b151561276157600080fd5b600a8280516115dc9291602001906130c5565b600154600160a060020a031681565b600160a060020a0381166000908152600b6020526040812060010154819083906601000000000000900460ff1615156127bb57600080fd5b600160a060020a0384166000908152600b602052604090206001810154909250640100000000900460ff166128545783600160a060020a03166370a082313060405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b151561283857600080fd5b5af1151561284557600080fd5b50505060405180519050612857565b81545b949350505050565b60005433600160a060020a039081169116148061288a575060035433600160a060020a039081169116145b151561289557600080fd5b600354600160a060020a03828116911614156128b057600080fd5b6004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600a805482908110610c1c57fe5b60005433600160a060020a0390811691161480612918575060035433600160a060020a039081169116145b151561292357600080fd5b8060008163ffffffff161015801561294f5750600c5463ffffffff640100000000909104811690821611155b151561295a57600080fd5b600c547f81cd2ffb37dd237c0e4e2a3de5265fcf9deb43d3e7801e80db9f1ccfba7ee6009068010000000000000000900463ffffffff168360405163ffffffff9283168152911660208201526040908101905180910390a150600c805463ffffffff90921668010000000000000000026bffffffff000000000000000019909216919091179055565b60005433600160a060020a039081169116146129fb57fe5b600054600160a060020a0382811691161415612a1657600080fd5b6001805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600254600160a060020a031681565b600080600080612a648787612395565b92508215801590612a755750848310155b1515612a8057600080fd5b600160a060020a0387166000908152600b602052604090206001810154909250640100000000900460ff1615612abf578154612abc9087612fc6565b82555b86600160a060020a03166323b872dd33308960405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b1515612b2057600080fd5b5af11515612b2d57600080fd5b505050604051805190501515612b3f57fe5b600254600160a060020a031663867904b4338560405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515612b9557600080fd5b5af11515612ba257600080fd5b505050612bb4836113df8560016115e1565b600254909150612bd1908890600160a060020a0316888685612fe7565b600254600160a060020a03808916917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a78891166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612c3657600080fd5b5af11515612c4357600080fd5b50505060405180519050612c568a612783565b600186015463ffffffff16604051928352602083019190915263ffffffff166040808301919091526060909101905180910390a2509095945050505050565b60025460009081908190819081908190600160a060020a03166370a082313360405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b1515612cf257600080fd5b5af11515612cff57600080fd5b50505060405180518911159050612d1557600080fd5b612d1f8989611c91565b94508415801590612d305750868510155b1515612d3b57600080fd5b600254600160a060020a03166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612d7a57600080fd5b5af11515612d8757600080fd5b505050604051805190509350612d9c89612783565b925082851080612db557508285148015612db557508388145b1515612dbd57fe5b600160a060020a0389166000908152600b602052604090206001810154909250640100000000900460ff1615612dfc578154612df99086612fd5565b82555b600254600160a060020a031663a24835d1338a60405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b1515612e5257600080fd5b5af11515612e5f57600080fd5b50505088600160a060020a031663a9059cbb338760405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515612eb657600080fd5b5af11515612ec357600080fd5b505050604051805190501515612ed557fe5b612ee4856113df8760016115e1565b600254909150612f0090600160a060020a03168a8a8885612fe7565b600254600160a060020a03808b16917f8a6a7f53b3c8fa1dc4b83e3f1be668c1b251ff8d44cdcb83eb3acec3fec6a78891166318160ddd6040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515612f6557600080fd5b5af11515612f7257600080fd5b50505060405180519050612f858c612783565b600186015463ffffffff16604051928352602083019190915263ffffffff166040808301919091526060909101905180910390a25092979650505050505050565b600082820183811015610cca57fe5b600081831015612fe157fe5b50900390565b7f800000000000000000000000000000000000000000000000000000000000000081111561301157fe5b33600160a060020a031684600160a060020a031686600160a060020a03167f276856b36cbc45526a0ba64f44611557a2a8b68662c5388e9fe6d72e86e1c8cb86868660405180848152602001838152602001828152602001935050505060405180910390a45050505050565b6000828202831580613099575082848281151561309657fe5b04145b1515610cca57fe5b8154818355818115116115dc576000838152602090206115dc918101908301613139565b828054828255906000526020600020908101928215613129579160200282015b82811115613129578251825473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0391909116178255602092909201916001909101906130e5565b50613135929150613153565b5090565b611c8e91905b80821115613135576000815560010161313f565b611c8e91905b8082111561313557805473ffffffffffffffffffffffffffffffffffffffff191681556001016131595600a165627a7a723058200f1eae2f2ab00bf29aa60bdcfa1e20210e1cf2f4bbc1ea7dc51d8fe40a6504240029 \ No newline at end of file diff --git a/test/helpers/BancorConverter.js b/test/helpers/BancorConverter.js new file mode 100755 index 0000000..0f857f3 --- /dev/null +++ b/test/helpers/BancorConverter.js @@ -0,0 +1,25 @@ +const fs = require('fs'); +const truffleContract = require('truffle-contract'); +const BancorConverter = artifacts.require('BancorConverter'); + +module.exports.new = async function(tokenAddress, registryAddress, maxConversionFee, reserveTokenAddress, ratio, version) { + if (version) { + const abi = fs.readFileSync(__dirname + `/../bin/bancor_converter_v${version}.abi`); + const bin = fs.readFileSync(__dirname + `/../bin/bancor_converter_v${version}.bin`); + const bancorConverter = truffleContract({abi: JSON.parse(abi), unlinked_binary: '0x' + bin}); + const block = await web3.eth.getBlock('latest'); + bancorConverter.setProvider(web3.currentProvider); + bancorConverter.defaults({from: web3.eth.accounts[0], gas: block.gasLimit}); + return await bancorConverter.new(tokenAddress, registryAddress, maxConversionFee, reserveTokenAddress, ratio); + } + return await BancorConverter.new(tokenAddress, registryAddress, maxConversionFee, reserveTokenAddress, ratio); +} + +module.exports.at = function(address, version) { + if (version) { + const abi = fs.readFileSync(__dirname + `/../bin/bancor_converter_v${version}.abi`); + const bancorConverter = truffleContract({abi: JSON.parse(abi)}); + return bancorConverter.at(address); + } + return BancorConverter.at(address); +} diff --git a/test/helpers/ContractRegistryClient.js b/test/helpers/ContractRegistryClient.js new file mode 100755 index 0000000..ea1d153 --- /dev/null +++ b/test/helpers/ContractRegistryClient.js @@ -0,0 +1,11 @@ +module.exports.CONTRACT_FEATURES = web3.fromAscii("ContractFeatures"); +module.exports.CONTRACT_REGISTRY = web3.fromAscii("ContractRegistry"); +module.exports.BANCOR_NETWORK = web3.fromAscii("BancorNetwork"); +module.exports.BANCOR_FORMULA = web3.fromAscii("BancorFormula"); +module.exports.BANCOR_CONVERTER_FACTORY = web3.fromAscii("BancorConverterFactory"); +module.exports.BANCOR_CONVERTER_UPGRADER = web3.fromAscii("BancorConverterUpgrader"); +module.exports.BANCOR_CONVERTER_REGISTRY = web3.fromAscii("BancorConverterRegistry"); +module.exports.BANCOR_CONVERTER_REGISTRY_DATA = web3.fromAscii("BancorConverterRegistryData"); +module.exports.BNT_TOKEN = web3.fromAscii("BNTToken"); +module.exports.BANCOR_X = web3.fromAscii("BancorX"); +module.exports.BANCOR_X_UPGRADER = web3.fromAscii("BancorXUpgrader"); diff --git a/test/helpers/FormulaConstants.js b/test/helpers/FormulaConstants.js new file mode 100755 index 0000000..055ff42 --- /dev/null +++ b/test/helpers/FormulaConstants.js @@ -0,0 +1,262 @@ +module.exports.MIN_PRECISION = 32; +module.exports.MAX_PRECISION = 127; +module.exports.maxExpArray = [ + /* 0 */ '0xd7', + /* 1 */ '0x19f', + /* 2 */ '0x31b', + /* 3 */ '0x5f6', + /* 4 */ '0xb6e', + /* 5 */ '0x15ec', + /* 6 */ '0x2a0c', + /* 7 */ '0x50a2', + /* 8 */ '0x9aa2', + /* 9 */ '0x1288c', + /* 10 */ '0x238b2', + /* 11 */ '0x4429a', + /* 12 */ '0x82b78', + /* 13 */ '0xfaadc', + /* 14 */ '0x1e0bb8', + /* 15 */ '0x399e96', + /* 16 */ '0x6e7f88', + /* 17 */ '0xd3e7a3', + /* 18 */ '0x1965fea', + /* 19 */ '0x30b5057', + /* 20 */ '0x5d681f3', + /* 21 */ '0xb320d03', + /* 22 */ '0x15784a40', + /* 23 */ '0x292c5bdd', + /* 24 */ '0x4ef57b9b', + /* 25 */ '0x976bd995', + /* 26 */ '0x122624e32', + /* 27 */ '0x22ce03cd5', + /* 28 */ '0x42beef808', + /* 29 */ '0x7ffffffff', + /* 30 */ '0xf577eded5', + /* 31 */ '0x1d6bd8b2eb', + /* 32 */ '0x386bfdba29', + /* 33 */ '0x6c3390ecc8', + /* 34 */ '0xcf8014760f', + /* 35 */ '0x18ded91f0e7', + /* 36 */ '0x2fb1d8fe082', + /* 37 */ '0x5b771955b36', + /* 38 */ '0xaf67a93bb50', + /* 39 */ '0x15060c256cb2', + /* 40 */ '0x285145f31ae5', + /* 41 */ '0x4d5156639708', + /* 42 */ '0x944620b0e70e', + /* 43 */ '0x11c592761c666', + /* 44 */ '0x2214d10d014ea', + /* 45 */ '0x415bc6d6fb7dd', + /* 46 */ '0x7d56e76777fc5', + /* 47 */ '0xf05dc6b27edad', + /* 48 */ '0x1ccf4b44bb4820', + /* 49 */ '0x373fc456c53bb7', + /* 50 */ '0x69f3d1c921891c', + /* 51 */ '0xcb2ff529eb71e4', + /* 52 */ '0x185a82b87b72e95', + /* 53 */ '0x2eb40f9f620fda6', + /* 54 */ '0x5990681d961a1ea', + /* 55 */ '0xabc25204e02828d', + /* 56 */ '0x14962dee9dc97640', + /* 57 */ '0x277abdcdab07d5a7', + /* 58 */ '0x4bb5ecca963d54ab', + /* 59 */ '0x9131271922eaa606', + /* 60 */ '0x116701e6ab0cd188d', + /* 61 */ '0x215f77c045fbe8856', + /* 62 */ '0x3ffffffffffffffff', + /* 63 */ '0x7abbf6f6abb9d087f', + /* 64 */ '0xeb5ec597592befbf4', + /* 65 */ '0x1c35fedd14b861eb04', + /* 66 */ '0x3619c87664579bc94a', + /* 67 */ '0x67c00a3b07ffc01fd6', + /* 68 */ '0xc6f6c8f8739773a7a4', + /* 69 */ '0x17d8ec7f04136f4e561', + /* 70 */ '0x2dbb8caad9b7097b91a', + /* 71 */ '0x57b3d49dda84556d6f6', + /* 72 */ '0xa830612b6591d9d9e61', + /* 73 */ '0x1428a2f98d728ae223dd', + /* 74 */ '0x26a8ab31cb8464ed99e1', + /* 75 */ '0x4a23105873875bd52dfd', + /* 76 */ '0x8e2c93b0e33355320ead', + /* 77 */ '0x110a688680a7530515f3e', + /* 78 */ '0x20ade36b7dbeeb8d79659', + /* 79 */ '0x3eab73b3bbfe282243ce1', + /* 80 */ '0x782ee3593f6d69831c453', + /* 81 */ '0xe67a5a25da41063de1495', + /* 82 */ '0x1b9fe22b629ddbbcdf8754', + /* 83 */ '0x34f9e8e490c48e67e6ab8b', + /* 84 */ '0x6597fa94f5b8f20ac16666', + /* 85 */ '0xc2d415c3db974ab32a5184', + /* 86 */ '0x175a07cfb107ed35ab61430', + /* 87 */ '0x2cc8340ecb0d0f520a6af58', + /* 88 */ '0x55e129027014146b9e37405', + /* 89 */ '0xa4b16f74ee4bb2040a1ec6c', + /* 90 */ '0x13bd5ee6d583ead3bd636b5c', + /* 91 */ '0x25daf6654b1eaa55fd64df5e', + /* 92 */ '0x4898938c9175530325b9d116', + /* 93 */ '0x8b380f3558668c46c91c49a2', + /* 94 */ '0x10afbbe022fdf442b2a522507', + /* 95 */ '0x1ffffffffffffffffffffffff', + /* 96 */ '0x3d5dfb7b55dce843f89a7dbcb', + /* 97 */ '0x75af62cbac95f7dfa3295ec26', + /* 98 */ '0xe1aff6e8a5c30f58221fbf899', + /* 99 */ '0x1b0ce43b322bcde4a56e8ada5a', + /* 100 */ '0x33e0051d83ffe00feb432b473b', + /* 101 */ '0x637b647c39cbb9d3d26c56e949', + /* 102 */ '0xbec763f8209b7a72b0afea0d31', + /* 103 */ '0x16ddc6556cdb84bdc8d12d22e6f', + /* 104 */ '0x2bd9ea4eed422ab6b7b072b029e', + /* 105 */ '0x54183095b2c8ececf30dd533d03', + /* 106 */ '0xa14517cc6b9457111eed5b8adf1', + /* 107 */ '0x13545598e5c23276ccf0ede68034', + /* 108 */ '0x2511882c39c3adea96fec2102329', + /* 109 */ '0x471649d87199aa990756806903c5', + /* 110 */ '0x88534434053a9828af9f37367ee6', + /* 111 */ '0x1056f1b5bedf75c6bcb2ce8aed428', + /* 112 */ '0x1f55b9d9ddff141121e70ebe0104e', + /* 113 */ '0x3c1771ac9fb6b4c18e229803dae82', + /* 114 */ '0x733d2d12ed20831ef0a4aead8c66d', + /* 115 */ '0xdcff115b14eedde6fc3aa5353f2e4', + /* 116 */ '0x1a7cf47248624733f355c5c1f0d1f1', + /* 117 */ '0x32cbfd4a7adc790560b3335687b89b', + /* 118 */ '0x616a0ae1edcba5599528c20605b3f6', + /* 119 */ '0xbad03e7d883f69ad5b0a186184e06b', + /* 120 */ '0x16641a07658687a905357ac0ebe198b', + /* 121 */ '0x2af09481380a0a35cf1ba02f36c6a56', + /* 122 */ '0x5258b7ba7725d902050f6360afddf96', + /* 123 */ '0x9deaf736ac1f569deb1b5ae3f36c130', + /* 124 */ '0x12ed7b32a58f552afeb26faf21deca06', + /* 125 */ '0x244c49c648baa98192dce88b42f53caf', + /* 126 */ '0x459c079aac334623648e24d17c74b3dc', + /* 127 */ '0x857ddf0117efa215952912839f6473e6', +]; +module.exports.maxValArray = [ + /* 0 */ '0x2550a7d99147ce113d27f304d24a422c3d', + /* 1 */ '0x1745f7d567fdd8c93da354496cf4dddf34', + /* 2 */ '0xb5301cf4bf20167721bcdbe218a66f1e0', + /* 3 */ '0x5e2d2ca56fae9ef2e524ba4d0f75b8754', + /* 4 */ '0x2f45acad795bce6dcd748391bb828dcea', + /* 5 */ '0x17f631b6609d1047920e1a1f9613f870d', + /* 6 */ '0xc29d4a7745ae89ef20a05db656441649', + /* 7 */ '0x6242dea9277cf2d473468985313625bb', + /* 8 */ '0x31aef9b37fbc57d1ca51c53eb472c345', + /* 9 */ '0x1923b23c38638957faeb8b4fe57b5ead', + /* 10 */ '0xcb919ec79bf364210433b9b9680eadd', + /* 11 */ '0x67186c63186761709a96a91d44ff2bf', + /* 12 */ '0x343e6242f854acd626b78022c4a8002', + /* 13 */ '0x1a7efb7b1b687ccb2bb413b92d5e413', + /* 14 */ '0xd72d0627fadb6aa6e0f3c994a5592a', + /* 15 */ '0x6d4f32a7dcd0924c122312b7522049', + /* 16 */ '0x37947990f145344d736c1e7e5cff2f', + /* 17 */ '0x1c49d8ceb31e3ef3e98703e0e656cc', + /* 18 */ '0xe69cb6255a180e2ead170f676fa3c', + /* 19 */ '0x75a24620898b4a19aafdfa67d23e8', + /* 20 */ '0x3c1419351dd33d49e1ce203728e25', + /* 21 */ '0x1eb97e709f819575e656eefb8bd98', + /* 22 */ '0xfbc4a1f867f03d4c057d522b6523', + /* 23 */ '0x812507c14867d2237468ba955def', + /* 24 */ '0x425b9d8ca5a58142d5172c3eb2b5', + /* 25 */ '0x2228e76a368b75ea80882c9f6010', + /* 26 */ '0x119ed9f43c52cdd38348ee8d7b23', + /* 27 */ '0x91bfcff5e91c7f115393af54bad', + /* 28 */ '0x4b8845f19f7b4a93653588ce846', + /* 29 */ '0x273fa600431f30b0f21b619c797', + /* 30 */ '0x1474840ba4069691110ff1bb823', + /* 31 */ '0xab212322b671a11d3647e3ecaf', + /* 32 */ '0x59ce8876bf3a3b1b396ae19c95', + /* 33 */ '0x2f523e50d3b0d68a3e39f2f06e', + /* 34 */ '0x190c4f51698c5ee5c3b34928a0', + /* 35 */ '0xd537c5d5647f2a79965d56f94', + /* 36 */ '0x72169649d403b5b512b40d5c2', + /* 37 */ '0x3d713a141a21a93a218c980c1', + /* 38 */ '0x215544c77538e6de9275431a6', + /* 39 */ '0x123c0edc8bf784d147024b7df', + /* 40 */ '0xa11eada236d9ccb5d9a46757', + /* 41 */ '0x59f185464ae514ade263ef14', + /* 42 */ '0x32d507935c586248656e95cb', + /* 43 */ '0x1d2270a4f18efd8eab5a27d7', + /* 44 */ '0x10f7bfaf758e3c1010bead08', + /* 45 */ '0xa101f6bc5df6cc4cf4cb56d', + /* 46 */ '0x61773c45cb6403833991e6e', + /* 47 */ '0x3c5f563f3abca8034b91c7d', + /* 48 */ '0x265cd2a70d374397f75a844', + /* 49 */ '0x1911bbf62c34780ee22ce8e', + /* 50 */ '0x10e3053085e97a7710c2e6d', + /* 51 */ '0xbbfc0e61443560740fa601', + /* 52 */ '0x874f16aa407949aebced14', + /* 53 */ '0x64df208d66f55c59261f5d', + /* 54 */ '0x4dee90487e19a58fbf52e9', + /* 55 */ '0x3e679f9e3b2f65e9d9b0db', + /* 56 */ '0x33c719b34c57f9f7a922f6', + /* 57 */ '0x2c7c090c36927c216fe17c', + /* 58 */ '0x2789fc1ccdbd02af70650f', + /* 59 */ '0x2451aae7a1741e150c6ae0', + /* 60 */ '0x22700f74722225e8c308e6', + /* 61 */ '0x21aae2600cf1170129eb92', + /* 62 */ '0x21e552192ec12eccaa1d44', + /* 63 */ '0x231a0b6c2a250a15897b8a', + /* 64 */ '0x255901ff2640b9b00fef5e', + /* 65 */ '0x28c842993fe2877ca68b09', + /* 66 */ '0x2da7b7138200abf065bc12', + /* 67 */ '0x34584e19c1677771772dbf', + /* 68 */ '0x3d678fd12af3f51aa5828a', + /* 69 */ '0x49a16c994ca36bb50c32c9', + /* 70 */ '0x5a2b2d67887520aacedab6', + /* 71 */ '0x70ac191abaee2a72987db6', + /* 72 */ '0x8f8afbb1a74e96379df7b1', + /* 73 */ '0xba4bd6d86b43467101fd6c', + /* 74 */ '0xf61f8e0679ef553e95c271', + /* 75 */ '0x14ac1e3b06c9771ad8f351c', + /* 76 */ '0x1c3d320c47b0e10030f080e', + /* 77 */ '0x272f678a02b5bd5dcc145a7', + /* 78 */ '0x3732bb25f4914992758a3aa', + /* 79 */ '0x4ee25a85a30b4e758af15a0', + /* 80 */ '0x724dbc7344a886ed20dbae2', + /* 81 */ '0xa7d64de739a14a222daf692', + /* 82 */ '0xf99876906cf6526b6b82ecc', + /* 83 */ '0x177bbaca105a36b48757a319', + /* 84 */ '0x23c442370233418f33964a65', + /* 85 */ '0x3716c05776b217ecbb587d11', + /* 86 */ '0x55c42bb597ed985a9d69778e', + /* 87 */ '0x86e8f9efa6efeba9e16b0a90', + /* 88 */ '0xd651f2e547d194ee8b6d9a69', + /* 89 */ '0x157b681e454d31a35819b1989', + /* 90 */ '0x22c414309a2b397b4f8e0eb28', + /* 91 */ '0x38c1a2330fcf634a5db1378a0', + /* 92 */ '0x5d6efaaf8133556840468bbbb', + /* 93 */ '0x9b0c82dee2e1f20d0a157a7ae', + /* 94 */ '0x10347bdd997b95a7905d850436', + /* 95 */ '0x1b4c902e273a586783055cede8', + /* 96 */ '0x2e50642e85a0b7c589bac2651b', + /* 97 */ '0x4f1b7f75028232ad3258b8b742', + /* 98 */ '0x880028111c381b5279db2271c3', + /* 99 */ '0xeb454460fe475acef6b927865e', + /* 100 */ '0x1996fab0c95ac4a2b5cfa8f555d', + /* 101 */ '0x2cc9f3994685c8d3224acb9fea1', + /* 102 */ '0x4ed2e079d693966878c7149351a', + /* 103 */ '0x8b740d663b523dad8b67451d8fc', + /* 104 */ '0xf7f73c5d826e196ff66a259204c', + /* 105 */ '0x1bb0d7eb2857065dcad087986fa6', + /* 106 */ '0x31b4dfa1eedd2bd17d3504820344', + /* 107 */ '0x599fae8ac47c48cf034887f489bb', + /* 108 */ '0xa249948898a0e444bffa21361f42', + /* 109 */ '0x12711786051c98ca2acc4adf7ba6a', + /* 110 */ '0x21a98821bf01e72cc3f724b65a121', + /* 111 */ '0x3dad0dd7c71f7b443dddd56fede23', + /* 112 */ '0x716933ca69ac1b439f976665fafdf', + /* 113 */ '0xd143a4beebca9707458aad7b22dcd', + /* 114 */ '0x18369cb4cd8522c1b28abc22a3e805', + /* 115 */ '0x2cf816f46d1971ec18f0ffb6922e86', + /* 116 */ '0x53c58e5a59ee4d9fd7f747f67a3aac', + /* 117 */ '0x9c833e3c0364561037250933eab9a9', + /* 118 */ '0x1253c9d983f03e6a0955355049411cb', + /* 119 */ '0x226e05852615979ea99f6ef68dbab51', + /* 120 */ '0x40d8c81134ee9e16db1e0108defbb9f', + /* 121 */ '0x7a70173a27075f4b9482d36deadc951', + /* 122 */ '0xe7b966d76665f99c3fb1791404f62c6', + /* 123 */ '0x1b78e22c38ae6aa69d36b8ccfade23fd', + /* 124 */ '0x3439aeef615a970c9678397b6ad71179', + /* 125 */ '0x637d37d6cb204d7419ac094d7e89f0dd', + /* 126 */ '0xbde80a98943810876a7852209de22be2', + /* 127 */ '0x16b3160a3c604c6667ff40ff1882b0fcf', +]; diff --git a/test/helpers/Utils.js b/test/helpers/Utils.js new file mode 100755 index 0000000..05a6585 --- /dev/null +++ b/test/helpers/Utils.js @@ -0,0 +1,24 @@ +const PREFIX = "VM Exception while processing transaction: "; + +async function tryCatch(promise, message) { + try { + await promise; + throw null; + } + catch (error) { + assert(error, "Expected an error but did not get one"); + assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); + } +}; + +module.exports = { + zeroAddress : "0x0000000000000000000000000000000000000000", + zeroBytes32 : "0x0000000000000000000000000000000000000000000000000000000000000000", + catchRevert : async function(promise) {await tryCatch(promise, "revert" );}, + catchOutOfGas : async function(promise) {await tryCatch(promise, "out of gas" );}, + catchInvalidJump : async function(promise) {await tryCatch(promise, "invalid JUMP" );}, + catchInvalidOpcode : async function(promise) {await tryCatch(promise, "invalid opcode" );}, + catchStackOverflow : async function(promise) {await tryCatch(promise, "stack overflow" );}, + catchStackUnderflow : async function(promise) {await tryCatch(promise, "stack underflow" );}, + catchStaticStateChange : async function(promise) {await tryCatch(promise, "static state change");}, +}; diff --git a/truffle-config.js b/truffle-config.js new file mode 100755 index 0000000..564295f --- /dev/null +++ b/truffle-config.js @@ -0,0 +1,53 @@ +require('dotenv').config(); + +const HDWalletProvider = require('@truffle/hdwallet-provider'); // @notice - Should new module. +const mnemonic = process.env.MNEMONIC; + + +module.exports = { + networks: { + ropsten: { + provider: function() { + return new HDWalletProvider(mnemonic, process.env.RPC_URL_ROPSTEN) + //return new HDWalletProvider("Replace here with your mnemonic word", process.env.RPC_URL_ROPSTEN) + }, + network_id: '3', + //gas: 4465030, + //gasPrice: 10000000000, + skipDryRun: true + }, + development: { + host: "localhost", + port: 7545, + network_id: "*", // Match any network id + gasPrice: 20000000000, // Gas price used for deploys + gas: 6721975 // Gas limit used for deploys + }, + production: { + host: "localhost", + port: 7545, + network_id: "*", // Match any network id + gasPrice: 20000000000, // Gas price used for deploys + gas: 6721975 // Gas limit used for deploys + }, + coverage: { // See + host: "localhost", + port: 7555, // Also in .solcover.js + network_id: "*", // Match any network id + gasPrice: 0x1, // Gas price used for deploys + gas: 0x1fffffffffffff // Gas limit used for deploys + } + }, + mocha: { + enableTimeouts: false, + useColors: true, + bail: true, + reporter: "list" // See + }, + solc: { + optimizer: { + enabled: true, + runs: 200 + } + } +} diff --git a/utils/README.md b/utils/README.md new file mode 100755 index 0000000..dc39d07 --- /dev/null +++ b/utils/README.md @@ -0,0 +1,129 @@ +## Utilities + +### [Prerequisites](../../README.md#prerequisites) + +### [Installation](../../README.md#installation) + +### Retrieve Contract Version + +```bash +node retrieve_contract_version.js + Ethereum node address + Designated contract address +``` + +### Verify Network Path Finder + +```bash +node verify_network_path_finder.js + Ethereum node address + BancorNetworkPathFinder contract address +``` + +### Migrate Converter Registry + +```bash +node migrate_converter_registry.js + Ethereum node address + Account private key + Old BancorConverterRegistry contract address + New BancorConverterRegistry contract address +``` + +### Deploy Network Emulation + +```bash +node deploy_network_emulation.js + Configuration file name + Ethereum node address + Account private key +``` + +This process can also be executed via `truffle deploy` or `truffle migrate` provided with the same input parameters: +```bash +truffle deploy + Configuration file name + Ethereum node address + Account private key +``` + +The configuration file is updated during the process, in order to allow resuming a prematurely-terminated execution. + +Here is an example of the initial configuration file which should be provided to the process: +```json +{ + "etherTokenParams": { + "name": "Bancor Ether Token", + "symbol": "ETH", + "supply": "12800000000000000" + }, + "smartToken0Params": { + "name": "Bancor Network Token", + "symbol": "BNT", + "decimals": 18, + "supply": "69100000000000000000" + }, + "smartToken1Params": { + "name": "ETH/BNT Relay Token", + "symbol": "ETHBNT", + "decimals": 18, + "supply": "13800000000000000000" + }, + "smartToken2Params": { + "name": "XXX/BNT Relay Token", + "symbol": "XXXBNT", + "decimals": 18, + "supply": "8380000000000000000" + }, + "smartToken3Params": { + "name": "YYY/BNT Relay Token", + "symbol": "YYYBNT", + "decimals": 18, + "supply": "93900000000000000" + }, + "smartToken4Params": { + "name": "Smart Token Of Chayot", + "symbol": "STC", + "decimals": 18, + "supply": "56500000000000000000" + }, + "erc20TokenAParams": { + "name": "XXX Standard Token", + "symbol": "XXX", + "decimals": 18, + "supply": "1000000000000000000000" + }, + "erc20TokenBParams": { + "name": "YYY Standard Token", + "symbol": "YYY", + "decimals": 18, + "supply": "36000000000000000000" + }, + "converter1Params": { + "fee": 1000, + "ratio1": 500000, + "reserve1": "7950000000000000000", + "ratio2": 500000, + "reserve2": "12700000000000000" + }, + "converter2Params": { + "fee": 1000, + "ratio1": 500000, + "reserve1": "340000000000000000", + "ratio2": 500000, + "reserve2": "1040000000000000000" + }, + "converter3Params": { + "fee": 2000, + "ratio1": 500000, + "reserve1": "369000000000000000", + "ratio2": 500000, + "reserve2": "84800000000000000" + }, + "converter4Params": { + "fee": 3000, + "ratio1": 100000, + "reserve1": "41100000000000000" + } +} +``` diff --git a/utils/deploy_network_emulation.js b/utils/deploy_network_emulation.js new file mode 100755 index 0000000..2a94f19 --- /dev/null +++ b/utils/deploy_network_emulation.js @@ -0,0 +1,198 @@ +const fs = require("fs"); +const Web3 = require("web3"); + +const CFG_FILE_NAME = process.argv[2]; +const NODE_ADDRESS = process.argv[3]; +const PRIVATE_KEY = process.argv[4]; + +const ARTIFACTS_DIR = __dirname + "/../build/"; + +const MIN_GAS_LIMIT = 100000; + +function get() { + return JSON.parse(fs.readFileSync(CFG_FILE_NAME, {encoding: "utf8"})); +} + +function set(record) { + fs.writeFileSync(CFG_FILE_NAME, JSON.stringify({...get(), ...record}, null, 4)); +} + +async function scan(message) { + process.stdout.write(message); + return await new Promise(function(resolve, reject) { + process.stdin.resume(); + process.stdin.once("data", function(data) { + process.stdin.pause(); + resolve(data.toString().trim()); + }); + }); +} + +async function getGasPrice(web3) { + while (true) { + const nodeGasPrice = await web3.eth.getGasPrice(); + const userGasPrice = await scan(`Enter gas-price or leave empty to use ${nodeGasPrice}: `); + if (/^\d+$/.test(userGasPrice)) + return userGasPrice; + if (userGasPrice == "") + return nodeGasPrice; + console.log("Illegal gas-price"); + } +} + +async function getTransactionReceipt(web3) { + while (true) { + const hash = await scan("Enter transaction-hash or leave empty to retry: "); + if (/^0x([0-9A-Fa-f]{64})$/.test(hash)) { + const receipt = await web3.eth.getTransactionReceipt(hash); + if (receipt) + return receipt; + console.log("Invalid transaction-hash"); + } + else if (hash) { + console.log("Illegal transaction-hash"); + } + else { + return null; + } + } +} + +async function send(web3, account, gasPrice, transaction, value = 0) { + while (true) { + try { + const options = { + to : transaction._parent._address, + data : transaction.encodeABI(), + gas : Math.max(await transaction.estimateGas({from: account.address}), MIN_GAS_LIMIT), + gasPrice: gasPrice ? gasPrice : await getGasPrice(web3), + value : value, + }; + const signed = await web3.eth.accounts.signTransaction(options, account.privateKey); + const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction); + return receipt; + } + catch (error) { + console.log(error.message); + const receipt = await getTransactionReceipt(web3); + if (receipt) + return receipt; + } + } +} + +async function deploy(web3, account, gasPrice, contractId, contractName, contractArgs) { + if (get()[contractId] == undefined) { + const abi = fs.readFileSync(ARTIFACTS_DIR + contractName + ".abi", {encoding: "utf8"}); + const bin = fs.readFileSync(ARTIFACTS_DIR + contractName + ".bin", {encoding: "utf8"}); + const contract = new web3.eth.Contract(JSON.parse(abi)); + const options = {data: "0x" + bin, arguments: contractArgs}; + const transaction = contract.deploy(options); + const receipt = await send(web3, account, gasPrice, transaction); + const args = transaction.encodeABI().slice(options.data.length); + console.log(`${contractId} deployed at ${receipt.contractAddress}`); + set({[contractId]: {name: contractName, addr: receipt.contractAddress, args: args}}); + } + return deployed(web3, contractName, get()[contractId].addr); +} + +function deployed(web3, contractName, contractAddr) { + const abi = fs.readFileSync(ARTIFACTS_DIR + contractName + ".abi", {encoding: "utf8"}); + return new web3.eth.Contract(JSON.parse(abi), contractAddr); +} + +async function run() { + const web3 = new Web3(NODE_ADDRESS); + + const gasPrice = await getGasPrice(web3); + const account = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY); + const web3Func = (func, ...args) => func(web3, account, gasPrice, ...args); + + const etherTokenParams = get().etherTokenParams ; + const smartToken0Params = get().smartToken0Params; + const smartToken1Params = get().smartToken1Params; + const smartToken2Params = get().smartToken2Params; + const smartToken3Params = get().smartToken3Params; + const smartToken4Params = get().smartToken4Params; + const erc20TokenAParams = get().erc20TokenAParams; + const erc20TokenBParams = get().erc20TokenBParams; + const converter1Params = get().converter1Params ; + const converter2Params = get().converter2Params ; + const converter3Params = get().converter3Params ; + const converter4Params = get().converter4Params ; + + const contractRegistry = await web3Func(deploy, "contractRegistry" , "ContractRegistry" , []); + const contractFeatures = await web3Func(deploy, "contractFeatures" , "ContractFeatures" , []); + const bancorFormula = await web3Func(deploy, "bancorFormula" , "BancorFormula" , []); + const bancorNetwork = await web3Func(deploy, "bancorNetwork" , "BancorNetwork" , [contractRegistry._address]); + const bancorNetworkPathFinder = await web3Func(deploy, "bancorNetworkPathFinder" , "BancorNetworkPathFinder" , [contractRegistry._address]); + const bancorConverterRegistry = await web3Func(deploy, "bancorConverterRegistry" , "BancorConverterRegistry" , [contractRegistry._address]); + const bancorConverterRegistryData = await web3Func(deploy, "bancorConverterRegistryData", "BancorConverterRegistryData", [contractRegistry._address]); + const etherToken = await web3Func(deploy, "etherToken" , "EtherToken" , [etherTokenParams .name, etherTokenParams .symbol]); + const smartToken0 = await web3Func(deploy, "smartToken0" , "SmartToken" , [smartToken0Params.name, smartToken0Params.symbol, smartToken0Params.decimals]); + const smartToken1 = await web3Func(deploy, "smartToken1" , "SmartToken" , [smartToken1Params.name, smartToken1Params.symbol, smartToken1Params.decimals]); + const smartToken2 = await web3Func(deploy, "smartToken2" , "SmartToken" , [smartToken2Params.name, smartToken2Params.symbol, smartToken2Params.decimals]); + const smartToken3 = await web3Func(deploy, "smartToken3" , "SmartToken" , [smartToken3Params.name, smartToken3Params.symbol, smartToken3Params.decimals]); + const smartToken4 = await web3Func(deploy, "smartToken4" , "SmartToken" , [smartToken4Params.name, smartToken4Params.symbol, smartToken4Params.decimals]); + const erc20TokenA = await web3Func(deploy, "erc20TokenA" , "ERC20Token" , [erc20TokenAParams.name, erc20TokenAParams.symbol, erc20TokenAParams.decimals, erc20TokenAParams.supply]); + const erc20TokenB = await web3Func(deploy, "erc20TokenB" , "ERC20Token" , [erc20TokenBParams.name, erc20TokenBParams.symbol, erc20TokenBParams.decimals, erc20TokenBParams.supply]); + const bancorConverter1 = await web3Func(deploy, "bancorConverter1" , "BancorConverter" , [smartToken1._address, contractRegistry._address, converter1Params.fee, smartToken0._address, converter1Params.ratio1]); + const bancorConverter2 = await web3Func(deploy, "bancorConverter2" , "BancorConverter" , [smartToken2._address, contractRegistry._address, converter2Params.fee, smartToken0._address, converter2Params.ratio1]); + const bancorConverter3 = await web3Func(deploy, "bancorConverter3" , "BancorConverter" , [smartToken3._address, contractRegistry._address, converter3Params.fee, smartToken0._address, converter3Params.ratio1]); + const bancorConverter4 = await web3Func(deploy, "bancorConverter4" , "BancorConverter" , [smartToken4._address, contractRegistry._address, converter4Params.fee, smartToken0._address, converter4Params.ratio1]); + + let phase = 0; + if (get().phase == undefined) + set({phase}); + const execute = async (transaction, ...args) => { + if (get().phase == phase++) { + await web3Func(send, transaction, ...args); + console.log(`phase ${phase} executed`); + set({phase}); + } + }; + + await execute(etherToken.methods.deposit(), etherTokenParams.supply); + await execute(smartToken0.methods.issue(account.address, smartToken0Params.supply)); + await execute(smartToken1.methods.issue(account.address, smartToken1Params.supply)); + await execute(smartToken2.methods.issue(account.address, smartToken2Params.supply)); + await execute(smartToken3.methods.issue(account.address, smartToken3Params.supply)); + await execute(smartToken4.methods.issue(account.address, smartToken4Params.supply)); + await execute(bancorConverter1.methods.addReserve(etherToken ._address, converter1Params.ratio2)); + await execute(bancorConverter2.methods.addReserve(erc20TokenA._address, converter2Params.ratio2)); + await execute(bancorConverter3.methods.addReserve(erc20TokenB._address, converter3Params.ratio2)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("ContractRegistry" ), contractRegistry ._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("ContractFeatures" ), contractFeatures ._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("BancorFormula" ), bancorFormula ._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("BancorNetwork" ), bancorNetwork ._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("BancorNetworkPathFinder" ), bancorNetworkPathFinder ._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("BancorConverterRegistry" ), bancorConverterRegistry ._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("BancorConverterRegistryData"), bancorConverterRegistryData._address)); + await execute(contractRegistry.methods.registerAddress(Web3.utils.asciiToHex("BNTToken" ), smartToken0 ._address)); + await execute(smartToken0.methods.transfer(bancorConverter1._address, converter1Params.reserve1)); + await execute(smartToken0.methods.transfer(bancorConverter2._address, converter2Params.reserve1)); + await execute(smartToken0.methods.transfer(bancorConverter3._address, converter3Params.reserve1)); + await execute(smartToken0.methods.transfer(bancorConverter4._address, converter4Params.reserve1)); + await execute(etherToken .methods.transfer(bancorConverter1._address, converter1Params.reserve2)); + await execute(erc20TokenA.methods.transfer(bancorConverter2._address, converter2Params.reserve2)); + await execute(erc20TokenB.methods.transfer(bancorConverter3._address, converter3Params.reserve2)); + await execute(smartToken1.methods.transferOwnership(bancorConverter1._address)); + await execute(smartToken2.methods.transferOwnership(bancorConverter2._address)); + await execute(smartToken3.methods.transferOwnership(bancorConverter3._address)); + await execute(smartToken4.methods.transferOwnership(bancorConverter4._address)); + await execute(bancorConverter1.methods.acceptTokenOwnership()); + await execute(bancorConverter2.methods.acceptTokenOwnership()); + await execute(bancorConverter3.methods.acceptTokenOwnership()); + await execute(bancorConverter4.methods.acceptTokenOwnership()); + await execute(bancorConverterRegistry.methods.addConverter(bancorConverter1._address)); + await execute(bancorConverterRegistry.methods.addConverter(bancorConverter2._address)); + await execute(bancorConverterRegistry.methods.addConverter(bancorConverter3._address)); + await execute(bancorConverterRegistry.methods.addConverter(bancorConverter4._address)); + await execute(bancorNetworkPathFinder.methods.setAnchorToken(smartToken0._address)); + await execute(bancorNetwork.methods.registerEtherToken(etherToken._address, true)); + + if (web3.currentProvider.constructor.name == "WebsocketProvider") + web3.currentProvider.connection.close(); +} + +run(); \ No newline at end of file diff --git a/utils/migrate_converter_registry_1.js b/utils/migrate_converter_registry_1.js new file mode 100755 index 0000000..0dcb5fe --- /dev/null +++ b/utils/migrate_converter_registry_1.js @@ -0,0 +1,147 @@ +const Web3 = require("web3"); + +const NODE_ADDRESS = process.argv[2]; +const PRIVATE_KEY = process.argv[3]; +const OLD_REG_ADDR = process.argv[4]; +const NEW_REG_ADDR = process.argv[5]; + +const ZERO_ADDRESS = "0x".padEnd(42, "0"); + +const OLD_REGISTRY_ABI = [ + {"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[],"name":"tokenCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"_token","type":"address"}],"name":"converterCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_index","type":"uint32"}],"name":"converterAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, +]; + +const NEW_REGISTRY_ABI = [ + {"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"_token","type":"address"},{"name":"_index","type":"uint32"}],"name":"converterAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_converter","type":"address"}],"name":"registerConverter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, +]; + +async function scan(message) { + process.stdout.write(message); + return await new Promise(function(resolve, reject) { + process.stdin.resume(); + process.stdin.once("data", function(data) { + process.stdin.pause(); + resolve(data.toString().trim()); + }); + }); +} + +async function getGasPrice(web3) { + while (true) { + const nodeGasPrice = await web3.eth.getGasPrice(); + const userGasPrice = await scan(`Enter gas-price or leave empty to use ${nodeGasPrice}: `); + if (/^\d+$/.test(userGasPrice)) + return userGasPrice; + if (userGasPrice == "") + return nodeGasPrice; + console.log("Illegal gas-price"); + } +} + +async function getTransactionReceipt(web3) { + while (true) { + const hash = await scan("Enter transaction-hash or leave empty to retry: "); + if (/^0x([0-9A-Fa-f]{64})$/.test(hash)) { + const receipt = await web3.eth.getTransactionReceipt(hash); + if (receipt) + return receipt; + console.log("Invalid transaction-hash"); + } + else if (hash) { + console.log("Illegal transaction-hash"); + } + else { + return null; + } + } +} + +async function send(web3, account, gasPrice, transaction) { + while (true) { + try { + const options = { + to : transaction._parent._address, + data : transaction.encodeABI(), + gas : await transaction.estimateGas({from: account.address}), + gasPrice: gasPrice ? gasPrice : await getGasPrice(web3), + }; + const signed = await web3.eth.accounts.signTransaction(options, account.privateKey); + const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction); + return receipt; + } + catch (error) { + console.log(error.message); + const receipt = await getTransactionReceipt(web3); + if (receipt) + return receipt; + } + } +} + +async function rpc(func) { + while (true) { + try { + return await func.call(); + } + catch (error) { + if (!error.message.startsWith("Invalid JSON RPC response")) + throw error; + } + } +} + +async function run() { + const web3 = new Web3(NODE_ADDRESS); + const gasPrice = await getGasPrice(web3); + const account = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY); + + const oldRegistry = new web3.eth.Contract(OLD_REGISTRY_ABI, OLD_REG_ADDR); + const newRegistry = new web3.eth.Contract(NEW_REGISTRY_ABI, NEW_REG_ADDR); + + const tokenCount = await rpc(oldRegistry.methods.tokenCount()); + for (let i = 0; i < tokenCount; i++) { + const token = await rpc(oldRegistry.methods.tokens(i)); + const converterCount = await rpc(oldRegistry.methods.converterCount(token)); + for (let j = 0; j < converterCount; j++) { + const converter = await rpc(oldRegistry.methods.converterAddress(token, j)); + switch (await rpc(newRegistry.methods.converterAddress(token, j))) { + case ZERO_ADDRESS: + const receipt = await send(web3, account, gasPrice, newRegistry.methods.registerConverter(token, converter)); + console.log(`token ${i} out of ${tokenCount}, converter ${j} out of ${converterCount}: gas = ${receipt.gasUsed}`); + break; + case converter: + console.log(`token ${i} out of ${tokenCount}, converter ${j} out of ${converterCount}: completed successfully`); + break; + default: + console.log(`token ${i} out of ${tokenCount}, converter ${j} out of ${converterCount}: an error has occurred`); + break; + } + } + } + + const owner = await rpc(oldRegistry.methods.owner()); + switch (await rpc(newRegistry.methods.newOwner())) { + case ZERO_ADDRESS: + const receipt = await send(web3, account, gasPrice, newRegistry.methods.transferOwnership(owner)); + console.log(`ownership-transfer from ${account.address} to ${owner}: gas = ${receipt.gasUsed}`); + break; + case owner: + console.log(`ownership-transfer from ${account.address} to ${owner}: completed successfully`); + break; + default: + console.log(`ownership-transfer from ${account.address} to ${owner}: an error has occurred`); + break; + } + + if (web3.currentProvider.constructor.name == "WebsocketProvider") + web3.currentProvider.connection.close(); +} + +run(); \ No newline at end of file diff --git a/utils/migrate_converter_registry_2.js b/utils/migrate_converter_registry_2.js new file mode 100755 index 0000000..2fa396b --- /dev/null +++ b/utils/migrate_converter_registry_2.js @@ -0,0 +1,146 @@ +const Web3 = require("web3"); + +const NODE_ADDRESS = process.argv[2]; +const PRIVATE_KEY = process.argv[3]; +const OLD_REG_ADDR = process.argv[4]; +const NEW_REG_ADDR = process.argv[5]; + +const CONVERTER_ABI = [ + {"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, +]; + +const SMART_TOKEN_ABI = [ + {"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, +]; + +const OLD_REGISTRY_ABI = [ + {"constant":true,"inputs":[],"name":"tokenCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"_token","type":"address"}],"name":"latestConverterAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, +]; + +const NEW_REGISTRY_ABI = [ + {"constant":false,"inputs":[{"name":"_converter","type":"address"}],"name":"addConverter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, + {"constant":true,"inputs":[{"name":"_value","type":"address"}],"name":"isSmartToken","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"_value","type":"address"}],"name":"isLiquidityPool","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, + {"constant":true,"inputs":[{"name":"_converter","type":"address"}],"name":"isConverterValid","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, +]; + +async function scan(message) { + process.stdout.write(message); + return await new Promise(function(resolve, reject) { + process.stdin.resume(); + process.stdin.once("data", function(data) { + process.stdin.pause(); + resolve(data.toString().trim()); + }); + }); +} + +async function getGasPrice(web3) { + while (true) { + const nodeGasPrice = await web3.eth.getGasPrice(); + const userGasPrice = await scan(`Enter gas-price or leave empty to use ${nodeGasPrice}: `); + if (/^\d+$/.test(userGasPrice)) + return userGasPrice; + if (userGasPrice == "") + return nodeGasPrice; + console.log("Illegal gas-price"); + } +} + +async function getTransactionReceipt(web3) { + while (true) { + const hash = await scan("Enter transaction-hash or leave empty to retry: "); + if (/^0x([0-9A-Fa-f]{64})$/.test(hash)) { + const receipt = await web3.eth.getTransactionReceipt(hash); + if (receipt) + return receipt; + console.log("Invalid transaction-hash"); + } + else if (hash) { + console.log("Illegal transaction-hash"); + } + else { + return null; + } + } +} + +async function send(web3, account, gasPrice, transaction) { + while (true) { + try { + const options = { + to : transaction._parent._address, + data : transaction.encodeABI(), + gas : await transaction.estimateGas({from: account.address}), + gasPrice: gasPrice ? gasPrice : await getGasPrice(web3), + }; + const signed = await web3.eth.accounts.signTransaction(options, account.privateKey); + const receipt = await web3.eth.sendSignedTransaction(signed.rawTransaction); + return receipt; + } + catch (error) { + console.log(error.message); + const receipt = await getTransactionReceipt(web3); + if (receipt) + return receipt; + } + } +} + +async function rpc(func) { + while (true) { + try { + return await func.call(); + } + catch (error) { + if (!error.message.startsWith("Invalid JSON RPC response")) + throw error; + } + } +} + +async function isConverterValid(registry, converter) { + try { + return await rpc(registry.methods.isConverterValid(converter)); + } + catch (error) { + return false; + } +} + +async function run() { + const web3 = new Web3(NODE_ADDRESS); + const gasPrice = await getGasPrice(web3); + const account = web3.eth.accounts.privateKeyToAccount(PRIVATE_KEY); + + const oldRegistry = new web3.eth.Contract(OLD_REGISTRY_ABI, OLD_REG_ADDR); + const newRegistry = new web3.eth.Contract(NEW_REGISTRY_ABI, NEW_REG_ADDR); + + const tokenCount = await rpc(oldRegistry.methods.tokenCount()); + for (let i = 0; i < tokenCount; i++) { + const tokenAddr = await rpc(oldRegistry.methods.tokens(i)); + const converterAddr = await rpc(oldRegistry.methods.latestConverterAddress(tokenAddr)); + const converter = new web3.eth.Contract(CONVERTER_ABI, converterAddr); + const smartTokenAddr = await rpc(converter.methods.token()); + const smartToken = new web3.eth.Contract(SMART_TOKEN_ABI, smartTokenAddr); + const ownerAddr = await rpc(smartToken.methods.owner()); + if (await rpc(newRegistry.methods.isSmartToken(smartTokenAddr))) { + console.log(`token ${i} out of ${tokenCount}: already added`); + } + else if (await isConverterValid(newRegistry, ownerAddr)) { + const transaction = newRegistry.methods.addConverter(ownerAddr); + const receipt = await send(web3, account, gasPrice, transaction); + console.log(`token ${i} out of ${tokenCount}: gas used = ${receipt.gasUsed}`); + } + else { + console.log(`token ${i} out of ${tokenCount}: owner is not a valid converter`); + } + } + + if (web3.currentProvider.constructor.name == "WebsocketProvider") + web3.currentProvider.connection.close(); +} + +run(); \ No newline at end of file diff --git a/utils/retrieve_contract_version.js b/utils/retrieve_contract_version.js new file mode 100755 index 0000000..df483b9 --- /dev/null +++ b/utils/retrieve_contract_version.js @@ -0,0 +1,51 @@ +const Web3 = require("web3"); + +const NODE_ADDRESS = process.argv[2]; +const CONTRACT_ADDR = process.argv[3]; + +async function rpc(func) { + while (true) { + try { + return await func.call(); + } + catch (error) { + if (!error.message.startsWith("Invalid JSON RPC response")) + return ""; + } + } +} + +function parse(type, data) { + if (type.startsWith("bytes")) { + const list = []; + for (let i = 2; i < data.length; i += 2) { + const num = Number("0x" + data.slice(i, i + 2)); + if (32 <= num && num <= 126) + list.push(num); + else + break; + } + return String.fromCharCode(...list); + } + return data; +} + +async function run() { + const web3 = new Web3(NODE_ADDRESS); + + for (const type of ["string", "bytes32", "uint16"]) { + const abi = [{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":type}],"payable":false,"stateMutability":"view","type":"function"}]; + const contract = new web3.eth.Contract(abi , CONTRACT_ADDR); + const version = await rpc(contract.methods.version()); + const string = parse(type, version); + if (string) { + console.log(type + " version: " + string); + break; + } + } + + if (web3.currentProvider.constructor.name == "WebsocketProvider") + web3.currentProvider.connection.close(); +} + +run(); \ No newline at end of file diff --git a/utils/verify_network_path_finder.js b/utils/verify_network_path_finder.js new file mode 100755 index 0000000..d235b7b --- /dev/null +++ b/utils/verify_network_path_finder.js @@ -0,0 +1,140 @@ +const fs = require("fs"); +const Web3 = require("web3"); +const assert = require("assert"); + +const NODE_ADDRESS = process.argv[2]; +const PATH_FINDER_ADDRESS = process.argv[3]; + +const PATH_FINDER_ABI = JSON.parse(fs.readFileSync(__dirname + "/../build/BancorNetworkPathFinder.abi")); +const CONTRACT_REGISTRY_ABI = JSON.parse(fs.readFileSync(__dirname + "/../build/ContractRegistry.abi" )); +const CONVERTER_REGISTRY_ABI = JSON.parse(fs.readFileSync(__dirname + "/../build/BancorConverterRegistry.abi")); +const SMART_TOKEN_ABI = JSON.parse(fs.readFileSync(__dirname + "/../build/SmartToken.abi" )); +const CONVERTER_ABI = JSON.parse(fs.readFileSync(__dirname + "/../build/BancorConverter.abi" )); + +async function generatePath(web3, sourceToken, targetToken, anchorToken, converterRegistry) { + const sourcePath = await getPath(web3, sourceToken, anchorToken, converterRegistry); + const targetPath = await getPath(web3, targetToken, anchorToken, converterRegistry); + return getShortestPath(sourcePath, targetPath); +} + +async function getPath(web3, token, anchorToken, converterRegistry) { + if (token == anchorToken) + return [token]; + + const isSmartToken = await rpc(converterRegistry.methods.isSmartToken(token)); + const smartTokens = isSmartToken ? [token] : await rpc(converterRegistry.methods.getConvertibleTokenSmartTokens(token)); + for (const smartToken of smartTokens) { + const smartTokenContract = new web3.eth.Contract(SMART_TOKEN_ABI, smartToken); + const converterContract = new web3.eth.Contract(CONVERTER_ABI, await rpc(smartTokenContract.methods.owner())); + const connectorTokenCount = await rpc(converterContract.methods.connectorTokenCount()); + for (let i = 0; i < connectorTokenCount; i++) { + const connectorToken = await rpc(converterContract.methods.connectorTokens(i)); + if (connectorToken != token) { + const path = await getPath(web3, connectorToken, anchorToken, converterRegistry); + if (path.length > 0) + return [token, smartToken, ...path]; + } + } + } + + return []; +} + +function getShortestPath(sourcePath, targetPath) { + if (sourcePath.length > 0 && targetPath.length > 0) { + let i = sourcePath.length - 1; + let j = targetPath.length - 1; + while (i >= 0 && j >= 0 && sourcePath[i] == targetPath[j]) { + i--; + j--; + } + + const path = []; + for (let m = 0; m <= i + 1; m++) + path.push(sourcePath[m]); + for (let n = j; n >= 0; n--) + path.push(targetPath[n]); + + let length = 0; + for (let p = 0; p < path.length; p += 1) { + for (let q = p + 2; q < path.length - p % 2; q += 2) { + if (path[p] == path[q]) + p = q; + } + path[length++] = path[p]; + } + + return path.slice(0, length); + } + + return []; +} + +async function rpc(func) { + while (true) { + try { + return await func.call(); + } + catch (error) { + if (!error.message.startsWith("Invalid JSON RPC response")) + throw error; + } + } +} + +async function symbol(web3, token) { + for (const type of ["string", "bytes32"]) { + try { + const contract = new web3.eth.Contract([{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":type}],"payable":false,"stateMutability":"view","type":"function"}], token); + const symbol = await rpc(contract.methods.symbol()); + if (type.startsWith("bytes")) { + const list = []; + for (let i = 2; i < symbol.length; i += 2) { + const num = Number("0x" + symbol.slice(i, i + 2)); + if (32 <= num && num <= 126) + list.push(num); + else + break; + } + return String.fromCharCode(...list); + } + return symbol; + } + catch (error) { + } + } + return token; +} + +function print(convertibleTokens, i, j, sourceSymbol, targetSymbol, path) { + const total = convertibleTokens.length ** 2; + const count = convertibleTokens.length * i + j; + console.log(`path ${count} out of ${total} (from ${sourceSymbol} to ${targetSymbol}): ${path}`); +} + +async function run() { + const web3 = new Web3(NODE_ADDRESS); + const pathFinder = new web3.eth.Contract(PATH_FINDER_ABI, PATH_FINDER_ADDRESS); + const contractRegistry = new web3.eth.Contract(CONTRACT_REGISTRY_ABI, await rpc(pathFinder.methods.registry())); + const converterRegistry = new web3.eth.Contract(CONVERTER_REGISTRY_ABI, await rpc(contractRegistry.methods.addressOf(Web3.utils.asciiToHex("BancorConverterRegistry")))); + + const anchorToken = await rpc(pathFinder.methods.anchorToken()); + const convertibleTokens = await rpc(converterRegistry.methods.getConvertibleTokens()); + + for (let i = 0; i < convertibleTokens.length; i++) { + const sourceSymbol = await symbol(web3, convertibleTokens[i]); + for (let j = 0; j < convertibleTokens.length; j++) { + const targetSymbol = await symbol(web3, convertibleTokens[j]); + const expected = await generatePath(web3, convertibleTokens[i], convertibleTokens[j], anchorToken, converterRegistry); + const actual = await rpc(pathFinder.methods.generatePath(convertibleTokens[i], convertibleTokens[j])); + const path = await Promise.all(actual.map(token => symbol(web3, token))); + print(convertibleTokens, i, j, sourceSymbol, targetSymbol, path); + assert.equal(`${actual}`, `${expected}`); + } + } + + if (web3.currentProvider.constructor.name == "WebsocketProvider") + web3.currentProvider.connection.close(); +} + +run(); \ No newline at end of file