Swallow reverts in ERC20BridgeSampler (#2395)
* `@0x/erc20-bridge-sampler`: Do not query empty/unsigned orders. Swallow revets on DEX quotes. * `@0x/contracts-utils`: Add `DEV_UTILS_ADDRESS` and `KYBER_ETH_ADDRESS` to `DeploymentConstants`. * `@0x/contracts-erc20-bridge-sampler`: Address review comments.
This commit is contained in:
parent
a556d91673
commit
70870ffcd2
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.0.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Do not query empty/unsigned orders. Swallow revets on DEX quotes.",
|
||||||
|
"pr": 2365
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1575931811,
|
"timestamp": 1575931811,
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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 {
|
|
||||||
|
|
||||||
/// @dev Address of the 0x Exchange contract.
|
|
||||||
address constant public EXCHANGE_ADDRESS = 0x080bf510FCbF18b91105470639e9561022937712;
|
|
||||||
/// @dev Address of the Eth2Dai MatchingMarket contract.
|
|
||||||
address constant public ETH2DAI_ADDRESS = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
|
||||||
/// @dev Address of the UniswapExchangeFactory contract.
|
|
||||||
address constant public UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
|
||||||
/// @dev Address of the KyberNeworkProxy contract.
|
|
||||||
address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
|
|
||||||
/// @dev Address of the WETH contract.
|
|
||||||
address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
|
||||||
/// @dev Kyber ETH pseudo-address.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,11 +23,13 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFacto
|
|||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
|
import "./IDevUtils.sol";
|
||||||
import "./IERC20BridgeSampler.sol";
|
import "./IERC20BridgeSampler.sol";
|
||||||
import "./IEth2Dai.sol";
|
import "./IEth2Dai.sol";
|
||||||
import "./IKyberNetwork.sol";
|
import "./IKyberNetwork.sol";
|
||||||
import "./IUniswapExchangeQuotes.sol";
|
import "./IUniswapExchangeQuotes.sol";
|
||||||
import "./DeploymentConstants.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract ERC20BridgeSampler is
|
contract ERC20BridgeSampler is
|
||||||
@ -38,26 +40,32 @@ contract ERC20BridgeSampler is
|
|||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
/// index.
|
/// index.
|
||||||
function queryOrdersAndSampleSells(
|
function queryOrdersAndSampleSells(
|
||||||
LibOrder.Order[] memory orders,
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory orderSignatures,
|
||||||
address[] memory sources,
|
address[] memory sources,
|
||||||
uint256[] memory takerTokenAmounts
|
uint256[] memory takerTokenAmounts
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo[] memory orderInfos,
|
uint256[] memory orderFillableTakerAssetAmounts,
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
require(orders.length != 0, "EMPTY_ORDERS");
|
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
|
||||||
orderInfos = queryOrders(orders);
|
orderFillableTakerAssetAmounts = getOrderFillableTakerAssetAmounts(
|
||||||
|
orders,
|
||||||
|
orderSignatures
|
||||||
|
);
|
||||||
makerTokenAmountsBySource = sampleSells(
|
makerTokenAmountsBySource = sampleSells(
|
||||||
sources,
|
sources,
|
||||||
_assetDataToTokenAddress(orders[0].takerAssetData),
|
_assetDataToTokenAddress(orders[0].takerAssetData),
|
||||||
@ -68,26 +76,32 @@ contract ERC20BridgeSampler is
|
|||||||
|
|
||||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
/// index.
|
/// index.
|
||||||
function queryOrdersAndSampleBuys(
|
function queryOrdersAndSampleBuys(
|
||||||
LibOrder.Order[] memory orders,
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory orderSignatures,
|
||||||
address[] memory sources,
|
address[] memory sources,
|
||||||
uint256[] memory makerTokenAmounts
|
uint256[] memory makerTokenAmounts
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo[] memory orderInfos,
|
uint256[] memory orderFillableMakerAssetAmounts,
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
require(orders.length != 0, "EMPTY_ORDERS");
|
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
|
||||||
orderInfos = queryOrders(orders);
|
orderFillableMakerAssetAmounts = getOrderFillableMakerAssetAmounts(
|
||||||
|
orders,
|
||||||
|
orderSignatures
|
||||||
|
);
|
||||||
makerTokenAmountsBySource = sampleBuys(
|
makerTokenAmountsBySource = sampleBuys(
|
||||||
sources,
|
sources,
|
||||||
_assetDataToTokenAddress(orders[0].takerAssetData),
|
_assetDataToTokenAddress(orders[0].takerAssetData),
|
||||||
@ -96,18 +110,77 @@ contract ERC20BridgeSampler is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Queries the status of several native orders.
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
|
/// Effectively ignores orders that have empty signatures or
|
||||||
|
/// maker/taker asset amounts (returning 0).
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
/// @return orderInfos Order info for each respective order.
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
function queryOrders(LibOrder.Order[] memory orders)
|
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
|
function getOrderFillableTakerAssetAmounts(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory orderSignatures
|
||||||
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (LibOrder.OrderInfo[] memory orderInfos)
|
returns (uint256[] memory orderFillableTakerAssetAmounts)
|
||||||
{
|
{
|
||||||
uint256 numOrders = orders.length;
|
orderFillableTakerAssetAmounts = new uint256[](orders.length);
|
||||||
orderInfos = new LibOrder.OrderInfo[](numOrders);
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
for (uint256 i = 0; i < numOrders; i++) {
|
// Ignore orders with no signature or empty maker/taker amounts.
|
||||||
orderInfos[i] = _getExchangeContract().getOrderInfo(orders[i]);
|
if (orderSignatures[i].length == 0 ||
|
||||||
|
orders[i].makerAssetAmount == 0 ||
|
||||||
|
orders[i].takerAssetAmount == 0) {
|
||||||
|
orderFillableTakerAssetAmounts[i] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(
|
||||||
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
|
uint256 fillableTakerAssetAmount,
|
||||||
|
bool isValidSignature
|
||||||
|
) = IDevUtils(_getDevUtilsAddress()).getOrderRelevantState(
|
||||||
|
orders[i],
|
||||||
|
orderSignatures[i]
|
||||||
|
);
|
||||||
|
// The fillable amount is zero if the order is not fillable or if the
|
||||||
|
// signature is invalid.
|
||||||
|
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE) ||
|
||||||
|
!isValidSignature) {
|
||||||
|
orderFillableTakerAssetAmounts[i] = 0;
|
||||||
|
} else {
|
||||||
|
orderFillableTakerAssetAmounts[i] = fillableTakerAssetAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
|
/// Effectively ignores orders that have empty signatures or
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
|
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
|
function getOrderFillableMakerAssetAmounts(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory orderSignatures
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory orderFillableMakerAssetAmounts)
|
||||||
|
{
|
||||||
|
orderFillableMakerAssetAmounts = getOrderFillableTakerAssetAmounts(
|
||||||
|
orders,
|
||||||
|
orderSignatures
|
||||||
|
);
|
||||||
|
// `orderFillableMakerAssetAmounts` now holds taker asset amounts, so
|
||||||
|
// convert them to maker asset amounts.
|
||||||
|
for (uint256 i = 0; i < orders.length; ++i) {
|
||||||
|
if (orderFillableMakerAssetAmounts[i] != 0) {
|
||||||
|
orderFillableMakerAssetAmounts[i] = LibMath.getPartialAmountCeil(
|
||||||
|
orderFillableMakerAssetAmounts[i],
|
||||||
|
orders[i].takerAssetAmount,
|
||||||
|
orders[i].makerAssetAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,18 +260,24 @@ contract ERC20BridgeSampler is
|
|||||||
returns (uint256[] memory makerTokenAmounts)
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
{
|
{
|
||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
address _takerToken = takerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : takerToken;
|
address _takerToken = takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken;
|
||||||
address _makerToken = makerToken == _getWETHAddress() ? KYBER_ETH_ADDRESS : makerToken;
|
address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken;
|
||||||
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
|
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
|
||||||
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
|
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(uint256 rate,) = _getKyberNetworkContract().getExpectedRate(
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_takerToken,
|
_getKyberNetworkProxyAddress().staticcall(abi.encodeWithSelector(
|
||||||
_makerToken,
|
IKyberNetwork(0).getExpectedRate.selector,
|
||||||
takerTokenAmounts[i]
|
_takerToken,
|
||||||
);
|
_makerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 rate = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
rate = abi.decode(resultData, (uint256));
|
||||||
|
}
|
||||||
makerTokenAmounts[i] =
|
makerTokenAmounts[i] =
|
||||||
rate *
|
rate *
|
||||||
takerTokenAmounts[i] *
|
takerTokenAmounts[i] *
|
||||||
@ -227,11 +306,18 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 numSamples = takerTokenAmounts.length;
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
makerTokenAmounts[i] = _getEth2DaiContract().getBuyAmount(
|
(bool didSucceed, bytes memory resultData) =
|
||||||
makerToken,
|
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
|
||||||
takerToken,
|
IEth2Dai(0).getBuyAmount.selector,
|
||||||
takerTokenAmounts[i]
|
makerToken,
|
||||||
);
|
takerToken,
|
||||||
|
takerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 buyAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
}
|
||||||
|
makerTokenAmounts[i] = buyAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,11 +340,18 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 numSamples = makerTokenAmounts.length;
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
takerTokenAmounts = new uint256[](numSamples);
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
takerTokenAmounts[i] = _getEth2DaiContract().getPayAmount(
|
(bool didSucceed, bytes memory resultData) =
|
||||||
takerToken,
|
_getEth2DaiAddress().staticcall(abi.encodeWithSelector(
|
||||||
makerToken,
|
IEth2Dai(0).getPayAmount.selector,
|
||||||
makerTokenAmounts[i]
|
takerToken,
|
||||||
);
|
makerToken,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 sellAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
}
|
||||||
|
takerTokenAmounts[i] = sellAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,26 +373,38 @@ contract ERC20BridgeSampler is
|
|||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
|
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
if (makerToken == _getWETHAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = takerTokenExchange.getTokenToEthInputPrice(
|
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWETHAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
|
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethBought = takerTokenExchange.getTokenToEthInputPrice(
|
uint256 ethBought = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
makerTokenAmounts[i] = makerTokenExchange.getEthToTokenInputPrice(
|
if (ethBought != 0) {
|
||||||
ethBought
|
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
||||||
);
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
|
ethBought
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
makerTokenAmounts[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,26 +427,38 @@ contract ERC20BridgeSampler is
|
|||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
uint256 numSamples = makerTokenAmounts.length;
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
takerTokenAmounts = new uint256[](numSamples);
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWETHAddress() ?
|
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
|
||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWETHAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
if (makerToken == _getWETHAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
|
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
||||||
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWETHAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = makerTokenExchange.getEthToTokenOutputPrice(
|
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethSold = makerTokenExchange.getEthToTokenOutputPrice(
|
uint256 ethSold = _callUniswapExchangePriceFunction(
|
||||||
|
address(makerTokenExchange),
|
||||||
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
takerTokenAmounts[i] = takerTokenExchange.getTokenToEthOutputPrice(
|
if (ethSold != 0) {
|
||||||
ethSold
|
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
||||||
);
|
address(takerTokenExchange),
|
||||||
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
|
ethSold
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
takerTokenAmounts[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,6 +474,34 @@ contract ERC20BridgeSampler is
|
|||||||
return LibERC20Token.decimals(tokenAddress);
|
return LibERC20Token.decimals(tokenAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Gracefully calls a Uniswap pricing function.
|
||||||
|
/// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
|
||||||
|
/// @param functionSelector Selector of the target function.
|
||||||
|
/// @param inputAmount Quantity parameter particular to the pricing function.
|
||||||
|
/// @return outputAmount The returned amount from the function call. Will be
|
||||||
|
/// zero if the call fails or if `uniswapExchangeAddress` is zero.
|
||||||
|
function _callUniswapExchangePriceFunction(
|
||||||
|
address uniswapExchangeAddress,
|
||||||
|
bytes4 functionSelector,
|
||||||
|
uint256 inputAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 outputAmount)
|
||||||
|
{
|
||||||
|
if (uniswapExchangeAddress == address(0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
(bool didSucceed, bytes memory resultData) =
|
||||||
|
uniswapExchangeAddress.staticcall(abi.encodeWithSelector(
|
||||||
|
functionSelector,
|
||||||
|
inputAmount
|
||||||
|
));
|
||||||
|
if (didSucceed) {
|
||||||
|
outputAmount = abi.decode(resultData, (uint256));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Samples a supported sell source, defined by its address.
|
/// @dev Samples a supported sell source, defined by its address.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
@ -373,16 +518,16 @@ contract ERC20BridgeSampler is
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory makerTokenAmounts)
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
{
|
{
|
||||||
if (source == address(_getEth2DaiContract())) {
|
if (source == _getEth2DaiAddress()) {
|
||||||
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
|
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
|
||||||
}
|
}
|
||||||
if (source == address(_getUniswapExchangeFactoryContract())) {
|
if (source == _getUniswapExchangeFactoryAddress()) {
|
||||||
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
|
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
|
||||||
}
|
}
|
||||||
if (source == address(_getKyberNetworkContract())) {
|
if (source == _getKyberNetworkProxyAddress()) {
|
||||||
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
|
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
|
||||||
}
|
}
|
||||||
revert("UNSUPPORTED_SOURCE");
|
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Samples a supported buy source, defined by its address.
|
/// @dev Samples a supported buy source, defined by its address.
|
||||||
@ -401,13 +546,13 @@ contract ERC20BridgeSampler is
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory takerTokenAmounts)
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
{
|
{
|
||||||
if (source == address(_getEth2DaiContract())) {
|
if (source == _getEth2DaiAddress()) {
|
||||||
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
|
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
|
||||||
}
|
}
|
||||||
if (source == address(_getUniswapExchangeFactoryContract())) {
|
if (source == _getUniswapExchangeFactoryAddress()) {
|
||||||
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
|
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
|
||||||
}
|
}
|
||||||
revert("UNSUPPORTED_SOURCE");
|
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Retrive an existing Uniswap exchange contract.
|
/// @dev Retrive an existing Uniswap exchange contract.
|
||||||
@ -420,9 +565,9 @@ contract ERC20BridgeSampler is
|
|||||||
returns (IUniswapExchangeQuotes exchange)
|
returns (IUniswapExchangeQuotes exchange)
|
||||||
{
|
{
|
||||||
exchange = IUniswapExchangeQuotes(
|
exchange = IUniswapExchangeQuotes(
|
||||||
address(_getUniswapExchangeFactoryContract().getExchange(tokenAddress))
|
address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
|
||||||
|
.getExchange(tokenAddress))
|
||||||
);
|
);
|
||||||
require(address(exchange) != address(0), "UNSUPPORTED_UNISWAP_EXCHANGE");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Extract the token address from ERC20 proxy asset data.
|
/// @dev Extract the token address from ERC20 proxy asset data.
|
||||||
@ -433,19 +578,19 @@ contract ERC20BridgeSampler is
|
|||||||
pure
|
pure
|
||||||
returns (address tokenAddress)
|
returns (address tokenAddress)
|
||||||
{
|
{
|
||||||
require(assetData.length == 36, "INVALID_ASSET_DATA");
|
require(assetData.length == 36, "ERC20BridgeSampler/INVALID_ASSET_DATA");
|
||||||
bytes4 selector;
|
bytes4 selector;
|
||||||
assembly {
|
assembly {
|
||||||
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
|
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
|
||||||
tokenAddress := mload(add(assetData, 0x24))
|
tokenAddress := mload(add(assetData, 0x24))
|
||||||
}
|
}
|
||||||
require(selector == ERC20_PROXY_ID, "UNSUPPORTED_ASSET_PROXY");
|
require(selector == ERC20_PROXY_ID, "ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY");
|
||||||
}
|
}
|
||||||
|
|
||||||
function _assertValidPair(address makerToken, address takerToken)
|
function _assertValidPair(address makerToken, address takerToken)
|
||||||
private
|
private
|
||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
require(makerToken != takerToken, "INVALID_TOKEN_PAIR");
|
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
contracts/erc20-bridge-sampler/contracts/src/IDevUtils.sol
Normal file
45
contracts/erc20-bridge-sampler/contracts/src/IDevUtils.sol
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
|
interface IDevUtils {
|
||||||
|
|
||||||
|
/// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
|
||||||
|
/// @param order The order structure.
|
||||||
|
/// @param signature Signature provided by maker that proves the order's authenticity.
|
||||||
|
/// `0x01` can always be provided if the signature does not need to be validated.
|
||||||
|
/// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order),
|
||||||
|
/// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state),
|
||||||
|
/// and isValidSignature (validity of the provided signature).
|
||||||
|
/// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
|
||||||
|
/// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
|
||||||
|
/// amount of each asset that can be filled.
|
||||||
|
function getOrderRelevantState(LibOrder.Order calldata order, bytes calldata signature)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
|
uint256 fillableTakerAssetAmount,
|
||||||
|
bool isValidSignature
|
||||||
|
);
|
||||||
|
}
|
@ -19,59 +19,82 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
interface IERC20BridgeSampler {
|
interface IERC20BridgeSampler {
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell orders on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
/// @param takerTokenAmounts Taker sell amount for each sample.
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
/// index.
|
/// index.
|
||||||
function queryOrdersAndSampleSells(
|
function queryOrdersAndSampleSells(
|
||||||
LibOrder.Order[] calldata orders,
|
LibOrder.Order[] calldata orders,
|
||||||
|
bytes[] calldata orderSignatures,
|
||||||
address[] calldata sources,
|
address[] calldata sources,
|
||||||
uint256[] calldata takerTokenAmounts
|
uint256[] calldata takerTokenAmounts
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo[] memory orderInfos,
|
uint256[] memory orderFillableTakerAssetAmounts,
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @dev Query native orders and sample buy orders on multiple DEXes at once.
|
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
/// @param sources Address of each DEX. Passing in an unknown DEX will throw.
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
/// @param makerTokenAmounts Maker sell amount for each sample.
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
/// @return orderInfos `OrderInfo`s for each order in `orders`.
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
/// index.
|
/// index.
|
||||||
function queryOrdersAndSampleBuys(
|
function queryOrdersAndSampleBuys(
|
||||||
LibOrder.Order[] calldata orders,
|
LibOrder.Order[] calldata orders,
|
||||||
|
bytes[] calldata orderSignatures,
|
||||||
address[] calldata sources,
|
address[] calldata sources,
|
||||||
uint256[] calldata makerTokenAmounts
|
uint256[] calldata makerTokenAmounts
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo[] memory orderInfos,
|
uint256[] memory orderFillableMakerAssetAmounts,
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
uint256[][] memory makerTokenAmountsBySource
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @dev Queries the status of several native orders.
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
/// @return orderInfos Order info for each respective order.
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
function queryOrders(LibOrder.Order[] calldata orders)
|
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
|
function getOrderFillableTakerAssetAmounts(
|
||||||
|
LibOrder.Order[] calldata orders,
|
||||||
|
bytes[] calldata orderSignatures
|
||||||
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (LibOrder.OrderInfo[] memory orderInfos);
|
returns (uint256[] memory orderFillableTakerAssetAmounts);
|
||||||
|
|
||||||
|
/// @dev Queries the fillable maker asset amounts of native orders.
|
||||||
|
/// @param orders Native orders to query.
|
||||||
|
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||||
|
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||||
|
/// by each order in `orders`.
|
||||||
|
function getOrderFillableMakerAssetAmounts(
|
||||||
|
LibOrder.Order[] calldata orders,
|
||||||
|
bytes[] calldata orderSignatures
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory orderFillableMakerAssetAmounts);
|
||||||
|
|
||||||
/// @dev Sample sell quotes on multiple DEXes at once.
|
/// @dev Sample sell quotes on multiple DEXes at once.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
@ -23,6 +23,7 @@ import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
import "../src/ERC20BridgeSampler.sol";
|
import "../src/ERC20BridgeSampler.sol";
|
||||||
import "../src/IEth2Dai.sol";
|
import "../src/IEth2Dai.sol";
|
||||||
|
import "../src/IDevUtils.sol";
|
||||||
import "../src/IKyberNetwork.sol";
|
import "../src/IKyberNetwork.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -90,9 +91,28 @@ library LibDeterministicQuotes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract FailTrigger {
|
||||||
|
|
||||||
|
// Give this address a balance to force operations to fail.
|
||||||
|
address payable constant public FAILURE_ADDRESS = 0xe9dB8717BC5DFB20aaf538b4a5a02B7791FF430C;
|
||||||
|
|
||||||
|
// Funds `FAILURE_ADDRESS`.
|
||||||
|
function enableFailTrigger() external payable {
|
||||||
|
FAILURE_ADDRESS.transfer(msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _revertIfShouldFail() internal view {
|
||||||
|
if (FAILURE_ADDRESS.balance != 0) {
|
||||||
|
revert("FAIL_TRIGGERED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
contract TestERC20BridgeSamplerUniswapExchange is
|
contract TestERC20BridgeSamplerUniswapExchange is
|
||||||
IUniswapExchangeQuotes,
|
IUniswapExchangeQuotes,
|
||||||
DeploymentConstants
|
DeploymentConstants,
|
||||||
|
FailTrigger
|
||||||
{
|
{
|
||||||
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
|
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
|
||||||
|
|
||||||
@ -112,10 +132,11 @@ contract TestERC20BridgeSamplerUniswapExchange is
|
|||||||
view
|
view
|
||||||
returns (uint256 tokensBought)
|
returns (uint256 tokensBought)
|
||||||
{
|
{
|
||||||
|
_revertIfShouldFail();
|
||||||
return LibDeterministicQuotes.getDeterministicSellQuote(
|
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||||
salt,
|
salt,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
WETH_ADDRESS,
|
_getWethAddress(),
|
||||||
ethSold
|
ethSold
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -128,9 +149,10 @@ contract TestERC20BridgeSamplerUniswapExchange is
|
|||||||
view
|
view
|
||||||
returns (uint256 ethSold)
|
returns (uint256 ethSold)
|
||||||
{
|
{
|
||||||
|
_revertIfShouldFail();
|
||||||
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||||
salt,
|
salt,
|
||||||
WETH_ADDRESS,
|
_getWethAddress(),
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
tokensBought
|
tokensBought
|
||||||
);
|
);
|
||||||
@ -144,10 +166,11 @@ contract TestERC20BridgeSamplerUniswapExchange is
|
|||||||
view
|
view
|
||||||
returns (uint256 ethBought)
|
returns (uint256 ethBought)
|
||||||
{
|
{
|
||||||
|
_revertIfShouldFail();
|
||||||
return LibDeterministicQuotes.getDeterministicSellQuote(
|
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||||
salt,
|
salt,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
WETH_ADDRESS,
|
_getWethAddress(),
|
||||||
tokensSold
|
tokensSold
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,9 +183,10 @@ contract TestERC20BridgeSamplerUniswapExchange is
|
|||||||
view
|
view
|
||||||
returns (uint256 tokensSold)
|
returns (uint256 tokensSold)
|
||||||
{
|
{
|
||||||
|
_revertIfShouldFail();
|
||||||
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||||
salt,
|
salt,
|
||||||
WETH_ADDRESS,
|
_getWethAddress(),
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
ethBought
|
ethBought
|
||||||
);
|
);
|
||||||
@ -172,7 +196,8 @@ contract TestERC20BridgeSamplerUniswapExchange is
|
|||||||
|
|
||||||
contract TestERC20BridgeSamplerKyberNetwork is
|
contract TestERC20BridgeSamplerKyberNetwork is
|
||||||
IKyberNetwork,
|
IKyberNetwork,
|
||||||
DeploymentConstants
|
DeploymentConstants,
|
||||||
|
FailTrigger
|
||||||
{
|
{
|
||||||
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
|
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
|
||||||
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
@ -187,8 +212,9 @@ contract TestERC20BridgeSamplerKyberNetwork is
|
|||||||
view
|
view
|
||||||
returns (uint256 expectedRate, uint256)
|
returns (uint256 expectedRate, uint256)
|
||||||
{
|
{
|
||||||
fromToken = fromToken == ETH_ADDRESS ? WETH_ADDRESS : fromToken;
|
_revertIfShouldFail();
|
||||||
toToken = toToken == ETH_ADDRESS ? WETH_ADDRESS : toToken;
|
fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
|
||||||
|
toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
|
||||||
expectedRate = LibDeterministicQuotes.getDeterministicRate(
|
expectedRate = LibDeterministicQuotes.getDeterministicRate(
|
||||||
SALT,
|
SALT,
|
||||||
fromToken,
|
fromToken,
|
||||||
@ -199,7 +225,8 @@ contract TestERC20BridgeSamplerKyberNetwork is
|
|||||||
|
|
||||||
|
|
||||||
contract TestERC20BridgeSamplerEth2Dai is
|
contract TestERC20BridgeSamplerEth2Dai is
|
||||||
IEth2Dai
|
IEth2Dai,
|
||||||
|
FailTrigger
|
||||||
{
|
{
|
||||||
bytes32 constant private SALT = 0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7;
|
bytes32 constant private SALT = 0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7;
|
||||||
|
|
||||||
@ -213,6 +240,7 @@ contract TestERC20BridgeSamplerEth2Dai is
|
|||||||
view
|
view
|
||||||
returns (uint256 buyAmount)
|
returns (uint256 buyAmount)
|
||||||
{
|
{
|
||||||
|
_revertIfShouldFail();
|
||||||
return LibDeterministicQuotes.getDeterministicSellQuote(
|
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||||
SALT,
|
SALT,
|
||||||
payToken,
|
payToken,
|
||||||
@ -231,6 +259,7 @@ contract TestERC20BridgeSamplerEth2Dai is
|
|||||||
view
|
view
|
||||||
returns (uint256 payAmount)
|
returns (uint256 payAmount)
|
||||||
{
|
{
|
||||||
|
_revertIfShouldFail();
|
||||||
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||||
SALT,
|
SALT,
|
||||||
payToken,
|
payToken,
|
||||||
@ -269,7 +298,8 @@ contract TestERC20BridgeSamplerUniswapExchangeFactory is
|
|||||||
|
|
||||||
|
|
||||||
contract TestERC20BridgeSampler is
|
contract TestERC20BridgeSampler is
|
||||||
ERC20BridgeSampler
|
ERC20BridgeSampler,
|
||||||
|
FailTrigger
|
||||||
{
|
{
|
||||||
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
|
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
|
||||||
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
TestERC20BridgeSamplerEth2Dai public eth2Dai;
|
||||||
@ -288,18 +318,30 @@ contract TestERC20BridgeSampler is
|
|||||||
uniswap.createTokenExchanges(tokenAddresses);
|
uniswap.createTokenExchanges(tokenAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `IExchange.getOrderInfo()`, overridden to return deterministic order infos.
|
// `IDevUtils.getOrderRelevantState()`, overridden to return deterministic
|
||||||
function getOrderInfo(LibOrder.Order memory order)
|
// states.
|
||||||
|
function getOrderRelevantState(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
bytes memory
|
||||||
|
)
|
||||||
public
|
public
|
||||||
pure
|
view
|
||||||
returns (LibOrder.OrderInfo memory orderInfo)
|
returns (
|
||||||
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
|
uint256 fillableTakerAssetAmount,
|
||||||
|
bool isValidSignature
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// The order hash is just the hash of the salt.
|
// The order hash is just the hash of the salt.
|
||||||
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
||||||
// Everything else is derived from the hash.
|
// Everything else is derived from the hash.
|
||||||
orderInfo.orderHash = orderHash;
|
orderInfo.orderHash = orderHash;
|
||||||
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
|
orderInfo.orderStatus = uint8(uint256(orderHash) % uint8(-1));
|
||||||
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
orderInfo.orderTakerAssetFilledAmount =
|
||||||
|
uint256(orderHash) % order.takerAssetAmount;
|
||||||
|
fillableTakerAssetAmount =
|
||||||
|
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
||||||
|
isValidSignature = uint256(orderHash) % 2 == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden to return deterministic decimals.
|
// Overriden to return deterministic decimals.
|
||||||
@ -312,38 +354,38 @@ contract TestERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Overriden to point to a this contract.
|
// Overriden to point to a this contract.
|
||||||
function _getExchangeContract()
|
function _getDevUtilsAddress()
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
returns (IExchange zeroex)
|
returns (address devUtilAddress)
|
||||||
{
|
{
|
||||||
return IExchange(address(this));
|
return address(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden to point to a custom contract.
|
// Overriden to point to a custom contract.
|
||||||
function _getEth2DaiContract()
|
function _getEth2DaiAddress()
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
returns (IEth2Dai eth2dai_)
|
returns (address eth2daiAddress)
|
||||||
{
|
{
|
||||||
return eth2Dai;
|
return address(eth2Dai);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden to point to a custom contract.
|
// Overriden to point to a custom contract.
|
||||||
function _getUniswapExchangeFactoryContract()
|
function _getUniswapExchangeFactoryAddress()
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
returns (IUniswapExchangeFactory uniswap_)
|
returns (address uniswapAddress)
|
||||||
{
|
{
|
||||||
return uniswap;
|
return address(uniswap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overriden to point to a custom contract.
|
// Overriden to point to a custom contract.
|
||||||
function _getKyberNetworkContract()
|
function _getKyberNetworkProxyAddress()
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
returns (IKyberNetwork kyber_)
|
returns (address kyberAddress)
|
||||||
{
|
{
|
||||||
return kyber;
|
return address(kyber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
|
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
"abis": "./test/generated-artifacts/@(DeploymentConstants|ERC20BridgeSampler|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -5,16 +5,16 @@
|
|||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
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 ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||||
|
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
|
||||||
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
|
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
|
||||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||||
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
||||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
|
||||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
DeploymentConstants: DeploymentConstants as ContractArtifact,
|
|
||||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||||
|
IDevUtils: IDevUtils as ContractArtifact,
|
||||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
getRandomPortion,
|
getRandomPortion,
|
||||||
randomAddress,
|
randomAddress,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { Order, OrderInfo } from '@0x/types';
|
import { Order } from '@0x/types';
|
||||||
import { BigNumber, hexUtils } from '@0x/utils';
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@ -30,12 +30,13 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const INVALID_ASSET_DATA = hexUtils.random(37);
|
const INVALID_ASSET_DATA = hexUtils.random(37);
|
||||||
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
||||||
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
||||||
const EMPTY_ORDERS_ERROR = 'EMPTY_ORDERS';
|
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
|
||||||
const UNSUPPORTED_ASSET_PROXY_ERROR = 'UNSUPPORTED_ASSET_PROXY';
|
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
|
||||||
const INVALID_ASSET_DATA_ERROR = 'INVALID_ASSET_DATA';
|
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
|
||||||
const UNSUPPORTED_UNISWAP_EXCHANGE_ERROR = 'UNSUPPORTED_UNISWAP_EXCHANGE';
|
const UNSUPPORTED_SOURCE_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_SOURCE';
|
||||||
const UNSUPPORTED_SOURCE_ERROR = 'UNSUPPORTED_SOURCE';
|
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
|
||||||
const INVALID_TOKEN_PAIR_ERROR = 'INVALID_TOKEN_PAIR';
|
const MAKER_TOKEN = randomAddress();
|
||||||
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||||
@ -192,13 +193,22 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
return quotes;
|
return quotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeterministicOrderInfo(order: Order): OrderInfo {
|
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
||||||
const hash = getPackedHash(hexUtils.leftPad(order.salt, 32));
|
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
||||||
return {
|
const orderStatus = new BigNumber(hash).mod(255).toNumber();
|
||||||
orderHash: hash,
|
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
||||||
orderStatus: new BigNumber(hash).mod(255).toNumber(),
|
if (orderStatus !== 3 || !isValidSignature) {
|
||||||
orderTakerAssetFilledAmount: new BigNumber(hash).mod(order.takerAssetAmount),
|
return constants.ZERO_AMOUNT;
|
||||||
};
|
}
|
||||||
|
return order.takerAssetAmount.minus(new BigNumber(hash).mod(order.takerAssetAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeterministicFillableMakerAssetAmount(order: Order): BigNumber {
|
||||||
|
const takerAmount = getDeterministicFillableTakerAssetAmount(order);
|
||||||
|
return order.makerAssetAmount
|
||||||
|
.times(takerAmount)
|
||||||
|
.div(order.takerAssetAmount)
|
||||||
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getERC20AssetData(tokenAddress: string): string {
|
function getERC20AssetData(tokenAddress: string): string {
|
||||||
@ -238,57 +248,115 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
|
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('queryOrders()', () => {
|
async function enableFailTriggerAsync(): Promise<void> {
|
||||||
const MAKER_TOKEN = randomAddress();
|
await testContract.enableFailTrigger().awaitTransactionSuccessAsync({ value: 1 });
|
||||||
const TAKER_TOKEN = randomAddress();
|
}
|
||||||
|
|
||||||
it('returns the results of `getOrderInfo()` for each order', async () => {
|
describe('getOrderFillableTakerAssetAmounts()', () => {
|
||||||
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const expected = orders.map(getDeterministicOrderInfo);
|
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
||||||
const actual = await testContract.queryOrders(orders).callAsync();
|
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||||
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns empty for no orders', async () => {
|
it('returns empty for no orders', async () => {
|
||||||
const actual = await testContract.queryOrders([]).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts([], []).callAsync();
|
||||||
expect(actual).to.deep.eq([]);
|
expect(actual).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
||||||
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
||||||
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero for an order with an empty signature', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
|
const signatures: string[] = _.times(orders.length, () => constants.NULL_BYTES);
|
||||||
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOrderFillableMakerAssetAmounts()', () => {
|
||||||
|
it('returns the expected amount for each order', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
|
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
||||||
|
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
||||||
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty for no orders', async () => {
|
||||||
|
const actual = await testContract.getOrderFillableMakerAssetAmounts([], []).callAsync();
|
||||||
|
expect(actual).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
||||||
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
||||||
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero for an order with an empty signature', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
|
const signatures: string[] = _.times(orders.length, () => constants.NULL_BYTES);
|
||||||
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('queryOrdersAndSampleSells()', () => {
|
describe('queryOrdersAndSampleSells()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const TAKER_TOKEN = randomAddress();
|
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the results of `getOrderInfo()` for each order', async () => {
|
it('returns expected fillable amounts for each order', async () => {
|
||||||
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
|
||||||
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
|
|
||||||
const [orderInfos] = await testContract
|
const [orderInfos] = await testContract
|
||||||
.queryOrdersAndSampleSells(orders, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(orderInfos).to.deep.eq(expectedOrderInfos);
|
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return quotes for all sources', async () => {
|
it('can return quotes for all sources', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
||||||
const [, quotes] = await testContract
|
const [, quotes] = await testContract
|
||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => allSources[n]), sampleAmounts)
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
|
||||||
sampleAmounts,
|
|
||||||
)
|
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no orders are passed in', async () => {
|
it('throws if no orders are passed in', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells([], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
|
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => allSources[n]), getSampleAmounts(TAKER_TOKEN))
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||||
});
|
});
|
||||||
@ -296,7 +364,8 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with an unsupported source', async () => {
|
it('throws with an unsupported source', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
ORDERS,
|
||||||
|
SIGNATURES,
|
||||||
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
|
[...SELL_SOURCES.map(n => allSources[n]), randomAddress()],
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -307,10 +376,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with non-ERC20 maker asset data', async () => {
|
it('throws with non-ERC20 maker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -321,10 +391,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with non-ERC20 taker asset data', async () => {
|
it('throws with non-ERC20 taker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -335,10 +406,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with invalid maker asset data', async () => {
|
it('throws with invalid maker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
makerAssetData: INVALID_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -349,10 +421,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with invalid taker asset data', async () => {
|
it('throws with invalid taker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleSells(
|
.queryOrdersAndSampleSells(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
takerAssetData: INVALID_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
SELL_SOURCES.map(n => allSources[n]),
|
SELL_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
getSampleAmounts(TAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -362,39 +435,34 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('queryOrdersAndSampleBuys()', () => {
|
describe('queryOrdersAndSampleBuys()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const TAKER_TOKEN = randomAddress();
|
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the results of `getOrderInfo()` for each order', async () => {
|
it('returns expected fillable amounts for each order', async () => {
|
||||||
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
|
||||||
const expectedOrderInfos = orders.map(getDeterministicOrderInfo);
|
|
||||||
const [orderInfos] = await testContract
|
const [orderInfos] = await testContract
|
||||||
.queryOrdersAndSampleBuys(orders, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), takerTokenAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(orderInfos).to.deep.eq(expectedOrderInfos);
|
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return quotes for all sources', async () => {
|
it('can return quotes for all sources', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
||||||
const [, quotes] = await testContract
|
const [, quotes] = await testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => allSources[n]), sampleAmounts)
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
|
||||||
sampleAmounts,
|
|
||||||
)
|
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no orders are passed in', async () => {
|
it('throws if no orders are passed in', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys([], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
|
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => allSources[n]), getSampleAmounts(MAKER_TOKEN))
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
||||||
});
|
});
|
||||||
@ -402,7 +470,8 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with an unsupported source', async () => {
|
it('throws with an unsupported source', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
ORDERS,
|
||||||
|
SIGNATURES,
|
||||||
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
|
[...BUY_SOURCES.map(n => allSources[n]), randomAddress()],
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -414,7 +483,8 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const sources = [...BUY_SOURCES, 'Kyber'];
|
const sources = [...BUY_SOURCES, 'Kyber'];
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN),
|
ORDERS,
|
||||||
|
SIGNATURES,
|
||||||
sources.map(n => allSources[n]),
|
sources.map(n => allSources[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -425,10 +495,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with non-ERC20 maker asset data', async () => {
|
it('throws with non-ERC20 maker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -439,10 +510,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with non-ERC20 taker asset data', async () => {
|
it('throws with non-ERC20 taker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -453,10 +525,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with invalid maker asset data', async () => {
|
it('throws with invalid maker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
makerAssetData: INVALID_ASSET_DATA,
|
makerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -467,10 +540,11 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('throws with invalid taker asset data', async () => {
|
it('throws with invalid taker asset data', async () => {
|
||||||
const tx = testContract
|
const tx = testContract
|
||||||
.queryOrdersAndSampleBuys(
|
.queryOrdersAndSampleBuys(
|
||||||
createOrders(MAKER_TOKEN, TAKER_TOKEN).map(o => ({
|
ORDERS.map(o => ({
|
||||||
...o,
|
...o,
|
||||||
takerAssetData: INVALID_ASSET_DATA,
|
takerAssetData: INVALID_ASSET_DATA,
|
||||||
})),
|
})),
|
||||||
|
SIGNATURES,
|
||||||
BUY_SOURCES.map(n => allSources[n]),
|
BUY_SOURCES.map(n => allSources[n]),
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
getSampleAmounts(MAKER_TOKEN),
|
||||||
)
|
)
|
||||||
@ -480,9 +554,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleSells()', () => {
|
describe('sampleSells()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -528,9 +599,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleBuys()', () => {
|
describe('sampleBuys()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -583,10 +651,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleSellsFromKyberNetwork()', () => {
|
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -601,7 +666,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return many quotes', async () => {
|
it('can quote token - token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
@ -610,6 +675,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote token -> ETH', async () => {
|
it('can quote token -> ETH', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
|
||||||
@ -619,6 +694,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> ETH fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote ETH -> token', async () => {
|
it('can quote ETH -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
@ -627,12 +712,19 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if ETH -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleSellsFromEth2Dai()', () => {
|
blockchainTests.resets('sampleSellsFromEth2Dai()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -647,7 +739,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return many quotes', async () => {
|
it('can quote token -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
@ -656,6 +748,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote token -> ETH', async () => {
|
it('can quote token -> ETH', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
|
||||||
@ -665,6 +767,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> ETH fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote ETH -> token', async () => {
|
it('can quote ETH -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
@ -673,12 +785,19 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if ETH -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleBuysFromEth2Dai()', () => {
|
blockchainTests.resets('sampleBuysFromEth2Dai()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -693,7 +812,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return many quotes', async () => {
|
it('can quote token -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
@ -702,6 +821,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromEth2Dai(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote token -> ETH', async () => {
|
it('can quote token -> ETH', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Eth2Dai'], sampleAmounts);
|
||||||
@ -711,6 +840,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> ETH fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromEth2Dai(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote ETH -> token', async () => {
|
it('can quote ETH -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Eth2Dai'], sampleAmounts);
|
||||||
@ -719,12 +858,19 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
.callAsync();
|
.callAsync();
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if ETH -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromEth2Dai(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleSellsFromUniswap()', () => {
|
blockchainTests.resets('sampleSellsFromUniswap()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -739,7 +885,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return many quotes', async () => {
|
it('can quote token -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
@ -748,6 +894,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote token -> ETH', async () => {
|
it('can quote token -> ETH', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
|
||||||
@ -757,6 +913,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> ETH fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote ETH -> token', async () => {
|
it('can quote ETH -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
@ -766,27 +932,38 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no exchange exists for the maker token', async () => {
|
it('returns zero if ETH -> token fails', async () => {
|
||||||
const nonExistantToken = randomAddress();
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const tx = testContract
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no exchange exists for the taker token', async () => {
|
it('returns zero if no exchange exists for the maker token', async () => {
|
||||||
const nonExistantToken = randomAddress();
|
const nonExistantToken = randomAddress();
|
||||||
const tx = testContract
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero if no exchange exists for the taker token', async () => {
|
||||||
|
const nonExistantToken = randomAddress();
|
||||||
|
const sampleAmounts = getSampleAmounts(nonExistantToken);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleSellsFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sampleBuysFromUniswap()', () => {
|
blockchainTests.resets('sampleBuysFromUniswap()', () => {
|
||||||
const MAKER_TOKEN = randomAddress();
|
|
||||||
const TAKER_TOKEN = randomAddress();
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
@ -801,7 +978,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can return many quotes', async () => {
|
it('can quote token -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
@ -810,6 +987,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote token -> ETH', async () => {
|
it('can quote token -> ETH', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
|
||||||
@ -819,6 +1006,16 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> ETH fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
it('can quote ETH -> token', async () => {
|
it('can quote ETH -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
|
||||||
@ -828,20 +1025,34 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no exchange exists for the maker token', async () => {
|
it('returns zero if ETH -> token fails', async () => {
|
||||||
const nonExistantToken = randomAddress();
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
const tx = testContract
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, getSampleAmounts(TAKER_TOKEN))
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if no exchange exists for the taker token', async () => {
|
it('returns zero if no exchange exists for the maker token', async () => {
|
||||||
const nonExistantToken = randomAddress();
|
const nonExistantToken = randomAddress();
|
||||||
const tx = testContract
|
const sampleAmounts = getSampleAmounts(nonExistantToken);
|
||||||
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, getSampleAmounts(nonExistantToken))
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(TAKER_TOKEN, nonExistantToken, sampleAmounts)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_UNISWAP_EXCHANGE_ERROR);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero if no exchange exists for the taker token', async () => {
|
||||||
|
const nonExistantToken = randomAddress();
|
||||||
|
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromUniswap(nonExistantToken, MAKER_TOKEN, sampleAmounts)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
* 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/erc20_bridge_sampler';
|
||||||
|
export * from '../test/generated-wrappers/i_dev_utils';
|
||||||
export * from '../test/generated-wrappers/i_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_eth2_dai';
|
||||||
export * from '../test/generated-wrappers/i_kyber_network';
|
export * from '../test/generated-wrappers/i_kyber_network';
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/ERC20BridgeSampler.json",
|
"generated-artifacts/ERC20BridgeSampler.json",
|
||||||
"generated-artifacts/IERC20BridgeSampler.json",
|
"generated-artifacts/IERC20BridgeSampler.json",
|
||||||
"test/generated-artifacts/DeploymentConstants.json",
|
|
||||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||||
|
"test/generated-artifacts/IDevUtils.json",
|
||||||
"test/generated-artifacts/IERC20BridgeSampler.json",
|
"test/generated-artifacts/IERC20BridgeSampler.json",
|
||||||
"test/generated-artifacts/IEth2Dai.json",
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
"test/generated-artifacts/IKyberNetwork.json",
|
"test/generated-artifacts/IKyberNetwork.json",
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "4.0.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add `DEV_UTILS_ADDRESS` and `KYBER_ETH_ADDRESS` to `DeploymentConstants`.",
|
||||||
|
"pr": 2365
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1575931811,
|
"timestamp": 1575931811,
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
|
@ -38,6 +38,10 @@ contract DeploymentConstants {
|
|||||||
address constant private DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
|
address constant private DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
|
||||||
/// @dev Mainnet address of the `Chai` contract
|
/// @dev Mainnet address of the `Chai` contract
|
||||||
address constant private CHAI_ADDRESS = 0x06AF07097C9Eeb7fD685c692751D5C66dB49c215;
|
address constant private CHAI_ADDRESS = 0x06AF07097C9Eeb7fD685c692751D5C66dB49c215;
|
||||||
|
/// @dev Address of the 0x DevUtils contract.
|
||||||
|
address constant private DEV_UTILS_ADDRESS = 0xcCc2431a7335F21d9268bA62F0B32B0f2EFC463f;
|
||||||
|
/// @dev Kyber ETH pseudo-address.
|
||||||
|
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
/// @dev Overridable way to get the `KyberNetworkProxy` address.
|
/// @dev Overridable way to get the `KyberNetworkProxy` address.
|
||||||
/// @return kyberAddress The `IKyberNetworkProxy` address.
|
/// @return kyberAddress The `IKyberNetworkProxy` address.
|
||||||
@ -108,4 +112,14 @@ contract DeploymentConstants {
|
|||||||
{
|
{
|
||||||
return CHAI_ADDRESS;
|
return CHAI_ADDRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev An overridable way to retrieve the 0x `DevUtils` contract address.
|
||||||
|
/// @return devUtils The 0x `DevUtils` contract address.
|
||||||
|
function _getDevUtilsAddress()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (address devUtils)
|
||||||
|
{
|
||||||
|
return DEV_UTILS_ADDRESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user