Skip to content

Commit

Permalink
Merge commit 'e1efe6b68f4528287410adb0a7d5782273959b7b' into integrat…
Browse files Browse the repository at this point in the history
…e-api

# Conflicts:
#	src/entities/exit/parser.ts
#	src/entities/join/parser.ts
#	test/weightedExit.integration.test.ts
  • Loading branch information
Luiz Gustavo Abou Hatem De Liz committed Sep 26, 2023
2 parents d0c1116 + e1efe6b commit 50875db
Show file tree
Hide file tree
Showing 18 changed files with 433 additions and 320 deletions.
8 changes: 8 additions & 0 deletions .changeset/large-socks-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@balancer/sdk": minor
---

- Add join/exit pool support (non-nested pools)
- Weighted pool type
- Uses balancerHelpers to query amounts in/out rather than relying on specific pool math and associated data
- Integration tests run against local hardhat fork
13 changes: 5 additions & 8 deletions examples/exit/weighted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ dotenv.config();
import { BalancerApi } from "../../src/data/providers/balancer-api";
import {
ChainId,
CHAINS, ExitKind,
CHAINS, ExitKind, PoolExit,
SingleAssetExitInput,
Slippage,
Token,
Expand All @@ -22,7 +22,6 @@ import {
} from "viem";
import { PoolState } from "../../src/data/providers/balancer-api/modules/pool-state/types";
import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper";
import { ExitParser } from "../../src/entities/exit/parser";

const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql';
const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH
Expand Down Expand Up @@ -59,13 +58,11 @@ const exit = async () => {
blockNumber,
);


const exitParser = new ExitParser();
const weightedExit = exitParser.getExit(poolState.type);

const bptIn = TokenAmount.fromHumanAmount(bpt, '1');
const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL

const poolExit = new PoolExit();

const exitInput: SingleAssetExitInput = {
chainId,
rpcUrl,
Expand All @@ -74,10 +71,10 @@ const exit = async () => {
kind: ExitKind.SINGLE_ASSET,
};

const queryResult = await weightedExit.query(exitInput, poolState);
const queryResult = await poolExit.query(exitInput, poolState);

const { call, to, value, maxBptIn, minAmountsOut } =
weightedExit.buildCall({
poolExit.buildCall({
...queryResult,
slippage,
sender: testAddress,
Expand Down
11 changes: 4 additions & 7 deletions examples/join/weighted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import dotenv from 'dotenv';
dotenv.config();

import { BalancerApi } from "../../src/data/providers/balancer-api";
import { ChainId, CHAINS, JoinKind, Slippage, Token, TokenAmount, UnbalancedJoinInput } from "../../src";
import { ChainId, CHAINS, JoinKind, PoolJoin, Slippage, Token, TokenAmount, UnbalancedJoinInput } from "../../src";
import {
Client,
createTestClient,
Expand All @@ -15,7 +15,6 @@ import {
} from "viem";
import { PoolState } from "../../src/data/providers/balancer-api/modules/pool-state/types";
import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper";
import { JoinParser } from "../../src/entities/join/parser";

const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql';
const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH
Expand Down Expand Up @@ -51,9 +50,7 @@ const join = async () => {
);


const joinParser = new JoinParser();
const weightedJoin = joinParser.getJoin(poolState.type);

const poolJoin = new PoolJoin();
const poolTokens = poolState.tokens.map(
(t) => new Token(chainId, t.address, t.decimals),
);
Expand All @@ -69,10 +66,10 @@ const join = async () => {
kind: JoinKind.Unbalanced,
};

const queryResult = await weightedJoin.query(joinInput, poolState);
const queryResult = await poolJoin.query(joinInput, poolState);

const { call, to, value, maxAmountsIn, minBptOut } =
weightedJoin.buildCall({
poolJoin.buildCall({
...queryResult,
slippage,
sender: testAddress,
Expand Down
1 change: 1 addition & 0 deletions src/entities/exit/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './poolExit';
export * from './types';
21 changes: 0 additions & 21 deletions src/entities/exit/parser.ts
Original file line number Diff line number Diff line change
@@ -1,21 +0,0 @@
import { BaseExit, ExitConfig } from './types';
import { WeightedExit } from './weighted/weightedExit';

/*********************** Basic Helper to get exit class from pool type *************/

export class ExitParser {
private readonly poolExits: Record<string, BaseExit> = {};

constructor(config?: ExitConfig) {
const { customPoolExits } = config || {};
this.poolExits = {
WEIGHTED: new WeightedExit(),
// custom pool Exits take precedence over base Exits
...customPoolExits,
};
}

public getExit(poolType: string): BaseExit {
return this.poolExits[poolType];
}
}
52 changes: 52 additions & 0 deletions src/entities/exit/poolExit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
BaseExit,
ExitBuildOutput,
ExitCallInput,
ExitConfig,
ExitInput,
ExitQueryResult,
} from './types';
import { WeightedExit } from './weighted/weightedExit';
import { PoolStateInput } from '../types';
import { validateInputs } from './weighted/validateInputs';
import { getSortedTokens } from '../utils/getSortedTokens';

export class PoolExit {
private readonly poolExits: Record<string, BaseExit> = {};

constructor(config?: ExitConfig) {
const { customPoolExits } = config || {};
this.poolExits = {
WEIGHTED: new WeightedExit(),
// custom pool Exits take precedence over base Exits
...customPoolExits,
};
}

public getExit(poolType: string): BaseExit {
if (!this.poolExits[poolType]) {
throw new Error('Unsupported pool type');
}

return this.poolExits[poolType];
}

public async query(
input: ExitInput,
poolState: PoolStateInput,
): Promise<ExitQueryResult> {
validateInputs(input, poolState);

const sortedTokens = getSortedTokens(poolState.tokens, input.chainId);
const mappedPoolState = {
...poolState,
tokens: sortedTokens,
};

return this.getExit(poolState.type).query(input, mappedPoolState);
}

public buildCall(input: ExitCallInput): ExitBuildOutput {
return this.getExit(input.poolType).buildCall(input);
}
}
5 changes: 3 additions & 2 deletions src/entities/exit/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type ExitInput =

// Returned from a exit query
export type ExitQueryResult = {
poolType: string;
id: Address;
exitKind: ExitKind;
bptIn: TokenAmount;
Expand All @@ -54,7 +55,7 @@ export type ExitCallInput = ExitQueryResult & {
recipient: Address;
};

export type BuildOutput = {
export type ExitBuildOutput = {
call: Address;
to: Address;
value: bigint | undefined;
Expand All @@ -64,7 +65,7 @@ export type BuildOutput = {

export interface BaseExit {
query(input: ExitInput, poolState: PoolState): Promise<ExitQueryResult>;
buildCall(input: ExitCallInput): BuildOutput;
buildCall(input: ExitCallInput): ExitBuildOutput;
}

export type ExitConfig = {
Expand Down
4 changes: 2 additions & 2 deletions src/entities/exit/weighted/validateInputs.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ExitInput, ExitKind } from '..';
import { PoolState } from '../../types';
import { PoolStateInput } from '../../types';
import { areTokensInArray } from '../../utils/areTokensInArray';

export function validateInputs(input: ExitInput, poolState: PoolState) {
export function validateInputs(input: ExitInput, poolState: PoolStateInput) {
switch (input.kind) {
case ExitKind.UNBALANCED:
areTokensInArray(
Expand Down
27 changes: 14 additions & 13 deletions src/entities/exit/weighted/weightedExit.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { encodeFunctionData } from 'viem';
import { Token, TokenAmount, WeightedEncoder } from '../../..';
import { Token } from '../../token';
import { TokenAmount } from '../../tokenAmount';
import { WeightedEncoder } from '../../encoders/weighted';
import { Address } from '../../../types';
import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils';
import {
BALANCER_VAULT,
MAX_UINT256,
ZERO_ADDRESS,
} from '../../../utils/constants';
import { vaultAbi } from '../../../abi';
import { parseExitArgs } from '../../utils/parseExitArgs';
import {
BaseExit,
BuildOutput,
ExitBuildOutput,
ExitCallInput,
ExitInput,
ExitKind,
ExitQueryResult,
} from '../types';
import { getSortedTokens } from '../../utils';
import { PoolState, AmountsExit } from '../../types';
import { AmountsExit, PoolState } from '../../types';
import { doQueryExit } from '../../utils/doQueryExit';
import { validateInputs } from './validateInputs';

export class WeightedExit implements BaseExit {
public async query(
input: ExitInput,
poolState: PoolState,
): Promise<ExitQueryResult> {
validateInputs(input, poolState);

const sortedTokens = getSortedTokens(poolState.tokens, input.chainId);

const amounts = this.getAmountsQuery(sortedTokens, input);
const amounts = this.getAmountsQuery(poolState.tokens, input);

const userData = this.encodeUserData(input.kind, amounts);

Expand All @@ -35,7 +35,7 @@ export class WeightedExit implements BaseExit {
chainId: input.chainId,
exitWithNativeAsset: !!input.exitWithNativeAsset,
poolId: poolState.id,
sortedTokens,
sortedTokens: poolState.tokens,
sender: ZERO_ADDRESS,
recipient: ZERO_ADDRESS,
minAmountsOut: amounts.minAmountsOut,
Expand All @@ -57,6 +57,7 @@ export class WeightedExit implements BaseExit {
);

return {
poolType: poolState.type,
exitKind: input.kind,
id: poolState.id,
bptIn,
Expand Down Expand Up @@ -94,7 +95,7 @@ export class WeightedExit implements BaseExit {
}
}

public buildCall(input: ExitCallInput): BuildOutput {
public buildCall(input: ExitCallInput): ExitBuildOutput {
const amounts = this.getAmountsCall(input);

const userData = this.encodeUserData(input.exitKind, amounts);
Expand Down
75 changes: 2 additions & 73 deletions src/entities/join/index.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,2 @@
import { TokenAmount } from '../tokenAmount';
import { Slippage } from '../slippage';
import { PoolState } from '../types';
import { Address, Hex } from '../../types';

export enum JoinKind {
Init = 'Init',
Unbalanced = 'Unbalanced',
SingleAsset = 'SingleAsset',
Proportional = 'Proportional',
}

// This will be extended for each pools specific input requirements
export type BaseJoinInput = {
chainId: number;
rpcUrl: string;
useNativeAssetAsWrappedAmountIn?: boolean;
fromInternalBalance?: boolean;
};

export type InitJoinInput = BaseJoinInput & {
amountsIn: TokenAmount[];
kind: JoinKind.Init;
};

export type UnbalancedJoinInput = BaseJoinInput & {
amountsIn: TokenAmount[];
kind: JoinKind.Unbalanced;
};

export type SingleAssetJoinInput = BaseJoinInput & {
bptOut: TokenAmount;
tokenIn: Address;
kind: JoinKind.SingleAsset;
};

export type ProportionalJoinInput = BaseJoinInput & {
bptOut: TokenAmount;
kind: JoinKind.Proportional;
};

export type JoinInput =
| InitJoinInput
| UnbalancedJoinInput
| SingleAssetJoinInput
| ProportionalJoinInput;

// Returned from a join query
export type JoinQueryResult = {
poolId: Hex;
joinKind: JoinKind;
bptOut: TokenAmount;
amountsIn: TokenAmount[];
fromInternalBalance: boolean;
tokenInIndex?: number;
};

export type JoinCallInput = JoinQueryResult & {
slippage: Slippage;
sender: Address;
recipient: Address;
};

export interface BaseJoin {
query(input: JoinInput, poolState: PoolState): Promise<JoinQueryResult>;
buildCall(input: JoinCallInput): {
call: Hex;
to: Address;
value: bigint | undefined;
minBptOut: bigint;
maxAmountsIn: bigint[];
};
}
export * from './poolJoin';
export * from './types';
24 changes: 0 additions & 24 deletions src/entities/join/parser.ts
Original file line number Diff line number Diff line change
@@ -1,24 +0,0 @@
import { BaseJoin } from '.';
import { WeightedJoin } from './weighted/weightedJoin';

/*********************** Basic Helper to get join class from pool type *************/
export type JoinConfig = {
customPoolJoins: Record<string, BaseJoin>;
};

export class JoinParser {
private readonly poolJoins: Record<string, BaseJoin> = {};

constructor(config?: JoinConfig) {
const { customPoolJoins } = config || {};
this.poolJoins = {
WEIGHTED: new WeightedJoin(),
// custom pool Joins take precedence over base Joins
...customPoolJoins,
};
}

public getJoin(poolType: string): BaseJoin {
return this.poolJoins[poolType];
}
}
Loading

0 comments on commit 50875db

Please sign in to comment.