@0x/contracts-erc20-bridge-sampler
: Remove wrapper functions and introduce batchCall()
.
This commit is contained in:
committed by
Lawrence Forman
parent
1b83ebdf89
commit
6273a1ca73
@@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Catch reverts to `DevUtils` calls",
|
"note": "Catch reverts to `DevUtils` calls",
|
||||||
"pr": 2476
|
"pr": 2476
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove wrapper functions and introduce `batchCall()`",
|
||||||
|
"pr": 2477
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": 1581204851
|
"timestamp": 1581204851
|
||||||
|
@@ -21,7 +21,6 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
|
||||||
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-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-exchange-libs/contracts/src/LibMath.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||||
@@ -36,149 +35,31 @@ contract ERC20BridgeSampler is
|
|||||||
IERC20BridgeSampler,
|
IERC20BridgeSampler,
|
||||||
DeploymentConstants
|
DeploymentConstants
|
||||||
{
|
{
|
||||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
/// @dev Gas limit for DevUtils calls.
|
||||||
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 1500e3;
|
uint256 constant internal DEV_UTILS_CALL_GAS = 500e3; // 500k
|
||||||
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
/// @dev Gas limit for Kyber calls.
|
||||||
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 1000e3;
|
uint256 constant internal KYBER_CALL_GAS = 1500e3; // 1.5m
|
||||||
uint256 constant internal DEV_UTILS_CALL_GAS = 500e3;
|
/// @dev Gas limit for Uniswap calls.
|
||||||
address constant private UNISWAP_SOURCE = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
|
uint256 constant internal UNISWAP_CALL_GAS = 150e3; // 150k
|
||||||
address constant private ETH2DAI_SOURCE = 0x39755357759cE0d7f32dC8dC45414CCa409AE24e;
|
/// @dev Base gas limit for Eth2Dai calls.
|
||||||
address constant private KYBER_SOURCE = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
|
uint256 constant internal ETH2DAI_CALL_GAS = 1000e3; // 1m
|
||||||
|
|
||||||
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Call multiple public functions on this contract in a single transaction.
|
||||||
/// @param orders Batches of Native orders to query.
|
/// @param callDatas ABI-encoded call data for each function call.
|
||||||
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
/// @return callResults ABI-encoded results data for each call.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
function batchCall(bytes[] calldata callDatas)
|
||||||
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
external
|
||||||
/// @return ordersAndSamples How much taker asset can be filled
|
|
||||||
/// by each order in `orders`. Maker amounts bought for each source at
|
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryBatchOrdersAndSampleSells(
|
|
||||||
LibOrder.Order[][] memory orders,
|
|
||||||
bytes[][] memory orderSignatures,
|
|
||||||
address[] memory sources,
|
|
||||||
uint256[][] memory takerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
view
|
||||||
returns (
|
returns (bytes[] memory callResults)
|
||||||
OrdersAndSample[] memory ordersAndSamples
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
ordersAndSamples = new OrdersAndSample[](orders.length);
|
callResults = new bytes[](callDatas.length);
|
||||||
for (uint256 i = 0; i != orders.length; i++) {
|
for (uint256 i = 0; i != callDatas.length; ++i) {
|
||||||
(
|
(bool didSucceed, bytes memory resultData) = address(this).staticcall(callDatas[i]);
|
||||||
uint256[] memory orderFillableAssetAmounts,
|
if (!didSucceed) {
|
||||||
uint256[][] memory tokenAmountsBySource
|
assembly { revert(add(resultData, 0x20), mload(resultData)) }
|
||||||
) = queryOrdersAndSampleSells(orders[i], orderSignatures[i], sources, takerTokenAmounts[i]);
|
|
||||||
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
|
|
||||||
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
|
|
||||||
}
|
}
|
||||||
|
callResults[i] = resultData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
|
||||||
/// @param orders Batches of Native orders to query.
|
|
||||||
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
|
||||||
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
|
||||||
/// @return ordersAndSamples How much taker asset can be filled
|
|
||||||
/// by each order in `orders`. Taker amounts sold for each source at
|
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryBatchOrdersAndSampleBuys(
|
|
||||||
LibOrder.Order[][] memory orders,
|
|
||||||
bytes[][] memory orderSignatures,
|
|
||||||
address[] memory sources,
|
|
||||||
uint256[][] memory makerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
OrdersAndSample[] memory ordersAndSamples
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ordersAndSamples = new OrdersAndSample[](orders.length);
|
|
||||||
for (uint256 i = 0; i != orders.length; i++) {
|
|
||||||
(
|
|
||||||
uint256[] memory orderFillableAssetAmounts,
|
|
||||||
uint256[][] memory tokenAmountsBySource
|
|
||||||
) = queryOrdersAndSampleBuys(orders[i], orderSignatures[i], sources, makerTokenAmounts[i]);
|
|
||||||
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
|
|
||||||
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
|
||||||
/// @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 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
|
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryOrdersAndSampleSells(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
bytes[] memory orderSignatures,
|
|
||||||
address[] memory sources,
|
|
||||||
uint256[] memory takerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256[] memory orderFillableTakerAssetAmounts,
|
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
|
||||||
)
|
|
||||||
{
|
|
||||||
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
|
|
||||||
orderFillableTakerAssetAmounts = getOrderFillableTakerAssetAmounts(
|
|
||||||
orders,
|
|
||||||
orderSignatures
|
|
||||||
);
|
|
||||||
makerTokenAmountsBySource = sampleSells(
|
|
||||||
sources,
|
|
||||||
_assetDataToTokenAddress(orders[0].takerAssetData),
|
|
||||||
_assetDataToTokenAddress(orders[0].makerAssetData),
|
|
||||||
takerTokenAmounts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
|
||||||
/// @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 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
|
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryOrdersAndSampleBuys(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
bytes[] memory orderSignatures,
|
|
||||||
address[] memory sources,
|
|
||||||
uint256[] memory makerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256[] memory orderFillableMakerAssetAmounts,
|
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
|
||||||
)
|
|
||||||
{
|
|
||||||
require(orders.length != 0, "ERC20BridgeSampler/EMPTY_ORDERS");
|
|
||||||
orderFillableMakerAssetAmounts = getOrderFillableMakerAssetAmounts(
|
|
||||||
orders,
|
|
||||||
orderSignatures
|
|
||||||
);
|
|
||||||
makerTokenAmountsBySource = sampleBuys(
|
|
||||||
sources,
|
|
||||||
_assetDataToTokenAddress(orders[0].takerAssetData),
|
|
||||||
_assetDataToTokenAddress(orders[0].makerAssetData),
|
|
||||||
makerTokenAmounts
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
@@ -270,66 +151,6 @@ contract ERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Sample sell quotes on multiple DEXes at once.
|
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
|
||||||
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function sampleSells(
|
|
||||||
address[] memory sources,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] memory takerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256[][] memory makerTokenAmountsBySource)
|
|
||||||
{
|
|
||||||
uint256 numSources = sources.length;
|
|
||||||
makerTokenAmountsBySource = new uint256[][](numSources);
|
|
||||||
for (uint256 i = 0; i < numSources; i++) {
|
|
||||||
makerTokenAmountsBySource[i] = _sampleSellSource(
|
|
||||||
sources[i],
|
|
||||||
takerToken,
|
|
||||||
makerToken,
|
|
||||||
takerTokenAmounts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
|
||||||
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function sampleBuys(
|
|
||||||
address[] memory sources,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] memory makerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256[][] memory takerTokenAmountsBySource)
|
|
||||||
{
|
|
||||||
uint256 numSources = sources.length;
|
|
||||||
takerTokenAmountsBySource = new uint256[][](numSources);
|
|
||||||
for (uint256 i = 0; i < numSources; i++) {
|
|
||||||
takerTokenAmountsBySource[i] = _sampleBuySource(
|
|
||||||
sources[i],
|
|
||||||
takerToken,
|
|
||||||
makerToken,
|
|
||||||
makerTokenAmounts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Kyber.
|
/// @dev Sample sell quotes from Kyber.
|
||||||
/// @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).
|
||||||
@@ -354,7 +175,7 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_SAMPLE_CALL_GAS)(
|
_getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
IKyberNetwork(0).getExpectedRate.selector,
|
IKyberNetwork(0).getExpectedRate.selector,
|
||||||
_takerToken,
|
_takerToken,
|
||||||
@@ -396,7 +217,7 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
|
_getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
IEth2Dai(0).getBuyAmount.selector,
|
IEth2Dai(0).getBuyAmount.selector,
|
||||||
makerToken,
|
makerToken,
|
||||||
@@ -433,7 +254,7 @@ contract ERC20BridgeSampler is
|
|||||||
takerTokenAmounts = new uint256[](numSamples);
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
(bool didSucceed, bytes memory resultData) =
|
(bool didSucceed, bytes memory resultData) =
|
||||||
_getEth2DaiAddress().staticcall.gas(ETH2DAI_SAMPLE_CALL_GAS)(
|
_getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
IEth2Dai(0).getPayAmount.selector,
|
IEth2Dai(0).getPayAmount.selector,
|
||||||
takerToken,
|
takerToken,
|
||||||
@@ -599,7 +420,7 @@ contract ERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
bytes memory resultData;
|
bytes memory resultData;
|
||||||
(didSucceed, resultData) =
|
(didSucceed, resultData) =
|
||||||
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
functionSelector,
|
functionSelector,
|
||||||
inputAmount
|
inputAmount
|
||||||
@@ -609,59 +430,6 @@ contract ERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Samples a supported sell source, defined by its address.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
|
||||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
|
||||||
/// amount.
|
|
||||||
function _sampleSellSource(
|
|
||||||
address source,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] memory takerTokenAmounts
|
|
||||||
)
|
|
||||||
private
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts)
|
|
||||||
{
|
|
||||||
if (source == ETH2DAI_SOURCE) {
|
|
||||||
return sampleSellsFromEth2Dai(takerToken, makerToken, takerTokenAmounts);
|
|
||||||
}
|
|
||||||
if (source == UNISWAP_SOURCE) {
|
|
||||||
return sampleSellsFromUniswap(takerToken, makerToken, takerTokenAmounts);
|
|
||||||
}
|
|
||||||
if (source == KYBER_SOURCE) {
|
|
||||||
return sampleSellsFromKyberNetwork(takerToken, makerToken, takerTokenAmounts);
|
|
||||||
}
|
|
||||||
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Samples a supported buy source, defined by its address.
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
|
||||||
/// amount.
|
|
||||||
function _sampleBuySource(
|
|
||||||
address source,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] memory makerTokenAmounts
|
|
||||||
)
|
|
||||||
private
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts)
|
|
||||||
{
|
|
||||||
if (source == ETH2DAI_SOURCE) {
|
|
||||||
return sampleBuysFromEth2Dai(takerToken, makerToken, makerTokenAmounts);
|
|
||||||
}
|
|
||||||
if (source == UNISWAP_SOURCE) {
|
|
||||||
return sampleBuysFromUniswap(takerToken, makerToken, makerTokenAmounts);
|
|
||||||
}
|
|
||||||
revert("ERC20BridgeSampler/UNSUPPORTED_SOURCE");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Retrive an existing Uniswap exchange contract.
|
/// @dev Retrive an existing Uniswap exchange contract.
|
||||||
/// Throws if the exchange does not exist.
|
/// Throws if the exchange does not exist.
|
||||||
/// @param tokenAddress Address of the token contract.
|
/// @param tokenAddress Address of the token contract.
|
||||||
@@ -677,23 +445,9 @@ contract ERC20BridgeSampler is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Extract the token address from ERC20 proxy asset data.
|
/// @dev Assert that the tokens in a trade pair are valid.
|
||||||
/// @param assetData ERC20 asset data.
|
/// @param makerToken Address of the maker token.
|
||||||
/// @return tokenAddress The decoded token address.
|
/// @param takerToken Address of the taker token.
|
||||||
function _assetDataToTokenAddress(bytes memory assetData)
|
|
||||||
private
|
|
||||||
pure
|
|
||||||
returns (address tokenAddress)
|
|
||||||
{
|
|
||||||
require(assetData.length == 36, "ERC20BridgeSampler/INVALID_ASSET_DATA");
|
|
||||||
bytes4 selector;
|
|
||||||
assembly {
|
|
||||||
selector := and(mload(add(assetData, 0x20)), 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
|
|
||||||
tokenAddress := mload(add(assetData, 0x24))
|
|
||||||
}
|
|
||||||
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
|
||||||
|
@@ -23,98 +23,14 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|||||||
|
|
||||||
|
|
||||||
interface IERC20BridgeSampler {
|
interface IERC20BridgeSampler {
|
||||||
struct OrdersAndSample {
|
|
||||||
uint256[] orderFillableAssetAmounts;
|
|
||||||
uint256[][] tokenAmountsBySource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Call multiple public functions on this contract in a single transaction.
|
||||||
/// @param orders Batches of Native orders to query.
|
/// @param callDatas ABI-encoded call data for each function call.
|
||||||
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
/// @return callResults ABI-encoded results data for each call.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
function batchCall(bytes[] calldata callDatas)
|
||||||
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
|
||||||
/// @return ordersAndSamples How much taker asset can be filled
|
|
||||||
/// by each order in `orders`. Maker amounts bought for each source at
|
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryBatchOrdersAndSampleSells(
|
|
||||||
LibOrder.Order[][] calldata orders,
|
|
||||||
bytes[][] calldata orderSignatures,
|
|
||||||
address[] calldata sources,
|
|
||||||
uint256[][] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (
|
returns (bytes[] memory callResults);
|
||||||
OrdersAndSample[] memory ordersAndSamples
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
|
||||||
/// @param orders Batches of Native orders to query.
|
|
||||||
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
|
||||||
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
|
||||||
/// @return ordersAndSamples How much taker asset can be filled
|
|
||||||
/// by each order in `orders`. Taker amounts sold for each source at
|
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
|
||||||
/// index
|
|
||||||
function queryBatchOrdersAndSampleBuys(
|
|
||||||
LibOrder.Order[][] calldata orders,
|
|
||||||
bytes[][] calldata orderSignatures,
|
|
||||||
address[] calldata sources,
|
|
||||||
uint256[][] calldata makerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
OrdersAndSample[] memory ordersAndSamples
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
|
||||||
/// @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 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
|
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryOrdersAndSampleSells(
|
|
||||||
LibOrder.Order[] calldata orders,
|
|
||||||
bytes[] calldata orderSignatures,
|
|
||||||
address[] calldata sources,
|
|
||||||
uint256[] calldata takerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256[] memory orderFillableTakerAssetAmounts,
|
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
|
||||||
/// @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 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
|
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
|
||||||
/// index.
|
|
||||||
function queryOrdersAndSampleBuys(
|
|
||||||
LibOrder.Order[] calldata orders,
|
|
||||||
bytes[] calldata orderSignatures,
|
|
||||||
address[] calldata sources,
|
|
||||||
uint256[] calldata makerTokenAmounts
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256[] memory orderFillableMakerAssetAmounts,
|
|
||||||
uint256[][] memory makerTokenAmountsBySource
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
@@ -142,39 +58,78 @@ interface IERC20BridgeSampler {
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory orderFillableMakerAssetAmounts);
|
returns (uint256[] memory orderFillableMakerAssetAmounts);
|
||||||
|
|
||||||
/// @dev Sample sell quotes on multiple DEXes at once.
|
/// @dev Sample sell quotes from Kyber.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
|
||||||
/// @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).
|
||||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
/// @return makerTokenAmountsBySource Maker amounts bought for each source at
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
/// each taker token amount. First indexed by source index, then sample
|
/// amount.
|
||||||
/// index.
|
function sampleSellsFromKyberNetwork(
|
||||||
function sampleSells(
|
|
||||||
address[] calldata sources,
|
|
||||||
address takerToken,
|
address takerToken,
|
||||||
address makerToken,
|
address makerToken,
|
||||||
uint256[] calldata takerTokenAmounts
|
uint256[] calldata takerTokenAmounts
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (uint256[][] memory makerTokenAmountsBySource);
|
returns (uint256[] memory makerTokenAmounts);
|
||||||
|
|
||||||
/// @dev Query native orders and sample buy quotes on multiple DEXes at once.
|
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
||||||
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
|
||||||
/// @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).
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
/// @return takerTokenAmountsBySource Taker amounts sold for each source at
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
/// each maker token amount. First indexed by source index, then sample
|
/// amount.
|
||||||
/// index.
|
function sampleSellsFromEth2Dai(
|
||||||
function sampleBuys(
|
address takerToken,
|
||||||
address[] calldata sources,
|
address makerToken,
|
||||||
|
uint256[] calldata takerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts);
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Uniswap.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromUniswap(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] calldata takerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts);
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Uniswap.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromUniswap(
|
||||||
address takerToken,
|
address takerToken,
|
||||||
address makerToken,
|
address makerToken,
|
||||||
uint256[] calldata makerTokenAmounts
|
uint256[] calldata makerTokenAmounts
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
returns (uint256[][] memory takerTokenAmountsBySource);
|
returns (uint256[] memory takerTokenAmounts);
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Eth2Dai/Oasis.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Maker token sell amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromEth2Dai(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] calldata makerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts);
|
||||||
}
|
}
|
||||||
|
@@ -327,7 +327,6 @@ contract TestERC20BridgeSampler is
|
|||||||
bytes memory
|
bytes memory
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
|
||||||
returns (
|
returns (
|
||||||
LibOrder.OrderInfo memory orderInfo,
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
uint256 fillableTakerAssetAmount,
|
uint256 fillableTakerAssetAmount,
|
||||||
|
@@ -25,19 +25,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
|
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
|
||||||
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
|
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
|
||||||
const ERC20_PROXY_ID = '0xf47261b0';
|
const ERC20_PROXY_ID = '0xf47261b0';
|
||||||
const INVALID_ASSET_PROXY_ASSET_DATA = hexUtils.concat('0xf47261b1', hexUtils.leftPad(randomAddress()));
|
|
||||||
const INVALID_ASSET_DATA = hexUtils.random(37);
|
|
||||||
const SELL_SOURCES = ['Eth2Dai', 'Kyber', 'Uniswap'];
|
|
||||||
const BUY_SOURCES = ['Eth2Dai', 'Uniswap'];
|
|
||||||
const SOURCE_IDS: { [source: string]: string } = {
|
|
||||||
Uniswap: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95',
|
|
||||||
Eth2Dai: '0x39755357759ce0d7f32dc8dc45414cca409ae24e',
|
|
||||||
Kyber: '0x818e6fecd516ecc3849daf6845e3ec868087b755',
|
|
||||||
};
|
|
||||||
const EMPTY_ORDERS_ERROR = 'ERC20BridgeSampler/EMPTY_ORDERS';
|
|
||||||
const UNSUPPORTED_ASSET_PROXY_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_ASSET_PROXY';
|
|
||||||
const INVALID_ASSET_DATA_ERROR = 'ERC20BridgeSampler/INVALID_ASSET_DATA';
|
|
||||||
const UNSUPPORTED_SOURCE_ERROR = 'ERC20BridgeSampler/UNSUPPORTED_SOURCE';
|
|
||||||
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
|
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
|
||||||
const MAKER_TOKEN = randomAddress();
|
const MAKER_TOKEN = randomAddress();
|
||||||
const TAKER_TOKEN = randomAddress();
|
const TAKER_TOKEN = randomAddress();
|
||||||
@@ -324,329 +311,6 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('queryOrdersAndSampleSells()', () => {
|
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns expected fillable amounts for each order', async () => {
|
|
||||||
const takerTokenAmounts = getSampleAmounts(TAKER_TOKEN);
|
|
||||||
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableTakerAssetAmount);
|
|
||||||
const [orderInfos] = await testContract
|
|
||||||
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => SOURCE_IDS[n]), takerTokenAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can return quotes for all sources', async () => {
|
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
|
||||||
const [, quotes] = await testContract
|
|
||||||
.queryOrdersAndSampleSells(ORDERS, SIGNATURES, SELL_SOURCES.map(n => SOURCE_IDS[n]), sampleAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if no orders are passed in', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleSells([], [], SELL_SOURCES.map(n => SOURCE_IDS[n]), getSampleAmounts(TAKER_TOKEN))
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with an unsupported source', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleSells(
|
|
||||||
ORDERS,
|
|
||||||
SIGNATURES,
|
|
||||||
[...SELL_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with non-ERC20 maker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleSells(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with non-ERC20 taker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleSells(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with invalid maker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleSells(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
makerAssetData: INVALID_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with invalid taker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleSells(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
takerAssetData: INVALID_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
SELL_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('queryOrdersAndSampleBuys()', () => {
|
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns expected fillable amounts for each order', async () => {
|
|
||||||
const takerTokenAmounts = getSampleAmounts(MAKER_TOKEN);
|
|
||||||
const expectedFillableAmounts = ORDERS.map(getDeterministicFillableMakerAssetAmount);
|
|
||||||
const [orderInfos] = await testContract
|
|
||||||
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => SOURCE_IDS[n]), takerTokenAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(orderInfos).to.deep.eq(expectedFillableAmounts);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can return quotes for all sources', async () => {
|
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
|
||||||
const [, quotes] = await testContract
|
|
||||||
.queryOrdersAndSampleBuys(ORDERS, SIGNATURES, BUY_SOURCES.map(n => SOURCE_IDS[n]), sampleAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if no orders are passed in', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys([], [], BUY_SOURCES.map(n => SOURCE_IDS[n]), getSampleAmounts(MAKER_TOKEN))
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(EMPTY_ORDERS_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with an unsupported source', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys(
|
|
||||||
ORDERS,
|
|
||||||
SIGNATURES,
|
|
||||||
[...BUY_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if kyber is passed in as a source', async () => {
|
|
||||||
const sources = [...BUY_SOURCES, 'Kyber'];
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys(
|
|
||||||
ORDERS,
|
|
||||||
SIGNATURES,
|
|
||||||
sources.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with non-ERC20 maker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
makerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with non-ERC20 taker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
takerAssetData: INVALID_ASSET_PROXY_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_ASSET_PROXY_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with invalid maker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
makerAssetData: INVALID_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with invalid taker asset data', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.queryOrdersAndSampleBuys(
|
|
||||||
ORDERS.map(o => ({
|
|
||||||
...o,
|
|
||||||
takerAssetData: INVALID_ASSET_DATA,
|
|
||||||
})),
|
|
||||||
SIGNATURES,
|
|
||||||
BUY_SOURCES.map(n => SOURCE_IDS[n]),
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(INVALID_ASSET_DATA_ERROR);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sampleSells()', () => {
|
|
||||||
before(async () => {
|
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty quotes with no sample amounts', async () => {
|
|
||||||
const emptyQuotes = _.times(SELL_SOURCES.length, () => []);
|
|
||||||
const quotes = await testContract
|
|
||||||
.sampleSells(SELL_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(emptyQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can return quotes for all sources', async () => {
|
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, SELL_SOURCES, sampleAmounts);
|
|
||||||
const quotes = await testContract
|
|
||||||
.sampleSells(SELL_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can return quotes for some sources', async () => {
|
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
|
||||||
const sources = _.sampleSize(SELL_SOURCES, 1);
|
|
||||||
const expectedQuotes = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
|
||||||
const quotes = await testContract
|
|
||||||
.sampleSells(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with an unsupported source', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.sampleSells(
|
|
||||||
[...SELL_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
|
||||||
TAKER_TOKEN,
|
|
||||||
MAKER_TOKEN,
|
|
||||||
getSampleAmounts(TAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sampleBuys()', () => {
|
|
||||||
before(async () => {
|
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty quotes with no sample amounts', async () => {
|
|
||||||
const emptyQuotes = _.times(BUY_SOURCES.length, () => []);
|
|
||||||
const quotes = await testContract
|
|
||||||
.sampleBuys(BUY_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, [])
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(emptyQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can return quotes for all sources', async () => {
|
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, BUY_SOURCES, sampleAmounts);
|
|
||||||
const quotes = await testContract
|
|
||||||
.sampleBuys(BUY_SOURCES.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can return quotes for some sources', async () => {
|
|
||||||
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
|
|
||||||
const sources = _.sampleSize(BUY_SOURCES, 1);
|
|
||||||
const expectedQuotes = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, sources, sampleAmounts);
|
|
||||||
const quotes = await testContract
|
|
||||||
.sampleBuys(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
|
|
||||||
.callAsync();
|
|
||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws with an unsupported source', async () => {
|
|
||||||
const tx = testContract
|
|
||||||
.sampleBuys(
|
|
||||||
[...BUY_SOURCES.map(n => SOURCE_IDS[n]), randomAddress()],
|
|
||||||
TAKER_TOKEN,
|
|
||||||
MAKER_TOKEN,
|
|
||||||
getSampleAmounts(MAKER_TOKEN),
|
|
||||||
)
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('throws if kyber is passed in as a source', async () => {
|
|
||||||
const sources = [...BUY_SOURCES, 'Kyber'];
|
|
||||||
const tx = testContract
|
|
||||||
.sampleBuys(sources.map(n => SOURCE_IDS[n]), TAKER_TOKEN, MAKER_TOKEN, getSampleAmounts(MAKER_TOKEN))
|
|
||||||
.callAsync();
|
|
||||||
return expect(tx).to.revertWith(UNSUPPORTED_SOURCE_ERROR);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
|
blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
@@ -1051,4 +715,65 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq(expectedQuotes);
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('batchCall()', () => {
|
||||||
|
it('can call one function', async () => {
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
|
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||||
|
const calls = [
|
||||||
|
testContract.getOrderFillableTakerAssetAmounts(orders, signatures).getABIEncodedTransactionData(),
|
||||||
|
];
|
||||||
|
const r = await testContract.batchCall(calls).callAsync();
|
||||||
|
expect(r).to.be.length(1);
|
||||||
|
const actual = testContract.getABIDecodedReturnData<BigNumber[]>('getOrderFillableTakerAssetAmounts', r[0]);
|
||||||
|
expect(actual).to.deep.eq(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can call two functions', async () => {
|
||||||
|
const numOrders = _.random(1, 10);
|
||||||
|
const orders = _.times(2, () => createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders));
|
||||||
|
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
|
||||||
|
const expecteds = [
|
||||||
|
orders[0].map(getDeterministicFillableTakerAssetAmount),
|
||||||
|
orders[1].map(getDeterministicFillableMakerAssetAmount),
|
||||||
|
];
|
||||||
|
const calls = [
|
||||||
|
testContract.getOrderFillableTakerAssetAmounts(orders[0], signatures).getABIEncodedTransactionData(),
|
||||||
|
testContract.getOrderFillableMakerAssetAmounts(orders[1], signatures).getABIEncodedTransactionData(),
|
||||||
|
];
|
||||||
|
const r = await testContract.batchCall(calls).callAsync();
|
||||||
|
expect(r).to.be.length(2);
|
||||||
|
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0])).to.deep.eq(
|
||||||
|
expecteds[0],
|
||||||
|
);
|
||||||
|
expect(testContract.getABIDecodedReturnData('getOrderFillableMakerAssetAmounts', r[1])).to.deep.eq(
|
||||||
|
expecteds[1],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can make recursive calls', async () => {
|
||||||
|
const numOrders = _.random(1, 10);
|
||||||
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders);
|
||||||
|
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
|
||||||
|
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||||
|
let r = await testContract
|
||||||
|
.batchCall([
|
||||||
|
testContract
|
||||||
|
.batchCall([
|
||||||
|
testContract
|
||||||
|
.getOrderFillableTakerAssetAmounts(orders, signatures)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
])
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
])
|
||||||
|
.callAsync();
|
||||||
|
expect(r).to.be.length(1);
|
||||||
|
r = testContract.getABIDecodedReturnData<string[]>('batchCall', r[0]);
|
||||||
|
expect(r).to.be.length(1);
|
||||||
|
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0])).to.deep.eq(
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user