Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalised exit phantom tests #523

Merged
merged 5 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions balancer-js/src/lib/utils/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,16 @@ export class Logger {
console.error(`[ERROR] ${message}`);
}
}

time(message: string): void {
if (this.enableLogging) {
console.time(`[TIME] ${message}`);
}
}

timeEnd(message: string): void {
if (this.enableLogging) {
console.timeEnd(`[TIME] ${message}`);
}
}
}
9 changes: 7 additions & 2 deletions balancer-js/src/modules/data/pool/subgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { GraphQLQuery, Pool } from '@/types';
import { Network } from '@/lib/constants/network';
import { PoolsQueryVariables } from '../../subgraph/subgraph';
import { mapType } from './subgraph-helpers';
import { Logger } from '@/lib/utils/logger';

interface PoolsSubgraphRepositoryOptions {
url: string;
Expand Down Expand Up @@ -76,7 +77,8 @@ export class PoolsSubgraphRepository
* @returns Promise resolving to pools list
*/
private async fetchAllPools(): Promise<Pool[]> {
console.time('fetching pools');
const logger = Logger.getInstance();
logger.time('fetching pools');

if (this.blockHeight) {
this.query.args.block = { number: await this.blockHeight() };
Expand All @@ -88,14 +90,16 @@ export class PoolsSubgraphRepository
const { pool0, pool1000, pool2000 } = await this.client.AllPools(
formattedQuery
);
console.timeEnd('fetching pools');
logger.timeEnd('fetching pools');

return [...pool0, ...pool1000, ...pool2000].map((pool) =>
mapType(pool, this.chainId)
);
}

async fetch(options?: PoolsRepositoryFetchOptions): Promise<Pool[]> {
const logger = Logger.getInstance();
logger.time('fetching pools');
if (options?.skip) {
this.query.args.skip = options.skip;
}
Expand All @@ -112,6 +116,7 @@ export class PoolsSubgraphRepository
const { pools } = await this.client.Pools(formattedQuery);

this.skip = (options?.skip || 0) + pools.length;
logger.timeEnd('fetching pools');

return pools.map((pool) => mapType(pool, this.chainId));
}
Expand Down
22 changes: 14 additions & 8 deletions balancer-js/src/modules/data/pool/subgraphOnChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Pool } from '@/types';
import { getOnChainBalances } from '../../../modules/sor/pool-data/onChainData';
import { PoolsSubgraphRepository } from './subgraph';
import { isSameAddress } from '@/lib/utils';
import { Logger } from '@/lib/utils/logger';

interface PoolsSubgraphOnChainRepositoryOptions {
provider: Provider;
Expand Down Expand Up @@ -59,35 +60,40 @@ export class PoolsSubgraphOnChainRepository
* @returns Promise resolving to pools list
*/
private async fetchDefault(): Promise<Pool[]> {
console.time('fetching pools SG');
const pools = await this.poolsSubgraph.all();
console.timeEnd('fetching pools SG');
const filteredPools = this.filterPools(pools);
console.time(`fetching onchain ${filteredPools.length} pools`);

const logger = Logger.getInstance();
logger.time(`fetching onchain ${filteredPools.length} pools`);

const onchainPools = await getOnChainBalances(
filteredPools,
this.multicall,
this.vault,
this.provider
);
console.timeEnd(`fetching onchain ${filteredPools.length} pools`);

logger.timeEnd(`fetching onchain ${filteredPools.length} pools`);

return onchainPools;
}

async fetch(options?: PoolsRepositoryFetchOptions): Promise<Pool[]> {
console.time('fetching pools SG');
const pools = await this.poolsSubgraph.fetch(options);
console.timeEnd('fetching pools SG');
const filteredPools = this.filterPools(pools);
console.time(`fetching onchain ${filteredPools.length} pools`);

const logger = Logger.getInstance();
logger.time(`fetching onchain ${filteredPools.length} pools`);

const onchainPools = await getOnChainBalances(
filteredPools,
this.multicall,
this.vault,
this.provider
);
console.timeEnd(`fetching onchain ${filteredPools.length} pools`);

logger.timeEnd(`fetching onchain ${filteredPools.length} pools`);

return onchainPools;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ describe('generalised exit execution', async function () {
const network = Network.MAINNET;
const pool = ADDRESSES[network].bbausd;
const slippage = '10'; // 10 bps = 0.1%
let unwrappingTokensAmountsOut: string[];
let unwrappingTokensGasUsed: BigNumber;
let mainTokensAmountsOut: string[];
let mainTokensGasUsed: BigNumber;
const poolAddresses = Object.values(ADDRESSES[network]).map(
(address) => address.address
);
Expand All @@ -33,25 +29,9 @@ describe('generalised exit execution', async function () {
// Amount smaller than the underlying main token balance, which will cause the exit to be done directly
const mainExitAmount = unwrapExitAmount.div(amountRatio);

context('exit by unwrapping tokens', async () => {
it('should exit via unwrapping', async () => {
const { expectedAmountsOut, gasUsed } = await testFlow(
pool,
slippage,
unwrapExitAmount.toString(),
[ADDRESSES[network].DAI.address],
network,
blockNo,
poolAddresses
);
unwrappingTokensAmountsOut = expectedAmountsOut;
unwrappingTokensGasUsed = gasUsed;
});
});

context('exit to main tokens directly', async () => {
it('should exit to main tokens directly', async () => {
const { expectedAmountsOut, gasUsed } = await testFlow(
await testFlow(
pool,
slippage,
mainExitAmount.toString(),
Expand All @@ -60,24 +40,6 @@ describe('generalised exit execution', async function () {
blockNo,
poolAddresses
);
mainTokensAmountsOut = expectedAmountsOut;
mainTokensGasUsed = gasUsed;
});
});

context('exit by unwrapping vs exit to main tokens', async () => {
it('should return similar amounts (proportional to the input)', async () => {
mainTokensAmountsOut.forEach((amount, i) => {
const unwrappedAmount = BigNumber.from(
unwrappingTokensAmountsOut[i]
).div(amountRatio);
expect(
accuracy(unwrappedAmount, BigNumber.from(amount))
).to.be.closeTo(1, 1e-4); // inaccuracy should not be over 1 bps
});
});
it('should spend more gas when unwrapping tokens', async () => {
expect(unwrappingTokensGasUsed.gt(mainTokensGasUsed)).to.be.true;
});
});
});
Expand Down
41 changes: 12 additions & 29 deletions balancer-js/src/modules/exits/exits.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,35 +676,18 @@ export class Exit {
break;
}
case 'batchSwap': {
if (node.children.length === 1) {
const { modelRequest, encodedCall, assets, amounts } =
this.createSwap(
node,
exitChild as Node,
i,
minAmountOut,
sender,
recipient
);
modelRequests.push(modelRequest);
calls.push(encodedCall);
this.updateDeltas(deltas, assets, amounts);
} else {
const exitChildren = node.children.map((n) => n);
// TODO - is it correct to use minAmountsOutProportional?
const { modelRequest, encodedCall, assets, amounts } =
this.createBatchSwap(
node,
exitChildren,
i,
minAmountsOutProportional,
sender,
recipient
);
modelRequests.push(modelRequest);
calls.push(encodedCall);
this.updateDeltas(deltas, assets, amounts);
}
const { modelRequest, encodedCall, assets, amounts } =
this.createSwap(
node,
exitChild as Node,
i,
minAmountOut,
sender,
recipient
);
modelRequests.push(modelRequest);
calls.push(encodedCall);
this.updateDeltas(deltas, assets, amounts);
break;
}
case 'exitPool': {
Expand Down
2 changes: 1 addition & 1 deletion balancer-js/src/modules/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export class PoolGraph {
*/
static isProportionalPools(nodes: Node[]): boolean {
return nodes.every((node) => {
if (node.exitAction === 'exitPool') return node.isProportionalExit;
if (node.children.length > 1) return node.isProportionalExit;
else return true;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '@/lib/graphql/args-builder';

import { isSameAddress } from '@/lib/utils';
import { Logger } from '@/lib/utils/logger';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function mapPools(pools: any[]): SubgraphPoolBase[] {
Expand Down Expand Up @@ -87,14 +88,17 @@ export class SubgraphPoolDataService implements PoolDataService {
return mapped;
}

console.time(`fetching on-chain balances for ${mapped.length} pools`);
const logger = Logger.getInstance();
logger.time(`fetching on-chain balances for ${mapped.length} pools`);

const onChainBalances = await getOnChainBalances(
mapped,
this.network.addresses.contracts.multicall,
this.network.addresses.contracts.vault,
this.provider
);
console.timeEnd(`fetching on-chain balances for ${mapped.length} pools`);

logger.timeEnd(`fetching on-chain balances for ${mapped.length} pools`);

return onChainBalances;
}
Expand Down
Loading