Feature/bunny hop (#2647)

* `@0x/contracts-erc20-bridge-sampler`: Add TwoHopSampler + refactor

* `@0x/asset-swapper`: Refactor + add two-hop skeleton

* Round out two-hop support in asset-swapper

* Add BalancerSampler, use it for two-hop quotes

* Fix bugs discovered from simbot

* rebases are hard

* Add intermediate token to MultiHop source breakdown

* Fix market buy bugs

* Use hybrid on-chain/off-chain sampling for Balancer

* Another day, another rebase

* Update changelogs

* Address PR feedback, CI fixes

* Address more PR feedback
This commit is contained in:
mzhu25
2020-08-26 15:20:09 -07:00
committed by GitHub
parent 4f78f55c2a
commit bab34c2d21
68 changed files with 2714 additions and 2078 deletions

View File

@@ -18,7 +18,7 @@ import {
computeBalancerSellQuote,
} from '../src/utils/market_operation_utils/balancer_utils';
import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation_utils/sampler';
import { ERC20BridgeSource, FillData } from '../src/utils/market_operation_utils/types';
import { ERC20BridgeSource } from '../src/utils/market_operation_utils/types';
import { MockBalancerPoolsCache } from './utils/mock_balancer_pools_cache';
import { MockBancorService } from './utils/mock_bancor_service';
@@ -108,7 +108,7 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
dexOrderSampler.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
);
expect(fillableAmounts).to.deep.eq(expectedFillableAmounts);
});
@@ -124,7 +124,7 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
dexOrderSampler.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
);
expect(fillableAmounts).to.deep.eq(expectedFillableAmounts);
});
@@ -144,36 +144,32 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getKyberSellQuotes(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
),
dexOrderSampler.getKyberSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
);
expect(fillableAmounts.map(q => q.amount)).to.deep.eq(expectedMakerFillAmounts);
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
});
it('getLiquidityProviderSellQuotes()', async () => {
const expectedMakerToken = randomAddress();
const expectedTakerToken = randomAddress();
const registry = randomAddress();
const poolAddress = randomAddress();
const sampler = new MockSamplerContract({
sampleSellsFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, _fillAmounts) => {
expect(registryAddress).to.eq(registry);
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
return [toBaseUnitAmount(1001)];
return [[toBaseUnitAmount(1001)], poolAddress];
},
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [result] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getSellQuotesAsync(
dexOrderSampler.getSellQuotes(
[ERC20BridgeSource.LiquidityProvider],
expectedMakerToken,
expectedTakerToken,
[toBaseUnitAmount(1000)],
wethAddress,
dexOrderSampler.balancerPoolsCache,
registry,
),
);
@@ -183,7 +179,7 @@ describe('DexSampler tests', () => {
source: 'LiquidityProvider',
output: toBaseUnitAmount(1001),
input: toBaseUnitAmount(1000),
fillData: undefined,
fillData: { poolAddress },
},
],
]);
@@ -193,23 +189,23 @@ describe('DexSampler tests', () => {
const expectedMakerToken = randomAddress();
const expectedTakerToken = randomAddress();
const registry = randomAddress();
const poolAddress = randomAddress();
const sampler = new MockSamplerContract({
sampleBuysFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, _fillAmounts) => {
expect(registryAddress).to.eq(registry);
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
return [toBaseUnitAmount(999)];
return [[toBaseUnitAmount(999)], poolAddress];
},
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [result] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getBuyQuotesAsync(
dexOrderSampler.getBuyQuotes(
[ERC20BridgeSource.LiquidityProvider],
expectedMakerToken,
expectedTakerToken,
[toBaseUnitAmount(1000)],
wethAddress,
dexOrderSampler.balancerPoolsCache,
registry,
),
);
@@ -219,7 +215,7 @@ describe('DexSampler tests', () => {
source: 'LiquidityProvider',
output: toBaseUnitAmount(999),
input: toBaseUnitAmount(1000),
fillData: undefined,
fillData: { poolAddress },
},
],
]);
@@ -246,13 +242,12 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [result] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getSellQuotesAsync(
dexOrderSampler.getSellQuotes(
[ERC20BridgeSource.MultiBridge],
expectedMakerToken,
expectedTakerToken,
[toBaseUnitAmount(1000)],
randomAddress(),
dexOrderSampler.balancerPoolsCache,
randomAddress(),
multiBridge,
),
@@ -263,7 +258,7 @@ describe('DexSampler tests', () => {
source: 'MultiBridge',
output: toBaseUnitAmount(1001),
input: toBaseUnitAmount(1000),
fillData: undefined,
fillData: { poolAddress: multiBridge },
},
],
]);
@@ -284,13 +279,9 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getEth2DaiSellQuotes(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
),
dexOrderSampler.getEth2DaiSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
);
expect(fillableAmounts.map(q => q.amount)).to.deep.eq(expectedMakerFillAmounts);
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
});
it('getUniswapSellQuotes()', async () => {
@@ -308,13 +299,9 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getUniswapSellQuotes(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
),
dexOrderSampler.getUniswapSellQuotes(expectedMakerToken, expectedTakerToken, expectedTakerFillAmounts),
);
expect(fillableAmounts.map(q => q.amount)).to.deep.eq(expectedMakerFillAmounts);
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
});
it('getUniswapV2SellQuotes()', async () => {
@@ -331,12 +318,12 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getUniswapV2SellQuotes(
dexOrderSampler.getUniswapV2SellQuotes(
[expectedMakerToken, expectedTakerToken],
expectedTakerFillAmounts,
),
);
expect(fillableAmounts.map(q => q.amount)).to.deep.eq(expectedMakerFillAmounts);
expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
});
it('getEth2DaiBuyQuotes()', async () => {
@@ -354,13 +341,9 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getEth2DaiBuyQuotes(
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
),
dexOrderSampler.getEth2DaiBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
);
expect(fillableAmounts.map(q => q.amount)).to.deep.eq(expectedTakerFillAmounts);
expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts);
});
it('getUniswapBuyQuotes()', async () => {
@@ -378,13 +361,9 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getUniswapBuyQuotes(
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
),
dexOrderSampler.getUniswapBuyQuotes(expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
);
expect(fillableAmounts.map(q => q.amount)).to.deep.eq(expectedTakerFillAmounts);
expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts);
});
interface RatesBySource {
@@ -440,13 +419,12 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [quotes] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getSellQuotesAsync(
dexOrderSampler.getSellQuotes(
sources,
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
wethAddress,
dexOrderSampler.balancerPoolsCache,
),
);
const expectedQuotes = sources.map(s =>
@@ -457,7 +435,7 @@ describe('DexSampler tests', () => {
fillData:
s === ERC20BridgeSource.UniswapV2
? { tokenAddressPath: [expectedTakerToken, expectedMakerToken] }
: ((undefined as any) as FillData),
: {},
})),
);
const uniswapV2ETHQuotes = [
@@ -488,19 +466,14 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(
new MockSamplerContract({}),
undefined, // sampler overrides
undefined, // bancor service
undefined,
undefined,
balancerPoolsCache,
);
const [quotes] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getSellQuotesAsync(
[ERC20BridgeSource.Balancer],
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
wethAddress,
dexOrderSampler.balancerPoolsCache,
),
const quotes = await dexOrderSampler.getBalancerSellQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
);
const expectedQuotes = pools.map(p =>
expectedTakerFillAmounts.map(a => ({
@@ -535,28 +508,17 @@ describe('DexSampler tests', () => {
bancorService,
undefined, // balancer cache
);
const [quotes] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getSellQuotesAsync(
[ERC20BridgeSource.Bancor],
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
wethAddress,
undefined, // balancer pools cache
undefined, // liquidity provider registry address
undefined, // multibridge address
bancorService,
),
const quotes = await dexOrderSampler.getBancorSellQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
);
const expectedQuotes = [
expectedTakerFillAmounts.map(a => ({
source: ERC20BridgeSource.Bancor,
input: a,
output: a.multipliedBy(rate),
fillData: { path: [expectedTakerToken, expectedMakerToken], networkAddress },
})),
];
expect(quotes).to.have.lengthOf(1); // one set per pool
const expectedQuotes = expectedTakerFillAmounts.map(a => ({
source: ERC20BridgeSource.Bancor,
input: a,
output: a.multipliedBy(rate),
fillData: { path: [expectedTakerToken, expectedMakerToken], networkAddress },
}));
expect(quotes).to.deep.eq(expectedQuotes);
});
it('getBuyQuotes()', async () => {
@@ -596,13 +558,12 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [quotes] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getBuyQuotesAsync(
dexOrderSampler.getBuyQuotes(
sources,
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
wethAddress,
dexOrderSampler.balancerPoolsCache,
),
);
const expectedQuotes = sources.map(s =>
@@ -613,7 +574,7 @@ describe('DexSampler tests', () => {
fillData:
s === ERC20BridgeSource.UniswapV2
? { tokenAddressPath: [expectedTakerToken, expectedMakerToken] }
: ((undefined as any) as FillData),
: {},
})),
);
const uniswapV2ETHQuotes = [
@@ -644,19 +605,14 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(
new MockSamplerContract({}),
undefined, // sampler overrides
undefined, // bancor service
undefined,
undefined,
balancerPoolsCache,
);
const [quotes] = await dexOrderSampler.executeAsync(
await DexOrderSampler.ops.getBuyQuotesAsync(
[ERC20BridgeSource.Balancer],
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
wethAddress,
dexOrderSampler.balancerPoolsCache,
),
const quotes = await dexOrderSampler.getBalancerBuyQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
);
const expectedQuotes = pools.map(p =>
expectedMakerFillAmounts.map(a => ({
@@ -689,8 +645,8 @@ describe('DexSampler tests', () => {
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
DexOrderSampler.ops.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
DexOrderSampler.ops.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
dexOrderSampler.getOrderFillableMakerAmounts(ORDERS, exchangeAddress),
dexOrderSampler.getOrderFillableTakerAmounts(ORDERS, exchangeAddress),
);
expect(fillableMakerAmounts).to.deep.eq(expectedFillableMakerAmounts);
expect(fillableTakerAmounts).to.deep.eq(expectedFillableTakerAmounts);