From 048ac30ebee602a1b2944400e9f54769b427811f Mon Sep 17 00:00:00 2001 From: Iggy Grey Date: Fri, 22 Feb 2019 20:24:05 -0800 Subject: [PATCH 1/3] Added Kyber integration with some tests --- contracts/MyBitBurner.sol | 81 +- contracts/TrustERC721.sol | 6 +- contracts/TrustFactory.sol | 14 +- contracts/kyber/ConversionRates.sol | 373 +++ contracts/kyber/ConversionRatesInterface.sol | 17 + contracts/kyber/ExpectedRate.sol | 123 + contracts/kyber/ExpectedRateInterface.sol | 9 + contracts/kyber/FeeBurner.sol | 167 ++ contracts/kyber/FeeBurnerInterface.sol | 6 + contracts/kyber/KyberNetwork.sol | 652 +++++ contracts/kyber/KyberNetworkInterface.sol | 19 + contracts/kyber/KyberNetworkProxy.sol | 267 +++ contracts/kyber/KyberReserve.sol | 262 ++ contracts/kyber/KyberReserveInterface.sol | 21 + contracts/kyber/MockCentralBank.sol | 40 + contracts/kyber/MockDepositAddress.sol | 37 + contracts/kyber/MockDepositAddressEther.sol | 33 + contracts/kyber/MockDepositAddressToken.sol | 36 + contracts/kyber/MockExchange.sol | 64 + contracts/kyber/OrderIdManager.sol | 77 + contracts/kyber/OrderListFactoryInterface.sol | 9 + contracts/kyber/OrderListInterface.sol | 18 + contracts/kyber/OrderbookReserve.sol | 1018 ++++++++ contracts/kyber/OrderbookReserveInterface.sol | 10 + contracts/kyber/PermissionGroups.sol | 124 + contracts/kyber/SanityRatesInterface.sol | 7 + contracts/kyber/SimpleNetworkInterface.sol | 11 + contracts/kyber/Utils.sol | 64 + contracts/kyber/Utils2.sol | 49 + contracts/kyber/VolumeImbalanceRecorder.sol | 209 ++ contracts/kyber/WhiteList.sol | 59 + contracts/kyber/WhiteListInterface.sol | 5 + contracts/kyber/Withdrawable.sol | 35 + contracts/kyber/Wrapper.sol | 176 ++ contracts/token/BurnableERC20.sol | 13 +- contracts/token/ERC20.sol | 195 +- contracts/token/ERC20Interface.sol | 16 - contracts/token/{IERC721.sol => ERC721.sol} | 22 +- contracts/token/SampleERC20.sol | 191 ++ contracts/token/SampleERC721.sol | 4 +- migrations/2_deploy_trust.js | 148 +- networks/development/ERC20.js | 348 +++ networks/development/MyBitBurner.js | 254 ++ networks/development/MyBitToken.js | 348 +++ networks/development/NonFungibleToken.js | 263 ++ networks/development/TrustFactory.js | 286 +++ networks/development/accounts.json | 20 +- networks/development/contracts.json | 9 +- package-lock.json | 2132 +++++++++++++++++ package.json | 2 +- test/BurnableToken.js | 40 +- test/Kyber.js | 1069 +++++++++ test/MyBitBurner.js | 29 +- test/testERC20.js | 91 +- test/testERC721.js | 45 +- test/{test.js => testETH.js} | 68 +- truffle.js | 12 +- 57 files changed, 9245 insertions(+), 458 deletions(-) create mode 100644 contracts/kyber/ConversionRates.sol create mode 100644 contracts/kyber/ConversionRatesInterface.sol create mode 100644 contracts/kyber/ExpectedRate.sol create mode 100644 contracts/kyber/ExpectedRateInterface.sol create mode 100644 contracts/kyber/FeeBurner.sol create mode 100644 contracts/kyber/FeeBurnerInterface.sol create mode 100644 contracts/kyber/KyberNetwork.sol create mode 100644 contracts/kyber/KyberNetworkInterface.sol create mode 100644 contracts/kyber/KyberNetworkProxy.sol create mode 100644 contracts/kyber/KyberReserve.sol create mode 100644 contracts/kyber/KyberReserveInterface.sol create mode 100644 contracts/kyber/MockCentralBank.sol create mode 100644 contracts/kyber/MockDepositAddress.sol create mode 100644 contracts/kyber/MockDepositAddressEther.sol create mode 100644 contracts/kyber/MockDepositAddressToken.sol create mode 100644 contracts/kyber/MockExchange.sol create mode 100644 contracts/kyber/OrderIdManager.sol create mode 100644 contracts/kyber/OrderListFactoryInterface.sol create mode 100644 contracts/kyber/OrderListInterface.sol create mode 100644 contracts/kyber/OrderbookReserve.sol create mode 100644 contracts/kyber/OrderbookReserveInterface.sol create mode 100644 contracts/kyber/PermissionGroups.sol create mode 100644 contracts/kyber/SanityRatesInterface.sol create mode 100644 contracts/kyber/SimpleNetworkInterface.sol create mode 100644 contracts/kyber/Utils.sol create mode 100644 contracts/kyber/Utils2.sol create mode 100644 contracts/kyber/VolumeImbalanceRecorder.sol create mode 100644 contracts/kyber/WhiteList.sol create mode 100644 contracts/kyber/WhiteListInterface.sol create mode 100644 contracts/kyber/Withdrawable.sol create mode 100644 contracts/kyber/Wrapper.sol delete mode 100644 contracts/token/ERC20Interface.sol rename contracts/token/{IERC721.sol => ERC721.sol} (58%) create mode 100644 contracts/token/SampleERC20.sol create mode 100644 networks/development/ERC20.js create mode 100644 networks/development/MyBitBurner.js create mode 100644 networks/development/MyBitToken.js create mode 100644 networks/development/NonFungibleToken.js create mode 100644 networks/development/TrustFactory.js create mode 100644 package-lock.json create mode 100644 test/Kyber.js rename test/{test.js => testETH.js} (86%) diff --git a/contracts/MyBitBurner.sol b/contracts/MyBitBurner.sol index 518c1dc..15c32f7 100644 --- a/contracts/MyBitBurner.sol +++ b/contracts/MyBitBurner.sol @@ -1,39 +1,100 @@ pragma solidity ^0.4.24; +import "./SafeMath.sol"; import './token/BurnableERC20.sol'; - +import './token/ERC20.sol'; +interface KyberProxy{ + function getExpectedRate(address src, address dest, uint srcQty) external view returns (uint expectedRate, uint slippageRate); + function trade(address src, uint srcAmount, address dest, address destAddress, uint maxDestAmount,uint minConversionRate, address walletId) external payable returns(uint); +} /// @title A contract for burning MYB tokens as usage fee for dapps /// @author Kyle Dewhurst, MyBit Foundation /// @notice Allows Dapps to call this contract to burn MYB as a usage fee /// @dev This contract does not accept tokens. It only burns tokens from users wallets when approved to do so contract MyBitBurner { + using SafeMath for uint256; BurnableERC20 public mybToken; // The instance of the MyBitBurner contract + KyberProxy public kyber; // The interface for trading on Kyber address public owner; // Owner can add or remove authorized contracts + uint256 decimals; mapping (address => bool) public authorizedBurner; // A mapping showing which addresses are allowed to call the burn function // @notice constructor: instantiates myb token address and sets owner // @param (address) _myBitTokenAddress = The MyBit token address - constructor(address _myBitTokenAddress) + constructor(address _myBitTokenAddress, address _kyberAddress) public { mybToken = BurnableERC20(_myBitTokenAddress); + kyber = KyberProxy(_kyberAddress); owner = msg.sender; + uint dec = mybToken.decimals(); + decimals = 10**dec; } // @notice authorized contracts can burn mybit tokens here if the user has approved this contract to do so // @param (address) _tokenHolder = the address of the mybit token holder who wishes to burn _amount of tokens // @param (uint) _amount = the amount of tokens to be burnt (must include decimal places) - function burn(address _tokenHolder, uint _amount) + // @param (address) _burnToken = the address of the token that is being used to pay the fee + function burn(address _tokenHolder, uint _amount, address _burnToken) + payable external returns (bool) { require(authorizedBurner[msg.sender]); - //require(mybToken.allowance(_tokenHolder, address(this)) >= _amount); - require(mybToken.burnFrom(_tokenHolder, _amount)); - emit LogMYBBurned(_tokenHolder, msg.sender, _amount); + if(_burnToken == address(mybToken)){ + require(mybToken.burnFrom(_tokenHolder, _amount)); + emit LogMYBBurned(_tokenHolder, msg.sender, _amount); + } else { + //Calculate the estimate cost of given ERC20 to get convert to correct amount of platform token + (uint expectedRate, uint minRate) = kyber.getExpectedRate(_burnToken, address(mybToken), 0); + uint estimatedCost = expectedRate.mul(_amount).div(decimals); + if(_burnToken == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)){ + //Ether was chosen as the burn token + require(msg.value >= estimatedCost, 'Not enough funds'); + convert(_tokenHolder, _burnToken, address(mybToken), msg.value, _amount, minRate, true); + } else { + ERC20 burnToken = ERC20(_burnToken); + //Transfer burn token from the user + require(burnToken.transferFrom(_tokenHolder, address(this), estimatedCost)); + // Mitigate ERC20 Approve front-running attack, by initially setting + // allowance to 0 + require(burnToken.approve(address(kyber), 0)); + // Approve tokens so network can take them during the swap + burnToken.approve(address(kyber), estimatedCost); + convert(_tokenHolder, _burnToken, address(mybToken), estimatedCost, _amount, minRate, false); + } + //Get amount of the platform token held by this contract (in case it differs from the _amount parameter) + uint amount = mybToken.balanceOf(this); + //Burn the platform token + require(mybToken.burn(amount)); + emit LogMYBBurned(_tokenHolder, msg.sender, amount); + } return true; } + function convert(address _user, address _from, address _to, uint _amount, uint _max, uint _minRate, bool _eth) + private + returns (uint){ + uint balanceBefore; + uint change; + emit LogTrade(_from, _amount, _to, address(this), _max, _minRate, address(0)); + if(_eth == true){ + require(_amount <= address(this).balance, "Not enough funds in contract"); + balanceBefore = address(this).balance; + kyber.trade.value(_amount)(_from, _amount, _to, address(this), _max, _minRate, 0); + change = _amount.sub(balanceBefore.sub(address(this).balance)); + _user.transfer(change); + } else { + ERC20 erc20 = ERC20(_from); + balanceBefore = erc20.balanceOf(this); + kyber.trade(_from, _amount, _to, address(this), _max, _minRate, 0); + change = _amount.sub(balanceBefore.sub(erc20.balanceOf(this))); + erc20.transfer(_user, change); + } + return change; + } + + // @notice owner can authorize a contract to burn MyBit here // @param the address of the mybit dapp contract function authorizeBurner(address _burningContract) @@ -58,13 +119,6 @@ contract MyBitBurner { return true; } - // @notice fallback function. Rejects all ether - function () - payable - external { - revert(); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Modifiers ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -78,4 +132,5 @@ contract MyBitBurner { event LogMYBBurned(address indexed _tokenHolder, address indexed _burningContract, uint _amount); event LogBurnerAuthorized(address _owner, address _burningContract); event LogBurnerRemoved(address _owner, address _burningContract); + event LogTrade(address src, uint amount, address dest, address receiver, uint max, uint minRate, address walletID); } diff --git a/contracts/TrustERC721.sol b/contracts/TrustERC721.sol index 7a6ef41..3a28767 100644 --- a/contracts/TrustERC721.sol +++ b/contracts/TrustERC721.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import './SafeMath.sol'; -import './token/IERC721.sol'; +import './token/ERC721.sol'; // @title Trust contract // @author Yossi Pik @@ -17,7 +17,7 @@ contract TrustERC721 { uint public expiration; // Number of seconds until trust expires - IERC721 public token; // The token to be used for the trust + ERC721 public token; // The token to be used for the trust uint public trustTokenId; // token id intended for beneficiary @@ -38,7 +38,7 @@ contract TrustERC721 { beneficiary = _beneficiary; revocable = _revocable; expiration = block.timestamp.add(_expiration); - token = IERC721(_tokenContractAddress); + token = ERC721(_tokenContractAddress); } // @notice (payable) trustor can deposit tokens here once diff --git a/contracts/TrustFactory.sol b/contracts/TrustFactory.sol index 0d083f0..45a77b8 100644 --- a/contracts/TrustFactory.sol +++ b/contracts/TrustFactory.sol @@ -16,7 +16,7 @@ contract TrustFactory { MyBitBurner public mybBurner; // The MyBitBurner contract instance - uint public mybFee = uint256(250 * 10**18); // How much MYB to burn in order to create a Trust + uint public mybFee = uint256(250*10**18); // How much MYB to burn in order to create a Trust // @notice constructor: sets msg.sender as the owner, who has authority to close the factory constructor(address _mybTokenBurner) @@ -29,12 +29,12 @@ contract TrustFactory { // @param (address) _beneficiary = The address who is to receive ETH from Trust // @param (bool) _revokeable = Whether or not trustor is able to revoke contract or change _beneficiary // @param (uint) _expiration = Number of seconds until Trust expires - function deployTrust(address _beneficiary, bool _revokeable, uint _expiration) + function deployTrust(address _beneficiary, bool _revokeable, uint _expiration, address _burnToken) external payable { require(msg.value > 0); require(!expired); - require(mybBurner.burn(msg.sender, mybFee)); + require(mybBurner.burn(msg.sender, mybFee, _burnToken)); Trust newTrust = new Trust(msg.sender, _beneficiary, _revokeable, _expiration); newTrust.depositTrust.value(msg.value)(); emit LogNewTrust(msg.sender, _beneficiary, address(newTrust), msg.value); @@ -45,11 +45,11 @@ contract TrustFactory { // @param (bool) _revokeable = Whether or not trustor is able to revoke contract or change _beneficiary // @param (uint) _expiration = Number of seconds until Trust expires // @param (address) _tokenContractAddress = The address of the contract of the token which should be used for the trust - function createTrustERC20(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress) + function createTrustERC20(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress, address _burnToken) external payable{ require(!expired); - require(mybBurner.burn(msg.sender, mybFee)); + require(mybBurner.burn(msg.sender, mybFee, _burnToken)); TrustERC20 newTrust = new TrustERC20(msg.sender, _beneficiary, _revokeable, _expiration, _tokenContractAddress); emit LogNewTrustERC20(msg.sender, _beneficiary, address(newTrust)); } @@ -59,11 +59,11 @@ contract TrustFactory { // @param (bool) _revokeable = Whether or not trustor is able to revoke contract or change _beneficiary // @param (uint) _expiration = Number of seconds until Trust expires // @param (address) _tokenContractAddress = The address of the contract of the token which should be used for the trust - function createTrustERC721(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress) + function createTrustERC721(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress, address _burnToken) external payable{ require(!expired); - require(mybBurner.burn(msg.sender, mybFee)); + require(mybBurner.burn(msg.sender, mybFee, _burnToken)); TrustERC721 newTrust = new TrustERC721(msg.sender, _beneficiary, _revokeable, _expiration, _tokenContractAddress); emit LogNewTrustERC721(msg.sender, _beneficiary, address(newTrust)); } diff --git a/contracts/kyber/ConversionRates.sol b/contracts/kyber/ConversionRates.sol new file mode 100644 index 0000000..5fc5770 --- /dev/null +++ b/contracts/kyber/ConversionRates.sol @@ -0,0 +1,373 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./VolumeImbalanceRecorder.sol"; +import "./Utils.sol"; +import "./ConversionRatesInterface.sol"; + +contract ConversionRates is ConversionRatesInterface, VolumeImbalanceRecorder, Utils { + + // bps - basic rate steps. one step is 1 / 10000 of the rate. + struct StepFunction { + int[] x; // quantity for each step. Quantity of each step includes previous steps. + int[] y; // rate change per quantity step in bps. + } + + struct TokenData { + bool listed; // was added to reserve + bool enabled; // whether trade is enabled + + // position in the compact data + uint compactDataArrayIndex; + uint compactDataFieldIndex; + + // rate data. base and changes according to quantity and reserve balance. + // generally speaking. Sell rate is 1 / buy rate i.e. the buy in the other direction. + uint baseBuyRate; // in PRECISION units. see KyberConstants + uint baseSellRate; // PRECISION units. without (sell / buy) spread it is 1 / baseBuyRate + StepFunction buyRateQtyStepFunction; // in bps. higher quantity - bigger the rate. + StepFunction sellRateQtyStepFunction;// in bps. higher the qua + StepFunction buyRateImbalanceStepFunction; // in BPS. higher reserve imbalance - bigger the rate. + StepFunction sellRateImbalanceStepFunction; + } + + /* + this is the data for tokenRatesCompactData + but solidity compiler optimizer is sub-optimal, and cannot write this structure in a single storage write + so we represent it as bytes32 and do the byte tricks ourselves. + struct TokenRatesCompactData { + bytes14 buy; // change buy rate of token from baseBuyRate in 10 bps + bytes14 sell; // change sell rate of token from baseSellRate in 10 bps + + uint32 blockNumber; + } */ + uint public validRateDurationInBlocks = 10; // rates are valid for this amount of blocks + ERC20[] internal listedTokens; + mapping(address=>TokenData) internal tokenData; + bytes32[] internal tokenRatesCompactData; + uint public numTokensInCurrentCompactData = 0; + address public reserveContract; + uint constant internal NUM_TOKENS_IN_COMPACT_DATA = 14; + uint constant internal BYTES_14_OFFSET = (2 ** (8 * NUM_TOKENS_IN_COMPACT_DATA)); + uint constant internal MAX_STEPS_IN_FUNCTION = 10; + int constant internal MAX_BPS_ADJUSTMENT = 10 ** 11; // 1B % + int constant internal MIN_BPS_ADJUSTMENT = -100 * 100; // cannot go down by more than 100% + + constructor(address _admin) public VolumeImbalanceRecorder(_admin) + { } // solhint-disable-line no-empty-blocks + + function addToken(ERC20 token) public onlyAdmin { + + require(!tokenData[token].listed); + tokenData[token].listed = true; + listedTokens.push(token); + + if (numTokensInCurrentCompactData == 0) { + tokenRatesCompactData.length++; // add new structure + } + + tokenData[token].compactDataArrayIndex = tokenRatesCompactData.length - 1; + tokenData[token].compactDataFieldIndex = numTokensInCurrentCompactData; + + numTokensInCurrentCompactData = (numTokensInCurrentCompactData + 1) % NUM_TOKENS_IN_COMPACT_DATA; + + setGarbageToVolumeRecorder(token); + + setDecimals(token); + } + + function setCompactData(bytes14[] buy, bytes14[] sell, uint blockNumber, uint[] indices) public onlyOperator { + + require(buy.length == sell.length); + require(indices.length == buy.length); + require(blockNumber <= 0xFFFFFFFF); + + uint bytes14Offset = BYTES_14_OFFSET; + + for (uint i = 0; i < indices.length; i++) { + require(indices[i] < tokenRatesCompactData.length); + uint data = uint(buy[i]) | uint(sell[i]) * bytes14Offset | (blockNumber * (bytes14Offset * bytes14Offset)); + tokenRatesCompactData[indices[i]] = bytes32(data); + } + } + + function setBaseRate( + ERC20[] tokens, + uint[] baseBuy, + uint[] baseSell, + bytes14[] buy, + bytes14[] sell, + uint blockNumber, + uint[] indices + ) + public + onlyOperator + { + require(tokens.length == baseBuy.length); + require(tokens.length == baseSell.length); + require(sell.length == buy.length); + require(sell.length == indices.length); + + for (uint ind = 0; ind < tokens.length; ind++) { + require(tokenData[tokens[ind]].listed); + tokenData[tokens[ind]].baseBuyRate = baseBuy[ind]; + tokenData[tokens[ind]].baseSellRate = baseSell[ind]; + } + + setCompactData(buy, sell, blockNumber, indices); + } + + function setQtyStepFunction( + ERC20 token, + int[] xBuy, + int[] yBuy, + int[] xSell, + int[] ySell + ) + public + onlyOperator + { + require(xBuy.length == yBuy.length); + require(xSell.length == ySell.length); + require(xBuy.length <= MAX_STEPS_IN_FUNCTION); + require(xSell.length <= MAX_STEPS_IN_FUNCTION); + require(tokenData[token].listed); + + tokenData[token].buyRateQtyStepFunction = StepFunction(xBuy, yBuy); + tokenData[token].sellRateQtyStepFunction = StepFunction(xSell, ySell); + } + + function setImbalanceStepFunction( + ERC20 token, + int[] xBuy, + int[] yBuy, + int[] xSell, + int[] ySell + ) + public + onlyOperator + { + require(xBuy.length == yBuy.length); + require(xSell.length == ySell.length); + require(xBuy.length <= MAX_STEPS_IN_FUNCTION); + require(xSell.length <= MAX_STEPS_IN_FUNCTION); + require(tokenData[token].listed); + + tokenData[token].buyRateImbalanceStepFunction = StepFunction(xBuy, yBuy); + tokenData[token].sellRateImbalanceStepFunction = StepFunction(xSell, ySell); + } + + function setValidRateDurationInBlocks(uint duration) public onlyAdmin { + validRateDurationInBlocks = duration; + } + + function enableTokenTrade(ERC20 token) public onlyAdmin { + require(tokenData[token].listed); + require(tokenControlInfo[token].minimalRecordResolution != 0); + tokenData[token].enabled = true; + } + + function disableTokenTrade(ERC20 token) public onlyAlerter { + require(tokenData[token].listed); + tokenData[token].enabled = false; + } + + function setReserveAddress(address reserve) public onlyAdmin { + reserveContract = reserve; + } + + function recordImbalance( + ERC20 token, + int buyAmount, + uint rateUpdateBlock, + uint currentBlock + ) + public + { + require(msg.sender == reserveContract); + + if (rateUpdateBlock == 0) rateUpdateBlock = getRateUpdateBlock(token); + + return addImbalance(token, buyAmount, rateUpdateBlock, currentBlock); + } + + /* solhint-disable function-max-lines */ + function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint) { + // check if trade is enabled + if (!tokenData[token].enabled) return 0; + if (tokenControlInfo[token].minimalRecordResolution == 0) return 0; // token control info not set + + // get rate update block + bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex]; + + uint updateRateBlock = getLast4Bytes(compactData); + if (currentBlockNumber >= updateRateBlock + validRateDurationInBlocks) return 0; // rate is expired + // check imbalance + int totalImbalance; + int blockImbalance; + (totalImbalance, blockImbalance) = getImbalance(token, updateRateBlock, currentBlockNumber); + + // calculate actual rate + int imbalanceQty; + int extraBps; + int8 rateUpdate; + uint rate; + + if (buy) { + // start with base rate + rate = tokenData[token].baseBuyRate; + + // add rate update + rateUpdate = getRateByteFromCompactData(compactData, token, true); + extraBps = int(rateUpdate) * 10; + rate = addBps(rate, extraBps); + + // compute token qty + qty = getTokenQty(token, rate, qty); + imbalanceQty = int(qty); + totalImbalance += imbalanceQty; + + // add qty overhead + extraBps = executeStepFunction(tokenData[token].buyRateQtyStepFunction, int(qty)); + rate = addBps(rate, extraBps); + + // add imbalance overhead + extraBps = executeStepFunction(tokenData[token].buyRateImbalanceStepFunction, totalImbalance); + rate = addBps(rate, extraBps); + } else { + // start with base rate + rate = tokenData[token].baseSellRate; + + // add rate update + rateUpdate = getRateByteFromCompactData(compactData, token, false); + extraBps = int(rateUpdate) * 10; + rate = addBps(rate, extraBps); + + // compute token qty + imbalanceQty = -1 * int(qty); + totalImbalance += imbalanceQty; + + // add qty overhead + extraBps = executeStepFunction(tokenData[token].sellRateQtyStepFunction, int(qty)); + rate = addBps(rate, extraBps); + + // add imbalance overhead + extraBps = executeStepFunction(tokenData[token].sellRateImbalanceStepFunction, totalImbalance); + rate = addBps(rate, extraBps); + } + + if (abs(totalImbalance) >= getMaxTotalImbalance(token)) return 0; + if (abs(blockImbalance + imbalanceQty) >= getMaxPerBlockImbalance(token)) return 0; + + return rate; + } + /* solhint-enable function-max-lines */ + + function getBasicRate(ERC20 token, bool buy) public view returns(uint) { + if (buy) + return tokenData[token].baseBuyRate; + else + return tokenData[token].baseSellRate; + } + + function getCompactData(ERC20 token) public view returns(uint, uint, byte, byte) { + require(tokenData[token].listed); + + uint arrayIndex = tokenData[token].compactDataArrayIndex; + uint fieldOffset = tokenData[token].compactDataFieldIndex; + + return ( + arrayIndex, + fieldOffset, + byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, true)), + byte(getRateByteFromCompactData(tokenRatesCompactData[arrayIndex], token, false)) + ); + } + + function getTokenBasicData(ERC20 token) public view returns(bool, bool) { + return (tokenData[token].listed, tokenData[token].enabled); + } + + /* solhint-disable code-complexity */ + function getStepFunctionData(ERC20 token, uint command, uint param) public view returns(int) { + if (command == 0) return int(tokenData[token].buyRateQtyStepFunction.x.length); + if (command == 1) return tokenData[token].buyRateQtyStepFunction.x[param]; + if (command == 2) return int(tokenData[token].buyRateQtyStepFunction.y.length); + if (command == 3) return tokenData[token].buyRateQtyStepFunction.y[param]; + + if (command == 4) return int(tokenData[token].sellRateQtyStepFunction.x.length); + if (command == 5) return tokenData[token].sellRateQtyStepFunction.x[param]; + if (command == 6) return int(tokenData[token].sellRateQtyStepFunction.y.length); + if (command == 7) return tokenData[token].sellRateQtyStepFunction.y[param]; + + if (command == 8) return int(tokenData[token].buyRateImbalanceStepFunction.x.length); + if (command == 9) return tokenData[token].buyRateImbalanceStepFunction.x[param]; + if (command == 10) return int(tokenData[token].buyRateImbalanceStepFunction.y.length); + if (command == 11) return tokenData[token].buyRateImbalanceStepFunction.y[param]; + + if (command == 12) return int(tokenData[token].sellRateImbalanceStepFunction.x.length); + if (command == 13) return tokenData[token].sellRateImbalanceStepFunction.x[param]; + if (command == 14) return int(tokenData[token].sellRateImbalanceStepFunction.y.length); + if (command == 15) return tokenData[token].sellRateImbalanceStepFunction.y[param]; + + revert(); + } + /* solhint-enable code-complexity */ + + function getRateUpdateBlock(ERC20 token) public view returns(uint) { + bytes32 compactData = tokenRatesCompactData[tokenData[token].compactDataArrayIndex]; + return getLast4Bytes(compactData); + } + + function getListedTokens() public view returns(ERC20[]) { + return listedTokens; + } + + function getTokenQty(ERC20 token, uint ethQty, uint rate) internal view returns(uint) { + uint dstDecimals = getDecimals(token); + uint srcDecimals = ETH_DECIMALS; + + return calcDstQty(ethQty, srcDecimals, dstDecimals, rate); + } + + function getLast4Bytes(bytes32 b) internal pure returns(uint) { + // cannot trust compiler with not turning bit operations into EXP opcode + return uint(b) / (BYTES_14_OFFSET * BYTES_14_OFFSET); + } + + function getRateByteFromCompactData(bytes32 data, ERC20 token, bool buy) internal view returns(int8) { + uint fieldOffset = tokenData[token].compactDataFieldIndex; + uint byteOffset; + if (buy) + byteOffset = 32 - NUM_TOKENS_IN_COMPACT_DATA + fieldOffset; + else + byteOffset = 4 + fieldOffset; + + return int8(data[byteOffset]); + } + + function executeStepFunction(StepFunction f, int x) internal pure returns(int) { + uint len = f.y.length; + for (uint ind = 0; ind < len; ind++) { + if (x <= f.x[ind]) return f.y[ind]; + } + + return f.y[len-1]; + } + + function addBps(uint rate, int bps) internal pure returns(uint) { + require(rate <= MAX_RATE); + require(bps >= MIN_BPS_ADJUSTMENT); + require(bps <= MAX_BPS_ADJUSTMENT); + + uint maxBps = 100 * 100; + return (rate * uint(int(maxBps) + bps)) / maxBps; + } + + function abs(int x) internal pure returns(uint) { + if (x < 0) + return uint(-1 * x); + else + return uint(x); + } +} diff --git a/contracts/kyber/ConversionRatesInterface.sol b/contracts/kyber/ConversionRatesInterface.sol new file mode 100644 index 0000000..95ae90a --- /dev/null +++ b/contracts/kyber/ConversionRatesInterface.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + + +interface ConversionRatesInterface { + + function recordImbalance( + ERC20 token, + int buyAmount, + uint rateUpdateBlock, + uint currentBlock + ) + external; + + function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) external view returns(uint); +} diff --git a/contracts/kyber/ExpectedRate.sol b/contracts/kyber/ExpectedRate.sol new file mode 100644 index 0000000..234e908 --- /dev/null +++ b/contracts/kyber/ExpectedRate.sol @@ -0,0 +1,123 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./KyberNetwork.sol"; +import "./Withdrawable.sol"; +import "./ExpectedRateInterface.sol"; + + +contract ExpectedRate is Withdrawable, ExpectedRateInterface, Utils2 { + + KyberNetwork public kyberNetwork; + uint public quantityFactor = 2; + uint public worstCaseRateFactorInBps = 50; + uint constant UNIT_QTY_FOR_FEE_BURNER = 10 ** 18; + ERC20 public knc; + + constructor(KyberNetwork _kyberNetwork, ERC20 _knc, address _admin) public { + require(_admin != address(0)); + require(_knc != address(0)); + require(_kyberNetwork != address(0)); + kyberNetwork = _kyberNetwork; + admin = _admin; + knc = _knc; + } + + event QuantityFactorSet (uint newFactor, uint oldFactor, address sender); + + function setQuantityFactor(uint newFactor) public onlyOperator { + require(newFactor <= 100); + + emit QuantityFactorSet(newFactor, quantityFactor, msg.sender); + quantityFactor = newFactor; + } + + event MinSlippageFactorSet (uint newMin, uint oldMin, address sender); + + function setWorstCaseRateFactor(uint bps) public onlyOperator { + require(bps <= 100 * 100); + + emit MinSlippageFactorSet(bps, worstCaseRateFactorInBps, msg.sender); + worstCaseRateFactorInBps = bps; + } + + //@dev when srcQty too small or 0 the expected rate will be calculated without quantity, + // will enable rate reference before committing to any quantity + //@dev when srcQty too small (no actual dest qty) slippage rate will be 0. + function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty, bool usePermissionless) + public view + returns (uint expectedRate, uint slippageRate) + { + require(quantityFactor != 0); + require(srcQty <= MAX_QTY); + require(srcQty * quantityFactor <= MAX_QTY); + + if (srcQty == 0) srcQty = 1; + + uint bestReserve; + uint worstCaseSlippageRate; + + if (usePermissionless) { + (bestReserve, expectedRate) = kyberNetwork.findBestRate(src, dest, srcQty); + + if (quantityFactor != 1) { + (bestReserve, slippageRate) = kyberNetwork.findBestRate(src, dest, (srcQty * quantityFactor)); + } else { + slippageRate = expectedRate; + } + } else { + (bestReserve, expectedRate) = kyberNetwork.findBestRateOnlyPermission(src, dest, srcQty); + + if (quantityFactor != 1) { + (bestReserve, slippageRate) = kyberNetwork.findBestRateOnlyPermission(src, dest, + (srcQty * quantityFactor)); + } else { + slippageRate = expectedRate; + } + } + + if (expectedRate == 0) { + expectedRate = expectedRateSmallQty(src, dest, srcQty, usePermissionless); + } + + if (src == knc && + dest == ETH_TOKEN_ADDRESS && + srcQty == UNIT_QTY_FOR_FEE_BURNER ) + { + if (checkKncArbitrageRate(expectedRate)) expectedRate = 0; + } + + require(expectedRate <= MAX_RATE); + + worstCaseSlippageRate = ((10000 - worstCaseRateFactorInBps) * expectedRate) / 10000; + if (slippageRate >= worstCaseSlippageRate) { + slippageRate = worstCaseSlippageRate; + } + + return (expectedRate, slippageRate); + } + + function checkKncArbitrageRate(uint currentKncToEthRate) public view returns(bool) { + uint converseRate; + uint slippage; + (converseRate, slippage) = getExpectedRate(ETH_TOKEN_ADDRESS, knc, UNIT_QTY_FOR_FEE_BURNER, true); + require(converseRate <= MAX_RATE && currentKncToEthRate <= MAX_RATE); + return ((converseRate * currentKncToEthRate) > (PRECISION ** 2)); + } + + //@dev for small src quantities dest qty might be 0, then returned rate is zero. + //@dev for backward compatibility we would like to return non zero rate (correct one) for small src qty + function expectedRateSmallQty(ERC20 src, ERC20 dest, uint srcQty, bool usePermissionless) + internal view returns(uint) + { + address reserve; + uint rateSrcToEth; + uint rateEthToDest; + (reserve, rateSrcToEth) = kyberNetwork.searchBestRate(src, ETH_TOKEN_ADDRESS, srcQty, usePermissionless); + + uint ethQty = calcDestAmount(src, ETH_TOKEN_ADDRESS, srcQty, rateSrcToEth); + + (reserve, rateEthToDest) = kyberNetwork.searchBestRate(ETH_TOKEN_ADDRESS, dest, ethQty, usePermissionless); + return rateSrcToEth * rateEthToDest / PRECISION; + } +} diff --git a/contracts/kyber/ExpectedRateInterface.sol b/contracts/kyber/ExpectedRateInterface.sol new file mode 100644 index 0000000..54a1fce --- /dev/null +++ b/contracts/kyber/ExpectedRateInterface.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + + +interface ExpectedRateInterface { + function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty, bool usePermissionless) external view + returns (uint expectedRate, uint slippageRate); +} diff --git a/contracts/kyber/FeeBurner.sol b/contracts/kyber/FeeBurner.sol new file mode 100644 index 0000000..483cf29 --- /dev/null +++ b/contracts/kyber/FeeBurner.sol @@ -0,0 +1,167 @@ +pragma solidity ^0.4.24; + +import "./FeeBurnerInterface.sol"; +import "./Withdrawable.sol"; +import "./Utils2.sol"; +import "./KyberNetworkInterface.sol"; + + +interface BurnableToken { + function transferFrom(address _from, address _to, uint _value) external returns (bool); + function burnFrom(address _from, uint256 _value) external returns (bool); +} + + +contract FeeBurner is Withdrawable, FeeBurnerInterface, Utils2 { + + mapping(address=>uint) public reserveFeesInBps; + mapping(address=>address) public reserveKNCWallet; //wallet holding knc per reserve. from here burn and send fees. + mapping(address=>uint) public walletFeesInBps; // wallet that is the source of tx is entitled so some fees. + mapping(address=>uint) public reserveFeeToBurn; + mapping(address=>uint) public feePayedPerReserve; // track burned fees and sent wallet fees per reserve. + mapping(address=>mapping(address=>uint)) public reserveFeeToWallet; + address public taxWallet; + uint public taxFeeBps = 0; // burned fees are taxed. % out of burned fees. + + BurnableToken public knc; + KyberNetworkInterface public kyberNetwork; + uint public kncPerEthRatePrecision = 600 * PRECISION; //--> 1 ether = 600 knc tokens + + constructor( + address _admin, + BurnableToken _kncToken, + KyberNetworkInterface _kyberNetwork, + uint _initialKncToEthRatePrecision + ) + public + { + require(_admin != address(0)); + require(_kncToken != address(0)); + require(_kyberNetwork != address(0)); + require(_initialKncToEthRatePrecision != 0); + + kyberNetwork = _kyberNetwork; + admin = _admin; + knc = _kncToken; + kncPerEthRatePrecision = _initialKncToEthRatePrecision; + } + + event ReserveDataSet(address reserve, uint feeInBps, address kncWallet); + + function setReserveData(address reserve, uint feesInBps, address kncWallet) public onlyOperator { + require(feesInBps < 100); // make sure it is always < 1% + require(kncWallet != address(0)); + reserveFeesInBps[reserve] = feesInBps; + reserveKNCWallet[reserve] = kncWallet; + emit ReserveDataSet(reserve, feesInBps, kncWallet); + } + + event WalletFeesSet(address wallet, uint feesInBps); + + function setWalletFees(address wallet, uint feesInBps) public onlyAdmin { + require(feesInBps < 10000); // under 100% + walletFeesInBps[wallet] = feesInBps; + emit WalletFeesSet(wallet, feesInBps); + } + + event TaxFeesSet(uint feesInBps); + + function setTaxInBps(uint _taxFeeBps) public onlyAdmin { + require(_taxFeeBps < 10000); // under 100% + taxFeeBps = _taxFeeBps; + emit TaxFeesSet(_taxFeeBps); + } + + event TaxWalletSet(address taxWallet); + + function setTaxWallet(address _taxWallet) public onlyAdmin { + require(_taxWallet != address(0)); + taxWallet = _taxWallet; + emit TaxWalletSet(_taxWallet); + } + + event KNCRateSet(uint ethToKncRatePrecision, uint kyberEthKnc, uint kyberKncEth, address updater); + + function setKNCRate() public { + //query kyber for knc rate sell and buy + uint kyberEthKncRate; + uint kyberKncEthRate; + (kyberEthKncRate, ) = kyberNetwork.getExpectedRate(ETH_TOKEN_ADDRESS, ERC20(knc), (10 ** 18)); + (kyberKncEthRate, ) = kyberNetwork.getExpectedRate(ERC20(knc), ETH_TOKEN_ADDRESS, (10 ** 18)); + + //check "reasonable" spread == diff not too big. rate wasn't tampered. + require(kyberEthKncRate * kyberKncEthRate < PRECISION ** 2 * 2); + require(kyberEthKncRate * kyberKncEthRate > PRECISION ** 2 / 2); + + require(kyberEthKncRate <= MAX_RATE); + kncPerEthRatePrecision = kyberEthKncRate; + emit KNCRateSet(kncPerEthRatePrecision, kyberEthKncRate, kyberKncEthRate, msg.sender); + } + + event AssignFeeToWallet(address reserve, address wallet, uint walletFee); + event AssignBurnFees(address reserve, uint burnFee); + + function handleFees(uint tradeWeiAmount, address reserve, address wallet) public returns(bool) { + require(msg.sender == address(kyberNetwork)); + require(tradeWeiAmount <= MAX_QTY); + + uint kncAmount = calcDestAmount(ETH_TOKEN_ADDRESS, ERC20(knc), tradeWeiAmount, kncPerEthRatePrecision); + uint fee = kncAmount * reserveFeesInBps[reserve] / 10000; + + uint walletFee = fee * walletFeesInBps[wallet] / 10000; + require(fee >= walletFee); + uint feeToBurn = fee - walletFee; + + if (walletFee > 0) { + reserveFeeToWallet[reserve][wallet] += walletFee; + emit AssignFeeToWallet(reserve, wallet, walletFee); + } + + if (feeToBurn > 0) { + emit AssignBurnFees(reserve, feeToBurn); + reserveFeeToBurn[reserve] += feeToBurn; + } + + return true; + } + + event BurnAssignedFees(address indexed reserve, address sender, uint quantity); + + event SendTaxFee(address indexed reserve, address sender, address taxWallet, uint quantity); + + // this function is callable by anyone + function burnReserveFees(address reserve) public { + uint burnAmount = reserveFeeToBurn[reserve]; + uint taxToSend = 0; + require(burnAmount > 2); + reserveFeeToBurn[reserve] = 1; // leave 1 twei to avoid spikes in gas fee + if (taxWallet != address(0) && taxFeeBps != 0) { + taxToSend = (burnAmount - 1) * taxFeeBps / 10000; + require(burnAmount - 1 > taxToSend); + burnAmount -= taxToSend; + if (taxToSend > 0) { + require(knc.transferFrom(reserveKNCWallet[reserve], taxWallet, taxToSend)); + emit SendTaxFee(reserve, msg.sender, taxWallet, taxToSend); + } + } + require(knc.burnFrom(reserveKNCWallet[reserve], burnAmount - 1)); + + //update reserve "payments" so far + feePayedPerReserve[reserve] += (taxToSend + burnAmount - 1); + + emit BurnAssignedFees(reserve, msg.sender, (burnAmount - 1)); + } + + event SendWalletFees(address indexed wallet, address reserve, address sender); + + // this function is callable by anyone + function sendFeeToWallet(address wallet, address reserve) public { + uint feeAmount = reserveFeeToWallet[reserve][wallet]; + require(feeAmount > 1); + reserveFeeToWallet[reserve][wallet] = 1; // leave 1 twei to avoid spikes in gas fee + require(knc.transferFrom(reserveKNCWallet[reserve], wallet, feeAmount - 1)); + + feePayedPerReserve[reserve] += (feeAmount - 1); + emit SendWalletFees(wallet, reserve, msg.sender); + } +} diff --git a/contracts/kyber/FeeBurnerInterface.sol b/contracts/kyber/FeeBurnerInterface.sol new file mode 100644 index 0000000..a645d28 --- /dev/null +++ b/contracts/kyber/FeeBurnerInterface.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.4.24; + +interface FeeBurnerInterface { + function handleFees (uint tradeWeiAmount, address reserve, address wallet) external returns(bool); + function setReserveData(address reserve, uint feesInBps, address kncWallet) external; +} diff --git a/contracts/kyber/KyberNetwork.sol b/contracts/kyber/KyberNetwork.sol new file mode 100644 index 0000000..852ac50 --- /dev/null +++ b/contracts/kyber/KyberNetwork.sol @@ -0,0 +1,652 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./KyberReserveInterface.sol"; +import "./KyberNetworkInterface.sol"; +import "./Withdrawable.sol"; +import "./Utils2.sol"; +import "./WhiteListInterface.sol"; +import "./ExpectedRateInterface.sol"; +import "./FeeBurnerInterface.sol"; + + +/** + * @title Helps contracts guard against reentrancy attacks. + */ +contract ReentrancyGuard { + + /// @dev counter to allow mutex lock with only one SSTORE operation + uint256 private guardCounter = 1; + + /** + * @dev Prevents a function from calling itself, directly or indirectly. + * Calling one `nonReentrant` function from + * another is not supported. Instead, you can implement a + * `private` function doing the actual work, and an `external` + * wrapper marked as `nonReentrant`. + */ + modifier nonReentrant() { + guardCounter += 1; + uint256 localCounter = guardCounter; + _; + require(localCounter == guardCounter); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// @title Kyber Network main contract +contract KyberNetwork is Withdrawable, Utils2, KyberNetworkInterface, ReentrancyGuard { + + bytes public constant PERM_HINT = "PERM"; + uint public constant PERM_HINT_GET_RATE = 1 << 255; // for get rate. bit mask hint. + + uint public negligibleRateDiff = 10; // basic rate steps will be in 0.01% + KyberReserveInterface[] public reserves; + mapping(address=>ReserveType) public reserveType; + WhiteListInterface public whiteListContract; + ExpectedRateInterface public expectedRateContract; + FeeBurnerInterface public feeBurnerContract; + address public kyberNetworkProxyContract; + uint public maxGasPriceValue = 50 * 1000 * 1000 * 1000; // 50 gwei + bool public isEnabled = false; // network is enabled + mapping(bytes32=>uint) public infoFields; // this is only a UI field for external app. + + mapping(address=>address[]) public reservesPerTokenSrc; //reserves supporting token to eth + mapping(address=>address[]) public reservesPerTokenDest;//reserves support eth to token + + enum ReserveType {NONE, PERMISSIONED, PERMISSIONLESS} + bytes internal constant EMPTY_HINT = ""; + + constructor(address _admin) public { + require(_admin != address(0)); + admin = _admin; + } + + event EtherReceival(address indexed sender, uint amount); + + /* solhint-disable no-complex-fallback */ + // To avoid users trying to swap tokens using default payable function. We added this short code + // to verify Ethers will be received only from reserves if transferred without a specific function call. + function() public payable { + require(reserveType[msg.sender] != ReserveType.NONE); + emit EtherReceival(msg.sender, msg.value); + } + /* solhint-enable no-complex-fallback */ + + struct TradeInput { + address trader; + ERC20 src; + uint srcAmount; + ERC20 dest; + address destAddress; + uint maxDestAmount; + uint minConversionRate; + address walletId; + bytes hint; + } + + function tradeWHint( + address trader, + ERC20 src, + uint srcAmount, + ERC20 dest, + address destAddress, + uint maxDestAmount, + uint minConversionRate, + address walletId, + bytes hint + ) + external + nonReentrant + payable + returns(uint) + { + require(msg.sender == kyberNetworkProxyContract); + require((hint.length == 0) || (hint.length == 4)); + + TradeInput memory tradeInput; + + tradeInput.trader = trader; + tradeInput.src = src; + tradeInput.srcAmount = srcAmount; + tradeInput.dest = dest; + tradeInput.destAddress = destAddress; + tradeInput.maxDestAmount = maxDestAmount; + tradeInput.minConversionRate = minConversionRate; + tradeInput.walletId = walletId; + tradeInput.hint = hint; + + return trade(tradeInput); + } + + event AddReserveToNetwork(KyberReserveInterface indexed reserve, bool add, bool isPermissionless); + + /// @notice can be called only by operator + /// @dev add or deletes a reserve to/from the network. + /// @param reserve The reserve address. + /// @param isPermissionless is the new reserve from permissionless type. + function addReserve(KyberReserveInterface reserve, bool isPermissionless) public onlyOperator + returns(bool) + { + require(reserveType[reserve] == ReserveType.NONE); + reserves.push(reserve); + + reserveType[reserve] = isPermissionless ? ReserveType.PERMISSIONLESS : ReserveType.PERMISSIONED; + + emit AddReserveToNetwork(reserve, true, isPermissionless); + + return true; + } + + event RemoveReserveFromNetwork(KyberReserveInterface reserve); + + /// @notice can be called only by operator + /// @dev removes a reserve from Kyber network. + /// @param reserve The reserve address. + /// @param index in reserve array. + function removeReserve(KyberReserveInterface reserve, uint index) public onlyOperator + returns(bool) + { + + require(reserveType[reserve] != ReserveType.NONE); + require(reserves[index] == reserve); + + reserveType[reserve] = ReserveType.NONE; + reserves[index] = reserves[reserves.length - 1]; + reserves.length--; + + emit RemoveReserveFromNetwork(reserve); + + return true; + } + + event ListReservePairs(address indexed reserve, ERC20 src, ERC20 dest, bool add); + + /// @notice can be called only by operator + /// @dev allow or prevent a specific reserve to trade a pair of tokens + /// @param reserve The reserve address. + /// @param token token address + /// @param ethToToken will it support ether to token trade + /// @param tokenToEth will it support token to ether trade + /// @param add If true then list this pair, otherwise unlist it. + function listPairForReserve(address reserve, ERC20 token, bool ethToToken, bool tokenToEth, bool add) + public + onlyOperator + returns(bool) + { + require(reserveType[reserve] != ReserveType.NONE); + + if (ethToToken) { + listPairs(reserve, token, false, add); + + emit ListReservePairs(reserve, ETH_TOKEN_ADDRESS, token, add); + } + + if (tokenToEth) { + listPairs(reserve, token, true, add); + + if (add) { + require(token.approve(reserve, 2**255)); // approve infinity + } else { + require(token.approve(reserve, 0)); + } + + emit ListReservePairs(reserve, token, ETH_TOKEN_ADDRESS, add); + } + + setDecimals(token); + + return true; + } + + event WhiteListContractSet(WhiteListInterface newContract, WhiteListInterface currentContract); + + ///@param whiteList can be empty + function setWhiteList(WhiteListInterface whiteList) public onlyAdmin { + emit WhiteListContractSet(whiteList, whiteListContract); + whiteListContract = whiteList; + } + + event ExpectedRateContractSet(ExpectedRateInterface newContract, ExpectedRateInterface currentContract); + + function setExpectedRate(ExpectedRateInterface expectedRate) public onlyAdmin { + require(expectedRate != address(0)); + + emit ExpectedRateContractSet(expectedRate, expectedRateContract); + expectedRateContract = expectedRate; + } + + event FeeBurnerContractSet(FeeBurnerInterface newContract, FeeBurnerInterface currentContract); + + function setFeeBurner(FeeBurnerInterface feeBurner) public onlyAdmin { + require(feeBurner != address(0)); + + emit FeeBurnerContractSet(feeBurner, feeBurnerContract); + feeBurnerContract = feeBurner; + } + + event KyberNetwrokParamsSet(uint maxGasPrice, uint negligibleRateDiff); + + function setParams( + uint _maxGasPrice, + uint _negligibleRateDiff + ) + public + onlyAdmin + { + require(_negligibleRateDiff <= 100 * 100); // at most 100% + + maxGasPriceValue = _maxGasPrice; + negligibleRateDiff = _negligibleRateDiff; + emit KyberNetwrokParamsSet(maxGasPriceValue, negligibleRateDiff); + } + + event KyberNetworkSetEnable(bool isEnabled); + + function setEnable(bool _enable) public onlyAdmin { + if (_enable) { + require(feeBurnerContract != address(0)); + require(expectedRateContract != address(0)); + require(kyberNetworkProxyContract != address(0)); + } + isEnabled = _enable; + + emit KyberNetworkSetEnable(isEnabled); + } + + function setInfo(bytes32 field, uint value) public onlyOperator { + infoFields[field] = value; + } + + event KyberProxySet(address proxy, address sender); + + function setKyberProxy(address networkProxy) public onlyAdmin { + require(networkProxy != address(0)); + kyberNetworkProxyContract = networkProxy; + emit KyberProxySet(kyberNetworkProxyContract, msg.sender); + } + + /// @dev returns number of reserves + /// @return number of reserves + function getNumReserves() public view returns(uint) { + return reserves.length; + } + + /// @notice should be called off chain + /// @dev get an array of all reserves + /// @return An array of all reserves + function getReserves() public view returns(KyberReserveInterface[]) { + return reserves; + } + + function maxGasPrice() public view returns(uint) { + return maxGasPriceValue; + } + + function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) + public view + returns(uint expectedRate, uint slippageRate) + { + require(expectedRateContract != address(0)); + bool includePermissionless = true; + + if (srcQty & PERM_HINT_GET_RATE > 0) { + includePermissionless = false; + srcQty = srcQty & ~PERM_HINT_GET_RATE; + } + + return expectedRateContract.getExpectedRate(src, dest, srcQty, includePermissionless); + } + + function getExpectedRateOnlyPermission(ERC20 src, ERC20 dest, uint srcQty) + public view + returns(uint expectedRate, uint slippageRate) + { + require(expectedRateContract != address(0)); + return expectedRateContract.getExpectedRate(src, dest, srcQty, false); + } + + function getUserCapInWei(address user) public view returns(uint) { + if (whiteListContract == address(0)) return (2 ** 255); + return whiteListContract.getUserCapInWei(user); + } + + function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint) { + //future feature + user; + token; + require(false); + } + + struct BestRateResult { + uint rate; + address reserve1; + address reserve2; + uint weiAmount; + uint rateSrcToEth; + uint rateEthToDest; + uint destAmount; + } + + /// @notice use token address ETH_TOKEN_ADDRESS for ether + /// @dev best conversion rate for a pair of tokens, if number of reserves have small differences. randomize + /// @param src Src token + /// @param dest Destination token + /// @return obsolete - used to return best reserve index. not relevant anymore for this API. + function findBestRate(ERC20 src, ERC20 dest, uint srcAmount) public view returns(uint obsolete, uint rate) { + BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount, EMPTY_HINT); + return(0, result.rate); + } + + function findBestRateOnlyPermission(ERC20 src, ERC20 dest, uint srcAmount) + public + view + returns(uint obsolete, uint rate) + { + BestRateResult memory result = findBestRateTokenToToken(src, dest, srcAmount, PERM_HINT); + return(0, result.rate); + } + + function enabled() public view returns(bool) { + return isEnabled; + } + + function info(bytes32 field) public view returns(uint) { + return infoFields[field]; + } + + /* solhint-disable code-complexity */ + // Regarding complexity. Below code follows the required algorithm for choosing a reserve. + // It has been tested, reviewed and found to be clear enough. + //@dev this function always src or dest are ether. can't do token to token + function searchBestRate(ERC20 src, ERC20 dest, uint srcAmount, bool usePermissionless) + public + view + returns(address, uint) + { + uint bestRate = 0; + uint bestReserve = 0; + uint numRelevantReserves = 0; + + //return 1 for ether to ether + if (src == dest) return (reserves[bestReserve], PRECISION); + + address[] memory reserveArr; + + reserveArr = src == ETH_TOKEN_ADDRESS ? reservesPerTokenDest[dest] : reservesPerTokenSrc[src]; + + if (reserveArr.length == 0) return (reserves[bestReserve], bestRate); + + uint[] memory rates = new uint[](reserveArr.length); + uint[] memory reserveCandidates = new uint[](reserveArr.length); + + for (uint i = 0; i < reserveArr.length; i++) { + //list all reserves that have this token. + if (!usePermissionless && reserveType[reserveArr[i]] == ReserveType.PERMISSIONLESS) { + continue; + } + + rates[i] = (KyberReserveInterface(reserveArr[i])).getConversionRate(src, dest, srcAmount, block.number); + + if (rates[i] > bestRate) { + //best rate is highest rate + bestRate = rates[i]; + } + } + + if (bestRate > 0) { + uint smallestRelevantRate = (bestRate * 10000) / (10000 + negligibleRateDiff); + + for (i = 0; i < reserveArr.length; i++) { + if (rates[i] >= smallestRelevantRate) { + reserveCandidates[numRelevantReserves++] = i; + } + } + + if (numRelevantReserves > 1) { + //when encountering small rate diff from bestRate. draw from relevant reserves + bestReserve = reserveCandidates[uint(blockhash(block.number-1)) % numRelevantReserves]; + } else { + bestReserve = reserveCandidates[0]; + } + + bestRate = rates[bestReserve]; + } + + return (reserveArr[bestReserve], bestRate); + } + /* solhint-enable code-complexity */ + + function findBestRateTokenToToken(ERC20 src, ERC20 dest, uint srcAmount, bytes hint) internal view + returns(BestRateResult result) + { + //by default we use permission less reserves + bool usePermissionless = true; + + // if hint in first 4 bytes == 'PERM' only permissioned reserves will be used. + if ((hint.length >= 4) && (keccak256(abi.encodePacked(hint[0], hint[1], hint[2], hint[3])) == keccak256(abi.encodePacked(PERM_HINT)))) { + usePermissionless = false; + } + + (result.reserve1, result.rateSrcToEth) = + searchBestRate(src, ETH_TOKEN_ADDRESS, srcAmount, usePermissionless); + + result.weiAmount = calcDestAmount(src, ETH_TOKEN_ADDRESS, srcAmount, result.rateSrcToEth); + + (result.reserve2, result.rateEthToDest) = + searchBestRate(ETH_TOKEN_ADDRESS, dest, result.weiAmount, usePermissionless); + + result.destAmount = calcDestAmount(ETH_TOKEN_ADDRESS, dest, result.weiAmount, result.rateEthToDest); + + result.rate = calcRateFromQty(srcAmount, result.destAmount, getDecimals(src), getDecimals(dest)); + } + + function listPairs(address reserve, ERC20 token, bool isTokenToEth, bool add) internal { + uint i; + address[] storage reserveArr = reservesPerTokenDest[token]; + + if (isTokenToEth) { + reserveArr = reservesPerTokenSrc[token]; + } + + for (i = 0; i < reserveArr.length; i++) { + if (reserve == reserveArr[i]) { + if (add) { + break; //already added + } else { + //remove + reserveArr[i] = reserveArr[reserveArr.length - 1]; + reserveArr.length--; + break; + } + } + } + + if (add && i == reserveArr.length) { + //if reserve wasn't found add it + reserveArr.push(reserve); + } + } + + event KyberTrade(address indexed trader, ERC20 src, ERC20 dest, uint srcAmount, uint dstAmount, + address destAddress, uint ethWeiValue, address reserve1, address reserve2, bytes hint); + + /* solhint-disable function-max-lines */ + // Most of the lines here are functions calls spread over multiple lines. We find this function readable enough + /// @notice use token address ETH_TOKEN_ADDRESS for ether + /// @dev trade api for kyber network. + /// @param tradeInput structure of trade inputs + function trade(TradeInput tradeInput) internal returns(uint) { + require(isEnabled); + require(tx.gasprice <= maxGasPriceValue); + require(validateTradeInput(tradeInput.src, tradeInput.srcAmount, tradeInput.dest, tradeInput.destAddress)); + + BestRateResult memory rateResult = + findBestRateTokenToToken(tradeInput.src, tradeInput.dest, tradeInput.srcAmount, tradeInput.hint); + + require(rateResult.rate > 0); + require(rateResult.rate < MAX_RATE); + require(rateResult.rate >= tradeInput.minConversionRate); + + uint actualDestAmount; + uint weiAmount; + uint actualSrcAmount; + + (actualSrcAmount, weiAmount, actualDestAmount) = calcActualAmounts(tradeInput.src, + tradeInput.dest, + tradeInput.srcAmount, + tradeInput.maxDestAmount, + rateResult); + + require(getUserCapInWei(tradeInput.trader) >= weiAmount); + require(handleChange(tradeInput.src, tradeInput.srcAmount, actualSrcAmount, tradeInput.trader)); + + require(doReserveTrade( //src to ETH + tradeInput.src, + actualSrcAmount, + ETH_TOKEN_ADDRESS, + this, + weiAmount, + KyberReserveInterface(rateResult.reserve1), + rateResult.rateSrcToEth, + true)); + + require(doReserveTrade( //Eth to dest + ETH_TOKEN_ADDRESS, + weiAmount, + tradeInput.dest, + tradeInput.destAddress, + actualDestAmount, + KyberReserveInterface(rateResult.reserve2), + rateResult.rateEthToDest, + true)); + + if (tradeInput.src != ETH_TOKEN_ADDRESS) //"fake" trade. (ether to ether) - don't burn. + require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve1, tradeInput.walletId)); + if (tradeInput.dest != ETH_TOKEN_ADDRESS) //"fake" trade. (ether to ether) - don't burn. + require(feeBurnerContract.handleFees(weiAmount, rateResult.reserve2, tradeInput.walletId)); + + emit KyberTrade({ + trader: tradeInput.trader, + src: tradeInput.src, + dest: tradeInput.dest, + srcAmount: actualSrcAmount, + dstAmount: actualDestAmount, + destAddress: tradeInput.destAddress, + ethWeiValue: weiAmount, + reserve1: (tradeInput.src == ETH_TOKEN_ADDRESS) ? address(0) : rateResult.reserve1, + reserve2: (tradeInput.dest == ETH_TOKEN_ADDRESS) ? address(0) : rateResult.reserve2, + hint: tradeInput.hint + }); + + return actualDestAmount; + } + /* solhint-enable function-max-lines */ + + function calcActualAmounts (ERC20 src, ERC20 dest, uint srcAmount, uint maxDestAmount, BestRateResult rateResult) + internal view returns(uint actualSrcAmount, uint weiAmount, uint actualDestAmount) + { + if (rateResult.destAmount > maxDestAmount) { + actualDestAmount = maxDestAmount; + weiAmount = calcSrcAmount(ETH_TOKEN_ADDRESS, dest, actualDestAmount, rateResult.rateEthToDest); + actualSrcAmount = calcSrcAmount(src, ETH_TOKEN_ADDRESS, weiAmount, rateResult.rateSrcToEth); + require(actualSrcAmount <= srcAmount); + } else { + actualDestAmount = rateResult.destAmount; + actualSrcAmount = srcAmount; + weiAmount = rateResult.weiAmount; + } + } + + /// @notice use token address ETH_TOKEN_ADDRESS for ether + /// @dev do one trade with a reserve + /// @param src Src token + /// @param amount amount of src tokens + /// @param dest Destination token + /// @param destAddress Address to send tokens to + /// @param reserve Reserve to use + /// @param validate If true, additional validations are applicable + /// @return true if trade is successful + function doReserveTrade( + ERC20 src, + uint amount, + ERC20 dest, + address destAddress, + uint expectedDestAmount, + KyberReserveInterface reserve, + uint conversionRate, + bool validate + ) + internal + returns(bool) + { + uint callValue = 0; + + if (src == dest) { + //this is for a "fake" trade when both src and dest are ethers. + if (destAddress != (address(this))) + destAddress.transfer(amount); + return true; + } + + if (src == ETH_TOKEN_ADDRESS) { + callValue = amount; + } + + // reserve sends tokens/eth to network. network sends it to destination + require(reserve.trade.value(callValue)(src, amount, dest, this, conversionRate, validate)); + + if (destAddress != address(this)) { + //for token to token dest address is network. and Ether / token already here... + if (dest == ETH_TOKEN_ADDRESS) { + destAddress.transfer(expectedDestAmount); + } else { + require(dest.transfer(destAddress, expectedDestAmount)); + } + } + + return true; + } + + /// when user sets max dest amount we could have too many source tokens == change. so we send it back to user. + function handleChange (ERC20 src, uint srcAmount, uint requiredSrcAmount, address trader) internal returns (bool) { + + if (requiredSrcAmount < srcAmount) { + //if there is "change" send back to trader + if (src == ETH_TOKEN_ADDRESS) { + trader.transfer(srcAmount - requiredSrcAmount); + } else { + require(src.transfer(trader, (srcAmount - requiredSrcAmount))); + } + } + + return true; + } + + /// @notice use token address ETH_TOKEN_ADDRESS for ether + /// @dev checks that user sent ether/tokens to contract before trade + /// @param src Src token + /// @param srcAmount amount of src tokens + /// @return true if tradeInput is valid + function validateTradeInput(ERC20 src, uint srcAmount, ERC20 dest, address destAddress) + internal + view + returns(bool) + { + require(srcAmount <= MAX_QTY); + require(srcAmount != 0); + require(destAddress != address(0)); + require(src != dest); + + if (src == ETH_TOKEN_ADDRESS) { + require(msg.value == srcAmount); + } else { + require(msg.value == 0); + //funds should have been moved to this contract already. + require(src.balanceOf(this) >= srcAmount); + } + + return true; + } +} diff --git a/contracts/kyber/KyberNetworkInterface.sol b/contracts/kyber/KyberNetworkInterface.sol new file mode 100644 index 0000000..2f43fc6 --- /dev/null +++ b/contracts/kyber/KyberNetworkInterface.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + + +/// @title Kyber Network interface +interface KyberNetworkInterface { + function maxGasPrice() external view returns(uint); + function getUserCapInWei(address user) external view returns(uint); + function getUserCapInTokenWei(address user, ERC20 token) external view returns(uint); + function enabled() external view returns(bool); + function info(bytes32 id) external view returns(uint); + + function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) external view + returns (uint expectedRate, uint slippageRate); + + function tradeWHint(address trader, ERC20 src, uint srcAmount, ERC20 dest, address destAddress, + uint maxDestAmount, uint minConversionRate, address walletId, bytes hint) external payable returns(uint); +} diff --git a/contracts/kyber/KyberNetworkProxy.sol b/contracts/kyber/KyberNetworkProxy.sol new file mode 100644 index 0000000..06b22ed --- /dev/null +++ b/contracts/kyber/KyberNetworkProxy.sol @@ -0,0 +1,267 @@ +pragma solidity ^0.4.24; + + +import "../token/ERC20.sol"; +import "./Withdrawable.sol"; +import "./Utils2.sol"; +import "./KyberNetworkInterface.sol"; +import "./SimpleNetworkInterface.sol"; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// @title Kyber Network proxy for main contract +contract KyberNetworkProxy is SimpleNetworkInterface, Withdrawable, Utils2 { + + KyberNetworkInterface public kyberNetworkContract; + + constructor(address _admin) public { + require(_admin != address(0)); + admin = _admin; + } + + /// @notice use token address ETH_TOKEN_ADDRESS for ether + /// @dev makes a trade between src and dest token and send dest token to destAddress + /// @param src Src token + /// @param srcAmount amount of src tokens + /// @param dest Destination token + /// @param destAddress Address to send tokens to + /// @param maxDestAmount A limit on the amount of dest tokens + /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. + /// @param walletId is the wallet ID to send part of the fees + /// @return amount of actual dest tokens + function trade( + ERC20 src, + uint srcAmount, + ERC20 dest, + address destAddress, + uint maxDestAmount, + uint minConversionRate, + address walletId + ) + public + payable + returns(uint) + { + bytes memory hint; + + return tradeWithHint( + src, + srcAmount, + dest, + destAddress, + maxDestAmount, + minConversionRate, + walletId, + hint + ); + } + + /// @dev makes a trade between src and dest token and send dest tokens to msg sender + /// @param src Src token + /// @param srcAmount amount of src tokens + /// @param dest Destination token + /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. + /// @return amount of actual dest tokens + function swapTokenToToken( + ERC20 src, + uint srcAmount, + ERC20 dest, + uint minConversionRate + ) + public + returns(uint) + { + bytes memory hint; + + return tradeWithHint( + src, + srcAmount, + dest, + msg.sender, + MAX_QTY, + minConversionRate, + 0, + hint + ); + } + + /// @dev makes a trade from Ether to token. Sends token to msg sender + /// @param token Destination token + /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. + /// @return amount of actual dest tokens + function swapEtherToToken(ERC20 token, uint minConversionRate) public payable returns(uint) { + bytes memory hint; + + return tradeWithHint( + ETH_TOKEN_ADDRESS, + msg.value, + token, + msg.sender, + MAX_QTY, + minConversionRate, + 0, + hint + ); + } + + /// @dev makes a trade from token to Ether, sends Ether to msg sender + /// @param token Src token + /// @param srcAmount amount of src tokens + /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. + /// @return amount of actual dest tokens + function swapTokenToEther(ERC20 token, uint srcAmount, uint minConversionRate) public returns(uint) { + bytes memory hint; + + return tradeWithHint( + token, + srcAmount, + ETH_TOKEN_ADDRESS, + msg.sender, + MAX_QTY, + minConversionRate, + 0, + hint + ); + } + + struct UserBalance { + uint srcBalance; + uint destBalance; + } + + event ExecuteTrade(address indexed trader, ERC20 src, ERC20 dest, uint actualSrcAmount, uint actualDestAmount); + + /// @notice use token address ETH_TOKEN_ADDRESS for ether + /// @dev makes a trade between src and dest token and send dest token to destAddress + /// @param src Src token + /// @param srcAmount amount of src tokens + /// @param dest Destination token + /// @param destAddress Address to send tokens to + /// @param maxDestAmount A limit on the amount of dest tokens + /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. + /// @param walletId is the wallet ID to send part of the fees + /// @param hint will give hints for the trade. + /// @return amount of actual dest tokens + function tradeWithHint( + ERC20 src, + uint srcAmount, + ERC20 dest, + address destAddress, + uint maxDestAmount, + uint minConversionRate, + address walletId, + bytes hint + ) + public + payable + returns(uint) + { + require(src == ETH_TOKEN_ADDRESS || msg.value == 0); + + UserBalance memory userBalanceBefore; + + userBalanceBefore.srcBalance = getBalance(src, msg.sender); + userBalanceBefore.destBalance = getBalance(dest, destAddress); + + if (src == ETH_TOKEN_ADDRESS) { + userBalanceBefore.srcBalance += msg.value; + } else { + require(src.transferFrom(msg.sender, kyberNetworkContract, srcAmount)); + } + + uint reportedDestAmount = kyberNetworkContract.tradeWHint.value(msg.value)( + msg.sender, + src, + srcAmount, + dest, + destAddress, + maxDestAmount, + minConversionRate, + walletId, + hint + ); + + TradeOutcome memory tradeOutcome = calculateTradeOutcome( + userBalanceBefore.srcBalance, + userBalanceBefore.destBalance, + src, + dest, + destAddress + ); + + require(reportedDestAmount == tradeOutcome.userDeltaDestAmount); + require(tradeOutcome.userDeltaDestAmount <= maxDestAmount); + require(tradeOutcome.actualRate >= minConversionRate); + + emit ExecuteTrade(msg.sender, src, dest, tradeOutcome.userDeltaSrcAmount, tradeOutcome.userDeltaDestAmount); + return tradeOutcome.userDeltaDestAmount; + } + + event KyberNetworkSet(address newNetworkContract, address oldNetworkContract); + + function setKyberNetworkContract(KyberNetworkInterface _kyberNetworkContract) public onlyAdmin { + + require(_kyberNetworkContract != address(0)); + + emit KyberNetworkSet(_kyberNetworkContract, kyberNetworkContract); + + kyberNetworkContract = _kyberNetworkContract; + } + + function getExpectedRate(ERC20 src, ERC20 dest, uint srcQty) + public view + returns(uint expectedRate, uint slippageRate) + { + return kyberNetworkContract.getExpectedRate(src, dest, srcQty); + } + + function getUserCapInWei(address user) public view returns(uint) { + return kyberNetworkContract.getUserCapInWei(user); + } + + function getUserCapInTokenWei(address user, ERC20 token) public view returns(uint) { + return kyberNetworkContract.getUserCapInTokenWei(user, token); + } + + function maxGasPrice() public view returns(uint) { + return kyberNetworkContract.maxGasPrice(); + } + + function enabled() public view returns(bool) { + return kyberNetworkContract.enabled(); + } + + function info(bytes32 field) public view returns(uint) { + return kyberNetworkContract.info(field); + } + + struct TradeOutcome { + uint userDeltaSrcAmount; + uint userDeltaDestAmount; + uint actualRate; + } + + function calculateTradeOutcome (uint srcBalanceBefore, uint destBalanceBefore, ERC20 src, ERC20 dest, + address destAddress) + internal returns(TradeOutcome outcome) + { + uint userSrcBalanceAfter; + uint userDestBalanceAfter; + + userSrcBalanceAfter = getBalance(src, msg.sender); + userDestBalanceAfter = getBalance(dest, destAddress); + + //protect from underflow + require(userDestBalanceAfter > destBalanceBefore); + require(srcBalanceBefore > userSrcBalanceAfter); + + outcome.userDeltaDestAmount = userDestBalanceAfter - destBalanceBefore; + outcome.userDeltaSrcAmount = srcBalanceBefore - userSrcBalanceAfter; + + outcome.actualRate = calcRateFromQty( + outcome.userDeltaSrcAmount, + outcome.userDeltaDestAmount, + getDecimalsSafe(src), + getDecimalsSafe(dest) + ); + } +} diff --git a/contracts/kyber/KyberReserve.sol b/contracts/kyber/KyberReserve.sol new file mode 100644 index 0000000..78718b6 --- /dev/null +++ b/contracts/kyber/KyberReserve.sol @@ -0,0 +1,262 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./Utils.sol"; +import "./Withdrawable.sol"; +import "./ConversionRatesInterface.sol"; +import "./SanityRatesInterface.sol"; +import "./KyberReserveInterface.sol"; + + +/// @title Kyber Reserve contract +contract KyberReserve is KyberReserveInterface, Withdrawable, Utils { + + address public kyberNetwork; + bool public tradeEnabled; + ConversionRatesInterface public conversionRatesContract; + SanityRatesInterface public sanityRatesContract; + mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool + mapping(address=>address) public tokenWallet; + + constructor(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public { + require(_admin != address(0)); + require(_ratesContract != address(0)); + require(_kyberNetwork != address(0)); + kyberNetwork = _kyberNetwork; + conversionRatesContract = _ratesContract; + admin = _admin; + tradeEnabled = true; + } + + event DepositToken(ERC20 token, uint amount); + + function() public payable { + emit DepositToken(ETH_TOKEN_ADDRESS, msg.value); + } + + event TradeExecute( + address indexed origin, + address src, + uint srcAmount, + address destToken, + uint destAmount, + address destAddress + ); + + function trade( + ERC20 srcToken, + uint srcAmount, + ERC20 destToken, + address destAddress, + uint conversionRate, + bool validate + ) + public + payable + returns(bool) + { + require(tradeEnabled); + require(msg.sender == kyberNetwork); + + require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate)); + + return true; + } + + event TradeEnabled(bool enable); + + function enableTrade() public onlyAdmin returns(bool) { + tradeEnabled = true; + emit TradeEnabled(true); + + return true; + } + + function disableTrade() public onlyAlerter returns(bool) { + tradeEnabled = false; + emit TradeEnabled(false); + + return true; + } + + event WithdrawAddressApproved(ERC20 token, address addr, bool approve); + + function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin { + approvedWithdrawAddresses[keccak256(abi.encodePacked(token, addr))] = approve; + emit WithdrawAddressApproved(token, addr, approve); + + setDecimals(token); + if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) { + tokenWallet[token] = this; // by default + require(token.approve(this, 2 ** 255)); + } + } + + event NewTokenWallet(ERC20 token, address wallet); + + function setTokenWallet(ERC20 token, address wallet) public onlyAdmin { + require(wallet != address(0x0)); + tokenWallet[token] = wallet; + emit NewTokenWallet(token, wallet); + } + + event WithdrawFunds(ERC20 token, uint amount, address destination); + + function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) { + require(approvedWithdrawAddresses[keccak256(abi.encodePacked(token, destination))]); + + if (token == ETH_TOKEN_ADDRESS) { + destination.transfer(amount); + } else { + require(token.transferFrom(tokenWallet[token], destination, amount)); + } + + emit WithdrawFunds(token, amount, destination); + + return true; + } + + event SetContractAddresses(address network, address rate, address sanity); + + function setContracts( + address _kyberNetwork, + ConversionRatesInterface _conversionRates, + SanityRatesInterface _sanityRates + ) + public + onlyAdmin + { + require(_kyberNetwork != address(0)); + require(_conversionRates != address(0)); + + kyberNetwork = _kyberNetwork; + conversionRatesContract = _conversionRates; + sanityRatesContract = _sanityRates; + + emit SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract); + } + + //////////////////////////////////////////////////////////////////////////// + /// status functions /////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + function getBalance(ERC20 token) public view returns(uint) { + if (token == ETH_TOKEN_ADDRESS) + return address(this).balance; + else { + address wallet = tokenWallet[token]; + uint balanceOfWallet = token.balanceOf(wallet); + uint allowanceOfWallet = token.allowance(wallet, this); + + return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet; + } + } + + function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) { + uint dstDecimals = getDecimals(dest); + uint srcDecimals = getDecimals(src); + + return calcDstQty(srcQty, srcDecimals, dstDecimals, rate); + } + + function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) { + uint dstDecimals = getDecimals(dest); + uint srcDecimals = getDecimals(src); + + return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate); + } + + function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) { + ERC20 token; + bool isBuy; + + if (!tradeEnabled) return 0; + + if (ETH_TOKEN_ADDRESS == src) { + isBuy = true; + token = dest; + } else if (ETH_TOKEN_ADDRESS == dest) { + isBuy = false; + token = src; + } else { + return 0; // pair is not listed + } + + uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty); + uint destQty = getDestQty(src, dest, srcQty, rate); + + if (getBalance(dest) < destQty) return 0; + + if (sanityRatesContract != address(0)) { + uint sanityRate = sanityRatesContract.getSanityRate(src, dest); + if (rate > sanityRate) return 0; + } + + return rate; + } + + /// @dev do a trade + /// @param srcToken Src token + /// @param srcAmount Amount of src token + /// @param destToken Destination token + /// @param destAddress Destination address to send tokens to + /// @param validate If true, additional validations are applicable + /// @return true iff trade is successful + function doTrade( + ERC20 srcToken, + uint srcAmount, + ERC20 destToken, + address destAddress, + uint conversionRate, + bool validate + ) + internal + returns(bool) + { + // can skip validation if done at kyber network level + if (validate) { + require(conversionRate > 0); + if (srcToken == ETH_TOKEN_ADDRESS) + require(msg.value == srcAmount); + else + require(msg.value == 0); + } + + uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate); + // sanity check + require(destAmount > 0); + + // add to imbalance + ERC20 token; + int tradeAmount; + if (srcToken == ETH_TOKEN_ADDRESS) { + tradeAmount = int(destAmount); + token = destToken; + } else { + tradeAmount = -1 * int(srcAmount); + token = srcToken; + } + + conversionRatesContract.recordImbalance( + token, + tradeAmount, + 0, + block.number + ); + + // collect src tokens + if (srcToken != ETH_TOKEN_ADDRESS) { + require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount)); + } + + // send dest tokens + if (destToken == ETH_TOKEN_ADDRESS) { + destAddress.transfer(destAmount); + } else { + require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount)); + } + + emit TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress); + + return true; + } +} diff --git a/contracts/kyber/KyberReserveInterface.sol b/contracts/kyber/KyberReserveInterface.sol new file mode 100644 index 0000000..f2d718d --- /dev/null +++ b/contracts/kyber/KyberReserveInterface.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + +/// @title Kyber Reserve contract +interface KyberReserveInterface { + + function trade( + ERC20 srcToken, + uint srcAmount, + ERC20 destToken, + address destAddress, + uint conversionRate, + bool validate + ) + external + payable + returns(bool); + + function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) external view returns(uint); +} diff --git a/contracts/kyber/MockCentralBank.sol b/contracts/kyber/MockCentralBank.sol new file mode 100644 index 0000000..22e3431 --- /dev/null +++ b/contracts/kyber/MockCentralBank.sol @@ -0,0 +1,40 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + + +/// @title Mock Central Bank +/// @author Yaron Velner +/// @dev a dummy contract that simulates a bank that holds tokens and ether. centralized exchanges can convert tokens here. + + +contract MockCentralBank { + mapping(address=>bool) public owners; + + constructor() public { + owners[msg.sender] = true; + } + + function() payable public { } + + function withdrawEther(uint amount) public { + if (! owners[tx.origin]) revert(); + + msg.sender.transfer(amount); + } + + function withdrawToken(ERC20 token, uint amount) public { + if (!owners[tx.origin]) revert(); + + token.transfer(msg.sender,amount); + } + + function depositEther() public payable { + // just to simplify interaction with testrpc + } + + function addOwner(address newOwner) public { + if ( ! owners[tx.origin] ) revert(); + owners[newOwner] = true; + } +} diff --git a/contracts/kyber/MockDepositAddress.sol b/contracts/kyber/MockDepositAddress.sol new file mode 100644 index 0000000..9d56273 --- /dev/null +++ b/contracts/kyber/MockDepositAddress.sol @@ -0,0 +1,37 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./MockCentralBank.sol"; + +/// @title Mock Deposit Address +/// @author Ilan Doron +/// @dev a dummy contract that simulates a deposit address of a token on a specific exchange. allows reserve manager to deposit and withdraw + + +contract MockDepositAddress { + + MockCentralBank public bank; + address public owner; + + /// @dev Ctor of this + /// @param _bank bank address to work with for deposit and withdraw + /// @param _owner owner address for this contract. + constructor( MockCentralBank _bank, address _owner ) public { + owner = _owner; + bank = _bank; + } + + modifier onlyOwner( ) + { + require(msg.sender == owner); + _; + } + + event Withdraw(uint amount , address destianation); + + function withdraw(uint tokenAmount, address destination) public; + + function clearBalance(uint amount) public; + + function getBalance() public view returns (uint); +} diff --git a/contracts/kyber/MockDepositAddressEther.sol b/contracts/kyber/MockDepositAddressEther.sol new file mode 100644 index 0000000..788fc71 --- /dev/null +++ b/contracts/kyber/MockDepositAddressEther.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.4.24; + +import "./MockCentralBank.sol"; +import "./MockDepositAddress.sol"; + +/// @title Mock Deposit Address for Ethers +/// @author Ilan Doron +/// @dev a dummy contract that simulates a deposit address of a token on a specific exchange. +/// allows reserve manager to deposit and withdraw +contract MockDepositAddressEther is MockDepositAddress{ + + constructor(MockCentralBank _bank, address _owner) + MockDepositAddress(_bank, _owner) + public + {} + + function () public payable {} + + function withdraw(uint etherAmount, address destination) public onlyOwner { + bank.withdrawEther(etherAmount); + destination.transfer(etherAmount); + } + + function clearBalance( uint amount ) public onlyOwner { + if (address(this).balance >= amount) { + address(bank).transfer(amount); + } + } + + function getBalance() public view returns (uint) { + return address(this).balance; + } +} diff --git a/contracts/kyber/MockDepositAddressToken.sol b/contracts/kyber/MockDepositAddressToken.sol new file mode 100644 index 0000000..28094ce --- /dev/null +++ b/contracts/kyber/MockDepositAddressToken.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.4.24; + +import "./MockCentralBank.sol"; +import "./MockDepositAddress.sol"; + +/// @title Mock Deposit Address for token +/// @author Ilan Doron +/// @dev a dummy contract that simulates a deposit address of a token on a specific exchange. allows reserve manager to deposit and withdraw +contract MockDepositAddressToken is MockDepositAddress{ + + ERC20 public token; + + constructor(ERC20 _token, MockCentralBank _bank, address _owner) + MockDepositAddress(_bank, _owner) + public + { + token = _token; + } + + function withdraw(uint tokenAmount, address destination) public onlyOwner { + bank.withdrawToken(token, tokenAmount); + token.transfer(destination, tokenAmount); + } + + function clearBalance(uint amount) public + onlyOwner + { + if (token.balanceOf(this) >= amount) { + token.transfer(bank, amount); + } + } + + function getBalance() public view returns (uint) { + return token.balanceOf(this); + } +} diff --git a/contracts/kyber/MockExchange.sol b/contracts/kyber/MockExchange.sol new file mode 100644 index 0000000..6aaea7e --- /dev/null +++ b/contracts/kyber/MockExchange.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.4.24; + + +import "../token/ERC20.sol"; +import "./MockCentralBank.sol"; +import "./MockDepositAddressEther.sol"; +import "./MockDepositAddressToken.sol"; +import "./Utils.sol"; + + +/// @title Mock Exchange Deposit Address +/// @author Ilan Doron +/// @dev a dummy contract that simulates a deposit address of an exchange. allows user to deposit, withdraw and convert tokens. +contract MockExchange is Utils { + + string public exchangeName; + MockCentralBank public bank; + mapping(address=>bool) public owners; + mapping(address=>MockDepositAddress) public tokenDepositAddresses; + + constructor(string _exchangeName, MockCentralBank _bank) public { + exchangeName = _exchangeName; + bank = _bank; + owners[msg.sender] = true; + } + + modifier onlyOwner() + { + require(owners[msg.sender]); + _; + } + + function withdraw(ERC20 token, uint tokenAmount, address destination) public onlyOwner { + require(tokenDepositAddresses[token] != address(0)); + // withdraw from mockDepositaddress. which withdraws from bank + tokenDepositAddresses[token].withdraw(tokenAmount, destination); + } + + function clearBalances(ERC20[] tokens, uint[] amounts) public onlyOwner { + + for (uint i = 0; i < tokens.length; i++) { + if (tokenDepositAddresses[tokens[i]] == address(0)) continue; + tokenDepositAddresses[tokens[i]].clearBalance(amounts[i]); + } + } + + function getBalance( ERC20 token ) public view returns(uint) { + return tokenDepositAddresses[token].getBalance(); + } + + function addOwner( address newOwner ) public onlyOwner { + owners[newOwner] = true; + } + + function addMockDepositAddress( ERC20 token ) public onlyOwner { + + if (token == ETH_TOKEN_ADDRESS) + tokenDepositAddresses[token] = new MockDepositAddressEther(bank, this); + else + tokenDepositAddresses[token] = new MockDepositAddressToken(token, bank, this); + + bank.addOwner(tokenDepositAddresses[token]); + } +} diff --git a/contracts/kyber/OrderIdManager.sol b/contracts/kyber/OrderIdManager.sol new file mode 100644 index 0000000..62745fa --- /dev/null +++ b/contracts/kyber/OrderIdManager.sol @@ -0,0 +1,77 @@ +pragma solidity ^0.4.24; + + +contract OrderIdManager { + struct OrderIdData { + uint32 firstOrderId; + uint takenBitmap; + } + + uint constant public NUM_ORDERS = 32; + + function fetchNewOrderId(OrderIdData storage freeOrders) + internal + returns(uint32) + { + uint orderBitmap = freeOrders.takenBitmap; + uint bitPointer = 1; + + for (uint i = 0; i < NUM_ORDERS; ++i) { + + if ((orderBitmap & bitPointer) == 0) { + freeOrders.takenBitmap = orderBitmap | bitPointer; + return(uint32(uint(freeOrders.firstOrderId) + i)); + } + + bitPointer *= 2; + } + + revert(); + } + + /// @dev mark order as free to use. + function releaseOrderId(OrderIdData storage freeOrders, uint32 orderId) + internal + returns(bool) + { + require(orderId >= freeOrders.firstOrderId); + require(orderId < (freeOrders.firstOrderId + NUM_ORDERS)); + + uint orderBitNum = uint(orderId) - uint(freeOrders.firstOrderId); + uint bitPointer = uint(1) << orderBitNum; + + require(bitPointer & freeOrders.takenBitmap > 0); + + freeOrders.takenBitmap &= ~bitPointer; + return true; + } + + function allocateOrderIds( + OrderIdData storage makerOrders, + uint32 firstAllocatedId + ) + internal + returns(bool) + { + if (makerOrders.firstOrderId > 0) { + return false; + } + + makerOrders.firstOrderId = firstAllocatedId; + makerOrders.takenBitmap = 0; + + return true; + } + + function orderAllocationRequired(OrderIdData storage freeOrders) internal view returns (bool) { + + if (freeOrders.firstOrderId == 0) return true; + return false; + } + + function getNumActiveOrderIds(OrderIdData storage makerOrders) internal view returns (uint numActiveOrders) { + for (uint i = 0; i < NUM_ORDERS; ++i) { + if ((makerOrders.takenBitmap & (uint(1) << i)) > 0) numActiveOrders++; + } + } +} diff --git a/contracts/kyber/OrderListFactoryInterface.sol b/contracts/kyber/OrderListFactoryInterface.sol new file mode 100644 index 0000000..f0aec64 --- /dev/null +++ b/contracts/kyber/OrderListFactoryInterface.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.24; + + +import "./OrderListInterface.sol"; + + +interface OrderListFactoryInterface { + function newOrdersContract(address admin) external returns(OrderListInterface); +} diff --git a/contracts/kyber/OrderListInterface.sol b/contracts/kyber/OrderListInterface.sol new file mode 100644 index 0000000..dbe311c --- /dev/null +++ b/contracts/kyber/OrderListInterface.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.24; + + +interface OrderListInterface { + function getOrderDetails(uint32 orderId) external view returns (address, uint128, uint128, uint32, uint32); + function add(address maker, uint32 orderId, uint128 srcAmount, uint128 dstAmount) external returns (bool); + function remove(uint32 orderId) external returns (bool); + function update(uint32 orderId, uint128 srcAmount, uint128 dstAmount) external returns (bool); + function getFirstOrder() external view returns(uint32 orderId, bool isEmpty); + function allocateIds(uint32 howMany) external returns(uint32); + function findPrevOrderId(uint128 srcAmount, uint128 dstAmount) external view returns(uint32); + + function addAfterId(address maker, uint32 orderId, uint128 srcAmount, uint128 dstAmount, uint32 prevId) external + returns (bool); + + function updateWithPositionHint(uint32 orderId, uint128 srcAmount, uint128 dstAmount, uint32 prevId) external + returns(bool, uint); +} diff --git a/contracts/kyber/OrderbookReserve.sol b/contracts/kyber/OrderbookReserve.sol new file mode 100644 index 0000000..eeb148d --- /dev/null +++ b/contracts/kyber/OrderbookReserve.sol @@ -0,0 +1,1018 @@ +pragma solidity ^0.4.24; + + +import "./OrderListInterface.sol"; +import "./OrderIdManager.sol"; +import "./OrderbookReserveInterface.sol"; +import "./Utils2.sol"; +import "./KyberReserveInterface.sol"; + + +contract FeeBurnerRateInterface { + uint public kncPerEthRatePrecision; +} + + +interface MedianizerInterface { + function peek() external view returns (bytes32, bool); +} + + +contract OrderbookReserve is OrderIdManager, Utils2, KyberReserveInterface, OrderbookReserveInterface { + + uint public constant BURN_TO_STAKE_FACTOR = 5; // stake per order must be xfactor expected burn amount. + uint public constant MAX_BURN_FEE_BPS = 100; // 1% + uint public constant MIN_REMAINING_ORDER_RATIO = 2; // Ratio between min new order value and min order value. + uint public constant MAX_USD_PER_ETH = 100000; // Above this value price is surely compromised. + + uint32 constant public TAIL_ID = 1; // tail Id in order list contract + uint32 constant public HEAD_ID = 2; // head Id in order list contract + + struct OrderLimits { + uint minNewOrderSizeUsd; // Basis for setting min new order size Eth + uint maxOrdersPerTrade; // Limit number of iterated orders per trade / getRate loops. + uint minNewOrderSizeWei; // Below this value can't create new order. + uint minOrderSizeWei; // below this value order will be removed. + } + + uint public kncPerEthBaseRatePrecision; // according to base rate all stakes are calculated. + + struct ExternalContracts { + ERC20 kncToken; // not constant. to enable testing while not on main net + ERC20 token; // only supported token. + FeeBurnerRateInterface feeBurner; + address kyberNetwork; + MedianizerInterface medianizer; // price feed Eth - USD from maker DAO. + OrderListFactoryInterface orderListFactory; + } + + //struct for getOrderData() return value. used only in memory. + struct OrderData { + address maker; + uint32 nextId; + bool isLastOrder; + uint128 srcAmount; + uint128 dstAmount; + } + + OrderLimits public limits; + ExternalContracts public contracts; + + // sorted lists of orders. one list for token to Eth, other for Eth to token. + // Each order is added in the correct position in the list to keep it sorted. + OrderListInterface public tokenToEthList; + OrderListInterface public ethToTokenList; + + //funds data + mapping(address => mapping(address => uint)) public makerFunds; // deposited maker funds. + mapping(address => uint) public makerKnc; // for knc staking. + mapping(address => uint) public makerTotalOrdersWei; // per maker how many Wei in orders, for stake calculation. + + uint public makerBurnFeeBps; // knc burn fee per order that is taken. + + //each maker will have orders that will be reused. + mapping(address => OrderIdData) public makerOrdersTokenToEth; + mapping(address => OrderIdData) public makerOrdersEthToToken; + + constructor( + ERC20 knc, + ERC20 reserveToken, + address burner, + address network, + MedianizerInterface medianizer, + OrderListFactoryInterface factory, + uint minNewOrderUsd, + uint maxOrdersPerTrade, + uint burnFeeBps + ) + public + { + + require(knc != address(0)); + require(reserveToken != address(0)); + require(burner != address(0)); + require(network != address(0)); + require(medianizer != address(0)); + require(factory != address(0)); + require(burnFeeBps != 0); + require(burnFeeBps <= MAX_BURN_FEE_BPS); + require(maxOrdersPerTrade != 0); + require(minNewOrderUsd > 0); + + contracts.kyberNetwork = network; + contracts.feeBurner = FeeBurnerRateInterface(burner); + contracts.medianizer = medianizer; + contracts.orderListFactory = factory; + contracts.kncToken = knc; + contracts.token = reserveToken; + + makerBurnFeeBps = burnFeeBps; + limits.minNewOrderSizeUsd = minNewOrderUsd; + limits.maxOrdersPerTrade = maxOrdersPerTrade; + + require(setMinOrderSizeEth()); + + require(contracts.kncToken.approve(contracts.feeBurner, (2**255))); + + //can only support tokens with decimals() API + setDecimals(contracts.token); + + kncPerEthBaseRatePrecision = contracts.feeBurner.kncPerEthRatePrecision(); + } + + ///@dev separate init function for this contract, if this init is in the C'tor. gas consumption too high. + function init() public returns(bool) { + if ((tokenToEthList != address(0)) && (ethToTokenList != address(0))) return true; + if ((tokenToEthList != address(0)) || (ethToTokenList != address(0))) revert(); + + tokenToEthList = contracts.orderListFactory.newOrdersContract(this); + ethToTokenList = contracts.orderListFactory.newOrdersContract(this); + + return true; + } + + function setKncPerEthBaseRate() public { + uint kncPerEthRatePrecision = contracts.feeBurner.kncPerEthRatePrecision(); + if (kncPerEthRatePrecision < kncPerEthBaseRatePrecision) { + kncPerEthBaseRatePrecision = kncPerEthRatePrecision; + } + } + + function getConversionRate(ERC20 src, ERC20 dst, uint srcQty, uint blockNumber) public view returns(uint) { + require((src == ETH_TOKEN_ADDRESS) || (dst == ETH_TOKEN_ADDRESS)); + require((src == contracts.token) || (dst == contracts.token)); + require(srcQty <= MAX_QTY); + + if (kncRateBlocksTrade()) return 0; + + blockNumber; // in this reserve no order expiry == no use for blockNumber. here to avoid compiler warning. + + //user order ETH -> token is matched with maker order token -> ETH + OrderListInterface list = (src == ETH_TOKEN_ADDRESS) ? tokenToEthList : ethToTokenList; + + uint32 orderId; + OrderData memory orderData; + + uint128 userRemainingSrcQty = uint128(srcQty); + uint128 totalUserDstAmount = 0; + uint maxOrders = limits.maxOrdersPerTrade; + + for ( + (orderId, orderData.isLastOrder) = list.getFirstOrder(); + ((userRemainingSrcQty > 0) && (!orderData.isLastOrder) && (maxOrders-- > 0)); + orderId = orderData.nextId + ) { + orderData = getOrderData(list, orderId); + // maker dst quantity is the requested quantity he wants to receive. user src quantity is what user gives. + // so user src quantity is matched with maker dst quantity + if (orderData.dstAmount <= userRemainingSrcQty) { + totalUserDstAmount += orderData.srcAmount; + userRemainingSrcQty -= orderData.dstAmount; + } else { + totalUserDstAmount += uint128(uint(orderData.srcAmount) * uint(userRemainingSrcQty) / + uint(orderData.dstAmount)); + userRemainingSrcQty = 0; + } + } + + if (userRemainingSrcQty != 0) return 0; //not enough tokens to exchange. + + return calcRateFromQty(srcQty, totalUserDstAmount, getDecimals(src), getDecimals(dst)); + } + + event OrderbookReserveTrade(ERC20 srcToken, ERC20 dstToken, uint srcAmount, uint dstAmount); + + function trade( + ERC20 srcToken, + uint srcAmount, + ERC20 dstToken, + address dstAddress, + uint conversionRate, + bool validate + ) + public + payable + returns(bool) + { + require(msg.sender == contracts.kyberNetwork); + require((srcToken == ETH_TOKEN_ADDRESS) || (dstToken == ETH_TOKEN_ADDRESS)); + require((srcToken == contracts.token) || (dstToken == contracts.token)); + require(srcAmount <= MAX_QTY); + + conversionRate; + validate; + + if (srcToken == ETH_TOKEN_ADDRESS) { + require(msg.value == srcAmount); + } else { + require(msg.value == 0); + require(srcToken.transferFrom(msg.sender, this, srcAmount)); + } + + uint totalDstAmount = doTrade( + srcToken, + srcAmount, + dstToken + ); + + require(conversionRate <= calcRateFromQty(srcAmount, totalDstAmount, getDecimals(srcToken), + getDecimals(dstToken))); + + //all orders were successfully taken. send to dstAddress + if (dstToken == ETH_TOKEN_ADDRESS) { + dstAddress.transfer(totalDstAmount); + } else { + require(dstToken.transfer(dstAddress, totalDstAmount)); + } + + emit OrderbookReserveTrade(srcToken, dstToken, srcAmount, totalDstAmount); + return true; + } + + function doTrade( + ERC20 srcToken, + uint srcAmount, + ERC20 dstToken + ) + internal + returns(uint) + { + OrderListInterface list = (srcToken == ETH_TOKEN_ADDRESS) ? tokenToEthList : ethToTokenList; + + uint32 orderId; + OrderData memory orderData; + uint128 userRemainingSrcQty = uint128(srcAmount); + uint128 totalUserDstAmount = 0; + + for ( + (orderId, orderData.isLastOrder) = list.getFirstOrder(); + ((userRemainingSrcQty > 0) && (!orderData.isLastOrder)); + orderId = orderData.nextId + ) { + // maker dst quantity is the requested quantity he wants to receive. user src quantity is what user gives. + // so user src quantity is matched with maker dst quantity + orderData = getOrderData(list, orderId); + if (orderData.dstAmount <= userRemainingSrcQty) { + totalUserDstAmount += orderData.srcAmount; + userRemainingSrcQty -= orderData.dstAmount; + require(takeFullOrder({ + maker: orderData.maker, + orderId: orderId, + userSrc: srcToken, + userDst: dstToken, + userSrcAmount: orderData.dstAmount, + userDstAmount: orderData.srcAmount + })); + } else { + uint128 partialDstQty = uint128(uint(orderData.srcAmount) * uint(userRemainingSrcQty) / + uint(orderData.dstAmount)); + totalUserDstAmount += partialDstQty; + require(takePartialOrder({ + maker: orderData.maker, + orderId: orderId, + userSrc: srcToken, + userDst: dstToken, + userPartialSrcAmount: userRemainingSrcQty, + userTakeDstAmount: partialDstQty, + orderSrcAmount: orderData.srcAmount, + orderDstAmount: orderData.dstAmount + })); + userRemainingSrcQty = 0; + } + } + + require(userRemainingSrcQty == 0 && totalUserDstAmount > 0); + + return totalUserDstAmount; + } + + ///@param srcAmount is the token amount that will be payed. must be deposited before hand in the makers account. + ///@param dstAmount is the eth amount the maker expects to get for his tokens. + function submitTokenToEthOrder(uint128 srcAmount, uint128 dstAmount) + public + returns(bool) + { + return submitTokenToEthOrderWHint(srcAmount, dstAmount, 0); + } + + function submitTokenToEthOrderWHint(uint128 srcAmount, uint128 dstAmount, uint32 hintPrevOrder) + public + returns(bool) + { + uint32 newId = fetchNewOrderId(makerOrdersTokenToEth[msg.sender]); + return addOrder(false, newId, srcAmount, dstAmount, hintPrevOrder); + } + + ///@param srcAmount is the Ether amount that will be payed, must be deposited before hand. + ///@param dstAmount is the token amount the maker expects to get for his Ether. + function submitEthToTokenOrder(uint128 srcAmount, uint128 dstAmount) + public + returns(bool) + { + return submitEthToTokenOrderWHint(srcAmount, dstAmount, 0); + } + + function submitEthToTokenOrderWHint(uint128 srcAmount, uint128 dstAmount, uint32 hintPrevOrder) + public + returns(bool) + { + uint32 newId = fetchNewOrderId(makerOrdersEthToToken[msg.sender]); + return addOrder(true, newId, srcAmount, dstAmount, hintPrevOrder); + } + + ///@dev notice here a batch of orders represented in arrays. order x is represented by x cells of all arrays. + ///@dev all arrays expected to the same length. + ///@param isEthToToken per each order. is order x eth to token (= src is Eth) or vice versa. + ///@param srcAmount per each order. source amount for order x. + ///@param dstAmount per each order. destination amount for order x. + ///@param hintPrevOrder per each order what is the order it should be added after in ordered list. 0 for no hint. + ///@param isAfterPrevOrder per each order, set true if should be added in list right after previous added order. + function addOrderBatch(bool[] isEthToToken, uint128[] srcAmount, uint128[] dstAmount, + uint32[] hintPrevOrder, bool[] isAfterPrevOrder) + public + returns(bool) + { + require(isEthToToken.length == hintPrevOrder.length); + require(isEthToToken.length == dstAmount.length); + require(isEthToToken.length == srcAmount.length); + require(isEthToToken.length == isAfterPrevOrder.length); + + address maker = msg.sender; + uint32 prevId; + uint32 newId = 0; + + for (uint i = 0; i < isEthToToken.length; ++i) { + prevId = isAfterPrevOrder[i] ? newId : hintPrevOrder[i]; + newId = fetchNewOrderId(isEthToToken[i] ? makerOrdersEthToToken[maker] : makerOrdersTokenToEth[maker]); + require(addOrder(isEthToToken[i], newId, srcAmount[i], dstAmount[i], prevId)); + } + + return true; + } + + function updateTokenToEthOrder(uint32 orderId, uint128 newSrcAmount, uint128 newDstAmount) + public + returns(bool) + { + require(updateTokenToEthOrderWHint(orderId, newSrcAmount, newDstAmount, 0)); + return true; + } + + function updateTokenToEthOrderWHint( + uint32 orderId, + uint128 newSrcAmount, + uint128 newDstAmount, + uint32 hintPrevOrder + ) + public + returns(bool) + { + require(updateOrder(false, orderId, newSrcAmount, newDstAmount, hintPrevOrder)); + return true; + } + + function updateEthToTokenOrder(uint32 orderId, uint128 newSrcAmount, uint128 newDstAmount) + public + returns(bool) + { + return updateEthToTokenOrderWHint(orderId, newSrcAmount, newDstAmount, 0); + } + + function updateEthToTokenOrderWHint( + uint32 orderId, + uint128 newSrcAmount, + uint128 newDstAmount, + uint32 hintPrevOrder + ) + public + returns(bool) + { + require(updateOrder(true, orderId, newSrcAmount, newDstAmount, hintPrevOrder)); + return true; + } + + function updateOrderBatch(bool[] isEthToToken, uint32[] orderId, uint128[] newSrcAmount, + uint128[] newDstAmount, uint32[] hintPrevOrder) + public + returns(bool) + { + require(isEthToToken.length == orderId.length); + require(isEthToToken.length == newSrcAmount.length); + require(isEthToToken.length == newDstAmount.length); + require(isEthToToken.length == hintPrevOrder.length); + + for (uint i = 0; i < isEthToToken.length; ++i) { + require(updateOrder(isEthToToken[i], orderId[i], newSrcAmount[i], newDstAmount[i], + hintPrevOrder[i])); + } + + return true; + } + + event TokenDeposited(address indexed maker, uint amount); + + function depositToken(address maker, uint amount) public { + require(maker != address(0)); + require(amount < MAX_QTY); + + require(contracts.token.transferFrom(msg.sender, this, amount)); + + makerFunds[maker][contracts.token] += amount; + emit TokenDeposited(maker, amount); + } + + event EtherDeposited(address indexed maker, uint amount); + + function depositEther(address maker) public payable { + require(maker != address(0)); + + makerFunds[maker][ETH_TOKEN_ADDRESS] += msg.value; + emit EtherDeposited(maker, msg.value); + } + + event KncFeeDeposited(address indexed maker, uint amount); + + // knc will be staked per order. part of the amount will be used as fee. + function depositKncForFee(address maker, uint amount) public { + require(maker != address(0)); + require(amount < MAX_QTY); + + require(contracts.kncToken.transferFrom(msg.sender, this, amount)); + + makerKnc[maker] += amount; + + emit KncFeeDeposited(maker, amount); + + if (orderAllocationRequired(makerOrdersTokenToEth[maker])) { + require(allocateOrderIds( + makerOrdersTokenToEth[maker], /* makerOrders */ + tokenToEthList.allocateIds(uint32(NUM_ORDERS)) /* firstAllocatedId */ + )); + } + + if (orderAllocationRequired(makerOrdersEthToToken[maker])) { + require(allocateOrderIds( + makerOrdersEthToToken[maker], /* makerOrders */ + ethToTokenList.allocateIds(uint32(NUM_ORDERS)) /* firstAllocatedId */ + )); + } + } + + function withdrawToken(uint amount) public { + + address maker = msg.sender; + uint makerFreeAmount = makerFunds[maker][contracts.token]; + + require(makerFreeAmount >= amount); + + makerFunds[maker][contracts.token] -= amount; + + require(contracts.token.transfer(maker, amount)); + } + + function withdrawEther(uint amount) public { + + address maker = msg.sender; + uint makerFreeAmount = makerFunds[maker][ETH_TOKEN_ADDRESS]; + + require(makerFreeAmount >= amount); + + makerFunds[maker][ETH_TOKEN_ADDRESS] -= amount; + + maker.transfer(amount); + } + + function withdrawKncFee(uint amount) public { + + address maker = msg.sender; + + require(makerKnc[maker] >= amount); + require(makerUnlockedKnc(maker) >= amount); + + makerKnc[maker] -= amount; + + require(contracts.kncToken.transfer(maker, amount)); + } + + function cancelTokenToEthOrder(uint32 orderId) public returns(bool) { + require(cancelOrder(false, orderId)); + return true; + } + + function cancelEthToTokenOrder(uint32 orderId) public returns(bool) { + require(cancelOrder(true, orderId)); + return true; + } + + function setMinOrderSizeEth() public returns(bool) { + //get eth to $ from maker dao; + bytes32 usdPerEthInWei; + bool valid; + (usdPerEthInWei, valid) = contracts.medianizer.peek(); + require(valid); + + // ensuring that there is no underflow or overflow possible, + // even if the price is compromised + uint usdPerEth = uint(usdPerEthInWei) / (1 ether); + require(usdPerEth != 0); + require(usdPerEth < MAX_USD_PER_ETH); + + // set Eth order limits according to price + uint minNewOrderSizeWei = limits.minNewOrderSizeUsd * PRECISION * (1 ether) / uint(usdPerEthInWei); + + limits.minNewOrderSizeWei = minNewOrderSizeWei; + limits.minOrderSizeWei = limits.minNewOrderSizeWei / MIN_REMAINING_ORDER_RATIO; + + return true; + } + + ///@dev Each maker stakes per order KNC that is factor of the required burn amount. + ///@dev If Knc per Eth rate becomes lower by more then factor, stake will not be enough and trade will be blocked. + function kncRateBlocksTrade() public view returns (bool) { + return (contracts.feeBurner.kncPerEthRatePrecision() > kncPerEthBaseRatePrecision * BURN_TO_STAKE_FACTOR); + } + + function getTokenToEthAddOrderHint(uint128 srcAmount, uint128 dstAmount) public view returns (uint32) { + require(dstAmount >= limits.minNewOrderSizeWei); + return tokenToEthList.findPrevOrderId(srcAmount, dstAmount); + } + + function getEthToTokenAddOrderHint(uint128 srcAmount, uint128 dstAmount) public view returns (uint32) { + require(srcAmount >= limits.minNewOrderSizeWei); + return ethToTokenList.findPrevOrderId(srcAmount, dstAmount); + } + + function getTokenToEthUpdateOrderHint(uint32 orderId, uint128 srcAmount, uint128 dstAmount) + public + view + returns (uint32) + { + require(dstAmount >= limits.minNewOrderSizeWei); + uint32 prevId = tokenToEthList.findPrevOrderId(srcAmount, dstAmount); + address add; + uint128 noUse; + uint32 next; + + if (prevId == orderId) { + (add, noUse, noUse, prevId, next) = tokenToEthList.getOrderDetails(orderId); + } + + return prevId; + } + + function getEthToTokenUpdateOrderHint(uint32 orderId, uint128 srcAmount, uint128 dstAmount) + public + view + returns (uint32) + { + require(srcAmount >= limits.minNewOrderSizeWei); + uint32 prevId = ethToTokenList.findPrevOrderId(srcAmount, dstAmount); + address add; + uint128 noUse; + uint32 next; + + if (prevId == orderId) { + (add, noUse, noUse, prevId, next) = ethToTokenList.getOrderDetails(orderId); + } + + return prevId; + } + + function getTokenToEthOrder(uint32 orderId) + public view + returns ( + address _maker, + uint128 _srcAmount, + uint128 _dstAmount, + uint32 _prevId, + uint32 _nextId + ) + { + return tokenToEthList.getOrderDetails(orderId); + } + + function getEthToTokenOrder(uint32 orderId) + public view + returns ( + address _maker, + uint128 _srcAmount, + uint128 _dstAmount, + uint32 _prevId, + uint32 _nextId + ) + { + return ethToTokenList.getOrderDetails(orderId); + } + + function makerRequiredKncStake(address maker) public view returns (uint) { + return(calcKncStake(makerTotalOrdersWei[maker])); + } + + function makerUnlockedKnc(address maker) public view returns (uint) { + uint requiredKncStake = makerRequiredKncStake(maker); + if (requiredKncStake > makerKnc[maker]) return 0; + return (makerKnc[maker] - requiredKncStake); + } + + function calcKncStake(uint weiAmount) public view returns(uint) { + return(calcBurnAmount(weiAmount) * BURN_TO_STAKE_FACTOR); + } + + function calcBurnAmount(uint weiAmount) public view returns(uint) { + return(weiAmount * makerBurnFeeBps * kncPerEthBaseRatePrecision / (10000 * PRECISION)); + } + + function calcBurnAmountFromFeeBurner(uint weiAmount) public view returns(uint) { + return(weiAmount * makerBurnFeeBps * contracts.feeBurner.kncPerEthRatePrecision() / (10000 * PRECISION)); + } + + ///@dev This function is not fully optimized gas wise. Consider before calling on chain. + function getEthToTokenMakerOrderIds(address maker) public view returns(uint32[] orderList) { + OrderIdData storage makerOrders = makerOrdersEthToToken[maker]; + orderList = new uint32[](getNumActiveOrderIds(makerOrders)); + uint activeOrder = 0; + + for (uint32 i = 0; i < NUM_ORDERS; ++i) { + if ((makerOrders.takenBitmap & (uint(1) << i) > 0)) orderList[activeOrder++] = makerOrders.firstOrderId + i; + } + } + + ///@dev This function is not fully optimized gas wise. Consider before calling on chain. + function getTokenToEthMakerOrderIds(address maker) public view returns(uint32[] orderList) { + OrderIdData storage makerOrders = makerOrdersTokenToEth[maker]; + orderList = new uint32[](getNumActiveOrderIds(makerOrders)); + uint activeOrder = 0; + + for (uint32 i = 0; i < NUM_ORDERS; ++i) { + if ((makerOrders.takenBitmap & (uint(1) << i) > 0)) orderList[activeOrder++] = makerOrders.firstOrderId + i; + } + } + + ///@dev This function is not fully optimized gas wise. Consider before calling on chain. + function getEthToTokenOrderList() public view returns(uint32[] orderList) { + OrderListInterface list = ethToTokenList; + return getList(list); + } + + ///@dev This function is not fully optimized gas wise. Consider before calling on chain. + function getTokenToEthOrderList() public view returns(uint32[] orderList) { + OrderListInterface list = tokenToEthList; + return getList(list); + } + + event NewLimitOrder( + address indexed maker, + uint32 orderId, + bool isEthToToken, + uint128 srcAmount, + uint128 dstAmount, + bool addedWithHint + ); + + function addOrder(bool isEthToToken, uint32 newId, uint128 srcAmount, uint128 dstAmount, uint32 hintPrevOrder) + internal + returns(bool) + { + require(srcAmount < MAX_QTY); + require(dstAmount < MAX_QTY); + address maker = msg.sender; + + require(secureAddOrderFunds(maker, isEthToToken, srcAmount, dstAmount)); + require(validateLegalRate(srcAmount, dstAmount, isEthToToken)); + + bool addedWithHint = false; + OrderListInterface list = isEthToToken ? ethToTokenList : tokenToEthList; + + if (hintPrevOrder != 0) { + addedWithHint = list.addAfterId(maker, newId, srcAmount, dstAmount, hintPrevOrder); + } + + if (!addedWithHint) { + require(list.add(maker, newId, srcAmount, dstAmount)); + } + + emit NewLimitOrder(maker, newId, isEthToToken, srcAmount, dstAmount, addedWithHint); + + return true; + } + + event OrderUpdated( + address indexed maker, + bool isEthToToken, + uint orderId, + uint128 srcAmount, + uint128 dstAmount, + bool updatedWithHint + ); + + function updateOrder(bool isEthToToken, uint32 orderId, uint128 newSrcAmount, + uint128 newDstAmount, uint32 hintPrevOrder) + internal + returns(bool) + { + require(newSrcAmount < MAX_QTY); + require(newDstAmount < MAX_QTY); + address maker; + uint128 currDstAmount; + uint128 currSrcAmount; + uint32 noUse; + uint noUse2; + + require(validateLegalRate(newSrcAmount, newDstAmount, isEthToToken)); + + OrderListInterface list = isEthToToken ? ethToTokenList : tokenToEthList; + + (maker, currSrcAmount, currDstAmount, noUse, noUse) = list.getOrderDetails(orderId); + require(maker == msg.sender); + + if (!secureUpdateOrderFunds(maker, isEthToToken, currSrcAmount, currDstAmount, newSrcAmount, newDstAmount)) { + return false; + } + + bool updatedWithHint = false; + + if (hintPrevOrder != 0) { + (updatedWithHint, noUse2) = list.updateWithPositionHint(orderId, newSrcAmount, newDstAmount, hintPrevOrder); + } + + if (!updatedWithHint) { + require(list.update(orderId, newSrcAmount, newDstAmount)); + } + + emit OrderUpdated(maker, isEthToToken, orderId, newSrcAmount, newDstAmount, updatedWithHint); + + return true; + } + + event OrderCanceled(address indexed maker, bool isEthToToken, uint32 orderId, uint128 srcAmount, uint dstAmount); + + function cancelOrder(bool isEthToToken, uint32 orderId) internal returns(bool) { + + address maker = msg.sender; + OrderListInterface list = isEthToToken ? ethToTokenList : tokenToEthList; + OrderData memory orderData = getOrderData(list, orderId); + + require(orderData.maker == maker); + + uint weiAmount = isEthToToken ? orderData.srcAmount : orderData.dstAmount; + require(releaseOrderStakes(maker, weiAmount, 0)); + + require(removeOrder(list, maker, isEthToToken ? ETH_TOKEN_ADDRESS : contracts.token, orderId)); + + //funds go back to makers account + makerFunds[maker][isEthToToken ? ETH_TOKEN_ADDRESS : contracts.token] += orderData.srcAmount; + + emit OrderCanceled(maker, isEthToToken, orderId, orderData.srcAmount, orderData.dstAmount); + + return true; + } + + ///@param maker is the maker of this order + ///@param isEthToToken which order type the maker is updating / adding + ///@param srcAmount is the orders src amount (token or ETH) could be negative if funds are released. + function bindOrderFunds(address maker, bool isEthToToken, int srcAmount) + internal + returns(bool) + { + address fundsAddress = isEthToToken ? ETH_TOKEN_ADDRESS : contracts.token; + + if (srcAmount < 0) { + makerFunds[maker][fundsAddress] += uint(-srcAmount); + } else { + require(makerFunds[maker][fundsAddress] >= uint(srcAmount)); + makerFunds[maker][fundsAddress] -= uint(srcAmount); + } + + return true; + } + + ///@param maker is the maker address + ///@param weiAmount is the wei amount inside order that should result in knc staking + function bindOrderStakes(address maker, int weiAmount) internal returns(bool) { + + if (weiAmount < 0) { + uint decreaseWeiAmount = uint(-weiAmount); + if (decreaseWeiAmount > makerTotalOrdersWei[maker]) decreaseWeiAmount = makerTotalOrdersWei[maker]; + makerTotalOrdersWei[maker] -= decreaseWeiAmount; + return true; + } + + require(makerKnc[maker] >= calcKncStake(makerTotalOrdersWei[maker] + uint(weiAmount))); + + makerTotalOrdersWei[maker] += uint(weiAmount); + + return true; + } + + ///@dev if totalWeiAmount is 0 we only release stakes. + ///@dev if totalWeiAmount == weiForBurn. all staked amount will be burned. so no knc returned to maker + ///@param maker is the maker address + ///@param totalWeiAmount is total wei amount that was released from order - including taken wei amount. + ///@param weiForBurn is the part in order wei amount that was taken and should result in burning. + function releaseOrderStakes(address maker, uint totalWeiAmount, uint weiForBurn) internal returns(bool) { + + require(weiForBurn <= totalWeiAmount); + + if (totalWeiAmount > makerTotalOrdersWei[maker]) { + makerTotalOrdersWei[maker] = 0; + } else { + makerTotalOrdersWei[maker] -= totalWeiAmount; + } + + if (weiForBurn == 0) return true; + + uint burnAmount = calcBurnAmountFromFeeBurner(weiForBurn); + + require(makerKnc[maker] >= burnAmount); + makerKnc[maker] -= burnAmount; + + return true; + } + + ///@dev funds are valid only when required knc amount can be staked for this order. + function secureAddOrderFunds(address maker, bool isEthToToken, uint128 srcAmount, uint128 dstAmount) + internal returns(bool) + { + uint weiAmount = isEthToToken ? srcAmount : dstAmount; + + require(weiAmount >= limits.minNewOrderSizeWei); + require(bindOrderFunds(maker, isEthToToken, int(srcAmount))); + require(bindOrderStakes(maker, int(weiAmount))); + + return true; + } + + ///@dev funds are valid only when required knc amount can be staked for this order. + function secureUpdateOrderFunds(address maker, bool isEthToToken, uint128 prevSrcAmount, uint128 prevDstAmount, + uint128 newSrcAmount, uint128 newDstAmount) + internal + returns(bool) + { + uint weiAmount = isEthToToken ? newSrcAmount : newDstAmount; + int weiDiff = isEthToToken ? (int(newSrcAmount) - int(prevSrcAmount)) : + (int(newDstAmount) - int(prevDstAmount)); + + require(weiAmount >= limits.minNewOrderSizeWei); + + require(bindOrderFunds(maker, isEthToToken, int(newSrcAmount) - int(prevSrcAmount))); + + require(bindOrderStakes(maker, weiDiff)); + + return true; + } + + event FullOrderTaken(address maker, uint32 orderId, bool isEthToToken); + + function takeFullOrder( + address maker, + uint32 orderId, + ERC20 userSrc, + ERC20 userDst, + uint128 userSrcAmount, + uint128 userDstAmount + ) + internal + returns (bool) + { + OrderListInterface list = (userSrc == ETH_TOKEN_ADDRESS) ? tokenToEthList : ethToTokenList; + + //userDst == maker source + require(removeOrder(list, maker, userDst, orderId)); + + emit FullOrderTaken(maker, orderId, userSrc == ETH_TOKEN_ADDRESS); + + return takeOrder(maker, userSrc, userSrcAmount, userDstAmount, 0); + } + + event PartialOrderTaken(address maker, uint32 orderId, bool isEthToToken, bool isRemoved); + + function takePartialOrder( + address maker, + uint32 orderId, + ERC20 userSrc, + ERC20 userDst, + uint128 userPartialSrcAmount, + uint128 userTakeDstAmount, + uint128 orderSrcAmount, + uint128 orderDstAmount + ) + internal + returns(bool) + { + require(userPartialSrcAmount < orderDstAmount); + require(userTakeDstAmount < orderSrcAmount); + + //must reuse parameters, otherwise stack too deep error. + orderSrcAmount -= userTakeDstAmount; + orderDstAmount -= userPartialSrcAmount; + + OrderListInterface list = (userSrc == ETH_TOKEN_ADDRESS) ? tokenToEthList : ethToTokenList; + uint weiValueNotReleasedFromOrder = (userSrc == ETH_TOKEN_ADDRESS) ? orderDstAmount : orderSrcAmount; + uint additionalReleasedWei = 0; + + if (weiValueNotReleasedFromOrder < limits.minOrderSizeWei) { + // remaining order amount too small. remove order and add remaining funds to free funds + makerFunds[maker][userDst] += orderSrcAmount; + additionalReleasedWei = weiValueNotReleasedFromOrder; + + //for remove order we give makerSrc == userDst + require(removeOrder(list, maker, userDst, orderId)); + } else { + bool isSuccess; + + // update order values, taken order is always first order + (isSuccess,) = list.updateWithPositionHint(orderId, orderSrcAmount, orderDstAmount, HEAD_ID); + require(isSuccess); + } + + emit PartialOrderTaken(maker, orderId, userSrc == ETH_TOKEN_ADDRESS, additionalReleasedWei > 0); + + //stakes are returned for unused wei value + return(takeOrder(maker, userSrc, userPartialSrcAmount, userTakeDstAmount, additionalReleasedWei)); + } + + function takeOrder( + address maker, + ERC20 userSrc, + uint userSrcAmount, + uint userDstAmount, + uint additionalReleasedWei + ) + internal + returns(bool) + { + uint weiAmount = userSrc == (ETH_TOKEN_ADDRESS) ? userSrcAmount : userDstAmount; + + //token / eth already collected. just update maker balance + makerFunds[maker][userSrc] += userSrcAmount; + + // send dst tokens in one batch. not here + //handle knc stakes and fee. releasedWeiValue was released and not traded. + return releaseOrderStakes(maker, (weiAmount + additionalReleasedWei), weiAmount); + } + + function removeOrder( + OrderListInterface list, + address maker, + ERC20 makerSrc, + uint32 orderId + ) + internal returns(bool) + { + require(list.remove(orderId)); + OrderIdData storage orders = (makerSrc == ETH_TOKEN_ADDRESS) ? + makerOrdersEthToToken[maker] : makerOrdersTokenToEth[maker]; + require(releaseOrderId(orders, orderId)); + + return true; + } + + function getList(OrderListInterface list) internal view returns(uint32[] memory orderList) { + OrderData memory orderData; + uint32 orderId; + bool isEmpty; + + (orderId, isEmpty) = list.getFirstOrder(); + if (isEmpty) return(new uint32[](0)); + + uint numOrders = 0; + + for (; !orderData.isLastOrder; orderId = orderData.nextId) { + orderData = getOrderData(list, orderId); + numOrders++; + } + + orderList = new uint32[](numOrders); + + (orderId, orderData.isLastOrder) = list.getFirstOrder(); + + for (uint i = 0; i < numOrders; i++) { + orderList[i] = orderId; + orderData = getOrderData(list, orderId); + orderId = orderData.nextId; + } + } + + function getOrderData(OrderListInterface list, uint32 orderId) internal view returns (OrderData data) { + uint32 prevId; + (data.maker, data.srcAmount, data.dstAmount, prevId, data.nextId) = list.getOrderDetails(orderId); + data.isLastOrder = (data.nextId == TAIL_ID); + } + + function validateLegalRate (uint srcAmount, uint dstAmount, bool isEthToToken) + internal view returns(bool) + { + uint rate; + + /// notice, rate is calculated from taker perspective, + /// for taker amounts are opposite. order srcAmount will be DstAmount for taker. + if (isEthToToken) { + rate = calcRateFromQty(dstAmount, srcAmount, getDecimals(contracts.token), ETH_DECIMALS); + } else { + rate = calcRateFromQty(dstAmount, srcAmount, ETH_DECIMALS, getDecimals(contracts.token)); + } + + if (rate > MAX_RATE) return false; + return true; + } +} diff --git a/contracts/kyber/OrderbookReserveInterface.sol b/contracts/kyber/OrderbookReserveInterface.sol new file mode 100644 index 0000000..21d9780 --- /dev/null +++ b/contracts/kyber/OrderbookReserveInterface.sol @@ -0,0 +1,10 @@ +pragma solidity 0.4.24; + + +import "./OrderListFactoryInterface.sol"; + + +interface OrderbookReserveInterface { + function init() external returns(bool); + function kncRateBlocksTrade() external view returns(bool); +} diff --git a/contracts/kyber/PermissionGroups.sol b/contracts/kyber/PermissionGroups.sol new file mode 100644 index 0000000..0ad323c --- /dev/null +++ b/contracts/kyber/PermissionGroups.sol @@ -0,0 +1,124 @@ +pragma solidity ^0.4.24; + +contract PermissionGroups { + + address public admin; + address public pendingAdmin; + mapping(address=>bool) internal operators; + mapping(address=>bool) internal alerters; + address[] internal operatorsGroup; + address[] internal alertersGroup; + uint constant internal MAX_GROUP_SIZE = 50; + + constructor() public { + admin = msg.sender; + } + + modifier onlyAdmin() { + require(msg.sender == admin); + _; + } + + modifier onlyOperator() { + require(operators[msg.sender]); + _; + } + + modifier onlyAlerter() { + require(alerters[msg.sender]); + _; + } + + function getOperators () external view returns(address[]) { + return operatorsGroup; + } + + function getAlerters () external view returns(address[]) { + return alertersGroup; + } + + event TransferAdminPending(address pendingAdmin); + + /** + * @dev Allows the current admin to set the pendingAdmin address. + * @param newAdmin The address to transfer ownership to. + */ + function transferAdmin(address newAdmin) public onlyAdmin { + require(newAdmin != address(0)); + emit TransferAdminPending(pendingAdmin); + pendingAdmin = newAdmin; + } + + /** + * @dev Allows the current admin to set the admin in one tx. Useful initial deployment. + * @param newAdmin The address to transfer ownership to. + */ + function transferAdminQuickly(address newAdmin) public onlyAdmin { + require(newAdmin != address(0)); + emit TransferAdminPending(newAdmin); + emit AdminClaimed(newAdmin, admin); + admin = newAdmin; + } + + event AdminClaimed( address newAdmin, address previousAdmin); + + /** + * @dev Allows the pendingAdmin address to finalize the change admin process. + */ + function claimAdmin() public { + require(pendingAdmin == msg.sender); + emit AdminClaimed(pendingAdmin, admin); + admin = pendingAdmin; + pendingAdmin = address(0); + } + + event AlerterAdded (address newAlerter, bool isAdd); + + function addAlerter(address newAlerter) public onlyAdmin { + require(!alerters[newAlerter]); // prevent duplicates. + require(alertersGroup.length < MAX_GROUP_SIZE); + + emit AlerterAdded(newAlerter, true); + alerters[newAlerter] = true; + alertersGroup.push(newAlerter); + } + + function removeAlerter (address alerter) public onlyAdmin { + require(alerters[alerter]); + alerters[alerter] = false; + + for (uint i = 0; i < alertersGroup.length; ++i) { + if (alertersGroup[i] == alerter) { + alertersGroup[i] = alertersGroup[alertersGroup.length - 1]; + alertersGroup.length--; + emit AlerterAdded(alerter, false); + break; + } + } + } + + event OperatorAdded(address newOperator, bool isAdd); + + function addOperator(address newOperator) public onlyAdmin { + require(!operators[newOperator]); // prevent duplicates. + require(operatorsGroup.length < MAX_GROUP_SIZE); + + emit OperatorAdded(newOperator, true); + operators[newOperator] = true; + operatorsGroup.push(newOperator); + } + + function removeOperator (address operator) public onlyAdmin { + require(operators[operator]); + operators[operator] = false; + + for (uint i = 0; i < operatorsGroup.length; ++i) { + if (operatorsGroup[i] == operator) { + operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1]; + operatorsGroup.length -= 1; + emit OperatorAdded(operator, false); + break; + } + } + } +} diff --git a/contracts/kyber/SanityRatesInterface.sol b/contracts/kyber/SanityRatesInterface.sol new file mode 100644 index 0000000..e5007a2 --- /dev/null +++ b/contracts/kyber/SanityRatesInterface.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + +interface SanityRatesInterface { + function getSanityRate(ERC20 src, ERC20 dest) external view returns(uint); +} diff --git a/contracts/kyber/SimpleNetworkInterface.sol b/contracts/kyber/SimpleNetworkInterface.sol new file mode 100644 index 0000000..c67b39a --- /dev/null +++ b/contracts/kyber/SimpleNetworkInterface.sol @@ -0,0 +1,11 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + + +/// @title simple interface for Kyber Network +interface SimpleNetworkInterface { + function swapTokenToToken(ERC20 src, uint srcAmount, ERC20 dest, uint minConversionRate) external returns(uint); + function swapEtherToToken(ERC20 token, uint minConversionRate) external payable returns(uint); + function swapTokenToEther(ERC20 token, uint srcAmount, uint minConversionRate) external returns(uint); +} diff --git a/contracts/kyber/Utils.sol b/contracts/kyber/Utils.sol new file mode 100644 index 0000000..4c04dc6 --- /dev/null +++ b/contracts/kyber/Utils.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; + + +/// @title Kyber constants contract +contract Utils { + + ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee); + uint constant internal PRECISION = (10**18); + uint constant internal MAX_QTY = (10**28); // 10B tokens + uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH + uint constant internal MAX_DECIMALS = 18; + uint constant internal ETH_DECIMALS = 18; + mapping(address=>uint) internal decimals; + + function setDecimals(ERC20 token) internal { + if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS; + else decimals[token] = token.decimals(); + } + + function getDecimals(ERC20 token) internal view returns(uint) { + if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access + uint tokenDecimals = decimals[token]; + // technically, there might be token with decimals 0 + // moreover, very possible that old tokens have decimals 0 + // these tokens will just have higher gas fees. + if(tokenDecimals == 0) return token.decimals(); + + return tokenDecimals; + } + + function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) { + require(srcQty <= MAX_QTY); + require(rate <= MAX_RATE); + + if (dstDecimals >= srcDecimals) { + require((dstDecimals - srcDecimals) <= MAX_DECIMALS); + return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION; + } else { + require((srcDecimals - dstDecimals) <= MAX_DECIMALS); + return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals))); + } + } + + function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) { + require(dstQty <= MAX_QTY); + require(rate <= MAX_RATE); + + //source quantity is rounded up. to avoid dest quantity being too low. + uint numerator; + uint denominator; + if (srcDecimals >= dstDecimals) { + require((srcDecimals - dstDecimals) <= MAX_DECIMALS); + numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals))); + denominator = rate; + } else { + require((dstDecimals - srcDecimals) <= MAX_DECIMALS); + numerator = (PRECISION * dstQty); + denominator = (rate * (10**(dstDecimals - srcDecimals))); + } + return (numerator + denominator - 1) / denominator; //avoid rounding down errors + } +} diff --git a/contracts/kyber/Utils2.sol b/contracts/kyber/Utils2.sol new file mode 100644 index 0000000..500dfb1 --- /dev/null +++ b/contracts/kyber/Utils2.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.4.24; + +import "./Utils.sol"; + + +contract Utils2 is Utils { + + /// @dev get the balance of a user. + /// @param token The token type + /// @return The balance + function getBalance(ERC20 token, address user) public view returns(uint) { + if (token == ETH_TOKEN_ADDRESS) + return user.balance; + else + return token.balanceOf(user); + } + + function getDecimalsSafe(ERC20 token) internal returns(uint) { + + if (decimals[token] == 0) { + setDecimals(token); + } + + return decimals[token]; + } + + function calcDestAmount(ERC20 src, ERC20 dest, uint srcAmount, uint rate) internal view returns(uint) { + return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate); + } + + function calcSrcAmount(ERC20 src, ERC20 dest, uint destAmount, uint rate) internal view returns(uint) { + return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate); + } + + function calcRateFromQty(uint srcAmount, uint destAmount, uint srcDecimals, uint dstDecimals) + internal pure returns(uint) + { + require(srcAmount <= MAX_QTY, '1'); + require(destAmount <= MAX_QTY, '2'); + + if (dstDecimals >= srcDecimals) { + require((dstDecimals - srcDecimals) <= MAX_DECIMALS, '3'); + return (destAmount * PRECISION / ((10 ** (dstDecimals - srcDecimals)) * srcAmount)); + } else { + require((srcDecimals - dstDecimals) <= MAX_DECIMALS, '4'); + return (destAmount * PRECISION * (10 ** (srcDecimals - dstDecimals)) / srcAmount); + } + } +} diff --git a/contracts/kyber/VolumeImbalanceRecorder.sol b/contracts/kyber/VolumeImbalanceRecorder.sol new file mode 100644 index 0000000..5de05d0 --- /dev/null +++ b/contracts/kyber/VolumeImbalanceRecorder.sol @@ -0,0 +1,209 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./Withdrawable.sol"; + + +contract VolumeImbalanceRecorder is Withdrawable { + + uint constant internal SLIDING_WINDOW_SIZE = 5; + uint constant internal POW_2_64 = 2 ** 64; + + struct TokenControlInfo { + uint minimalRecordResolution; // can be roughly 1 cent + uint maxPerBlockImbalance; // in twei resolution + uint maxTotalImbalance; // max total imbalance (between rate updates) + // before halting trade + } + + mapping(address => TokenControlInfo) internal tokenControlInfo; + + struct TokenImbalanceData { + int lastBlockBuyUnitsImbalance; + uint lastBlock; + + int totalBuyUnitsImbalance; + uint lastRateUpdateBlock; + } + + mapping(address => mapping(uint=>uint)) public tokenImbalanceData; + + constructor(address _admin) public { + require(_admin != address(0)); + admin = _admin; + } + + function setTokenControlInfo( + ERC20 token, + uint minimalRecordResolution, + uint maxPerBlockImbalance, + uint maxTotalImbalance + ) + public + onlyAdmin + { + tokenControlInfo[token] = + TokenControlInfo( + minimalRecordResolution, + maxPerBlockImbalance, + maxTotalImbalance + ); + } + + function getTokenControlInfo(ERC20 token) public view returns(uint, uint, uint) { + return (tokenControlInfo[token].minimalRecordResolution, + tokenControlInfo[token].maxPerBlockImbalance, + tokenControlInfo[token].maxTotalImbalance); + } + + function addImbalance( + ERC20 token, + int buyAmount, + uint rateUpdateBlock, + uint currentBlock + ) + internal + { + uint currentBlockIndex = currentBlock % SLIDING_WINDOW_SIZE; + int recordedBuyAmount = int(buyAmount / int(tokenControlInfo[token].minimalRecordResolution)); + + int prevImbalance = 0; + + TokenImbalanceData memory currentBlockData = + decodeTokenImbalanceData(tokenImbalanceData[token][currentBlockIndex]); + + // first scenario - this is not the first tx in the current block + if (currentBlockData.lastBlock == currentBlock) { + if (uint(currentBlockData.lastRateUpdateBlock) == rateUpdateBlock) { + // just increase imbalance + currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount; + currentBlockData.totalBuyUnitsImbalance += recordedBuyAmount; + } else { + // imbalance was changed in the middle of the block + prevImbalance = getImbalanceInRange(token, rateUpdateBlock, currentBlock); + currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount; + currentBlockData.lastBlockBuyUnitsImbalance += recordedBuyAmount; + currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock); + } + } else { + // first tx in the current block + int currentBlockImbalance; + (prevImbalance, currentBlockImbalance) = getImbalanceSinceRateUpdate(token, rateUpdateBlock, currentBlock); + + currentBlockData.lastBlockBuyUnitsImbalance = recordedBuyAmount; + currentBlockData.lastBlock = uint(currentBlock); + currentBlockData.lastRateUpdateBlock = uint(rateUpdateBlock); + currentBlockData.totalBuyUnitsImbalance = int(prevImbalance) + recordedBuyAmount; + } + + tokenImbalanceData[token][currentBlockIndex] = encodeTokenImbalanceData(currentBlockData); + } + + function setGarbageToVolumeRecorder(ERC20 token) internal { + for (uint i = 0; i < SLIDING_WINDOW_SIZE; i++) { + tokenImbalanceData[token][i] = 0x1; + } + } + + function getImbalanceInRange(ERC20 token, uint startBlock, uint endBlock) internal view returns(int buyImbalance) { + // check the imbalance in the sliding window + require(startBlock <= endBlock); + + buyImbalance = 0; + + for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) { + TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]); + + if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) { + buyImbalance += int(perBlockData.lastBlockBuyUnitsImbalance); + } + } + } + + function getImbalanceSinceRateUpdate(ERC20 token, uint rateUpdateBlock, uint currentBlock) + internal view + returns(int buyImbalance, int currentBlockImbalance) + { + buyImbalance = 0; + currentBlockImbalance = 0; + uint latestBlock = 0; + int imbalanceInRange = 0; + uint startBlock = rateUpdateBlock; + uint endBlock = currentBlock; + + for (uint windowInd = 0; windowInd < SLIDING_WINDOW_SIZE; windowInd++) { + TokenImbalanceData memory perBlockData = decodeTokenImbalanceData(tokenImbalanceData[token][windowInd]); + + if (perBlockData.lastBlock <= endBlock && perBlockData.lastBlock >= startBlock) { + imbalanceInRange += perBlockData.lastBlockBuyUnitsImbalance; + } + + if (perBlockData.lastRateUpdateBlock != rateUpdateBlock) continue; + if (perBlockData.lastBlock < latestBlock) continue; + + latestBlock = perBlockData.lastBlock; + buyImbalance = perBlockData.totalBuyUnitsImbalance; + if (uint(perBlockData.lastBlock) == currentBlock) { + currentBlockImbalance = perBlockData.lastBlockBuyUnitsImbalance; + } + } + + if (buyImbalance == 0) { + buyImbalance = imbalanceInRange; + } + } + + function getImbalance(ERC20 token, uint rateUpdateBlock, uint currentBlock) + internal view + returns(int totalImbalance, int currentBlockImbalance) + { + + int resolution = int(tokenControlInfo[token].minimalRecordResolution); + + (totalImbalance, currentBlockImbalance) = + getImbalanceSinceRateUpdate( + token, + rateUpdateBlock, + currentBlock); + + totalImbalance *= resolution; + currentBlockImbalance *= resolution; + } + + function getMaxPerBlockImbalance(ERC20 token) internal view returns(uint) { + return tokenControlInfo[token].maxPerBlockImbalance; + } + + function getMaxTotalImbalance(ERC20 token) internal view returns(uint) { + return tokenControlInfo[token].maxTotalImbalance; + } + + function encodeTokenImbalanceData(TokenImbalanceData data) internal pure returns(uint) { + // check for overflows + require(data.lastBlockBuyUnitsImbalance < int(POW_2_64 / 2)); + require(data.lastBlockBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2)); + require(data.lastBlock < POW_2_64); + require(data.totalBuyUnitsImbalance < int(POW_2_64 / 2)); + require(data.totalBuyUnitsImbalance > int(-1 * int(POW_2_64) / 2)); + require(data.lastRateUpdateBlock < POW_2_64); + + // do encoding + uint result = uint(data.lastBlockBuyUnitsImbalance) & (POW_2_64 - 1); + result |= data.lastBlock * POW_2_64; + result |= (uint(data.totalBuyUnitsImbalance) & (POW_2_64 - 1)) * POW_2_64 * POW_2_64; + result |= data.lastRateUpdateBlock * POW_2_64 * POW_2_64 * POW_2_64; + + return result; + } + + function decodeTokenImbalanceData(uint input) internal pure returns(TokenImbalanceData) { + TokenImbalanceData memory data; + + data.lastBlockBuyUnitsImbalance = int(int64(input & (POW_2_64 - 1))); + data.lastBlock = uint(uint64((input / POW_2_64) & (POW_2_64 - 1))); + data.totalBuyUnitsImbalance = int(int64((input / (POW_2_64 * POW_2_64)) & (POW_2_64 - 1))); + data.lastRateUpdateBlock = uint(uint64((input / (POW_2_64 * POW_2_64 * POW_2_64)))); + + return data; + } +} diff --git a/contracts/kyber/WhiteList.sol b/contracts/kyber/WhiteList.sol new file mode 100644 index 0000000..8cd7567 --- /dev/null +++ b/contracts/kyber/WhiteList.sol @@ -0,0 +1,59 @@ +pragma solidity ^0.4.24; + +import "./Withdrawable.sol"; +import "./WhiteListInterface.sol"; +import "../token/ERC20.sol"; + + +contract WhiteList is WhiteListInterface, Withdrawable { + + uint public weiPerSgd; // amount of weis in 1 singapore dollar + mapping (address=>uint) public userCategory; // each user has a category defining cap on trade. 0 for standard. + mapping (uint=>uint) public categoryCap; // will define cap on trade amount per category in singapore Dollar. + uint constant public kgtHolderCategory = 2; + ERC20 public kgtToken; + + constructor(address _admin, ERC20 _kgtToken) public { + require(_admin != address(0)); + require(_kgtToken != address(0)); + kgtToken = _kgtToken; + admin = _admin; + } + + function getUserCapInWei(address user) external view returns (uint) { + uint category = getUserCategory(user); + return (categoryCap[category] * weiPerSgd); + } + + event UserCategorySet(address user, uint category); + + function setUserCategory(address user, uint category) public onlyOperator { + userCategory[user] = category; + emit UserCategorySet(user, category); + } + + event CategoryCapSet (uint category, uint sgdCap); + + function setCategoryCap(uint category, uint sgdCap) public onlyOperator { + categoryCap[category] = sgdCap; + emit CategoryCapSet(category, sgdCap); + } + + event SgdToWeiRateSet (uint rate); + + function setSgdToEthRate(uint _sgdToWeiRate) public onlyOperator { + weiPerSgd = _sgdToWeiRate; + emit SgdToWeiRateSet(_sgdToWeiRate); + } + + function getUserCategory (address user) public view returns(uint) { + uint category = userCategory[user]; + if (category == 0) { + //0 = default category. means category wasn't set. + if (kgtToken.balanceOf(user) > 0) { + category = kgtHolderCategory; + } + } + return category; + } +} diff --git a/contracts/kyber/WhiteListInterface.sol b/contracts/kyber/WhiteListInterface.sol new file mode 100644 index 0000000..1d7d832 --- /dev/null +++ b/contracts/kyber/WhiteListInterface.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.24; + +contract WhiteListInterface { + function getUserCapInWei(address user) external view returns (uint userCapWei); +} diff --git a/contracts/kyber/Withdrawable.sol b/contracts/kyber/Withdrawable.sol new file mode 100644 index 0000000..a4e5918 --- /dev/null +++ b/contracts/kyber/Withdrawable.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./PermissionGroups.sol"; + + +/** + * @title Contracts that should be able to recover tokens or ethers + * @author Ilan Doron + * @dev This allows to recover any tokens or Ethers received in a contract. + * This will prevent any accidental loss of tokens. + */ +contract Withdrawable is PermissionGroups { + + event TokenWithdraw(ERC20 token, uint amount, address sendTo); + + /** + * @dev Withdraw all ERC20 compatible tokens + * @param token ERC20 The address of the token contract + */ + function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin { + require(token.transfer(sendTo, amount)); + emit TokenWithdraw(token, amount, sendTo); + } + + event EtherWithdraw(uint amount, address sendTo); + + /** + * @dev Withdraw Ethers + */ + function withdrawEther(uint amount, address sendTo) external onlyAdmin { + sendTo.transfer(amount); + emit EtherWithdraw(amount, sendTo); + } +} diff --git a/contracts/kyber/Wrapper.sol b/contracts/kyber/Wrapper.sol new file mode 100644 index 0000000..fb9ecbb --- /dev/null +++ b/contracts/kyber/Wrapper.sol @@ -0,0 +1,176 @@ +pragma solidity ^0.4.24; + +import "../token/ERC20.sol"; +import "./KyberReserve.sol"; +import "./KyberNetwork.sol"; +import "./Utils.sol"; +import "./ConversionRates.sol"; +import "./KyberNetworkProxy.sol"; +import "./OrderbookReserve.sol"; + +contract Wrapper is Utils { + + function getBalances(address reserve, ERC20[] tokens) public view returns(uint[]) { + uint[] memory result = new uint[](tokens.length); + for (uint i = 0; i < tokens.length; i++) { + uint balance = 0; + if (tokens[i] == ETH_TOKEN_ADDRESS) { + balance = reserve.balance; + } else { + balance = tokens[i].balanceOf(reserve); + } + + result[i] = balance; + } + + return result; + } + + function getTokenAllowances(address owner, address spender, ERC20[] tokens) public view returns(uint[]) { + uint[] memory result = new uint[](tokens.length); + for (uint i = 0; i < tokens.length; i++) { + result[i] = tokens[i].allowance(owner, spender); + } + return result; + } + + function getByteFromBytes14(bytes14 x, uint byteInd) public pure returns(byte) { + require(byteInd <= 13); + return x[byteInd]; + } + + function getInt8FromByte(bytes14 x, uint byteInd) public pure returns(int8) { + require(byteInd <= 13); + return int8(x[byteInd]); + } + +// struct TokenRatesCompactData { +// bytes14 buy; // change buy rate of token from baseBuyRate in 10 bps +// bytes14 sell; // change sell rate of token from baseSellRate in 10 bps +// +// uint32 blockNumber; +// } +// +// function getDataFromCompact(TokenRatesCompactData compact, uint byteInd) public pure +// returns(int8 buyByte, int8 sellByte, uint blockNumber) +// { +// blockNumber = uint(compact.blockNumber); +//// return (compact.buy[byteInd], compact.sell[byteInd], uint(compact.blockNumber)); +// } + + function getCompactData(ConversionRates ratesContract, ERC20 token) internal view returns(int8,int8,uint) { + uint bulkIndex; uint index; byte buy; byte sell; uint updateBlock; + (bulkIndex, index, buy, sell) = ratesContract.getCompactData(token); + updateBlock = ratesContract.getRateUpdateBlock(token); + + return (int8(buy), int8(sell), updateBlock); + } + + function getTokenRates(ConversionRates ratesContract, ERC20[] tokenList) + public view + returns(uint[], uint[], int8[], int8[], uint[]) + { + uint[] memory buyBases = new uint[](tokenList.length); + uint[] memory sellBases = new uint[](tokenList.length); + int8[] memory compactBuy = new int8[](tokenList.length); + int8[] memory compactSell = new int8[](tokenList.length); + uint[] memory updateBlock = new uint[](tokenList.length); + + for (uint i = 0; i < tokenList.length; i++) { + buyBases[i] = ratesContract.getBasicRate(tokenList[i], true); + sellBases[i] = ratesContract.getBasicRate(tokenList[i], false); + + (compactBuy[i], compactSell[i], updateBlock[i]) = getCompactData(ratesContract, tokenList[i]); + } + + return (buyBases, sellBases, compactBuy, compactSell, updateBlock); + } + + function getTokenIndicies(ConversionRates ratesContract, ERC20[] tokenList) public view returns(uint[], uint[]) { + uint[] memory bulkIndices = new uint[](tokenList.length); + uint[] memory tokenIndexInBulk = new uint[](tokenList.length); + + for (uint i = 0; i < tokenList.length; i++) { + uint bulkIndex; uint index; byte buy; byte sell; + (bulkIndex, index, buy, sell) = ratesContract.getCompactData(tokenList[i]); + + bulkIndices[i] = bulkIndex; + tokenIndexInBulk[i] = index; + } + + return (bulkIndices,tokenIndexInBulk); + } + + + function getExpectedRates( KyberNetwork network, ERC20[] srcs, ERC20[] dests, uint[] qty ) + public view returns(uint[], uint[]) + { + require( srcs.length == dests.length ); + require( srcs.length == qty.length ); + + uint[] memory rates = new uint[](srcs.length); + uint[] memory slippage = new uint[](srcs.length); + for ( uint i = 0; i < srcs.length; i++ ) { + (rates[i],slippage[i]) = network.getExpectedRate(srcs[i],dests[i],qty[i]); + } + + return (rates, slippage); + } + + // iterate from startIndex to endIndex inclusive + function getListPermissionlessTokensAndDecimals( + KyberNetworkProxy networkProxy, + uint startIndex, + uint endIndex + ) + public + view + returns (ERC20[] memory permissionlessTokens, uint[] memory decimals, bool isEnded) + { + KyberNetwork network = KyberNetwork(networkProxy.kyberNetworkContract()); + uint numReserves = network.getNumReserves(); + if (startIndex >= numReserves || startIndex > endIndex) { + // no need to iterate + permissionlessTokens = new ERC20[](0); + decimals = new uint[](0); + isEnded = true; + return (permissionlessTokens, decimals, isEnded); + } + uint endIterator = numReserves <= endIndex ? numReserves - 1 : endIndex; + uint numberTokens = 0; + uint rID; // reserveID + ERC20 token; + KyberReserveInterface reserve; + // count number of tokens in unofficial reserves + for(rID = startIndex; rID <= endIterator; rID++) { + reserve = network.reserves(rID); + if ( reserve != address(0) + && network.reserveType(reserve) == KyberNetwork.ReserveType.PERMISSIONLESS) + { + // permissionless reserve + (, token , , , ,) = OrderbookReserve(reserve).contracts(); + if (token != address(0)) { numberTokens += 1; } + } + } + permissionlessTokens = new ERC20[](numberTokens); + decimals = new uint[](numberTokens); + numberTokens = 0; + // get final list of tokens and decimals in unofficial reserves + for(rID = startIndex; rID <= endIterator; rID++) { + reserve = network.reserves(rID); + if ( reserve != address(0) + && network.reserveType(reserve) == KyberNetwork.ReserveType.PERMISSIONLESS) + { + // permissionless reserve + (, token , , , ,) = OrderbookReserve(reserve).contracts(); + if (token != address(0)) { + permissionlessTokens[numberTokens] = token; + decimals[numberTokens] = getDecimals(token); + numberTokens += 1; + } + } + } + isEnded = endIterator == numReserves - 1; + return (permissionlessTokens, decimals, isEnded); + } +} diff --git a/contracts/token/BurnableERC20.sol b/contracts/token/BurnableERC20.sol index 54a4716..b2c1514 100644 --- a/contracts/token/BurnableERC20.sol +++ b/contracts/token/BurnableERC20.sol @@ -1,9 +1,10 @@ pragma solidity ^0.4.24; -// @title An interface to interact with Burnable ERC20 tokens -interface BurnableERC20 { - - function allowance(address tokenOwner, address spender) external view returns (uint remaining); - function burnFrom(address _tokenHolder, uint _amount) external returns (bool success); - +// @title An interface to interact with Burnable ERC20 tokens +interface BurnableERC20 { + function decimals() external view returns (uint8); + function balanceOf(address tokenOwner) external view returns (uint balance); + function allowance(address tokenOwner, address spender) external view returns (uint remaining); + function burn(uint _amount) external returns (bool success); + function burnFrom(address _tokenHolder, uint _amount) external returns (bool success); } diff --git a/contracts/token/ERC20.sol b/contracts/token/ERC20.sol index c8cc55f..1e2c2bb 100644 --- a/contracts/token/ERC20.sol +++ b/contracts/token/ERC20.sol @@ -1,184 +1,17 @@ -pragma solidity ^0.4.24; - -// Standard ERC20 contract with Burning capabilities. -// https://theethereum.wiki/w/index.php/ERC20_Token_Standard - -import '../SafeMath.sol'; -import './ERC20Interface.sol'; - +pragma solidity 0.4.24; // ---------------------------------------------------------------------------- -// Receive approval and then execute function +// ERC Token Standard #20 Interface +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md // ---------------------------------------------------------------------------- -contract ApproveAndCallFallBack { - function receiveApproval(address from, uint tokens, address token, bytes data) public; -} - -// ------------------------------------------------------------------------ -// Standard ERC20 Token Contract. -// Fixed Supply with burn capabilities -// ------------------------------------------------------------------------ -contract ERC20 is ERC20Interface{ - using SafeMath for uint; - - // ------------------------------------------------------------------------ - /// Token supply, balances and allowance - // ------------------------------------------------------------------------ - uint internal supply; - mapping (address => uint) internal balances; - mapping (address => mapping (address => uint)) internal allowed; - - // ------------------------------------------------------------------------ - // Token Information - // ------------------------------------------------------------------------ - string public name; // Full Token name - uint8 public decimals; // How many decimals to show - string public symbol; // An identifier - - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - constructor(uint _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) - public { - balances[msg.sender] = _initialAmount; // Give the creator all initial tokens - supply = _initialAmount; // Update total supply - name = _tokenName; // Set the name for display purposes - decimals = _decimalUnits; // Amount of decimals for display purposes - symbol = _tokenSymbol; // Set the symbol for display purposes - emit Transfer(address(0), msg.sender, _initialAmount); // Transfer event indicating token creation - } - - - // ------------------------------------------------------------------------ - // Transfer _amount tokens to address _to - // Sender must have enough tokens. Cannot send to 0x0. - // ------------------------------------------------------------------------ - function transfer(address _to, uint _amount) - public - returns (bool success) { - require(_to != address(0)); // Use burn() function instead - require(_to != address(this)); - balances[msg.sender] = balances[msg.sender].sub(_amount); - balances[_to] = balances[_to].add(_amount); - emit Transfer(msg.sender, _to, _amount); - return true; - } - - // ------------------------------------------------------------------------ - // Transfer _amount of tokens if _from has allowed msg.sender to do so - // _from must have enough tokens + must have approved msg.sender - // ------------------------------------------------------------------------ - function transferFrom(address _from, address _to, uint _amount) - public - returns (bool success) { - require(_to != address(0)); - require(_to != address(this)); - balances[_from] = balances[_from].sub(_amount); - allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); - balances[_to] = balances[_to].add(_amount); - emit Transfer(_from, _to, _amount); - return true; - } - - // ------------------------------------------------------------------------ - // Token owner can approve for `spender` to transferFrom(...) `tokens` - // from the token owner's account - // ------------------------------------------------------------------------ - function approve(address _spender, uint _amount) - public - returns (bool success) { - allowed[msg.sender][_spender] = _amount; - emit Approval(msg.sender, _spender, _amount); - return true; - } - - - // ------------------------------------------------------------------------ - // Token holder can notify a contract that it has been approved - // to spend _amount of tokens - // ------------------------------------------------------------------------ - function approveAndCall(address _spender, uint _amount, bytes _data) - public - returns (bool success) { - allowed[msg.sender][_spender] = _amount; - emit Approval(msg.sender, _spender, _amount); - ApproveAndCallFallBack(_spender).receiveApproval(msg.sender, _amount, this, _data); - return true; - } - - // ------------------------------------------------------------------------ - // Removes senders tokens from supply. - // Lowers user balance and totalSupply by _amount - // ------------------------------------------------------------------------ - function burn(uint _amount) - public - returns (bool success) { - balances[msg.sender] = balances[msg.sender].sub(_amount); - supply = supply.sub(_amount); - emit LogBurn(msg.sender, _amount); - emit Transfer(msg.sender, address(0), _amount); - return true; - } - - // ------------------------------------------------------------------------ - // An approved sender can burn _amount tokens of user _from - // Lowers user balance and supply by _amount - // ------------------------------------------------------------------------ - function burnFrom(address _from, uint _amount) - public - returns (bool success) { - require(allowed[_from][msg.sender] >= _amount); //Shouldn't be able to subtract from user's balance if the call won't got through - balances[_from] = balances[_from].sub(_amount); // Subtract from the targeted balance - allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); // Subtract from the sender's allowance - supply = supply.sub(_amount); // Update supply - emit LogBurn(_from, _amount); - emit Transfer(_from, address(0), _amount); - return true; - } - - // ------------------------------------------------------------------------ - // Returns the number of tokens in circulation - // ------------------------------------------------------------------------ - function totalSupply() - public - view - returns (uint tokenSupply) { - return supply; - } - - // ------------------------------------------------------------------------ - // Returns the token balance of user - // ------------------------------------------------------------------------ - function balanceOf(address _tokenHolder) - public - view - returns (uint balance) { - return balances[_tokenHolder]; - } - - // ------------------------------------------------------------------------ - // Returns amount of tokens _spender is allowed to transfer or burn - // ------------------------------------------------------------------------ - function allowance(address _tokenHolder, address _spender) - public - view - returns (uint remaining) { - return allowed[_tokenHolder][_spender]; - } - - - // ------------------------------------------------------------------------ - // Fallback function - // Won't accept ETH - // ------------------------------------------------------------------------ - function () - public - payable { - revert(); - } - - // ------------------------------------------------------------------------ - // Event: Logs the amount of tokens burned and the address of the burner - // ------------------------------------------------------------------------ - event LogBurn(address indexed _burner, uint indexed _amountBurned); +interface ERC20 { + function decimals() external view returns (uint8); + function totalSupply() external view returns (uint); + function balanceOf(address tokenOwner) external view returns (uint balance); + function allowance(address tokenOwner, address spender) external view returns (uint remaining); + function transfer(address to, uint tokens) external returns (bool success); + function approve(address spender, uint tokens) external returns (bool success); + function transferFrom(address from, address to, uint tokens) external returns (bool success); + + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } diff --git a/contracts/token/ERC20Interface.sol b/contracts/token/ERC20Interface.sol deleted file mode 100644 index c638373..0000000 --- a/contracts/token/ERC20Interface.sol +++ /dev/null @@ -1,16 +0,0 @@ -pragma solidity 0.4.24; -// ---------------------------------------------------------------------------- -// ERC Token Standard #20 Interface -// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md -// ---------------------------------------------------------------------------- -contract ERC20Interface { - function totalSupply() public constant returns (uint); - function balanceOf(address tokenOwner) public constant returns (uint balance); - function allowance(address tokenOwner, address spender) public constant returns (uint remaining); - function transfer(address to, uint tokens) public returns (bool success); - function approve(address spender, uint tokens) public returns (bool success); - function transferFrom(address from, address to, uint tokens) public returns (bool success); - - event Transfer(address indexed from, address indexed to, uint tokens); - event Approval(address indexed tokenOwner, address indexed spender, uint tokens); -} diff --git a/contracts/token/IERC721.sol b/contracts/token/ERC721.sol similarity index 58% rename from contracts/token/IERC721.sol rename to contracts/token/ERC721.sol index 78875ec..2e872ce 100644 --- a/contracts/token/IERC721.sol +++ b/contracts/token/ERC721.sol @@ -5,22 +5,22 @@ pragma solidity ^0.4.24; * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md */ -contract IERC721 { +interface ERC721 { event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - function balanceOf(address owner) public view returns (uint256 balance); - function ownerOf(uint256 tokenId) public view returns (address owner); + function balanceOf(address owner) external view returns (uint256 balance); + function ownerOf(uint256 tokenId) external view returns (address owner); - function approve(address to, uint256 tokenId) public; - function getApproved(uint256 tokenId) public view returns (address operator); + function approve(address to, uint256 tokenId) external; + function getApproved(uint256 tokenId) external view returns (address operator); - function setApprovalForAll(address operator, bool _approved) public; - function isApprovedForAll(address owner, address operator) public view returns (bool); + function setApprovalForAll(address operator, bool _approved) external; + function isApprovedForAll(address owner, address operator) external view returns (bool); - function transferFrom(address from, address to, uint256 tokenId) public; + function transferFrom(address from, address to, uint256 tokenId) external; -// function safeTransferFrom(address from, address to, uint256 tokenId) public; -// function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) public; -} \ No newline at end of file +// function safeTransferFrom(address from, address to, uint256 tokenId) external; +// function safeTransferFrom(address from, address to, uint256 tokenId, bytes data) external; +} diff --git a/contracts/token/SampleERC20.sol b/contracts/token/SampleERC20.sol new file mode 100644 index 0000000..1cfdf82 --- /dev/null +++ b/contracts/token/SampleERC20.sol @@ -0,0 +1,191 @@ +pragma solidity ^0.4.24; + +// Standard ERC20 contract with Burning capabilities. +// https://theethereum.wiki/w/index.php/ERC20_Token_Standard + +import '../SafeMath.sol'; +import './ERC20.sol'; + +// ---------------------------------------------------------------------------- +// Receive approval and then execute function +// ---------------------------------------------------------------------------- +contract ApproveAndCallFallBack { + function receiveApproval(address from, uint tokens, address token, bytes data) public; +} + +// ------------------------------------------------------------------------ +// Standard ERC20 Token Contract. +// Fixed Supply with burn capabilities +// ------------------------------------------------------------------------ +contract SampleERC20 is ERC20{ + using SafeMath for uint; + + // ------------------------------------------------------------------------ + /// Token supply, balances and allowance + // ------------------------------------------------------------------------ + uint internal supply; + mapping (address => uint) internal balances; + mapping (address => mapping (address => uint)) internal allowed; + + // ------------------------------------------------------------------------ + // Token Information + // ------------------------------------------------------------------------ + string public name; // Full Token name + uint8 public decimals; // How many decimals to show + string public symbol; // An identifier + + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + constructor(uint _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) + public { + balances[msg.sender] = _initialAmount; // Give the creator all initial tokens + supply = _initialAmount; // Update total supply + name = _tokenName; // Set the name for display purposes + decimals = _decimalUnits; // Amount of decimals for display purposes + symbol = _tokenSymbol; // Set the symbol for display purposes + emit Transfer(address(0), msg.sender, _initialAmount); // Transfer event indicating token creation + } + + + // ------------------------------------------------------------------------ + // Transfer _amount tokens to address _to + // Sender must have enough tokens. Cannot send to 0x0. + // ------------------------------------------------------------------------ + function transfer(address _to, uint _amount) + public + returns (bool success) { + require(_to != address(0)); // Use burn() function instead + require(_to != address(this)); + balances[msg.sender] = balances[msg.sender].sub(_amount); + balances[_to] = balances[_to].add(_amount); + emit Transfer(msg.sender, _to, _amount); + return true; + } + + // ------------------------------------------------------------------------ + // Transfer _amount of tokens if _from has allowed msg.sender to do so + // _from must have enough tokens + must have approved msg.sender + // ------------------------------------------------------------------------ + function transferFrom(address _from, address _to, uint _amount) + public + returns (bool success) { + require(_to != address(0)); + require(_to != address(this)); + balances[_from] = balances[_from].sub(_amount); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); + balances[_to] = balances[_to].add(_amount); + emit Transfer(_from, _to, _amount); + return true; + } + + // ------------------------------------------------------------------------ + // Token owner can approve for `spender` to transferFrom(...) `tokens` + // from the token owner's account + // ------------------------------------------------------------------------ + function approve(address _spender, uint _amount) + public + returns (bool success) { + allowed[msg.sender][_spender] = _amount; + emit Approval(msg.sender, _spender, _amount); + return true; + } + + + // ------------------------------------------------------------------------ + // Token holder can notify a contract that it has been approved + // to spend _amount of tokens + // ------------------------------------------------------------------------ + function approveAndCall(address _spender, uint _amount, bytes _data) + public + returns (bool success) { + allowed[msg.sender][_spender] = _amount; + emit Approval(msg.sender, _spender, _amount); + ApproveAndCallFallBack(_spender).receiveApproval(msg.sender, _amount, this, _data); + return true; + } + + // ------------------------------------------------------------------------ + // Removes senders tokens from supply. + // Lowers user balance and totalSupply by _amount + // ------------------------------------------------------------------------ + function burn(uint _amount) + public + returns (bool success) { + balances[msg.sender] = balances[msg.sender].sub(_amount); + supply = supply.sub(_amount); + emit LogBurn(msg.sender, _amount); + emit Transfer(msg.sender, address(0), _amount); + return true; + } + + // ------------------------------------------------------------------------ + // An approved sender can burn _amount tokens of user _from + // Lowers user balance and supply by _amount + // ------------------------------------------------------------------------ + function burnFrom(address _from, uint _amount) + public + returns (bool success) { + require(allowed[_from][msg.sender] >= _amount); //Shouldn't be able to subtract from user's balance if the call won't got through + balances[_from] = balances[_from].sub(_amount); // Subtract from the targeted balance + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_amount); // Subtract from the sender's allowance + supply = supply.sub(_amount); // Update supply + emit LogBurn(_from, _amount); + emit Transfer(_from, address(0), _amount); + return true; + } + + // ------------------------------------------------------------------------ + // Returns the number of tokens in circulation + // ------------------------------------------------------------------------ + function totalSupply() + public + view + returns (uint tokenSupply) { + return supply; + } + + // ------------------------------------------------------------------------ + // Returns the number of decimals to display + // ------------------------------------------------------------------------ + function decimals() public view returns (uint8) { + return decimals; + } + + // ------------------------------------------------------------------------ + // Returns the token balance of user + // ------------------------------------------------------------------------ + function balanceOf(address _tokenHolder) + public + view + returns (uint balance) { + return balances[_tokenHolder]; + } + + // ------------------------------------------------------------------------ + // Returns amount of tokens _spender is allowed to transfer or burn + // ------------------------------------------------------------------------ + function allowance(address _tokenHolder, address _spender) + public + view + returns (uint remaining) { + return allowed[_tokenHolder][_spender]; + } + + + // ------------------------------------------------------------------------ + // Fallback function + // Won't accept ETH + // ------------------------------------------------------------------------ + function () + public + payable { + revert(); + } + + // ------------------------------------------------------------------------ + // Event: Logs the amount of tokens burned and the address of the burner + // ------------------------------------------------------------------------ + event LogBurn(address indexed _burner, uint indexed _amountBurned); +} diff --git a/contracts/token/SampleERC721.sol b/contracts/token/SampleERC721.sol index 3f503fe..faae669 100644 --- a/contracts/token/SampleERC721.sol +++ b/contracts/token/SampleERC721.sol @@ -1,13 +1,13 @@ pragma solidity ^0.4.0; -import "./IERC721.sol"; +import "./ERC721.sol"; import "../SafeMath.sol"; /** * @title Sample NFT * @dev ERC721 dummy token for tests */ -contract SampleERC721 is IERC721{ +contract SampleERC721 is ERC721{ using SafeMath for uint256; diff --git a/migrations/2_deploy_trust.js b/migrations/2_deploy_trust.js index 8251dcd..2abc7a6 100644 --- a/migrations/2_deploy_trust.js +++ b/migrations/2_deploy_trust.js @@ -1,101 +1,111 @@ -const fs = require('fs'); -const bn = require('bignumber.js'); const SafeMath = artifacts.require("./SafeMath.sol"); -const Token = artifacts.require("./token/ERC20.sol"); +const Token = artifacts.require("./token/SampleERC20.sol"); const NFT = artifacts.require("./token/SampleERC721.sol"); const Trust = artifacts.require("./Trust.sol"); const TrustFactory = artifacts.require("./TrustFactory.sol"); const MyBitBurner = artifacts.require("./MyBitBurner.sol"); +const fs = require('fs'); +const bn = require('bignumber.js'); +bn.config({ EXPONENTIAL_AT: 80 }); module.exports = function(deployer, network, accounts) { - const WEI = 10**18; - const tokenSupply = 100000000*WEI; // 100 million - - var myb, erc20, trust, trustFactory, burner, nft; - - deployer.then(function(){ - - return deployer.deploy(SafeMath); + if(network != 'coverage' && network != 'development'){ + const WEI = bn(10**18); + const tokenSupply = bn(100000000).times(WEI); // 100 million + + let myb, erc20, trust, trustFactory, burner, kyber, nft; + + if(network == 'mainnet'){ + kyber = '0x818E6FECD516Ecc3849DAf6845e3EC868087B755'; + } else if (network == 'ropsten'){ + kyber = '0x818E6FECD516Ecc3849DAf6845e3EC868087B755'; + } else { + kyber = '0x0000000000000000000000000000000000000000'; + } - }).then(function(){ + deployer.then(function(){ - //Link safemath library - deployer.link(SafeMath, - Token, - Trust, - MyBitBurner); + return deployer.deploy(SafeMath); - return Token.new(tokenSupply, "MyBit", 18, "MYB"); + }).then(function(){ - }).then(function(instance) { + //Link safemath library + deployer.link(SafeMath, + Token, + Trust, + MyBitBurner); - myb = instance; - for(var i=1; i { - if (err) throw err; - console.log('Contracts Saved'); - }); - fs.writeFile('networks/' + network + '/accounts.json', accounts_json, (err) => { - if (err) throw err; - console.log('Accounts Saved'); - }); + }).then(function() { - var instanceList = [{name:"MyBitToken", instance:myb}, {name:"ERC20", instance:erc20}, {name:"NonFungibleToken", instance:nft}, {name:"MyBitBurner", instance:burner}, {name:"TrustFactory", instance:trustFactory}]; + var addresses = { + "MyBitToken" : myb.address, + "ERC20Token" : erc20.address, + "NonFungibleToken" : nft.address, + "MyBitBurner" : burner.address, + "TrustFactory" : trustFactory.address + }; - for(var i=0; i { + var contracts_json = JSON.stringify(addresses, null, 4); + var accounts_json = JSON.stringify(accounts, null, 4); + fs.writeFile('networks/' + network + '/contracts.json', contracts_json, (err) => { if (err) throw err; + console.log('Contracts Saved'); + }); + fs.writeFile('networks/' + network + '/accounts.json', accounts_json, (err) => { + if (err) throw err; + console.log('Accounts Saved'); }); - } - console.log('JS Saved'); - }); -}; + var instanceList = [{name:"MyBitToken", instance:myb}, {name:"ERC20", instance:erc20}, {name:"NonFungibleToken", instance:nft}, {name:"MyBitBurner", instance:burner}, {name:"TrustFactory", instance:trustFactory}]; + + for(var i=0; i { + if (err) throw err; + }); + } + console.log('JS Saved'); + }); + } +}; diff --git a/networks/development/ERC20.js b/networks/development/ERC20.js new file mode 100644 index 0000000..564ccd9 --- /dev/null +++ b/networks/development/ERC20.js @@ -0,0 +1,348 @@ +export const ADDRESS = '0x8fcB9bB096098C3457E28f68D95A21FA4E675a94'; +export const ABI = [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "inputs": [ + { + "name": "_initialAmount", + "type": "uint256" + }, + { + "name": "_tokenName", + "type": "string" + }, + { + "name": "_decimalUnits", + "type": "uint8" + }, + { + "name": "_tokenSymbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_burner", + "type": "address" + }, + { + "indexed": true, + "name": "_amountBurned", + "type": "uint256" + } + ], + "name": "LogBurn", + "type": "event", + "signature": "0x38d762ef507761291a578e921acfe29c1af31a7331ea03e391cf16cfc4d4f581" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "tokens", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "tokenOwner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "tokens", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa9059cbb" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "approveAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xcae9ca51" + }, + { + "constant": false, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x42966c68" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x79cc6790" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "tokenSupply", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x18160ddd" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenHolder", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenHolder", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" + } +]; \ No newline at end of file diff --git a/networks/development/MyBitBurner.js b/networks/development/MyBitBurner.js new file mode 100644 index 0000000..d1bce4b --- /dev/null +++ b/networks/development/MyBitBurner.js @@ -0,0 +1,254 @@ +export const ADDRESS = '0x83531556471a720Bea45F3dE8daB27b2f910C129'; +export const ABI = [ + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x8da5cb5b" + }, + { + "constant": true, + "inputs": [], + "name": "kyber", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xa2d10ba5" + }, + { + "constant": true, + "inputs": [], + "name": "mybToken", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xbdad900d" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "authorizedBurner", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xeac0448d" + }, + { + "inputs": [ + { + "name": "_myBitTokenAddress", + "type": "address" + }, + { + "name": "_kyberAddress", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_tokenHolder", + "type": "address" + }, + { + "indexed": true, + "name": "_burningContract", + "type": "address" + }, + { + "indexed": false, + "name": "_amount", + "type": "uint256" + } + ], + "name": "LogMYBBurned", + "type": "event", + "signature": "0xc87395c9a7116ce3ca8faa3215953102d6de8710810272a66800b11bbb588aa6" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "_owner", + "type": "address" + }, + { + "indexed": false, + "name": "_burningContract", + "type": "address" + } + ], + "name": "LogBurnerAuthorized", + "type": "event", + "signature": "0xeac9f8ba2060889a150970f57c3dc197c983f39a315533d66c967fcdcb6c14f3" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "_owner", + "type": "address" + }, + { + "indexed": false, + "name": "_burningContract", + "type": "address" + } + ], + "name": "LogBurnerRemoved", + "type": "event", + "signature": "0xecd371131643ad82a1ff7354a1c2c90d3f05c2b4eddec9c137d8f546922edb4f" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "src", + "type": "address" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "name": "dest", + "type": "address" + }, + { + "indexed": false, + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "name": "max", + "type": "uint256" + }, + { + "indexed": false, + "name": "minRate", + "type": "uint256" + }, + { + "indexed": false, + "name": "walletID", + "type": "address" + } + ], + "name": "LogTrade", + "type": "event", + "signature": "0xa128b2b1a4d54009d705dd70a0f79ecdb40e4c338e484124fa54ace8c2a7acf3" + }, + { + "constant": false, + "inputs": [ + { + "name": "_tokenHolder", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_burnToken", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0xb8ce670d" + }, + { + "constant": false, + "inputs": [ + { + "name": "_burningContract", + "type": "address" + } + ], + "name": "authorizeBurner", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x34376ca0" + }, + { + "constant": false, + "inputs": [ + { + "name": "_burningContract", + "type": "address" + } + ], + "name": "removeBurner", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x02846858" + } +]; \ No newline at end of file diff --git a/networks/development/MyBitToken.js b/networks/development/MyBitToken.js new file mode 100644 index 0000000..5d244aa --- /dev/null +++ b/networks/development/MyBitToken.js @@ -0,0 +1,348 @@ +export const ADDRESS = '0x7E0380dc5aD2a53BA4FeFdE4CaD0F2DAe130e5b8'; +export const ABI = [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x06fdde03" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x95d89b41" + }, + { + "inputs": [ + { + "name": "_initialAmount", + "type": "uint256" + }, + { + "name": "_tokenName", + "type": "string" + }, + { + "name": "_decimalUnits", + "type": "uint8" + }, + { + "name": "_tokenSymbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_burner", + "type": "address" + }, + { + "indexed": true, + "name": "_amountBurned", + "type": "uint256" + } + ], + "name": "LogBurn", + "type": "event", + "signature": "0x38d762ef507761291a578e921acfe29c1af31a7331ea03e391cf16cfc4d4f581" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "tokens", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "tokenOwner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "tokens", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa9059cbb" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "approveAndCall", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xcae9ca51" + }, + { + "constant": false, + "inputs": [ + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x42966c68" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x79cc6790" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "tokenSupply", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x18160ddd" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x313ce567" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenHolder", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenHolder", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xdd62ed3e" + } +]; \ No newline at end of file diff --git a/networks/development/NonFungibleToken.js b/networks/development/NonFungibleToken.js new file mode 100644 index 0000000..209ee80 --- /dev/null +++ b/networks/development/NonFungibleToken.js @@ -0,0 +1,263 @@ +export const ADDRESS = '0x7a4F4658863a79Ca196252D31f22B5E8a49c24d1'; +export const ABI = [ + { + "constant": true, + "inputs": [], + "name": "_owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xb2bdfa7b" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": true, + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event", + "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event", + "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event", + "signature": "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x40c10f19" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x70a08231" + }, + { + "constant": true, + "inputs": [ + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x6352211e" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x095ea7b3" + }, + { + "constant": true, + "inputs": [ + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x081812fc" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xa22cb465" + }, + { + "constant": true, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xe985e9c5" + }, + { + "constant": false, + "inputs": [ + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x23b872dd" + } +]; \ No newline at end of file diff --git a/networks/development/TrustFactory.js b/networks/development/TrustFactory.js new file mode 100644 index 0000000..fd2b0a4 --- /dev/null +++ b/networks/development/TrustFactory.js @@ -0,0 +1,286 @@ +export const ADDRESS = '0x57328E20CB87874cB571DCcC0f3195E9313f2700'; +export const ABI = [ + { + "constant": true, + "inputs": [], + "name": "expired", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x4c2067c7" + }, + { + "constant": true, + "inputs": [], + "name": "mybFee", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x80cb11a6" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0x8da5cb5b" + }, + { + "constant": true, + "inputs": [], + "name": "mybBurner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function", + "signature": "0xef9fa23c" + }, + { + "inputs": [ + { + "name": "_mybTokenBurner", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor", + "signature": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_trustor", + "type": "address" + }, + { + "indexed": true, + "name": "_beneficiary", + "type": "address" + }, + { + "indexed": false, + "name": "_trustAddress", + "type": "address" + }, + { + "indexed": false, + "name": "_amount", + "type": "uint256" + } + ], + "name": "LogNewTrust", + "type": "event", + "signature": "0x693384e1ab379d22cb2a64d90d94ef5ae0464a1582c357aa38cb40c04930c67a" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_trustor", + "type": "address" + }, + { + "indexed": true, + "name": "_beneficiary", + "type": "address" + }, + { + "indexed": false, + "name": "_trustAddress", + "type": "address" + } + ], + "name": "LogNewTrustERC20", + "type": "event", + "signature": "0x79272e4b4e78746822c5edf64ff0318b76c0740ef55ccac1dc7a3f63d2367fa4" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_trustor", + "type": "address" + }, + { + "indexed": true, + "name": "_beneficiary", + "type": "address" + }, + { + "indexed": false, + "name": "_trustAddress", + "type": "address" + } + ], + "name": "LogNewTrustERC721", + "type": "event", + "signature": "0x9003de4faa116c5cf141e0b2f34b82c6178a7a08ef0d0e835677d003a3b54a97" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "_oldFee", + "type": "uint256" + }, + { + "indexed": false, + "name": "_newFee", + "type": "uint256" + } + ], + "name": "LogMYBFeeChange", + "type": "event", + "signature": "0x335805f4e6fab172c9ec930ef512eab0affc6a0f0c672642b83ecf379b39866d" + }, + { + "constant": false, + "inputs": [ + { + "name": "_beneficiary", + "type": "address" + }, + { + "name": "_revokeable", + "type": "bool" + }, + { + "name": "_expiration", + "type": "uint256" + }, + { + "name": "_burnToken", + "type": "address" + } + ], + "name": "deployTrust", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0x8a3ffe93" + }, + { + "constant": false, + "inputs": [ + { + "name": "_beneficiary", + "type": "address" + }, + { + "name": "_revokeable", + "type": "bool" + }, + { + "name": "_expiration", + "type": "uint256" + }, + { + "name": "_tokenContractAddress", + "type": "address" + }, + { + "name": "_burnToken", + "type": "address" + } + ], + "name": "createTrustERC20", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0x9214964f" + }, + { + "constant": false, + "inputs": [ + { + "name": "_beneficiary", + "type": "address" + }, + { + "name": "_revokeable", + "type": "bool" + }, + { + "name": "_expiration", + "type": "uint256" + }, + { + "name": "_tokenContractAddress", + "type": "address" + }, + { + "name": "_burnToken", + "type": "address" + } + ], + "name": "createTrustERC721", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function", + "signature": "0xf51f5ba5" + }, + { + "constant": false, + "inputs": [], + "name": "closeFactory", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0xd1dff3ab" + }, + { + "constant": false, + "inputs": [ + { + "name": "_newFee", + "type": "uint256" + } + ], + "name": "changeMYBFee", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function", + "signature": "0x8871c163" + } +]; \ No newline at end of file diff --git a/networks/development/accounts.json b/networks/development/accounts.json index 5fb445c..decac01 100644 --- a/networks/development/accounts.json +++ b/networks/development/accounts.json @@ -1,12 +1,12 @@ [ - "0xcc757f6c70214527d50a05a6ce55d80bc8fff238", - "0xcd9caf5ce2533d64e59eba362c104eae2ec26043", - "0x84f4ee165de0e9cd1c3ca7c1fcccc88a448df653", - "0x767133e726cd2733c5312321d716d57262a3d22f", - "0x8efe48da73a7e32d68715a2829f46a8c7cfcd250", - "0x76151b2e2f8855fbc5562d41bf5f0ba1f26c2beb", - "0x990ac98feddb365a13232eeceeda03494d7e0220", - "0x8f068fb227d63b8bfe15e78162d5fb11a701fe6b", - "0x14f01f1a5cce2d7152403c54d131415226887e63", - "0xb39d49ad933d8cf4aad77fecbd0f3cc0865f1e13" + "0xD888B6AdCc19032bdd0Bd236DD312AdF4996acb4", + "0xC8e26FCd871682437D790cCd203C21B615a6C811", + "0xeF535C4FE0ffc25Bd93391285AC63B554466f8BB", + "0x573970472Fc4535a0832d07B463AC9368ecfB961", + "0x22D5ca998a21C4ACd931F23b6FbcCCbdFcf35729", + "0x99C838179aAfbE99b6B21b2CBD2d716c4fdCE03e", + "0x8CD5F905A4029a64aBf27e5e998f1cAA571E07A8", + "0x7A10E285A2f9952B008b9F70dC5326F0e9879028", + "0x68676b54eC725202418C38A3Ce7bbb4a44f3741d", + "0xa0cB13c2164a95905D605be74edd8d6341857f84" ] \ No newline at end of file diff --git a/networks/development/contracts.json b/networks/development/contracts.json index c676a19..759db01 100644 --- a/networks/development/contracts.json +++ b/networks/development/contracts.json @@ -1,6 +1,7 @@ { - "MyBitToken": "0xf36a1143eaca05757c61058877ba4568a612dc28", - "ERC20Token": "0x4b328fcc26aa1da766e1c0532f97f64e5c9153eb", - "MyBitBurner": "0xfb5f50a3673c00b7c7a3f8655fcac2b0078732e0", - "TrustFactory": "0x8d6086c7698517d375a1681aae76507b534eb620" + "MyBitToken": "0x7E0380dc5aD2a53BA4FeFdE4CaD0F2DAe130e5b8", + "ERC20Token": "0x8fcB9bB096098C3457E28f68D95A21FA4E675a94", + "NonFungibleToken": "0x7a4F4658863a79Ca196252D31f22B5E8a49c24d1", + "MyBitBurner": "0x83531556471a720Bea45F3dE8daB27b2f910C129", + "TrustFactory": "0x57328E20CB87874cB571DCcC0f3195E9313f2700" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9bafbe9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2132 @@ +{ + "name": "dapp-trust", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, + "bindings": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.4.0.tgz", + "integrity": "sha512-7znEVX22Djn+nYjxCWKDne0RRloa9XfYa84yk3s+HkE3LpDYZmhArYr9O9huBoHY3/oXispx5LorIX7Sl2CgSQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "browserify-sha3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.4.tgz", + "integrity": "sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY=", + "dev": true, + "requires": { + "js-sha3": "^0.6.1", + "safe-buffer": "^5.1.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "command-exists": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz", + "integrity": "sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==" + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "coveralls": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.3.tgz", + "integrity": "sha512-viNfeGlda2zJr8Gj1zqXpDMRjw9uM54p7wzZdvLRyOgnAfCe974Dq4veZkjJdxQXbmdppu6flEajFYseHYaUhg==", + "requires": { + "growl": "~> 1.10.0", + "js-yaml": "^3.11.0", + "lcov-parse": "^0.0.10", + "log-driver": "^1.2.7", + "minimist": "^1.2.0", + "request": "^2.86.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "ethereumjs-testrpc-sc": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/ethereumjs-testrpc-sc/-/ethereumjs-testrpc-sc-6.1.6.tgz", + "integrity": "sha512-iv2qiGBFgk9mn5Nq2enX8dG5WQ7Lk+FCqpnxfPfH4Ns8KLPwttmNOy264nh3SXDJJvcQwz/XnlLteDQVILotbg==", + "dev": true, + "requires": { + "source-map-support": "^0.5.3" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "ganache-cli": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ganache-cli/-/ganache-cli-6.3.0.tgz", + "integrity": "sha512-8SyzfX2ipRVBx1fBZLg3j8I3E334U3Vazk5mEpYcWqnIjC2ace6jtOXHG4aTuAvSz3+HzQ8p8pRjOJxdDZ2pnQ==", + "requires": { + "bn.js": "4.11.8", + "source-map-support": "0.5.9", + "yargs": "11.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "bn.js": { + "version": "4.11.8", + "bundled": true + }, + "buffer-from": { + "version": "1.1.1", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "bundled": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.4", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^3.0.2" + } + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-limit": { + "version": "1.3.0", + "bundled": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "source-map": { + "version": "0.6.1", + "bundled": true + }, + "source-map-support": { + "version": "0.5.9", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "y18n": { + "version": "3.2.1", + "bundled": true + }, + "yallist": { + "version": "3.0.2", + "bundled": true + }, + "yargs": { + "version": "11.1.0", + "bundled": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + }, + "handlebars": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", + "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", + "dev": true, + "requires": { + "async": "^2.5.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "js-sha3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.6.1.tgz", + "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "keccakjs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", + "integrity": "sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg==", + "dev": true, + "requires": { + "browserify-sha3": "^0.0.4", + "sha3": "^1.2.2" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lcov-parse": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", + "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "requires": { + "mime-db": "~1.38.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==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original-require": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/original-require/-/original-require-1.0.1.tgz", + "integrity": "sha1-DxMEcVhM0zURxew4yNWSE/msXiA=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "req-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-1.0.1.tgz", + "integrity": "sha1-DXOurpJm5penj3l2AZZ352rPD/8=", + "dev": true, + "requires": { + "req-from": "^1.0.1" + } + }, + "req-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-1.0.1.tgz", + "integrity": "sha1-v4HaUUeUfTLRO5R9wSpYrUWHNQ4=", + "dev": true, + "requires": { + "resolve-from": "^2.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "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==" + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "sha3": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.2.tgz", + "integrity": "sha1-pmxQmN5MJbyIM27ItIF9AFvKe6k=", + "dev": true, + "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=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sol-explore": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/sol-explore/-/sol-explore-1.6.2.tgz", + "integrity": "sha1-Q66MQZ/TrAVqBfip0fsQIs1B7MI=", + "dev": true + }, + "solc": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.24.tgz", + "integrity": "sha512-2xd7Cf1HeVwrIb6Bu1cwY2/TaLRodrppCq3l7rhLimFQgmxptXhTC3+/wesVLpB09F1A2kZgvbMOgH7wvhFnBQ==", + "requires": { + "fs-extra": "^0.30.0", + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" + } + }, + "solidity-coverage": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.5.11.tgz", + "integrity": "sha512-qikdsSi6+9XbfvwA0aI7HUVpF9fIFNqRWTw23M89GMDY+b6Gj0wWU9IngJS0fimoZIAdEp3bfChxvpfVcrUesg==", + "dev": true, + "requires": { + "death": "^1.1.0", + "ethereumjs-testrpc-sc": "6.1.6", + "istanbul": "^0.4.5", + "keccakjs": "^0.2.1", + "req-cwd": "^1.0.1", + "shelljs": "^0.7.4", + "sol-explore": "^1.6.2", + "solidity-parser-sc": "0.4.11", + "tree-kill": "^1.2.0", + "web3": "^0.18.4" + } + }, + "solidity-parser-sc": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/solidity-parser-sc/-/solidity-parser-sc-0.4.11.tgz", + "integrity": "sha512-1kV5iC7m3CtMDfmHaVNwz2saSGQVIuF16rIxU417Al38MVCWHMQQ5vT6cmLsNwDe60S74auobWij9vNawSeOyw==", + "dev": true, + "requires": { + "mocha": "^4.1.0", + "pegjs": "^0.10.0", + "yargs": "^4.6.0" + } + }, + "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.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "requires": { + "has-flag": "^2.0.0" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "dev": true + }, + "truffle": { + "version": "5.0.0-beta.2", + "resolved": "https://registry.npmjs.org/truffle/-/truffle-5.0.0-beta.2.tgz", + "integrity": "sha512-D15MsJeKWRNxbx2Vmy50gH8z4gjBYecJIUADBBBL593hkVnhZ1ADgkIujCvvrbD6Pj69Vg5Ky/nJXl7M9TCjsg==", + "requires": { + "mocha": "^4.1.0", + "original-require": "1.0.1", + "solc": "^0.5.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "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=" + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "solc": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.5.4.tgz", + "integrity": "sha512-Jz3yz2mct0AYzR83/jBgxDqrLXTHhYUg2G2PVJbMMt5Vu+8e3Of1Mn3nvjPw5mh46jrzt8l4fBN7vHqG5ZF0cw==", + "requires": { + "command-exists": "^1.2.8", + "fs-extra": "^0.30.0", + "keccak": "^1.0.2", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "tmp": "0.0.33", + "yargs": "^11.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "yargs": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true, + "optional": true + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "web3": { + "version": "0.18.4", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.18.4.tgz", + "integrity": "sha1-gewXhBRUkfLqqJVbMcBgSeB8Xn0=", + "dev": true, + "requires": { + "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "crypto-js": "^3.1.4", + "utf8": "^2.1.1", + "xhr2": "*", + "xmlhttprequest": "*" + }, + "dependencies": { + "bignumber.js": { + "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "dev": true + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=", + "dev": true + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } +} diff --git a/package.json b/package.json index 64e3d99..4bda6af 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "ganache-cli": "^6.1.8", "request": "^2.87.0", "solc": "0.4.24", - "truffle": "4.1.14" + "truffle": "5.0.0-beta.2" }, "devDependencies": { "solidity-coverage": "^0.5.11" diff --git a/test/BurnableToken.js b/test/BurnableToken.js index a23d70e..70d0a18 100644 --- a/test/BurnableToken.js +++ b/test/BurnableToken.js @@ -1,17 +1,17 @@ var bn = require('bignumber.js'); -const Token = artifacts.require("./ERC20.sol"); +const Token = artifacts.require("./SampleERC20.sol"); -const owner = web3.eth.accounts[0]; -const user1 = web3.eth.accounts[1]; -const user2 = web3.eth.accounts[2]; -const tokenHolders = [user1, user2]; +const tokenSupply = '180000000000000000000000000'; +const tokenPerAccount = '1000000000000000000000'; -const tokenSupply = 180000000000000000000000000; -const tokenPerAccount = 1000000000000000000000; +contract('Token', async(accounts) => { + const owner = accounts[0]; + const user1 = accounts[1]; + const user2 = accounts[2]; + const tokenHolders = [user1, user2]; -contract('Token', async() => { let token; it('Deploy Token', async() => { @@ -35,7 +35,7 @@ contract('Token', async() => { it('Fail to send ether to token contract', async() => { let err; try{ - await web3.eth.sendTransaction({from:user1, to: token.address, value: 10000}) + await web3.eth.sendTransaction({from:user1, to: token.address, value: '10000'}) } catch(e){ err = e; } @@ -45,7 +45,7 @@ contract('Token', async() => { it('Fail to transfer', async() => { let err; try{ - await token.transfer(token.address, 1000); + await token.transfer(token.address, '1000'); } catch(e){ err = e; } @@ -55,7 +55,7 @@ contract('Token', async() => { it('Fail to transfer', async() => { let err; try{ - await token.transfer(0, 1000); + await token.transfer(0, '1000'); } catch(e){ err = e; } @@ -65,7 +65,7 @@ contract('Token', async() => { it('Fail to transfer from', async() => { let err; try{ - await token.transferFrom(user1, token.address, 1000); + await token.transferFrom(user1, token.address, '1000'); } catch(e){ err = e; } @@ -75,7 +75,7 @@ contract('Token', async() => { it('Fail to transfer from', async() => { let err; try{ - await token.transferFrom(user1, 0, 1000); + await token.transferFrom(user1, 0, '1000'); } catch(e){ err = e; } @@ -88,24 +88,24 @@ contract('Token', async() => { }); it('Approve user', async() => { - await token.approve(user1, 10000, {from: user2}); - assert.equal(await token.allowance(user2, user1), 10000); + await token.approve(user1, '10000', {from: user2}); + assert.equal(await token.allowance(user2, user1), '10000'); }); it('Transfer From', async() => { - await token.transferFrom(user2, user1, 5000, {from: user1}); - assert.equal(await token.allowance(user2, user1), 5000); + await token.transferFrom(user2, user1, '5000', {from: user1}); + assert.equal(await token.allowance(user2, user1), '5000'); }); it('Burn tokens', async() => { - await token.burn(5000, {from: user1}); + await token.burn('5000', {from: user1}); assert.equal(await token.balanceOf(user1), tokenPerAccount); }); it('Fail to burn from', async() => { let err; try{ - await token.burnFrom(user2, 10000, {from: user1}); + await token.burnFrom(user2, '10000', {from: user1}); } catch(e){ err = e; } @@ -113,7 +113,7 @@ contract('Token', async() => { }); it('Burn From', async() => { - await token.burnFrom(user2, 5000, {from: user1}); + await token.burnFrom(user2, '5000', {from: user1}); assert.equal(await token.allowance(user2, user1), 0); }); diff --git a/test/Kyber.js b/test/Kyber.js new file mode 100644 index 0000000..3d3c794 --- /dev/null +++ b/test/Kyber.js @@ -0,0 +1,1069 @@ +const MyBitBurner = artifacts.require("./MyBitBurner.sol"); +const TrustFactory = artifacts.require("./TrustFactory.sol"); +const TestToken = artifacts.require("./SampleERC20.sol"); +const Reserve = artifacts.require("./KyberReserve.sol"); +const Network = artifacts.require("./KyberNetwork.sol"); +const NetworkProxy = artifacts.require("./KyberNetworkProxy.sol"); +const ConversionRates = artifacts.require("./ConversionRates.sol"); +const Bank = artifacts.require("./MockCentralBank.sol"); +const Whitelist = artifacts.require("./WhiteList.sol"); +const FeeBurner = artifacts.require("./FeeBurner.sol"); +const ExpectedRate = artifacts.require("./ExpectedRate.sol"); +const Wrapper = artifacts.require("./Wrapper.sol"); +const CentralizedExchange = artifacts.require("./MockExchange.sol"); +const BigNumber = require('bignumber.js'); +BigNumber.config({ EXPONENTIAL_AT: 80 }); + +const ETH = new BigNumber(10**18); +const tokenPerAccount = new BigNumber(10**40); + +let burner, factory; + +let operatorID, assetURI, assetAddress; + +var tokenSymbol = [];//["OMG", "DGD", "CVC", "FUN", "MCO", "GNT", "ADX", "PAY", + //"BAT", "KNC", "EOS", "LINK"]; +var tokenName = [];//[ "OmiseGO", "Digix", "Civic", "FunFair", "Monaco", "Golem", +//"Adex", "TenX", "BasicAttention", "KyberNetwork", "Eos", "ChainLink" ]; + +var internalUseTokens = [] +var listedTokens = [] + +var tokenDecimals = [];//[18,9,8,8,8,18,4,18,18,18,18,18] + +var tokenInitialReserveBalance = []; + +var reserveInitialEth; + +var tokenInstance = []; +var kncInstance; +var kgtInstance; +const kgtName = "Kyber genesis token"; +const kgtSymbol = "KGT"; +const kgtDec = 0; + + +var conversionRate = (((new BigNumber(10)).pow(18)).times(2)); +var counterConversionRate = (((new BigNumber(10)).pow(18)).div(2)); + +const expBlock = 10**10; +const validBlockDuration = 256; +const maxGas = 4612388; +const precisionUnits = new BigNumber(10 ** 18); +var tokenOwner; + +var networkProxy; +var networkProxyOwner; + +var network; +var networkOwner; + +var reserve; +var reserveOwner; + +var ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; +var ethChecksum = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" +var emptyAddress = "0x0000000000000000000000000000000000000000"; + +var exchanges = [];// ["Bittrex", "Liqui", "Huobi", "Binance", "Bitfinex"]; +var exchangesInstance = []; +var exchangeDepositAddresses = []; +var supportedTokens = {} + +var bank; +var wrapper; + +var whitelist; +var conversionRates; +var feeBurner; +var expectedRate; + +var nam;// = "0xc6bc2f7b73da733366985f5f5b485262b45a77a3"; +var victor_1;// = "0x760d30979eb313a2d23c53e4fb55986183b0ffd9"; +var victor_2;// = "0xEDd15B61505180B3A0C25B193dF27eF10214D851"; +var victor_3;// = "0x13922f1857c0677f79e4bbb16ad2c49faa620829"; +var duc;// = "0x25B8b1F2c21A70B294231C007e834Ad2de04f51F"; + + +var outputFileName; + +//////////////////////////////////////////////////////////////////////////////// + +var getNetwork = function(){ + var id = web3.version.network; + if(id >= 1500000000000){ + return "testrpc"; + } else if(id == 5777) { + return "ganache"; + } else if( id == 17 || id == 4447) { + return "dev"; + } else if( id == 42 ) { + return "kovan"; + } else if( id == 3 ) { + return "ropsten"; + } else { + return "unknown"; + } +}; + +var deployTokens = function( owner ){ + return new Promise(function (fulfill, reject){ + + var inputs = []; + + for (var i = 0 ; i < tokenSymbol.length ; i++ ) { + inputs.push(i); + } + + + //deploy all tokens from json + return inputs.reduce(function (promise, item) { + return promise.then(function () { + var symbol = tokenSymbol[item]; + var name = tokenName[item]; + var decimals = tokenDecimals[item]; + return TestToken.new(BigNumber(10**68).toString(), name, decimals, symbol, {from:owner}); + }).then(function(instance){ + if( tokenSymbol[item] === "KNC" ) { + console.log("found knc"); + kncInstance = instance; + } + tokenInstance.push(instance); + }) + }, Promise.resolve()).then(function(){ + return TestToken.new(BigNumber(10**68).toString(), kgtName, kgtDec, kgtSymbol).then(function (instance) { + kgtInstance = instance; + }).then(function(){ + fulfill(true); + }); + }).catch(function(err){ + reject(err); + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var transferFundsToBank = function( owner, bankAddress, amount ) { + return new Promise(function (fulfill, reject){ + + var inputs = []; + + for (var i = 0 ; i < tokenInstance.length ; i++ ) { + inputs.push(tokenInstance[i]); + } + + return inputs.reduce(function (promise, item) { + return promise.then(function () { + return item.transfer(bankAddress, amount, {from:owner}); + }); + + }, Promise.resolve()).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +var getBlockNumberWithPromise = function( ) { + return new Promise(function(fulfill, reject){ + web3.eth.getBlockNumber(function(error, result){ + if( error ) { + return reject(error); + } + else { + return fulfill(result); + } + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var depositTokensToReserve = function( owner, reserveInstance ) { + return new Promise(function (fulfill, reject){ + + var inputs = []; + + for (var i = 0 ; i < tokenInstance.length ; i++ ) { + inputs.push(i); + } + + var actualAmount; + return inputs.reduce(function (promise, item) { + var token = tokenInstance[item]; + var amount = tokenInitialReserveBalance[item]; + return promise.then(function () { + return token.decimals(); + }).then(function(decimals){ + actualAmount = new BigNumber(amount).times(new BigNumber(10).pow(decimals)); + //console.log(token.address); + //console.log(actualAmount.toString()); + return token.transfer(reserveInstance.address, actualAmount, {from:owner}); + //return token.approve(reserveInstance.address, actualAmount, {from:owner}); + //}).then(function(){ + //return reserve.depositToken(token.address, actualAmount, {from:owner}) + }).then(function(){ + // send some tokens to duc + return token.transfer(duc, actualAmount,{from:owner}); + }); + + }, Promise.resolve()).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var createExchanges = function( owner, bankAddress ) { + return new Promise(function (fulfill, reject){ + + var inputs = []; + + for (var i = 0 ; i < exchanges.length ; i++ ) { + inputs.push(exchanges[i]); + } + + return inputs.reduce(function (promise, item) { + return promise.then(function () { + return CentralizedExchange.new(item, bankAddress, {from:owner}); + }).then(function(instance){ + exchangesInstance.push(instance); + return addDepositAddressToExchange(instance, owner, item); + }); + }, Promise.resolve()).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +///////////////////////////////////////////////////////////////// + + +var addDepositAddressToExchange = function( exchange, owner, exchangeName ) { + return new Promise(function (fulfill, reject){ + + var tokens = []; + var depositAddresses = {}; //dict (JS object) of deposit address per token for this exchange + + //create array of tokens + for (var i = 0 ; i < tokenInstance.length ; i++ ) { + if (supportedTokens[exchangeName].indexOf(tokenSymbol[i].toLowerCase()) >= 0) { + tokens.push(i); + } + } + + return tokens.reduce(function (promise, item) { + return promise.then(function () { + return exchange.addMockDepositAddress( tokenInstance[item].address, {from:owner}); + }).then(function(){ + return exchange.tokenDepositAddresses(tokenInstance[item].address) + }).then (function (mockDepositAddress){ + depositAddresses[tokenSymbol[item]] = mockDepositAddress; + return reserve.approveWithdrawAddress(tokenInstance[item].address, mockDepositAddress, true); + }); + }, Promise.resolve()).then(function(){ + return exchange.addMockDepositAddress(ethAddress, {from:owner}); + }).then(function(){ + return exchange.tokenDepositAddresses(ethAddress); + }).then(function(depositAddress) { + depositAddresses["ETH"] = depositAddress; + exchangeDepositAddresses.push(depositAddresses); + return reserve.approveWithdrawAddress(ethAddress, depositAddress, true); + }).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var approveIntermediateAccount = function( addr ) { + return new Promise(function (fulfill, reject){ + + var tokens = []; + + //create array of tokens + for (var i = 0 ; i < tokenInstance.length ; i++ ) { + tokens.push(i); + } + + return tokens.reduce(function (promise, item) { + return promise.then(function () { + return reserve.approveWithdrawAddress(tokenInstance[item].address, addr, true); + }); + }, Promise.resolve()).then(function(){ + return reserve.approveWithdrawAddress(ethAddress, addr, true); + }).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var transferOwnershipInExchangesAndBank = function( owner, newOwners ) { + return new Promise(function (fulfill, reject){ + + var inputs = []; + function OwnerAndExchange( owner, exchangesInstance) { + this.owner = owner; + this.exchangesInstance = exchangesInstance; + } + + for (var i = 0 ; i < exchanges.length ; i++ ) { + for( var j = 0 ; j < newOwners.length ; j++ ) { + inputs.push(new OwnerAndExchange(newOwners[j],exchangesInstance[i])); + } + } + + return inputs.reduce(function (promise, item) { + return promise.then(function () { + return item.exchangesInstance.addOwner(item.owner); + }).then(function(){ + return bank.addOwner(item.owner); + }); + + }, Promise.resolve()).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var listTokens = function( owner, reserve, network, expBlock, rate, convRate ) { + return new Promise(function (fulfill, reject){ + + var inputs = []; + + for (var i = 0 ; i < tokenInstance.length ; i++ ) { + inputs.push(tokenInstance[i]); + } + + return inputs.reduce(function (promise, item) { + var tokenAddress = item.address; + return promise.then(function () { + // list (eth=>token) in reserve + // list (token=>eth) in reserve + // list (eth=>token) in network + // list (token=>eth) in network + return conversionRates.addToken( tokenAddress ); + }).then(function(){ + return item.decimals(); + }).then(function(decimals){ + return conversionRates.setTokenControlInfo( tokenAddress, + BigNumber(10**(decimals-2)).toString(), + BigNumber(10 ** decimals).times(50000).toString(), + BigNumber(10 ** decimals).times(1000000).toString() ); + }).then(function(){ + return conversionRates.enableTokenTrade( tokenAddress ); + }).then(function(){ + var x = [0]; + var y = [0]; + return conversionRates.setQtyStepFunction(tokenAddress, + x, + y, + x, + y ); + }).then(function(){ + var x = [0]; + var y = [0]; + return conversionRates.setImbalanceStepFunction(tokenAddress, + x, + y, + x, + y ); + }).then(function(){ + return conversionRates.setBaseRate( [tokenAddress], + [convRate], + [rate], + ["0x000000"], + ["0x000000"], + 0, + [0] ); + }).then(function(){ + return network.listPairForReserve(reserve.address, + tokenAddress, + true, + true, + true, + {from:networkOwner}); + }); + + }, Promise.resolve()).then(function(){ + fulfill(true); + }).catch(function(err){ + reject(err); + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +var sendEtherWithPromise = function( sender, recv, amount ) { + return new Promise(function(fulfill, reject){ + web3.eth.sendTransaction({to: recv, from: sender, value: amount}, function(error, result){ + if( error ) { + return reject(error); + } + else { + return fulfill(true); + } + }); + }); +}; + +//////////////////////////////////////////////////////////////////////////////// + +contract('Kyber', function(accounts) { + + beforeEach(function(done){ + done(); + }); + afterEach(function(done){ + done(); + }); + + it("setup parameters", function() { + // tokens + var tokenInfo = { + "OMG": { + "name": "OmiseGO", + "decimals": 18, + "reserve balance": 1062.4452 + }, + "KNC": { + "name": "KyberNetwork", + "decimals": 18, + "reserve balance": 3291.3726 + }, + "EOS": { + "name": "Eos", + "decimals": 18, + "reserve balance": 1890.2334 + }, + "SNT": { + "name": "STATUS", + "decimals": 18, + "reserve balance": 74100.06 + }, + "ELF": { + "name": "AELF", + "decimals": 18, + "reserve balance": 9381.696 + }, + "POWR": { + "name": "Power Ledger", + "decimals": 6, + "reserve balance": 21139.446 + }, + "MANA": { + "name": "MANA", + "decimals": 18, + "reserve balance": 83010.6 + }, + "BAT": { + "name": "Basic Attention Token", + "decimals": 18, + "reserve balance": 24832.062 + }, + "REQ": { + "name": "Request", + "decimals": 18, + "reserve balance": 57109.62 + }, + "GTO": { + "name": "Gifto", + "decimals": 5, + "reserve balance": 36007.17 + }, + "RDN": { + "name": "Raiden", + "decimals": 18, + "reserve balance": 3595.0146 + }, + "APPC": { + "name": "AppCoins", + "decimals": 18, + "reserve balance": 14453.868 + }, + "ENG": { + "name": "Enigma", + "decimals": 8, + "reserve balance": 4226.7708 + }, + "SALT": { + "name": "Salt", + "decimals": 8, + "reserve balance": 2918.7378 + }, + "BQX": { + "name": "Ethos", + "decimals": 8 + }, + "ADX": { + "name": "AdEx", + "decimals": 4 + }, + "AST": { + "name": "AirSwap", + "decimals": 4 + }, + "RCN": { + "name": "RipioCreditNetwork", + "decimals": 18 + }, + "ZIL": { + "name": "Zilliqa", + "decimals": 12 + }, + "LINK": { + "name": "ChainLink", + "decimals": 18 + }, + "DAI": { + "name": "DAI", + "decimals": 18 + }, + "DGX": { + "name": "Digix Gold", + "decimals": 9 + } + }; + Object.keys(tokenInfo).forEach(function(key) { + var val = tokenInfo[key]; + var symbol = key; + var name = val["name"]; + var decimals = val["decimals"]; + var initialBalance = val["reserve balance"]; + if( initialBalance === undefined ) { + initialBalance = 1000000; + } + + tokenSymbol.push(key); + tokenName.push(name); + tokenDecimals.push(decimals); + tokenInitialReserveBalance.push(initialBalance); + }); + + internalUseTokens = ["omg","knc","eos","snt","elf","powr","mana","bat","req","gto","rdn", + "appc","eng","salt","bqx","ast","zil","link","dgx"] + listedTokens = ["omg","knc","eos","snt","elf","powr","mana","bat","req","gto","rdn","appc", + "eng","salt","bqx","adx","ast","rcn","zil","link","dai"] + + exchanges = ["bittrex","liqui","huobi","binance","bitfinex"] + supportedTokens = { + "bittrex": [ + "omg", + "snt", + "powr", + "mana", + "bat", + "eng", + "salt" + ], + "liqui": [ + "omg", + "knc", + "eos", + "snt", + "bat", + "eng" + ], + "huobi": [ + "omg", + "eos", + "elf", + "powr", + "mana", + "bat", + "req", + "rdn", + "appc", + "eng", + "salt" + ], + "binance": [ + "omg", + "knc", + "eos", + "snt", + "elf", + "powr", + "mana", + "bat", + "req", + "gto", + "rdn", + "appc", + "eng", + "salt", + "bqx" + ], + "bitfinex": [ + "omg", + "eos", + "snt", + "bat" + ] + } + + victor_1 = "0x760d30979eb313a2d23c53e4fb55986183b0ffd9"; + victor_2 = "0xEDd15B61505180B3A0C25B193dF27eF10214D851"; + victor_3 = "0x13922f1857c0677f79e4bbb16ad2c49faa620829"; + nam = [ + "0x385baa4d78c91e5ce6ccafab9e96fdc83ea4427d", + "0x1d217486cdd98c6f565ef567cba26dc331660fb6", + "0x2ebac32cb5b6c1eebabe59e288d120fb3422cef7", + "0x7b29938afb14cd0eaa5abf2519c9e7c052f6a278", + "0x9ca354a72d66127875db293765179a767315058e", + "0xc6bc2f7b73da733366985f5f5b485262b45a77a3" + ]; + duc = "0x25B8b1F2c21A70B294231C007e834Ad2de04f51F"; + + + // reserve initial ether + reserveInitialEth = 16; + }); + + + it("create tokens", function() { +// console.log(accounts[0]); + this.timeout(31000000); + tokenOwner = accounts[0]; + return deployTokens(tokenOwner); + }); + + it("create bank and transfer funds", function() { + this.timeout(31000000); + var amount = new BigNumber(10**58); + return Bank.new().then(function(instance){ + bank = instance; + return transferFundsToBank(tokenOwner, bank.address, amount.toString()); + // TODO - deposit ether + }).then(function(){ + return bank.depositEther({value:10}); // deposit 10 wei + }).then(function(){ + var bankInitialEth = new BigNumber(10**18).times(10); + console.log("depositing " + bankInitialEth.toString() + " ether to bank"); + return sendEtherWithPromise(accounts[0],bank.address,bankInitialEth); + }); + }); + + it("create whitelist", function() { + this.timeout(31000000); + return Whitelist.new(accounts[0], kgtInstance.address).then(function(instance){ + whitelist = instance; + return whitelist.addOperator(accounts[0]); + }).then(function(){ + return whitelist.setCategoryCap(0,5000); + }).then(function(){ + return whitelist.setCategoryCap(1,0); + }).then(function(){ + return whitelist.setCategoryCap(2,1000); + }).then(function(){ + return whitelist.setUserCategory("0x9f1a678b0079773b5c4f5aa8573132d2b8bcb1e7",1); + }).then(function(){ + //transfer kgt to this user to it will be treated as category 2. + kgtInstance.transfer("0x089bAa07Eb9097031bABC99DBa4222D85521883E", 1); + }).then(function(){ + return whitelist.setSgdToEthRate((new BigNumber(10).pow(15)).times(2)); + }); + }); + + it("create network", function() { + this.timeout(31000000); + networkOwner = accounts[0]; + networkOperator = accounts[0]; + return Network.new(networkOwner,{gas:maxGas}).then(function(instance){ + network = instance; + }); + }); + + it("create network proxy", function() { + this.timeout(31000000); + networkProxyOwner = accounts[0]; + return NetworkProxy.new(networkProxyOwner,{gas:maxGas}).then(function(instance){ + networkProxy = instance; + }); + }); + + it("create conversionRates", function() { + this.timeout(31000000); + return ConversionRates.new(accounts[0],{gas:maxGas}).then(function(instance){ + conversionRates = instance; + return conversionRates.addOperator(accounts[0],{from:accounts[0]}); + }); + }); + + it("create reserve and deposit tokens", function() { + this.timeout(30000000); + reserveOwner = accounts[0]; + return Reserve.new(network.address, conversionRates.address, reserveOwner,{gas:maxGas}).then(function(instance){ + reserve = instance; + }).then(function(){ + return conversionRates.setValidRateDurationInBlocks(new BigNumber(1000000)); + }).then(function(){ + return conversionRates.setReserveAddress(reserve.address); + }).then(function(){ + return depositTokensToReserve( tokenOwner, reserve ); + }).then(function(){ + var initAmount = 10; + console.log("depositing " + initAmount.toString() + " ether to reserve"); + var amount = new BigNumber(initAmount).times(10**18); + return sendEtherWithPromise(accounts[0],reserve.address,amount); + }); + }); + + it("create exchanges", function() { + this.timeout(31000000); + return createExchanges( tokenOwner, bank.address ); + }); + + it ("approve intermediate account", function() { + this.timeout(31000000); + return approveIntermediateAccount(victor_3); + }); + + it("withdraw ETH from exchange", function() { + this.timeout(31000000); + return exchangesInstance[0].withdraw(ethAddress,1,accounts[0],{from:tokenOwner}); + }); + + it("withdraw token from exchange", function() { + this.timeout(31000000); + var depositAddress = exchangeDepositAddresses[0][tokenSymbol[0]]; + return exchangesInstance[1].withdraw(tokenInstance[0].address,2,depositAddress,{from:tokenOwner}).then(function(){ + return tokenInstance[0].balanceOf(depositAddress); + }).then(function(result){ + assert.equal(result.valueOf(), new BigNumber(2).valueOf(), "unexpected balance"); + }); + }); + + it("withdraw token from exchange to exchange and clear funds", function() { + this.timeout(31000000); + var depositAddress = exchangeDepositAddresses[0][tokenSymbol[0]]; + return exchangesInstance[0].clearBalances([tokenInstance[0].address, ethAddress],[1,0]).then(function(){ + return tokenInstance[0].balanceOf(depositAddress); + }).then(function(result){ + assert.equal(result.valueOf(), new BigNumber(1).valueOf(), "unexpected balance"); + }); + }); + + it("create burning fees", function() { + this.timeout(31000000); + initialKncRate = precisionUnits.times(431); + return FeeBurner.new(accounts[0],kncInstance.address, network.address, initialKncRate).then(function(instance){ + feeBurner = instance; + return feeBurner.addOperator(accounts[0],{from:accounts[0]}); + }).then(function(result){ + return kncInstance.approve(feeBurner.address, new BigNumber(10**18).times(10000),{from:accounts[0]}); + }).then(function(){ + // set fees for reserve + // 0.25% from accounts + return feeBurner.setReserveData(reserve.address,25, accounts[0]); + }).then(function(){ + return feeBurner.setWalletFees(emptyAddress,50); + }).then(function(){ + return feeBurner.setTaxInBps(2000); + })/*.then(function(){ + return feeBurner.setTaxWallet(0); // zero address will revert + })*/; + }); + + it("create expected rate", function() { + this.timeout(31000000); + return ExpectedRate.new(network.address, kncInstance.address, accounts[0]).then(function(instance){ + expectedRate = instance; + }).then(function(){ + return expectedRate.addOperator(accounts[0]); + }).then(function(){ + return expectedRate.setWorstCaseRateFactor(500); + }); + }); + + it("set network proxy params", function() { + this.timeout(31000000); + // set contracts and enable network + return networkProxy.setKyberNetworkContract(network.address); + }); + + it("set network params", function() { + this.timeout(31000000); + // set contracts and enable network + + return network.setWhiteList(whitelist.address).then(function(){ + return network.setExpectedRate(expectedRate.address); + }).then(function(){ + return network.setFeeBurner(feeBurner.address); + }).then(function(){ + return network.setKyberProxy(networkProxy.address); + }).then(function(){ + return network.setParams(50*10**9, 15); //50 gwei, 15 negligible diff + }).then( function() { + return network.setEnable(true); + }).then( function() { + return network.addOperator(networkOperator); + }); + }); + + it("add reserve to network", function() { + this.timeout(31000000); + return network.addReserve(reserve.address, true, {from:networkOwner}); + }); + + it("list tokens", function() { + this.timeout(30000000); + return listTokens( tokenOwner, reserve, network, expBlock, conversionRate, counterConversionRate ); + }); + + it("create wrapper", function() { + this.timeout(31000000); + var balance0; + var balance1; + var allowance0; + var allowance1; + return Wrapper.new().then(function(instance){ + wrapper = instance; + return wrapper.getBalances( reserve.address, [tokenInstance[0].address, + tokenInstance[1].address] ); + }).then(function(result){ + balance0 = result[0]; + balance1 = result[1]; + return tokenInstance[0].balanceOf(reserve.address); + }).then(function(result){ + assert.equal(BigNumber(balance0).eq(result), true, "unexpected balance 0"); + return tokenInstance[1].balanceOf(reserve.address); + }).then(function(result){ + assert.equal(BigNumber(balance1).eq(result), true, "unexpected balance 1"); + return wrapper.getTokenAllowances(tokenOwner, networkProxy.address, [tokenInstance[0].address, tokenInstance[1].address]); + }).then(function(result){ + allowance0 = result[0]; + allowance1 = result[1]; + return tokenInstance[0].allowance(tokenOwner, networkProxy.address); + }).then(function(result){ + assert.equal(BigNumber(allowance0).eq(result), true, "unexpected allowance 0"); + return tokenInstance[1].allowance(tokenOwner, networkProxy.address); + }).then(function(result){ + assert.equal(BigNumber(allowance1).eq(result), true, "unexpected allowance 1"); + //return wrapper.getRates( reserve.address, [tokenInstance[0].address, + // tokenInstance[1].address], [ethAddress, ethAddress]); + }).then(function(result){ + //console.log("==="); + //console.log(result); + //console.log("==="); + }); + }); + + it("add operator in conversionRates", function() { + this.timeout(31000000); + return conversionRates.addOperator(victor_1); + }); + + it("add operator in conversionRates", function() { + this.timeout(31000000); + return conversionRates.addOperator(victor_2); + }); + + it("add operator in expectedRate", function() { + this.timeout(31000000); + return expectedRate.addOperator(victor_1); + }); + + it("add operator in expectedRate", function() { + this.timeout(31000000); + return expectedRate.addOperator(victor_2); + }); + + it("add operator in reserve", function() { + this.timeout(31000000); + return reserve.addOperator(victor_1); + }); + + it("add operator in reserve", function() { + this.timeout(31000000); + return reserve.addOperator(victor_2); + }); + + it("transfer ownership in exchanges", function() { + this.timeout(30000000); + return transferOwnershipInExchangesAndBank(tokenOwner,nam).then(function(){ + return exchangesInstance[1].owners(nam[1]); + }).then(function(result){ + assert.equal(result.valueOf(),true.valueOf(), "unexpected owner address"); + }); + }); + + + it("make some optimizations", function() { + // send 1 twei to kyber network + return tokenInstance[1].transfer(network.address,0).then(function(){ + // send 1 wei of knc to fee burner + return tokenInstance[1].transfer("0x001adbc838ede392b5b054a47f8b8c28f2fa9f3c",1); + }).then(function(){ + return kncInstance.transfer(feeBurner.address,1); + }).then(function(){ + return tokenInstance[1].balanceOf(network.address); + }).then(function(result){ + console.log("balance", result.valueOf()); + }); + }); + + + it("set eth to dgd rate", function() { + return getBlockNumberWithPromise().then(function(blockNumber){ + return conversionRates.setBaseRate( [tokenInstance[1].address], + ["0x47d40a969bd7c0021"], + [conversionRate], + ["0x000000"], + ["0x000000"], + blockNumber, + [0] ); + }); + }); + + +/* + it("do a single exchange", function() { + this.timeout(31000000); + var dgdAddress = tokenInstance[1].address; + var ethAmount = new BigNumber(10**16); + var rate = BigNumber("0x47d40a969bd7c0021"); + console.log('Rate: ', rate.toString()); + var expectedDgd = (ethAmount * rate / 10**18) / (10**18 / 10**tokenDecimals[1]); + var destAddress = "0x001adbc838ede392b5b054a47f8b8c28f2fa9f3c"; + + return networkProxy.trade(ethAddress, + ethAmount.toString(), + dgdAddress, + destAddress, + new BigNumber(2).pow(255), + 0,emptyAddress,{from: accounts[5], value:ethAmount.toString(), gasPrice:49 * 10**9}).then(function(result){ + //for( var i = 0 ; i < result.receipt.logs.length ; i++ ) + //console.log(result.receipt.logs[i].data); + + + return tokenInstance[1].balanceOf(destAddress); + }).then(function(result){ + console.log('Result: ', result.toString()); + if( result.valueOf() > expectedDgd.valueOf() + 100 ) { + assert.fail("unexpected dgd balance", result.valueOf(), expectedDgd.valueOf() ); + } + if( result.valueOf() < expectedDgd.valueOf() - 100 ) { + assert.fail("unexpected dgd balance", result.valueOf(), expectedDgd.valueOf() ); + } + }).then(function(){ + return tokenInstance[1].balanceOf(network.address); + }).then(function(result){ + console.log("balance 2", result.valueOf()); + }); + }); + + it("do converse exchange", function() { + this.timeout(31000000); + var tokenInd = 1; + var dgdAddress = tokenInstance[tokenInd].address; + var dgdAmount = 7**tokenDecimals[tokenInd];//zelda + var rate = conversionRate; + var destAddress = "0x001adbc838ede392b5b054a47f8b8c28f2fa9f3c"; + + return tokenInstance[tokenInd].approve(networkProxy.address,dgdAmount).then(function(){ + return networkProxy.trade(dgdAddress, + dgdAmount, + ethAddress, + destAddress, + new BigNumber(2).pow(255), + 0,emptyAddress,{value:0, gasPrice:49* 10**9}); + }).then(function(result){ + for( var i = 0 ; i < result.receipt.logs.length ; i++ ) { + console.log(result.receipt.logs[i].args); + } + }); + }); +*/ + it("check time duration block", function() { + this.timeout(31000000); + return conversionRates.validRateDurationInBlocks().then(function(result){ + assert.equal(result.valueOf(), 1000000, "unexpected valid rate duration block"); + }); + }); + + it("print addresses", function() { + tokensDict = {}; + console.log("\ntokens"); + tokensDict["ETH"] = {"address" : ethAddress.toString(16), + "name" : "Ethereum", + "decimals" : 18, + "internal use": true, + "listed": true}; + for( var i = 0 ; i < tokenSymbol.length ; i++ ) { + //console.log(tokenSymbol[i] + " : " + tokenInstance[i].address ); + var symbol = tokenSymbol[i].toLowerCase(); + tokenDict = { + "address" : tokenInstance[i].address, + "name" : tokenName[i], + "decimals" : tokenDecimals[i], + "internal use": internalUseTokens.indexOf(symbol) >= 0, + "listed": listedTokens.indexOf(symbol) >= 0 + }; + tokensDict[tokenSymbol[i]] = tokenDict; + } + + exchangesDepositAddressesDict = {}; + exchangesAddressDict = {}; + for( var exchangeInd = 0 ; exchangeInd < exchanges.length ; exchangeInd++ ) { + exchangesAddressDict[exchanges[exchangeInd]] = exchangesInstance[exchangeInd].address; + exchangesDepositAddressesDict[exchanges[exchangeInd]] = exchangeDepositAddresses[exchangeInd]; + } + + dict = { "tokens" : tokensDict, "exchangesAddress" : exchangesAddressDict, "exchanges" : exchangesDepositAddressesDict }; + dict["bank"] = bank.address; + dict["reserve"] = reserve.address; + dict["pricing"] = conversionRates.address; + dict["network"] = networkProxy.address; + dict["internal network"] = network.address; + dict["wrapper"] = wrapper.address; + dict["feeburner"] = feeBurner.address; + dict["KGT address"] = kgtInstance.address; + dict["third_party_reserves"] = []; + }); + + it("reduce valid block duration to: " + validBlockDuration, function() { + this.timeout(31000000); + return conversionRates.setValidRateDurationInBlocks(validBlockDuration); + }); + + it("Should deploy Burner and Trust Factory", async() => { + //Spread second token to users + for (var i = 1; i < accounts.length; i++) { + //console.log(accounts[i]); + await tokenInstance[1].transfer(accounts[i], tokenPerAccount.toString()); + await tokenInstance[2].transfer(accounts[i], tokenPerAccount.toString()); + } + burner = await MyBitBurner.new(tokenInstance[0].address, networkProxy.address); + factory = await TrustFactory.new(burner.address); + await burner.authorizeBurner(factory.address); + await factory.changeMYBFee(BigNumber(10**12).toString()); + }); + + it("Should burn tokenInstance 1", async() => { + await tokenInstance[1].approve(burner.address, tokenPerAccount.toString(), {from: accounts[1]}); + let balanceBefore = await tokenInstance[1].balanceOf(accounts[1]); + await factory.deployTrust(accounts[2], true, '100', tokenInstance[1].address, {from: accounts[1], value: ETH.toString()}); + let balanceAfter = await tokenInstance[1].balanceOf(accounts[1]); + assert.equal(BigNumber(balanceAfter).lt(balanceBefore), true); + }); +}); diff --git a/test/MyBitBurner.js b/test/MyBitBurner.js index 8129019..75dda17 100644 --- a/test/MyBitBurner.js +++ b/test/MyBitBurner.js @@ -1,20 +1,21 @@ var bn = require('bignumber.js'); -const Token = artifacts.require("./ERC20.sol"); +const Token = artifacts.require("./SampleERC20.sol"); const Burner = artifacts.require("./MyBitBurner.sol"); -const owner = web3.eth.accounts[0]; -const user1 = web3.eth.accounts[1]; -const user2 = web3.eth.accounts[2]; -const tokenHolders = [user1, user2]; +const tokenSupply = '180000000000000000000000000'; +const tokenPerAccount = '1000000000000000000000'; -const tokenSupply = 180000000000000000000000000; -const tokenPerAccount = 1000000000000000000000; +contract('Burner', async(accounts) => { + const owner = accounts[0]; + const user1 = accounts[1]; + const user2 = accounts[2]; + const tokenHolders = [user1, user2]; -contract('Burner', async() => { let burner; let token; + let kyberAddress = '0x0000000000000000000000000000000000000000' //Just passing an empty address since we're not testing kyber it('Deploy Token', async() => { token = await Token.new(tokenSupply, "MyBit", 18, "MYB"); @@ -35,13 +36,13 @@ contract('Burner', async() => { }); it('Deploy MyBitBurner', async() => { - burner = await Burner.new(token.address); + burner = await Burner.new(token.address, kyberAddress); }); it('Fail to send ether', async() => { let err; try{ - await web3.eth.sendTransaction({from:user1, to: burner.address, value: 10000}) + await web3.eth.sendTransaction({from:user1, to: burner.address, value: '10000'}) } catch(e){ err = e; } @@ -51,7 +52,7 @@ contract('Burner', async() => { it('Fail to burn tokens', async() => { let err; try{ - await burner.burn(user2, 1000, {from: user1}); + await burner.burn(user2, '1000', token.address, {from: user1}); } catch(e){ err = e; console.log('Address not authorized') @@ -86,7 +87,7 @@ contract('Burner', async() => { it('Fail to burn tokens', async() => { let err; try{ - await burner.burn(user2, 1000, {from: user1}); + await burner.burn(user2, '1000', token.address, {from: user1}); } catch(e){ err = e; console.log('User has not given allowance'); @@ -95,8 +96,8 @@ contract('Burner', async() => { }); it('Burn tokens', async() => { - await token.approve(burner.address, 1000, {from: user2}); - await burner.burn(user2, 1000, {from: user1}); + await token.approve(burner.address, '1000', {from: user2}); + await burner.burn(user2, '1000', token.address, {from: user1}); }); it('Revoke authorization', async() => { diff --git a/test/testERC20.js b/test/testERC20.js index e5b030d..e71a830 100644 --- a/test/testERC20.js +++ b/test/testERC20.js @@ -1,25 +1,25 @@ var bn = require('bignumber.js'); /* Contracts */ -const Token = artifacts.require("./token/ERC20.sol"); +const Token = artifacts.require("./token/SampleERC20.sol"); const Trust = artifacts.require("./TrustERC20.sol"); const TrustFactory = artifacts.require("./TrustFactory.sol"); const MyBitBurner = artifacts.require("./MyBitBurner.sol"); -const WEI = 1000000000000000000; +const WEI = '1000000000000000000'; contract('Trust - Using ERC20 Token', async (accounts) => { - const owner = web3.eth.accounts[0]; - const trustor = web3.eth.accounts[1]; - const beneficiary = web3.eth.accounts[2]; - const beneficiary2 = web3.eth.accounts[3]; - const beneficiary3 = web3.eth.accounts[4]; + const owner = accounts[0]; + const trustor = accounts[1]; + const beneficiary = accounts[2]; + const beneficiary2 = accounts[3]; + const beneficiary3 = accounts[4]; const beneficiaries = [beneficiary, beneficiary2, beneficiary3]; - const tokenSupply = 180000000000000000000000000; - const tokenPerAccount = 1000000000000000000000; + const tokenSupply = '180000000000000000000000000'; + const tokenPerAccount = '1000000000000000000000'; - let burnFee = 250000000000000000000; + let burnFee = '250000000000000000000'; let numTrustsMade = 0; let originalBeneficiary; //Original beneficiary @@ -36,6 +36,7 @@ contract('Trust - Using ERC20 Token', async (accounts) => { let tokenAddress; let erc20Address; let burnerAddress; + let kyberAddress = '0x0000000000000000000000000000000000000000' //Just passing an empty address since we're not testing kyber // Deploy token contract it ('Deploy MyBit Token contract', async() => { @@ -49,14 +50,14 @@ contract('Trust - Using ERC20 Token', async (accounts) => { // Give every user tokenPerAccount amount of tokens it("Spread tokens to users", async () => { - for (var i = 1; i < web3.eth.accounts.length; i++) { - //console.log(web3.eth.accounts[i]); - await token.transfer(web3.eth.accounts[i], tokenPerAccount); - let userBalance = await token.balanceOf(web3.eth.accounts[i]); - assert.equal(userBalance, tokenPerAccount); + for (var i = 1; i < accounts.length; i++) { + //console.log(accounts[i]); + await token.transfer(accounts[i], tokenPerAccount); + let userBalance = await token.balanceOf(accounts[i]); + assert.equal(bn(userBalance).eq(tokenPerAccount), true); } // Check token ledger is correct - let totalTokensCirculating = (web3.eth.accounts.length - 1) * (tokenPerAccount); + let totalTokensCirculating = bn(accounts.length - 1).times(tokenPerAccount); let remainingTokens = bn(tokenSupply).minus(totalTokensCirculating); let ledgerTrue = bn(await token.balanceOf(owner)).eq(remainingTokens); assert.equal(ledgerTrue, true); @@ -68,34 +69,34 @@ contract('Trust - Using ERC20 Token', async (accounts) => { erc20Address = await erc20.address; // console.log(erc20Address); - assert.equal(await erc20.totalSupply(), tokenSupply); - assert.equal(await erc20.balanceOf(owner), tokenSupply); + assert.equal(bn(await erc20.totalSupply()).eq(tokenSupply), true); + assert.equal(bn(await erc20.balanceOf(owner)).eq(tokenSupply), true); }); // Give every user tokenPerAccount amount of tokens it("Spread ERC20 tokens to users", async () => { - for (var i = 1; i < web3.eth.accounts.length - 1; i++) { - await erc20.transfer(web3.eth.accounts[i], tokenPerAccount); - let userBalance = await erc20.balanceOf(web3.eth.accounts[i]); - assert.equal(userBalance, tokenPerAccount); + for (var i = 1; i < accounts.length - 1; i++) { + await erc20.transfer(accounts[i], tokenPerAccount); + let userBalance = await erc20.balanceOf(accounts[i]); + assert.equal(bn(userBalance).eq(tokenPerAccount), true); } // Check token ledger is correct - let totalTokensCirculating = (web3.eth.accounts.length - 2) * (tokenPerAccount); + let totalTokensCirculating = bn(accounts.length).minus(2).times(tokenPerAccount); let remainingTokens = bn(tokenSupply).minus(totalTokensCirculating); let ledgerTrue = bn(await erc20.balanceOf(owner)).eq(remainingTokens); assert.equal(ledgerTrue, true); }); it ('Deploy MyBitBurner contract', async() => { - myBitBurner = await MyBitBurner.new(tokenAddress); + myBitBurner = await MyBitBurner.new(tokenAddress, kyberAddress); burnerAddress = await myBitBurner.address; - assert.equal(await myBitBurner.owner(), web3.eth.accounts[0]); + assert.equal(await myBitBurner.owner(), accounts[0]); // console.log(burnerAddress); }); it ('Deploy TrustFactory contract', async() => { trustFactory = await TrustFactory.new(burnerAddress); - assert.equal(await trustFactory.mybFee(), burnFee); + assert.equal(bn(await trustFactory.mybFee()).eq(burnFee), true); let tfAddress = await trustFactory.address; // console.log(tfAddress); await myBitBurner.authorizeBurner(tfAddress); @@ -104,13 +105,13 @@ contract('Trust - Using ERC20 Token', async (accounts) => { }); it('Deploy ERC20 Trust contract', async() => { - let trustBalance = (2 * WEI); + let trustBalance = bn(2).times(WEI); let balanceStart = await erc20.balanceOf(trustor); // console.log('Balance at Start: ' + bn(balanceStart)); await token.approve(burnerAddress, burnFee, {from: trustor}); let trustExpiration = 10; - let tx = await trustFactory.createTrustERC20(beneficiary, true, trustExpiration, erc20Address, {from: trustor}); + let tx = await trustFactory.createTrustERC20(beneficiary, true, trustExpiration, erc20Address, tokenAddress, {from: trustor}); numTrustsMade += 1; let trustAddress = tx.logs[0].args._trustAddress; // console.log('Trust Address: ' + trustAddress); @@ -119,8 +120,8 @@ contract('Trust - Using ERC20 Token', async (accounts) => { trust = await Trust.at(trustAddress); // trust = await Trust.at(trustAddress); - await erc20.approve(trustAddress, trustBalance, {from: trustor}); - await trust.depositTrust(trustBalance, {from: trustor}); + await erc20.approve(trustAddress, trustBalance.toString(), {from: trustor}); + await trust.depositTrust(trustBalance.toString(), {from: trustor}); //Confirm burnt tokens let userBalance = await token.balanceOf(trustor); @@ -140,7 +141,7 @@ contract('Trust - Using ERC20 Token', async (accounts) => { //Check trust assert.equal(trustor, await trust.trustor()); assert.equal(beneficiary, await trust.beneficiary()); - assert.equal(trustBalance, await trust.trustBalance()); + assert.equal(bn(await trust.trustBalance()).eq(trustBalance), true); }); it('Attemp to deposit in trust', async() => { @@ -175,7 +176,7 @@ contract('Trust - Using ERC20 Token', async (accounts) => { it('Change Expiration', async() => { //Change expiration to 0 await trust.changeExpiration(0, {from: trustor}); - assert.equal(0, await trust.secUntilExpiration()); + assert.equal(bn(await trust.secUntilExpiration()).eq(0), true); }); it("Expect withdraw to fail: Wrong Beneficiary", async() => { @@ -200,7 +201,7 @@ contract('Trust - Using ERC20 Token', async (accounts) => { console.log('Move forward in time'); }); //Widthdraw - assert.equal(await trust.secUntilExpiration(), 0); + assert.equal(bn(await trust.secUntilExpiration()).eq(0), true); let tx = await trust.withdraw({from: beneficiary}); let balanceETHAfter = await web3.eth.getBalance(beneficiary); @@ -213,8 +214,8 @@ contract('Trust - Using ERC20 Token', async (accounts) => { assert.equal(expectedERC20Balance.eq(balanceERC20After), true); //Check that only gas was used assert.equal(bn(balanceETHBefore).minus(gasUsed).eq(balanceETHAfter), true); - trustBalance = await trust.trustBalance(); - assert.equal(trustBalance, 0); + trustBalance = bn(await trust.trustBalance()); + assert.equal(trustBalance.eq(0), true); }); it("Expect withdraw to fail: Trust already withdrawn", async() => { @@ -229,7 +230,7 @@ contract('Trust - Using ERC20 Token', async (accounts) => { await token.approve(burnerAddress, burnFee, {from: trustor}); let trustExpiration = 1000; - let tx = await trustFactory.createTrustERC20(beneficiary, true, trustExpiration, erc20Address, {from: trustor}); + let tx = await trustFactory.createTrustERC20(beneficiary, true, trustExpiration, erc20Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; let err; @@ -242,11 +243,11 @@ contract('Trust - Using ERC20 Token', async (accounts) => { }); it('Deploy ERC20 Trust contract again', async() => { - let trustBalance = (2 * WEI); + let trustBalance = bn(2).times(WEI).toString(); await token.approve(burnerAddress, burnFee, {from: trustor}); let trustExpiration = 1000; - let tx = await trustFactory.createTrustERC20(beneficiary, true, trustExpiration, erc20Address, {from: trustor}); + let tx = await trustFactory.createTrustERC20(beneficiary, true, trustExpiration, erc20Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; trust = await Trust.at(trustAddress); @@ -299,17 +300,17 @@ contract('Trust - Using ERC20 Token', async (accounts) => { it('Fail to deploy ERC20 Trust - burner not approved', async() => { let err; try { - await trustFactory.createTrustERC20(beneficiary, true, 1000, erc20Address, {from: trustor}); + await trustFactory.createTrustERC20(beneficiary, true, '1000', erc20Address, tokenAddress, {from: trustor}); } catch(e) { err = e; } assert.notEqual(err, null); }); it('Fail to deploy ERC20 Trust - balance too low', async() => { - const noBalance = web3.eth.accounts[web3.eth.accounts.length - 1]; - let trustBalance = 2 * tokenPerAccount; + const noBalance = accounts[accounts.length - 1]; + let trustBalance = bn(2).times(tokenPerAccount).toString(); await token.approve(burnerAddress, burnFee, {from: noBalance}); - let tx = await trustFactory.createTrustERC20(beneficiary, true, 1000, erc20Address, {from: noBalance}); + let tx = await trustFactory.createTrustERC20(beneficiary, true, '1000', erc20Address, tokenAddress, {from: noBalance}); let trustAddress = tx.logs[0].args._trustAddress; trust = await Trust.at(trustAddress); await erc20.approve(trustAddress, trustBalance, {from: noBalance}); @@ -324,7 +325,7 @@ contract('Trust - Using ERC20 Token', async (accounts) => { it('Fail to revoke unrevocable trust', async() => { await token.approve(burnerAddress, burnFee, {from: trustor}); - let tx = await trustFactory.createTrustERC20(beneficiary, false, 1000, erc20Address, {from: trustor}); + let tx = await trustFactory.createTrustERC20(beneficiary, false, 1000, erc20Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; trust = await Trust.at(trustAddress); @@ -344,10 +345,10 @@ contract('Trust - Using ERC20 Token', async (accounts) => { let err; try { await token.approve(burnerAddress, burnFee, {from: trustor}); - await trustFactory.createTrustERC20(beneficiary, true, 10, erc20Address, {from: trustor}); + await trustFactory.createTrustERC20(beneficiary, true, 10, erc20Address, tokenAddress, {from: trustor}); } catch(e) { err = e; } assert.notEqual(err, null); }); -}); \ No newline at end of file +}); diff --git a/test/testERC721.js b/test/testERC721.js index 28260bb..9797cac 100644 --- a/test/testERC721.js +++ b/test/testERC721.js @@ -1,7 +1,7 @@ var bn = require('bignumber.js'); /* Contracts */ -const Token = artifacts.require("./token/ERC20.sol"); +const Token = artifacts.require("./token/SampleERC20.sol"); const ERC721 = artifacts.require("./token/SampleERC721.sol"); const Trust = artifacts.require("./TrustERC721.sol"); const TrustFactory = artifacts.require("./TrustFactory.sol"); @@ -10,15 +10,15 @@ const MyBitBurner = artifacts.require("./MyBitBurner.sol"); const WEI = 1000000000000000000; contract('Trust - Using ERC721', async (accounts) => { - const owner = web3.eth.accounts[0]; - const trustor = web3.eth.accounts[1]; - const beneficiary = web3.eth.accounts[2]; - const beneficiary2 = web3.eth.accounts[3]; + const owner = accounts[0]; + const trustor = accounts[1]; + const beneficiary = accounts[2]; + const beneficiary2 = accounts[3]; - const tokenSupply = 180000000000000000000000000; - const tokenPerAccount = 1000000000000000000000; + const tokenSupply = '180000000000000000000000000'; + const tokenPerAccount = '1000000000000000000000'; - let burnFee = 250000000000000000000; + let burnFee = '250000000000000000000'; // Contract instances let token; // Token contract instance @@ -32,6 +32,7 @@ contract('Trust - Using ERC721', async (accounts) => { let tokenAddress; let erc721Address; let burnerAddress; + let kyberAddress = '0x0000000000000000000000000000000000000000' //Just passing an empty address since we're not testing kyber // Deploy token contract it ('Deploy MyBit Token contract', async() => { @@ -45,14 +46,14 @@ contract('Trust - Using ERC721', async (accounts) => { // Give every user tokenPerAccount amount of tokens it("Spread tokens to users", async () => { - for (var i = 1; i < web3.eth.accounts.length; i++) { - //console.log(web3.eth.accounts[i]); - await token.transfer(web3.eth.accounts[i], tokenPerAccount); - let userBalance = await token.balanceOf(web3.eth.accounts[i]); + for (var i = 1; i < accounts.length; i++) { + //console.log(accounts[i]); + await token.transfer(accounts[i], tokenPerAccount); + let userBalance = await token.balanceOf(accounts[i]); assert.equal(userBalance, tokenPerAccount); } // Check token ledger is correct - let totalTokensCirculating = (web3.eth.accounts.length - 1) * (tokenPerAccount); + let totalTokensCirculating = bn(accounts.length).minus(1).times(tokenPerAccount); let remainingTokens = bn(tokenSupply).minus(totalTokensCirculating); let ledgerTrue = bn(await token.balanceOf(owner)).eq(remainingTokens); assert.equal(ledgerTrue, true); @@ -79,9 +80,9 @@ contract('Trust - Using ERC721', async (accounts) => { }); it ('Deploy MyBitBurner contract', async() => { - myBitBurner = await MyBitBurner.new(tokenAddress); + myBitBurner = await MyBitBurner.new(tokenAddress, kyberAddress); burnerAddress = await myBitBurner.address; - assert.equal(await myBitBurner.owner(), web3.eth.accounts[0]); + assert.equal(await myBitBurner.owner(), accounts[0]); // console.log(burnerAddress); }); @@ -103,7 +104,7 @@ contract('Trust - Using ERC721', async (accounts) => { await token.approve(burnerAddress, burnFee, {from: trustor}); let trustExpiration = 1000; - let tx = await trustFactory.createTrustERC721(beneficiary, true, trustExpiration, erc721Address, {from: trustor}); + let tx = await trustFactory.createTrustERC721(beneficiary, true, trustExpiration, erc721Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; // console.log('Trust Address: ' + trustAddress); @@ -204,7 +205,7 @@ contract('Trust - Using ERC721', async (accounts) => { await token.approve(burnerAddress, burnFee, {from: trustor}); let trustExpiration = 1000; - let tx = await trustFactory.createTrustERC721(beneficiary, true, trustExpiration, erc721Address, {from: trustor}); + let tx = await trustFactory.createTrustERC721(beneficiary, true, trustExpiration, erc721Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; trust = await Trust.at(trustAddress); @@ -257,7 +258,7 @@ contract('Trust - Using ERC721', async (accounts) => { it('Fail to deploy ERC721 Trust - burner not approved', async() => { let err; try { - await trustFactory.createTrustERC721(beneficiary, true, 1000, erc721Address, {from: trustor}); + await trustFactory.createTrustERC721(beneficiary, true, 1000, erc721Address, tokenAddress, {from: trustor}); } catch(e) { err = e; } assert.notEqual(err, null); @@ -265,7 +266,7 @@ contract('Trust - Using ERC721', async (accounts) => { it('Fail to deploy ERC721 Trust - not owner', async() => { await token.approve(burnerAddress, burnFee, {from: trustor}); - let tx = await trustFactory.createTrustERC721(beneficiary, true, 1000, erc721Address, {from: trustor}); + let tx = await trustFactory.createTrustERC721(beneficiary, true, 1000, erc721Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; trust = await Trust.at(trustAddress); @@ -279,7 +280,7 @@ contract('Trust - Using ERC721', async (accounts) => { it('Fail to revoke unrevocable trust', async() => { await token.approve(burnerAddress, burnFee, {from: trustor}); - let tx = await trustFactory.createTrustERC721(beneficiary, false, 1000, erc721Address, {from: trustor}); + let tx = await trustFactory.createTrustERC721(beneficiary, false, 1000, erc721Address, tokenAddress, {from: trustor}); let trustAddress = tx.logs[0].args._trustAddress; trust = await Trust.at(trustAddress); @@ -299,7 +300,7 @@ contract('Trust - Using ERC721', async (accounts) => { let err; try { await token.approve(burnerAddress, burnFee, {from: trustor}); - await trustFactory.createTrustERC721(beneficiary, true, 10, erc721Address, {from: trustor}); + await trustFactory.createTrustERC721(beneficiary, true, 10, erc721Address, tokenAddress, {from: trustor}); } catch(e) { err = e; } assert.notEqual(err, null); @@ -366,4 +367,4 @@ contract('Trust - Using ERC721', async (accounts) => { assert.notEqual(err, null); }); -}); \ No newline at end of file +}); diff --git a/test/test.js b/test/testETH.js similarity index 86% rename from test/test.js rename to test/testETH.js index fed374f..8a28fb1 100644 --- a/test/test.js +++ b/test/testETH.js @@ -1,7 +1,7 @@ var bn = require('bignumber.js'); /* Contracts */ -const Token = artifacts.require("./token/ERC20.sol"); +const Token = artifacts.require("./token/SampleERC20.sol"); const Trust = artifacts.require("./Trust.sol"); const TrustFactory = artifacts.require("./TrustFactory.sol"); const MyBitBurner = artifacts.require("./MyBitBurner.sol"); @@ -9,18 +9,18 @@ const MyBitBurner = artifacts.require("./MyBitBurner.sol"); const WEI = 1000000000000000000; contract('Trust - Deploying and storing all contracts + validation', async (accounts) => { - const owner = web3.eth.accounts[0]; - const trustor = web3.eth.accounts[1]; - const beneficiary = web3.eth.accounts[2]; - const beneficiary2 = web3.eth.accounts[3]; - const beneficiary3 = web3.eth.accounts[4]; + const owner = accounts[0]; + const trustor = accounts[1]; + const beneficiary = accounts[2]; + const beneficiary2 = accounts[3]; + const beneficiary3 = accounts[4]; const beneficiaries = [beneficiary, beneficiary2, beneficiary3]; - const thief = web3.eth.accounts[9]; + const thief = accounts[9]; - const tokenSupply = 180000000000000000000000000; - const tokenPerAccount = 1000000000000000000000; + const tokenSupply = '180000000000000000000000000'; + const tokenPerAccount = '1000000000000000000000'; - let burnFee = 250000000000000000000; + let burnFee = '250000000000000000000'; let numTrustsMade = 0; let originalBeneficiary; //Original beneficiary @@ -35,7 +35,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco // Contract addresses let tokenAddress; let burnerAddress; - + let kyberAddress = '0x0000000000000000000000000000000000000000' //Just passing an empty address since we're not testing kyber // Deploy token contract it ('Deploy MyBit Token contract', async() => { @@ -49,23 +49,23 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco // Give every user tokenPerAccount amount of tokens it("Spread tokens to users", async () => { - for (var i = 1; i < web3.eth.accounts.length; i++) { - //console.log(web3.eth.accounts[i]); - await token.transfer(web3.eth.accounts[i], tokenPerAccount); - let userBalance = await token.balanceOf(web3.eth.accounts[i]); + for (var i = 1; i < accounts.length; i++) { + //console.log(accounts[i]); + await token.transfer(accounts[i], tokenPerAccount); + let userBalance = await token.balanceOf(accounts[i]); assert.equal(userBalance, tokenPerAccount); } // Check token ledger is correct - let totalTokensCirculating = (web3.eth.accounts.length - 1) * (tokenPerAccount); + let totalTokensCirculating = bn(accounts.length).minus(1).times(tokenPerAccount); let remainingTokens = bn(tokenSupply).minus(totalTokensCirculating); let ledgerTrue = bn(await token.balanceOf(owner)).eq(remainingTokens); assert.equal(ledgerTrue, true); }); it ('Deploy MyBitBurner contract', async() => { - myBitBurner = await MyBitBurner.new(tokenAddress); + myBitBurner = await MyBitBurner.new(tokenAddress, kyberAddress); burnerAddress = await myBitBurner.address; - assert.equal(await myBitBurner.owner(), web3.eth.accounts[0]); + assert.equal(await myBitBurner.owner(), accounts[0]); console.log(burnerAddress); }); @@ -88,13 +88,13 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco }); it('Change MyB Fee', async() => { - burnFee = 200000000000000000000; + burnFee = '200000000000000000000'; await trustFactory.changeMYBFee(burnFee); }); it('Fail to deploy trust', async() => { try{ - await trustFactory.deployTrust(beneficiary, true, 4, {from: trustor}); + await trustFactory.deployTrust(beneficiary, true, 4, token.address, {from: trustor}); }catch(e){ console.log('No money given to trust'); } @@ -102,7 +102,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco it('Fail to deploy trust', async() => { try{ - await trustFactory.deployTrust(beneficiary, true, 4, {from: trustor, value: (2 * WEI) }); + await trustFactory.deployTrust(beneficiary, true, 4, token.address, {from: trustor, value: bn(2).times(WEI).toString() }); }catch(e){ console.log('No permission given to burn MyB tokens'); } @@ -114,8 +114,8 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco //Give MyBitBurner permission to handle user tokens (limit burnFee) await token.approve(burnerAddress, burnFee, {from: trustor}); - let trustExpiration = 6; - tx = await trustFactory.deployTrust(beneficiary, true, trustExpiration, {from: trustor, value: (2 * WEI) }); + let trustExpiration = '100'; + tx = await trustFactory.deployTrust(beneficiary, true, trustExpiration, token.address, {from: trustor, value: bn(2).times(WEI).toString() }); numTrustsMade += 1; trustAddress = tx.logs[0].args._trustAddress; console.log('Trust Address: ' + trustAddress); @@ -137,13 +137,14 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco assert.equal(beneficiary, await trust.beneficiary()); assert.equal((2 * WEI), await trust.trustBalance()); let expiration = await trust.expiration(); - let now = await web3.eth.getBlock('latest').timestamp; - assert.equal(now + trustExpiration, expiration); + let block = await web3.eth.getBlock('latest'); + let now = block.timestamp; + assert.equal(bn(now).plus(trustExpiration).eq(expiration), true); }); it('Fail to pay trust factory contract', async() => { try{ - await web3.eth.sendTransaction({from:trustor,to:trustFactory.address, value:0.1*WEI}); + await web3.eth.sendTransaction({from:trustor,to:trustFactory.address, value:bn(WEI).dividedBy(10)}); }catch(e){ console.log('Cannot send money directly to a trust factory contract'); } @@ -151,7 +152,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco it('Fail to pay trust contract', async() => { try{ - await web3.eth.sendTransaction({from:trustor,to:trustAddress, value:0.1*WEI}); + await web3.eth.sendTransaction({from:trustor,to:trustAddress, value:bn(WEI).dividedBy(10)}); }catch(e){ console.log('Cannot send money directly to a trust contract'); } @@ -159,7 +160,8 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco it('Revoke Trust', async() => { let balanceBefore = await web3.eth.getBalance(trustor); - let beforeExpirationTrue = bn(await trust.expiration()).gt(await web3.eth.getBlock('latest').timestamp); + let block = await web3.eth.getBlock('latest'); + let beforeExpirationTrue = bn(await trust.expiration()).gt(block.timestamp); assert.equal(beforeExpirationTrue, true); // Revoke trust @@ -198,7 +200,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco //Give MyBitBurner permission to handle user tokens (limit burnFee) await token.approve(burnerAddress, burnFee, {from: trustor}); //Use trustFactory to deploy trust - tx = await trustFactory.deployTrust(beneficiary, true, 10, {from: trustor, value: (2 * WEI) }); + tx = await trustFactory.deployTrust(beneficiary, true, 10, token.address, {from: trustor, value: bn(2).times(WEI).toString() }); numTrustsMade += 1; trustAddress = tx.logs[0].args._trustAddress; console.log('Trust Address: ' + trustAddress); @@ -310,7 +312,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco await token.approve(burnerAddress, burnFee, {from: trustor}); //Use trustFactory to deploy trust let err; - try { await trustFactory.deployTrust(beneficiary, false, 10, {from: trustor}); } + try { await trustFactory.deployTrust(beneficiary, false, 10, token.address, {from: trustor}); } catch(e) { err = e; } assert.notEqual(err, null); }); @@ -324,7 +326,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco //Give MyBitBurner permission to handle user tokens (limit burnFee) await token.approve(burnerAddress, burnFee, {from: trustor}); //Use trustFactory to deploy trust - tx = await trustFactory.deployTrust(beneficiary, false, 20, {from: trustor, value: (2 * WEI) }); + tx = await trustFactory.deployTrust(beneficiary, false, 20, token.address, {from: trustor, value: (2 * WEI) }); //Confirm burnt tokens let userBalance = await token.balanceOf(trustor); assert.equal(bn(userBalance).eq(bn(trustorBalanceBefore).minus(newFee)), true); @@ -337,7 +339,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco let trustorBalanceBefore = await token.balanceOf(trustor); await token.approve(burnerAddress, burnFee, {from: trustor}); //Use trustFactory to deploy trust - tx = await trustFactory.deployTrust(beneficiary, false, 20, {from: trustor, value: (2 * WEI) }); + tx = await trustFactory.deployTrust(beneficiary, false, 20, token.address, {from: trustor, value: (2 * WEI) }); trustAddress = tx.logs[0].args._trustAddress; console.log('Trust Address: ' + trustAddress); @@ -422,7 +424,7 @@ contract('Trust - Deploying and storing all contracts + validation', async (acco it("Try to deploy another trust when factory is closed", async() => { let err; - try { await trustFactory.deployTrust(beneficiary, false, 20, {from: trustor, value: (2 * WEI) }); } + try { await trustFactory.deployTrust(beneficiary, false, 20, token.address, {from: trustor, value: (2 * WEI) }); } catch(e) { err = e; } assert.notEqual(err, null); }); diff --git a/truffle.js b/truffle.js index 5a84974..6dac5a1 100644 --- a/truffle.js +++ b/truffle.js @@ -3,9 +3,19 @@ module.exports = { development: { host: "localhost", port: 8545, - gas: 6500000, network_id: "*", gasPrice: 1 + }, + }, + solc: { + optimizer: { + enabled: true, + runs: 200 + } + }, + compilers: { + solc: { + version: "0.4.24" } } }; From b38d8823cc13eba069f148deff03607f721cb097 Mon Sep 17 00:00:00 2001 From: Iggy Grey Date: Mon, 25 Feb 2019 19:16:49 -0800 Subject: [PATCH 2/3] Increased coverage, made changes to TrustFactory for using Ether as burn payment --- .gitignore | 1 + .solcover.js | 4 ++++ contracts/MyBitBurner.sol | 4 +++- contracts/TrustFactory.sol | 36 ++++++++++++++++++++++++----------- coverage.json | 2 +- package.json | 2 +- test/Kyber.js | 39 ++++++++++++++++++++++++++++++++++++++ test/MyBitBurner.js | 10 ---------- truffle.js | 7 +++++++ 9 files changed, 81 insertions(+), 24 deletions(-) create mode 100644 .solcover.js diff --git a/.gitignore b/.gitignore index cdd92b0..5792020 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules build dist coverageEnv +package-lock.json ### Node ### diff --git a/.solcover.js b/.solcover.js new file mode 100644 index 0000000..4b9a53d --- /dev/null +++ b/.solcover.js @@ -0,0 +1,4 @@ +module.exports = { + skipFiles: ['kyber/'], + deepSkip: true +}; diff --git a/contracts/MyBitBurner.sol b/contracts/MyBitBurner.sol index 15c32f7..2af4c43 100644 --- a/contracts/MyBitBurner.sol +++ b/contracts/MyBitBurner.sol @@ -94,7 +94,6 @@ contract MyBitBurner { return change; } - // @notice owner can authorize a contract to burn MyBit here // @param the address of the mybit dapp contract function authorizeBurner(address _burningContract) @@ -119,6 +118,9 @@ contract MyBitBurner { return true; } + // @notice fallback function. Needs to be open to receive returned Ether from kyber.trade() + function() external payable {} + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Modifiers ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/TrustFactory.sol b/contracts/TrustFactory.sol index 45a77b8..7935dad 100644 --- a/contracts/TrustFactory.sol +++ b/contracts/TrustFactory.sol @@ -34,10 +34,17 @@ contract TrustFactory { payable { require(msg.value > 0); require(!expired); - require(mybBurner.burn(msg.sender, mybFee, _burnToken)); + uint amount; + //If burn token is Ether, burn a portion of the trust Ether to pay fees, else burn the token indicated + if(_burnToken == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)){ + amount = ethBurn(msg.value); + } else { + require(mybBurner.burn(msg.sender, mybFee, _burnToken)); + amount = msg.value; + } Trust newTrust = new Trust(msg.sender, _beneficiary, _revokeable, _expiration); - newTrust.depositTrust.value(msg.value)(); - emit LogNewTrust(msg.sender, _beneficiary, address(newTrust), msg.value); + newTrust.depositTrust.value(amount)(); + emit LogNewTrust(msg.sender, _beneficiary, address(newTrust), amount); } // @notice TrustERC20 should be deployed in 2 steps to allow authorization to spend tokens @@ -49,7 +56,7 @@ contract TrustFactory { external payable{ require(!expired); - require(mybBurner.burn(msg.sender, mybFee, _burnToken)); + require(mybBurner.burn.value(msg.value)(msg.sender, mybFee, _burnToken)); TrustERC20 newTrust = new TrustERC20(msg.sender, _beneficiary, _revokeable, _expiration, _tokenContractAddress); emit LogNewTrustERC20(msg.sender, _beneficiary, address(newTrust)); } @@ -63,7 +70,7 @@ contract TrustFactory { external payable{ require(!expired); - require(mybBurner.burn(msg.sender, mybFee, _burnToken)); + require(mybBurner.burn.value(msg.value)(msg.sender, mybFee, _burnToken)); TrustERC721 newTrust = new TrustERC721(msg.sender, _beneficiary, _revokeable, _expiration, _tokenContractAddress); emit LogNewTrustERC721(msg.sender, _beneficiary, address(newTrust)); } @@ -86,12 +93,19 @@ contract TrustFactory { emit LogMYBFeeChange(oldFee, mybFee); } - // @notice fallback function. Rejects all ether - function () - external - payable { - revert(); - } + // @notice burn fees from Ether payment given, return the change after convert+burn + function ethBurn(uint _amount) + private + returns (uint) { + uint balanceBefore = address(this).balance; + require(mybBurner.burn.value(_amount)(address(this), mybFee, address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE))); + uint change = _amount - (balanceBefore - address(this).balance); + require(change <= address(this).balance, "Uh-oh, not enough funds"); + return change; + } + + // @notice fallback function. Needs to be open to receive returned Ether from ethBurn() + function () external payable {} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/coverage.json b/coverage.json index 2813f53..5425609 100644 --- a/coverage.json +++ b/coverage.json @@ -1 +1 @@ -{"contracts/MyBitBurner.sol":{"l":{"20":5,"21":5,"30":19,"32":18,"33":14,"34":14,"43":7,"44":6,"45":6,"46":6,"55":3,"56":2,"57":2,"58":2,"65":1,"74":13,"75":10},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/MyBitBurner.sol","s":{"1":5,"2":5,"3":19,"4":18,"5":14,"6":14,"7":7,"8":6,"9":6,"10":6,"11":3,"12":2,"13":2,"14":1,"15":13},"b":{"1":[18,1],"2":[14,4],"3":[6,1],"4":[2,1],"5":[10,3]},"f":{"1":5,"2":19,"3":7,"4":3,"5":1,"6":13},"fnMap":{"1":{"name":null,"line":18,"loc":{"start":{"line":18,"column":2},"end":{"line":19,"column":10}}},"2":{"name":"burn","line":27,"loc":{"start":{"line":27,"column":2},"end":{"line":29,"column":18}}},"3":{"name":"authorizeBurner","line":39,"loc":{"start":{"line":39,"column":2},"end":{"line":42,"column":18}}},"4":{"name":"removeBurner","line":51,"loc":{"start":{"line":51,"column":2},"end":{"line":54,"column":18}}},"5":{"name":null,"line":62,"loc":{"start":{"line":62,"column":2},"end":{"line":64,"column":12}}},"6":{"name":"onlyOwner","line":73,"loc":{"start":{"line":73,"column":2},"end":{"line":73,"column":20}}}},"statementMap":{"1":{"start":{"line":20,"column":4},"end":{"line":20,"column":47}},"2":{"start":{"line":21,"column":4},"end":{"line":21,"column":21}},"3":{"start":{"line":30,"column":4},"end":{"line":30,"column":40}},"4":{"start":{"line":32,"column":4},"end":{"line":32,"column":52}},"5":{"start":{"line":33,"column":4},"end":{"line":33,"column":56}},"6":{"start":{"line":34,"column":4},"end":{"line":34,"column":15}},"7":{"start":{"line":43,"column":4},"end":{"line":43,"column":47}},"8":{"start":{"line":44,"column":4},"end":{"line":44,"column":44}},"9":{"start":{"line":45,"column":4},"end":{"line":45,"column":58}},"10":{"start":{"line":46,"column":4},"end":{"line":46,"column":15}},"11":{"start":{"line":55,"column":4},"end":{"line":55,"column":46}},"12":{"start":{"line":57,"column":4},"end":{"line":57,"column":55}},"13":{"start":{"line":58,"column":4},"end":{"line":58,"column":15}},"14":{"start":{"line":65,"column":4},"end":{"line":65,"column":11}},"15":{"start":{"line":74,"column":4},"end":{"line":74,"column":31}}},"branchMap":{"1":{"line":30,"type":"if","locations":[{"start":{"line":30,"column":4},"end":{"line":30,"column":4}},{"start":{"line":30,"column":4},"end":{"line":30,"column":4}}]},"2":{"line":32,"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":32,"column":4}},{"start":{"line":32,"column":4},"end":{"line":32,"column":4}}]},"3":{"line":43,"type":"if","locations":[{"start":{"line":43,"column":4},"end":{"line":43,"column":4}},{"start":{"line":43,"column":4},"end":{"line":43,"column":4}}]},"4":{"line":55,"type":"if","locations":[{"start":{"line":55,"column":4},"end":{"line":55,"column":4}},{"start":{"line":55,"column":4},"end":{"line":55,"column":4}}]},"5":{"line":74,"type":"if","locations":[{"start":{"line":74,"column":4},"end":{"line":74,"column":4}},{"start":{"line":74,"column":4},"end":{"line":74,"column":4}}]}}},"contracts/SafeMath.sol":{"l":{"14":3,"15":1,"17":2,"18":2,"19":1,"29":1,"36":272,"37":271,"44":249,"45":249,"46":247},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/SafeMath.sol","s":{"1":3,"2":1,"3":2,"4":2,"5":1,"6":1,"7":272,"8":271,"9":249,"10":249,"11":247},"b":{"1":[1,2],"2":[1,1],"3":[271,1],"4":[247,2]},"f":{"1":3,"2":1,"3":272,"4":249},"fnMap":{"1":{"name":"mul","line":13,"loc":{"start":{"line":13,"column":2},"end":{"line":13,"column":68}}},"2":{"name":"div","line":25,"loc":{"start":{"line":25,"column":2},"end":{"line":25,"column":68}}},"3":{"name":"sub","line":35,"loc":{"start":{"line":35,"column":2},"end":{"line":35,"column":68}}},"4":{"name":"add","line":43,"loc":{"start":{"line":43,"column":2},"end":{"line":43,"column":68}}}},"statementMap":{"1":{"start":{"line":14,"column":4},"end":{"line":14,"column":753}},"2":{"start":{"line":15,"column":6},"end":{"line":15,"column":14}},"3":{"start":{"line":17,"column":4},"end":{"line":17,"column":20}},"4":{"start":{"line":18,"column":4},"end":{"line":18,"column":21}},"5":{"start":{"line":19,"column":4},"end":{"line":19,"column":12}},"6":{"start":{"line":29,"column":4},"end":{"line":29,"column":16}},"7":{"start":{"line":36,"column":4},"end":{"line":36,"column":17}},"8":{"start":{"line":37,"column":4},"end":{"line":37,"column":16}},"9":{"start":{"line":44,"column":4},"end":{"line":44,"column":20}},"10":{"start":{"line":45,"column":4},"end":{"line":45,"column":17}},"11":{"start":{"line":46,"column":4},"end":{"line":46,"column":12}}},"branchMap":{"1":{"line":14,"type":"if","locations":[{"start":{"line":14,"column":4},"end":{"line":14,"column":4}},{"start":{"line":14,"column":4},"end":{"line":14,"column":4}}]},"2":{"line":18,"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":4}},{"start":{"line":18,"column":4},"end":{"line":18,"column":4}}]},"3":{"line":36,"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":4}},{"start":{"line":36,"column":4},"end":{"line":36,"column":4}}]},"4":{"line":45,"type":"if","locations":[{"start":{"line":45,"column":4},"end":{"line":45,"column":4}},{"start":{"line":45,"column":4},"end":{"line":45,"column":4}}]}}},"contracts/SafeMathWrapper.sol":{"l":{"9":3,"13":1,"17":2,"21":2},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/SafeMathWrapper.sol","s":{"1":3,"2":1,"3":2,"4":2},"b":{},"f":{"1":3,"2":1,"3":2,"4":2},"fnMap":{"1":{"name":"multiply","line":8,"loc":{"start":{"line":8,"column":2},"end":{"line":8,"column":73}}},"2":{"name":"divide","line":12,"loc":{"start":{"line":12,"column":2},"end":{"line":12,"column":71}}},"3":{"name":"subtract","line":16,"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":73}}},"4":{"name":"addto","line":20,"loc":{"start":{"line":20,"column":2},"end":{"line":20,"column":70}}}},"statementMap":{"1":{"start":{"line":9,"column":4},"end":{"line":9,"column":19}},"2":{"start":{"line":13,"column":4},"end":{"line":13,"column":19}},"3":{"start":{"line":17,"column":4},"end":{"line":17,"column":19}},"4":{"start":{"line":21,"column":4},"end":{"line":21,"column":19}}},"branchMap":{}},"contracts/token/BurnableERC20.sol":{"l":{},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/token/BurnableERC20.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"contracts/token/ERC20.sol":{"l":{"43":8,"44":8,"45":8,"46":8,"47":8,"48":8,"59":211,"60":210,"61":209,"62":209,"63":209,"64":209,"74":5,"75":4,"76":3,"77":3,"78":3,"79":3,"80":3,"90":22,"91":22,"92":22,"103":0,"104":0,"105":0,"106":0,"116":1,"117":1,"118":1,"119":1,"120":1,"130":20,"131":15,"132":15,"133":15,"134":15,"135":15,"136":15,"146":5,"156":168,"166":3,"177":1},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/token/ERC20.sol","s":{"1":8,"2":8,"3":8,"4":8,"5":8,"6":8,"7":211,"8":210,"9":209,"10":209,"11":209,"12":209,"13":5,"14":4,"15":3,"16":3,"17":3,"18":3,"19":3,"20":22,"21":22,"22":22,"23":0,"24":0,"25":0,"26":0,"27":1,"28":1,"29":1,"30":1,"31":1,"32":20,"33":15,"34":15,"35":15,"36":15,"37":15,"38":15,"39":5,"40":168,"41":3,"42":1},"b":{"1":[210,1],"2":[209,1],"3":[4,1],"4":[3,1],"5":[15,5]},"f":{"1":8,"2":211,"3":5,"4":22,"5":0,"6":1,"7":20,"8":5,"9":168,"10":3,"11":1},"fnMap":{"1":{"name":null,"line":41,"loc":{"start":{"line":41,"column":4},"end":{"line":42,"column":12}}},"2":{"name":"transfer","line":56,"loc":{"start":{"line":56,"column":4},"end":{"line":58,"column":28}}},"3":{"name":"transferFrom","line":71,"loc":{"start":{"line":71,"column":4},"end":{"line":73,"column":28}}},"4":{"name":"approve","line":87,"loc":{"start":{"line":87,"column":4},"end":{"line":89,"column":28}}},"5":{"name":"approveAndCall","line":100,"loc":{"start":{"line":100,"column":4},"end":{"line":102,"column":28}}},"6":{"name":"burn","line":113,"loc":{"start":{"line":113,"column":4},"end":{"line":115,"column":28}}},"7":{"name":"burnFrom","line":127,"loc":{"start":{"line":127,"column":4},"end":{"line":129,"column":28}}},"8":{"name":"totalSupply","line":142,"loc":{"start":{"line":142,"column":4},"end":{"line":145,"column":32}}},"9":{"name":"balanceOf","line":152,"loc":{"start":{"line":152,"column":4},"end":{"line":155,"column":28}}},"10":{"name":"allowance","line":162,"loc":{"start":{"line":162,"column":4},"end":{"line":165,"column":30}}},"11":{"name":null,"line":174,"loc":{"start":{"line":174,"column":4},"end":{"line":176,"column":13}}}},"statementMap":{"1":{"start":{"line":43,"column":8},"end":{"line":43,"column":44}},"2":{"start":{"line":44,"column":8},"end":{"line":44,"column":30}},"3":{"start":{"line":45,"column":8},"end":{"line":45,"column":24}},"4":{"start":{"line":46,"column":8},"end":{"line":46,"column":31}},"5":{"start":{"line":47,"column":8},"end":{"line":47,"column":28}},"6":{"start":{"line":48,"column":8},"end":{"line":48,"column":61}},"7":{"start":{"line":59,"column":8},"end":{"line":59,"column":33}},"8":{"start":{"line":60,"column":8},"end":{"line":60,"column":36}},"9":{"start":{"line":61,"column":8},"end":{"line":61,"column":63}},"10":{"start":{"line":62,"column":8},"end":{"line":62,"column":49}},"11":{"start":{"line":63,"column":8},"end":{"line":63,"column":47}},"12":{"start":{"line":64,"column":8},"end":{"line":64,"column":19}},"13":{"start":{"line":74,"column":8},"end":{"line":74,"column":33}},"14":{"start":{"line":75,"column":8},"end":{"line":75,"column":36}},"15":{"start":{"line":76,"column":8},"end":{"line":76,"column":53}},"16":{"start":{"line":77,"column":8},"end":{"line":77,"column":75}},"17":{"start":{"line":78,"column":8},"end":{"line":78,"column":49}},"18":{"start":{"line":79,"column":8},"end":{"line":79,"column":42}},"19":{"start":{"line":80,"column":8},"end":{"line":80,"column":19}},"20":{"start":{"line":90,"column":8},"end":{"line":90,"column":46}},"21":{"start":{"line":91,"column":8},"end":{"line":91,"column":52}},"22":{"start":{"line":92,"column":8},"end":{"line":92,"column":19}},"23":{"start":{"line":103,"column":8},"end":{"line":103,"column":46}},"24":{"start":{"line":104,"column":8},"end":{"line":104,"column":52}},"25":{"start":{"line":105,"column":8},"end":{"line":105,"column":39}},"26":{"start":{"line":106,"column":8},"end":{"line":106,"column":19}},"27":{"start":{"line":116,"column":8},"end":{"line":116,"column":63}},"28":{"start":{"line":117,"column":8},"end":{"line":117,"column":35}},"29":{"start":{"line":118,"column":8},"end":{"line":118,"column":41}},"30":{"start":{"line":119,"column":8},"end":{"line":119,"column":54}},"31":{"start":{"line":120,"column":8},"end":{"line":120,"column":19}},"32":{"start":{"line":130,"column":8},"end":{"line":130,"column":53}},"33":{"start":{"line":131,"column":8},"end":{"line":131,"column":53}},"34":{"start":{"line":132,"column":8},"end":{"line":132,"column":75}},"35":{"start":{"line":133,"column":8},"end":{"line":133,"column":35}},"36":{"start":{"line":134,"column":8},"end":{"line":134,"column":36}},"37":{"start":{"line":135,"column":8},"end":{"line":135,"column":49}},"38":{"start":{"line":136,"column":8},"end":{"line":136,"column":19}},"39":{"start":{"line":146,"column":8},"end":{"line":146,"column":21}},"40":{"start":{"line":156,"column":8},"end":{"line":156,"column":37}},"41":{"start":{"line":166,"column":8},"end":{"line":166,"column":46}},"42":{"start":{"line":177,"column":8},"end":{"line":177,"column":15}}},"branchMap":{"1":{"line":59,"type":"if","locations":[{"start":{"line":59,"column":8},"end":{"line":59,"column":8}},{"start":{"line":59,"column":8},"end":{"line":59,"column":8}}]},"2":{"line":60,"type":"if","locations":[{"start":{"line":60,"column":8},"end":{"line":60,"column":8}},{"start":{"line":60,"column":8},"end":{"line":60,"column":8}}]},"3":{"line":74,"type":"if","locations":[{"start":{"line":74,"column":8},"end":{"line":74,"column":8}},{"start":{"line":74,"column":8},"end":{"line":74,"column":8}}]},"4":{"line":75,"type":"if","locations":[{"start":{"line":75,"column":8},"end":{"line":75,"column":8}},{"start":{"line":75,"column":8},"end":{"line":75,"column":8}}]},"5":{"line":130,"type":"if","locations":[{"start":{"line":130,"column":8},"end":{"line":130,"column":8}},{"start":{"line":130,"column":8},"end":{"line":130,"column":8}}]}}},"contracts/token/ERC20Interface.sol":{"l":{},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/token/ERC20Interface.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"contracts/token/IERC721.sol":{"l":{},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/token/IERC721.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"contracts/token/SampleERC721.sol":{"l":{"30":4,"43":9,"44":8,"46":7,"47":7,"49":7,"50":7,"59":2,"60":1,"69":31,"70":31,"71":29,"83":5,"84":5,"85":4,"87":3,"88":3,"98":4,"99":3,"109":1,"110":1,"111":1,"121":2,"133":7,"135":6,"179":12,"180":12,"191":7,"195":6,"206":6,"207":5,"209":5,"211":5,"212":5,"214":5,"216":5,"224":5,"225":3,"231":10,"232":9},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/token/SampleERC721.sol","s":{"1":4,"2":9,"3":8,"4":7,"5":7,"6":7,"7":7,"8":2,"9":1,"10":31,"11":31,"12":29,"13":5,"14":5,"15":4,"16":3,"17":3,"18":4,"19":3,"20":1,"21":1,"22":1,"23":2,"24":7,"25":6,"26":12,"27":12,"28":7,"29":6,"30":6,"31":5,"32":5,"33":5,"34":5,"35":5,"36":5,"37":5,"38":3,"39":10},"b":{"1":[8,1],"2":[7,1],"3":[1,1],"4":[29,2],"5":[4,1],"6":[3,1],"7":[3,1],"8":[1,0],"9":[6,1],"10":[5,1],"11":[5,0],"12":[3,2],"13":[9,1]},"f":{"1":4,"2":9,"3":2,"4":31,"5":5,"6":4,"7":1,"8":2,"9":7,"10":12,"11":7,"12":6,"13":5,"14":10},"fnMap":{"1":{"name":null,"line":28,"loc":{"start":{"line":28,"column":4},"end":{"line":29,"column":12}}},"2":{"name":"mint","line":39,"loc":{"start":{"line":39,"column":4},"end":{"line":42,"column":20}}},"3":{"name":"balanceOf","line":58,"loc":{"start":{"line":58,"column":4},"end":{"line":58,"column":65}}},"4":{"name":"ownerOf","line":68,"loc":{"start":{"line":68,"column":4},"end":{"line":68,"column":65}}},"5":{"name":"approve","line":82,"loc":{"start":{"line":82,"column":4},"end":{"line":82,"column":54}}},"6":{"name":"getApproved","line":97,"loc":{"start":{"line":97,"column":4},"end":{"line":97,"column":69}}},"7":{"name":"setApprovalForAll","line":108,"loc":{"start":{"line":108,"column":4},"end":{"line":108,"column":62}}},"8":{"name":"isApprovedForAll","line":120,"loc":{"start":{"line":120,"column":4},"end":{"line":120,"column":87}}},"9":{"name":"transferFrom","line":132,"loc":{"start":{"line":132,"column":4},"end":{"line":132,"column":73}}},"10":{"name":"_exists","line":178,"loc":{"start":{"line":178,"column":4},"end":{"line":178,"column":64}}},"11":{"name":"_isApprovedOrOwner","line":190,"loc":{"start":{"line":190,"column":4},"end":{"line":190,"column":92}}},"12":{"name":"_transferFrom","line":205,"loc":{"start":{"line":205,"column":4},"end":{"line":205,"column":76}}},"13":{"name":"_clearApproval","line":223,"loc":{"start":{"line":223,"column":4},"end":{"line":223,"column":50}}},"14":{"name":"onlyOwner","line":230,"loc":{"start":{"line":230,"column":4},"end":{"line":230,"column":20}}}},"statementMap":{"1":{"start":{"line":30,"column":8},"end":{"line":30,"column":26}},"2":{"start":{"line":43,"column":8},"end":{"line":43,"column":32}},"3":{"start":{"line":44,"column":8},"end":{"line":44,"column":33}},"4":{"start":{"line":46,"column":8},"end":{"line":46,"column":32}},"5":{"start":{"line":47,"column":8},"end":{"line":47,"column":59}},"6":{"start":{"line":49,"column":8},"end":{"line":49,"column":46}},"7":{"start":{"line":50,"column":8},"end":{"line":50,"column":19}},"8":{"start":{"line":59,"column":8},"end":{"line":59,"column":35}},"9":{"start":{"line":60,"column":8},"end":{"line":60,"column":39}},"10":{"start":{"line":69,"column":8},"end":{"line":69,"column":43}},"11":{"start":{"line":70,"column":8},"end":{"line":70,"column":35}},"12":{"start":{"line":71,"column":8},"end":{"line":71,"column":20}},"13":{"start":{"line":83,"column":8},"end":{"line":83,"column":39}},"14":{"start":{"line":84,"column":8},"end":{"line":84,"column":27}},"15":{"start":{"line":85,"column":8},"end":{"line":85,"column":74}},"16":{"start":{"line":87,"column":8},"end":{"line":87,"column":36}},"17":{"start":{"line":88,"column":8},"end":{"line":88,"column":41}},"18":{"start":{"line":98,"column":8},"end":{"line":98,"column":32}},"19":{"start":{"line":99,"column":8},"end":{"line":99,"column":39}},"20":{"start":{"line":109,"column":8},"end":{"line":109,"column":32}},"21":{"start":{"line":110,"column":8},"end":{"line":110,"column":52}},"22":{"start":{"line":111,"column":8},"end":{"line":111,"column":53}},"23":{"start":{"line":121,"column":8},"end":{"line":121,"column":50}},"24":{"start":{"line":133,"column":8},"end":{"line":133,"column":55}},"25":{"start":{"line":135,"column":8},"end":{"line":135,"column":39}},"26":{"start":{"line":179,"column":8},"end":{"line":179,"column":43}},"27":{"start":{"line":180,"column":8},"end":{"line":180,"column":34}},"28":{"start":{"line":191,"column":8},"end":{"line":191,"column":39}},"29":{"start":{"line":195,"column":8},"end":{"line":195,"column":104}},"30":{"start":{"line":206,"column":8},"end":{"line":206,"column":40}},"31":{"start":{"line":207,"column":8},"end":{"line":207,"column":32}},"32":{"start":{"line":209,"column":8},"end":{"line":209,"column":30}},"33":{"start":{"line":211,"column":8},"end":{"line":211,"column":63}},"34":{"start":{"line":212,"column":8},"end":{"line":212,"column":59}},"35":{"start":{"line":214,"column":8},"end":{"line":214,"column":32}},"36":{"start":{"line":216,"column":8},"end":{"line":216,"column":40}},"37":{"start":{"line":224,"column":8},"end":{"line":224,"column":8964}},"38":{"start":{"line":225,"column":12},"end":{"line":225,"column":48}},"39":{"start":{"line":231,"column":8},"end":{"line":231,"column":36}}},"branchMap":{"1":{"line":43,"type":"if","locations":[{"start":{"line":43,"column":8},"end":{"line":43,"column":8}},{"start":{"line":43,"column":8},"end":{"line":43,"column":8}}]},"2":{"line":44,"type":"if","locations":[{"start":{"line":44,"column":8},"end":{"line":44,"column":8}},{"start":{"line":44,"column":8},"end":{"line":44,"column":8}}]},"3":{"line":59,"type":"if","locations":[{"start":{"line":59,"column":8},"end":{"line":59,"column":8}},{"start":{"line":59,"column":8},"end":{"line":59,"column":8}}]},"4":{"line":70,"type":"if","locations":[{"start":{"line":70,"column":8},"end":{"line":70,"column":8}},{"start":{"line":70,"column":8},"end":{"line":70,"column":8}}]},"5":{"line":84,"type":"if","locations":[{"start":{"line":84,"column":8},"end":{"line":84,"column":8}},{"start":{"line":84,"column":8},"end":{"line":84,"column":8}}]},"6":{"line":85,"type":"if","locations":[{"start":{"line":85,"column":8},"end":{"line":85,"column":8}},{"start":{"line":85,"column":8},"end":{"line":85,"column":8}}]},"7":{"line":98,"type":"if","locations":[{"start":{"line":98,"column":8},"end":{"line":98,"column":8}},{"start":{"line":98,"column":8},"end":{"line":98,"column":8}}]},"8":{"line":109,"type":"if","locations":[{"start":{"line":109,"column":8},"end":{"line":109,"column":8}},{"start":{"line":109,"column":8},"end":{"line":109,"column":8}}]},"9":{"line":133,"type":"if","locations":[{"start":{"line":133,"column":8},"end":{"line":133,"column":8}},{"start":{"line":133,"column":8},"end":{"line":133,"column":8}}]},"10":{"line":206,"type":"if","locations":[{"start":{"line":206,"column":8},"end":{"line":206,"column":8}},{"start":{"line":206,"column":8},"end":{"line":206,"column":8}}]},"11":{"line":207,"type":"if","locations":[{"start":{"line":207,"column":8},"end":{"line":207,"column":8}},{"start":{"line":207,"column":8},"end":{"line":207,"column":8}}]},"12":{"line":224,"type":"if","locations":[{"start":{"line":224,"column":8},"end":{"line":224,"column":8}},{"start":{"line":224,"column":8},"end":{"line":224,"column":8}}]},"13":{"line":231,"type":"if","locations":[{"start":{"line":231,"column":8},"end":{"line":231,"column":8}},{"start":{"line":231,"column":8},"end":{"line":231,"column":8}}]}}},"contracts/Trust.sol":{"l":{"33":4,"34":4,"35":4,"36":4,"46":6,"47":4,"48":4,"49":4,"58":1,"59":1,"60":1,"69":2,"70":1,"71":1,"72":1,"73":1,"74":1,"85":2,"86":2,"87":1,"88":1,"99":2,"100":1,"101":1,"102":1,"109":1,"120":2,"121":1,"130":23,"131":21,"136":9,"137":8,"142":6,"143":5},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/Trust.sol","s":{"1":4,"2":4,"3":4,"4":4,"5":6,"6":4,"7":4,"8":4,"9":1,"10":1,"11":2,"12":1,"13":1,"14":1,"15":2,"16":2,"17":1,"18":1,"19":2,"20":1,"21":1,"22":1,"23":1,"24":2,"25":1,"26":1,"27":23,"28":9,"29":6},"b":{"1":[4,2],"2":[1,1],"3":[1,1],"4":[1,1],"5":[21,2],"6":[8,1],"7":[5,1]},"f":{"1":4,"2":6,"3":1,"4":2,"5":2,"6":2,"7":1,"8":2,"9":23,"10":9,"11":6},"fnMap":{"1":{"name":null,"line":31,"loc":{"start":{"line":31,"column":1},"end":{"line":32,"column":9}}},"2":{"name":"depositTrust","line":41,"loc":{"start":{"line":41,"column":1},"end":{"line":45,"column":10}}},"3":{"name":"revoke","line":53,"loc":{"start":{"line":53,"column":1},"end":{"line":57,"column":14}}},"4":{"name":"withdraw","line":64,"loc":{"start":{"line":64,"column":1},"end":{"line":68,"column":17}}},"5":{"name":"changeExpiration","line":79,"loc":{"start":{"line":79,"column":1},"end":{"line":84,"column":16}}},"6":{"name":"changeBeneficiary","line":93,"loc":{"start":{"line":93,"column":1},"end":{"line":98,"column":16}}},"7":{"name":null,"line":106,"loc":{"start":{"line":106,"column":1},"end":{"line":108,"column":10}}},"8":{"name":"secUntilExpiration","line":117,"loc":{"start":{"line":117,"column":1},"end":{"line":120,"column":17}}},"9":{"name":"lessThan","line":130,"loc":{"start":{"line":130,"column":1},"end":{"line":130,"column":37}}},"10":{"name":"onlySender","line":136,"loc":{"start":{"line":136,"column":1},"end":{"line":136,"column":46}}},"11":{"name":"isRevocable","line":142,"loc":{"start":{"line":142,"column":1},"end":{"line":142,"column":22}}}},"statementMap":{"1":{"start":{"line":33,"column":2},"end":{"line":33,"column":19}},"2":{"start":{"line":34,"column":2},"end":{"line":34,"column":27}},"3":{"start":{"line":35,"column":2},"end":{"line":35,"column":23}},"4":{"start":{"line":36,"column":2},"end":{"line":36,"column":46}},"5":{"start":{"line":46,"column":2},"end":{"line":46,"column":27}},"6":{"start":{"line":47,"column":2},"end":{"line":47,"column":24}},"7":{"start":{"line":48,"column":3},"end":{"line":48,"column":44}},"8":{"start":{"line":49,"column":3},"end":{"line":49,"column":41}},"9":{"start":{"line":58,"column":2},"end":{"line":58,"column":48}},"10":{"start":{"line":60,"column":2},"end":{"line":60,"column":25}},"11":{"start":{"line":69,"column":2},"end":{"line":69,"column":26}},"12":{"start":{"line":70,"column":2},"end":{"line":70,"column":27}},"13":{"start":{"line":73,"column":2},"end":{"line":73,"column":39}},"14":{"start":{"line":74,"column":2},"end":{"line":74,"column":13}},"15":{"start":{"line":85,"column":2},"end":{"line":85,"column":32}},"16":{"start":{"line":86,"column":2},"end":{"line":86,"column":45}},"17":{"start":{"line":87,"column":2},"end":{"line":87,"column":54}},"18":{"start":{"line":88,"column":2},"end":{"line":88,"column":13}},"19":{"start":{"line":99,"column":2},"end":{"line":99,"column":36}},"20":{"start":{"line":100,"column":2},"end":{"line":100,"column":51}},"21":{"start":{"line":101,"column":2},"end":{"line":101,"column":27}},"22":{"start":{"line":102,"column":2},"end":{"line":102,"column":13}},"23":{"start":{"line":109,"column":2},"end":{"line":109,"column":9}},"24":{"start":{"line":121,"column":2},"end":{"line":121,"column":48}},"25":{"start":{"line":121,"column":38},"end":{"line":121,"column":46}},"26":{"start":{"line":122,"column":2},"end":{"line":122,"column":40}},"27":{"start":{"line":131,"column":2},"end":{"line":131,"column":17}},"28":{"start":{"line":137,"column":2},"end":{"line":137,"column":39}},"29":{"start":{"line":143,"column":2},"end":{"line":143,"column":19}}},"branchMap":{"1":{"line":46,"type":"if","locations":[{"start":{"line":46,"column":2},"end":{"line":46,"column":2}},{"start":{"line":46,"column":2},"end":{"line":46,"column":2}}]},"2":{"line":69,"type":"if","locations":[{"start":{"line":69,"column":2},"end":{"line":69,"column":2}},{"start":{"line":69,"column":2},"end":{"line":69,"column":2}}]},"3":{"line":99,"type":"if","locations":[{"start":{"line":99,"column":2},"end":{"line":99,"column":2}},{"start":{"line":99,"column":2},"end":{"line":99,"column":2}}]},"4":{"line":121,"type":"if","locations":[{"start":{"line":121,"column":2},"end":{"line":121,"column":2}},{"start":{"line":121,"column":2},"end":{"line":121,"column":2}}]},"5":{"line":131,"type":"if","locations":[{"start":{"line":131,"column":2},"end":{"line":131,"column":2}},{"start":{"line":131,"column":2},"end":{"line":131,"column":2}}]},"6":{"line":137,"type":"if","locations":[{"start":{"line":137,"column":2},"end":{"line":137,"column":2}},{"start":{"line":137,"column":2},"end":{"line":137,"column":2}}]},"7":{"line":143,"type":"if","locations":[{"start":{"line":143,"column":2},"end":{"line":143,"column":2}},{"start":{"line":143,"column":2},"end":{"line":143,"column":2}}]}}},"contracts/TrustERC20.sol":{"l":{"35":5,"36":5,"37":5,"38":5,"39":5,"50":5,"51":4,"52":2,"53":2,"54":2,"55":2,"64":1,"65":1,"66":1,"67":1,"76":2,"77":1,"78":1,"79":1,"80":1,"81":1,"92":1,"93":1,"94":1,"95":1,"106":2,"107":1,"108":1,"109":1,"116":1,"127":2,"128":1,"137":20,"138":18,"143":8,"144":7,"149":5,"150":4},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/TrustERC20.sol","s":{"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":4,"8":2,"9":2,"10":2,"11":1,"12":1,"13":2,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":2,"22":1,"23":1,"24":1,"25":1,"26":2,"27":1,"28":1,"29":20,"30":8,"31":5},"b":{"1":[4,1],"2":[2,2],"3":[1,1],"4":[1,1],"5":[1,1],"6":[18,2],"7":[7,1],"8":[4,1]},"f":{"1":5,"2":5,"3":1,"4":2,"5":1,"6":2,"7":1,"8":2,"9":20,"10":8,"11":5},"fnMap":{"1":{"name":null,"line":33,"loc":{"start":{"line":33,"column":1},"end":{"line":34,"column":9}}},"2":{"name":"depositTrust","line":45,"loc":{"start":{"line":45,"column":1},"end":{"line":49,"column":10}}},"3":{"name":"revoke","line":59,"loc":{"start":{"line":59,"column":1},"end":{"line":63,"column":14}}},"4":{"name":"withdraw","line":71,"loc":{"start":{"line":71,"column":1},"end":{"line":75,"column":17}}},"5":{"name":"changeExpiration","line":86,"loc":{"start":{"line":86,"column":1},"end":{"line":91,"column":16}}},"6":{"name":"changeBeneficiary","line":100,"loc":{"start":{"line":100,"column":1},"end":{"line":105,"column":16}}},"7":{"name":null,"line":113,"loc":{"start":{"line":113,"column":1},"end":{"line":115,"column":10}}},"8":{"name":"secUntilExpiration","line":124,"loc":{"start":{"line":124,"column":1},"end":{"line":127,"column":17}}},"9":{"name":"lessThan","line":137,"loc":{"start":{"line":137,"column":1},"end":{"line":137,"column":37}}},"10":{"name":"onlySender","line":143,"loc":{"start":{"line":143,"column":1},"end":{"line":143,"column":46}}},"11":{"name":"isRevocable","line":149,"loc":{"start":{"line":149,"column":1},"end":{"line":149,"column":22}}}},"statementMap":{"1":{"start":{"line":35,"column":2},"end":{"line":35,"column":19}},"2":{"start":{"line":36,"column":2},"end":{"line":36,"column":27}},"3":{"start":{"line":37,"column":2},"end":{"line":37,"column":23}},"4":{"start":{"line":38,"column":2},"end":{"line":38,"column":46}},"5":{"start":{"line":39,"column":2},"end":{"line":39,"column":37}},"6":{"start":{"line":50,"column":2},"end":{"line":50,"column":27}},"7":{"start":{"line":51,"column":2},"end":{"line":51,"column":48}},"8":{"start":{"line":52,"column":2},"end":{"line":52,"column":24}},"9":{"start":{"line":53,"column":2},"end":{"line":53,"column":41}},"10":{"start":{"line":55,"column":2},"end":{"line":55,"column":38}},"11":{"start":{"line":65,"column":2},"end":{"line":65,"column":48}},"12":{"start":{"line":67,"column":2},"end":{"line":67,"column":25}},"13":{"start":{"line":76,"column":2},"end":{"line":76,"column":26}},"14":{"start":{"line":77,"column":2},"end":{"line":77,"column":27}},"15":{"start":{"line":80,"column":2},"end":{"line":80,"column":39}},"16":{"start":{"line":81,"column":2},"end":{"line":81,"column":13}},"17":{"start":{"line":92,"column":2},"end":{"line":92,"column":32}},"18":{"start":{"line":93,"column":2},"end":{"line":93,"column":43}},"19":{"start":{"line":94,"column":2},"end":{"line":94,"column":54}},"20":{"start":{"line":95,"column":2},"end":{"line":95,"column":13}},"21":{"start":{"line":106,"column":2},"end":{"line":106,"column":36}},"22":{"start":{"line":107,"column":2},"end":{"line":107,"column":51}},"23":{"start":{"line":108,"column":2},"end":{"line":108,"column":27}},"24":{"start":{"line":109,"column":2},"end":{"line":109,"column":13}},"25":{"start":{"line":116,"column":2},"end":{"line":116,"column":9}},"26":{"start":{"line":128,"column":2},"end":{"line":128,"column":48}},"27":{"start":{"line":128,"column":38},"end":{"line":128,"column":46}},"28":{"start":{"line":129,"column":2},"end":{"line":129,"column":40}},"29":{"start":{"line":138,"column":2},"end":{"line":138,"column":17}},"30":{"start":{"line":144,"column":2},"end":{"line":144,"column":39}},"31":{"start":{"line":150,"column":2},"end":{"line":150,"column":19}}},"branchMap":{"1":{"line":50,"type":"if","locations":[{"start":{"line":50,"column":2},"end":{"line":50,"column":2}},{"start":{"line":50,"column":2},"end":{"line":50,"column":2}}]},"2":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":2},"end":{"line":51,"column":2}},{"start":{"line":51,"column":2},"end":{"line":51,"column":2}}]},"3":{"line":76,"type":"if","locations":[{"start":{"line":76,"column":2},"end":{"line":76,"column":2}},{"start":{"line":76,"column":2},"end":{"line":76,"column":2}}]},"4":{"line":106,"type":"if","locations":[{"start":{"line":106,"column":2},"end":{"line":106,"column":2}},{"start":{"line":106,"column":2},"end":{"line":106,"column":2}}]},"5":{"line":128,"type":"if","locations":[{"start":{"line":128,"column":2},"end":{"line":128,"column":2}},{"start":{"line":128,"column":2},"end":{"line":128,"column":2}}]},"6":{"line":138,"type":"if","locations":[{"start":{"line":138,"column":2},"end":{"line":138,"column":2}},{"start":{"line":138,"column":2},"end":{"line":138,"column":2}}]},"7":{"line":144,"type":"if","locations":[{"start":{"line":144,"column":2},"end":{"line":144,"column":2}},{"start":{"line":144,"column":2},"end":{"line":144,"column":2}}]},"8":{"line":150,"type":"if","locations":[{"start":{"line":150,"column":2},"end":{"line":150,"column":2}},{"start":{"line":150,"column":2},"end":{"line":150,"column":2}}]}}},"contracts/TrustERC721.sol":{"l":{"37":4,"38":4,"39":4,"40":4,"41":4,"51":4,"52":3,"53":2,"54":2,"55":2,"56":2,"65":1,"66":1,"67":1,"68":1,"69":1,"78":2,"79":1,"80":1,"81":1,"82":1,"83":1,"84":1,"95":1,"96":1,"97":1,"98":1,"109":2,"110":1,"111":1,"112":1,"119":1,"130":2,"131":1,"140":14,"141":12,"146":8,"147":7,"152":5,"153":4},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/TrustERC721.sol","s":{"1":4,"2":4,"3":4,"4":4,"5":4,"6":4,"7":3,"8":2,"9":2,"10":2,"11":1,"12":1,"13":1,"14":2,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":2,"24":1,"25":1,"26":1,"27":1,"28":2,"29":1,"30":1,"31":14,"32":8,"33":5},"b":{"1":[3,1],"2":[2,1],"3":[1,1],"4":[1,1],"5":[1,1],"6":[12,2],"7":[7,1],"8":[4,1]},"f":{"1":4,"2":4,"3":1,"4":2,"5":1,"6":2,"7":1,"8":2,"9":14,"10":8,"11":5},"fnMap":{"1":{"name":null,"line":35,"loc":{"start":{"line":35,"column":1},"end":{"line":36,"column":9}}},"2":{"name":"depositTrust","line":47,"loc":{"start":{"line":47,"column":1},"end":{"line":50,"column":10}}},"3":{"name":"revoke","line":60,"loc":{"start":{"line":60,"column":1},"end":{"line":64,"column":14}}},"4":{"name":"withdraw","line":73,"loc":{"start":{"line":73,"column":1},"end":{"line":77,"column":17}}},"5":{"name":"changeExpiration","line":89,"loc":{"start":{"line":89,"column":1},"end":{"line":94,"column":16}}},"6":{"name":"changeBeneficiary","line":103,"loc":{"start":{"line":103,"column":1},"end":{"line":108,"column":16}}},"7":{"name":null,"line":116,"loc":{"start":{"line":116,"column":1},"end":{"line":118,"column":10}}},"8":{"name":"secUntilExpiration","line":127,"loc":{"start":{"line":127,"column":1},"end":{"line":130,"column":17}}},"9":{"name":"lessThan","line":140,"loc":{"start":{"line":140,"column":1},"end":{"line":140,"column":37}}},"10":{"name":"onlySender","line":146,"loc":{"start":{"line":146,"column":1},"end":{"line":146,"column":46}}},"11":{"name":"isRevocable","line":152,"loc":{"start":{"line":152,"column":1},"end":{"line":152,"column":22}}}},"statementMap":{"1":{"start":{"line":37,"column":2},"end":{"line":37,"column":19}},"2":{"start":{"line":38,"column":2},"end":{"line":38,"column":27}},"3":{"start":{"line":39,"column":2},"end":{"line":39,"column":23}},"4":{"start":{"line":40,"column":2},"end":{"line":40,"column":46}},"5":{"start":{"line":41,"column":2},"end":{"line":41,"column":39}},"6":{"start":{"line":51,"column":2},"end":{"line":51,"column":27}},"7":{"start":{"line":52,"column":2},"end":{"line":52,"column":47}},"8":{"start":{"line":53,"column":2},"end":{"line":53,"column":24}},"9":{"start":{"line":54,"column":2},"end":{"line":54,"column":24}},"10":{"start":{"line":56,"column":2},"end":{"line":56,"column":39}},"11":{"start":{"line":66,"column":2},"end":{"line":66,"column":48}},"12":{"start":{"line":68,"column":2},"end":{"line":68,"column":24}},"13":{"start":{"line":69,"column":2},"end":{"line":69,"column":25}},"14":{"start":{"line":78,"column":2},"end":{"line":78,"column":27}},"15":{"start":{"line":79,"column":2},"end":{"line":79,"column":28}},"16":{"start":{"line":81,"column":2},"end":{"line":81,"column":24}},"17":{"start":{"line":83,"column":2},"end":{"line":83,"column":40}},"18":{"start":{"line":84,"column":2},"end":{"line":84,"column":13}},"19":{"start":{"line":95,"column":2},"end":{"line":95,"column":32}},"20":{"start":{"line":96,"column":2},"end":{"line":96,"column":43}},"21":{"start":{"line":97,"column":2},"end":{"line":97,"column":54}},"22":{"start":{"line":98,"column":2},"end":{"line":98,"column":13}},"23":{"start":{"line":109,"column":2},"end":{"line":109,"column":36}},"24":{"start":{"line":110,"column":2},"end":{"line":110,"column":51}},"25":{"start":{"line":111,"column":2},"end":{"line":111,"column":27}},"26":{"start":{"line":112,"column":2},"end":{"line":112,"column":13}},"27":{"start":{"line":119,"column":2},"end":{"line":119,"column":9}},"28":{"start":{"line":131,"column":2},"end":{"line":131,"column":48}},"29":{"start":{"line":131,"column":38},"end":{"line":131,"column":46}},"30":{"start":{"line":132,"column":2},"end":{"line":132,"column":40}},"31":{"start":{"line":141,"column":2},"end":{"line":141,"column":17}},"32":{"start":{"line":147,"column":2},"end":{"line":147,"column":39}},"33":{"start":{"line":153,"column":2},"end":{"line":153,"column":19}}},"branchMap":{"1":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":2},"end":{"line":51,"column":2}},{"start":{"line":51,"column":2},"end":{"line":51,"column":2}}]},"2":{"line":52,"type":"if","locations":[{"start":{"line":52,"column":2},"end":{"line":52,"column":2}},{"start":{"line":52,"column":2},"end":{"line":52,"column":2}}]},"3":{"line":78,"type":"if","locations":[{"start":{"line":78,"column":2},"end":{"line":78,"column":2}},{"start":{"line":78,"column":2},"end":{"line":78,"column":2}}]},"4":{"line":109,"type":"if","locations":[{"start":{"line":109,"column":2},"end":{"line":109,"column":2}},{"start":{"line":109,"column":2},"end":{"line":109,"column":2}}]},"5":{"line":131,"type":"if","locations":[{"start":{"line":131,"column":2},"end":{"line":131,"column":2}},{"start":{"line":131,"column":2},"end":{"line":131,"column":2}}]},"6":{"line":141,"type":"if","locations":[{"start":{"line":141,"column":2},"end":{"line":141,"column":2}},{"start":{"line":141,"column":2},"end":{"line":141,"column":2}}]},"7":{"line":147,"type":"if","locations":[{"start":{"line":147,"column":2},"end":{"line":147,"column":2}},{"start":{"line":147,"column":2},"end":{"line":147,"column":2}}]},"8":{"line":153,"type":"if","locations":[{"start":{"line":153,"column":2},"end":{"line":153,"column":2}},{"start":{"line":153,"column":2},"end":{"line":153,"column":2}}]}}},"contracts/TrustFactory.sol":{"l":{"24":4,"25":4,"35":8,"36":6,"37":5,"38":4,"39":4,"40":4,"51":7,"52":6,"53":5,"54":5,"65":6,"66":5,"67":4,"68":4,"76":4,"77":3,"84":3,"85":3,"86":3,"93":1,"103":9,"104":7},"path":"/mnt/c/Users/Steven/MyBit-Trust.tech/contracts/TrustFactory.sol","s":{"1":4,"2":4,"3":8,"4":6,"5":5,"6":4,"7":4,"8":7,"9":6,"10":5,"11":5,"12":6,"13":5,"14":4,"15":4,"16":4,"17":3,"18":3,"19":3,"20":3,"21":1,"22":9},"b":{"1":[6,2],"2":[5,1],"3":[4,1],"4":[6,1],"5":[5,1],"6":[5,1],"7":[4,1],"8":[3,1],"9":[7,2]},"f":{"1":4,"2":8,"3":7,"4":6,"5":4,"6":3,"7":1,"8":9},"fnMap":{"1":{"name":null,"line":22,"loc":{"start":{"line":22,"column":2},"end":{"line":23,"column":10}}},"2":{"name":"deployTrust","line":32,"loc":{"start":{"line":32,"column":2},"end":{"line":34,"column":11}}},"3":{"name":"createTrustERC20","line":48,"loc":{"start":{"line":48,"column":2},"end":{"line":50,"column":10}}},"4":{"name":"createTrustERC721","line":62,"loc":{"start":{"line":62,"column":2},"end":{"line":64,"column":10}}},"5":{"name":"closeFactory","line":73,"loc":{"start":{"line":73,"column":2},"end":{"line":75,"column":12}}},"6":{"name":"changeMYBFee","line":81,"loc":{"start":{"line":81,"column":2},"end":{"line":83,"column":12}}},"7":{"name":null,"line":90,"loc":{"start":{"line":90,"column":2},"end":{"line":92,"column":10}}},"8":{"name":"onlyOwner","line":102,"loc":{"start":{"line":102,"column":2},"end":{"line":102,"column":20}}}},"statementMap":{"1":{"start":{"line":24,"column":4},"end":{"line":24,"column":21}},"2":{"start":{"line":25,"column":4},"end":{"line":25,"column":43}},"3":{"start":{"line":35,"column":4},"end":{"line":35,"column":25}},"4":{"start":{"line":36,"column":4},"end":{"line":36,"column":20}},"5":{"start":{"line":37,"column":4},"end":{"line":37,"column":46}},"6":{"start":{"line":38,"column":4},"end":{"line":38,"column":81}},"7":{"start":{"line":40,"column":4},"end":{"line":40,"column":76}},"8":{"start":{"line":51,"column":4},"end":{"line":51,"column":20}},"9":{"start":{"line":52,"column":4},"end":{"line":52,"column":46}},"10":{"start":{"line":53,"column":4},"end":{"line":53,"column":114}},"11":{"start":{"line":54,"column":4},"end":{"line":54,"column":70}},"12":{"start":{"line":65,"column":4},"end":{"line":65,"column":20}},"13":{"start":{"line":66,"column":4},"end":{"line":66,"column":46}},"14":{"start":{"line":67,"column":4},"end":{"line":67,"column":116}},"15":{"start":{"line":68,"column":4},"end":{"line":68,"column":71}},"16":{"start":{"line":76,"column":4},"end":{"line":76,"column":21}},"17":{"start":{"line":77,"column":4},"end":{"line":77,"column":17}},"18":{"start":{"line":84,"column":4},"end":{"line":84,"column":23}},"19":{"start":{"line":85,"column":4},"end":{"line":85,"column":19}},"20":{"start":{"line":86,"column":4},"end":{"line":86,"column":40}},"21":{"start":{"line":93,"column":2},"end":{"line":93,"column":9}},"22":{"start":{"line":103,"column":4},"end":{"line":103,"column":31}}},"branchMap":{"1":{"line":35,"type":"if","locations":[{"start":{"line":35,"column":4},"end":{"line":35,"column":4}},{"start":{"line":35,"column":4},"end":{"line":35,"column":4}}]},"2":{"line":36,"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":4}},{"start":{"line":36,"column":4},"end":{"line":36,"column":4}}]},"3":{"line":37,"type":"if","locations":[{"start":{"line":37,"column":4},"end":{"line":37,"column":4}},{"start":{"line":37,"column":4},"end":{"line":37,"column":4}}]},"4":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":4},"end":{"line":51,"column":4}},{"start":{"line":51,"column":4},"end":{"line":51,"column":4}}]},"5":{"line":52,"type":"if","locations":[{"start":{"line":52,"column":4},"end":{"line":52,"column":4}},{"start":{"line":52,"column":4},"end":{"line":52,"column":4}}]},"6":{"line":65,"type":"if","locations":[{"start":{"line":65,"column":4},"end":{"line":65,"column":4}},{"start":{"line":65,"column":4},"end":{"line":65,"column":4}}]},"7":{"line":66,"type":"if","locations":[{"start":{"line":66,"column":4},"end":{"line":66,"column":4}},{"start":{"line":66,"column":4},"end":{"line":66,"column":4}}]},"8":{"line":76,"type":"if","locations":[{"start":{"line":76,"column":4},"end":{"line":76,"column":4}},{"start":{"line":76,"column":4},"end":{"line":76,"column":4}}]},"9":{"line":103,"type":"if","locations":[{"start":{"line":103,"column":4},"end":{"line":103,"column":4}},{"start":{"line":103,"column":4},"end":{"line":103,"column":4}}]}}}} \ No newline at end of file +{"contracts/MyBitBurner.sol":{"l":{"28":5,"29":5,"30":5,"31":5,"32":5,"43":28,"44":26,"45":22,"46":14,"49":4,"50":4,"51":4,"53":1,"54":1,"56":3,"58":3,"61":1,"63":1,"64":1,"67":2,"69":2,"70":2,"72":16,"78":2,"79":2,"80":2,"81":2,"82":1,"83":1,"84":1,"85":1,"86":1,"88":1,"89":1,"90":1,"91":1,"92":1,"94":2,"103":8,"104":6,"105":6,"106":6,"115":4,"116":2,"117":2,"118":2,"130":18,"131":12},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/MyBitBurner.sol","s":{"1":5,"2":5,"3":5,"4":5,"5":5,"6":28,"7":26,"8":22,"9":14,"10":4,"11":4,"12":4,"13":1,"14":1,"15":3,"16":3,"17":1,"18":1,"19":2,"20":2,"21":2,"22":16,"23":2,"24":2,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":2,"32":8,"33":6,"34":6,"35":6,"36":4,"37":2,"38":2,"39":18},"b":{"1":[26,2],"2":[22,4],"3":[14,8],"4":[1,3],"5":[1,0],"6":[1,2],"7":[1,0],"8":[2,0],"9":[1,1],"10":[1,0],"11":[6,2],"12":[2,2],"13":[12,6]},"f":{"1":5,"2":28,"3":2,"4":8,"5":4,"6":1,"7":18},"fnMap":{"1":{"name":null,"line":26,"loc":{"start":{"line":26,"column":2},"end":{"line":27,"column":10}}},"2":{"name":"burn","line":39,"loc":{"start":{"line":39,"column":2},"end":{"line":42,"column":18}}},"3":{"name":"convert","line":75,"loc":{"start":{"line":75,"column":2},"end":{"line":77,"column":17}}},"4":{"name":"authorizeBurner","line":99,"loc":{"start":{"line":99,"column":2},"end":{"line":102,"column":18}}},"5":{"name":"removeBurner","line":111,"loc":{"start":{"line":111,"column":2},"end":{"line":114,"column":18}}},"6":{"name":null,"line":122,"loc":{"start":{"line":122,"column":2},"end":{"line":122,"column":29}}},"7":{"name":"onlyOwner","line":129,"loc":{"start":{"line":129,"column":2},"end":{"line":129,"column":20}}}},"statementMap":{"1":{"start":{"line":28,"column":4},"end":{"line":28,"column":47}},"2":{"start":{"line":29,"column":4},"end":{"line":29,"column":36}},"3":{"start":{"line":30,"column":4},"end":{"line":30,"column":21}},"4":{"start":{"line":31,"column":4},"end":{"line":31,"column":33}},"5":{"start":{"line":32,"column":4},"end":{"line":32,"column":21}},"6":{"start":{"line":43,"column":4},"end":{"line":43,"column":40}},"7":{"start":{"line":44,"column":4},"end":{"line":44,"column":2134}},"8":{"start":{"line":45,"column":6},"end":{"line":45,"column":54}},"9":{"start":{"line":46,"column":6},"end":{"line":46,"column":58}},"10":{"start":{"line":49,"column":6},"end":{"line":49,"column":96}},"11":{"start":{"line":50,"column":6},"end":{"line":50,"column":65}},"12":{"start":{"line":51,"column":6},"end":{"line":51,"column":2578}},"13":{"start":{"line":53,"column":8},"end":{"line":53,"column":62}},"14":{"start":{"line":54,"column":8},"end":{"line":54,"column":94}},"15":{"start":{"line":56,"column":8},"end":{"line":56,"column":42}},"16":{"start":{"line":58,"column":8},"end":{"line":58,"column":82}},"17":{"start":{"line":61,"column":8},"end":{"line":61,"column":52}},"18":{"start":{"line":64,"column":8},"end":{"line":64,"column":99}},"19":{"start":{"line":67,"column":6},"end":{"line":67,"column":43}},"20":{"start":{"line":69,"column":6},"end":{"line":69,"column":35}},"21":{"start":{"line":70,"column":6},"end":{"line":70,"column":57}},"22":{"start":{"line":72,"column":4},"end":{"line":72,"column":15}},"23":{"start":{"line":80,"column":4},"end":{"line":80,"column":81}},"24":{"start":{"line":81,"column":4},"end":{"line":81,"column":4019}},"25":{"start":{"line":82,"column":6},"end":{"line":82,"column":78}},"26":{"start":{"line":83,"column":6},"end":{"line":83,"column":42}},"27":{"start":{"line":85,"column":6},"end":{"line":85,"column":67}},"28":{"start":{"line":88,"column":6},"end":{"line":88,"column":31}},"29":{"start":{"line":89,"column":6},"end":{"line":89,"column":42}},"30":{"start":{"line":91,"column":6},"end":{"line":91,"column":67}},"31":{"start":{"line":94,"column":4},"end":{"line":94,"column":17}},"32":{"start":{"line":103,"column":4},"end":{"line":103,"column":47}},"33":{"start":{"line":104,"column":4},"end":{"line":104,"column":44}},"34":{"start":{"line":105,"column":4},"end":{"line":105,"column":58}},"35":{"start":{"line":106,"column":4},"end":{"line":106,"column":15}},"36":{"start":{"line":115,"column":4},"end":{"line":115,"column":46}},"37":{"start":{"line":117,"column":4},"end":{"line":117,"column":55}},"38":{"start":{"line":118,"column":4},"end":{"line":118,"column":15}},"39":{"start":{"line":130,"column":4},"end":{"line":130,"column":31}}},"branchMap":{"1":{"line":43,"type":"if","locations":[{"start":{"line":43,"column":4},"end":{"line":43,"column":4}},{"start":{"line":43,"column":4},"end":{"line":43,"column":4}}]},"2":{"line":44,"type":"if","locations":[{"start":{"line":44,"column":4},"end":{"line":44,"column":4}},{"start":{"line":44,"column":4},"end":{"line":44,"column":4}}]},"3":{"line":45,"type":"if","locations":[{"start":{"line":45,"column":6},"end":{"line":45,"column":6}},{"start":{"line":45,"column":6},"end":{"line":45,"column":6}}]},"4":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":6},"end":{"line":51,"column":6}},{"start":{"line":51,"column":6},"end":{"line":51,"column":6}}]},"5":{"line":53,"type":"if","locations":[{"start":{"line":53,"column":8},"end":{"line":53,"column":8}},{"start":{"line":53,"column":8},"end":{"line":53,"column":8}}]},"6":{"line":58,"type":"if","locations":[{"start":{"line":58,"column":8},"end":{"line":58,"column":8}},{"start":{"line":58,"column":8},"end":{"line":58,"column":8}}]},"7":{"line":61,"type":"if","locations":[{"start":{"line":61,"column":8},"end":{"line":61,"column":8}},{"start":{"line":61,"column":8},"end":{"line":61,"column":8}}]},"8":{"line":69,"type":"if","locations":[{"start":{"line":69,"column":6},"end":{"line":69,"column":6}},{"start":{"line":69,"column":6},"end":{"line":69,"column":6}}]},"9":{"line":81,"type":"if","locations":[{"start":{"line":81,"column":4},"end":{"line":81,"column":4}},{"start":{"line":81,"column":4},"end":{"line":81,"column":4}}]},"10":{"line":82,"type":"if","locations":[{"start":{"line":82,"column":6},"end":{"line":82,"column":6}},{"start":{"line":82,"column":6},"end":{"line":82,"column":6}}]},"11":{"line":103,"type":"if","locations":[{"start":{"line":103,"column":4},"end":{"line":103,"column":4}},{"start":{"line":103,"column":4},"end":{"line":103,"column":4}}]},"12":{"line":115,"type":"if","locations":[{"start":{"line":115,"column":4},"end":{"line":115,"column":4}},{"start":{"line":115,"column":4},"end":{"line":115,"column":4}}]},"13":{"line":130,"type":"if","locations":[{"start":{"line":130,"column":4},"end":{"line":130,"column":4}},{"start":{"line":130,"column":4},"end":{"line":130,"column":4}}]}}},"contracts/SafeMath.sol":{"l":{"14":6,"15":1,"17":5,"18":5,"19":5,"29":5,"36":369,"37":366,"44":334,"45":334,"46":332},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/SafeMath.sol","s":{"1":6,"2":1,"3":5,"4":5,"5":5,"6":5,"7":369,"8":366,"9":334,"10":334,"11":332},"b":{"1":[1,5],"2":[5,0],"3":[366,3],"4":[332,2]},"f":{"1":6,"2":5,"3":369,"4":334},"fnMap":{"1":{"name":"mul","line":13,"loc":{"start":{"line":13,"column":2},"end":{"line":13,"column":68}}},"2":{"name":"div","line":25,"loc":{"start":{"line":25,"column":2},"end":{"line":25,"column":68}}},"3":{"name":"sub","line":35,"loc":{"start":{"line":35,"column":2},"end":{"line":35,"column":68}}},"4":{"name":"add","line":43,"loc":{"start":{"line":43,"column":2},"end":{"line":43,"column":68}}}},"statementMap":{"1":{"start":{"line":14,"column":4},"end":{"line":14,"column":740}},"2":{"start":{"line":15,"column":6},"end":{"line":15,"column":14}},"3":{"start":{"line":17,"column":4},"end":{"line":17,"column":20}},"4":{"start":{"line":18,"column":4},"end":{"line":18,"column":21}},"5":{"start":{"line":19,"column":4},"end":{"line":19,"column":12}},"6":{"start":{"line":29,"column":4},"end":{"line":29,"column":16}},"7":{"start":{"line":36,"column":4},"end":{"line":36,"column":17}},"8":{"start":{"line":37,"column":4},"end":{"line":37,"column":16}},"9":{"start":{"line":44,"column":4},"end":{"line":44,"column":20}},"10":{"start":{"line":45,"column":4},"end":{"line":45,"column":17}},"11":{"start":{"line":46,"column":4},"end":{"line":46,"column":12}}},"branchMap":{"1":{"line":14,"type":"if","locations":[{"start":{"line":14,"column":4},"end":{"line":14,"column":4}},{"start":{"line":14,"column":4},"end":{"line":14,"column":4}}]},"2":{"line":18,"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":4}},{"start":{"line":18,"column":4},"end":{"line":18,"column":4}}]},"3":{"line":36,"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":4}},{"start":{"line":36,"column":4},"end":{"line":36,"column":4}}]},"4":{"line":45,"type":"if","locations":[{"start":{"line":45,"column":4},"end":{"line":45,"column":4}},{"start":{"line":45,"column":4},"end":{"line":45,"column":4}}]}}},"contracts/SafeMathWrapper.sol":{"l":{"9":2,"13":1,"17":2,"21":1},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/SafeMathWrapper.sol","s":{"1":2,"2":1,"3":2,"4":1},"b":{},"f":{"1":2,"2":1,"3":2,"4":1},"fnMap":{"1":{"name":"multiply","line":8,"loc":{"start":{"line":8,"column":2},"end":{"line":8,"column":73}}},"2":{"name":"divide","line":12,"loc":{"start":{"line":12,"column":2},"end":{"line":12,"column":71}}},"3":{"name":"subtract","line":16,"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":73}}},"4":{"name":"addto","line":20,"loc":{"start":{"line":20,"column":2},"end":{"line":20,"column":70}}}},"statementMap":{"1":{"start":{"line":9,"column":4},"end":{"line":9,"column":19}},"2":{"start":{"line":13,"column":4},"end":{"line":13,"column":19}},"3":{"start":{"line":17,"column":4},"end":{"line":17,"column":19}},"4":{"start":{"line":21,"column":4},"end":{"line":21,"column":19}}},"branchMap":{}},"contracts/token/BurnableERC20.sol":{"l":{},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/token/BurnableERC20.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"contracts/token/ERC20.sol":{"l":{},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/token/ERC20.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"contracts/token/ERC721.sol":{"l":{},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/token/ERC721.sol","s":{},"b":{},"f":{},"fnMap":{},"statementMap":{},"branchMap":{}},"contracts/token/SampleERC20.sol":{"l":{"43":29,"44":29,"45":29,"46":29,"47":29,"48":29,"59":287,"60":287,"61":285,"62":285,"63":285,"64":285,"74":12,"75":12,"76":10,"77":10,"78":8,"79":8,"80":8,"90":70,"91":70,"92":70,"103":0,"104":0,"105":0,"106":0,"116":3,"117":3,"118":3,"119":3,"120":3,"130":25,"131":15,"132":15,"133":15,"134":15,"135":15,"136":15,"146":5,"153":163,"163":204,"173":18,"184":1},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/token/SampleERC20.sol","s":{"1":29,"2":29,"3":29,"4":29,"5":29,"6":29,"7":287,"8":287,"9":285,"10":285,"11":285,"12":285,"13":12,"14":12,"15":10,"16":10,"17":8,"18":8,"19":8,"20":70,"21":70,"22":70,"23":0,"24":0,"25":0,"26":0,"27":3,"28":3,"29":3,"30":3,"31":3,"32":25,"33":15,"34":15,"35":15,"36":15,"37":15,"38":15,"39":5,"40":163,"41":204,"42":18,"43":1},"b":{"1":[287,0],"2":[285,2],"3":[12,0],"4":[10,2],"5":[15,10]},"f":{"1":29,"2":287,"3":12,"4":70,"5":0,"6":3,"7":25,"8":5,"9":163,"10":204,"11":18,"12":1},"fnMap":{"1":{"name":null,"line":41,"loc":{"start":{"line":41,"column":4},"end":{"line":42,"column":12}}},"2":{"name":"transfer","line":56,"loc":{"start":{"line":56,"column":4},"end":{"line":58,"column":28}}},"3":{"name":"transferFrom","line":71,"loc":{"start":{"line":71,"column":4},"end":{"line":73,"column":28}}},"4":{"name":"approve","line":87,"loc":{"start":{"line":87,"column":4},"end":{"line":89,"column":28}}},"5":{"name":"approveAndCall","line":100,"loc":{"start":{"line":100,"column":4},"end":{"line":102,"column":28}}},"6":{"name":"burn","line":113,"loc":{"start":{"line":113,"column":4},"end":{"line":115,"column":28}}},"7":{"name":"burnFrom","line":127,"loc":{"start":{"line":127,"column":4},"end":{"line":129,"column":28}}},"8":{"name":"totalSupply","line":142,"loc":{"start":{"line":142,"column":4},"end":{"line":145,"column":32}}},"9":{"name":"decimals","line":152,"loc":{"start":{"line":152,"column":4},"end":{"line":152,"column":49}}},"10":{"name":"balanceOf","line":159,"loc":{"start":{"line":159,"column":4},"end":{"line":162,"column":28}}},"11":{"name":"allowance","line":169,"loc":{"start":{"line":169,"column":4},"end":{"line":172,"column":30}}},"12":{"name":null,"line":181,"loc":{"start":{"line":181,"column":4},"end":{"line":183,"column":13}}}},"statementMap":{"1":{"start":{"line":43,"column":8},"end":{"line":43,"column":44}},"2":{"start":{"line":44,"column":8},"end":{"line":44,"column":30}},"3":{"start":{"line":45,"column":8},"end":{"line":45,"column":24}},"4":{"start":{"line":46,"column":8},"end":{"line":46,"column":31}},"5":{"start":{"line":47,"column":8},"end":{"line":47,"column":28}},"6":{"start":{"line":48,"column":8},"end":{"line":48,"column":61}},"7":{"start":{"line":59,"column":8},"end":{"line":59,"column":33}},"8":{"start":{"line":60,"column":8},"end":{"line":60,"column":36}},"9":{"start":{"line":61,"column":8},"end":{"line":61,"column":63}},"10":{"start":{"line":62,"column":8},"end":{"line":62,"column":49}},"11":{"start":{"line":63,"column":8},"end":{"line":63,"column":47}},"12":{"start":{"line":64,"column":8},"end":{"line":64,"column":19}},"13":{"start":{"line":74,"column":8},"end":{"line":74,"column":33}},"14":{"start":{"line":75,"column":8},"end":{"line":75,"column":36}},"15":{"start":{"line":76,"column":8},"end":{"line":76,"column":53}},"16":{"start":{"line":77,"column":8},"end":{"line":77,"column":75}},"17":{"start":{"line":78,"column":8},"end":{"line":78,"column":49}},"18":{"start":{"line":79,"column":8},"end":{"line":79,"column":42}},"19":{"start":{"line":80,"column":8},"end":{"line":80,"column":19}},"20":{"start":{"line":90,"column":8},"end":{"line":90,"column":46}},"21":{"start":{"line":91,"column":8},"end":{"line":91,"column":52}},"22":{"start":{"line":92,"column":8},"end":{"line":92,"column":19}},"23":{"start":{"line":103,"column":8},"end":{"line":103,"column":46}},"24":{"start":{"line":104,"column":8},"end":{"line":104,"column":52}},"25":{"start":{"line":105,"column":8},"end":{"line":105,"column":39}},"26":{"start":{"line":106,"column":8},"end":{"line":106,"column":19}},"27":{"start":{"line":116,"column":8},"end":{"line":116,"column":63}},"28":{"start":{"line":117,"column":8},"end":{"line":117,"column":35}},"29":{"start":{"line":118,"column":8},"end":{"line":118,"column":41}},"30":{"start":{"line":119,"column":8},"end":{"line":119,"column":54}},"31":{"start":{"line":120,"column":8},"end":{"line":120,"column":19}},"32":{"start":{"line":130,"column":8},"end":{"line":130,"column":53}},"33":{"start":{"line":131,"column":8},"end":{"line":131,"column":53}},"34":{"start":{"line":132,"column":8},"end":{"line":132,"column":75}},"35":{"start":{"line":133,"column":8},"end":{"line":133,"column":35}},"36":{"start":{"line":134,"column":8},"end":{"line":134,"column":36}},"37":{"start":{"line":135,"column":8},"end":{"line":135,"column":49}},"38":{"start":{"line":136,"column":8},"end":{"line":136,"column":19}},"39":{"start":{"line":146,"column":8},"end":{"line":146,"column":21}},"40":{"start":{"line":153,"column":6},"end":{"line":153,"column":21}},"41":{"start":{"line":163,"column":8},"end":{"line":163,"column":37}},"42":{"start":{"line":173,"column":8},"end":{"line":173,"column":46}},"43":{"start":{"line":184,"column":8},"end":{"line":184,"column":15}}},"branchMap":{"1":{"line":59,"type":"if","locations":[{"start":{"line":59,"column":8},"end":{"line":59,"column":8}},{"start":{"line":59,"column":8},"end":{"line":59,"column":8}}]},"2":{"line":60,"type":"if","locations":[{"start":{"line":60,"column":8},"end":{"line":60,"column":8}},{"start":{"line":60,"column":8},"end":{"line":60,"column":8}}]},"3":{"line":74,"type":"if","locations":[{"start":{"line":74,"column":8},"end":{"line":74,"column":8}},{"start":{"line":74,"column":8},"end":{"line":74,"column":8}}]},"4":{"line":75,"type":"if","locations":[{"start":{"line":75,"column":8},"end":{"line":75,"column":8}},{"start":{"line":75,"column":8},"end":{"line":75,"column":8}}]},"5":{"line":130,"type":"if","locations":[{"start":{"line":130,"column":8},"end":{"line":130,"column":8}},{"start":{"line":130,"column":8},"end":{"line":130,"column":8}}]}}},"contracts/token/SampleERC721.sol":{"l":{"30":3,"43":9,"44":9,"46":7,"47":7,"49":7,"50":7,"59":1,"60":1,"69":33,"70":33,"71":29,"83":7,"84":7,"85":5,"87":3,"88":3,"98":4,"99":3,"109":2,"110":2,"111":2,"121":3,"133":7,"135":5,"179":13,"180":13,"191":7,"195":5,"206":5,"207":5,"209":5,"211":5,"212":5,"214":5,"216":5,"224":5,"225":3,"231":11,"232":9},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/token/SampleERC721.sol","s":{"1":3,"2":9,"3":9,"4":7,"5":7,"6":7,"7":7,"8":1,"9":1,"10":33,"11":33,"12":29,"13":7,"14":7,"15":5,"16":3,"17":3,"18":4,"19":3,"20":2,"21":2,"22":2,"23":3,"24":7,"25":5,"26":13,"27":13,"28":7,"29":5,"30":5,"31":5,"32":5,"33":5,"34":5,"35":5,"36":5,"37":5,"38":3,"39":11},"b":{"1":[9,0],"2":[7,2],"3":[1,0],"4":[29,4],"5":[5,2],"6":[3,2],"7":[3,1],"8":[2,0],"9":[5,2],"10":[5,0],"11":[5,0],"12":[3,2],"13":[9,2]},"f":{"1":3,"2":9,"3":1,"4":33,"5":7,"6":4,"7":2,"8":3,"9":7,"10":13,"11":7,"12":5,"13":5,"14":11},"fnMap":{"1":{"name":null,"line":28,"loc":{"start":{"line":28,"column":4},"end":{"line":29,"column":12}}},"2":{"name":"mint","line":39,"loc":{"start":{"line":39,"column":4},"end":{"line":42,"column":20}}},"3":{"name":"balanceOf","line":58,"loc":{"start":{"line":58,"column":4},"end":{"line":58,"column":65}}},"4":{"name":"ownerOf","line":68,"loc":{"start":{"line":68,"column":4},"end":{"line":68,"column":65}}},"5":{"name":"approve","line":82,"loc":{"start":{"line":82,"column":4},"end":{"line":82,"column":54}}},"6":{"name":"getApproved","line":97,"loc":{"start":{"line":97,"column":4},"end":{"line":97,"column":69}}},"7":{"name":"setApprovalForAll","line":108,"loc":{"start":{"line":108,"column":4},"end":{"line":108,"column":62}}},"8":{"name":"isApprovedForAll","line":120,"loc":{"start":{"line":120,"column":4},"end":{"line":120,"column":87}}},"9":{"name":"transferFrom","line":132,"loc":{"start":{"line":132,"column":4},"end":{"line":132,"column":73}}},"10":{"name":"_exists","line":178,"loc":{"start":{"line":178,"column":4},"end":{"line":178,"column":64}}},"11":{"name":"_isApprovedOrOwner","line":190,"loc":{"start":{"line":190,"column":4},"end":{"line":190,"column":92}}},"12":{"name":"_transferFrom","line":205,"loc":{"start":{"line":205,"column":4},"end":{"line":205,"column":76}}},"13":{"name":"_clearApproval","line":223,"loc":{"start":{"line":223,"column":4},"end":{"line":223,"column":50}}},"14":{"name":"onlyOwner","line":230,"loc":{"start":{"line":230,"column":4},"end":{"line":230,"column":20}}}},"statementMap":{"1":{"start":{"line":30,"column":8},"end":{"line":30,"column":26}},"2":{"start":{"line":43,"column":8},"end":{"line":43,"column":32}},"3":{"start":{"line":44,"column":8},"end":{"line":44,"column":33}},"4":{"start":{"line":46,"column":8},"end":{"line":46,"column":32}},"5":{"start":{"line":47,"column":8},"end":{"line":47,"column":59}},"6":{"start":{"line":49,"column":8},"end":{"line":49,"column":46}},"7":{"start":{"line":50,"column":8},"end":{"line":50,"column":19}},"8":{"start":{"line":59,"column":8},"end":{"line":59,"column":35}},"9":{"start":{"line":60,"column":8},"end":{"line":60,"column":39}},"10":{"start":{"line":69,"column":8},"end":{"line":69,"column":43}},"11":{"start":{"line":70,"column":8},"end":{"line":70,"column":35}},"12":{"start":{"line":71,"column":8},"end":{"line":71,"column":20}},"13":{"start":{"line":83,"column":8},"end":{"line":83,"column":39}},"14":{"start":{"line":84,"column":8},"end":{"line":84,"column":27}},"15":{"start":{"line":85,"column":8},"end":{"line":85,"column":74}},"16":{"start":{"line":87,"column":8},"end":{"line":87,"column":36}},"17":{"start":{"line":88,"column":8},"end":{"line":88,"column":41}},"18":{"start":{"line":98,"column":8},"end":{"line":98,"column":32}},"19":{"start":{"line":99,"column":8},"end":{"line":99,"column":39}},"20":{"start":{"line":109,"column":8},"end":{"line":109,"column":32}},"21":{"start":{"line":110,"column":8},"end":{"line":110,"column":52}},"22":{"start":{"line":111,"column":8},"end":{"line":111,"column":53}},"23":{"start":{"line":121,"column":8},"end":{"line":121,"column":50}},"24":{"start":{"line":133,"column":8},"end":{"line":133,"column":55}},"25":{"start":{"line":135,"column":8},"end":{"line":135,"column":39}},"26":{"start":{"line":179,"column":8},"end":{"line":179,"column":43}},"27":{"start":{"line":180,"column":8},"end":{"line":180,"column":34}},"28":{"start":{"line":191,"column":8},"end":{"line":191,"column":39}},"29":{"start":{"line":195,"column":8},"end":{"line":195,"column":104}},"30":{"start":{"line":206,"column":8},"end":{"line":206,"column":40}},"31":{"start":{"line":207,"column":8},"end":{"line":207,"column":32}},"32":{"start":{"line":209,"column":8},"end":{"line":209,"column":30}},"33":{"start":{"line":211,"column":8},"end":{"line":211,"column":63}},"34":{"start":{"line":212,"column":8},"end":{"line":212,"column":59}},"35":{"start":{"line":214,"column":8},"end":{"line":214,"column":32}},"36":{"start":{"line":216,"column":8},"end":{"line":216,"column":40}},"37":{"start":{"line":224,"column":8},"end":{"line":224,"column":8739}},"38":{"start":{"line":225,"column":12},"end":{"line":225,"column":48}},"39":{"start":{"line":231,"column":8},"end":{"line":231,"column":36}}},"branchMap":{"1":{"line":43,"type":"if","locations":[{"start":{"line":43,"column":8},"end":{"line":43,"column":8}},{"start":{"line":43,"column":8},"end":{"line":43,"column":8}}]},"2":{"line":44,"type":"if","locations":[{"start":{"line":44,"column":8},"end":{"line":44,"column":8}},{"start":{"line":44,"column":8},"end":{"line":44,"column":8}}]},"3":{"line":59,"type":"if","locations":[{"start":{"line":59,"column":8},"end":{"line":59,"column":8}},{"start":{"line":59,"column":8},"end":{"line":59,"column":8}}]},"4":{"line":70,"type":"if","locations":[{"start":{"line":70,"column":8},"end":{"line":70,"column":8}},{"start":{"line":70,"column":8},"end":{"line":70,"column":8}}]},"5":{"line":84,"type":"if","locations":[{"start":{"line":84,"column":8},"end":{"line":84,"column":8}},{"start":{"line":84,"column":8},"end":{"line":84,"column":8}}]},"6":{"line":85,"type":"if","locations":[{"start":{"line":85,"column":8},"end":{"line":85,"column":8}},{"start":{"line":85,"column":8},"end":{"line":85,"column":8}}]},"7":{"line":98,"type":"if","locations":[{"start":{"line":98,"column":8},"end":{"line":98,"column":8}},{"start":{"line":98,"column":8},"end":{"line":98,"column":8}}]},"8":{"line":109,"type":"if","locations":[{"start":{"line":109,"column":8},"end":{"line":109,"column":8}},{"start":{"line":109,"column":8},"end":{"line":109,"column":8}}]},"9":{"line":133,"type":"if","locations":[{"start":{"line":133,"column":8},"end":{"line":133,"column":8}},{"start":{"line":133,"column":8},"end":{"line":133,"column":8}}]},"10":{"line":206,"type":"if","locations":[{"start":{"line":206,"column":8},"end":{"line":206,"column":8}},{"start":{"line":206,"column":8},"end":{"line":206,"column":8}}]},"11":{"line":207,"type":"if","locations":[{"start":{"line":207,"column":8},"end":{"line":207,"column":8}},{"start":{"line":207,"column":8},"end":{"line":207,"column":8}}]},"12":{"line":224,"type":"if","locations":[{"start":{"line":224,"column":8},"end":{"line":224,"column":8}},{"start":{"line":224,"column":8},"end":{"line":224,"column":8}}]},"13":{"line":231,"type":"if","locations":[{"start":{"line":231,"column":8},"end":{"line":231,"column":8}},{"start":{"line":231,"column":8},"end":{"line":231,"column":8}}]}}},"contracts/Trust.sol":{"l":{"33":6,"34":6,"35":6,"36":6,"46":10,"47":6,"48":6,"49":6,"58":1,"59":1,"60":1,"69":3,"70":1,"71":1,"72":1,"73":1,"74":1,"85":3,"86":3,"87":1,"88":1,"99":1,"100":1,"101":1,"102":1,"109":1,"120":2,"121":1,"130":36,"131":32,"136":12,"137":10,"142":7,"143":5},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/Trust.sol","s":{"1":6,"2":6,"3":6,"4":6,"5":10,"6":6,"7":6,"8":6,"9":1,"10":1,"11":3,"12":1,"13":1,"14":1,"15":3,"16":3,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":2,"25":1,"26":1,"27":36,"28":12,"29":7},"b":{"1":[6,4],"2":[1,2],"3":[1,0],"4":[1,1],"5":[32,4],"6":[10,2],"7":[5,2]},"f":{"1":6,"2":10,"3":1,"4":3,"5":3,"6":1,"7":1,"8":2,"9":36,"10":12,"11":7},"fnMap":{"1":{"name":null,"line":31,"loc":{"start":{"line":31,"column":1},"end":{"line":32,"column":9}}},"2":{"name":"depositTrust","line":41,"loc":{"start":{"line":41,"column":1},"end":{"line":45,"column":10}}},"3":{"name":"revoke","line":53,"loc":{"start":{"line":53,"column":1},"end":{"line":57,"column":14}}},"4":{"name":"withdraw","line":64,"loc":{"start":{"line":64,"column":1},"end":{"line":68,"column":17}}},"5":{"name":"changeExpiration","line":79,"loc":{"start":{"line":79,"column":1},"end":{"line":84,"column":16}}},"6":{"name":"changeBeneficiary","line":93,"loc":{"start":{"line":93,"column":1},"end":{"line":98,"column":16}}},"7":{"name":null,"line":106,"loc":{"start":{"line":106,"column":1},"end":{"line":108,"column":10}}},"8":{"name":"secUntilExpiration","line":117,"loc":{"start":{"line":117,"column":1},"end":{"line":120,"column":17}}},"9":{"name":"lessThan","line":130,"loc":{"start":{"line":130,"column":1},"end":{"line":130,"column":37}}},"10":{"name":"onlySender","line":136,"loc":{"start":{"line":136,"column":1},"end":{"line":136,"column":46}}},"11":{"name":"isRevocable","line":142,"loc":{"start":{"line":142,"column":1},"end":{"line":142,"column":22}}}},"statementMap":{"1":{"start":{"line":33,"column":2},"end":{"line":33,"column":19}},"2":{"start":{"line":34,"column":2},"end":{"line":34,"column":27}},"3":{"start":{"line":35,"column":2},"end":{"line":35,"column":23}},"4":{"start":{"line":36,"column":2},"end":{"line":36,"column":46}},"5":{"start":{"line":46,"column":2},"end":{"line":46,"column":27}},"6":{"start":{"line":47,"column":2},"end":{"line":47,"column":24}},"7":{"start":{"line":48,"column":3},"end":{"line":48,"column":44}},"8":{"start":{"line":49,"column":3},"end":{"line":49,"column":41}},"9":{"start":{"line":58,"column":2},"end":{"line":58,"column":48}},"10":{"start":{"line":60,"column":2},"end":{"line":60,"column":25}},"11":{"start":{"line":69,"column":2},"end":{"line":69,"column":26}},"12":{"start":{"line":70,"column":2},"end":{"line":70,"column":27}},"13":{"start":{"line":73,"column":2},"end":{"line":73,"column":39}},"14":{"start":{"line":74,"column":2},"end":{"line":74,"column":13}},"15":{"start":{"line":85,"column":2},"end":{"line":85,"column":32}},"16":{"start":{"line":86,"column":2},"end":{"line":86,"column":45}},"17":{"start":{"line":87,"column":2},"end":{"line":87,"column":54}},"18":{"start":{"line":88,"column":2},"end":{"line":88,"column":13}},"19":{"start":{"line":99,"column":2},"end":{"line":99,"column":36}},"20":{"start":{"line":100,"column":2},"end":{"line":100,"column":51}},"21":{"start":{"line":101,"column":2},"end":{"line":101,"column":27}},"22":{"start":{"line":102,"column":2},"end":{"line":102,"column":13}},"23":{"start":{"line":109,"column":2},"end":{"line":109,"column":9}},"24":{"start":{"line":121,"column":2},"end":{"line":121,"column":48}},"25":{"start":{"line":121,"column":38},"end":{"line":121,"column":46}},"26":{"start":{"line":122,"column":2},"end":{"line":122,"column":40}},"27":{"start":{"line":131,"column":2},"end":{"line":131,"column":17}},"28":{"start":{"line":137,"column":2},"end":{"line":137,"column":39}},"29":{"start":{"line":143,"column":2},"end":{"line":143,"column":19}}},"branchMap":{"1":{"line":46,"type":"if","locations":[{"start":{"line":46,"column":2},"end":{"line":46,"column":2}},{"start":{"line":46,"column":2},"end":{"line":46,"column":2}}]},"2":{"line":69,"type":"if","locations":[{"start":{"line":69,"column":2},"end":{"line":69,"column":2}},{"start":{"line":69,"column":2},"end":{"line":69,"column":2}}]},"3":{"line":99,"type":"if","locations":[{"start":{"line":99,"column":2},"end":{"line":99,"column":2}},{"start":{"line":99,"column":2},"end":{"line":99,"column":2}}]},"4":{"line":121,"type":"if","locations":[{"start":{"line":121,"column":2},"end":{"line":121,"column":2}},{"start":{"line":121,"column":2},"end":{"line":121,"column":2}}]},"5":{"line":131,"type":"if","locations":[{"start":{"line":131,"column":2},"end":{"line":131,"column":2}},{"start":{"line":131,"column":2},"end":{"line":131,"column":2}}]},"6":{"line":137,"type":"if","locations":[{"start":{"line":137,"column":2},"end":{"line":137,"column":2}},{"start":{"line":137,"column":2},"end":{"line":137,"column":2}}]},"7":{"line":143,"type":"if","locations":[{"start":{"line":143,"column":2},"end":{"line":143,"column":2}},{"start":{"line":143,"column":2},"end":{"line":143,"column":2}}]}}},"contracts/TrustERC20.sol":{"l":{"35":5,"36":5,"37":5,"38":5,"39":5,"50":8,"51":6,"52":2,"53":2,"54":2,"55":2,"64":1,"65":1,"66":1,"67":1,"76":3,"77":1,"78":1,"79":1,"80":1,"81":1,"92":1,"93":1,"94":1,"95":1,"106":1,"107":1,"108":1,"109":1,"116":1,"127":2,"128":0,"137":30,"138":28,"143":12,"144":8,"149":5,"150":3},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/TrustERC20.sol","s":{"1":5,"2":5,"3":5,"4":5,"5":5,"6":8,"7":6,"8":2,"9":2,"10":2,"11":1,"12":1,"13":3,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":2,"27":2,"28":0,"29":30,"30":12,"31":5},"b":{"1":[6,2],"2":[2,4],"3":[1,2],"4":[1,0],"5":[2,0],"6":[28,2],"7":[8,4],"8":[3,2]},"f":{"1":5,"2":8,"3":1,"4":3,"5":1,"6":1,"7":1,"8":2,"9":30,"10":12,"11":5},"fnMap":{"1":{"name":null,"line":33,"loc":{"start":{"line":33,"column":1},"end":{"line":34,"column":9}}},"2":{"name":"depositTrust","line":45,"loc":{"start":{"line":45,"column":1},"end":{"line":49,"column":10}}},"3":{"name":"revoke","line":59,"loc":{"start":{"line":59,"column":1},"end":{"line":63,"column":14}}},"4":{"name":"withdraw","line":71,"loc":{"start":{"line":71,"column":1},"end":{"line":75,"column":17}}},"5":{"name":"changeExpiration","line":86,"loc":{"start":{"line":86,"column":1},"end":{"line":91,"column":16}}},"6":{"name":"changeBeneficiary","line":100,"loc":{"start":{"line":100,"column":1},"end":{"line":105,"column":16}}},"7":{"name":null,"line":113,"loc":{"start":{"line":113,"column":1},"end":{"line":115,"column":10}}},"8":{"name":"secUntilExpiration","line":124,"loc":{"start":{"line":124,"column":1},"end":{"line":127,"column":17}}},"9":{"name":"lessThan","line":137,"loc":{"start":{"line":137,"column":1},"end":{"line":137,"column":37}}},"10":{"name":"onlySender","line":143,"loc":{"start":{"line":143,"column":1},"end":{"line":143,"column":46}}},"11":{"name":"isRevocable","line":149,"loc":{"start":{"line":149,"column":1},"end":{"line":149,"column":22}}}},"statementMap":{"1":{"start":{"line":35,"column":2},"end":{"line":35,"column":19}},"2":{"start":{"line":36,"column":2},"end":{"line":36,"column":27}},"3":{"start":{"line":37,"column":2},"end":{"line":37,"column":23}},"4":{"start":{"line":38,"column":2},"end":{"line":38,"column":46}},"5":{"start":{"line":39,"column":2},"end":{"line":39,"column":37}},"6":{"start":{"line":50,"column":2},"end":{"line":50,"column":27}},"7":{"start":{"line":51,"column":2},"end":{"line":51,"column":48}},"8":{"start":{"line":52,"column":2},"end":{"line":52,"column":24}},"9":{"start":{"line":53,"column":2},"end":{"line":53,"column":41}},"10":{"start":{"line":55,"column":2},"end":{"line":55,"column":38}},"11":{"start":{"line":65,"column":2},"end":{"line":65,"column":48}},"12":{"start":{"line":67,"column":2},"end":{"line":67,"column":25}},"13":{"start":{"line":76,"column":2},"end":{"line":76,"column":26}},"14":{"start":{"line":77,"column":2},"end":{"line":77,"column":27}},"15":{"start":{"line":80,"column":2},"end":{"line":80,"column":39}},"16":{"start":{"line":81,"column":2},"end":{"line":81,"column":13}},"17":{"start":{"line":92,"column":2},"end":{"line":92,"column":32}},"18":{"start":{"line":93,"column":2},"end":{"line":93,"column":43}},"19":{"start":{"line":94,"column":2},"end":{"line":94,"column":54}},"20":{"start":{"line":95,"column":2},"end":{"line":95,"column":13}},"21":{"start":{"line":106,"column":2},"end":{"line":106,"column":36}},"22":{"start":{"line":107,"column":2},"end":{"line":107,"column":51}},"23":{"start":{"line":108,"column":2},"end":{"line":108,"column":27}},"24":{"start":{"line":109,"column":2},"end":{"line":109,"column":13}},"25":{"start":{"line":116,"column":2},"end":{"line":116,"column":9}},"26":{"start":{"line":128,"column":2},"end":{"line":128,"column":48}},"27":{"start":{"line":128,"column":38},"end":{"line":128,"column":46}},"28":{"start":{"line":129,"column":2},"end":{"line":129,"column":40}},"29":{"start":{"line":138,"column":2},"end":{"line":138,"column":17}},"30":{"start":{"line":144,"column":2},"end":{"line":144,"column":39}},"31":{"start":{"line":150,"column":2},"end":{"line":150,"column":19}}},"branchMap":{"1":{"line":50,"type":"if","locations":[{"start":{"line":50,"column":2},"end":{"line":50,"column":2}},{"start":{"line":50,"column":2},"end":{"line":50,"column":2}}]},"2":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":2},"end":{"line":51,"column":2}},{"start":{"line":51,"column":2},"end":{"line":51,"column":2}}]},"3":{"line":76,"type":"if","locations":[{"start":{"line":76,"column":2},"end":{"line":76,"column":2}},{"start":{"line":76,"column":2},"end":{"line":76,"column":2}}]},"4":{"line":106,"type":"if","locations":[{"start":{"line":106,"column":2},"end":{"line":106,"column":2}},{"start":{"line":106,"column":2},"end":{"line":106,"column":2}}]},"5":{"line":128,"type":"if","locations":[{"start":{"line":128,"column":2},"end":{"line":128,"column":2}},{"start":{"line":128,"column":2},"end":{"line":128,"column":2}}]},"6":{"line":138,"type":"if","locations":[{"start":{"line":138,"column":2},"end":{"line":138,"column":2}},{"start":{"line":138,"column":2},"end":{"line":138,"column":2}}]},"7":{"line":144,"type":"if","locations":[{"start":{"line":144,"column":2},"end":{"line":144,"column":2}},{"start":{"line":144,"column":2},"end":{"line":144,"column":2}}]},"8":{"line":150,"type":"if","locations":[{"start":{"line":150,"column":2},"end":{"line":150,"column":2}},{"start":{"line":150,"column":2},"end":{"line":150,"column":2}}]}}},"contracts/TrustERC721.sol":{"l":{"37":4,"38":4,"39":4,"40":4,"41":4,"51":6,"52":4,"53":2,"54":2,"55":2,"56":2,"65":1,"66":1,"67":1,"68":1,"69":1,"78":3,"79":1,"80":1,"81":1,"82":1,"83":1,"84":1,"95":1,"96":1,"97":1,"98":1,"109":1,"110":1,"111":1,"112":1,"119":1,"130":2,"131":1,"140":20,"141":16,"146":10,"147":8,"152":5,"153":3},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/TrustERC721.sol","s":{"1":4,"2":4,"3":4,"4":4,"5":4,"6":6,"7":4,"8":2,"9":2,"10":2,"11":1,"12":1,"13":1,"14":3,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":2,"29":1,"30":1,"31":20,"32":10,"33":5},"b":{"1":[4,2],"2":[2,2],"3":[1,2],"4":[1,0],"5":[1,1],"6":[16,4],"7":[8,2],"8":[3,2]},"f":{"1":4,"2":6,"3":1,"4":3,"5":1,"6":1,"7":1,"8":2,"9":20,"10":10,"11":5},"fnMap":{"1":{"name":null,"line":35,"loc":{"start":{"line":35,"column":1},"end":{"line":36,"column":9}}},"2":{"name":"depositTrust","line":47,"loc":{"start":{"line":47,"column":1},"end":{"line":50,"column":10}}},"3":{"name":"revoke","line":60,"loc":{"start":{"line":60,"column":1},"end":{"line":64,"column":14}}},"4":{"name":"withdraw","line":73,"loc":{"start":{"line":73,"column":1},"end":{"line":77,"column":17}}},"5":{"name":"changeExpiration","line":89,"loc":{"start":{"line":89,"column":1},"end":{"line":94,"column":16}}},"6":{"name":"changeBeneficiary","line":103,"loc":{"start":{"line":103,"column":1},"end":{"line":108,"column":16}}},"7":{"name":null,"line":116,"loc":{"start":{"line":116,"column":1},"end":{"line":118,"column":10}}},"8":{"name":"secUntilExpiration","line":127,"loc":{"start":{"line":127,"column":1},"end":{"line":130,"column":17}}},"9":{"name":"lessThan","line":140,"loc":{"start":{"line":140,"column":1},"end":{"line":140,"column":37}}},"10":{"name":"onlySender","line":146,"loc":{"start":{"line":146,"column":1},"end":{"line":146,"column":46}}},"11":{"name":"isRevocable","line":152,"loc":{"start":{"line":152,"column":1},"end":{"line":152,"column":22}}}},"statementMap":{"1":{"start":{"line":37,"column":2},"end":{"line":37,"column":19}},"2":{"start":{"line":38,"column":2},"end":{"line":38,"column":27}},"3":{"start":{"line":39,"column":2},"end":{"line":39,"column":23}},"4":{"start":{"line":40,"column":2},"end":{"line":40,"column":46}},"5":{"start":{"line":41,"column":2},"end":{"line":41,"column":38}},"6":{"start":{"line":51,"column":2},"end":{"line":51,"column":27}},"7":{"start":{"line":52,"column":2},"end":{"line":52,"column":47}},"8":{"start":{"line":53,"column":2},"end":{"line":53,"column":24}},"9":{"start":{"line":54,"column":2},"end":{"line":54,"column":24}},"10":{"start":{"line":56,"column":2},"end":{"line":56,"column":39}},"11":{"start":{"line":66,"column":2},"end":{"line":66,"column":48}},"12":{"start":{"line":68,"column":2},"end":{"line":68,"column":24}},"13":{"start":{"line":69,"column":2},"end":{"line":69,"column":25}},"14":{"start":{"line":78,"column":2},"end":{"line":78,"column":27}},"15":{"start":{"line":79,"column":2},"end":{"line":79,"column":28}},"16":{"start":{"line":81,"column":2},"end":{"line":81,"column":24}},"17":{"start":{"line":83,"column":2},"end":{"line":83,"column":40}},"18":{"start":{"line":84,"column":2},"end":{"line":84,"column":13}},"19":{"start":{"line":95,"column":2},"end":{"line":95,"column":32}},"20":{"start":{"line":96,"column":2},"end":{"line":96,"column":43}},"21":{"start":{"line":97,"column":2},"end":{"line":97,"column":54}},"22":{"start":{"line":98,"column":2},"end":{"line":98,"column":13}},"23":{"start":{"line":109,"column":2},"end":{"line":109,"column":36}},"24":{"start":{"line":110,"column":2},"end":{"line":110,"column":51}},"25":{"start":{"line":111,"column":2},"end":{"line":111,"column":27}},"26":{"start":{"line":112,"column":2},"end":{"line":112,"column":13}},"27":{"start":{"line":119,"column":2},"end":{"line":119,"column":9}},"28":{"start":{"line":131,"column":2},"end":{"line":131,"column":48}},"29":{"start":{"line":131,"column":38},"end":{"line":131,"column":46}},"30":{"start":{"line":132,"column":2},"end":{"line":132,"column":40}},"31":{"start":{"line":141,"column":2},"end":{"line":141,"column":17}},"32":{"start":{"line":147,"column":2},"end":{"line":147,"column":39}},"33":{"start":{"line":153,"column":2},"end":{"line":153,"column":19}}},"branchMap":{"1":{"line":51,"type":"if","locations":[{"start":{"line":51,"column":2},"end":{"line":51,"column":2}},{"start":{"line":51,"column":2},"end":{"line":51,"column":2}}]},"2":{"line":52,"type":"if","locations":[{"start":{"line":52,"column":2},"end":{"line":52,"column":2}},{"start":{"line":52,"column":2},"end":{"line":52,"column":2}}]},"3":{"line":78,"type":"if","locations":[{"start":{"line":78,"column":2},"end":{"line":78,"column":2}},{"start":{"line":78,"column":2},"end":{"line":78,"column":2}}]},"4":{"line":109,"type":"if","locations":[{"start":{"line":109,"column":2},"end":{"line":109,"column":2}},{"start":{"line":109,"column":2},"end":{"line":109,"column":2}}]},"5":{"line":131,"type":"if","locations":[{"start":{"line":131,"column":2},"end":{"line":131,"column":2}},{"start":{"line":131,"column":2},"end":{"line":131,"column":2}}]},"6":{"line":141,"type":"if","locations":[{"start":{"line":141,"column":2},"end":{"line":141,"column":2}},{"start":{"line":141,"column":2},"end":{"line":141,"column":2}}]},"7":{"line":147,"type":"if","locations":[{"start":{"line":147,"column":2},"end":{"line":147,"column":2}},{"start":{"line":147,"column":2},"end":{"line":147,"column":2}}]},"8":{"line":153,"type":"if","locations":[{"start":{"line":153,"column":2},"end":{"line":153,"column":2}},{"start":{"line":153,"column":2},"end":{"line":153,"column":2}}]}}},"contracts/TrustFactory.sol":{"l":{"24":4,"25":4,"35":16,"36":12,"37":10,"39":10,"40":1,"42":9,"43":5,"45":6,"46":6,"47":6,"58":9,"59":7,"60":5,"61":5,"72":8,"73":6,"74":4,"75":4,"83":5,"84":3,"91":4,"92":4,"93":4,"100":1,"101":1,"102":1,"103":1,"104":1,"117":11,"118":9},"path":"/home/peter/Documents/Work/MyBit/MyBit-Trust.tech/contracts/TrustFactory.sol","s":{"1":4,"2":4,"3":16,"4":12,"5":10,"6":1,"7":9,"8":5,"9":6,"10":6,"11":9,"12":7,"13":5,"14":5,"15":8,"16":6,"17":4,"18":4,"19":5,"20":3,"21":4,"22":4,"23":4,"24":1,"25":1,"26":1,"27":1,"28":1,"29":11},"b":{"1":[12,4],"2":[10,2],"3":[1,9],"4":[5,4],"5":[7,2],"6":[5,2],"7":[6,2],"8":[4,2],"9":[3,2],"10":[1,0],"11":[1,0],"12":[9,2]},"f":{"1":4,"2":16,"3":9,"4":8,"5":5,"6":4,"7":1,"8":2,"9":11},"fnMap":{"1":{"name":null,"line":22,"loc":{"start":{"line":22,"column":2},"end":{"line":23,"column":10}}},"2":{"name":"deployTrust","line":32,"loc":{"start":{"line":32,"column":2},"end":{"line":34,"column":11}}},"3":{"name":"createTrustERC20","line":55,"loc":{"start":{"line":55,"column":2},"end":{"line":57,"column":10}}},"4":{"name":"createTrustERC721","line":69,"loc":{"start":{"line":69,"column":2},"end":{"line":71,"column":10}}},"5":{"name":"closeFactory","line":80,"loc":{"start":{"line":80,"column":2},"end":{"line":82,"column":12}}},"6":{"name":"changeMYBFee","line":88,"loc":{"start":{"line":88,"column":2},"end":{"line":90,"column":12}}},"7":{"name":"ethBurn","line":97,"loc":{"start":{"line":97,"column":2},"end":{"line":99,"column":18}}},"8":{"name":null,"line":108,"loc":{"start":{"line":108,"column":2},"end":{"line":108,"column":30}}},"9":{"name":"onlyOwner","line":116,"loc":{"start":{"line":116,"column":2},"end":{"line":116,"column":20}}}},"statementMap":{"1":{"start":{"line":24,"column":4},"end":{"line":24,"column":21}},"2":{"start":{"line":25,"column":4},"end":{"line":25,"column":43}},"3":{"start":{"line":35,"column":4},"end":{"line":35,"column":25}},"4":{"start":{"line":36,"column":4},"end":{"line":36,"column":20}},"5":{"start":{"line":39,"column":4},"end":{"line":39,"column":1494}},"6":{"start":{"line":40,"column":6},"end":{"line":40,"column":32}},"7":{"start":{"line":42,"column":6},"end":{"line":42,"column":60}},"8":{"start":{"line":43,"column":6},"end":{"line":43,"column":23}},"9":{"start":{"line":45,"column":4},"end":{"line":45,"column":81}},"10":{"start":{"line":47,"column":4},"end":{"line":47,"column":73}},"11":{"start":{"line":58,"column":4},"end":{"line":58,"column":20}},"12":{"start":{"line":59,"column":4},"end":{"line":59,"column":75}},"13":{"start":{"line":60,"column":4},"end":{"line":60,"column":114}},"14":{"start":{"line":61,"column":4},"end":{"line":61,"column":70}},"15":{"start":{"line":72,"column":4},"end":{"line":72,"column":20}},"16":{"start":{"line":73,"column":4},"end":{"line":73,"column":75}},"17":{"start":{"line":74,"column":4},"end":{"line":74,"column":116}},"18":{"start":{"line":75,"column":4},"end":{"line":75,"column":71}},"19":{"start":{"line":83,"column":4},"end":{"line":83,"column":21}},"20":{"start":{"line":84,"column":4},"end":{"line":84,"column":17}},"21":{"start":{"line":91,"column":4},"end":{"line":91,"column":23}},"22":{"start":{"line":92,"column":4},"end":{"line":92,"column":19}},"23":{"start":{"line":93,"column":4},"end":{"line":93,"column":40}},"24":{"start":{"line":100,"column":4},"end":{"line":100,"column":45}},"25":{"start":{"line":101,"column":4},"end":{"line":101,"column":117}},"26":{"start":{"line":102,"column":4},"end":{"line":102,"column":66}},"27":{"start":{"line":103,"column":4},"end":{"line":103,"column":70}},"28":{"start":{"line":104,"column":4},"end":{"line":104,"column":17}},"29":{"start":{"line":117,"column":4},"end":{"line":117,"column":31}}},"branchMap":{"1":{"line":35,"type":"if","locations":[{"start":{"line":35,"column":4},"end":{"line":35,"column":4}},{"start":{"line":35,"column":4},"end":{"line":35,"column":4}}]},"2":{"line":36,"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":4}},{"start":{"line":36,"column":4},"end":{"line":36,"column":4}}]},"3":{"line":39,"type":"if","locations":[{"start":{"line":39,"column":4},"end":{"line":39,"column":4}},{"start":{"line":39,"column":4},"end":{"line":39,"column":4}}]},"4":{"line":42,"type":"if","locations":[{"start":{"line":42,"column":6},"end":{"line":42,"column":6}},{"start":{"line":42,"column":6},"end":{"line":42,"column":6}}]},"5":{"line":58,"type":"if","locations":[{"start":{"line":58,"column":4},"end":{"line":58,"column":4}},{"start":{"line":58,"column":4},"end":{"line":58,"column":4}}]},"6":{"line":59,"type":"if","locations":[{"start":{"line":59,"column":4},"end":{"line":59,"column":4}},{"start":{"line":59,"column":4},"end":{"line":59,"column":4}}]},"7":{"line":72,"type":"if","locations":[{"start":{"line":72,"column":4},"end":{"line":72,"column":4}},{"start":{"line":72,"column":4},"end":{"line":72,"column":4}}]},"8":{"line":73,"type":"if","locations":[{"start":{"line":73,"column":4},"end":{"line":73,"column":4}},{"start":{"line":73,"column":4},"end":{"line":73,"column":4}}]},"9":{"line":83,"type":"if","locations":[{"start":{"line":83,"column":4},"end":{"line":83,"column":4}},{"start":{"line":83,"column":4},"end":{"line":83,"column":4}}]},"10":{"line":101,"type":"if","locations":[{"start":{"line":101,"column":4},"end":{"line":101,"column":4}},{"start":{"line":101,"column":4},"end":{"line":101,"column":4}}]},"11":{"line":103,"type":"if","locations":[{"start":{"line":103,"column":4},"end":{"line":103,"column":4}},{"start":{"line":103,"column":4},"end":{"line":103,"column":4}}]},"12":{"line":117,"type":"if","locations":[{"start":{"line":117,"column":4},"end":{"line":117,"column":4}},{"start":{"line":117,"column":4},"end":{"line":117,"column":4}}]}}}} \ No newline at end of file diff --git a/package.json b/package.json index 4bda6af..b2deb44 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,6 @@ "truffle": "5.0.0-beta.2" }, "devDependencies": { - "solidity-coverage": "^0.5.11" + "solidity-coverage": "0.5.11" } } diff --git a/test/Kyber.js b/test/Kyber.js index 3d3c794..0161388 100644 --- a/test/Kyber.js +++ b/test/Kyber.js @@ -1059,6 +1059,17 @@ contract('Kyber', function(accounts) { await factory.changeMYBFee(BigNumber(10**12).toString()); }); + it("Should fail to burn tokenInstance 1", async() => { + let err; + try{ + //Approval not given to transfer token to burner address + await factory.deployTrust(accounts[2], true, '100', tokenInstance[1].address, {from: accounts[1], value: ETH.toString()}); + } catch (e) { + err = e; + } + assert.notEqual(err, undefined); + }); + it("Should burn tokenInstance 1", async() => { await tokenInstance[1].approve(burner.address, tokenPerAccount.toString(), {from: accounts[1]}); let balanceBefore = await tokenInstance[1].balanceOf(accounts[1]); @@ -1066,4 +1077,32 @@ contract('Kyber', function(accounts) { let balanceAfter = await tokenInstance[1].balanceOf(accounts[1]); assert.equal(BigNumber(balanceAfter).lt(balanceBefore), true); }); + + it("Should burn ETH", async() => { + let trustAmount = BigNumber(10).times(ETH); + let balanceBefore = await web3.eth.getBalance(accounts[1]); + let block = await web3.eth.getBlock('latest'); + tx = await factory.deployTrust(accounts[2], true, '100', ethAddress, {from: accounts[1], value: trustAmount.toString(), gas: '0xfffffffffff'}); + /* + console.log(tx.logs); + let logs = await burner.getPastEvents('LogTrade', {filter: {}, fromBlock: block.number}); + for(var i=0; i { burner = await Burner.new(token.address, kyberAddress); }); - it('Fail to send ether', async() => { - let err; - try{ - await web3.eth.sendTransaction({from:user1, to: burner.address, value: '10000'}) - } catch(e){ - err = e; - } - assert.notEqual(err, undefined); - }); - it('Fail to burn tokens', async() => { let err; try{ diff --git a/truffle.js b/truffle.js index 6dac5a1..df8a736 100644 --- a/truffle.js +++ b/truffle.js @@ -6,6 +6,13 @@ module.exports = { network_id: "*", gasPrice: 1 }, + coverage: { + host: "localhost", + port: 8555, + network_id: "*", + gas: 0xfffffffffff, + gasPrice: 0x01 + } }, solc: { optimizer: { From 2066c020948fb82c4fe9c60a077a59b31001df1c Mon Sep 17 00:00:00 2001 From: Iggy Grey Date: Mon, 25 Feb 2019 21:10:30 -0800 Subject: [PATCH 3/3] Fix gas amount on test --- test/Kyber.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Kyber.js b/test/Kyber.js index 0161388..a2e9995 100644 --- a/test/Kyber.js +++ b/test/Kyber.js @@ -1082,7 +1082,7 @@ contract('Kyber', function(accounts) { let trustAmount = BigNumber(10).times(ETH); let balanceBefore = await web3.eth.getBalance(accounts[1]); let block = await web3.eth.getBlock('latest'); - tx = await factory.deployTrust(accounts[2], true, '100', ethAddress, {from: accounts[1], value: trustAmount.toString(), gas: '0xfffffffffff'}); + tx = await factory.deployTrust(accounts[2], true, '100', ethAddress, {from: accounts[1], value: trustAmount.toString()}); /* console.log(tx.logs); let logs = await burner.getPastEvents('LogTrade', {filter: {}, fromBlock: block.number});