diff --git a/contracts/asset-proxy/contracts/src/interfaces/IUniswapExchange.sol b/contracts/asset-proxy/contracts/src/interfaces/IUniswapExchange.sol index 20fa61ab58..7278da3978 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/IUniswapExchange.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/IUniswapExchange.sol @@ -67,11 +67,4 @@ interface IUniswapExchange { ) external returns (uint256 tokensBought); - - /// @dev Retrieves the token that is associated with this exchange. - /// @return tokenAddress The token address. - function toTokenAddress() - external - view - returns (address tokenAddress); } diff --git a/contracts/erc20-bridge-sampler/contracts/src/DeploymentConstants.sol b/contracts/erc20-bridge-sampler/contracts/src/DeploymentConstants.sol new file mode 100644 index 0000000000..cc99e2e62c --- /dev/null +++ b/contracts/erc20-bridge-sampler/contracts/src/DeploymentConstants.sol @@ -0,0 +1,86 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; +import "./IEth2Dai.sol"; +import "./IKyberNetwork.sol"; + + +contract DeploymentConstants { + + address constant public EXCHANGE_ADDRESS = 0x080bf510FCbF18b91105470639e9561022937712; + address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e; + address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95; + address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; + address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + /// @dev An overridable way to retrieve the 0x Exchange contract. + /// @return zeroex The 0x Exchange contract. + function _getExchangeContract() + internal + view + returns (IExchange zeroex) + { + return IExchange(EXCHANGE_ADDRESS); + } + + /// @dev An overridable way to retrieve the Eth2Dai exchange contract. + /// @return eth2dai The Eth2Dai exchange contract. + function _getEth2DaiContract() + internal + view + returns (IEth2Dai eth2dai) + { + return IEth2Dai(ETH2DAI_ADDRESS); + } + + /// @dev An overridable way to retrieve the Uniswap exchange factory contract. + /// @return uniswap The UniswapExchangeFactory contract. + function _getUniswapExchangeFactoryContract() + internal + view + returns (IUniswapExchangeFactory uniswap) + { + return IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS); + } + + /// @dev An overridable way to retrieve the Kyber network proxy contract. + /// @return kyber The KyberNeworkProxy contract. + function _getKyberNetworkContract() + internal + view + returns (IKyberNetwork kyber) + { + return IKyberNetwork(KYBER_NETWORK_PROXY_ADDRESS); + } + + /// @dev An overridable way to retrieve the WETH contract address. + /// @return weth The WETH contract address. + function _getWETHAddress() + internal + view + returns (address weth) + { + return WETH_ADDRESS; + } +} diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index 7ed04b5642..897b32ff00 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -1,25 +1,40 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol"; import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "./IERC20BridgeSampler.sol"; -import "./IExchange.sol"; import "./IEth2Dai.sol"; import "./IKyberNetwork.sol"; import "./IUniswapExchangeQuotes.sol"; +import "./DeploymentConstants.sol"; contract ERC20BridgeSampler is - IERC20BridgeSampler + IERC20BridgeSampler, + DeploymentConstants { bytes4 constant internal ERC20_PROXY_ID = bytes4(keccak256("ERC20Token(address)")); - address constant public EXCHANGE_ADDRESS = 0x080bf510FCbF18b91105470639e9561022937712; // V2 - address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e; - address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95; - address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; - address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /// @dev Query native orders and sample sell quotes on multiple DEXes at once. /// @param orders Native orders to query. @@ -30,14 +45,14 @@ contract ERC20BridgeSampler is /// each taker token amount. First indexed by source index, then sample /// index. function queryOrdersAndSampleSells( - IExchange.Order[] memory orders, + LibOrder.Order[] memory orders, address[] memory sources, uint256[] memory takerTokenAmounts ) public view returns ( - IExchange.OrderInfo[] memory orderInfos, + LibOrder.OrderInfo[] memory orderInfos, uint256[][] memory makerTokenAmountsBySource ) { @@ -60,14 +75,14 @@ contract ERC20BridgeSampler is /// each maker token amount. First indexed by source index, then sample /// index. function queryOrdersAndSampleBuys( - IExchange.Order[] memory orders, + LibOrder.Order[] memory orders, address[] memory sources, uint256[] memory makerTokenAmounts ) public view returns ( - IExchange.OrderInfo[] memory orderInfos, + LibOrder.OrderInfo[] memory orderInfos, uint256[][] memory makerTokenAmountsBySource ) { @@ -83,15 +98,15 @@ contract ERC20BridgeSampler is /// @dev Queries the status of several native orders. /// @param orders Native orders to query. /// @return orderInfos Order info for each respective order. - function queryOrders(IExchange.Order[] memory orders) + function queryOrders(LibOrder.Order[] memory orders) public view - returns (IExchange.OrderInfo[] memory orderInfos) + returns (LibOrder.OrderInfo[] memory orderInfos) { uint256 numOrders = orders.length; - orderInfos = new IExchange.OrderInfo[](numOrders); + orderInfos = new LibOrder.OrderInfo[](numOrders); for (uint256 i = 0; i < numOrders; i++) { - orderInfos[i] = IExchange(EXCHANGE_ADDRESS).getOrderInfo(orders[i]); + orderInfos[i] = _getExchangeContract().getOrderInfo(orders[i]); } } @@ -170,14 +185,14 @@ contract ERC20BridgeSampler is view returns (uint256[] memory makerTokenAmounts) { - address _takerToken = takerToken == WETH_ADDRESS ? KYBER_ETH_ADDRESS : takerToken; - address _makerToken = makerToken == WETH_ADDRESS ? KYBER_ETH_ADDRESS : makerToken; + address _takerToken = takerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : takerToken; + address _makerToken = makerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : makerToken; uint256 takerTokenDecimals = LibERC20Token.decimals(takerToken); uint256 makerTokenDecimals = LibERC20Token.decimals(makerToken); uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); for (uint256 i = 0; i < numSamples; i++) { - (uint256 rate,) = IKyberNetwork(KYBER_NETWORK_PROXY_ADDRESS).getExpectedRate( + (uint256 rate,) = _getKyberNetworkContract().getExpectedRate( _takerToken, _makerToken, takerTokenAmounts[i] @@ -185,8 +200,9 @@ contract ERC20BridgeSampler is makerTokenAmounts[i] = rate * takerTokenAmounts[i] * - makerTokenDecimals / - (10 ** 18 * takerTokenDecimals); + 10 ** makerTokenDecimals / + 10 ** takerTokenDecimals / + 10 ** 18; } } @@ -208,7 +224,7 @@ contract ERC20BridgeSampler is uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); for (uint256 i = 0; i < numSamples; i++) { - makerTokenAmounts[i] = IEth2Dai(ETH2DAI_ADDRESS).getBuyAmount( + makerTokenAmounts[i] = _getEth2DaiContract().getBuyAmount( makerToken, takerToken, takerTokenAmounts[i] @@ -234,7 +250,7 @@ contract ERC20BridgeSampler is uint256 numSamples = makerTokenAmounts.length; takerTokenAmounts = new uint256[](numSamples); for (uint256 i = 0; i < numSamples; i++) { - takerTokenAmounts[i] = IEth2Dai(ETH2DAI_ADDRESS).getPayAmount( + takerTokenAmounts[i] = _getEth2DaiContract().getPayAmount( takerToken, makerToken, makerTokenAmounts[i] @@ -259,16 +275,16 @@ contract ERC20BridgeSampler is { uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); - IUniswapExchangeQuotes takerTokenExchange = takerToken == WETH_ADDRESS ? + IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ? IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken); - IUniswapExchangeQuotes makerTokenExchange = makerToken == WETH_ADDRESS ? + IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ? IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken); for (uint256 i = 0; i < numSamples; i++) { - if (makerToken == WETH_ADDRESS) { + if (makerToken == _getWETHAddress()) { makerTokenAmounts[i] = takerTokenExchange.getTokenToEthInputPrice( takerTokenAmounts[i] ); - } else if (takerToken == WETH_ADDRESS) { + } else if (takerToken == _getWETHAddress()) { makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice( takerTokenAmounts[i] ); @@ -300,16 +316,16 @@ contract ERC20BridgeSampler is { uint256 numSamples = makerTokenAmounts.length; takerTokenAmounts = new uint256[](numSamples); - IUniswapExchangeQuotes takerTokenExchange = takerToken == WETH_ADDRESS ? + IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ? IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken); - IUniswapExchangeQuotes makerTokenExchange = makerToken == WETH_ADDRESS ? + IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ? IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken); for (uint256 i = 0; i < numSamples; i++) { - if (makerToken == WETH_ADDRESS) { + if (makerToken == _getWETHAddress()) { takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice( makerTokenAmounts[i] ); - } else if (takerToken == WETH_ADDRESS) { + } else if (takerToken == _getWETHAddress()) { takerTokenAmounts[i] = makerTokenExchange.getEthToTokenOutputPrice( makerTokenAmounts[i] ); @@ -340,13 +356,13 @@ contract ERC20BridgeSampler is view returns (uint256[] memory makerTokenAmounts) { - if (source == ETH2DAI_ADDRESS) { + if (source == address(_getEth2DaiContract())) { return sampleSellFromEth2Dai(takerToken, makerToken, takerTokenAmounts); } - if (source == UNISWAP_EXCHANGE_FACTORY_ADDRESS) { + if (source == address(_getUniswapExchangeFactoryContract())) { return sampleSellFromUniswap(takerToken, makerToken, takerTokenAmounts); } - if (source == KYBER_NETWORK_PROXY_ADDRESS) { + if (source == address(_getKyberNetworkContract())) { return sampleSellFromKyberNetwork(takerToken, makerToken, takerTokenAmounts); } revert("UNSUPPORTED_SOURCE"); @@ -368,10 +384,10 @@ contract ERC20BridgeSampler is view returns (uint256[] memory takerTokenAmounts) { - if (source == ETH2DAI_ADDRESS) { + if (source == address(_getEth2DaiContract())) { return sampleBuyFromEth2Dai(takerToken, makerToken, makerTokenAmounts); } - if (source == UNISWAP_EXCHANGE_FACTORY_ADDRESS) { + if (source == address(_getUniswapExchangeFactoryContract())) { return sampleBuyFromUniswap(takerToken, makerToken, makerTokenAmounts); } revert("UNSUPPORTED_SOURCE"); @@ -387,10 +403,7 @@ contract ERC20BridgeSampler is returns (IUniswapExchangeQuotes exchange) { exchange = IUniswapExchangeQuotes( - address( - IUniswapExchangeFactory(UNISWAP_EXCHANGE_FACTORY_ADDRESS) - .getExchange(tokenAddress) - ) + address(_getUniswapExchangeFactoryContract().getExchange(tokenAddress)) ); require(address(exchange) != address(0), "UNSUPPORTED_UNISWAP_EXCHANGE"); } diff --git a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol index 1760a0e2bb..bdbc92d393 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol @@ -1,7 +1,26 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; -import "./IExchange.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; interface IERC20BridgeSampler { @@ -15,14 +34,14 @@ interface IERC20BridgeSampler { /// each taker token amount. First indexed by source index, then sample /// index. function queryOrdersAndSampleSells( - IExchange.Order[] calldata orders, + LibOrder.Order[] calldata orders, address[] calldata sources, uint256[] calldata takerTokenAmounts ) external view returns ( - IExchange.OrderInfo[] memory orderInfos, + LibOrder.OrderInfo[] memory orderInfos, uint256[][] memory makerTokenAmountsBySource ); @@ -35,24 +54,24 @@ interface IERC20BridgeSampler { /// each maker token amount. First indexed by source index, then sample /// index. function queryOrdersAndSampleBuys( - IExchange.Order[] calldata orders, + LibOrder.Order[] calldata orders, address[] calldata sources, uint256[] calldata makerTokenAmounts ) external view returns ( - IExchange.OrderInfo[] memory orderInfos, + LibOrder.OrderInfo[] memory orderInfos, uint256[][] memory makerTokenAmountsBySource ); /// @dev Queries the status of several native orders. /// @param orders Native orders to query. /// @return orderInfos Order info for each respective order. - function queryOrders(IExchange.Order[] calldata orders) + function queryOrders(LibOrder.Order[] calldata orders) external view - returns (IExchange.OrderInfo[] memory orderInfos); + returns (LibOrder.OrderInfo[] memory orderInfos); /// @dev Sample sell quotes on multiple DEXes at once. /// @param sources Address of each DEX. Passing in an unsupported DEX will throw. diff --git a/contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol b/contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol index fbd96d89c3..ea68aca77f 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IEth2Dai.sol @@ -1,3 +1,21 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + pragma solidity ^0.5.9; diff --git a/contracts/erc20-bridge-sampler/contracts/src/IExchange.sol b/contracts/erc20-bridge-sampler/contracts/src/IExchange.sol deleted file mode 100644 index b2bea43179..0000000000 --- a/contracts/erc20-bridge-sampler/contracts/src/IExchange.sol +++ /dev/null @@ -1,34 +0,0 @@ -pragma solidity ^0.5.9; -pragma experimental ABIEncoderV2; - - -interface IExchange { - - struct Order { - address makerAddress; - address takerAddress; - address feeRecipientAddress; - address senderAddress; - uint256 makerAssetAmount; - uint256 takerAssetAmount; - uint256 makerFee; - uint256 takerFee; - uint256 expirationTimeSeconds; - uint256 salt; - bytes makerAssetData; - bytes takerAssetData; - // bytes makerFeeAssetData; - // bytes takerFeeAssetData; - } - - struct OrderInfo { - uint8 orderStatus; - bytes32 orderHash; - uint256 orderTakerAssetFilledAmount; - } - - function getOrderInfo(Order calldata order) - external - view - returns (OrderInfo memory orderInfo); -} diff --git a/contracts/erc20-bridge-sampler/contracts/src/IKyberNetwork.sol b/contracts/erc20-bridge-sampler/contracts/src/IKyberNetwork.sol index 9420f982c9..4dc8ca134d 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IKyberNetwork.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IKyberNetwork.sol @@ -1,3 +1,21 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + pragma solidity ^0.5.9; diff --git a/contracts/erc20-bridge-sampler/contracts/src/IUniswapExchangeQuotes.sol b/contracts/erc20-bridge-sampler/contracts/src/IUniswapExchangeQuotes.sol index 1de760e89f..2c0dc600a4 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IUniswapExchangeQuotes.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IUniswapExchangeQuotes.sol @@ -1,3 +1,21 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + pragma solidity ^0.5.9; diff --git a/contracts/erc20-bridge-sampler/contracts/src/LibERC20Token.sol b/contracts/erc20-bridge-sampler/contracts/src/LibERC20Token.sol index 463dd1ea10..97df57b0d9 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/LibERC20Token.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/LibERC20Token.sol @@ -1,3 +1,21 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + pragma solidity ^0.5.9; diff --git a/contracts/erc20-bridge-sampler/contracts/test/TestERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/test/TestERC20BridgeSampler.sol new file mode 100644 index 0000000000..df9001cd69 --- /dev/null +++ b/contracts/erc20-bridge-sampler/contracts/test/TestERC20BridgeSampler.sol @@ -0,0 +1,320 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.5.9; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "../src/ERC20BridgeSampler.sol"; +import "../src/IEth2Dai.sol"; +import "../src/IKyberNetwork.sol"; + + +library LibDeterministicQuotes { + + uint256 private constant MAX_RATE = 10 ** 20; + uint256 private constant MIN_RATE = 10 ** 16; + + function getDeterministicSellQuote( + bytes32 salt, + address sellToken, + address buyToken, + uint256 sellAmount + ) + internal + pure + returns (uint256 buyAmount) + { + uint256 sellBase = 10 ** getDeterministicTokenDecimals(sellToken); + uint256 buyBase = 10 ** getDeterministicTokenDecimals(buyToken); + uint256 rate = _getDeterministicRate(salt, sellToken, buyToken); + return sellAmount * rate * buyBase / sellBase / 10 ** 18; + } + + function getDeterministicBuyQuote( + bytes32 salt, + address sellToken, + address buyToken, + uint256 buyAmount + ) + internal + pure + returns (uint256 sellAmount) + { + uint256 sellBase = 10 ** getDeterministicTokenDecimals(sellToken); + uint256 buyBase = 10 ** getDeterministicTokenDecimals(buyToken); + uint256 rate = _getDeterministicRate(salt, sellToken, buyToken); + return buyAmount * 10 ** 18 * sellBase / rate / buyBase; + } + + function getDeterministicTokenDecimals(address token) + internal + pure + returns (uint8 decimals) + { + bytes32 seed = keccak256(abi.encodePacked(token)); + return uint8(uint256(seed) % 18) + 6; + } + + function _getDeterministicRate(bytes32 salt, address sellToken, address buyToken) + private + pure + returns (uint256 rate) + { + bytes32 seed = keccak256(abi.encodePacked(salt, sellToken, buyToken)); + return MIN_RATE + uint256(seed) % MAX_RATE; + } +} + + +contract TestERC20BridgeSamplerUniswapExchange is + IUniswapExchangeQuotes +{ + bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab; + + address public tokenAddress; + bytes32 public salt; + address private _wethAddress; + + constructor(address _tokenAddress) public { + _wethAddress = msg.sender; + tokenAddress = _tokenAddress; + salt = keccak256(abi.encodePacked(BASE_SALT, _tokenAddress)); + } + + // Deterministic `IUniswapExchangeQuotes.getEthToTokenInputPrice()`. + function getEthToTokenInputPrice( + uint256 ethSold + ) + external + view + returns (uint256 tokensBought) + { + return LibDeterministicQuotes.getDeterministicSellQuote( + salt, + _wethAddress, + tokenAddress, + ethSold + ); + } + + // Deterministic `IUniswapExchangeQuotes.getEthToTokenOutputPrice()`. + function getEthToTokenOutputPrice( + uint256 tokensBought + ) + external + view + returns (uint256 ethSold) + { + return LibDeterministicQuotes.getDeterministicBuyQuote( + salt, + _wethAddress, + tokenAddress, + tokensBought + ); + } + + // Deterministic `IUniswapExchangeQuotes.getTokenToEthInputPrice()`. + function getTokenToEthInputPrice( + uint256 tokensSold + ) + external + view + returns (uint256 ethBought) + { + return LibDeterministicQuotes.getDeterministicSellQuote( + salt, + tokenAddress, + _wethAddress, + tokensSold + ); + } + + // Deterministic `IUniswapExchangeQuotes.getTokenToEthOutputPrice()`. + function getTokenToEthOutputPrice( + uint256 ethBought + ) + external + view + returns (uint256 tokensSold) + { + return LibDeterministicQuotes.getDeterministicBuyQuote( + salt, + tokenAddress, + _wethAddress, + ethBought + ); + } +} + + +contract TestERC20BridgeSampler is + ERC20BridgeSampler +{ + bytes32 constant private KYBER_SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7; + bytes32 constant private ETH2DAI_SALT = 0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7; + + mapping (address => IUniswapExchangeQuotes) private _uniswapExchangesByToken; + + // Creates Uniswap exchange contracts for tokens. + function createTokenExchanges(address[] calldata tokenAddresses) + external + { + for (uint256 i = 0; i < tokenAddresses.length; i++) { + address tokenAddress = tokenAddresses[i]; + _uniswapExchangesByToken[tokenAddress] = + new TestERC20BridgeSamplerUniswapExchange(tokenAddress); + } + } + + // `IERC20.decimals()` (for WETH). + function decimals() + external + view + returns (uint8 decimalPlaces) + { + return 18; + } + + // Deterministic `IKyberNetwork.getExpectedRate()`. + function getExpectedRate( + address fromToken, + address toToken, + uint256 fromAmount + ) + external + view + returns (uint256 expectedRate, uint256 slippageRate) + { + uint256 quote = LibDeterministicQuotes.getDeterministicSellQuote( + KYBER_SALT, + fromToken, + toToken, + fromAmount + ); + expectedRate = + 10 ** 18 * + (quote / LibDeterministicQuotes.getDeterministicTokenDecimals(toToken)) / + (fromAmount * LibDeterministicQuotes.getDeterministicTokenDecimals(fromToken)); + } + + // Deterministic `IEth2Dai.getBuyAmount()`. + function getBuyAmount( + address buyToken, + address payToken, + uint256 payAmount + ) + external + view + returns (uint256 buyAmount) + { + return LibDeterministicQuotes.getDeterministicSellQuote( + ETH2DAI_SALT, + payToken, + buyToken, + payAmount + ); + } + + // Deterministic `IEth2Dai.getPayAmount()`. + function getPayAmount( + address payToken, + address buyToken, + uint256 buyAmount + ) + external + view + returns (uint256 payAmount) + { + return LibDeterministicQuotes.getDeterministicBuyQuote( + ETH2DAI_SALT, + payToken, + buyToken, + buyAmount + ); + } + + // `IUniswapExchangeFactory.getExchange()`. + function getExchange(address tokenAddress) + external + view + returns (address) + { + return address(_uniswapExchangesByToken[tokenAddress]); + } + + // `IExchange.getOrderInfo()`, overridden to return deterministic order infos. + function getOrderInfo(LibOrder.Order memory order) + public + view + returns (LibOrder.OrderInfo memory orderInfo) + { + // The order hash is just the hash of the salt. + bytes32 orderHash = keccak256(abi.encode(order.salt)); + // Everything else is derived from the hash. + orderInfo.orderHash = orderHash; + orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1)); + orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount; + } + + // Overriden to point to this contract. + function _getExchangeContract() + internal + view + returns (IExchange zeroex) + { + return IExchange(address(this)); + } + + // Overriden to point to this contract. + function _getEth2DaiContract() + internal + view + returns (IEth2Dai eth2dai) + { + return IEth2Dai(address(this)); + } + + // Overriden to point to this contract. + function _getUniswapExchangeFactoryContract() + internal + view + returns (IUniswapExchangeFactory uniswap) + { + return IUniswapExchangeFactory(address(this)); + } + + // Overriden to point to this contract. + function _getKyberNetworkContract() + internal + view + returns (IKyberNetwork kyber) + { + return IKyberNetwork(address(this)); + } + + // Overriden to point to this contract. + function _getWETHAddress() + internal + view + returns (address weth) + { + return address(this); + } +} diff --git a/contracts/erc20-bridge-sampler/package.json b/contracts/erc20-bridge-sampler/package.json index 435290db4e..b96ed8b7b8 100644 --- a/contracts/erc20-bridge-sampler/package.json +++ b/contracts/erc20-bridge-sampler/package.json @@ -38,7 +38,7 @@ "config": { "publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", - "abis": "./test/generated-artifacts/@(ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IExchange|IKyberNetwork|IUniswapExchangeQuotes|LibERC20Token).json" + "abis": "./test/generated-artifacts/@(DeploymentConstants|ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|LibERC20Token|TestERC20BridgeSampler).json" }, "repository": { "type": "git", @@ -54,6 +54,7 @@ "@0x/contracts-asset-proxy": "^2.3.0-beta.1", "@0x/contracts-erc20": "^2.3.0-beta.1", "@0x/contracts-exchange-libs": "^3.1.0-beta.1", + "@0x/contracts-exchange": "^2.2.0-beta.1", "@0x/contracts-gen": "^1.1.0-beta.1", "@0x/contracts-test-utils": "^3.2.0-beta.1", "@0x/contracts-utils": "^3.3.0-beta.1", diff --git a/contracts/erc20-bridge-sampler/test/artifacts.ts b/contracts/erc20-bridge-sampler/test/artifacts.ts index 8d964ab27d..388c59b542 100644 --- a/contracts/erc20-bridge-sampler/test/artifacts.ts +++ b/contracts/erc20-bridge-sampler/test/artifacts.ts @@ -5,19 +5,21 @@ */ import { ContractArtifact } from 'ethereum-types'; +import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json'; import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json'; import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json'; import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; -import * as IExchange from '../test/generated-artifacts/IExchange.json'; import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json'; import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json'; import * as LibERC20Token from '../test/generated-artifacts/LibERC20Token.json'; +import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json'; export const artifacts = { + DeploymentConstants: DeploymentConstants as ContractArtifact, ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact, IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact, IEth2Dai: IEth2Dai as ContractArtifact, - IExchange: IExchange as ContractArtifact, IKyberNetwork: IKyberNetwork as ContractArtifact, IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact, LibERC20Token: LibERC20Token as ContractArtifact, + TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact, }; diff --git a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts new file mode 100644 index 0000000000..33740967a7 --- /dev/null +++ b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts @@ -0,0 +1,24 @@ +import { blockchainTests, expect, getRandomInteger } from '@0x/contracts-test-utils'; +import { BigNumber } from '@0x/utils'; + +import { artifacts } from './artifacts'; +import { TestERC20BridgeSamplerContract } from './wrappers'; + +blockchainTests('erc20-bridge-sampler', env => { + let testContract: TestERC20BridgeSamplerContract; + + before(async () => { + testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync( + artifacts.TestERC20BridgeSampler, + env.provider, + env.txDefaults, + {}, + ); + }); + + describe('queryOrders()', () => { + it('returns the results of `getOrderInfo()` for each order', async () => { + + }); + }); +}); diff --git a/contracts/erc20-bridge-sampler/test/wrappers.ts b/contracts/erc20-bridge-sampler/test/wrappers.ts index 6e52b706ae..c4980e30fd 100644 --- a/contracts/erc20-bridge-sampler/test/wrappers.ts +++ b/contracts/erc20-bridge-sampler/test/wrappers.ts @@ -3,10 +3,11 @@ * Warning: This file is auto-generated by contracts-gen. Don't edit manually. * ----------------------------------------------------------------------------- */ +export * from '../test/generated-wrappers/deployment_constants'; export * from '../test/generated-wrappers/erc20_bridge_sampler'; export * from '../test/generated-wrappers/i_erc20_bridge_sampler'; export * from '../test/generated-wrappers/i_eth2_dai'; -export * from '../test/generated-wrappers/i_exchange'; export * from '../test/generated-wrappers/i_kyber_network'; export * from '../test/generated-wrappers/i_uniswap_exchange_quotes'; export * from '../test/generated-wrappers/lib_erc20_token'; +export * from '../test/generated-wrappers/test_erc20_bridge_sampler'; diff --git a/contracts/erc20-bridge-sampler/tsconfig.json b/contracts/erc20-bridge-sampler/tsconfig.json index a668bb9f1c..d5a7f69ea0 100644 --- a/contracts/erc20-bridge-sampler/tsconfig.json +++ b/contracts/erc20-bridge-sampler/tsconfig.json @@ -5,13 +5,14 @@ "files": [ "generated-artifacts/ERC20BridgeSampler.json", "generated-artifacts/IERC20BridgeSampler.json", + "test/generated-artifacts/DeploymentConstants.json", "test/generated-artifacts/ERC20BridgeSampler.json", "test/generated-artifacts/IERC20BridgeSampler.json", "test/generated-artifacts/IEth2Dai.json", - "test/generated-artifacts/IExchange.json", "test/generated-artifacts/IKyberNetwork.json", "test/generated-artifacts/IUniswapExchangeQuotes.json", - "test/generated-artifacts/LibERC20Token.json" + "test/generated-artifacts/LibERC20Token.json", + "test/generated-artifacts/TestERC20BridgeSampler.json" ], "exclude": ["./deploy/solc/solc_bin"] }