From 31adce941f022386a23812be2b258996d558984e Mon Sep 17 00:00:00 2001 From: kongliangzhong Date: Mon, 27 Aug 2018 14:33:10 +0800 Subject: [PATCH] initial commit --- .gitignore | 6 + README.md | 2 + abi/AddressUtil.abi | 1 + abi/BytesUtil.abi | 1 + abi/Claimable.abi | 1 + abi/ERC20.abi | 1 + abi/ERC20Token.abi | 1 + abi/IBrokerInterceptor.abi | 1 + abi/IBrokerRegistry.abi | 1 + abi/IExchange.abi | 1 + abi/IFeeHolder.abi | 1 + abi/IMinerInterceptor.abi | 1 + abi/IMinerRegistry.abi | 1 + abi/IOrderBook.abi | 1 + abi/IOrderInterceptor.abi | 1 + abi/IOrderRegistry.abi | 1 + abi/IRingInterceptor.abi | 1 + abi/ITokenFactory.abi | 1 + abi/ITokenRegistry.abi | 1 + abi/ITradeDelegate.abi | 1 + abi/ITransferableMultsig.abi | 1 + abi/MathBytes32.abi | 1 + abi/MathUint.abi | 1 + abi/MathUint8.abi | 1 + abi/MemoryUtil.abi | 1 + abi/MultihashUtil.abi | 1 + abi/NoDefaultFunc.abi | 1 + abi/Ownable.abi | 1 + abi/StringUtil.abi | 1 + abi/TransferableMultsig.abi | 1 + globals.d.ts | 11 + package-lock.json | 2459 ++++++++++++++++++++++++++++++++++ package.json | 46 + src/artifacts.ts | 29 + src/bitstream.ts | 158 +++ src/context.ts | 75 ++ src/encode_spec.ts | 71 + src/ensure.ts | 7 + src/exchange_deserializer.ts | 179 +++ src/expectThrow.ts | 12 + src/mining.ts | 81 ++ src/mining_spec.ts | 21 + src/multihash.ts | 112 ++ src/order.ts | 228 ++++ src/order_spec.ts | 66 + src/participation_spec.ts | 13 + src/protocol_simulator.ts | 177 +++ src/ring.ts | 702 ++++++++++ src/rings_generator.ts | 309 +++++ src/tax.ts | 97 ++ src/types.ts | 130 ++ src/xor.ts | 8 + test/rings_config.ts | 699 ++++++++++ test/simulator_test.ts | 11 + tsconfig.json | 22 + 55 files changed, 5759 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 abi/AddressUtil.abi create mode 100644 abi/BytesUtil.abi create mode 100644 abi/Claimable.abi create mode 100644 abi/ERC20.abi create mode 100644 abi/ERC20Token.abi create mode 100644 abi/IBrokerInterceptor.abi create mode 100644 abi/IBrokerRegistry.abi create mode 100644 abi/IExchange.abi create mode 100644 abi/IFeeHolder.abi create mode 100644 abi/IMinerInterceptor.abi create mode 100644 abi/IMinerRegistry.abi create mode 100644 abi/IOrderBook.abi create mode 100644 abi/IOrderInterceptor.abi create mode 100644 abi/IOrderRegistry.abi create mode 100644 abi/IRingInterceptor.abi create mode 100644 abi/ITokenFactory.abi create mode 100644 abi/ITokenRegistry.abi create mode 100644 abi/ITradeDelegate.abi create mode 100644 abi/ITransferableMultsig.abi create mode 100644 abi/MathBytes32.abi create mode 100644 abi/MathUint.abi create mode 100644 abi/MathUint8.abi create mode 100644 abi/MemoryUtil.abi create mode 100644 abi/MultihashUtil.abi create mode 100644 abi/NoDefaultFunc.abi create mode 100644 abi/Ownable.abi create mode 100644 abi/StringUtil.abi create mode 100644 abi/TransferableMultsig.abi create mode 100644 globals.d.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/artifacts.ts create mode 100644 src/bitstream.ts create mode 100644 src/context.ts create mode 100644 src/encode_spec.ts create mode 100644 src/ensure.ts create mode 100644 src/exchange_deserializer.ts create mode 100644 src/expectThrow.ts create mode 100644 src/mining.ts create mode 100644 src/mining_spec.ts create mode 100644 src/multihash.ts create mode 100644 src/order.ts create mode 100644 src/order_spec.ts create mode 100644 src/participation_spec.ts create mode 100644 src/protocol_simulator.ts create mode 100644 src/ring.ts create mode 100644 src/rings_generator.ts create mode 100644 src/tax.ts create mode 100644 src/types.ts create mode 100644 src/xor.ts create mode 100644 test/rings_config.ts create mode 100644 test/simulator_test.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4e6fce --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +build/ +transpiled/ +.DS_Store +.node-* +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a8a43d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +## Loopring protocol simulator core + A javascript library for simulating loopring protocol version 2.0 diff --git a/abi/AddressUtil.abi b/abi/AddressUtil.abi new file mode 100644 index 0000000..b80ab41 --- /dev/null +++ b/abi/AddressUtil.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isContract","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/abi/BytesUtil.abi b/abi/BytesUtil.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/BytesUtil.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/Claimable.abi b/abi/Claimable.abi new file mode 100644 index 0000000..d9d3bed --- /dev/null +++ b/abi/Claimable.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[],"name":"claimOwnership","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":"pendingOwner","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"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] \ No newline at end of file diff --git a/abi/ERC20.abi b/abi/ERC20.abi new file mode 100644 index 0000000..9c5103f --- /dev/null +++ b/abi/ERC20.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/abi/ERC20Token.abi b/abi/ERC20Token.abi new file mode 100644 index 0000000..4cf7954 --- /dev/null +++ b/abi/ERC20Token.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_totalSupply","type":"uint256"},{"name":"_firstHolder","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}] \ No newline at end of file diff --git a/abi/IBrokerInterceptor.abi b/abi/IBrokerInterceptor.abi new file mode 100644 index 0000000..6ed0081 --- /dev/null +++ b/abi/IBrokerInterceptor.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"broker","type":"address"},{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"onTokenSpent","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"broker","type":"address"},{"name":"token","type":"address"}],"name":"getAllowance","outputs":[{"name":"allowance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/abi/IBrokerRegistry.abi b/abi/IBrokerRegistry.abi new file mode 100644 index 0000000..0cbabe0 --- /dev/null +++ b/abi/IBrokerRegistry.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"broker","type":"address"}],"name":"getBroker","outputs":[{"name":"registered","type":"bool"},{"name":"interceptor","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"broker","type":"address"},{"name":"interceptor","type":"address"}],"name":"registerBroker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"broker","type":"address"}],"name":"unregisterBroker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getBrokers","outputs":[{"name":"brokers","type":"address[]"},{"name":"interceptors","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unregisterAllBrokers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"broker","type":"address"},{"indexed":false,"name":"interceptor","type":"address"}],"name":"BrokerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"broker","type":"address"},{"indexed":false,"name":"interceptor","type":"address"}],"name":"BrokerUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"}],"name":"AllBrokersUnregistered","type":"event"}] \ No newline at end of file diff --git a/abi/IExchange.abi b/abi/IExchange.abi new file mode 100644 index 0000000..4972b7d --- /dev/null +++ b/abi/IExchange.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"OPTION_MASK_CAP_BY_AMOUNTB","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"orderHashes","type":"bytes"}],"name":"cancelOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"OPTION_MASK_ALL_OR_NONE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersForTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"data","type":"bytes"}],"name":"submitRings","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_broker","type":"address"},{"indexed":true,"name":"_feeRecipient","type":"address"},{"components":[{"name":"orderHash","type":"bytes32"},{"name":"owner","type":"address"},{"name":"tokenS","type":"address"},{"name":"amountS","type":"uint256"},{"name":"split","type":"int256"},{"name":"lrcFee","type":"int256"}],"indexed":false,"name":"_fills","type":"tuple[]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_broker","type":"address"},{"indexed":false,"name":"_orderHashes","type":"bytes32[]"}],"name":"OrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_broker","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelledForTradingPair","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_broker","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"}] \ No newline at end of file diff --git a/abi/IFeeHolder.abi b/abi/IFeeHolder.abi new file mode 100644 index 0000000..b3dad1d --- /dev/null +++ b/abi/IFeeHolder.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"feeBalances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"batch","type":"bytes32[]"}],"name":"batchAddFeeBalances","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"value","type":"uint256"}],"name":"withdrawToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] diff --git a/abi/IMinerInterceptor.abi b/abi/IMinerInterceptor.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/IMinerInterceptor.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/IMinerRegistry.abi b/abi/IMinerRegistry.abi new file mode 100644 index 0000000..eed1dd5 --- /dev/null +++ b/abi/IMinerRegistry.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"miner","type":"address"}],"name":"registerMiner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"miner","type":"address"}],"name":"unregisterMiner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"feeRecipient","type":"address"},{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getMiners","outputs":[{"name":"miners","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unregisterAllMiners","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"feeRecipient","type":"address"},{"name":"miner","type":"address"}],"name":"isMinerRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"minersMap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeRecipient","type":"address"},{"indexed":false,"name":"miner","type":"address"}],"name":"MinerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeRecipient","type":"address"},{"indexed":false,"name":"miner","type":"address"}],"name":"MinerUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"feeRecipient","type":"address"}],"name":"AllMinersUnregistered","type":"event"}] \ No newline at end of file diff --git a/abi/IOrderBook.abi b/abi/IOrderBook.abi new file mode 100644 index 0000000..0efbf8a --- /dev/null +++ b/abi/IOrderBook.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"owner","type":"address"}],"name":"submitOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/abi/IOrderInterceptor.abi b/abi/IOrderInterceptor.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/IOrderInterceptor.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/IOrderRegistry.abi b/abi/IOrderRegistry.abi new file mode 100644 index 0000000..42b4e9c --- /dev/null +++ b/abi/IOrderRegistry.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"hash","type":"bytes32"}],"name":"isOrderHashRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/abi/IRingInterceptor.abi b/abi/IRingInterceptor.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/IRingInterceptor.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/ITokenFactory.abi b/abi/ITokenFactory.abi new file mode 100644 index 0000000..2dfa03b --- /dev/null +++ b/abi/ITokenFactory.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"},{"name":"totalSupply","type":"uint256"}],"name":"createToken","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"totalSupply","type":"uint256"},{"indexed":false,"name":"firstHolder","type":"address"}],"name":"TokenCreated","type":"event"}] \ No newline at end of file diff --git a/abi/ITokenRegistry.abi b/abi/ITokenRegistry.abi new file mode 100644 index 0000000..bc445db --- /dev/null +++ b/abi/ITokenRegistry.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"agency","type":"address"}],"name":"registerAgency","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"agencies","outputs":[{"name":"","type":"address"}],"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":false,"inputs":[{"name":"addr","type":"address"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unregisterAllAgencies","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getSymbolByAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"agency","type":"address"}],"name":"unregisterAgency","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"agency","type":"address"}],"name":"AgencyRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"agency","type":"address"}],"name":"AgencyUnregistered","type":"event"},{"anonymous":false,"inputs":[],"name":"AllAgenciesUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"}] \ No newline at end of file diff --git a/abi/ITradeDelegate.abi b/abi/ITradeDelegate.abi new file mode 100644 index 0000000..6d5c9aa --- /dev/null +++ b/abi/ITradeDelegate.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"filled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"walletSplitPercentage","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"}],"name":"AddressDeauthorized","type":"event"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"batch","type":"bytes32[]"}],"name":"batchTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"filledInfo","type":"bytes32[]"}],"name":"batchUpdateFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"orderHash","type":"bytes32"}],"name":"setCancelled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"amount","type":"uint256"}],"name":"addFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"amount","type":"uint256"}],"name":"setFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"setCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"owner","type":"address"},{"name":"tokenPair","type":"bytes20"},{"name":"cutoff","type":"uint256"}],"name":"setTradingPairCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"orderInfo","type":"bytes32[]"}],"name":"batchCheckCutoffsAndCancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"suspend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"resume","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/abi/ITransferableMultsig.abi b/abi/ITransferableMultsig.abi new file mode 100644 index 0000000..71d46ca --- /dev/null +++ b/abi/ITransferableMultsig.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/abi/MathBytes32.abi b/abi/MathBytes32.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/MathBytes32.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/MathUint.abi b/abi/MathUint.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/MathUint.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/MathUint8.abi b/abi/MathUint8.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/MathUint8.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/MemoryUtil.abi b/abi/MemoryUtil.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/MemoryUtil.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/MultihashUtil.abi b/abi/MultihashUtil.abi new file mode 100644 index 0000000..4539a03 --- /dev/null +++ b/abi/MultihashUtil.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"SIG_PREFIX","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/abi/NoDefaultFunc.abi b/abi/NoDefaultFunc.abi new file mode 100644 index 0000000..c7d8bb3 --- /dev/null +++ b/abi/NoDefaultFunc.abi @@ -0,0 +1 @@ +[{"payable":true,"stateMutability":"payable","type":"fallback"}] \ No newline at end of file diff --git a/abi/Ownable.abi b/abi/Ownable.abi new file mode 100644 index 0000000..985a440 --- /dev/null +++ b/abi/Ownable.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"owner","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"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] \ No newline at end of file diff --git a/abi/StringUtil.abi b/abi/StringUtil.abi new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/abi/StringUtil.abi @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/abi/TransferableMultsig.abi b/abi/TransferableMultsig.abi new file mode 100644 index 0000000..b377694 --- /dev/null +++ b/abi/TransferableMultsig.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"owners","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"threshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"}] \ No newline at end of file diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000..43d03a1 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,11 @@ +declare module 'bn.js'; +declare module 'lodash'; +declare module 'es6-promisify'; +declare module 'ethereumjs-abi'; +declare module 'ethereumjs-util'; +declare module 'web3'; +declare module 'bitwise-xor'; +declare module 'ethereum-tx-decoder'; + +declare var assert: any; +declare var web3: any; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ccc4623 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2459 @@ +{ + "name": "protocol-simulator-core", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "requires": { + "@types/connect": "3.4.32", + "@types/node": "10.1.2" + } + }, + "@types/browserify": { + "version": "12.0.33", + "resolved": "https://registry.npmjs.org/@types/browserify/-/browserify-12.0.33.tgz", + "integrity": "sha512-mY6dYfq1Ns3Xqz/JFUcyoWaXtm0XDoNhkU1vCwM/ULM5zqNL+SbtacJhce/JCgPeCdbqdVqq77tJ4HwdtypSxg==", + "requires": { + "@types/insert-module-globals": "7.0.0", + "@types/node": "10.1.2" + } + }, + "@types/chai": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", + "integrity": "sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==", + "dev": true + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "10.1.2" + } + }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/express": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.11.1.tgz", + "integrity": "sha512-ttWle8cnPA5rAelauSWeWJimtY2RsUf2aspYZs7xPHiWgOlPn6nnUfBMtrkcnjFJuIHJF4gNOdVvpLK2Zmvh6g==", + "requires": { + "@types/body-parser": "1.17.0", + "@types/express-serve-static-core": "4.11.2", + "@types/serve-static": "1.13.2" + } + }, + "@types/express-serve-static-core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.11.2.tgz", + "integrity": "sha512-5ukJmirhZqJh/jEDFn40GANZYtO95C7Pu3Xd9s8hHCtGhZORDVXiFtKLHKDE/s8T72Uvy4BZSTqsgFQMWGg/RA==", + "requires": { + "@types/events": "1.2.0", + "@types/node": "10.1.2" + } + }, + "@types/insert-module-globals": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/insert-module-globals/-/insert-module-globals-7.0.0.tgz", + "integrity": "sha512-zudCJPwluh1VUDB6Gl/OQdRp+fYy3+47huJB/JMQubMS2p+sH18MCVK4WUz3FqaWLB12yh5ELxVR/+tqwlm/qA==", + "requires": { + "@types/node": "10.1.2" + } + }, + "@types/mime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", + "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" + }, + "@types/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-YeDiSEzznwZwwp766SJ6QlrTyBYUGPSIwmREHVTmktUYiT/WADdWtpt9iH0KuUSf8lZLdI4lP0X6PBzPo5//JQ==", + "dev": true + }, + "@types/node": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.1.2.tgz", + "integrity": "sha512-bjk1RIeZBCe/WukrFToIVegOf91Pebr8cXYBwLBIsfiGWVQ+ifwWsT59H3RxrWzWrzd1l/Amk1/ioY5Fq3/bpA==" + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "requires": { + "@types/express-serve-static-core": "4.11.2", + "@types/mime": "2.0.0" + } + }, + "@types/underscore": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.8.8.tgz", + "integrity": "sha512-EquzRwzAAs04anQ8/6MYXFKvHoD+MIlF+gu87EDda7dN9zrKvQYHsc9VFAPB1xY4tUHQVvBMtjsHrvof2EE1Mg==" + }, + "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=" + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "2.1.1" + } + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bignumber.js": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.1.0.tgz", + "integrity": "sha512-ioirFr31dV5NwDdw6bFYCi9a62dBhGHohVmWYh0VS84GGbQsb69kIZ1wyqXNqFaPJmvIt9EXpqenk/0hc8tiTQ==" + }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bitwise-xor": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/bitwise-xor/-/bitwise-xor-0.0.0.tgz", + "integrity": "sha1-BAqBcrW7jMVisLcRnyMLKhp4Dj0=" + }, + "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.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "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.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.16" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + } + } + }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.4.1", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "2.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==", + "dev": true, + "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.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "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.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.2" + } + }, + "browserify-sha3": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.1.tgz", + "integrity": "sha1-P/NKMAbvFcD7NWflQbkaI0ASPRE=", + "requires": { + "js-sha3": "0.3.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "4.1.0", + "map-obj": "2.0.0", + "quick-lru": "1.1.0" + } + }, + "capture-stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "ci-info": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", + "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", + "dev": true + }, + "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.3", + "safe-buffer": "5.1.2" + } + }, + "clang-format": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clang-format/-/clang-format-1.2.3.tgz", + "integrity": "sha512-x90Hac4ERacGDcZSvHKK58Ga0STuMD+Doi5g0iG2zf7wlJef5Huvhs/3BvMRFxwRYyYSdl6mpQNrtfMxE8MQzw==", + "dev": true, + "requires": { + "async": "1.5.2", + "glob": "7.1.2", + "resolve": "1.7.1" + } + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, + "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=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "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=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.3.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.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=" + }, + "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=", + "dev": true, + "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.4", + "inherits": "2.0.3", + "md5.js": "1.3.4", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" + } + }, + "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.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.2", + "sha.js": "2.4.11" + } + }, + "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=", + "dev": true, + "requires": { + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" + }, + "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=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } + }, + "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" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "1.2.0", + "map-obj": "1.0.1" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "deep-extend": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "drbg.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", + "requires": { + "browserify-aes": "1.2.0", + "create-hash": "1.2.0", + "create-hmac": "1.1.7" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "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.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es6-promisify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.0.tgz", + "integrity": "sha512-8Tbqjrb8lC85dd81haajYwuRmiU2rkqNAFnlvQOJeeKqdUloIlI+JcUqeJruV4rCm5Y7oNU7jfs2FbmxhRR/2g==" + }, + "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=", + "dev": true + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "ethereum-tx-decoder": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ethereum-tx-decoder/-/ethereum-tx-decoder-2.0.1.tgz", + "integrity": "sha1-dqhPeVGpGFnl22UxcFhFpUvQy2w=", + "requires": { + "ethers": "3.0.17" + } + }, + "ethereumjs-abi": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz", + "integrity": "sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE=", + "requires": { + "bn.js": "4.11.8", + "ethereumjs-util": "4.5.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz", + "integrity": "sha1-PpQosxfuvaPXJg2FT93alUsfG8Y=", + "requires": { + "bn.js": "4.11.8", + "create-hash": "1.2.0", + "keccakjs": "0.2.1", + "rlp": "2.0.0", + "secp256k1": "3.5.0" + } + } + } + }, + "ethereumjs-util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", + "requires": { + "bn.js": "4.11.8", + "create-hash": "1.2.0", + "ethjs-util": "0.1.4", + "keccak": "1.4.0", + "rlp": "2.0.0", + "safe-buffer": "5.1.2", + "secp256k1": "3.5.0" + } + }, + "ethers": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-3.0.17.tgz", + "integrity": "sha512-aIhNY2xlONbSRvBTA9TKYgPGocUGlPKNy/6weV0URweEtHWVHgVARTVdhw345UZ56T0j7FMnT7wQaUO7v/o1iQ==", + "requires": { + "aes-js": "3.0.0", + "bn.js": "4.11.8", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "inherits": "2.0.1", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "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.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "inherits": "2.0.1" + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } + } + }, + "ethjs-util": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.4.tgz", + "integrity": "sha1-HItoeSV0RO9NPz+7rC3tEs2ZfZM=", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "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.2" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + } + }, + "express": { + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "requires": { + "accepts": "1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "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.3", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "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": { + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + } + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.23", + "tmp": "0.0.33" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://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" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "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=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "1.3.5" + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "gts": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/gts/-/gts-0.6.0.tgz", + "integrity": "sha512-MCh3HNzLA1zvnW8lStH58n6U7SaGCJwd0Y2fUWJklHdtpMB9zaGe8SR7l0DqqPf+t0hNoFu2KmRFxHBnkeeKrA==", + "dev": true, + "requires": { + "chalk": "2.4.1", + "clang-format": "1.2.3", + "inquirer": "5.2.0", + "meow": "5.0.0", + "pify": "3.0.0", + "rimraf": "2.6.2", + "tslint": "5.10.0", + "update-notifier": "2.5.0", + "write-file-atomic": "2.3.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "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.3", + "safe-buffer": "5.1.2" + } + }, + "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.1" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "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.1.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "hosted-git-info": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", + "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://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" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "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==", + "dev": true + }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.2.0", + "figures": "2.0.0", + "lodash": "4.17.10", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rxjs": "5.5.10", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + } + }, + "ipaddr.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", + "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-ci": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", + "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", + "dev": true, + "requires": { + "ci-info": "1.1.3" + } + }, + "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=", + "dev": true + }, + "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=", + "dev": true, + "requires": { + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "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=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "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=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, + "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=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-sha3": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.3.1.tgz", + "integrity": "sha1-hhIoAhQvCChQKg0d7h2V4lO7AkM=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "esprima": "4.0.0" + } + }, + "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==", + "dev": true + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "1.3.0", + "inherits": "2.0.3", + "nan": "2.10.0", + "safe-buffer": "5.1.2" + } + }, + "keccakjs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.1.tgz", + "integrity": "sha1-HWM6+QfvMFu/ny+mFtVsRFYd+k0=", + "requires": { + "browserify-sha3": "0.0.1", + "sha3": "1.2.2" + } + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "4.0.1" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "4.0.0", + "pify": "3.0.0", + "strip-bom": "3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "3.0.0" + } + }, + "make-error": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", + "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", + "dev": true + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dev": true, + "requires": { + "camelcase-keys": "4.2.0", + "decamelize-keys": "1.1.0", + "loud-rejection": "1.6.0", + "minimist-options": "3.0.2", + "normalize-package-data": "2.4.0", + "read-pkg-up": "3.0.0", + "redent": "2.0.0", + "trim-newlines": "2.0.0", + "yargs-parser": "10.0.0" + } + }, + "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=" + }, + "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.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "1.33.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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "is-plain-obj": "1.1.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "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=", + "dev": true + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "2.6.0", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" + } + }, + "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=", + "dev": true, + "requires": { + "path-key": "2.0.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=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.2.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=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "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=", + "dev": true, + "requires": { + "p-limit": "1.2.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=", + "dev": true + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.5.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "1.3.1", + "json-parse-better-errors": "1.0.2" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "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=", + "dev": true + }, + "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=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "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=" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", + "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.6.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "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.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.4.0" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "rc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", + "dev": true, + "requires": { + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "4.0.0", + "normalize-package-data": "2.4.0", + "path-type": "3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "3.2.0", + "strip-indent": "2.0.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "requires": { + "rc": "1.2.7", + "safe-buffer": "5.1.2" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "1.2.7" + } + }, + "resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "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.4", + "inherits": "2.0.3" + } + }, + "rlp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.0.0.tgz", + "integrity": "sha1-nbOE/0uJqPYVY9kjldhiWxjzr7A=" + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rxjs": { + "version": "5.5.10", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz", + "integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "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==", + "dev": true + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "secp256k1": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.0.tgz", + "integrity": "sha512-e5QIJl8W7Y4tT6LHffVcZAxJjvpgE5Owawv6/XCYPQljE9aP2NFFddQ8OYMKhdLshNu88FfL3qCN3/xYkXGRsA==", + "requires": { + "bindings": "1.3.0", + "bip66": "1.1.5", + "bn.js": "4.11.8", + "create-hash": "1.2.0", + "drbg.js": "1.0.1", + "elliptic": "6.4.0", + "nan": "2.10.0", + "safe-buffer": "5.1.2" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "5.5.0" + } + }, + "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.3", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" + } + }, + "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" + } + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "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.3", + "safe-buffer": "5.1.2" + } + }, + "sha3": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.2.tgz", + "integrity": "sha1-pmxQmN5MJbyIM27ItIF9AFvKe6k=", + "requires": { + "nan": "2.10.0" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "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=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", + "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", + "dev": true, + "requires": { + "buffer-from": "1.0.0", + "source-map": "0.6.1" + } + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "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=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "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-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "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=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "0.7.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "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=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, + "ts-node": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.0.5.tgz", + "integrity": "sha512-iNhWu2hli9/1p9PGLJ/4OZS+NR0IVEVk63KCrH3qa7jJZxXa+oPheBj5JyM8tuQM9RkBpw1PrNUEUPJR9wTGDw==", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.4.1", + "diff": "3.5.0", + "make-error": "1.3.4", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.5.6", + "yn": "2.0.0" + } + }, + "tslib": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.1.tgz", + "integrity": "sha512-avfPS28HmGLLc2o4elcc2EIq2FcH++Yo5YxpBZi9Yw93BCTGFthI4HPE4Rpep6vSYQaK8e69PelM44tPj+RaQg==", + "dev": true + }, + "tslint": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.10.0.tgz", + "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.4.1", + "commander": "2.15.1", + "diff": "3.5.0", + "glob": "7.1.2", + "js-yaml": "3.11.0", + "minimatch": "3.0.4", + "resolve": "1.7.1", + "semver": "5.5.0", + "tslib": "1.9.1", + "tsutils": "2.27.1" + } + }, + "tsutils": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", + "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", + "dev": true, + "requires": { + "tslib": "1.9.1" + } + }, + "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" + } + }, + "typescript": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.3.tgz", + "integrity": "sha512-K7g15Bb6Ra4lKf7Iq2l/I5/En+hLIHmxWZGq3D4DIRNFxMNV6j2SHSvDOqs2tGd4UvD/fJvrwopzQXjLrT7Itw==", + "dev": true + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "1.0.0" + } + }, + "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=", + "dev": true + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "1.3.0", + "chalk": "2.4.1", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.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=", + "dev": true, + "requires": { + "prepend-http": "1.0.4" + } + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "dev": true, + "requires": { + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "web3": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.14.1.tgz", + "integrity": "sha1-JpBxfCBTVufe848RucTHEr/kSiI=", + "requires": { + "bignumber.js": "github:debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9", + "crypto-js": "3.1.8", + "utf8": "2.1.2", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "bignumber.js": { + "version": "github:debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9" + } + } + }, + "web3-typescript-typings": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/web3-typescript-typings/-/web3-typescript-typings-0.10.2.tgz", + "integrity": "sha1-qZA4FdKooNvXP9XbN0Bw3gvTBJc=", + "dev": true, + "requires": { + "bignumber.js": "4.1.0" + }, + "dependencies": { + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==", + "dev": true + } + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "widest-line": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", + "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", + "dev": true, + "requires": { + "string-width": "2.1.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", + "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs-parser": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.0.0.tgz", + "integrity": "sha512-+DHejWujTVYeMHLff8U96rLc4uE4Emncoftvn5AjhB1Jw1pWxLzgBUT/WYbPrHmy6YPEBTZQx5myHhVcuuu64g==", + "dev": true, + "requires": { + "camelcase": "4.1.0" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5d37bd0 --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "protocol-simulator-core", + "version": "0.1.0", + "description": "loopring protocol simulator core lib", + "main": "main.js", + "types": "globals.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/Loopring/protocol-simulator-core" + }, + "scripts": { + "test": "mocha -r ts-node/register test/**/*.ts", + "compile": "tsc -p .", + "fix": "gts fix", + "prepare": "npm run compile", + "pretest": "npm run compile" + }, + "keywords": [], + "author": "loopring", + "license": "ISC", + "devDependencies": { + "@types/chai": "^4.1.3", + "@types/mocha": "^5.2.0", + "@types/node": "^10.1.2", + "gts": "^0.6.0", + "mocha": "^5.2.0", + "ts-node": "^6.0.5", + "typescript": "^2.8.3", + "web3-typescript-typings": "^0.10.2" + }, + "dependencies": { + "@types/browserify": "^12.0.33", + "@types/express": "^4.11.1", + "@types/underscore": "^1.8.8", + "express": "^4.16.3", + "bignumber.js": "^7.1.0", + "bitwise-xor": "0.0.0", + "bn.js": "^4.11.8", + "es6-promisify": "^6.0.0", + "ethereum-tx-decoder": "^2.0.1", + "ethereumjs-abi": "^0.6.5", + "ethereumjs-util": "^5.2.0", + "lodash": "^4.17.10", + "web3": "^0.14.1" + } +} diff --git a/src/artifacts.ts b/src/artifacts.ts new file mode 100644 index 0000000..d225c91 --- /dev/null +++ b/src/artifacts.ts @@ -0,0 +1,29 @@ + +export class Artifacts { + public TokenRegistry: any; + public SymbolRegistry: any; + public Exchange: any; + public TradeDelegate: any; + public BrokerRegistry: any; + public OrderRegistry: any; + public MinerRegistry: any; + public DummyToken: any; + public DummyAgency: any; + public DummyBrokerInterceptor: any; + public FeeHolder: any; + public OrderBook: any; + constructor(artifacts: any) { + this.TokenRegistry = artifacts.require("impl/TokenRegistry"); + this.SymbolRegistry = artifacts.require("impl/SymbolRegistry"); + this.Exchange = artifacts.require("impl/Exchange"); + this.TradeDelegate = artifacts.require("impl/TradeDelegate"); + this.BrokerRegistry = artifacts.require("impl/BrokerRegistry"); + this.OrderRegistry = artifacts.require("impl/OrderRegistry"); + this.MinerRegistry = artifacts.require("impl/MinerRegistry"); + this.DummyToken = artifacts.require("test/DummyToken"); + this.DummyAgency = artifacts.require("test/DummyAgency"); + this.DummyBrokerInterceptor = artifacts.require("test/DummyBrokerInterceptor"); + this.FeeHolder = artifacts.require("impl/FeeHolder"); + this.OrderBook = artifacts.require("impl/OrderBook"); + } +} diff --git a/src/bitstream.ts b/src/bitstream.ts new file mode 100644 index 0000000..bd32161 --- /dev/null +++ b/src/bitstream.ts @@ -0,0 +1,158 @@ +import { BigNumber } from "bignumber.js"; +import abi = require("ethereumjs-abi"); + +export class Bitstream { + private data: string; + + constructor(private initialData: string = "") { + this.data = initialData; + if (this.data.startsWith("0x")) { + this.data = this.data.slice(2); + } + } + + public getData() { + if (this.data.length === 0) { + return "0x0"; + } else { + return "0x" + this.data; + } + } + + public getBytes32Array() { + if (this.data.length === 0) { + return []; + } else { + assert.equal(this.length() % 32, 0, "Bitstream not compatible with bytes32[]"); + return this.data.match(/.{1,64}/g).map((element) => "0x" + element); + } + } + + public addBigNumber(x: BigNumber, numBytes = 32) { + this.data += this.padString(web3.toHex(x).slice(2), numBytes * 2); + } + + public addNumber(x: number, numBytes = 4) { + // Check if we need to encode this number as negative + if (x < 0) { + const encoded = abi.rawEncode(["int256"], [x.toString(10)]); + const hex = encoded.toString("hex").slice(-(numBytes * 2)); + this.addHex(hex); + } else { + this.addBigNumber(new BigNumber(x), numBytes); + } + } + + public addAddress(x: string, numBytes = 20) { + this.data += this.padString(x.slice(2), numBytes * 2); + } + + public addHex(x: string) { + if (x.startsWith("0x")) { + this.data += x.slice(2); + } else { + this.data += x; + } + } + + public addRawBytes(bs: string) { + const bsHex = web3.toHex(bs); + // console.log("bsHex:", bsHex); + this.data += bsHex.slice(2); + } + + public extractUint8(offset: number) { + return parseInt(this.extractBytes1(offset).toString("hex"), 16); + } + + public extractUint16(offset: number) { + return parseInt(this.extractBytesX(offset, 2).toString("hex"), 16); + } + + public extractUint(offset: number) { + return new BigNumber(this.extractBytesX(offset, 32).toString("hex"), 16); + } + + public extractAddress(offset: number) { + return "0x" + this.extractBytesX(offset, 20).toString("hex"); + } + + public extractBytes1(offset: number) { + return this.extractBytesX(offset, 1); + } + + public extractBytes32(offset: number) { + return this.extractBytesX(offset, 32); + } + + public extractBytesX(offset: number, length: number) { + const start = offset * 2; + const end = start + length * 2; + return new Buffer(this.data.substring(start, end), "hex"); + } + + public copyToUint16Array(offset: number, arraySize: number) { + const resultArray: number[] = []; + for (let i = 0; i < arraySize; i++) { + resultArray.push(this.extractUint16(offset + i * 2)); + } + return resultArray; + } + + public copyToUint8ArrayList(offset: number, innerArraySizeList: number[]) { + const arraySize = innerArraySizeList.length; + const resultArray: number[][] = []; + for (let i = 0; i < arraySize; i++) { + const len = innerArraySizeList[i]; + resultArray[i] = []; + for (let j = 0; j < len; j++) { + resultArray[i].push(this.extractUint8(offset + j)); + } + offset += len; + } + return resultArray; + } + + public copyToAddressArray(offset: number, arraySize: number) { + const resultArray: string[] = []; + for (let i = 0; i < arraySize; i++) { + resultArray.push(this.extractAddress(offset + i * 20)); + } + return resultArray; + } + + public copyToUintArray(offset: number, arraySize: number) { + const resultArray: BigNumber[] = []; + for (let i = 0; i < arraySize; i++) { + resultArray.push(this.extractUint(offset + i * 32)); + } + return resultArray; + } + + public copyToBytesArray(offset: number, innerBytesSizeList: number[]) { + const arraySize = innerBytesSizeList.length; + const resultArray: string[] = []; + for (let i = 0; i < arraySize; i++) { + const len = innerBytesSizeList[i]; + resultArray[i] = "0x" + this.extractBytesX(offset, len).toString("hex"); + offset += len; + } + return resultArray; + } + + // Returns the number of bytes of data + public length() { + return this.data.length / 2; + } + + private padString(x: string, targetLength: number) { + if (x.length > targetLength) { + throw Error("0x" + x + " is too big to fit in the requested length (" + targetLength + ")"); + } + while (x.length < targetLength) { + x = "0" + x; + } + return x; + } + +} diff --git a/src/context.ts b/src/context.ts new file mode 100644 index 0000000..4bd54cf --- /dev/null +++ b/src/context.ts @@ -0,0 +1,75 @@ +import fs = require("fs"); +import { Tax } from "./tax"; + +export class Context { + + public blockNumber: number; + public blockTimestamp: number; + public lrcAddress: string; + public tax: Tax; + public feePercentageBase: number; + + public ERC20Contract: any; + public TokenRegistryContract: any; + public TradeDelegateContract: any; + public BrokerRegistryContract: any; + public OrderRegistryContract: any; + public MinerRegistryContract: any; + public BrokerInterceptorContract: any; + public FeeHolderContract: any; + + public tokenRegistry: any; + public tradeDelegate: any; + public orderBrokerRegistry: any; + public minerBrokerRegistry: any; + public orderRegistry: any; + public minerRegistry: any; + public feeHolder: any; + + constructor(blockNumber: number, + blockTimestamp: number, + tokenRegistryAddress: string, + tradeDelegateAddress: string, + orderBrokerRegistryAddress: string, + minerBrokerRegistryAddress: string, + orderRegistryAddress: string, + minerRegistryAddress: string, + feeHolderAddress: string, + lrcAddress: string, + tax: Tax, + feePercentageBase: number) { + this.blockNumber = blockNumber; + this.blockTimestamp = blockTimestamp; + this.lrcAddress = lrcAddress; + this.tax = tax; + this.feePercentageBase = feePercentageBase; + + const ABIPath = "ABI/latest/"; + const erc20Abi = fs.readFileSync(ABIPath + "ERC20.abi", "ascii"); + const tokenRegistryAbi = fs.readFileSync(ABIPath + "ITokenRegistry.abi", "ascii"); + const tradeDelegateAbi = fs.readFileSync(ABIPath + "ITradeDelegate.abi", "ascii"); + const brokerRegistryAbi = fs.readFileSync(ABIPath + "IBrokerRegistry.abi", "ascii"); + const orderRegistryAbi = fs.readFileSync(ABIPath + "IOrderRegistry.abi", "ascii"); + const minerRegistryAbi = fs.readFileSync(ABIPath + "IMinerRegistry.abi", "ascii"); + const brokerInterceptorAbi = fs.readFileSync(ABIPath + "IBrokerInterceptor.abi", "ascii"); + const feeHolderAbi = fs.readFileSync(ABIPath + "IFeeHolder.abi", "ascii"); + + this.ERC20Contract = web3.eth.contract(JSON.parse(erc20Abi)); + this.TokenRegistryContract = web3.eth.contract(JSON.parse(tokenRegistryAbi)); + this.TradeDelegateContract = web3.eth.contract(JSON.parse(tradeDelegateAbi)); + this.BrokerRegistryContract = web3.eth.contract(JSON.parse(brokerRegistryAbi)); + this.OrderRegistryContract = web3.eth.contract(JSON.parse(orderRegistryAbi)); + this.MinerRegistryContract = web3.eth.contract(JSON.parse(minerRegistryAbi)); + this.FeeHolderContract = web3.eth.contract(JSON.parse(feeHolderAbi)); + this.BrokerInterceptorContract = web3.eth.contract(JSON.parse(brokerInterceptorAbi)); + + this.tokenRegistry = this.TokenRegistryContract.at(tokenRegistryAddress); + this.tradeDelegate = this.TradeDelegateContract.at(tradeDelegateAddress); + this.orderBrokerRegistry = this.BrokerRegistryContract.at(orderBrokerRegistryAddress); + this.minerBrokerRegistry = this.BrokerRegistryContract.at(minerBrokerRegistryAddress); + this.orderRegistry = this.OrderRegistryContract.at(orderRegistryAddress); + this.minerRegistry = this.MinerRegistryContract.at(minerRegistryAddress); + this.feeHolder = this.FeeHolderContract.at(feeHolderAddress); + } + +} diff --git a/src/encode_spec.ts b/src/encode_spec.ts new file mode 100644 index 0000000..1f24cf0 --- /dev/null +++ b/src/encode_spec.ts @@ -0,0 +1,71 @@ + +export class EncodeSpec { + + private data: number[]; + + constructor(private specData: number[]) { + this.data = specData; + } + + public orderSpecSize() { + return this.data[0]; + } + + public ringSpecSize() { + return this.data[1]; + } + + public addressListSize() { + return this.data[2]; + } + + public uintListSize() { + return this.data[3]; + } + + public uint16ListSize() { + return this.data[4]; + } + + public bytesListSize() { + return this.data[5]; + } + + public spendableListSize() { + return this.data[6]; + } + + public ringSpecSizeArray() { + const arrayLen = this.data[1]; + const sizeArray: number[] = []; + for (let i = 0; i < arrayLen; i++) { + sizeArray.push(this.data[7 + i]); + } + return sizeArray; + } + + public ringSpecSizeI(i: number) { + const ringSize = this.ringSpecSize(); + assert(i < ringSize); + return this.data[7 + i]; + } + + public ringSpecsDataLen() { + const arrayLen = this.ringSpecSize(); + let dataLen = 0; + for (let i = 0; i < arrayLen; i++) { + dataLen += this.ringSpecSizeI(i); + } + return dataLen; + } + + public bytesListSizeArray() { + const ringSpecLength = this.ringSpecSize(); + const listSize = this.bytesListSize(); + const sizeArray: number[] = []; + for (let i = 0; i < listSize; i++) { + sizeArray.push(this.data[7 + ringSpecLength + i]); + } + return sizeArray; + } +} diff --git a/src/ensure.ts b/src/ensure.ts new file mode 100644 index 0000000..7dd6df7 --- /dev/null +++ b/src/ensure.ts @@ -0,0 +1,7 @@ + +export function ensure(valid: boolean, description: string) { + if (!valid) { + console.log(description); + } + return valid; +} diff --git a/src/exchange_deserializer.ts b/src/exchange_deserializer.ts new file mode 100644 index 0000000..69afb75 --- /dev/null +++ b/src/exchange_deserializer.ts @@ -0,0 +1,179 @@ +import { BigNumber } from "bignumber.js"; +import BN = require("bn.js"); +import abi = require("ethereumjs-abi"); +import { Bitstream } from "./bitstream"; +import { Context } from "./context"; +import { EncodeSpec } from "./encode_spec"; +import { Mining } from "./mining"; +import { MiningSpec } from "./mining_spec"; +import { OrderUtil } from "./order"; +import { OrderSpec } from "./order_spec"; +import { ParticipationSpec } from "./participation_spec"; +import { Ring } from "./ring"; +import { OrderInfo, RingMinedEvent, RingsInfo, SimulatorReport, Spendable, TransferItem } from "./types"; + +export class ExchangeDeserializer { + + private context: Context; + + private data: string; + + private addressList?: string[]; + private uintList?: BigNumber[]; + private uint16List?: number[]; + private bytesList?: string[]; + private spendableList?: Spendable[]; + + private addressListIdx: number = 0; + private uintListIdx: number = 0; + private uint16ListIdx: number = 0; + private bytesListIdx: number = 0; + + constructor(context: Context) { + this.context = context; + } + + public deserialize(data: string): [Mining, OrderInfo[], number[][]] { + + const bitstream = new Bitstream(data); + + const encodeSpecsLen = bitstream.extractUint16(0); + let offset = 2; + const encodeSpecs = new EncodeSpec(bitstream.copyToUint16Array(offset, encodeSpecsLen)); + offset += 2 * encodeSpecsLen; + + const miningSpec = new MiningSpec(bitstream.extractUint16(offset)); + offset += 2; + const orderSpecs = bitstream.copyToUint16Array(offset, encodeSpecs.orderSpecSize()); + offset += 2 * encodeSpecs.orderSpecSize(); + + const ringSpecs = bitstream.copyToUint8ArrayList(offset, encodeSpecs.ringSpecSizeArray()); + offset += 1 * encodeSpecs.ringSpecsDataLen(); + + this.addressList = bitstream.copyToAddressArray(offset, encodeSpecs.addressListSize()); + offset += 20 * encodeSpecs.addressListSize(); + + this.uintList = bitstream.copyToUintArray(offset, encodeSpecs.uintListSize()); + offset += 32 * encodeSpecs.uintListSize(); + + this.uint16List = bitstream.copyToUint16Array(offset, encodeSpecs.uint16ListSize()); + offset += 2 * encodeSpecs.uint16ListSize(); + + this.bytesList = bitstream.copyToBytesArray(offset, encodeSpecs.bytesListSizeArray()); + + this.spendableList = []; + for (let i = 0; i < encodeSpecs.spendableListSize(); i++) { + const spendable = { + initialized: false, + amount: 0, + reserved: 0, + }; + this.spendableList.push(spendable); + } + + const mining = new Mining( + this.context, + (miningSpec.hasFeeRecipient() ? this.nextAddress() : undefined), + (miningSpec.hasMiner() ? this.nextAddress() : undefined), + (miningSpec.hasSignature() ? this.nextBytes() : undefined), + ); + + const orders = this.assembleOrders(orderSpecs); + const rings = this.assembleRings(ringSpecs, orders); + + return [mining, orders, rings]; + } + + private assembleOrders(specs: number[]) { + const size = specs.length; + const orders: OrderInfo[] = []; + for (let i = 0; i < size; i++) { + orders.push(this.assembleOrder(specs[i])); + } + return orders; + } + + private assembleOrder(specData: number) { + const spec = new OrderSpec(specData); + const order: OrderInfo = { + owner: this.nextAddress(), + tokenS: this.nextAddress(), + tokenB: null, + amountS: this.nextUint().toNumber(), + amountB: this.nextUint().toNumber(), + validSince: this.nextUint().toNumber(), + tokenSpendableS: this.spendableList[this.nextUint16()], + tokenSpendableFee: this.spendableList[this.nextUint16()], + dualAuthAddr: spec.hasDualAuth() ? this.nextAddress() : undefined, + broker: spec.hasBroker() ? this.nextAddress() : undefined, + brokerSpendableS: spec.hasBroker() ? this.spendableList[this.nextUint16()] : undefined, + brokerSpendableFee: spec.hasBroker() ? this.spendableList[this.nextUint16()] : undefined, + orderInterceptor: spec.hasOrderInterceptor() ? this.nextAddress() : undefined, + walletAddr: spec.hasWallet() ? this.nextAddress() : undefined, + validUntil: spec.hasValidUntil() ? this.nextUint().toNumber() : undefined, + sig: spec.hasSignature() ? this.nextBytes() : undefined, + dualAuthSig: spec.hasDualAuthSig() ? this.nextBytes() : undefined, + allOrNone: spec.allOrNone(), + feeToken: spec.hasFeeToken() ? this.nextAddress() : this.context.lrcAddress, + feeAmount: spec.hasFeeAmount() ? this.nextUint().toNumber() : 0, + feePercentage: spec.hasFeePercentage() ? this.nextUint16() : 0, + waiveFeePercentage: spec.hasWaiveFeePercentage() ? this.toInt16(this.nextUint16()) : 0, + tokenSFeePercentage: spec.hasTokenSFeePercentage() ? this.nextUint16() : 0, + tokenBFeePercentage: spec.hasTokenBFeePercentage() ? this.nextUint16() : 0, + }; + return order; + } + + private assembleRings(specs: number[][], orders: OrderInfo[]) { + const size = specs.length; + const rings: number[][] = []; + for (let i = 0; i < size; i++) { + rings.push(this.assembleRing(specs[i], orders)); + } + return rings; + } + + private assembleRing(pspecs: number[], orders: OrderInfo[]) { + const size = pspecs.length; + + const ring: number[] = []; + for (let i = 0; i < size; i++) { + const pspec = new ParticipationSpec(pspecs[i]); + const order = orders[pspec.orderIndex()]; + ring.push(pspec.orderIndex()); + } + + // Set tokenB of orders using the tokenS from the next order + for (let i = 0; i < ring.length; i++) { + orders[ring[i]].tokenB = orders[ring[(i + 1) % ring.length]].tokenS; + } + + return ring; + } + + private nextAddress() { + return this.addressList[this.addressListIdx++]; + } + + private nextUint() { + return this.uintList[this.uintListIdx++]; + } + + private nextUint16() { + return this.uint16List[this.uint16ListIdx++]; + } + + private nextBytes() { + return this.bytesList[this.bytesListIdx++]; + } + + private toInt16(x: number) { + // Check if the number is negative + if (x >> 15 === 1) { + const decoded = abi.rawDecode(["int16"], new Buffer("F".repeat(60) + x.toString(16), "hex")); + return decoded.toString(); + } else { + return x; + } + } +} diff --git a/src/expectThrow.ts b/src/expectThrow.ts new file mode 100644 index 0000000..8d5a2d3 --- /dev/null +++ b/src/expectThrow.ts @@ -0,0 +1,12 @@ +export async function expectThrow(promise: Promise) { + try { + await promise; + } catch (error) { + const invalidOpcode = error.message.search("invalid opcode") >= 0; + const revert = error.message.search("revert") >= 0; + assert(invalidOpcode || revert, + "Expected throw, got '" + error + "' instead"); + return; + } + assert.fail("Expected throw not received"); +} diff --git a/src/mining.ts b/src/mining.ts new file mode 100644 index 0000000..a90a274 --- /dev/null +++ b/src/mining.ts @@ -0,0 +1,81 @@ +import BN = require("bn.js"); +import ABI = require("ethereumjs-abi"); +import { Context } from "./context"; +import { MultiHashUtil } from "./multihash"; +import { OrderUtil } from "./order"; +import { Ring } from "./ring"; +import { OrderInfo, TransferItem } from "./types"; + +export class Mining { + + public feeRecipient: string; + + public miner?: string; + public sig?: string; + + public hash?: Buffer; + public interceptor?: string; + + private context: Context; + + private multiHashUtil = new MultiHashUtil(); + + constructor(context: Context, + feeRecipient: string, + miner: string, + sig: string) { + this.context = context; + this.feeRecipient = feeRecipient; + this.miner = miner; + this.sig = sig; + } + + public async updateMinerAndInterceptor() { + if (!this.miner) { + this.miner = this.feeRecipient; + } else { + const [registered, interceptor] = await this.context.minerBrokerRegistry.getBroker( + this.feeRecipient, + this.miner, + ); + // assert(registered, "miner unregistered"); + if (registered) { + this.interceptor = interceptor; + } + } + } + + public updateHash(rings: Ring[]) { + let ringHashes = rings[0].hash; + for (let i = 1; i < rings.length; i++) { + ringHashes = this.xor(ringHashes, rings[i].hash); + } + const args = [ + this.feeRecipient, + this.miner ? this.miner : "0x0", + "0x" + ringHashes.toString("hex"), + ]; + const argTypes = [ + "address", + "address", + "bytes32", + ]; + this.hash = ABI.soliditySHA3(argTypes, args); + } + + public checkMinerSignature(transactionOrigin: string) { + if (!this.sig) { + return (transactionOrigin === this.miner); + } else { + return this.multiHashUtil.verifySignature(this.miner, this.hash, this.sig); + } + } + + private xor(s1: Buffer, s2: Buffer) { + assert(s1.length === s2.length, "can't xor buffers of unequal length"); + const x1 = new BN(s1.toString("hex"), 16); + const x2 = new BN(s2.toString("hex"), 16); + const result = x1.xor(x2); + return new Buffer(result.toString(16, s1.length), "hex"); + } +} diff --git a/src/mining_spec.ts b/src/mining_spec.ts new file mode 100644 index 0000000..2826cfa --- /dev/null +++ b/src/mining_spec.ts @@ -0,0 +1,21 @@ + +export class MiningSpec { + + private data: number; + + constructor(private specData: number) { + this.data = specData; + } + + public hasFeeRecipient() { + return (this.data & (1 << 0)) !== 0; + } + + public hasMiner() { + return (this.data & (1 << 1)) !== 0; + } + + public hasSignature() { + return (this.data & (1 << 2)) !== 0; + } +} diff --git a/src/multihash.ts b/src/multihash.ts new file mode 100644 index 0000000..161778d --- /dev/null +++ b/src/multihash.ts @@ -0,0 +1,112 @@ +import { BigNumber } from "bignumber.js"; +import BN = require("bn.js"); +import promisify = require("es6-promisify"); +import ABI = require("ethereumjs-abi"); +import ethUtil = require("ethereumjs-util"); +import Web3 = require("web3"); +import { Bitstream } from "./bitstream"; +import { OrderInfo, RingsInfo, SignAlgorithm } from "./types"; + +export class MultiHashUtil { + + public web3Instance: Web3; + + constructor() { + try { + if (web3) { + this.web3Instance = web3; + } + } catch (err) { + console.log("get web3 instance in Order class failed. err:", err); + } + } + + public async signOrderAsync(order: OrderInfo) { + const signer = order.broker ? order.broker : order.owner; + return await this.signAsync(order.signAlgorithm, order.hash, signer); + } + + public async signAsync(algorithm: SignAlgorithm, hash: Buffer, address: string) { + // Default to standard Ethereum signing + algorithm = Object.is(algorithm, undefined) ? SignAlgorithm.Ethereum : algorithm; + + const sig = new Bitstream(); + sig.addNumber(algorithm, 1); + switch (+algorithm) { + case SignAlgorithm.Ethereum: + await this.signEthereumAsync(sig, hash, address); + return sig.getData(); + case SignAlgorithm.EIP712: + await this.signEIP712Async(sig, hash, address); + return sig.getData(); + case SignAlgorithm.None: + return null; + default: + throw Error("Unsupported hashing algorithm: " + algorithm); + } + } + + public verifySignature(signer: string, hash: Buffer, multihash: string) { + const bitstream = new Bitstream(multihash); + assert(bitstream.length() >= 2, "invalid multihash format"); + const algorithm = bitstream.extractUint8(0); + const size = bitstream.extractUint8(1); + assert.equal(bitstream.length(), (2 + size), "bad multihash size"); + + if (algorithm === SignAlgorithm.Ethereum) { + assert.notEqual(signer, "0x0", "invalid signer address"); + assert.equal(size, 65, "bad Ethereum multihash size"); + + const v = bitstream.extractUint8(2); + const r = bitstream.extractBytes32(3); + const s = bitstream.extractBytes32(3 + 32); + + try { + const msgHash = ethUtil.hashPersonalMessage(hash); + const pub = ethUtil.ecrecover(msgHash, v, r, s); + const recoveredAddress = "0x" + ethUtil.pubToAddress(pub).toString("hex"); + return signer === recoveredAddress; + } catch { + return false; + } + } else { + return false; + } + } + + private async signEthereumAsync(sig: Bitstream, hash: Buffer, address: string) { + const signature = await promisify(this.web3Instance.eth.sign)(address, ethUtil.bufferToHex(hash)); + const { v, r, s } = ethUtil.fromRpcSig(signature); + + sig.addNumber(1 + 32 + 32, 1); + sig.addNumber(v, 1); + sig.addHex(ethUtil.bufferToHex(r)); + sig.addHex(ethUtil.bufferToHex(s)); + } + + // TODO: Actually implement this correctly, the standard is not widely supported yet + private async signEIP712Async(sig: Bitstream, hash: Buffer, address: string) { + throw Error("EIP712 signing currently not implemented."); + + /*const orderHash = this.getOrderHash(order); + + const msgParams = [ + {type: "string", name: "Owner", value: order.owner}, + ]; + + const signature = await web3.eth.signTypedData(msgParams, order.owner); + const { v, r, s } = ethUtil.fromRpcSig(signature); + + // await web3.currentProvider.sendAsync({ + // method: "eth_signTypedData", + // params: [msgParams, order.owner], + // from: order.owner, + // }, (err?: Error, result?: Web3.JSONRPCResponsePayload) => { console.log("Hashing: " + result.result); }); + + sig.addNumber(1 + 32 + 32, 1); + sig.addNumber(v, 1); + sig.addHex(ethUtil.bufferToHex(r)); + sig.addHex(ethUtil.bufferToHex(s));*/ + } + +} diff --git a/src/order.ts b/src/order.ts new file mode 100644 index 0000000..8822e54 --- /dev/null +++ b/src/order.ts @@ -0,0 +1,228 @@ +import { BigNumber } from "bignumber.js"; +import BN = require("bn.js"); +import ABI = require("ethereumjs-abi"); +import { Context } from "./context"; +import { ensure } from "./ensure"; +import { MultiHashUtil } from "./multihash"; +import { OrderInfo, Spendable } from "./types"; + +export class OrderUtil { + + private context: Context; + private multiHashUtil = new MultiHashUtil(); + + constructor(context: Context) { + this.context = context; + } + + public async updateBrokerAndInterceptor(order: OrderInfo) { + if (!order.broker) { + order.broker = order.owner; + } else { + const [registered, brokerInterceptor] = await this.context.orderBrokerRegistry.getBroker( + order.owner, + order.broker, + ); + order.valid = order.valid && ensure(registered, "order broker is not registered"); + order.brokerInterceptor = + (brokerInterceptor === "0x0000000000000000000000000000000000000000") ? undefined : brokerInterceptor; + } + } + + public async validateInfo(order: OrderInfo) { + let valid = true; + valid = valid && ensure(order.owner ? true : false, "invalid order owner"); + valid = valid && ensure(order.tokenS ? true : false, "invalid order tokenS"); + valid = valid && ensure(order.tokenB ? true : false, "invalid order tokenB"); + valid = valid && ensure(order.amountS !== 0, "invalid order amountS"); + valid = valid && ensure(order.amountB !== 0, "invalid order amountB"); + valid = valid && ensure(order.feePercentage < this.context.feePercentageBase, "invalid fee percentage"); + valid = valid && ensure(order.waiveFeePercentage <= this.context.feePercentageBase, "invalid waive percentage"); + valid = valid && ensure(order.waiveFeePercentage >= -this.context.feePercentageBase, "invalid waive percentage"); + valid = valid && ensure(order.tokenSFeePercentage < this.context.feePercentageBase, "invalid tokenS percentage"); + valid = valid && ensure(order.tokenBFeePercentage < this.context.feePercentageBase, "invalid tokenB percentage"); + + const blockTimestamp = this.context.blockTimestamp; + valid = valid && ensure(order.validSince <= blockTimestamp, "order is too early to match"); + valid = valid && ensure(order.validUntil ? order.validUntil > blockTimestamp : true, "order is expired"); + + order.valid = order.valid && valid; + } + + public async checkBrokerSignature(order: OrderInfo) { + let signatureValid = true; + if (!order.sig) { + signatureValid = await this.context.orderRegistry.isOrderHashRegistered(order.broker, order.hash); + } else { + signatureValid = this.multiHashUtil.verifySignature(order.broker, order.hash, order.sig); + } + order.valid = order.valid && ensure(signatureValid, "invalid order signature"); + } + + public checkDualAuthSignature(order: OrderInfo, miningHash: Buffer) { + if (order.dualAuthSig) { + const signatureValid = this.multiHashUtil.verifySignature(order.dualAuthAddr, miningHash, order.dualAuthSig); + order.valid = order.valid && ensure(signatureValid, "invalid order dual auth signature"); + } + } + + public getOrderHash(order: OrderInfo) { + const MAX_UINT = new BN("f".repeat(64), 16); + const argsPart1 = [ + order.owner, + order.tokenS, + order.tokenB, + this.toBN(order.amountS), + this.toBN(order.amountB), + order.dualAuthAddr ? order.dualAuthAddr : "0x0", + order.broker ? order.broker : "0x0", + order.orderInterceptor ? order.orderInterceptor : "0x0", + order.walletAddr ? order.walletAddr : "0x0", + order.validSince ? this.toBN(order.validSince) : this.toBN(0), + order.validUntil ? this.toBN(order.validUntil) : MAX_UINT, + order.allOrNone, + ]; + const argTypesPart1 = [ + "address", + "address", + "address", + "uint256", + "uint256", + "address", + "address", + "address", + "address", + "uint256", + "uint256", + "bool", + ]; + const orderHashPart1 = ABI.soliditySHA3(argTypesPart1, argsPart1); + + const argsPart2 = [ + order.feeToken ? order.feeToken : this.context.lrcAddress, + order.feeAmount ? this.toBN(order.feeAmount) : this.toBN(0), + order.feePercentage ? this.toBN(order.feePercentage) : this.toBN(0), + order.tokenSFeePercentage ? this.toBN(order.tokenSFeePercentage) : this.toBN(0), + order.tokenBFeePercentage ? this.toBN(order.tokenBFeePercentage) : this.toBN(0), + ]; + const argTypesPart2 = [ + "address", + "uint256", + "uint16", + "uint16", + "uint16", + ]; + const orderHashPart2 = ABI.soliditySHA3(argTypesPart2, argsPart2); + + const args = [ + orderHashPart1, + orderHashPart2, + ]; + const argTypes = [ + "bytes32", + "bytes32", + ]; + const orderHash = ABI.soliditySHA3(argTypes, args); + return orderHash; + } + + public async updateStates(orderInfo: OrderInfo) { + orderInfo.filledAmountS = await this.context.tradeDelegate.filled("0x" + orderInfo.hash.toString("hex")).toNumber(); + } + + public async getSpendableS(order: OrderInfo) { + const spendable = await this.getSpendable(order.tokenS, + order.owner, + order.broker, + order.brokerInterceptor, + order.tokenSpendableS, + order.brokerSpendableS); + return spendable; + } + + public async getSpendableFee(order: OrderInfo) { + const spendable = await this.getSpendable(order.feeToken, + order.owner, + order.broker, + order.brokerInterceptor, + order.tokenSpendableFee, + order.brokerSpendableFee); + return spendable; + } + + public async reserveAmountS(order: OrderInfo, + amount: number) { + assert((await this.getSpendableS(order)) >= amount, "spendableS >= reserve amount"); + order.tokenSpendableS.reserved += amount; + } + + public async reserveAmountFee(order: OrderInfo, + amount: number) { + assert((await this.getSpendableFee(order)) >= amount, "spendableFee >= reserve amount"); + order.tokenSpendableFee.reserved += amount; + } + + public resetReservations(order: OrderInfo) { + order.tokenSpendableS.reserved = 0; + order.tokenSpendableFee.reserved = 0; + } + + public async getERC20Spendable(spender: string, + tokenAddress: string, + owner: string) { + const token = this.context.ERC20Contract.at(tokenAddress); + const balance = await token.balanceOf(owner); + const allowance = await token.allowance(owner, spender); + const spendable = Math.min(balance, allowance); + return spendable; + } + + public async getBrokerAllowance(tokenAddr: string, + owner: string, + broker: string, + brokerInterceptor: string) { + let allowance = 1e64; + if (brokerInterceptor) { + try { + allowance = await this.context.BrokerInterceptorContract.at(brokerInterceptor).getAllowance( + owner, + broker, + tokenAddr, + ); + } catch { + allowance = 0; + } + } + return allowance; + } + + private async getSpendable(token: string, + owner: string, + broker: string, + brokerInterceptor: string, + tokenSpendable: Spendable, + brokerSpendable: Spendable) { + if (!tokenSpendable.initialized) { + tokenSpendable.amount = await this.getERC20Spendable(this.context.tradeDelegate.address, + token, + owner); + tokenSpendable.initialized = true; + } + let spendable = tokenSpendable.amount; + if (brokerInterceptor) { + if (!brokerSpendable.initialized) { + brokerSpendable.amount = await this.getBrokerAllowance(token, + owner, + broker, + brokerInterceptor); + brokerSpendable.initialized = true; + } + spendable = (brokerSpendable.amount < spendable) ? brokerSpendable.amount : spendable; + } + return spendable - tokenSpendable.reserved; + } + + private toBN(n: number) { + return new BN((new BigNumber(n)).toString(10), 10); + } +} diff --git a/src/order_spec.ts b/src/order_spec.ts new file mode 100644 index 0000000..9425e95 --- /dev/null +++ b/src/order_spec.ts @@ -0,0 +1,66 @@ + +export class OrderSpec { + + private data: number; + + constructor(private specData: number) { + this.data = specData; + } + + public hasDualAuth() { + return (this.data & (1 << 0)) !== 0; + } + + public hasBroker() { + return (this.data & (1 << 1)) !== 0; + } + + public hasOrderInterceptor() { + return (this.data & (1 << 2)) !== 0; + } + + public hasWallet() { + return (this.data & (1 << 3)) !== 0; + } + + public hasValidUntil() { + return (this.data & (1 << 4)) !== 0; + } + + public allOrNone() { + return (this.data & (1 << 5)) !== 0; + } + + public hasSignature() { + return (this.data & (1 << 6)) !== 0; + } + + public hasDualAuthSig() { + return (this.data & (1 << 7)) !== 0; + } + + public hasFeeToken() { + return (this.data & (1 << 8)) !== 0; + } + + public hasFeeAmount() { + return (this.data & (1 << 9)) !== 0; + } + + public hasFeePercentage() { + return (this.data & (1 << 10)) !== 0; + } + + public hasWaiveFeePercentage() { + return (this.data & (1 << 11)) !== 0; + } + + public hasTokenSFeePercentage() { + return (this.data & (1 << 12)) !== 0; + } + + public hasTokenBFeePercentage() { + return (this.data & (1 << 13)) !== 0; + } + +} diff --git a/src/participation_spec.ts b/src/participation_spec.ts new file mode 100644 index 0000000..ea5f983 --- /dev/null +++ b/src/participation_spec.ts @@ -0,0 +1,13 @@ + +export class ParticipationSpec { + + private data: number; + + constructor(private specData: number) { + this.data = specData; + } + + public orderIndex() { + return this.data; + } +} diff --git a/src/protocol_simulator.ts b/src/protocol_simulator.ts new file mode 100644 index 0000000..ceee40e --- /dev/null +++ b/src/protocol_simulator.ts @@ -0,0 +1,177 @@ +import { BigNumber } from "bignumber.js"; +import BN = require("bn.js"); +import { Bitstream } from "./bitstream"; +import { Context } from "./context"; +import { ensure } from "./ensure"; +import { ExchangeDeserializer } from "./exchange_deserializer"; +import { Mining } from "./mining"; +import { OrderUtil } from "./order"; +import { Ring } from "./ring"; +import { OrderInfo, RingMinedEvent, RingsInfo, SimulatorReport, TransferItem } from "./types"; +import { xor } from "./xor"; + +export class ProtocolSimulator { + + public walletSplitPercentage: number; + public context: Context; + + private ringIndex: number = 0; + private orderUtil: OrderUtil; + + constructor(walletSplitPercentage: number, context: Context) { + this.walletSplitPercentage = walletSplitPercentage; + this.context = context; + this.orderUtil = new OrderUtil(context); + } + + public deserialize(data: string, + transactionOrigin: string) { + const exchangeDeserializer = new ExchangeDeserializer(this.context); + const [mining, orders, rings] = exchangeDeserializer.deserialize(data); + + const ringsInfo: RingsInfo = { + rings, + orders, + feeRecipient: mining.feeRecipient, + miner: mining.miner, + sig: mining.sig, + transactionOrigin, + }; + return ringsInfo; + } + + public async simulateAndReport(ringsInfo: RingsInfo) { + const mining = new Mining( + this.context, + ringsInfo.feeRecipient ? ringsInfo.feeRecipient : ringsInfo.transactionOrigin, + ringsInfo.miner, + ringsInfo.sig, + ); + + const orders = ringsInfo.orders; + + const rings: Ring[] = []; + for (const indexes of ringsInfo.rings) { + const ringOrders: OrderInfo[] = []; + for (const orderIndex of indexes) { + const orderInfo = ringsInfo.orders[orderIndex]; + ringOrders.push(orderInfo); + } + const ring = new Ring( + this.context, + ringOrders, + ringsInfo.miner, + ringsInfo.feeRecipient, + ); + rings.push(ring); + } + + for (const order of orders) { + order.valid = true; + await this.orderUtil.validateInfo(order); + order.hash = this.orderUtil.getOrderHash(order); + await this.orderUtil.updateBrokerAndInterceptor(order); + await this.orderUtil.checkBrokerSignature(order); + } + await this.checkCutoffsAndCancelledOrders(orders); + + for (const ring of rings) { + ring.updateHash(); + } + + mining.updateHash(rings); + await mining.updateMinerAndInterceptor(); + assert(mining.checkMinerSignature(ringsInfo.transactionOrigin) === true, + "Invalid miner signature"); + + for (const order of orders) { + this.orderUtil.checkDualAuthSignature(order, mining.hash); + } + + for (const order of orders) { + await this.orderUtil.updateStates(order); + } + + const ringMinedEvents: RingMinedEvent[] = []; + const transferItems: TransferItem[] = []; + const feeBalances: { [id: string]: any; } = {}; + for (const ring of rings) { + ring.checkOrdersValid(); + ring.checkForSubRings(); + await ring.checkTokensRegistered(); + ring.checkP2P(mining); + if (ring.valid) { + const ringReport = await this.simulateAndReportSingle(ring, feeBalances); + ringMinedEvents.push(ringReport.ringMinedEvent); + transferItems.push(...ringReport.transferItems); + } + } + + // Simulate the token transfers of all rings + const balances: { [id: string]: any; } = {}; + for (const transfer of transferItems) { + if (!balances[transfer.token]) { + balances[transfer.token] = {}; + } + if (!balances[transfer.token][transfer.from]) { + balances[transfer.token][transfer.from] = 0; + } + if (!balances[transfer.token][transfer.to]) { + balances[transfer.token][transfer.to] = 0; + } + balances[transfer.token][transfer.from] -= transfer.amount; + balances[transfer.token][transfer.to] += transfer.amount; + } + // Check if we haven't spent more funds than the owner owns + for (const token of Object.keys(balances)) { + for (const owner of Object.keys(balances[token])) { + const spendable = await this.orderUtil.getERC20Spendable(this.context.tradeDelegate.address, + token, + owner); + const finalBalance = spendable + balances[token][owner]; + const epsilon = 1000; + assert(finalBalance >= -epsilon, "can't sell more tokens than the owner owns"); + } + } + + const filledAmounts: { [hash: string]: number; } = {}; + for (const order of orders) { + filledAmounts[order.hash.toString("hex")] = order.filledAmountS ? order.filledAmountS : 0; + } + + const simulatorReport: SimulatorReport = { + ringMinedEvents, + transferItems, + feeBalances, + filledAmounts, + }; + return simulatorReport; + } + + private async simulateAndReportSingle(ring: Ring, feeBalances: { [id: string]: any; }) { + await ring.calculateFillAmountAndFee(); + const transferItems = await ring.getRingTransferItems(this.walletSplitPercentage, feeBalances); + const ringMinedEvent: RingMinedEvent = { + ringIndex: new BigNumber(this.ringIndex++), + }; + return {ringMinedEvent, transferItems}; + } + + private async checkCutoffsAndCancelledOrders(orders: OrderInfo[]) { + const bitstream = new Bitstream(); + for (const order of orders) { + bitstream.addAddress(order.owner, 32); + bitstream.addHex(order.hash.toString("hex")); + bitstream.addNumber(order.validSince, 32); + bitstream.addHex(xor(order.tokenS, order.tokenB, 20)); + bitstream.addNumber(0, 12); + } + + const ordersValid = await this.context.tradeDelegate.batchCheckCutoffsAndCancelled(bitstream.getBytes32Array()); + + const bits = new BN(ordersValid.toString(16), 16); + for (const [i, order] of orders.entries()) { + order.valid = order.valid && ensure(bits.testn(i), "order is cancelled or cut off"); + } + } +} diff --git a/src/ring.ts b/src/ring.ts new file mode 100644 index 0000000..6bb5bc4 --- /dev/null +++ b/src/ring.ts @@ -0,0 +1,702 @@ +import ABI = require("ethereumjs-abi"); +import { Bitstream } from "./bitstream"; +import { Context } from "./context"; +import { ensure } from "./ensure"; +import { Mining } from "./mining"; +import { OrderUtil } from "./order"; +import { OrderInfo, TransferItem } from "./types"; + +export class Ring { + + public orders: OrderInfo[]; + public owner: string; + public feeRecipient: string; + public hash?: Buffer; + public minerFeesToOrdersPercentage?: number; + public P2P: boolean; + public valid: boolean; + + private context: Context; + private orderUtil: OrderUtil; + private walletSplitPercentage: number; + + private feeBalances: { [id: string]: any; } = {}; + + constructor(context: Context, + orders: OrderInfo[], + owner: string, + feeRecipient: string, + ) { + this.context = context; + this.orders = orders; + this.owner = owner; + this.feeRecipient = feeRecipient; + this.P2P = false; + this.valid = true; + this.minerFeesToOrdersPercentage = 0; + + this.orderUtil = new OrderUtil(context); + } + + public updateHash() { + const orderHashes = new Bitstream(); + for (const order of this.orders) { + orderHashes.addHex(order.hash.toString("hex")); + orderHashes.addNumber(order.waiveFeePercentage ? order.waiveFeePercentage : 0, 2); + } + this.hash = ABI.soliditySHA3(["bytes"], [Buffer.from(orderHashes.getData().slice(2), "hex")]); + } + + public checkOrdersValid() { + this.valid = this.valid && ensure(this.orders.length > 1 && this.orders.length <= 8, "invald ring size"); + for (const order of this.orders) { + this.valid = this.valid && ensure(order.valid, "ring contains invalid order"); + } + } + + public checkForSubRings() { + for (let i = 0; i < this.orders.length - 1; i++) { + const tokenS = this.orders[i].tokenS; + for (let j = i + 1; j < this.orders.length; j++) { + this.valid = this.valid && ensure(tokenS !== this.orders[j].tokenS, "ring has sub-rings"); + } + } + } + + public async checkTokensRegistered() { + const tokens: string[] = []; + for (const order of this.orders) { + tokens.push(order.tokenS); + } + const tokensRegistered = await this.context.tokenRegistry.areAllTokensRegistered(tokens); + this.valid = this.valid && ensure(tokensRegistered, "ring uses unregistered tokens"); + } + + public checkP2P(mining: Mining) { + // This is a P2P ring when the signer of the ring is an owner of an order in the ring + for (const order of this.orders) { + if (order.owner === mining.miner) { + this.P2P = true; + return; + } + } + } + + public async calculateFillAmountAndFee() { + // Invalid order data could cause a divide by zero in the calculations + if (!this.valid) { + return; + } + + for (const order of this.orders) { + await this.setMaxFillAmounts(order); + } + + let smallest = 0; + const ringSize = this.orders.length; + + for (let i = ringSize - 1; i >= 0; i--) { + smallest = this.resize(i, smallest); + } + + for (let i = ringSize - 1; i >= smallest; i--) { + this.resize(i, smallest); + } + + for (let i = 0; i < ringSize; i++) { + const prevIndex = (i + ringSize - 1) % ringSize; + const prevOrder = this.orders[prevIndex]; + const order = this.orders[i]; + + if (order.fillAmountS >= prevOrder.fillAmountB) { + await this.calculateFeesAndTaxes(order, prevOrder, this.P2P); + if (order.waiveFeePercentage < 0) { + this.minerFeesToOrdersPercentage += -order.waiveFeePercentage; + } + } else { + this.valid = ensure(false, "ring cannot be settled"); + } + } + this.valid = this.valid && ensure(this.minerFeesToOrdersPercentage <= this.context.feePercentageBase, + "miner distributes more than 100% of its fees to order owners"); + + // Ring calculations are done. Make sure te remove all reservations for this ring + for (const order of this.orders) { + this.orderUtil.resetReservations(order); + } + } + + public async setMaxFillAmounts(order: OrderInfo) { + const remainingS = order.amountS - order.filledAmountS; + order.ringSpendableS = await this.orderUtil.getSpendableS(order); + order.fillAmountS = Math.min(order.ringSpendableS, remainingS); + if (this.P2P) { + // If this is a P2P ring we may have to pay a (pre-trading) percentage tokenS to the wallet + // We have to make sure the order owner can pay that percentage, otherwise we'll have to sell + // less tokenS. + const feeS = this.calculatePreTradingPercentage(order.fillAmountS, + order.tokenSFeePercentage, + this.context.feePercentageBase); + const taxS = this.context.tax.calculateTax(order.tokenS, false, true, feeS); + const totalAmountS = order.fillAmountS + feeS + taxS; + if (totalAmountS > order.ringSpendableS) { + // This will very, very slightly underestimate fillAmountS to keep calculations simple + const taxRateTokenS = this.context.tax.getTaxRate(order.tokenS, false, true); + const totalAddedPercentage = order.tokenSFeePercentage * (this.context.feePercentageBase + taxRateTokenS); + const totalPercentageBase = this.context.feePercentageBase * this.context.feePercentageBase; + if (totalAddedPercentage >= totalPercentageBase) { + this.valid = ensure(false, "totalAddedPercentage >= totalPercentageBase"); + return; + } + const maxFeeAndTaxAmountS = Math.floor(order.ringSpendableS * totalAddedPercentage / + totalPercentageBase); + order.fillAmountS = order.ringSpendableS - maxFeeAndTaxAmountS; + } + } + order.fillAmountB = Math.floor(order.fillAmountS * order.amountB / order.amountS); + } + + public async calculateFeesAndTaxes(order: OrderInfo, prevOrder: OrderInfo, P2P: boolean) { + if (this.P2P) { + // Calculate P2P fees + order.fillAmountFee = 0; + if (order.walletAddr) { + order.fillAmountFeeS = this.calculatePreTradingPercentage(order.fillAmountS, + order.tokenSFeePercentage, + this.context.feePercentageBase); + order.fillAmountFeeB = Math.floor(order.fillAmountB * order.tokenBFeePercentage / + this.context.feePercentageBase); + } else { + order.fillAmountFeeS = 0; + order.fillAmountFeeB = 0; + } + + // The taker gets the margin + order.splitS = 0; + } else { + // Calculate matching fees + order.fillAmountFee = Math.floor(order.feeAmount * order.fillAmountS / order.amountS); + order.fillAmountFeeS = 0; + order.fillAmountFeeB = 0; + + // We have to pay with tokenB if the owner can't pay the complete feeAmount in feeToken + const feeAmountTax = this.context.tax.calculateTax(order.feeToken, false, this.P2P, order.fillAmountFee); + const totalAmountFeeToken = order.fillAmountFee + feeAmountTax; + + // If nextOrder.feeToken == nextOrder.tokenB and the order doesn't have the necessary tokenB amount + // we use feePercentage instead, which should give similar results if the data is set correctly + // in the order + await this.orderUtil.reserveAmountS(order, order.fillAmountS); + order.ringSpendableFee = await this.orderUtil.getSpendableFee(order); + if (totalAmountFeeToken > order.ringSpendableFee) { + order.fillAmountFeeB += Math.floor(order.fillAmountB * order.feePercentage / this.context.feePercentageBase); + // fillAmountB still contains fillAmountFeeB! This makes the subsequent calculations easier. + order.fillAmountFee = 0; + } else { + await this.orderUtil.reserveAmountFee(order, totalAmountFeeToken); + } + + // Miner can waive fees for this order. If waiveFeePercentage > 0 this is a simple reduction in fees. + if (order.waiveFeePercentage > 0) { + order.fillAmountFee = Math.floor(order.fillAmountFee * + (this.context.feePercentageBase - order.waiveFeePercentage) / + this.context.feePercentageBase); + order.fillAmountFeeB = Math.floor(order.fillAmountFeeB * + (this.context.feePercentageBase - order.waiveFeePercentage) / + this.context.feePercentageBase); + // fillAmountFeeS is always 0 + } else if (order.waiveFeePercentage < 0) { + // No fees need to be paid by this order + order.fillAmountFee = 0; + order.fillAmountFeeB = 0; + } + + // The miner/wallet gets the margin + order.splitS = order.fillAmountS - prevOrder.fillAmountB; + order.fillAmountS = prevOrder.fillAmountB; + } + + // Calculate consumer taxes. These are applied on top of the calculated fees + order.taxFee = this.context.tax.calculateTax(order.feeToken, false, this.P2P, order.fillAmountFee); + order.taxS = this.context.tax.calculateTax(order.tokenS, false, this.P2P, order.fillAmountFeeS); + order.taxB = this.context.tax.calculateTax(order.tokenB, false, this.P2P, order.fillAmountFeeB); + } + + public adjustOrderState(order: OrderInfo) { + const filledAmountS = order.fillAmountS + order.splitS; + const totalAmountS = filledAmountS + order.taxS; + const totalAmountFee = order.fillAmountFee + order.taxFee; + order.filledAmountS += filledAmountS; + // Update spendables + order.tokenSpendableS.amount -= totalAmountS; + order.tokenSpendableFee.amount -= totalAmountFee; + if (order.brokerInterceptor) { + order.brokerSpendableS.amount -= totalAmountS; + order.brokerSpendableFee.amount -= totalAmountFee; + } + // Checks + assert(order.tokenSpendableS.amount >= 0, "spendableS should be positive"); + assert(order.tokenSpendableFee.amount >= 0, "spendableFee should be positive"); + assert(order.filledAmountS <= order.amountS, "filledAmountS <= amountS"); + } + + public async getRingTransferItems(walletSplitPercentage: number, feeBalances: { [id: string]: any; }) { + this.walletSplitPercentage = walletSplitPercentage; + if (walletSplitPercentage > 100 && walletSplitPercentage < 0) { + throw new Error("invalid walletSplitPercentage:" + walletSplitPercentage); + } + if (!this.valid) { + return []; + } + + const transferItems = await this.transferTokens(); + await this.payFees(); + + // Validate how the ring is settled + this.validateSettlement(transferItems); + + // Adjust orders + for (const order of this.orders) { + this.adjustOrderState(order); + } + + // Add the fee balances to the global fee list + for (const token of Object.keys(this.feeBalances)) { + for (const owner of Object.keys(this.feeBalances[token])) { + if (!feeBalances[token]) { + feeBalances[token] = {}; + } + if (!feeBalances[token][owner]) { + feeBalances[token][owner] = await this.context.feeHolder.feeBalances(token, owner).toNumber(); + } + feeBalances[token][owner] += this.feeBalances[token][owner]; + } + } + + return transferItems; + } + + private transferTokens() { + const ringSize = this.orders.length; + const transferItems: TransferItem[] = []; + for (let i = 0; i < ringSize; i++) { + const prevIndex = (i + ringSize - 1) % ringSize; + const order = this.orders[i]; + const prevOrder = this.orders[prevIndex]; + const feeHolder = this.context.feeHolder.address; + + // If the buyer needs to pay fees in a percentage of tokenB, the seller needs + // to send that amount of tokenS to the fee holder contract. + const amountSToBuyer = order.fillAmountS - prevOrder.fillAmountFeeB - prevOrder.taxB; + let amountSToFeeHolder = order.splitS + order.fillAmountFeeS + order.taxS + + prevOrder.fillAmountFeeB + prevOrder.taxB; + let amountFeeToFeeHolder = order.fillAmountFee + order.taxFee; + if (order.tokenS === order.feeToken) { + amountSToFeeHolder += amountFeeToFeeHolder; + amountFeeToFeeHolder = 0; + } + + this.addTokenTransfer(transferItems, order.tokenS, order.owner, prevOrder.owner, amountSToBuyer); + this.addTokenTransfer(transferItems, order.tokenS, order.owner, feeHolder, amountSToFeeHolder); + this.addTokenTransfer(transferItems, order.feeToken, order.owner, feeHolder, amountFeeToFeeHolder); + } + return transferItems; + } + + private addTokenTransfer(transferItems: TransferItem[], token: string, from: string, to: string, amount: number) { + if (amount > 0) { + transferItems.push({token, from, to, amount}); + } + } + + private async payFees() { + const ringSize = this.orders.length; + for (let i = 0; i < ringSize; i++) { + const order = this.orders[i]; + const feeInTokenS = order.fillAmountFeeS + order.splitS; + await this.payFeesAndTaxes(order.feeToken, order.fillAmountFee, order.taxFee, order.walletAddr); + await this.payFeesAndTaxes(order.tokenS, feeInTokenS, order.taxS, order.walletAddr); + await this.payFeesAndTaxes(order.tokenB, order.fillAmountFeeB, order.taxB, order.walletAddr); + } + } + + private async payFeesAndTaxes(token: string, amount: number, consumerTax: number, wallet: string) { + if (amount === 0) { + assert.equal(consumerTax, 0, "If fee == 0 no tax should be paid"); + return; + } + if (this.P2P && !wallet) { + assert.equal(amount, 0, "In a P2P ring no fees should be paid when no wallet is provided"); + } + + const walletPercentage = this.P2P ? 100 : + (wallet ? this.walletSplitPercentage : 0); + + const incomeTax = this.context.tax.calculateTax(token, true, this.P2P, amount); + const incomeAfterTax = amount - incomeTax; + + const feeToWallet = Math.floor(incomeAfterTax * walletPercentage / 100); + const minerFee = incomeAfterTax - feeToWallet; + + let feeToMiner = minerFee; + // Fees can be paid out in different tokens so we can't easily accumulate the total fee + // that needs to be paid out to order owners. So we pay out each part out here to all orders that need it. + if (this.minerFeesToOrdersPercentage > 0) { + // Subtract all fees the miner pays to the orders + feeToMiner = Math.floor(minerFee * (this.context.feePercentageBase - this.minerFeesToOrdersPercentage) / + this.context.feePercentageBase); + // Pay out the fees to the orders + for (const order of this.orders) { + if (order.waiveFeePercentage < 0) { + const feeToOwner = Math.floor(minerFee * (-order.waiveFeePercentage) / this.context.feePercentageBase); + await this.payFee(token, order.owner, feeToOwner); + } + } + } + await this.payFee(token, wallet, feeToWallet); + await this.payFee(token, this.feeRecipient, feeToMiner); + // Pay the tax with the feeHolder as owner + await this.payFee(token, this.context.feeHolder.address, consumerTax + incomeTax); + } + + private async payFee(token: string, owner: string, amount: number) { + if (!token || !owner || !amount) { + return; + } + if (!this.feeBalances[token]) { + this.feeBalances[token] = {}; + } + if (!this.feeBalances[token][owner]) { + this.feeBalances[token][owner] = 0; + } + this.feeBalances[token][owner] += amount; + } + + private resize(i: number, smallest: number) { + let newSmallest = smallest; + const j = (i + this.orders.length - 1) % this.orders.length; + const order = this.orders[i]; + const prevOrder = this.orders[j]; + + if (prevOrder.fillAmountB > order.fillAmountS) { + newSmallest = i; + prevOrder.fillAmountB = order.fillAmountS; + prevOrder.fillAmountS = Math.floor(prevOrder.fillAmountB * prevOrder.amountS / prevOrder.amountB); + } + + return newSmallest; + } + + private calculatePreTradingPercentage(value: number, percentage: number, percentageBase: number) { + assert(percentage < percentageBase); + return Math.floor((value * percentageBase) / (percentageBase - percentage)) - value; + } + + private validateSettlement(transfers: TransferItem[]) { + const ringSize = this.orders.length; + for (let i = 0; i < ringSize; i++) { + const prevIndex = (i + ringSize - 1) % ringSize; + const order = this.orders[i]; + const prevOrder = this.orders[prevIndex]; + + console.log("order.spendableS: " + order.ringSpendableS / 1e18); + console.log("order.spendableFee: " + order.ringSpendableFee / 1e18); + console.log("order.amountS: " + order.amountS / 1e18); + console.log("order.amountB: " + order.amountB / 1e18); + console.log("order.feeAmount: " + order.feeAmount / 1e18); + console.log("order expected rate: " + order.amountS / order.amountB); + console.log("order.fillAmountS: " + order.fillAmountS / 1e18); + console.log("order.fillAmountB: " + order.fillAmountB / 1e18); + console.log("order.splitS: " + order.splitS / 1e18); + console.log("order actual rate: " + (order.fillAmountS + order.splitS) / order.fillAmountB); + console.log("order.fillAmountFee: " + order.fillAmountFee / 1e18); + console.log("order.fillAmountFeeS: " + order.fillAmountFeeS / 1e18); + console.log("order.fillAmountFeeB: " + order.fillAmountFeeB / 1e18); + console.log("order.taxFee: " + order.taxFee / 1e18); + console.log("order.taxS: " + order.taxS / 1e18); + console.log("order.taxB: " + order.taxB / 1e18); + console.log("tokenS percentage: " + (this.P2P ? order.tokenSFeePercentage : 0) / + this.context.feePercentageBase); + // tokenSFeePercentage is pre-trading so the percentage is on the total tokenS paid + console.log("tokenS real percentage: " + order.fillAmountFeeS / + (order.fillAmountS + order.fillAmountFeeS)); + console.log("tokenB percentage: " + + (this.P2P ? order.tokenBFeePercentage : order.feePercentage) / this.context.feePercentageBase); + console.log("tokenB real percentage: " + order.fillAmountFeeB / order.fillAmountB); + console.log("----------------------------------------------"); + + const epsilon = 1000; + + // Sanity checks + assert(order.fillAmountS >= 0, "fillAmountS should be positive"); + assert(order.splitS >= 0, "splitS should be positive"); + assert(order.fillAmountFee >= 0, "fillAmountFee should be positive"); + assert(order.fillAmountFeeS >= 0, "fillAmountFeeS should be positive"); + assert(order.fillAmountFeeB >= 0, "fillAmountFeeB should be positive"); + assert(order.taxFee >= 0, "taxFee should be positive"); + assert(order.taxS >= 0, "taxS should be positive"); + assert(order.taxB >= 0, "taxB should be positive"); + + // General fill requirements + assert((order.fillAmountS + order.splitS) <= order.amountS, "fillAmountS + splitS <= amountS"); + assert(order.fillAmountB <= order.amountB + epsilon, "fillAmountB <= amountB"); + assert(order.fillAmountFee <= order.feeAmount, "fillAmountFee <= feeAmount"); + if (order.fillAmountS > 0 || order.fillAmountB > 0) { + const orderRate = order.amountS / order.amountB; + const rate = (order.fillAmountS + order.splitS) / order.fillAmountB; + this.assertNumberEqualsWithPrecision(rate, orderRate, "fill rates need to match order rate"); + } + + // Check who gets the margin + if (this.P2P) { + // Taker gets all margin + assert(order.fillAmountS >= prevOrder.fillAmountB, "fillAmountS >= prev.fillAmountB"); + assert.equal(order.splitS, 0, "splitS should be 0 in P2P ring"); + } else { + // Miner gets all margin + assert.equal(order.fillAmountS, prevOrder.fillAmountB, "fillAmountS == prev.fillAmountB"); + } + + // Spendable limitations + { + const totalAmountTokenS = order.fillAmountS + order.splitS + order.fillAmountFeeS + order.taxS; + const totalAmountTokenFee = order.fillAmountFee + order.taxFee; + if (order.tokenS === order.feeToken) { + assert(totalAmountTokenS + totalAmountTokenFee <= order.ringSpendableS + epsilon, + "totalAmountTokenS + totalAmountTokenFee <= spendableS"); + } else { + assert(totalAmountTokenS <= order.ringSpendableS + epsilon, "totalAmountTokenS <= spendableS"); + assert(totalAmountTokenFee <= (order.ringSpendableFee ? order.ringSpendableFee : 0) + epsilon, + "totalAmountTokenFee <= spendableFee"); + } + } + + // Ensure fees are calculated correctly + if (this.P2P) { + // Fee cannot be paid in tokenFee + assert.equal(order.fillAmountFee, 0, "Cannot pay in tokenFee in P2P ring"); + // Check if fees were calculated correctly for the expected rate + if (order.walletAddr) { + // fees in tokenS + { + const rate = order.fillAmountFeeS / (order.fillAmountS + order.fillAmountFeeS); + this.assertNumberEqualsWithPrecision(rate, order.tokenSFeePercentage, + "tokenS fee rate needs to match given rate"); + } + // fees in tokenB + { + const rate = order.fillAmountFeeB / order.fillAmountB; + this.assertNumberEqualsWithPrecision(rate, order.tokenBFeePercentage, + "tokenB fee rate needs to match given rate"); + } + } else { + // No fees need to be paid when no wallet is given + assert.equal(order.fillAmountFeeS, 0, "No fees need to paid without wallet in a P2P ring"); + assert.equal(order.fillAmountFeeB, 0, "No fees need to paid without wallet in a P2P ring"); + } + } else { + // Fee cannot be paid in tokenS + assert.equal(order.fillAmountFeeS, 0, "Cannot pay in tokenS"); + // Fees need to be paid either in feeToken OR tokenB, never both at the same time + assert(!(order.fillAmountFee > 0 && order.fillAmountFeeB > 0), "fees should be paid in tokenFee OR tokenB"); + + // Fees can only be paid in tokenB when the owner doesn't have enought funds to pay in feeToken + if (order.fillAmountFeeB > 0) { + const fee = Math.floor(order.feeAmount * (order.fillAmountS + order.splitS) / order.amountS); + const tax = this.context.tax.calculateTax(order.feeToken, false, false, fee); + assert(fee + tax > order.ringSpendableFee, "fees should be paid in tokenFee if possible"); + } + + if (order.waiveFeePercentage < 0) { + // If the miner waives the fees for this order all fees need to be 0 + assert.equal(order.fillAmountFee, 0, "No fees need to be paid if miner waives fees"); + assert.equal(order.fillAmountFeeB, 0, "No fees need to be paid if miner waives fees"); + } else { + if (order.fillAmountFeeB > 0) { + const rate = order.fillAmountFeeB / order.fillAmountB; + const feePercentageAfterMinerReduction = order.feePercentage * + (this.context.feePercentageBase - order.waiveFeePercentage) / this.context.feePercentageBase; + this.assertNumberEqualsWithPrecision(rate, feePercentageAfterMinerReduction, + "tokenB fee should match feePercentage after miner reduction"); + } + if (order.fillAmountFee > 0) { + const filledPercentage = (order.fillAmountS + order.splitS) / order.amountS; + const rate = order.fillAmountFee / order.feeAmount; + const filledPercentageAfterMinerReduction = filledPercentage * + (this.context.feePercentageBase - order.waiveFeePercentage) / this.context.feePercentageBase; + this.assertNumberEqualsWithPrecision(rate, filledPercentageAfterMinerReduction, + "feeAmount rate should match filledPercentage after miner reduction"); + } + } + } + + // Ensure income taxes are calculated correctly + if (order.fillAmountFee > 0) { + const taxRate = this.context.tax.getTaxRate(order.feeToken, false, this.P2P); + const rate = order.taxFee / order.fillAmountFee; + this.assertNumberEqualsWithPrecision(rate, taxRate, + "taxFee rate needs to match expected tax rate"); + } + if (order.fillAmountFeeS > 0) { + const taxRate = this.context.tax.getTaxRate(order.tokenS, false, this.P2P); + const rate = order.taxS / order.fillAmountFeeS; + this.assertNumberEqualsWithPrecision(rate, taxRate, + "taxS rate needs to match expected tax rate"); + } + if (order.fillAmountFeeB > 0) { + const taxRate = this.context.tax.getTaxRate(order.tokenB, false, this.P2P); + const rate = order.taxB / order.fillAmountFeeB; + this.assertNumberEqualsWithPrecision(rate, taxRate, + "taxB rate needs to match expected tax rate"); + } + + // Ensure fees in tokenB can be paid with the amount bought + assert(prevOrder.fillAmountFeeB + prevOrder.taxB <= order.fillAmountS + epsilon, + "Can't pay more in tokenB fees than what was bought"); + } + + // Ensure balances are updated correctly + // Simulate the token transfers in the ring + const balances: { [id: string]: any; } = {}; + for (const transfer of transfers) { + if (!balances[transfer.token]) { + balances[transfer.token] = {}; + } + if (!balances[transfer.token][transfer.from]) { + balances[transfer.token][transfer.from] = 0; + } + if (!balances[transfer.token][transfer.to]) { + balances[transfer.token][transfer.to] = 0; + } + balances[transfer.token][transfer.from] -= transfer.amount; + balances[transfer.token][transfer.to] += transfer.amount; + } + // Accumulate owner balances and accumulate fees + const expectedBalances: { [id: string]: any; } = {}; + const expectedFeeHolderBalances: { [id: string]: number; } = {}; + for (let i = 0; i < ringSize; i++) { + const order = this.orders[i]; + const nextOrder = this.orders[(i + 1) % ringSize]; + // Owner balances + const expectedBalanceS = -(order.fillAmountS + order.splitS + order.fillAmountFeeS + order.taxS); + // In P2P rings nextOrder.fillAmountS > order.fillAmountB because the taker gets the margin + const expectedBalanceB = nextOrder.fillAmountS - order.fillAmountFeeB - order.taxB; + const expectedBalanceFeeToken = -(order.fillAmountFee + order.taxFee); + + // Accumulate balances + if (!expectedBalances[order.owner]) { + expectedBalances[order.owner] = {}; + } + if (!expectedBalances[order.owner][order.tokenS]) { + expectedBalances[order.owner][order.tokenS] = 0; + } + if (!expectedBalances[order.owner][order.tokenB]) { + expectedBalances[order.owner][order.tokenB] = 0; + } + if (!expectedBalances[order.owner][order.feeToken]) { + expectedBalances[order.owner][order.feeToken] = 0; + } + expectedBalances[order.owner][order.tokenS] += expectedBalanceS; + expectedBalances[order.owner][order.tokenB] += expectedBalanceB; + expectedBalances[order.owner][order.feeToken] += expectedBalanceFeeToken; + + // Accumulate fees + if (!expectedFeeHolderBalances[order.tokenS]) { + expectedFeeHolderBalances[order.tokenS] = 0; + } + if (!expectedFeeHolderBalances[order.tokenB]) { + expectedFeeHolderBalances[order.tokenB] = 0; + } + if (!expectedFeeHolderBalances[order.feeToken]) { + expectedFeeHolderBalances[order.feeToken] = 0; + } + expectedFeeHolderBalances[order.tokenS] += order.splitS + order.fillAmountFeeS + order.taxS; + expectedFeeHolderBalances[order.tokenB] += order.fillAmountFeeB + order.taxB; + expectedFeeHolderBalances[order.feeToken] += order.fillAmountFee + order.taxFee; + } + // Check balances of all owners + for (let i = 0; i < ringSize; i++) { + const order = this.orders[i]; + const balanceS = (balances[order.tokenS] && balances[order.tokenS][order.owner]) + ? balances[order.tokenS][order.owner] : 0; + const balanceB = (balances[order.tokenB] && balances[order.tokenB][order.owner]) + ? balances[order.tokenB][order.owner] : 0; + const balanceFeeToken = (balances[order.feeToken] && balances[order.feeToken][order.owner]) + ? balances[order.feeToken][order.owner] : 0; + this.assertNumberEqualsWithPrecision(balanceS, expectedBalances[order.owner][order.tokenS], + "Order owner tokenS balance should match expected value"); + this.assertNumberEqualsWithPrecision(balanceB, expectedBalances[order.owner][order.tokenB], + "Order owner tokenB balance should match expected value"); + this.assertNumberEqualsWithPrecision(balanceFeeToken, expectedBalances[order.owner][order.feeToken], + "Order owner feeToken balance should match expected value"); + } + // Check fee holder balances of all possible tokens used to pay fees + for (const token of [...Object.keys(expectedFeeHolderBalances), ...Object.keys(balances)]) { + const feeAddress = this.context.feeHolder.address; + const expectedBalance = expectedFeeHolderBalances[token] ? expectedFeeHolderBalances[token] : 0; + const balance = (balances[token] && balances[token][feeAddress]) ? balances[token][feeAddress] : 0; + this.assertNumberEqualsWithPrecision(balance, expectedBalance, + "FeeHolder balance after transfers should match expected value"); + } + + // Ensure fee payments match perfectly with total amount fees paid by all orders + { + const totalFees: { [id: string]: number; } = {}; + const consumerTaxes: { [id: string]: number; } = {}; + for (let i = 0; i < ringSize; i++) { + const order = this.orders[i]; + // Fees + if (!totalFees[order.feeToken]) { + totalFees[order.feeToken] = 0; + } + totalFees[order.feeToken] += order.fillAmountFee; + if (!totalFees[order.tokenS]) { + totalFees[order.tokenS] = 0; + } + totalFees[order.tokenS] += order.fillAmountFeeS + order.splitS; + if (!totalFees[order.tokenB]) { + totalFees[order.tokenB] = 0; + } + totalFees[order.tokenB] += order.fillAmountFeeB; + // Consumer taxes + if (!consumerTaxes[order.feeToken]) { + consumerTaxes[order.feeToken] = 0; + } + consumerTaxes[order.feeToken] += order.taxFee; + if (!consumerTaxes[order.tokenS]) { + consumerTaxes[order.tokenS] = 0; + } + consumerTaxes[order.tokenS] += order.taxS; + if (!consumerTaxes[order.tokenB]) { + consumerTaxes[order.tokenB] = 0; + } + consumerTaxes[order.tokenB] += order.taxB; + } + + for (const token of Object.keys(this.feeBalances)) { + let totalFee = 0; + let totalTax = 0; + for (const owner of Object.keys(this.feeBalances[token])) { + const balance = this.feeBalances[token][owner]; + if (owner === this.context.feeHolder.address) { + totalTax += balance; + } else { + totalFee += balance; + } + } + const totalIncomeTax = this.context.tax.calculateTax(token, true, this.P2P, totalFees[token]); + const incomeAfterTax = totalFees[token] - totalIncomeTax; + this.assertNumberEqualsWithPrecision(incomeAfterTax, totalFee, + "Total income distributed needs to match paid fees after tax"); + this.assertNumberEqualsWithPrecision(consumerTaxes[token] + totalIncomeTax, totalTax, + "Total tax distributed needs to match consumer tax + income tax"); + } + } + } + + private assertNumberEqualsWithPrecision(n1: number, n2: number, description: string, precision: number = 8) { + const numStr1 = (n1 / 1e18).toFixed(precision); + const numStr2 = (n2 / 1e18).toFixed(precision); + return assert.equal(Number(numStr1), Number(numStr2), description); + } +} diff --git a/src/rings_generator.ts b/src/rings_generator.ts new file mode 100644 index 0000000..ce63bf2 --- /dev/null +++ b/src/rings_generator.ts @@ -0,0 +1,309 @@ +import { BigNumber } from "bignumber.js"; +import BN = require("bn.js"); +import promisify = require("es6-promisify"); +import ABI = require("ethereumjs-abi"); +import ethUtil = require("ethereumjs-util"); +import Web3 = require("web3"); +import { Bitstream } from "./bitstream"; +import { Context } from "./context"; +import { MultiHashUtil } from "./multihash"; +import { OrderUtil } from "./order"; +import { Ring } from "./ring"; +import { OrderInfo, RingsInfo, RingsSubmitParam, SignAlgorithm, Spendable } from "./types"; + +export class RingsGenerator { + private multiHashUtil = new MultiHashUtil(); + private orderUtil: OrderUtil; + private context: Context; + + constructor(context: Context) { + this.context = context; + this.orderUtil = new OrderUtil(context); + } + + public async setupRingsAsync(rings: RingsInfo) { + // Setup orders + for (const order of rings.orders) { + order.hash = this.orderUtil.getOrderHash(order); + if (/*order.sig === undefined*/true) { + order.sig = await this.multiHashUtil.signOrderAsync(order); + } + } + + // Calculate all ring hashes + const ringHashes: string[] = []; + for (const ring of rings.rings) { + const orderHashes = new Bitstream(); + for (const order of ring) { + orderHashes.addHex(rings.orders[order].hash.toString("hex")); + orderHashes.addNumber(rings.orders[order].waiveFeePercentage ? rings.orders[order].waiveFeePercentage : 0, 2); + } + const ringHash = ABI.soliditySHA3(["bytes"], [Buffer.from(orderHashes.getData().slice(2), "hex")]); + ringHashes.push("0x" + ringHash.toString("hex")); + } + + // XOR ring hashes together for the mining hash + let ringHashesXOR = ringHashes[0]; + for (let i = 1; i < ringHashes.length; i++) { + ringHashesXOR = this.xor(ringHashesXOR, ringHashes[i], 32); + } + + // Calculate mining hash + const feeRecipient = rings.feeRecipient ? rings.feeRecipient : rings.transactionOrigin; + const args = [ + feeRecipient, + rings.miner ? rings.miner : "0x0", + ringHashesXOR, + ]; + const argTypes = [ + "address", + "address", + "bytes32", + ]; + rings.hash = ABI.soliditySHA3(argTypes, args); + + // Calculate mining signature if needed + const miner = rings.miner ? rings.miner : feeRecipient; + if (miner === rings.transactionOrigin) { + rings.sig = undefined; + } else { + rings.sig = await this.multiHashUtil.signAsync(rings.signAlgorithm, rings.hash, miner); + } + + // Dual Authoring + for (const order of rings.orders) { + if (/*order.dualAuthSig === undefined*/true) { + if (order.dualAuthAddr) { + order.dualAuthSig = await this.multiHashUtil.signAsync( + order.dualAuthSignAlgorithm, + rings.hash, + order.dualAuthAddr, + ); + } + } + } + } + + public toSubmitableParam(rings: RingsInfo) { + const numSpendables = this.setupSpendables(rings); + const param = this.ringsToParam(rings); + + const encodeSpecs: number[] = []; + const len = 7 + param.ringSpecs.length + param.bytesList.length; + encodeSpecs.push(len); + encodeSpecs.push(param.orderSpecs.length); + encodeSpecs.push(param.ringSpecs.length); + encodeSpecs.push(param.addressList.length); + encodeSpecs.push(param.uintList.length); + encodeSpecs.push(param.uint16List.length); + encodeSpecs.push(param.bytesList.length); + encodeSpecs.push(numSpendables); + param.ringSpecs.forEach((rs) => encodeSpecs.push(rs.length)); + // Bytes arrays start with 0x and have 2 characters/byte + param.bytesList.forEach((bs) => encodeSpecs.push((bs.length - 2) / 2)); + + return this.submitParamToBytes(param, encodeSpecs); + } + + private setupSpendables(rings: RingsInfo) { + let numSpendables = 0; + const ownerTokens: { [id: string]: any; } = {}; + const ownerBrokerTokens: { [id: string]: any; } = {}; + for (const order of rings.orders) { + const tokenFee = order.feeToken ? order.feeToken : this.context.lrcAddress; + // Token spendables + if (!ownerTokens[order.owner]) { + ownerTokens[order.owner] = {}; + } + if (!ownerTokens[order.owner][order.tokenS]) { + ownerTokens[order.owner][order.tokenS] = { + index: numSpendables++, + }; + } + order.tokenSpendableS = ownerTokens[order.owner][order.tokenS]; + if (!ownerTokens[order.owner][tokenFee]) { + ownerTokens[order.owner][tokenFee] = { + index: numSpendables++, + }; + } + order.tokenSpendableFee = ownerTokens[order.owner][tokenFee]; + // Broker allowances + if (order.broker) { + if (!ownerBrokerTokens[order.owner]) { + ownerBrokerTokens[order.owner] = {}; + } + if (!ownerBrokerTokens[order.owner][order.broker]) { + ownerBrokerTokens[order.owner][order.broker] = {}; + } + if (!ownerBrokerTokens[order.owner][order.broker][order.tokenS]) { + ownerBrokerTokens[order.owner][order.broker][order.tokenS] = { + index: numSpendables++, + }; + } + order.brokerSpendableS = ownerBrokerTokens[order.owner][order.broker][order.tokenS]; + if (!ownerBrokerTokens[order.owner][order.broker][tokenFee]) { + ownerBrokerTokens[order.owner][order.broker][tokenFee] = { + index: numSpendables++, + }; + } + order.brokerSpendableFee = ownerBrokerTokens[order.owner][order.broker][tokenFee]; + } + } + return numSpendables; + } + + private ringsToParam(ringsInfo: RingsInfo) { + const param: RingsSubmitParam = { + miningSpec: 0, + orderSpecs: [], + ringSpecs: [], + addressList: [], + uintList: [], + uint16List: [], + bytesList: [], + }; + + this.calculateMiningSepc(ringsInfo, param); + param.ringSpecs = ringsInfo.rings; + ringsInfo.orders.map((o) => this.calculateOrderSpec(o, param)); + + ringsInfo.orders.forEach((o) => console.log(o)); + return param; + } + + private calculateMiningSepc(ringsInfo: RingsInfo, param: RingsSubmitParam) { + let miningSpec = 0; + if (ringsInfo.feeRecipient) { + miningSpec += 1; + param.addressList.push(ringsInfo.feeRecipient); + } + + if (ringsInfo.miner) { + miningSpec += 1 << 1; + param.addressList.push(ringsInfo.miner); + } + + if (ringsInfo.sig) { + miningSpec += 1 << 2; + param.bytesList.push(ringsInfo.sig); + } + + param.miningSpec = miningSpec; + } + + private calculateOrderSpec(order: OrderInfo, param: RingsSubmitParam) { + param.addressList.push(order.owner); + param.addressList.push(order.tokenS); + // param.addressList.push(order.tokenB); + param.uintList.push(new BigNumber(order.amountS)); + param.uintList.push(new BigNumber(order.amountB)); + param.uintList.push(new BigNumber(order.validSince)); + param.uint16List.push(order.tokenSpendableS.index); + param.uint16List.push(order.tokenSpendableFee.index); + + // param.addressList.push(order.delegateContract); + + let spec = 0; + if (order.dualAuthAddr) { + spec += 1; + param.addressList.push(order.dualAuthAddr); + } + + if (order.broker) { + spec += 1 << 1; + param.addressList.push(order.broker); + param.uint16List.push(order.brokerSpendableS.index); + param.uint16List.push(order.brokerSpendableFee.index); + } + if (order.orderInterceptor) { + spec += 1 << 2; + param.addressList.push(order.orderInterceptor); + } + if (order.walletAddr) { + spec += 1 << 3; + param.addressList.push(order.walletAddr); + } + + if (order.validUntil) { + spec += 1 << 4; + param.uintList.push(new BigNumber(order.validUntil)); + } + if (order.allOrNone) { + spec += 1 << 5; + param.uintList.push(new BigNumber(1)); + } + + if (order.sig) { + spec += 1 << 6; + param.bytesList.push(order.sig); + } + + if (order.dualAuthSig) { + spec += 1 << 7; + param.bytesList.push(order.dualAuthSig); + } + + if (order.feeToken && order.feeToken !== this.context.lrcAddress) { + spec += 1 << 8; + param.addressList.push(order.feeToken); + } + + if (order.feeAmount) { + spec += 1 << 9; + param.uintList.push(new BigNumber(order.feeAmount)); + } + + if (order.feePercentage) { + spec += 1 << 10; + param.uint16List.push(order.feePercentage); + } + + if (order.waiveFeePercentage) { + spec += 1 << 11; + param.uint16List.push(order.waiveFeePercentage); + } + + if (order.tokenSFeePercentage) { + spec += 1 << 12; + param.uint16List.push(order.tokenSFeePercentage); + } + + if (order.tokenBFeePercentage) { + spec += 1 << 13; + param.uint16List.push(order.tokenBFeePercentage); + } + + param.orderSpecs.push(spec); + } + + private submitParamToBytes(param: RingsSubmitParam, encodeSpecs: number[]) { + // console.log("encodeSpecs:", encodeSpecs); + // console.log("param.orderSpecs:", param.orderSpecs); + // console.log("addrList:", param.addressList); + // console.log("uintList:", param.uintList); + // console.log("bytesList:", param.bytesList); + // console.log("param.orderSpecs:", param.orderSpecs); + // console.log("param.ringSpecs:", param.ringSpecs); + + const stream = new Bitstream(); + encodeSpecs.forEach((i) => stream.addNumber(i, 2)); + stream.addNumber(param.miningSpec, 2); + param.orderSpecs.forEach((i) => stream.addNumber(i, 2)); + const ringSpecsFlattened = [].concat(...param.ringSpecs); + // console.log("ringSpecsFlattened:", ringSpecsFlattened); + ringSpecsFlattened.forEach((i) => stream.addNumber(i, 1)); + param.addressList.forEach((a) => stream.addAddress(a)); + param.uintList.forEach((bn) => stream.addBigNumber(bn)); + param.uint16List.forEach((n) => stream.addNumber(n, 2)); + param.bytesList.forEach((bs) => stream.addRawBytes(bs)); + + return stream.getData(); + } + + private xor(s1: string, s2: string, numBytes: number) { + const x1 = new BN(s1.slice(2), 16); + const x2 = new BN(s2.slice(2), 16); + const result = x1.xor(x2); + return "0x" + result.toString(16, numBytes * 2); + } +} diff --git a/src/tax.ts b/src/tax.ts new file mode 100644 index 0000000..56e6905 --- /dev/null +++ b/src/tax.ts @@ -0,0 +1,97 @@ + +enum TokenType { + LRC = 0, + ETH = 1, + Other = 2, +} + +export class Tax { + + public matchingConsumerLRC: number; + public matchingConsumerETH: number; + public matchingConsumerOther: number; + public matchingIncomeLRC: number; + public matchingIncomeETH: number; + public matchingIncomeOther: number; + public p2pConsumerLRC: number; + public p2pConsumerETH: number; + public p2pConsumerOther: number; + public p2pIncomeLRC: number; + public p2pIncomeETH: number; + public p2pIncomeOther: number; + public percentageBase: number; + public lrcTokenAddress: string; + public wethTokenAddress: string; + + constructor(matchingConsumerLRC: number, + matchingConsumerETH: number, + matchingConsumerOther: number, + matchingIncomeLRC: number, + matchingIncomeETH: number, + matchingIncomeOther: number, + p2pConsumerLRC: number, + p2pConsumerETH: number, + p2pConsumerOther: number, + p2pIncomeLRC: number, + p2pIncomeETH: number, + p2pIncomeOther: number, + percentageBase: number, + lrcTokenAddress: string, + wethTokenAddress: string) { + this.matchingConsumerLRC = matchingConsumerLRC; + this.matchingConsumerETH = matchingConsumerETH; + this.matchingConsumerOther = matchingConsumerOther; + this.matchingIncomeLRC = matchingIncomeLRC; + this.matchingIncomeETH = matchingIncomeETH; + this.matchingIncomeOther = matchingIncomeOther; + this.p2pConsumerLRC = p2pConsumerLRC; + this.p2pConsumerETH = p2pConsumerETH; + this.p2pConsumerOther = p2pConsumerOther; + this.p2pIncomeLRC = p2pIncomeLRC; + this.p2pIncomeETH = p2pIncomeETH; + this.p2pIncomeOther = p2pIncomeOther; + this.percentageBase = percentageBase; + this.lrcTokenAddress = lrcTokenAddress; + this.wethTokenAddress = wethTokenAddress; + } + + public calculateTax(token: string, income: boolean, P2P: boolean, amount: number) { + if (amount === 0) { + return 0; + } + const taxRate = this.getTaxRate(token, income, P2P); + return Math.floor(amount * taxRate / this.percentageBase); + } + + public getTaxRate(token: string, income: boolean, P2P: boolean) { + const tokenType = this.getTokenType(token); + if (P2P) { + if (income) { + const taxes = [this.p2pIncomeLRC, this.p2pIncomeETH, this.p2pIncomeOther]; + return taxes[tokenType]; + } else { + const taxes = [this.p2pConsumerLRC, this.p2pConsumerETH, this.p2pConsumerOther]; + return taxes[tokenType]; + } + } else { + if (income) { + const taxes = [this.matchingIncomeLRC, this.matchingIncomeETH, this.matchingIncomeOther]; + return taxes[tokenType]; + } else { + const taxes = [this.matchingConsumerLRC, this.matchingConsumerETH, this.matchingConsumerOther]; + return taxes[tokenType]; + } + } + } + + private getTokenType(token: string) { + if (token === this.lrcTokenAddress) { + return TokenType.LRC; + } else if (token === this.wethTokenAddress) { + return TokenType.ETH; + } else { + return TokenType.Other; + } + } + +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..a000ef1 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,130 @@ +import { BigNumber } from "bignumber.js"; + +// Make sure to keep this in sync with the Multihash smart contract +export enum SignAlgorithm { + Ethereum = 0, // Sign with web3.eth_sign + // Should be compatible with Trezor now (with latest firmware): + // https://github.com/ethereum/go-ethereum/issues/14794#issuecomment-392028942 + EIP712 = 1, // Sign with web3.eth.signTypedData + // EIP712: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md + None = 255, // Do not sign +} + +export interface Spendable { + initialized?: boolean; + amount?: number; + reserved?: number; + index?: number; +} + +export interface OrderInfo { + // required fields in contract + owner?: string; + tokenS: string; + tokenB: string; + amountS: number; + amountB: number; + validSince?: number; + tokenSpendableS?: Spendable; + tokenSpendableFee?: Spendable; + + // optional fields + dualAuthAddr?: string; // spec value 1 + broker?: string; // spec value 1 << 1 + brokerSpendableS?: Spendable; + brokerSpendableFee?: Spendable; + orderInterceptor?: string; // spec value 1 << 2 + walletAddr?: string; // spec value 1 << 3 + validUntil?: number; // spec value 1 << 4 + allOrNone?: boolean; // spec value 1 << 5 + sig?: string; // spec value 1 << 6 + dualAuthSig?: string; // spec value 1 << 7 + feeToken?: string; // spec value 1 << 8 + feeAmount?: number; // spec value 1 << 9 + feePercentage?: number; // spec value 1 << 10 + waiveFeePercentage?: number; // spec value 1 << 11 + tokenSFeePercentage?: number; // spec value 1 << 12 + tokenBFeePercentage?: number; // spec value 1 << 13 + + // helper field + filledAmountS?: number; + fillAmountS?: number; + fillAmountB?: number; + fillAmountFee?: number; + fillAmountFeeS?: number; + fillAmountFeeB?: number; + taxFee?: number; + taxS?: number; + taxB?: number; + splitS?: number; + brokerInterceptor?: string; + valid?: boolean; + + // test fields + ringSpendableS?: number; + ringSpendableFee?: number; + + hash?: Buffer; + signAlgorithm?: SignAlgorithm; + dualAuthSignAlgorithm?: SignAlgorithm; + + index?: number; + ownerIndex?: number; + balanceS?: number; + balanceFee?: number; + + [key: string]: any; +} + +export interface Participation { + order: OrderInfo; + splitS?: number; + feeAmount?: number; + fillAmountS?: number; + fillAmountB?: number; +} + +export interface RingsSubmitParam { + miningSpec: number; + orderSpecs: number[]; + ringSpecs: number[][]; + addressList: string[]; + uintList: BigNumber[]; + uint16List: number[]; + bytesList: string[]; +} + +export interface RingsInfo { + description?: string; + feeRecipient?: string; // spec value: 1 + miner?: string; // spec value: 1 << 1 + sig?: string; // spec value: 1 << 2 + rings: number[][]; + orders: OrderInfo[]; + + signAlgorithm?: SignAlgorithm; + hash?: Buffer; + transactionOrigin?: string; + + transactionOriginOwnerIndex?: number; + + [key: string]: any; +} + +export interface SimulatorReport { + ringMinedEvents: RingMinedEvent[]; + transferItems: TransferItem[]; + feeBalances: { [id: string]: any; }; + filledAmounts: { [hash: string]: number; }; +} + +export interface TransferItem { + token: string; + from: string; + to: string; + amount: number; +} + +export interface RingMinedEvent { + ringIndex: BigNumber; +} diff --git a/src/xor.ts b/src/xor.ts new file mode 100644 index 0000000..54cb164 --- /dev/null +++ b/src/xor.ts @@ -0,0 +1,8 @@ +import BN = require("bn.js"); + +export function xor(s1: string, s2: string, numBytes: number) { + const x1 = new BN(s1.slice(2), 16); + const x2 = new BN(s2.slice(2), 16); + const result = x1.xor(x2); + return "0x" + result.toString(16, numBytes * 2); +} diff --git a/test/rings_config.ts b/test/rings_config.ts new file mode 100644 index 0000000..4151526 --- /dev/null +++ b/test/rings_config.ts @@ -0,0 +1,699 @@ +import { RingsInfo, SignAlgorithm } from "../src/types"; + +const tokenInfos = { + development: [ + { + decimals: 18, + name: "Loopring Coin", + symbol: "LRC", + }, + { + decimals: 18, + name: "EOS", + symbol: "EOS", + }, + { + decimals: 18, + name: "Augur Reputation Token", + symbol: "REP", + }, + { + decimals: 18, + name: "Raiden network", + symbol: "RDN", + }, + { + decimals: 18, + name: "gifto", + symbol: "GTO", + }, + { + decimals: 18, + name: "Wrapper Ether", + symbol: "WETH", + }, + ], +}; + + +const tokenSymbols = tokenInfos.development.map((t) => t.symbol); + +export const ringsInfoList: RingsInfo[] = [ + { + description: "single 2-size ring, prices match exactly", + signAlgorithm: SignAlgorithm.Ethereum, + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[0], // use symbol for address, while replace with actual address later. + tokenB: tokenSymbols[1], + amountS: 3e18, + amountB: 1e18, + signAlgorithm: SignAlgorithm.Ethereum, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[0], + amountS: 1e18, + amountB: 3e18, + dualAuthSignAlgorithm: SignAlgorithm.Ethereum, + }, + ], + }, + + { + description: "single 2-size ring, with price gap", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 5e18, + amountB: 45e18, + }, + ], + }, + + { + description: "single 2-size ring, pay fee in WETH", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: "LRC", + tokenB: "GTO", + amountS: 100e18, + amountB: 10e18, + feeToken: "WETH", + feeAmount: 1.0e18, + balanceFee: 1.5e18, + }, + { + index: 1, + tokenS: "GTO", + tokenB: "LRC", + amountS: 10e18, + amountB: 100e18, + }, + ], + }, + + { + description: "single 2-size ring, pay fee in non LRC/WETH token", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: "LRC", + tokenB: "WETH", + amountS: 100e18, + amountB: 10e18, + feeToken: "GTO", + feeAmount: 1.0e18, + balanceFee: 2.0e18, + }, + { + index: 1, + tokenS: "WETH", + tokenB: "LRC", + amountS: 10e18, + amountB: 100e18, + }, + ], + }, + + { + description: "single 2-size ring, no funds available", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[0], + tokenB: tokenSymbols[1], + amountS: 8e18, + amountB: 1e18, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[0], + amountS: 1e18, + amountB: 8e18, + balanceS: 0, + }, + ], + }, + + { + description: "single 2-size ring, insufficient fee funds available (tokenB fallback)", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: "WETH", + tokenB: "GTO", + amountS: 8e18, + amountB: 1e18, + }, + { + index: 1, + tokenS: "GTO", + tokenB: "WETH", + amountS: 1e18, + amountB: 8e18, + feeToken: "LRC", + feeAmount: 0.5e18, + balanceFee: 0, + }, + ], + }, + + { + description: "single 2-size ring, token sold also used for paying fees (sufficient funds)", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: "WETH", + tokenB: "LRC", + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: "LRC", + tokenB: "WETH", + amountS: 10e18, + amountB: 100e18, + feeToken: "LRC", + feeAmount: 1.0e18, + balanceS: 10.0e18, + balanceFee: 1.01e18, + }, + ], + }, + + { + description: "single 2-size ring, token sold also used for paying fees (insufficient funds)", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: "WETH", + tokenB: "LRC", + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: "LRC", + tokenB: "WETH", + amountS: 10e18, + amountB: 100e18, + feeToken: "LRC", + feeAmount: 1.0e18, + balanceS: 7.0e18, + balanceFee: 0.5e18, + }, + ], + }, + + { + description: "self-trading in 2-size ring", + rings: [[0, 1]], + orders: [ + { + index: 0, + ownerIndex: 0, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 3e18, + amountB: 1e18, + feeAmount: 1e18, + balanceFee: 1.1e18, + }, + { + index: 1, + ownerIndex: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 1e18, + amountB: 3e18, + feeAmount: 1e18, + balanceFee: 1.1e18, + }, + ], + }, + + { + description: "simple single 3-size ring, prices match exactly", + rings: [[0, 1, 2]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 10e18, + amountB: 100e18, + }, + { + index: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 100e18, + amountB: 5e18, + }, + { + index: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[1], + amountS: 5e18, + amountB: 10e18, + }, + ], + }, + + { + description: "simple single 3-size ring, with price gap", + rings: [[0, 1, 2]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 5e18, + amountB: 45e18, + }, + { + index: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[1], + amountS: 3e18, + amountB: 2e18, + }, + ], + }, + + { + description: "simple single 3-size ring, miner waives fees for order", + rings: [[0, 1, 2]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 10e18, + amountB: 45e18, + }, + { + index: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[1], + amountS: 45e18, + amountB: 100e18, + feeAmount: 1e18, + waiveFeePercentage: 660, // = 66% + }, + ], + }, + + { + description: "simple single 3-size ring, miner splits fees with order", + rings: [[0, 1, 2]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 10e18, + amountB: 45e18, + feeAmount: 1e18, + waiveFeePercentage: -330, // = -33% + }, + { + index: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[1], + amountS: 45e18, + amountB: 100e18, + feeAmount: 1e18, + waiveFeePercentage: 250, // = 25% + }, + ], + }, + + { + description: "simple single 3-size ring, cannot be settled", + rings: [[0, 1, 2]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 5e18, + amountB: 45e18, + }, + { + index: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[1], + amountS: 2e18, + amountB: 3e18, + }, + ], + }, + + { + description: "multiple 2-size ring, prices match exactly", + rings: [[0, 1], [2, 3]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[0], + tokenB: tokenSymbols[1], + amountS: 3e18, + amountB: 1e18, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[0], + amountS: 1e18, + amountB: 3e18, + }, + { + index: 2, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 3, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[2], + amountS: 5e18, + amountB: 50e18, + }, + ], + }, + + { + description: "multiple 2-size ring, same owner in different orders", + rings: [[0, 1], [2, 3]], + orders: [ + { + index: 0, + ownerIndex: 0, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 3e18, + amountB: 1e18, + }, + { + index: 1, + ownerIndex: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 1e18, + amountB: 3e18, + feeAmount: 1e18, + balanceFee: 1.1e18, + }, + { + index: 2, + ownerIndex: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[3], + amountS: 100e18, + amountB: 10e18, + feeAmount: 1e18, + balanceFee: 1.1e18, + }, + { + index: 3, + ownerIndex: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[2], + amountS: 10e18, + amountB: 100e18, + }, + ], + }, + + { + description: "multiple 2-size rings, share the same order, prices match exactly", + rings: [[0, 1], [0, 2]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 5e18, + amountB: 45e18, + }, + { + index: 2, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 5e18, + amountB: 45e18, + }, + ], + }, + + { + description: "P2P: single 2-size ring, prices match exactly", + transactionOriginOwnerIndex: 1, + rings: [[0, 1]], + orders: [ + { + index: 0, + ownerIndex: 0, + tokenS: tokenSymbols[0], + tokenB: tokenSymbols[1], + amountS: 100e18, + amountB: 1e18, + tokenSFeePercentage: 15, // == 1.5% + }, + { + index: 1, + ownerIndex: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[0], + amountS: 0.01e18, + amountB: 1e18, + tokenBFeePercentage: 25, // == 2.5% + }, + ], + }, + + { + description: "P2P: single 2-size ring, with price gap", + transactionOriginOwnerIndex: 1, + rings: [[0, 1]], + orders: [ + { + index: 0, + ownerIndex: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 100e18, + amountB: 1e18, + tokenSFeePercentage: 15, // == 1.5% + }, + { + index: 1, + ownerIndex: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 0.02e18, + amountB: 1e18, + tokenBFeePercentage: 25, // == 2.5% + }, + ], + }, + + { + description: "P2P: single 2-size ring, insufficient funds", + transactionOriginOwnerIndex: 1, + rings: [[0, 1]], + orders: [ + { + index: 0, + ownerIndex: 0, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[2], + amountS: 100e18, + amountB: 1e18, + tokenBFeePercentage: 15, // == 1.5% + }, + { + index: 1, + ownerIndex: 1, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 0.02e18, + amountB: 1e18, + tokenSFeePercentage: 25, // == 2.5% + balanceS: 0.012e18, + }, + ], + }, + + { + description: "P2P: invalid tokenSFeePercentage", + transactionOriginOwnerIndex: 0, + rings: [[0, 1]], + orders: [ + { + index: 0, + ownerIndex: 0, + tokenS: tokenSymbols[0], + tokenB: tokenSymbols[1], + amountS: 100e18, + amountB: 1e18, + tokenSFeePercentage: 2000, // == 200% + }, + { + index: 1, + ownerIndex: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[0], + amountS: 0.01e18, + amountB: 1e18, + tokenBFeePercentage: 25, // == 2.5% + }, + ], + }, + + { + description: "ring with invalid order", + rings: [[0, 1]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[0], + tokenB: tokenSymbols[1], + amountS: 0, + amountB: 3e18, + feeAmount: 0, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[0], + amountS: 1e18, + amountB: 0, + }, + ], + }, + + { + description: "single order ring", + rings: [[0]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[0], + tokenB: tokenSymbols[0], + amountS: 2e18, + amountB: 2e18, + }, + ], + }, + + { + description: "ring with sub-ring", + rings: [[0, 1, 2, 3, 4, 5]], + orders: [ + { + index: 0, + tokenS: tokenSymbols[2], + tokenB: tokenSymbols[1], + amountS: 100e18, + amountB: 10e18, + }, + { + index: 1, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[3], + amountS: 10e18, + amountB: 50e18, + }, + { + index: 2, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[4], + amountS: 50e18, + amountB: 25e18, + }, + { + index: 3, + tokenS: tokenSymbols[4], + tokenB: tokenSymbols[1], + amountS: 25e18, + amountB: 20e18, + }, + { + index: 4, + tokenS: tokenSymbols[1], + tokenB: tokenSymbols[3], + amountS: 20e18, + amountB: 30e18, + }, + { + index: 5, + tokenS: tokenSymbols[3], + tokenB: tokenSymbols[2], + amountS: 30e18, + amountB: 100e18, + }, + ], + }, +]; diff --git a/test/simulator_test.ts b/test/simulator_test.ts new file mode 100644 index 0000000..636b61f --- /dev/null +++ b/test/simulator_test.ts @@ -0,0 +1,11 @@ +import assert = require("assert"); +import { ringsInfoList } from "./rings_config"; + +describe("protocol-simulator", () => { + + it("simulate and report", () => { + + assert(true, "true"); + }); + +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..54e1bf0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "./node_modules/gts/tsconfig-google.json", + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "sourceMap": true, + "inlineSources": true, + "strictNullChecks": false, + "strict": true, + "strictPropertyInitialization": false, + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "node_modules/web3-typescript-typings/index.d.ts", + "./globals.d.ts", + "src/*.ts", + "src/**/*.ts", + "test/*.ts", + "test/**/*.ts" + ] +}