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

Query Improvements - Making the build queries interface easier to use #473

Merged
merged 10 commits into from
Jul 10, 2023
14 changes: 7 additions & 7 deletions balancer-js/examples/pools/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { BalancerSDK, PoolWithMethods } from '@balancer-labs/sdk'
import { parseEther, formatEther } from '@ethersproject/units'
import { BigNumber } from "@ethersproject/bignumber";

const sdk = new BalancerSDK({
network: 1,
Expand All @@ -21,19 +22,18 @@ const {
// Joining with a single token
const queryJoin = async (pool: PoolWithMethods) => {
const token = pool.tokensList[0];
const maxAmountsInByToken = new Map<string,BigNumber>([[token, parseEther('1')]]);
const joinExactInQuery = pool.buildQueryJoinExactIn({
maxAmountsIn: pool.tokensList.map((t) =>
parseEther(t === token ? '1' : '0')
),
maxAmountsInByToken
});

const response = await contracts.balancerHelpers.callStatic.queryJoin(
...joinExactInQuery
);

console.log(`Joining ${pool.poolType}`);
console.log(`Joining ${ pool.poolType }`);
console.table({
tokens: pool.tokensList.map((t) => `${t.slice(0, 6)}...${t.slice(38, 42)}`),
tokens: pool.tokensList.map((t) => `${ t.slice(0, 6) }...${ t.slice(38, 42) }`),
amountsIn: response.amountsIn.map(formatEther),
bptOut: formatEther(response.bptOut),
});
Expand All @@ -50,9 +50,9 @@ const queryExit = async (pool: PoolWithMethods) => {
...exitToSingleToken
);

console.log(`Exiting ${pool.poolType}`);
console.log(`Exiting ${ pool.poolType }`);
console.table({
tokens: pool.tokensList.map((t) => `${t.slice(0, 6)}...${t.slice(38, 42)}`),
tokens: pool.tokensList.map((t) => `${ t.slice(0, 6) }...${ t.slice(38, 42) }`),
amountsOut: response.amountsOut.map(formatEther),
bptIn: formatEther(response.bptIn),
});
Expand Down
85 changes: 44 additions & 41 deletions balancer-js/src/modules/pools/queries/params_builder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as PoolQueries from './types';
import { BigNumber } from '@ethersproject/bignumber';
import { AddressZero, Zero, MaxUint256 } from '@ethersproject/constants';
import { getEncoder } from './get_encoder';
import { removeItem } from '@/lib/utils';
Expand All @@ -20,26 +21,32 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {

/**
* Encodes the query to get expected amount of BPT when joining a Pool with exact token inputs
*
* @param maxAmountsIn - the amounts each of token to deposit in the pool as liquidity, order needs to match pool.tokensList
* @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens
* @param maxAmountsInByToken - The amounts each of token, mapped by token address, to deposit in the pool as liquidity,
* doesn't need to have all tokens, only the ones that will be deposited
* @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens (optional)
*/
buildQueryJoinExactIn({
sender = AddressZero,
recipient = sender,
maxAmountsIn,
maxAmountsInByToken,
minimumBPT = Zero,
fromInternalBalance = false,
}: PoolQueries.JoinExactInParams): PoolQueries.queryJoinParams {
const bptIndex = this.pool.tokensList.findIndex((token) =>
this.pool.id.includes(token)
);

const assets = [...this.pool.tokensList];

let maxInWithoutBpt = [...maxAmountsIn];
const maxAmountsIn = this.pool.tokensList.map(
(tokenAddress) =>
maxAmountsInByToken.get(tokenAddress) ?? BigNumber.from('0')
);

let maxInWithoutBpt;

// Remove BPT token from amounts for user data
if (bptIndex > -1) {
maxInWithoutBpt = removeItem(maxAmountsIn, bptIndex);
} else {
maxInWithoutBpt = maxAmountsIn;
}
Comment on lines +43 to 50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let maxInWithoutBpt;
// Remove BPT token from amounts for user data
if (bptIndex > -1) {
maxInWithoutBpt = removeItem(maxAmountsIn, bptIndex);
} else {
maxInWithoutBpt = maxAmountsIn;
}
let maxInWithoutBpt = maxAmountsIn;
// Remove BPT token from amounts for user data
if (bptIndex > -1) {
maxInWithoutBpt = removeItem(maxAmountsIn, bptIndex);
}


const userData = this.encoder.joinExactTokensInForBPTOut(
Expand All @@ -49,13 +56,13 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {

const params = [
this.pool.id,
sender,
recipient,
AddressZero, // sender is ignored on query join, so it can be just address zero
AddressZero, // same as sender
{
assets,
maxAmountsIn,
userData,
fromInternalBalance,
fromInternalBalance: false, // from internal balance is ignored on query join, can be just false
},
] as PoolQueries.queryJoinParams;

Expand All @@ -65,17 +72,14 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {
/**
* Encodes the query to get expected token amount when joining a Pool specifying fixed BPT out.
*
* @param maxAmountsIn - max limits of amounts provided as liquidity, can be set to zero, ordered same as pool.tokensList
* @param maxAmountIn - The max expected amount for tokenIn (optional)
* @param bptOut - the expected BPT for providing liquidity
* @param tokenIn - address of a token joining the pool
*/
buildQueryJoinExactOut({
sender = AddressZero,
recipient = sender,
maxAmountsIn = [],
maxAmountIn,
bptOut,
tokenIn,
fromInternalBalance = false,
}: PoolQueries.JoinExactOutParams): PoolQueries.queryJoinParams {
const bptIndex = this.pool.tokensList.findIndex((token) =>
this.pool.id.includes(token)
Expand All @@ -87,16 +91,20 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {
const tokenIndex = tokensWithoutBpt.indexOf(tokenIn);

const userData = this.encoder.joinTokenInForExactBPTOut(bptOut, tokenIndex);

const maxAmountsIn = maxAmountIn
? this.pool.tokensList.map((tokenAddress) =>
tokenAddress === tokenIn ? maxAmountIn : '0'
)
: [];
const params = [
this.pool.id,
sender,
recipient,
AddressZero, // sender is ignored on query join, so it can be just address zero
AddressZero, // same as sender
{
assets: this.pool.tokensList,
maxAmountsIn,
userData,
fromInternalBalance,
fromInternalBalance: false, // from internal balance is ignored on query join, can be just false
},
] as PoolQueries.queryJoinParams;

Expand All @@ -106,17 +114,14 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {
/**
* Encodes the query for exiting the pool to a single token
*
* @param minAmountsOut - minimum expected amounts, can be set to zero for a query, ordered same as pool.tokensList
* @param minAmountOut - minimum expected amount for token out
* @param bptIn - BPT, shares of the pool liquidity
* @param tokenOut - address of an exit liquidity token
*/
buildQueryExitToSingleToken({
sender = AddressZero,
recipient = sender,
minAmountsOut = [],
minAmountOut,
bptIn,
tokenOut,
toInternalBalance = false,
}: PoolQueries.ExitToSingleTokenParams): PoolQueries.queryExitParams {
const bptIndex = this.pool.tokensList.findIndex((token) =>
this.pool.id.includes(token)
Expand All @@ -131,16 +136,20 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {
bptIn,
tokenIndex
);

const minAmountsOut = minAmountOut
? this.pool.tokensList.map((tokenAddress) =>
tokenAddress === tokenOut ? minAmountOut : '0'
)
: [];
const params = [
this.pool.id,
sender,
recipient,
AddressZero, // sender is ignored on query join, so it can be just address zero
AddressZero, // same as sender
{
assets: this.pool.tokensList,
minAmountsOut,
userData,
toInternalBalance,
toInternalBalance: false, // to internal balance is ignored on query join, can be just false
},
] as PoolQueries.queryExitParams;

Expand All @@ -155,11 +164,8 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {
* @param bptIn - BPT, shares of the pool liquidity
*/
buildQueryExitProportionally({
sender = AddressZero,
recipient = sender,
minAmountsOut = [],
bptIn,
toInternalBalance = false,
}: PoolQueries.ExitProportionallyParams): PoolQueries.queryExitParams {
if (!this.encoder.exitExactBPTInForTokensOut) {
throw 'Proportional exit not implemented';
Expand All @@ -169,13 +175,13 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {

const params = [
this.pool.id,
sender,
recipient,
AddressZero, // sender is ignored on query join, so it can be just address zero
AddressZero, // same as sender
{
assets: this.pool.tokensList,
minAmountsOut,
userData,
toInternalBalance,
toInternalBalance: false,
},
] as PoolQueries.queryExitParams;

Expand All @@ -189,11 +195,8 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {
* @param maxBptIn - BPT, shares of the pool liquidity, can be set to zero for a query
*/
buildQueryExitExactOut({
sender = AddressZero,
recipient = sender,
minAmountsOut,
maxBptIn = MaxUint256,
toInternalBalance = false,
}: PoolQueries.ExitExactOutParams): PoolQueries.queryExitParams {
const bptIndex = this.pool.tokensList.findIndex((token) =>
this.pool.id.includes(token)
Expand All @@ -212,13 +215,13 @@ export class ParamsBuilder implements PoolQueries.ParamsBuilder {

const params = [
this.pool.id,
sender,
recipient,
AddressZero, // sender is ignored on query join, so it can be just address zero
AddressZero, // same as sender
{
assets: this.pool.tokensList,
minAmountsOut,
userData,
toInternalBalance,
toInternalBalance: false, // to internal balance is ignored on query join, can be just false
},
] as PoolQueries.queryExitParams;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect } from 'chai';
import { BalancerSDK, Network, PoolType } from '@/.';
import { bn } from '@/lib/utils';
import { ParamsBuilder } from '.';

import { BigNumber } from '@ethersproject/bignumber';
dotenv.config();

const rpcUrl = process.env.ALCHEMY_URL || 'http://127.0.0.1:8545';
Expand Down Expand Up @@ -69,11 +69,12 @@ describe('join and exit queries', () => {
});

it('should joinExactIn', async () => {
const maxAmountsIn = Array(pool.tokensList.length).fill(bn(0));
maxAmountsIn[1] = bn(1);
const maxAmountsInByToken = new Map<string, BigNumber>([
[pool.tokensList[1], bn(1)],
]);

const params = queryParams.buildQueryJoinExactIn({
maxAmountsIn,
maxAmountsInByToken,
});
const join = await balancerHelpers.callStatic.queryJoin(...params);
expect(Number(join.bptOut)).to.be.gt(0);
Expand Down Expand Up @@ -116,10 +117,12 @@ describe('join and exit queries', () => {
pool.id.includes(token)
);
const minAmountsOut = Array(pool.tokensList.length).fill(bn(1));
const tokensOut = pool.tokensList;
if (bptIndex > -1) minAmountsOut[bptIndex] = bn(0);

const params = queryParams.buildQueryExitExactOut({
minAmountsOut,
tokensOut,
});
const exit = await balancerHelpers.callStatic.queryExit(...params);
expect(Number(exit.bptIn)).to.be.gt(0);
Expand Down
30 changes: 12 additions & 18 deletions balancer-js/src/modules/pools/queries/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ export interface Encoder {
amountsIn: BigNumber[],
minimumBPT: BigNumber
): string;

joinTokenInForExactBPTOut(
bptAmountOut: BigNumber,
enterTokenIndex: number
): string;

exitExactBPTInForOneTokenOut(
bptAmountIn: BigNumber,
exitTokenIndex: number
): string;

exitExactBPTInForTokensOut?(bptAmountIn: BigNumber): string;

exitBPTInForExactTokensOut(
amountsOut: BigNumber[],
maxBPTAmountIn: BigNumber
Expand All @@ -23,11 +27,15 @@ export interface Encoder {

export interface ParamsBuilder {
buildQueryJoinExactIn(params: JoinExactInParams): queryJoinParams;

buildQueryJoinExactOut(params: JoinExactOutParams): queryJoinParams;

buildQueryExitToSingleToken(params: ExitToSingleTokenParams): queryExitParams;

buildQueryExitProportionally(
params: ExitProportionallyParams
): queryExitParams;

buildQueryExitExactOut(params: ExitExactOutParams): queryExitParams;
}

Expand All @@ -43,45 +51,31 @@ export interface Pool {
}

export interface JoinExactInParams {
sender?: string;
recipient?: string;
maxAmountsIn: BigNumber[];
maxAmountsInByToken: Map<string, BigNumber>;
minimumBPT?: BigNumber;
fromInternalBalance?: boolean;
}

export interface JoinExactOutParams {
sender?: string;
recipient?: string;
maxAmountsIn?: BigNumber[];
maxAmountIn?: BigNumber;
bptOut: BigNumber;
tokenIn: string;
fromInternalBalance?: boolean;
}

export interface ExitToSingleTokenParams {
sender?: string;
recipient?: string;
minAmountsOut?: BigNumber[];
minAmountOut?: BigNumber;
bptIn: BigNumber;
tokenOut: string;
toInternalBalance?: boolean;
}

export interface ExitProportionallyParams {
sender?: string;
recipient?: string;
minAmountsOut?: BigNumber[];
bptIn: BigNumber;
toInternalBalance?: boolean;
}

export interface ExitExactOutParams {
sender?: string;
recipient?: string;
minAmountsOut: BigNumber[];
tokensOut: string[];
maxBptIn?: BigNumber;
toInternalBalance?: boolean;
}

export type queryJoinParams = [
Expand Down