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:
@@ -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);
|
||||
|
Reference in New Issue
Block a user