feat: Balancer V2 integration (#206)

* add balancer v2

* fetch balancer v2 pools from subgraph

* feat: initial stab at a Balancer V2 Sampler WIP

* feat: add sampling for buys and fix build issues WIP [untested]

* fix: BalancerV2Sampler implementation issues, works on Kovan

* chore: BalancerV2 sampling boilerplate

* fix: update Balancer V2 mainnet address

* fix: consolidate differences between the 2 working branches

* fix: use mainnet Balancer V2 subgraph

* fix: stack too deep by minimizing and inline Balancer V2 vault interface

* fix: address review comments and clean up

* fix: sampler vault interface and pools cache assuming a pool has swaps

* address more review comments

* fix: TS type issues and add a comment about deadline argument

* fix: pools_cache_tests incorrect token addresses, prettier incompat

* fix: make ERC20BridgeSampler support BalancerV2 non view sampler fns

* fix: use a struct for passing encoded bridge data for Balancer V2

* chore: add changelog entries

* fix: improve gas accuracy of gas schedule for Balancer V2 & Maker Psm

* fix: don't exclude sources with stale caches & wait for cache refresh

* rebase

* `@0x/asset-swapper`: Fix stack too deep errors in sampler

Co-authored-by: Kim Persson <kimpersson88@gmail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
Xianny
2021-05-05 02:01:28 -07:00
committed by GitHub
parent a2643674ca
commit f9a794af93
37 changed files with 933 additions and 748 deletions

View File

@@ -9,14 +9,12 @@ import {
} from '@0x/contracts-test-utils';
import { FillQuoteTransformerOrderType, LimitOrderFields, SignatureType } from '@0x/protocol-utils';
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import { Pool } from '@balancer-labs/sor/dist/types';
import * as _ from 'lodash';
import { SignedOrder } from '../src/types';
import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation_utils/sampler';
import { ERC20BridgeSource, TokenAdjacencyGraph } from '../src/utils/market_operation_utils/types';
import { MockBalancerPoolsCache } from './utils/mock_balancer_pools_cache';
import { MockSamplerContract } from './utils/mock_sampler_contract';
import { generatePseudoRandomSalt } from './utils/utils';
@@ -112,7 +110,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -137,7 +134,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -166,7 +162,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -200,7 +195,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
{
[poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost },
},
@@ -245,7 +239,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
{
[poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost },
},
@@ -291,7 +284,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -325,7 +317,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -358,7 +349,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -391,7 +381,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -425,7 +414,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -490,7 +478,6 @@ describe('DexSampler tests', () => {
sampler,
undefined,
undefined,
undefined,
tokenAdjacencyGraph,
undefined,
async () => undefined,
@@ -542,38 +529,6 @@ describe('DexSampler tests', () => {
expect(quotes).to.have.lengthOf(sources.length + additionalSourceCount);
expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes));
});
it('getSellQuotes() fetches pools but not samples from Balancer', async () => {
// HACK
// We disabled the off-chain sampling due to incorrect data observed between
// on-chain and off-chain sampling
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
const pools: Pool[] = [generateBalancerPool(), generateBalancerPool()];
const balancerPoolsCache = new MockBalancerPoolsCache({
getPoolsForPairAsync: async (takerToken: string, makerToken: string) => {
expect(takerToken).equal(expectedTakerToken);
expect(makerToken).equal(expectedMakerToken);
return Promise.resolve(pools);
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
new MockSamplerContract({}),
undefined,
balancerPoolsCache,
undefined,
undefined,
undefined,
async () => undefined,
);
const quotes = await dexOrderSampler.getBalancerSellQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
);
expect(quotes).to.have.lengthOf(0);
});
it('getBuyQuotes()', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
@@ -620,7 +575,6 @@ describe('DexSampler tests', () => {
sampler,
undefined,
undefined,
undefined,
tokenAdjacencyGraph,
undefined,
async () => undefined,
@@ -665,80 +619,39 @@ describe('DexSampler tests', () => {
expect(quotes).to.have.lengthOf(sources.length + 1);
expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes));
});
it('getBuyQuotes() uses samples from Balancer', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
const pools: Pool[] = [generateBalancerPool(), generateBalancerPool()];
const balancerPoolsCache = new MockBalancerPoolsCache({
getPoolsForPairAsync: async (takerToken: string, makerToken: string) => {
expect(takerToken).equal(expectedTakerToken);
expect(makerToken).equal(expectedMakerToken);
return Promise.resolve(pools);
},
describe('batched operations', () => {
it('getLimitOrderFillableMakerAssetAmounts(), getLimitOrderFillableTakerAssetAmounts()', async () => {
const expectedFillableTakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const expectedFillableMakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const sampler = new MockSamplerContract({
getLimitOrderFillableMakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableMakerAmounts;
},
getLimitOrderFillableTakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableTakerAmounts;
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getLimitOrderFillableMakerAmounts(ORDERS, exchangeProxyAddress),
dexOrderSampler.getLimitOrderFillableTakerAmounts(ORDERS, exchangeProxyAddress),
);
expect(fillableMakerAmounts).to.deep.eq(expectedFillableMakerAmounts);
expect(fillableTakerAmounts).to.deep.eq(expectedFillableTakerAmounts);
});
const dexOrderSampler = new DexOrderSampler(
chainId,
new MockSamplerContract({}),
undefined,
balancerPoolsCache,
undefined,
undefined,
undefined,
async () => undefined,
);
const quotes = await dexOrderSampler.getBalancerBuyQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
);
expect(quotes).to.have.lengthOf(0);
});
});
describe('batched operations', () => {
it('getLimitOrderFillableMakerAssetAmounts(), getLimitOrderFillableTakerAssetAmounts()', async () => {
const expectedFillableTakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const expectedFillableMakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const sampler = new MockSamplerContract({
getLimitOrderFillableMakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableMakerAmounts;
},
getLimitOrderFillableTakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableTakerAmounts;
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getLimitOrderFillableMakerAmounts(ORDERS, exchangeProxyAddress),
dexOrderSampler.getLimitOrderFillableTakerAmounts(ORDERS, exchangeProxyAddress),
);
expect(fillableMakerAmounts).to.deep.eq(expectedFillableMakerAmounts);
expect(fillableTakerAmounts).to.deep.eq(expectedFillableTakerAmounts);
});
});
});
function generateBalancerPool(): Pool {
return {
id: randomAddress(),
balanceIn: getRandomInteger(1, 1e18),
balanceOut: getRandomInteger(1, 1e18),
weightIn: getRandomInteger(0, 1e5),
weightOut: getRandomInteger(0, 1e5),
swapFee: getRandomInteger(0, 1e5),
};
}
// tslint:disable-next-line: max-file-line-count