protocol/packages/asset-swapper/test/contracts/erc20_bridge_sampler_test.ts
Lawrence Forman 470e9a4697
AS: Balancer V2 batchSwap (#462)
* Draft. PoC pseudo code showing general idea for resuing SOR path creation logic and adding multihop support.

* Add actual Balancer SDK function calls.

* Update to handle buys.

* Correct taker>maker for buy.

* Draft. PoC pseudo code showing general idea for resuing SOR path creation logic and adding multihop support.

* make it build

* rebase

* add BalancerV2Batch protocol

* add BalancerV2Batch protocol

* get balancer v2 multihop working

* fix BalancerV2Batch for sells (buys still iffy)

* fix buys, appease linter and prettier

* remove unused RPC URL from balancer sdk construction

* update changelogs

* clean up comments
add event loop yield in `BalancerV2SwapInfoCache.loadTopPools()`

* add negative result check on balancerv2batch swap output

* compiler hack

* reintroduce CompilerHack

* delete unused multibridge sampler

* remove compilerhack

* reintroduce compilerhack

* try to fix CI compile errors

* plz work

* plz work

* pretty plz work

* yay it works, also address feedback

* appease linter

* deploy new FQTs

Co-authored-by: johngrantuk <johngrantuk@googlemail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2022-04-22 02:43:41 -04:00

984 lines
48 KiB
TypeScript

// import {
// blockchainTests,
// constants,
// expect,
// getRandomInteger,
// getRandomPortion,
// randomAddress,
// } from '@0x/contracts-test-utils';
// import { SignatureType } from '@0x/protocol-utils';
// import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils';
// import * as _ from 'lodash';
//
// import { FillQuoteTransformerOrderType, LimitOrderFields } from '../../src';
// import { SamplerCallResult, SignedNativeOrder } from '../../src/types';
// import { artifacts } from '../artifacts';
// import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '../wrappers';
//
// // tslint:disable: custom-no-magic-numbers
//
// const { NULL_ADDRESS } = constants;
// // HACK(dorothy-zbornak): Disabled because these tests are flakey and all this logic is moving to
// // the sampler service anyway.
// blockchainTests.skip('erc20-bridge-sampler', env => {
// let testContract: TestERC20BridgeSamplerContract;
// const RATE_DENOMINATOR = constants.ONE_ETHER;
// const MIN_RATE = new BigNumber('0.01');
// const MAX_RATE = new BigNumber('100');
// const MIN_DECIMALS = 4;
// const MAX_DECIMALS = 20;
// const WETH_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
// const KYBER_SALT = '0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7';
// const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
// const UNISWAP_V2_SALT = '0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1';
// const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
// const MAKER_TOKEN = randomAddress();
// const TAKER_TOKEN = randomAddress();
// const INTERMEDIATE_TOKEN = randomAddress();
// const KYBER_RESERVE_OFFSET = new BigNumber(0);
// let KYBER_ADDRESS = '';
// let UNISWAP_ADDRESS = '';
// let UNISWAP_V2_ROUTER = '';
//
// before(async () => {
// testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
// artifacts.TestERC20BridgeSampler,
// env.provider,
// { ...env.txDefaults, gas: 100e6 },
// {},
// );
// UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync();
// KYBER_ADDRESS = await testContract.kyber().callAsync();
// UNISWAP_ADDRESS = await testContract.uniswap().callAsync();
// });
//
// function getPackedHash(...args: string[]): string {
// return hexUtils.hash(hexUtils.concat(...args.map(a => hexUtils.toHex(a))));
// }
//
// function getUniswapExchangeSalt(tokenAddress: string): string {
// return getPackedHash(UNISWAP_BASE_SALT, tokenAddress);
// }
//
// function getDeterministicRate(salt: string, sellToken: string, buyToken: string): BigNumber {
// const hash = getPackedHash(salt, sellToken, buyToken);
// const _minRate = RATE_DENOMINATOR.times(MIN_RATE);
// const _maxRate = RATE_DENOMINATOR.times(MAX_RATE);
// return new BigNumber(hash)
// .mod(_maxRate.minus(_minRate))
// .plus(_minRate)
// .div(RATE_DENOMINATOR);
// }
//
// function getDeterministicTokenDecimals(token: string): number {
// if (token === WETH_ADDRESS) {
// return 18;
// }
// // HACK(dorothy-zbornak): Linter will complain about the addition not being
// // between two numbers, even though they are.
// // tslint:disable-next-line restrict-plus-operands
// return new BigNumber(getPackedHash(token)).mod(MAX_DECIMALS - MIN_DECIMALS).toNumber() + MIN_DECIMALS;
// }
//
// function getDeterministicSellQuote(
// salt: string,
// sellToken: string,
// buyToken: string,
// sellAmount: BigNumber,
// ): BigNumber {
// const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken));
// const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken));
// const rate = getDeterministicRate(salt, sellToken, buyToken);
// return sellAmount
// .times(rate)
// .times(buyBase)
// .dividedToIntegerBy(sellBase);
// }
//
// function getDeterministicBuyQuote(
// salt: string,
// sellToken: string,
// buyToken: string,
// buyAmount: BigNumber,
// ): BigNumber {
// const sellBase = new BigNumber(10).pow(getDeterministicTokenDecimals(sellToken));
// const buyBase = new BigNumber(10).pow(getDeterministicTokenDecimals(buyToken));
// const rate = getDeterministicRate(salt, sellToken, buyToken);
// return buyAmount
// .times(sellBase)
// .dividedToIntegerBy(rate)
// .dividedToIntegerBy(buyBase);
// }
//
// function areAddressesEqual(a: string, b: string): boolean {
// return a.toLowerCase() === b.toLowerCase();
// }
//
// function getDeterministicUniswapSellQuote(sellToken: string, buyToken: string, sellAmount: BigNumber): BigNumber {
// if (areAddressesEqual(buyToken, WETH_ADDRESS)) {
// return getDeterministicSellQuote(getUniswapExchangeSalt(sellToken), sellToken, WETH_ADDRESS, sellAmount);
// }
// if (areAddressesEqual(sellToken, WETH_ADDRESS)) {
// return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, sellAmount);
// }
// const ethBought = getDeterministicSellQuote(
// getUniswapExchangeSalt(sellToken),
// sellToken,
// WETH_ADDRESS,
// sellAmount,
// );
// return getDeterministicSellQuote(getUniswapExchangeSalt(buyToken), buyToken, WETH_ADDRESS, ethBought);
// }
//
// function getDeterministicUniswapBuyQuote(sellToken: string, buyToken: string, buyAmount: BigNumber): BigNumber {
// if (areAddressesEqual(buyToken, WETH_ADDRESS)) {
// return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, buyAmount);
// }
// if (areAddressesEqual(sellToken, WETH_ADDRESS)) {
// return getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount);
// }
// const ethSold = getDeterministicBuyQuote(getUniswapExchangeSalt(buyToken), WETH_ADDRESS, buyToken, buyAmount);
// return getDeterministicBuyQuote(getUniswapExchangeSalt(sellToken), WETH_ADDRESS, sellToken, ethSold);
// }
//
// function getDeterministicSellQuotes(
// sellToken: string,
// buyToken: string,
// sources: string[],
// sampleAmounts: BigNumber[],
// ): BigNumber[][] {
// const quotes: BigNumber[][] = [];
// for (const source of sources) {
// const sampleOutputs = [];
// for (const amount of sampleAmounts) {
// if (source === 'Kyber') {
// sampleOutputs.push(getDeterministicSellQuote(KYBER_SALT, sellToken, buyToken, amount));
// } else if (source === 'Uniswap') {
// sampleOutputs.push(getDeterministicUniswapSellQuote(sellToken, buyToken, amount));
// }
// }
// quotes.push(sampleOutputs);
// }
// return quotes;
// }
//
// function getDeterministicBuyQuotes(
// sellToken: string,
// buyToken: string,
// sources: string[],
// sampleAmounts: BigNumber[],
// ): BigNumber[][] {
// const quotes: BigNumber[][] = [];
// for (const source of sources) {
// const sampleOutputs = [];
// for (const amount of sampleAmounts) {
// if (source === 'Kyber') {
// sampleOutputs.push(getDeterministicBuyQuote(KYBER_SALT, sellToken, buyToken, amount));
// } else if (source === 'Uniswap') {
// sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount));
// }
// }
// quotes.push(sampleOutputs);
// }
// return quotes;
// }
//
// function getDeterministicUniswapV2SellQuote(path: string[], sellAmount: BigNumber): BigNumber {
// let bought = sellAmount;
// for (let i = 0; i < path.length - 1; ++i) {
// bought = getDeterministicSellQuote(UNISWAP_V2_SALT, path[i], path[i + 1], bought);
// }
// return bought;
// }
//
// function getDeterministicUniswapV2BuyQuote(path: string[], buyAmount: BigNumber): BigNumber {
// let sold = buyAmount;
// for (let i = path.length - 1; i > 0; --i) {
// sold = getDeterministicBuyQuote(UNISWAP_V2_SALT, path[i - 1], path[i], sold);
// }
// return sold;
// }
//
// function getDeterministicFillableTakerAssetAmount(order: SignedNativeOrder): BigNumber {
// const hash = getPackedHash(hexUtils.leftPad(order.order.salt));
// return new BigNumber(hash).mod(order.order.takerAmount);
// }
//
// function getDeterministicFillableMakerAssetAmount(order: SignedNativeOrder): BigNumber {
// const takerAmount = getDeterministicFillableTakerAssetAmount(order);
// return order.order.makerAmount
// .times(takerAmount)
// .div(order.order.takerAmount)
// .integerValue(BigNumber.ROUND_UP);
// }
//
// function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
// const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
// const _upperLimit = getRandomPortion(getRandomInteger(1000, 50000).times(10 ** tokenDecimals));
// const _count = count || _.random(1, 16);
// const d = _upperLimit.div(_count);
// return _.times(_count, i => d.times((i + 1) / _count).integerValue());
// }
//
// function createOrder(makerToken: string, takerToken: string): SignedNativeOrder {
// return {
// order: {
// chainId: 1337,
// verifyingContract: randomAddress(),
// maker: randomAddress(),
// taker: randomAddress(),
// pool: NULL_BYTES,
// sender: NULL_ADDRESS,
// feeRecipient: randomAddress(),
// makerAmount: getRandomInteger(1, 1e18),
// takerAmount: getRandomInteger(1, 1e18),
// takerTokenFeeAmount: getRandomInteger(1, 1e18),
// makerToken,
// takerToken,
// salt: new BigNumber(hexUtils.random()),
// expiry: getRandomInteger(0, 2 ** 32),
// },
// signature: { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign },
// type: FillQuoteTransformerOrderType.Limit,
// };
// }
//
// function createOrders(makerToken: string, takerToken: string, count?: number): SignedNativeOrder[] {
// return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
// }
//
// async function enableFailTriggerAsync(): Promise<void> {
// await testContract.enableFailTrigger().awaitTransactionSuccessAsync({ value: 1 });
// }
//
// function expectQuotesWithinRange(
// quotes: BigNumber[],
// expectedQuotes: BigNumber[],
// maxSlippage: BigNumber | number,
// ): void {
// quotes.forEach((_q, i) => {
// // If we're within 1 base unit of a low decimal token
// // then that's as good as we're going to get (and slippage is "high")
// if (
// expectedQuotes[i].isZero() ||
// BigNumber.max(expectedQuotes[i], quotes[i])
// .minus(BigNumber.min(expectedQuotes[i], quotes[i]))
// .eq(1)
// ) {
// return;
// }
// const slippage = quotes[i]
// .dividedBy(expectedQuotes[i])
// .minus(1)
// .decimalPlaces(4);
// expect(slippage, `quote[${i}]: ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.gte(0);
// expect(slippage, `quote[${i}] ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.lte(
// new BigNumber(maxSlippage),
// );
// });
// }
//
// describe('getOrderFillableTakerAssetAmounts()', () => {
// it('returns the expected amount for each order', async () => {
// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
// const expected = orders.map(getDeterministicFillableTakerAssetAmount);
// const actual = await testContract
// .getLimitOrderFillableTakerAssetAmounts(
// // tslint:disable-next-line:no-unnecessary-type-assertion
// orders.map(o => o.order as LimitOrderFields),
// orders.map(o => o.signature),
// NULL_ADDRESS,
// )
// .callAsync();
// expect(actual).to.deep.eq(expected);
// });
//
// it('returns empty for no orders', async () => {
// const actual = await testContract.getLimitOrderFillableTakerAssetAmounts([], [], NULL_ADDRESS).callAsync();
// expect(actual).to.deep.eq([]);
// });
// });
//
// describe('getOrderFillableMakerAssetAmounts()', () => {
// it('returns the expected amount for each order', async () => {
// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
// const expected = orders.map(getDeterministicFillableMakerAssetAmount);
// const actual = await testContract
// .getLimitOrderFillableMakerAssetAmounts(
// // tslint:disable-next-line:no-unnecessary-type-assertion
// orders.map(o => o.order as LimitOrderFields),
// orders.map(o => o.signature),
// NULL_ADDRESS,
// )
// .callAsync();
// expect(actual).to.deep.eq(expected);
// });
//
// it('returns empty for no orders', async () => {
// const actual = await testContract.getLimitOrderFillableMakerAssetAmounts([], [], NULL_ADDRESS).callAsync();
// expect(actual).to.deep.eq([]);
// });
// });
//
// blockchainTests.resets('sampleSellsFromKyberNetwork()', () => {
// let kyberOpts = {
// hintHandler: NULL_ADDRESS,
// networkProxy: NULL_ADDRESS,
// weth: WETH_ADDRESS,
// reserveOffset: KYBER_RESERVE_OFFSET,
// hint: NULL_BYTES,
// };
// before(async () => {
// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
// kyberOpts = {
// ...kyberOpts,
// hintHandler: KYBER_ADDRESS,
// networkProxy: KYBER_ADDRESS,
// };
// });
//
// it('throws if tokens are the same', async () => {
// const tx = testContract.sampleSellsFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
// });
//
// it('can return no quotes', async () => {
// const [, , quotes] = await testContract
// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, [])
// .callAsync();
// expect(quotes).to.deep.eq([]);
// });
//
// 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(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> ETH', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
// const [, , quotes] = await testContract
// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
// const [, , quotes] = await testContract
// .sampleSellsFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// 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(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote ETH -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
// const [, , quotes] = await testContract
// .sampleSellsFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// 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(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
// });
//
// blockchainTests.resets('sampleBuysFromKyberNetwork()', () => {
// let kyberOpts = {
// hintHandler: NULL_ADDRESS,
// networkProxy: NULL_ADDRESS,
// weth: WETH_ADDRESS,
// reserveOffset: KYBER_RESERVE_OFFSET,
// hint: NULL_BYTES,
// };
// const ACCEPTABLE_SLIPPAGE = 0.0005;
// before(async () => {
// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
// kyberOpts = {
// ...kyberOpts,
// hintHandler: KYBER_ADDRESS,
// networkProxy: KYBER_ADDRESS,
// };
// });
//
// it('throws if tokens are the same', async () => {
// const tx = testContract.sampleBuysFromKyberNetwork(kyberOpts, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
// });
//
// it('can return no quotes', async () => {
// const [, , quotes] = await testContract
// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, [])
// .callAsync();
// expect(quotes).to.deep.eq([]);
// });
//
// it('can quote token -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
// const [, , quotes] = await testContract
// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
// });
//
// 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
// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> ETH', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
// const [, , quotes] = await testContract
// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
// .callAsync();
// expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
// });
//
// 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
// .sampleBuysFromKyberNetwork(kyberOpts, TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote ETH -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
// const [, , quotes] = await testContract
// .sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
// });
//
// 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
// .sampleBuysFromKyberNetwork(kyberOpts, WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
// });
//
// blockchainTests.resets('sampleSellsFromUniswap()', () => {
// const UNISWAP_ETH_ADDRESS = NULL_ADDRESS;
// before(async () => {
// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
// });
//
// it('throws if tokens are the same', async () => {
// const tx = testContract.sampleSellsFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
// });
//
// it('can return no quotes', async () => {
// const quotes = await testContract
// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
// .callAsync();
// expect(quotes).to.deep.eq([]);
// });
//
// it('can quote token -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
// const quotes = await testContract
// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// 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(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> ETH', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
// const quotes = await testContract
// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
// .callAsync();
// 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(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote ETH -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
// const quotes = await testContract
// .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// 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
// .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('returns zero if no exchange exists for the maker token', async () => {
// const nonExistantToken = randomAddress();
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
// const quotes = await testContract
// .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts)
// .callAsync();
// 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(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
// });
//
// blockchainTests.resets('sampleBuysFromUniswap()', () => {
// const UNISWAP_ETH_ADDRESS = NULL_ADDRESS;
// before(async () => {
// await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
// });
//
// it('throws if tokens are the same', async () => {
// const tx = testContract.sampleBuysFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
// return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
// });
//
// it('can return no quotes', async () => {
// const quotes = await testContract
// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
// .callAsync();
// expect(quotes).to.deep.eq([]);
// });
//
// it('can quote token -> token', async () => {
// const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
// const quotes = await testContract
// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// 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(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> ETH', async () => {
// const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
// const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
// const quotes = await testContract
// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
// .callAsync();
// 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(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote ETH -> token', async () => {
// const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
// const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
// const quotes = await testContract
// .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// 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
// .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('returns zero if no exchange exists for the maker token', async () => {
// const nonExistantToken = randomAddress();
// const sampleAmounts = getSampleAmounts(nonExistantToken);
// const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
// const quotes = await testContract
// .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts)
// .callAsync();
// 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(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
// });
//
// describe('liquidity provider', () => {
// const xAsset = randomAddress();
// const yAsset = randomAddress();
// const sampleAmounts = getSampleAmounts(yAsset);
// let liquidityProvider: DummyLiquidityProviderContract;
//
// before(async () => {
// liquidityProvider = await DummyLiquidityProviderContract.deployFrom0xArtifactAsync(
// artifacts.DummyLiquidityProvider,
// env.provider,
// env.txDefaults,
// {},
// );
// });
//
// it('should be able to query sells from the liquidity provider', async () => {
// const quotes = await testContract
// .sampleSellsFromLiquidityProvider(liquidityProvider.address, yAsset, xAsset, sampleAmounts)
// .callAsync();
// quotes.forEach((value, idx) => {
// expect(value).is.bignumber.eql(sampleAmounts[idx].minus(1));
// });
// });
//
// it('should be able to query buys from the liquidity provider', async () => {
// const quotes = await testContract
// .sampleBuysFromLiquidityProvider(liquidityProvider.address, yAsset, xAsset, sampleAmounts)
// .callAsync();
// quotes.forEach((value, idx) => {
// expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
// });
// });
//
// it('should just return zeros if the liquidity provider does not exist', async () => {
// const quotes = await testContract
// .sampleBuysFromLiquidityProvider(randomAddress(), yAsset, xAsset, sampleAmounts)
// .callAsync();
// quotes.forEach(value => {
// expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
// });
// });
// });
//
// blockchainTests.resets('sampleSellsFromUniswapV2()', () => {
// function predictSellQuotes(path: string[], sellAmounts: BigNumber[]): BigNumber[] {
// return sellAmounts.map(a => getDeterministicUniswapV2SellQuote(path, a));
// }
//
// it('can return no quotes', async () => {
// const quotes = await testContract
// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], [])
// .callAsync();
// expect(quotes).to.deep.eq([]);
// });
//
// it('can quote token -> token', async () => {
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const expectedQuotes = predictSellQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts);
// const quotes = await testContract
// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
// .callAsync();
// 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
// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> token -> token', async () => {
// const intermediateToken = randomAddress();
// const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
// const expectedQuotes = predictSellQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts);
// const quotes = await testContract
// .sampleSellsFromUniswapV2(
// UNISWAP_V2_ROUTER,
// [TAKER_TOKEN, intermediateToken, MAKER_TOKEN],
// sampleAmounts,
// )
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
// });
//
// blockchainTests.resets('sampleBuysFromUniswapV2()', () => {
// function predictBuyQuotes(path: string[], buyAmounts: BigNumber[]): BigNumber[] {
// return buyAmounts.map(a => getDeterministicUniswapV2BuyQuote(path, a));
// }
//
// it('can return no quotes', async () => {
// const quotes = await testContract
// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], [])
// .callAsync();
// expect(quotes).to.deep.eq([]);
// });
//
// it('can quote token -> token', async () => {
// const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
// const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts);
// const quotes = await testContract
// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
// .callAsync();
// 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
// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
//
// it('can quote token -> token -> token', async () => {
// const intermediateToken = randomAddress();
// const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
// const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts);
// const quotes = await testContract
// .sampleBuysFromUniswapV2(
// UNISWAP_V2_ROUTER,
// [TAKER_TOKEN, intermediateToken, MAKER_TOKEN],
// sampleAmounts,
// )
// .callAsync();
// expect(quotes).to.deep.eq(expectedQuotes);
// });
// });
//
// describe('batchCall()', () => {
// it('can call one function', async () => {
// const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
// const expected = orders.map(getDeterministicFillableTakerAssetAmount);
// const calls = [
// testContract
// .getLimitOrderFillableTakerAssetAmounts(
// // tslint:disable-next-line:no-unnecessary-type-assertion
// orders.map(o => o.order as LimitOrderFields),
// orders.map(o => o.signature),
// NULL_ADDRESS,
// )
// .getABIEncodedTransactionData(),
// ];
// const r = await testContract.batchCall(calls).callAsync();
// expect(r).to.be.length(1);
// const actual = testContract.getABIDecodedReturnData<BigNumber[]>(
// 'getLimitOrderFillableTakerAssetAmounts',
// r[0].data,
// );
// 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 expecteds = [
// orders[0].map(getDeterministicFillableTakerAssetAmount),
// orders[1].map(getDeterministicFillableMakerAssetAmount),
// ];
// const calls = [
// testContract
// .getLimitOrderFillableTakerAssetAmounts(
// // tslint:disable-next-line:no-unnecessary-type-assertion
// orders[0].map(o => o.order as LimitOrderFields),
// orders[0].map(o => o.signature),
// NULL_ADDRESS,
// )
// .getABIEncodedTransactionData(),
// testContract
// .getLimitOrderFillableMakerAssetAmounts(
// // tslint:disable-next-line:no-unnecessary-type-assertion
// orders[1].map(o => o.order as LimitOrderFields),
// orders[1].map(o => o.signature),
// NULL_ADDRESS,
// )
// .getABIEncodedTransactionData(),
// ];
// const r = await testContract.batchCall(calls).callAsync();
// expect(r).to.be.length(2);
// expect(
// testContract.getABIDecodedReturnData('getLimitOrderFillableTakerAssetAmounts', r[0].data),
// ).to.deep.eq(expecteds[0]);
// expect(
// testContract.getABIDecodedReturnData('getLimitOrderFillableMakerAssetAmounts', r[1].data),
// ).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 expected = orders.map(getDeterministicFillableTakerAssetAmount);
// let r = await testContract
// .batchCall([
// testContract
// .batchCall([
// testContract
// .getLimitOrderFillableTakerAssetAmounts(
// // tslint:disable-next-line:no-unnecessary-type-assertion
// orders.map(o => o.order as LimitOrderFields),
// orders.map(o => o.signature),
// NULL_ADDRESS,
// )
// .getABIEncodedTransactionData(),
// ])
// .getABIEncodedTransactionData(),
// ])
// .callAsync();
// expect(r).to.be.length(1);
// r = testContract.getABIDecodedReturnData<SamplerCallResult[]>('batchCall', r[0].data);
// expect(r).to.be.length(1);
// expect(
// testContract.getABIDecodedReturnData('getLimitOrderFillableTakerAssetAmounts', r[0].data),
// ).to.deep.eq(expected);
// });
// });
//
// blockchainTests.resets('TwoHopSampler', () => {
// before(async () => {
// await testContract
// .createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN, INTERMEDIATE_TOKEN])
// .awaitTransactionSuccessAsync();
// });
//
// it('sampleTwoHopSell', async () => {
// // tslint:disable-next-line no-unnecessary-type-assertion
// const sellAmount = _.last(getSampleAmounts(TAKER_TOKEN))!;
// const uniswapV2FirstHopPath = [TAKER_TOKEN, INTERMEDIATE_TOKEN];
// const uniswapV2FirstHop = testContract
// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2FirstHopPath, [constants.ZERO_AMOUNT])
// .getABIEncodedTransactionData();
//
// const uniswapV2SecondHopPath = [INTERMEDIATE_TOKEN, randomAddress(), MAKER_TOKEN];
// const uniswapV2SecondHop = testContract
// .sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2SecondHopPath, [constants.ZERO_AMOUNT])
// .getABIEncodedTransactionData();
//
// const firstHopQuotes = [getDeterministicUniswapV2SellQuote(uniswapV2FirstHopPath, sellAmount)];
// const expectedIntermediateAssetAmount = BigNumber.max(...firstHopQuotes);
// const secondHopQuotes = [
// getDeterministicUniswapV2SellQuote(uniswapV2SecondHopPath, expectedIntermediateAssetAmount),
// ];
// const expectedBuyAmount = BigNumber.max(...secondHopQuotes);
//
// const [firstHop, secondHop, buyAmount] = await testContract
// .sampleTwoHopSell([uniswapV2FirstHop], [uniswapV2SecondHop], sellAmount)
// .callAsync();
// expect(firstHop.sourceIndex, 'First hop source index').to.bignumber.equal(
// firstHopQuotes.findIndex(quote => quote.isEqualTo(expectedIntermediateAssetAmount)),
// );
// expect(secondHop.sourceIndex, 'Second hop source index').to.bignumber.equal(
// secondHopQuotes.findIndex(quote => quote.isEqualTo(expectedBuyAmount)),
// );
// expect(buyAmount, 'Two hop buy amount').to.bignumber.equal(expectedBuyAmount);
// });
// it('sampleTwoHopBuy', async () => {
// // tslint:disable-next-line no-unnecessary-type-assertion
// const buyAmount = _.last(getSampleAmounts(MAKER_TOKEN))!;
// const uniswapV2FirstHopPath = [TAKER_TOKEN, INTERMEDIATE_TOKEN];
// const uniswapV2FirstHop = testContract
// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2FirstHopPath, [constants.ZERO_AMOUNT])
// .getABIEncodedTransactionData();
//
// const uniswapV2SecondHopPath = [INTERMEDIATE_TOKEN, randomAddress(), MAKER_TOKEN];
// const uniswapV2SecondHop = testContract
// .sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2SecondHopPath, [constants.ZERO_AMOUNT])
// .getABIEncodedTransactionData();
//
// const secondHopQuotes = [getDeterministicUniswapV2BuyQuote(uniswapV2SecondHopPath, buyAmount)];
// const expectedIntermediateAssetAmount = BigNumber.min(...secondHopQuotes);
//
// const firstHopQuotes = [
// getDeterministicUniswapV2BuyQuote(uniswapV2FirstHopPath, expectedIntermediateAssetAmount),
// ];
// const expectedSellAmount = BigNumber.min(...firstHopQuotes);
//
// const [firstHop, secondHop, sellAmount] = await testContract
// .sampleTwoHopBuy([uniswapV2FirstHop], [uniswapV2SecondHop], buyAmount)
// .callAsync();
// expect(firstHop.sourceIndex, 'First hop source index').to.bignumber.equal(
// firstHopQuotes.findIndex(quote => quote.isEqualTo(expectedSellAmount)),
// );
// expect(secondHop.sourceIndex, 'Second hop source index').to.bignumber.equal(
// secondHopQuotes.findIndex(quote => quote.isEqualTo(expectedIntermediateAssetAmount)),
// );
// expect(sellAmount, 'Two hop sell amount').to.bignumber.equal(expectedSellAmount);
// });
// });
// });