From 91b735be9e3bd109b523a1226b8d92a3747289e5 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 3 Aug 2023 12:13:32 +0100 Subject: [PATCH 001/199] WIP Join implementation. --- src/joinHelper.ts | 205 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/joinHelper.ts diff --git a/src/joinHelper.ts b/src/joinHelper.ts new file mode 100644 index 00000000..8e8537f6 --- /dev/null +++ b/src/joinHelper.ts @@ -0,0 +1,205 @@ +import { TokenAmount } from './entities'; + +/*********************** Base Types - used globally across pools *******************/ +// Returned from API and used as input +export type PoolState = { + id: string; + assets: string[]; + // TODO - Possibly add encoding info here? +}; + +// Returned from a Query +export type QueryResult = { + id: string; + assets: string[]; + joinKind: string; + bptOut: TokenAmount; + amountsIn: TokenAmount[]; +}; + +// This will be extended for each pools specific input requirements +export type BaseInput = { + chainId: number; + rpcUrl: string; +}; + +export interface BaseJoin { + getInstance(): BaseJoin; + query(input: BaseInput, poolState: PoolState): Promise; + getCall(input: QueryResult & { slippage: string }): string; // TODO - Best way to represent slippage? +} + +/*********************** Example For Weighted Pool Implementation ******************/ +export type ExactIn = BaseInput & { + tokenAmounts: TokenAmount[]; +}; + +export type ExactOut = BaseInput & { + bptAmount: TokenAmount; +}; + +export type Init = BaseInput & { + tokenAmounts: TokenAmount[]; +}; + +export class JoinWeighted implements BaseJoin { + // TODO - Probably not needed + getInstance(): JoinWeighted { + return new JoinWeighted(); + } + + public async query( + input: Init | ExactIn | ExactOut, + poolState: PoolState, + ): Promise { + // TODO - This would need extended to work with relayer + + let swapKind: string; + let userData = ''; + if ('bptAmount' in input) { + const exactBptOut = input.bptAmount; + userData = `Encode userData as exact out [${exactBptOut}]`; + swapKind = 'ExactOut'; + } else { + // TODO - If we allow only assets the user wants instead of full pool tokens we need to map them here + const amountsIn = input.tokenAmounts.map((t) => + t.amount.toString(), + ); + // We can use 0 here as only querying + const minBpt = '0'; + userData = `Encode userData as exact in [${amountsIn}, ${minBpt}]`; + swapKind = 'ExactIn'; + } + + const queryArgs = this.getJoinParameters( + poolState.id, + poolState.assets, + '0x', + '0x', + Array(poolState.assets.length).fill('0'), // We use 0 as only querying + userData, + ); + + // Do query and get bptOut/amountsIn + + return { + joinKind: swapKind, + id: poolState.id, + assets: poolState.assets, + bptOut: {} as TokenAmount, + amountsIn: [{} as TokenAmount], + }; + } + + public getCall( + input: QueryResult & { + slippage: string; + sender: string; + receiver: string; + }, + ): string { + let maxAmountsIn: string[]; + let userData: string; + if (input.joinKind === 'INIT') { + maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); + userData = `Encode userData as init [${maxAmountsIn}]`; + } else if (input.joinKind === 'ExactIn') { + maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); + const minBptOut = input.bptOut; // TODO sub slippage here + userData = `Encode userData as exact in [${maxAmountsIn}, ${minBptOut}]`; + } else { + maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); // TODO add slippage here + const exactBptOut = input.bptOut; + userData = `Encode userData as exact out [${exactBptOut}]`; + } + const queryArgs = this.getJoinParameters( + input.id, + input.assets, + input.sender, + input.receiver, + maxAmountsIn, + userData, + ); + // Encode data + return 'thisisanencodedjoin'; + } + + private getJoinParameters( + poolId: string, + assets: string[], + sender: string, + recipient: string, + maxAmountsIn: string[], + userData: String, + ) { + const joinPoolRequest = { + assets, + maxAmountsIn, + userData, + fromInternalBalance: false, + }; + + return { + poolId, + sender, + recipient, + joinPoolRequest, + }; + } +} +/***********************************************************************************/ + +/*********************** Basic Helper to get join class from pool type *************/ +export type JoinConfig = { + customPoolFactories?: Record; +}; + +export class JoinHelper { + private readonly poolFactories: Record = {}; + + constructor({ customPoolFactories = {} }: JoinConfig) { + this.poolFactories['weighted'] = new JoinWeighted(); + this.poolFactories = { ...customPoolFactories }; + // custom pool factories take precedence over base factories + } + + public async getJoin(poolType: string): Promise { + // TODO - Need to parse + return this.poolFactories[poolType]; + } +} + +/*********************** Mock To Represent API Requirements **********************/ +type ApiResponse = { + id: string; + type: string; + assets: string[]; +}; + +export class MockApi { + public async getPool(id: string): Promise { + return { + id, + type: 'Weighted', + assets: ['0xtoken1Addr', '0xtoken2Addr'], + }; + } +} + +const api = new MockApi(); +/******************************************************************************/ + +/*********************** Example of Possible Flow *****************************/ + +const joinHelper = new JoinHelper({}); +const poolId = '0xpoolId'; +// Calls API +const poolFromApi = await api.getPool(poolId); +const join = await joinHelper.getJoin(poolFromApi.type); +const queryInput: ExactIn = { + tokenAmounts: ['amount1', 'amount2'], +}; +const queryResult = await join.query(queryInput, poolFromApi); +const call = await join.getCall({ ...queryResult, slippage: '10' }); +console.log(call); // Make call +/******************************************************************************/ From f297e2b87657626eca2e930b26029fd45ea976c4 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 29 Aug 2023 16:38:25 -0300 Subject: [PATCH 002/199] Initial suggestion for join implementation structure (WIP) --- src/entities/index.ts | 1 + src/entities/join.ts | 25 +++ src/entities/pools/index.ts | 36 ++++ src/entities/pools/weighted/index.ts | 1 + src/entities/pools/weighted/weightedJoin.ts | 187 ++++++++++++++++++ src/joinHelper.ts | 205 -------------------- test/weightedJoin.test.ts | 48 +++++ 7 files changed, 298 insertions(+), 205 deletions(-) create mode 100644 src/entities/join.ts create mode 100644 src/entities/pools/weighted/weightedJoin.ts delete mode 100644 src/joinHelper.ts create mode 100644 test/weightedJoin.test.ts diff --git a/src/entities/index.ts b/src/entities/index.ts index 66d20d26..853163fa 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -3,3 +3,4 @@ export * from './swap'; export * from './token'; export * from './tokenAmount'; export * from './pools/'; +export * from './join'; diff --git a/src/entities/join.ts b/src/entities/join.ts new file mode 100644 index 00000000..ca0bfb21 --- /dev/null +++ b/src/entities/join.ts @@ -0,0 +1,25 @@ +import { BaseJoin } from './pools'; +import { JoinWeighted } from './pools/weighted'; + +/*********************** Basic Helper to get join class from pool type *************/ +export type JoinConfig = { + customPoolFactories: Record; +}; + +export class JoinHelper { + private readonly poolFactories: Record = {}; + + constructor(config?: JoinConfig) { + const { customPoolFactories } = config || {}; + this.poolFactories = { + weighted: new JoinWeighted(), + // custom pool factories take precedence over base factories + ...customPoolFactories, + }; + } + + public getJoin(poolType: string): BaseJoin { + // TODO - Need to parse + return this.poolFactories[poolType]; + } +} diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index fc7d6e9a..0e50401b 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -33,3 +33,39 @@ export interface BasePoolFactory { isPoolForFactory(pool: RawPool): boolean; create(chainId: number, pool: RawPool): BasePool; } + +// Returned from API and used as input +export type PoolState = { + id: string; + type: string; + assets: string[]; // already properly sorted in case different versions sort them differently + // TODO - Possibly add encoding info here? +}; + +// This will be extended for each pools specific input requirements +export type JoinInput = { + tokenAmounts: TokenAmount[]; + chainId: number; + rpcUrl: string; + isInit?: boolean; +}; + +// Returned from a join query +export type JoinQueryResult = { + id: string; + assets: string[]; + joinKind: string; + bptOut: TokenAmount; + amountsIn: TokenAmount[]; +}; + +export interface BaseJoin { + getInstance(): BaseJoin; + query(input: JoinInput, poolState: PoolState): Promise; + // TODO - Best way to represent slippage? + getCall(input: JoinQueryResult & { slippage: string }): { + call: string; + to: string; + value: string; + }; +} diff --git a/src/entities/pools/weighted/index.ts b/src/entities/pools/weighted/index.ts index f5256553..36268989 100644 --- a/src/entities/pools/weighted/index.ts +++ b/src/entities/pools/weighted/index.ts @@ -1,3 +1,4 @@ export * from './weightedFactory'; export * from './weightedPool'; export * from './weightedMath'; +export * from './weightedJoin'; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts new file mode 100644 index 00000000..10a8ab58 --- /dev/null +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -0,0 +1,187 @@ +import { BaseJoin, JoinInput, JoinQueryResult, PoolState } from '..'; +import { Address } from '../../../types'; +import { ZERO_ADDRESS, getPoolAddress } from '../../../utils'; +import { TokenAmount } from '../../tokenAmount'; + +export class JoinWeighted implements BaseJoin { + // TODO - Probably not needed + getInstance(): JoinWeighted { + return new JoinWeighted(); + } + + public async query( + input: JoinInput, + poolState: PoolState, + ): Promise { + // TODO - This would need extended to work with relayer + + this.checkInputs(input, poolState); + + // infer join kind by input tokens + const poolAddress = getPoolAddress(poolState.id) as Address; + const joinKind = this.getJoinKind(input, poolAddress); + + // Initialize join parameters + let amountsIn = Array(poolState.assets.length).fill('0'); + let userData = ''; + + switch (joinKind) { + case 'Init': { + amountsIn = this.getAmountsIn(input, poolState.assets); + userData = `Encode userData as Init [${amountsIn}]`; + break; + } + case 'GivenIn': { + amountsIn = this.getAmountsIn(input, poolState.assets); + const bptOut = '0'; + userData = `Encode userData as GivenIn [${amountsIn}, ${bptOut}]`; + break; + } + case 'GivenOut': { + const bptOut = input.tokenAmounts[0].amount.toString(); + userData = `Encode userData as exact out [${bptOut}]`; + break; + } + default: + throw new Error('Invalid join kind'); + } + + const queryArgs = this.getJoinParameters({ + poolId: poolState.id, + assets: poolState.assets, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + maxAmountsIn: amountsIn, + userData, + }); + + // Do query and get bptOut/amountsIn + console.log(queryArgs); + + return { + joinKind, + id: poolState.id, + assets: poolState.assets, + bptOut: {} as TokenAmount, // TODO: update with query result if needed + amountsIn: [{} as TokenAmount], // TODO: update with query result if needed + }; + } + + public getCall( + input: JoinQueryResult & { + slippage: string; + sender: string; + receiver: string; + }, + ): { call: string; to: string; value: string } { + let maxAmountsIn: string[]; + let userData: string; + + switch (input.joinKind) { + case 'Init': { + maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); + userData = `Encode userData as init [${maxAmountsIn}]`; + break; + } + case 'GivenIn': { + maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); + const minBptOut = input.bptOut; // TODO sub slippage here + userData = `Encode userData as exact in [${maxAmountsIn}, ${minBptOut}]`; + break; + } + case 'GivenOut': { + maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); // TODO add slippage here + const exactBptOut = input.bptOut; + userData = `Encode userData as exact out [${exactBptOut}]`; + break; + } + default: + throw new Error('Invalid join kind'); + } + + const queryArgs = this.getJoinParameters({ + poolId: input.id, + assets: input.assets, + sender: input.sender, + recipient: input.receiver, + maxAmountsIn, + userData, + }); + + const call = JSON.stringify(queryArgs); // TODO - Encode data + + // Encode data + return { + call, + to: '0xbalancerVaultAddress', + value: '0', // TODO: ETH value when joining with ETH + }; + } + + private getJoinParameters({ + poolId, + assets, + sender, + recipient, + maxAmountsIn, + userData, + }: { + poolId: string; + assets: string[]; + sender: string; + recipient: string; + maxAmountsIn: string[]; + userData: string; + }) { + const joinPoolRequest = { + assets, // with BPT + maxAmountsIn, // with BPT + userData, // wihtout BPT + fromInternalBalance: false, + }; + + return { + poolId, + sender, + recipient, + joinPoolRequest, + }; + } + + private checkInputs(input: JoinInput, poolState: PoolState) { + const tokensIn = input.tokenAmounts.map((t) => t.token.address); + if (input.tokenAmounts.length === 0) { + throw new Error('Must specify at least one input'); + } else if (tokensIn.some((t) => !poolState.assets.includes(t))) { + throw new Error('Input token not in pool'); + } else if (tokensIn.includes(getPoolAddress(poolState.id) as Address)) { + if (tokensIn.length > 1) { + throw new Error('Cannot join with BPT and other tokens'); + } else if (input.isInit) { + throw new Error('Cannot init with BPT'); + } + } + } + + private getJoinKind(input: JoinInput, poolAddress: Address): string { + const tokensIn = input.tokenAmounts.map((t) => t.token.address); + if (tokensIn.includes(poolAddress)) { + return 'GivenOut'; + } else { + return input.isInit ? 'Init' : 'GivenIn'; + } + } + + private getAmountsIn(input: JoinInput, poolAssets: string[]): string[] { + return poolAssets.map((asset) => { + let amountIn = '0'; + const tokenIn = input.tokenAmounts.find( + (t) => t.token.address === asset, + ); + if (tokenIn) { + amountIn = tokenIn.amount.toString(); + } + return amountIn; + }); + } +} diff --git a/src/joinHelper.ts b/src/joinHelper.ts deleted file mode 100644 index 8e8537f6..00000000 --- a/src/joinHelper.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { TokenAmount } from './entities'; - -/*********************** Base Types - used globally across pools *******************/ -// Returned from API and used as input -export type PoolState = { - id: string; - assets: string[]; - // TODO - Possibly add encoding info here? -}; - -// Returned from a Query -export type QueryResult = { - id: string; - assets: string[]; - joinKind: string; - bptOut: TokenAmount; - amountsIn: TokenAmount[]; -}; - -// This will be extended for each pools specific input requirements -export type BaseInput = { - chainId: number; - rpcUrl: string; -}; - -export interface BaseJoin { - getInstance(): BaseJoin; - query(input: BaseInput, poolState: PoolState): Promise; - getCall(input: QueryResult & { slippage: string }): string; // TODO - Best way to represent slippage? -} - -/*********************** Example For Weighted Pool Implementation ******************/ -export type ExactIn = BaseInput & { - tokenAmounts: TokenAmount[]; -}; - -export type ExactOut = BaseInput & { - bptAmount: TokenAmount; -}; - -export type Init = BaseInput & { - tokenAmounts: TokenAmount[]; -}; - -export class JoinWeighted implements BaseJoin { - // TODO - Probably not needed - getInstance(): JoinWeighted { - return new JoinWeighted(); - } - - public async query( - input: Init | ExactIn | ExactOut, - poolState: PoolState, - ): Promise { - // TODO - This would need extended to work with relayer - - let swapKind: string; - let userData = ''; - if ('bptAmount' in input) { - const exactBptOut = input.bptAmount; - userData = `Encode userData as exact out [${exactBptOut}]`; - swapKind = 'ExactOut'; - } else { - // TODO - If we allow only assets the user wants instead of full pool tokens we need to map them here - const amountsIn = input.tokenAmounts.map((t) => - t.amount.toString(), - ); - // We can use 0 here as only querying - const minBpt = '0'; - userData = `Encode userData as exact in [${amountsIn}, ${minBpt}]`; - swapKind = 'ExactIn'; - } - - const queryArgs = this.getJoinParameters( - poolState.id, - poolState.assets, - '0x', - '0x', - Array(poolState.assets.length).fill('0'), // We use 0 as only querying - userData, - ); - - // Do query and get bptOut/amountsIn - - return { - joinKind: swapKind, - id: poolState.id, - assets: poolState.assets, - bptOut: {} as TokenAmount, - amountsIn: [{} as TokenAmount], - }; - } - - public getCall( - input: QueryResult & { - slippage: string; - sender: string; - receiver: string; - }, - ): string { - let maxAmountsIn: string[]; - let userData: string; - if (input.joinKind === 'INIT') { - maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); - userData = `Encode userData as init [${maxAmountsIn}]`; - } else if (input.joinKind === 'ExactIn') { - maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); - const minBptOut = input.bptOut; // TODO sub slippage here - userData = `Encode userData as exact in [${maxAmountsIn}, ${minBptOut}]`; - } else { - maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); // TODO add slippage here - const exactBptOut = input.bptOut; - userData = `Encode userData as exact out [${exactBptOut}]`; - } - const queryArgs = this.getJoinParameters( - input.id, - input.assets, - input.sender, - input.receiver, - maxAmountsIn, - userData, - ); - // Encode data - return 'thisisanencodedjoin'; - } - - private getJoinParameters( - poolId: string, - assets: string[], - sender: string, - recipient: string, - maxAmountsIn: string[], - userData: String, - ) { - const joinPoolRequest = { - assets, - maxAmountsIn, - userData, - fromInternalBalance: false, - }; - - return { - poolId, - sender, - recipient, - joinPoolRequest, - }; - } -} -/***********************************************************************************/ - -/*********************** Basic Helper to get join class from pool type *************/ -export type JoinConfig = { - customPoolFactories?: Record; -}; - -export class JoinHelper { - private readonly poolFactories: Record = {}; - - constructor({ customPoolFactories = {} }: JoinConfig) { - this.poolFactories['weighted'] = new JoinWeighted(); - this.poolFactories = { ...customPoolFactories }; - // custom pool factories take precedence over base factories - } - - public async getJoin(poolType: string): Promise { - // TODO - Need to parse - return this.poolFactories[poolType]; - } -} - -/*********************** Mock To Represent API Requirements **********************/ -type ApiResponse = { - id: string; - type: string; - assets: string[]; -}; - -export class MockApi { - public async getPool(id: string): Promise { - return { - id, - type: 'Weighted', - assets: ['0xtoken1Addr', '0xtoken2Addr'], - }; - } -} - -const api = new MockApi(); -/******************************************************************************/ - -/*********************** Example of Possible Flow *****************************/ - -const joinHelper = new JoinHelper({}); -const poolId = '0xpoolId'; -// Calls API -const poolFromApi = await api.getPool(poolId); -const join = await joinHelper.getJoin(poolFromApi.type); -const queryInput: ExactIn = { - tokenAmounts: ['amount1', 'amount2'], -}; -const queryResult = await join.query(queryInput, poolFromApi); -const call = await join.getCall({ ...queryResult, slippage: '10' }); -console.log(call); // Make call -/******************************************************************************/ diff --git a/test/weightedJoin.test.ts b/test/weightedJoin.test.ts new file mode 100644 index 00000000..422ba461 --- /dev/null +++ b/test/weightedJoin.test.ts @@ -0,0 +1,48 @@ +// pnpm test -- weightedJoin.test.ts +import { describe, expect, test, beforeAll } from 'vitest'; +import { + JoinHelper, + JoinInput, + PoolState, + Token, + TokenAmount, +} from '../src/entities'; +import { ChainId } from '../src/utils'; + +describe('weighted join test', () => { + let api: MockApi; + beforeAll(() => { + api = new MockApi(); + }); + test('should join', async () => { + const joinHelper = new JoinHelper(); + const poolId = '0xpoolId'; + // Calls API + const poolFromApi = await api.getPool(poolId); + const join = joinHelper.getJoin(poolFromApi.type); + const tokenIn = new Token(ChainId.MAINNET, '0xtoken1Addr', 18); + const queryInput: JoinInput = { + tokenAmounts: [TokenAmount.fromHumanAmount(tokenIn, '1')], + chainId: ChainId.MAINNET, + rpcUrl: '', + }; + const queryResult = await join.query(queryInput, poolFromApi); + const call = join.getCall({ ...queryResult, slippage: '10' }); + console.log(call); // Make call + expect(true).toEqual(true); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: string): Promise { + return { + id, + type: 'Weighted', + assets: ['0xtoken1Addr', '0xtoken2Addr'], + }; + } +} + +/******************************************************************************/ From 7e4bd2e0cf73b75f7012a3669010ff9891ca18ba Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 30 Aug 2023 11:26:02 -0300 Subject: [PATCH 003/199] Minor refactor to apply code review suggestions --- src/entities/join.ts | 20 ++++++++++---------- src/entities/pools/index.ts | 2 +- src/entities/pools/weighted/weightedJoin.ts | 8 ++++---- test/weightedJoin.test.ts | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/entities/join.ts b/src/entities/join.ts index ca0bfb21..cee7f98c 100644 --- a/src/entities/join.ts +++ b/src/entities/join.ts @@ -1,25 +1,25 @@ import { BaseJoin } from './pools'; -import { JoinWeighted } from './pools/weighted'; +import { WeightedJoin } from './pools/weighted'; /*********************** Basic Helper to get join class from pool type *************/ export type JoinConfig = { - customPoolFactories: Record; + customPoolJoins: Record; }; -export class JoinHelper { - private readonly poolFactories: Record = {}; +export class JoinParser { + private readonly poolJoins: Record = {}; constructor(config?: JoinConfig) { - const { customPoolFactories } = config || {}; - this.poolFactories = { - weighted: new JoinWeighted(), - // custom pool factories take precedence over base factories - ...customPoolFactories, + const { customPoolJoins } = config || {}; + this.poolJoins = { + weighted: new WeightedJoin(), + // custom pool Joins take precedence over base Joins + ...customPoolJoins, }; } public getJoin(poolType: string): BaseJoin { // TODO - Need to parse - return this.poolFactories[poolType]; + return this.poolJoins[poolType]; } } diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index 0e50401b..94ba067f 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -63,7 +63,7 @@ export interface BaseJoin { getInstance(): BaseJoin; query(input: JoinInput, poolState: PoolState): Promise; // TODO - Best way to represent slippage? - getCall(input: JoinQueryResult & { slippage: string }): { + buildCall(input: JoinQueryResult & { slippage: string }): { call: string; to: string; value: string; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index 10a8ab58..b5267679 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -3,10 +3,10 @@ import { Address } from '../../../types'; import { ZERO_ADDRESS, getPoolAddress } from '../../../utils'; import { TokenAmount } from '../../tokenAmount'; -export class JoinWeighted implements BaseJoin { +export class WeightedJoin implements BaseJoin { // TODO - Probably not needed - getInstance(): JoinWeighted { - return new JoinWeighted(); + getInstance(): WeightedJoin { + return new WeightedJoin(); } public async query( @@ -67,7 +67,7 @@ export class JoinWeighted implements BaseJoin { }; } - public getCall( + public buildCall( input: JoinQueryResult & { slippage: string; sender: string; diff --git a/test/weightedJoin.test.ts b/test/weightedJoin.test.ts index 422ba461..15c62a42 100644 --- a/test/weightedJoin.test.ts +++ b/test/weightedJoin.test.ts @@ -1,8 +1,8 @@ // pnpm test -- weightedJoin.test.ts import { describe, expect, test, beforeAll } from 'vitest'; import { - JoinHelper, JoinInput, + JoinParser, PoolState, Token, TokenAmount, @@ -15,19 +15,19 @@ describe('weighted join test', () => { api = new MockApi(); }); test('should join', async () => { - const joinHelper = new JoinHelper(); + const joins = new JoinParser(); const poolId = '0xpoolId'; // Calls API const poolFromApi = await api.getPool(poolId); - const join = joinHelper.getJoin(poolFromApi.type); + const weightedJoin = joins.getJoin(poolFromApi.type); const tokenIn = new Token(ChainId.MAINNET, '0xtoken1Addr', 18); const queryInput: JoinInput = { tokenAmounts: [TokenAmount.fromHumanAmount(tokenIn, '1')], chainId: ChainId.MAINNET, rpcUrl: '', }; - const queryResult = await join.query(queryInput, poolFromApi); - const call = join.getCall({ ...queryResult, slippage: '10' }); + const queryResult = await weightedJoin.query(queryInput, poolFromApi); + const call = weightedJoin.buildCall({ ...queryResult, slippage: '10' }); console.log(call); // Make call expect(true).toEqual(true); }); From 6be1c05d080a5e55e2fabadf5480210f571e6e8f Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 30 Aug 2023 20:38:18 -0300 Subject: [PATCH 004/199] Add encoders and query --- src/abi/balancerHelpers.ts | 148 +++++++++++++++++++ src/entities/encoders/index.ts | 13 ++ src/entities/encoders/weighted.ts | 125 ++++++++++++++++ src/entities/pools/index.ts | 14 +- src/entities/pools/weighted/weightedJoin.ts | 152 +++++++++++++------- src/utils/constants.ts | 12 ++ test/weightedJoin.test.ts | 7 +- 7 files changed, 411 insertions(+), 60 deletions(-) create mode 100644 src/abi/balancerHelpers.ts create mode 100644 src/entities/encoders/index.ts create mode 100644 src/entities/encoders/weighted.ts diff --git a/src/abi/balancerHelpers.ts b/src/abi/balancerHelpers.ts new file mode 100644 index 00000000..96388c91 --- /dev/null +++ b/src/abi/balancerHelpers.ts @@ -0,0 +1,148 @@ +export const balancerHelpersAbi = [ + { + inputs: [ + { + internalType: 'contract IVault', + name: '_vault', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'poolId', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + components: [ + { + internalType: 'contract IAsset[]', + name: 'assets', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'minAmountsOut', + type: 'uint256[]', + }, + { + internalType: 'bytes', + name: 'userData', + type: 'bytes', + }, + { + internalType: 'bool', + name: 'toInternalBalance', + type: 'bool', + }, + ], + internalType: 'struct IVault.ExitPoolRequest', + name: 'request', + type: 'tuple', + }, + ], + name: 'queryExit', + outputs: [ + { + internalType: 'uint256', + name: 'bptIn', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'amountsOut', + type: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'poolId', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + components: [ + { + internalType: 'contract IAsset[]', + name: 'assets', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'maxAmountsIn', + type: 'uint256[]', + }, + { + internalType: 'bytes', + name: 'userData', + type: 'bytes', + }, + { + internalType: 'bool', + name: 'fromInternalBalance', + type: 'bool', + }, + ], + internalType: 'struct IVault.JoinPoolRequest', + name: 'request', + type: 'tuple', + }, + ], + name: 'queryJoin', + outputs: [ + { + internalType: 'uint256', + name: 'bptOut', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'amountsIn', + type: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'vault', + outputs: [ + { + internalType: 'contract IVault', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; diff --git a/src/entities/encoders/index.ts b/src/entities/encoders/index.ts new file mode 100644 index 00000000..a1761c57 --- /dev/null +++ b/src/entities/encoders/index.ts @@ -0,0 +1,13 @@ +import { SupportedRawPoolTypes } from '../../data/types'; +import { WeightedPoolEncoder } from './weighted'; + +export const getEncoder = ( + poolType: SupportedRawPoolTypes | string, +): typeof WeightedPoolEncoder | undefined => { + switch (poolType) { + case 'Weighted': + return WeightedPoolEncoder; + default: + return undefined; + } +}; diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts new file mode 100644 index 00000000..c6824948 --- /dev/null +++ b/src/entities/encoders/weighted.ts @@ -0,0 +1,125 @@ +import { encodeAbiParameters } from 'viem'; +import { Address } from '../../types'; + +export enum WeightedPoolJoinKind { + INIT = 0, + EXACT_TOKENS_IN_FOR_BPT_OUT = 1, + TOKEN_IN_FOR_EXACT_BPT_OUT = 2, + ALL_TOKENS_IN_FOR_EXACT_BPT_OUT = 3, +} + +export enum WeightedPoolExitKind { + EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, + EXACT_BPT_IN_FOR_TOKENS_OUT = 1, + BPT_IN_FOR_EXACT_TOKENS_OUT = 2, + MANAGEMENT_FEE_TOKENS_OUT = 3, +} + +export class WeightedPoolEncoder { + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } + + /** + * Encodes the userData parameter for providing the initial liquidity to a WeightedPool + * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances + */ + static joinInit = (amountsIn: bigint[]): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }], + [WeightedPoolJoinKind.INIT, amountsIn], + ); + + /** + * Encodes the userData parameter for joining a WeightedPool with exact token inputs + * @param amountsIn - the amounts each of token to deposit in the pool as liquidity + * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens + */ + static joinGivenIn = (amountsIn: bigint[], minimumBPT: bigint): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], + [ + WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + amountsIn, + minimumBPT, + ], + ); + + /** + * Encodes the userData parameter for joining a WeightedPool with a single token to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + * @param enterTokenIndex - the index of the token to be provided as liquidity + */ + static joinGivenOut = ( + bptAmountOut: bigint, + enterTokenIndex?: number, + ): Address => { + if (enterTokenIndex === undefined) { + // if enterTokenIndex is not provided, it's assumed to be an allTokensIn + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, + bptAmountOut, + ], + ); + } else { + // if enterTokenIndex is provided, it's assumed to be an allTokensIn + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT, + bptAmountOut, + enterTokenIndex, + ], + ); + } + }; + + /** + * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + * @param enterTokenIndex - the index of the token to removed from the pool + */ + static exitGivenIn = ( + bptAmountIn: bigint, + exitTokenIndex?: number, + ): Address => { + if (exitTokenIndex === undefined) { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn], + ); + } else { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, + bptAmountIn, + exitTokenIndex, + ], + ); + } + }; + + /** + * Encodes the userData parameter for exiting a WeightedPool by removing exact amounts of tokens + * @param amountsOut - the amounts of each token to be withdrawn from the pool + * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens + */ + static exitGivenOut = ( + amountsOut: bigint[], + maxBPTAmountIn: bigint, + ): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, + amountsOut, + maxBPTAmountIn, + ], + ); +} diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index 94ba067f..ad174d96 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -1,5 +1,5 @@ import { Hex } from 'viem'; -import { PoolType, SwapKind } from '../../types'; +import { Address, PoolType, SwapKind } from '../../types'; import { Token, TokenAmount } from '../'; import { RawPool } from '../../data/types'; @@ -52,18 +52,24 @@ export type JoinInput = { // Returned from a join query export type JoinQueryResult = { - id: string; - assets: string[]; + id: Address; + assets: Address[]; joinKind: string; bptOut: TokenAmount; amountsIn: TokenAmount[]; }; +export type JoinCallInput = JoinQueryResult & { + slippage: string; + sender: Address; + recipient: Address; +}; + export interface BaseJoin { getInstance(): BaseJoin; query(input: JoinInput, poolState: PoolState): Promise; // TODO - Best way to represent slippage? - buildCall(input: JoinQueryResult & { slippage: string }): { + buildCall(input: JoinCallInput): { call: string; to: string; value: string; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index b5267679..4ce196ce 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -1,7 +1,24 @@ -import { BaseJoin, JoinInput, JoinQueryResult, PoolState } from '..'; +import { createPublicClient, encodeFunctionData, http } from 'viem'; +import { + BaseJoin, + JoinCallInput, + JoinInput, + JoinQueryResult, + PoolState, +} from '..'; import { Address } from '../../../types'; -import { ZERO_ADDRESS, getPoolAddress } from '../../../utils'; +import { + BALANCER_HELPERS, + BALANCER_VAULT, + CHAINS, + ZERO_ADDRESS, + getPoolAddress, +} from '../../../utils'; +import { WeightedPoolEncoder } from '../../encoders/weighted'; import { TokenAmount } from '../../tokenAmount'; +import { balancerHelpersAbi } from '../../../abi/balancerHelpers'; +import { Token } from '../../token'; +import { vaultAbi } from '../../../abi'; export class WeightedJoin implements BaseJoin { // TODO - Probably not needed @@ -22,24 +39,27 @@ export class WeightedJoin implements BaseJoin { const joinKind = this.getJoinKind(input, poolAddress); // Initialize join parameters - let amountsIn = Array(poolState.assets.length).fill('0'); - let userData = ''; + let maxAmountsIn = Array(poolState.assets.length).fill(0n); + let userData: Address; switch (joinKind) { case 'Init': { - amountsIn = this.getAmountsIn(input, poolState.assets); - userData = `Encode userData as Init [${amountsIn}]`; + maxAmountsIn = this.getAmountsIn(input, poolState.assets); + userData = WeightedPoolEncoder.joinInit(maxAmountsIn); break; } case 'GivenIn': { - amountsIn = this.getAmountsIn(input, poolState.assets); - const bptOut = '0'; - userData = `Encode userData as GivenIn [${amountsIn}, ${bptOut}]`; + maxAmountsIn = this.getAmountsIn(input, poolState.assets); + const bptOut = 0n; + userData = WeightedPoolEncoder.joinGivenIn( + maxAmountsIn, + bptOut, + ); break; } case 'GivenOut': { - const bptOut = input.tokenAmounts[0].amount.toString(); - userData = `Encode userData as exact out [${bptOut}]`; + const bptOut = input.tokenAmounts[0].amount; + userData = WeightedPoolEncoder.joinGivenOut(bptOut); break; } default: @@ -47,52 +67,75 @@ export class WeightedJoin implements BaseJoin { } const queryArgs = this.getJoinParameters({ - poolId: poolState.id, - assets: poolState.assets, + poolId: poolState.id as Address, + assets: poolState.assets as Address[], sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, - maxAmountsIn: amountsIn, + maxAmountsIn, userData, }); - // Do query and get bptOut/amountsIn - console.log(queryArgs); + const client = createPublicClient({ + transport: http(input.rpcUrl), + chain: CHAINS[input.chainId], + }); + + const { result } = await client.simulateContract({ + address: BALANCER_HELPERS[input.chainId], + abi: balancerHelpersAbi, + functionName: 'queryJoin', + args: queryArgs, + }); + + const [queryBptOut, queryAmountsIn] = result; + + const bpt = new Token(input.chainId, poolAddress, 18); + const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); + + const poolTokens = poolState.assets.map( + (a) => new Token(input.chainId, a as Address, 18), + ); // TODO: get pool token decimals from API + const amountsIn = queryAmountsIn.map((a, i) => + TokenAmount.fromRawAmount(poolTokens[i], a), + ); return { joinKind, - id: poolState.id, - assets: poolState.assets, - bptOut: {} as TokenAmount, // TODO: update with query result if needed - amountsIn: [{} as TokenAmount], // TODO: update with query result if needed + id: poolState.id as Address, + assets: poolState.assets as Address[], + bptOut, + amountsIn, }; } - public buildCall( - input: JoinQueryResult & { - slippage: string; - sender: string; - receiver: string; - }, - ): { call: string; to: string; value: string } { - let maxAmountsIn: string[]; - let userData: string; + public buildCall(input: JoinCallInput): { + call: string; + to: Address; + value: string; + } { + let maxAmountsIn: bigint[]; + let userData: Address; switch (input.joinKind) { case 'Init': { - maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); - userData = `Encode userData as init [${maxAmountsIn}]`; + maxAmountsIn = input.amountsIn.map((a) => a.amount); + userData = WeightedPoolEncoder.joinInit(maxAmountsIn); break; } case 'GivenIn': { - maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); - const minBptOut = input.bptOut; // TODO sub slippage here - userData = `Encode userData as exact in [${maxAmountsIn}, ${minBptOut}]`; + maxAmountsIn = input.amountsIn.map((a) => a.amount); + const minBptOut = input.bptOut.amount; // TODO sub slippage here + userData = WeightedPoolEncoder.joinGivenIn( + maxAmountsIn, + minBptOut, + ); break; } case 'GivenOut': { - maxAmountsIn = input.amountsIn.map((a) => a.amount.toString()); // TODO add slippage here - const exactBptOut = input.bptOut; - userData = `Encode userData as exact out [${exactBptOut}]`; + maxAmountsIn = input.amountsIn.map((a) => a.amount); // TODO add slippage here + userData = WeightedPoolEncoder.joinGivenOut( + input.bptOut.amount, + ); break; } default: @@ -103,17 +146,21 @@ export class WeightedJoin implements BaseJoin { poolId: input.id, assets: input.assets, sender: input.sender, - recipient: input.receiver, + recipient: input.recipient, maxAmountsIn, userData, }); - const call = JSON.stringify(queryArgs); // TODO - Encode data + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'joinPool', + args: queryArgs, + }); // Encode data return { call, - to: '0xbalancerVaultAddress', + to: BALANCER_VAULT, value: '0', // TODO: ETH value when joining with ETH }; } @@ -126,12 +173,12 @@ export class WeightedJoin implements BaseJoin { maxAmountsIn, userData, }: { - poolId: string; - assets: string[]; - sender: string; - recipient: string; - maxAmountsIn: string[]; - userData: string; + poolId: Address; + assets: readonly Address[]; + sender: Address; + recipient: Address; + maxAmountsIn: readonly bigint[]; + userData: Address; }) { const joinPoolRequest = { assets, // with BPT @@ -140,12 +187,7 @@ export class WeightedJoin implements BaseJoin { fromInternalBalance: false, }; - return { - poolId, - sender, - recipient, - joinPoolRequest, - }; + return [poolId, sender, recipient, joinPoolRequest] as const; } private checkInputs(input: JoinInput, poolState: PoolState) { @@ -172,14 +214,14 @@ export class WeightedJoin implements BaseJoin { } } - private getAmountsIn(input: JoinInput, poolAssets: string[]): string[] { + private getAmountsIn(input: JoinInput, poolAssets: string[]): bigint[] { return poolAssets.map((asset) => { - let amountIn = '0'; + let amountIn = 0n; const tokenIn = input.tokenAmounts.find( (t) => t.token.address === asset, ); if (tokenIn) { - amountIn = tokenIn.amount.toString(); + amountIn = tokenIn.amount; } return amountIn; }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 3ecee182..3a05c86a 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -122,6 +122,18 @@ export const BALANCER_POOL_DATA_QUERIES_ADDRESSES: Record = { [ChainId.ZKEVM]: '0xF24917fB88261a37Cc57F686eBC831a5c0B9fD39', }; +export const BALANCER_VAULT = '0xBA12222222228d8Ba445958a75a0704d566BF2C8'; + +export const BALANCER_HELPERS: Record = { + [ChainId.ARBITRUM_ONE]: '0x77d46184d22ca6a3726a2f500c776767b6a3d6ab', + [ChainId.AVALANCHE]: '0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9', + [ChainId.GNOSIS_CHAIN]: '0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9', + [ChainId.MAINNET]: '0x5addcca35b7a0d07c74063c48700c8590e87864e', + [ChainId.OPTIMISM]: '0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9', + [ChainId.POLYGON]: '0x239e55f427d44c3cc793f49bfb507ebe76638a2b', + [ChainId.ZKEVM]: '0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9', +}; + export const NATIVE_ASSETS = { [ChainId.MAINNET]: new Token( ChainId.MAINNET, diff --git a/test/weightedJoin.test.ts b/test/weightedJoin.test.ts index 15c62a42..1e990f08 100644 --- a/test/weightedJoin.test.ts +++ b/test/weightedJoin.test.ts @@ -27,7 +27,12 @@ describe('weighted join test', () => { rpcUrl: '', }; const queryResult = await weightedJoin.query(queryInput, poolFromApi); - const call = weightedJoin.buildCall({ ...queryResult, slippage: '10' }); + const call = weightedJoin.buildCall({ + ...queryResult, + slippage: '10', + sender: '0xsenderAddr', + recipient: '0xrecipientAddr', + }); console.log(call); // Make call expect(true).toEqual(true); }); From 71315d245bba6ad758646847d95ee8fb29df669b Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 30 Aug 2023 20:42:53 -0300 Subject: [PATCH 005/199] Refactor the code to use pool address instead of pool id --- src/entities/pools/index.ts | 5 +++-- src/entities/pools/weighted/weightedJoin.ts | 17 ++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index ad174d96..e6e5da2e 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -36,9 +36,10 @@ export interface BasePoolFactory { // Returned from API and used as input export type PoolState = { - id: string; + id: Address; + address: Address; type: string; - assets: string[]; // already properly sorted in case different versions sort them differently + assets: Address[]; // already properly sorted in case different versions sort them differently // TODO - Possibly add encoding info here? }; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index 4ce196ce..c46a322c 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -35,8 +35,7 @@ export class WeightedJoin implements BaseJoin { this.checkInputs(input, poolState); // infer join kind by input tokens - const poolAddress = getPoolAddress(poolState.id) as Address; - const joinKind = this.getJoinKind(input, poolAddress); + const joinKind = this.getJoinKind(input, poolState.address); // Initialize join parameters let maxAmountsIn = Array(poolState.assets.length).fill(0n); @@ -67,8 +66,8 @@ export class WeightedJoin implements BaseJoin { } const queryArgs = this.getJoinParameters({ - poolId: poolState.id as Address, - assets: poolState.assets as Address[], + poolId: poolState.id, + assets: poolState.assets, sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, maxAmountsIn, @@ -89,11 +88,11 @@ export class WeightedJoin implements BaseJoin { const [queryBptOut, queryAmountsIn] = result; - const bpt = new Token(input.chainId, poolAddress, 18); + const bpt = new Token(input.chainId, poolState.address, 18); const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); const poolTokens = poolState.assets.map( - (a) => new Token(input.chainId, a as Address, 18), + (a) => new Token(input.chainId, a, 18), ); // TODO: get pool token decimals from API const amountsIn = queryAmountsIn.map((a, i) => TokenAmount.fromRawAmount(poolTokens[i], a), @@ -101,8 +100,8 @@ export class WeightedJoin implements BaseJoin { return { joinKind, - id: poolState.id as Address, - assets: poolState.assets as Address[], + id: poolState.id, + assets: poolState.assets, bptOut, amountsIn, }; @@ -196,7 +195,7 @@ export class WeightedJoin implements BaseJoin { throw new Error('Must specify at least one input'); } else if (tokensIn.some((t) => !poolState.assets.includes(t))) { throw new Error('Input token not in pool'); - } else if (tokensIn.includes(getPoolAddress(poolState.id) as Address)) { + } else if (tokensIn.includes(poolState.address)) { if (tokensIn.length > 1) { throw new Error('Cannot join with BPT and other tokens'); } else if (input.isInit) { From e264a8bbff5f9e854dd9aeb1862efbe044a446e3 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 30 Aug 2023 20:57:00 -0300 Subject: [PATCH 006/199] Fix lint errors --- src/entities/pools/weighted/weightedJoin.ts | 1 - test/weightedJoin.test.ts | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index c46a322c..9d6b43b7 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -12,7 +12,6 @@ import { BALANCER_VAULT, CHAINS, ZERO_ADDRESS, - getPoolAddress, } from '../../../utils'; import { WeightedPoolEncoder } from '../../encoders/weighted'; import { TokenAmount } from '../../tokenAmount'; diff --git a/test/weightedJoin.test.ts b/test/weightedJoin.test.ts index 1e990f08..68f4be4c 100644 --- a/test/weightedJoin.test.ts +++ b/test/weightedJoin.test.ts @@ -7,7 +7,8 @@ import { Token, TokenAmount, } from '../src/entities'; -import { ChainId } from '../src/utils'; +import { ChainId, getPoolAddress } from '../src/utils'; +import { Address } from '../src/types'; describe('weighted join test', () => { let api: MockApi; @@ -41,9 +42,10 @@ describe('weighted join test', () => { /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: string): Promise { + public async getPool(id: Address): Promise { return { id, + address: getPoolAddress(id) as Address, type: 'Weighted', assets: ['0xtoken1Addr', '0xtoken2Addr'], }; From 2c423b6d6a14bdf75759152bf8fa85c02df80d82 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 31 Aug 2023 11:13:03 -0300 Subject: [PATCH 007/199] Refactor WeightedPoolEncoder to WeightedEncoder --- src/entities/encoders/index.ts | 6 +++--- src/entities/encoders/weighted.ts | 2 +- src/entities/pools/weighted/weightedJoin.ts | 22 +++++++-------------- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/entities/encoders/index.ts b/src/entities/encoders/index.ts index a1761c57..b1b600de 100644 --- a/src/entities/encoders/index.ts +++ b/src/entities/encoders/index.ts @@ -1,12 +1,12 @@ import { SupportedRawPoolTypes } from '../../data/types'; -import { WeightedPoolEncoder } from './weighted'; +import { WeightedEncoder } from './weighted'; export const getEncoder = ( poolType: SupportedRawPoolTypes | string, -): typeof WeightedPoolEncoder | undefined => { +): typeof WeightedEncoder | undefined => { switch (poolType) { case 'Weighted': - return WeightedPoolEncoder; + return WeightedEncoder; default: return undefined; } diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index c6824948..87d77a06 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -15,7 +15,7 @@ export enum WeightedPoolExitKind { MANAGEMENT_FEE_TOKENS_OUT = 3, } -export class WeightedPoolEncoder { +export class WeightedEncoder { /** * Cannot be constructed. */ diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index 9d6b43b7..2b180161 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -13,7 +13,7 @@ import { CHAINS, ZERO_ADDRESS, } from '../../../utils'; -import { WeightedPoolEncoder } from '../../encoders/weighted'; +import { WeightedEncoder } from '../../encoders/weighted'; import { TokenAmount } from '../../tokenAmount'; import { balancerHelpersAbi } from '../../../abi/balancerHelpers'; import { Token } from '../../token'; @@ -43,21 +43,18 @@ export class WeightedJoin implements BaseJoin { switch (joinKind) { case 'Init': { maxAmountsIn = this.getAmountsIn(input, poolState.assets); - userData = WeightedPoolEncoder.joinInit(maxAmountsIn); + userData = WeightedEncoder.joinInit(maxAmountsIn); break; } case 'GivenIn': { maxAmountsIn = this.getAmountsIn(input, poolState.assets); const bptOut = 0n; - userData = WeightedPoolEncoder.joinGivenIn( - maxAmountsIn, - bptOut, - ); + userData = WeightedEncoder.joinGivenIn(maxAmountsIn, bptOut); break; } case 'GivenOut': { const bptOut = input.tokenAmounts[0].amount; - userData = WeightedPoolEncoder.joinGivenOut(bptOut); + userData = WeightedEncoder.joinGivenOut(bptOut); break; } default: @@ -117,23 +114,18 @@ export class WeightedJoin implements BaseJoin { switch (input.joinKind) { case 'Init': { maxAmountsIn = input.amountsIn.map((a) => a.amount); - userData = WeightedPoolEncoder.joinInit(maxAmountsIn); + userData = WeightedEncoder.joinInit(maxAmountsIn); break; } case 'GivenIn': { maxAmountsIn = input.amountsIn.map((a) => a.amount); const minBptOut = input.bptOut.amount; // TODO sub slippage here - userData = WeightedPoolEncoder.joinGivenIn( - maxAmountsIn, - minBptOut, - ); + userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); break; } case 'GivenOut': { maxAmountsIn = input.amountsIn.map((a) => a.amount); // TODO add slippage here - userData = WeightedPoolEncoder.joinGivenOut( - input.bptOut.amount, - ); + userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); break; } default: From fc845e78ade287f633ee0353565efcca10f6e4bf Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 31 Aug 2023 11:13:21 -0300 Subject: [PATCH 008/199] Add decimals to tokens on PoolState --- src/entities/pools/index.ts | 6 ++++- src/entities/pools/weighted/weightedJoin.ts | 26 +++++++++++---------- test/weightedJoin.test.ts | 5 +++- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index e6e5da2e..b8ad1932 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -39,7 +39,11 @@ export type PoolState = { id: Address; address: Address; type: string; - assets: Address[]; // already properly sorted in case different versions sort them differently + // TODO: is it ok to replace by Token here? Or should we stick to basic types? + tokens: { + address: Address; + decimals: number; + }[]; // already properly sorted in case different versions sort them differently // TODO - Possibly add encoding info here? }; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index 2b180161..015892d0 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -37,17 +37,18 @@ export class WeightedJoin implements BaseJoin { const joinKind = this.getJoinKind(input, poolState.address); // Initialize join parameters - let maxAmountsIn = Array(poolState.assets.length).fill(0n); + let maxAmountsIn = Array(poolState.tokens.length).fill(0n); let userData: Address; + const poolAssets = poolState.tokens.map((t) => t.address); switch (joinKind) { case 'Init': { - maxAmountsIn = this.getAmountsIn(input, poolState.assets); + maxAmountsIn = this.getAmountsIn(input, poolAssets); userData = WeightedEncoder.joinInit(maxAmountsIn); break; } case 'GivenIn': { - maxAmountsIn = this.getAmountsIn(input, poolState.assets); + maxAmountsIn = this.getAmountsIn(input, poolAssets); const bptOut = 0n; userData = WeightedEncoder.joinGivenIn(maxAmountsIn, bptOut); break; @@ -63,7 +64,7 @@ export class WeightedJoin implements BaseJoin { const queryArgs = this.getJoinParameters({ poolId: poolState.id, - assets: poolState.assets, + assets: poolAssets, sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, maxAmountsIn, @@ -75,21 +76,21 @@ export class WeightedJoin implements BaseJoin { chain: CHAINS[input.chainId], }); - const { result } = await client.simulateContract({ + const { + result: [queryBptOut, queryAmountsIn], + } = await client.simulateContract({ address: BALANCER_HELPERS[input.chainId], abi: balancerHelpersAbi, functionName: 'queryJoin', args: queryArgs, }); - const [queryBptOut, queryAmountsIn] = result; - const bpt = new Token(input.chainId, poolState.address, 18); const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); - const poolTokens = poolState.assets.map( - (a) => new Token(input.chainId, a, 18), - ); // TODO: get pool token decimals from API + const poolTokens = poolState.tokens.map( + (token) => new Token(input.chainId, token.address, token.decimals), + ); const amountsIn = queryAmountsIn.map((a, i) => TokenAmount.fromRawAmount(poolTokens[i], a), ); @@ -97,7 +98,7 @@ export class WeightedJoin implements BaseJoin { return { joinKind, id: poolState.id, - assets: poolState.assets, + assets: poolAssets, bptOut, amountsIn, }; @@ -182,9 +183,10 @@ export class WeightedJoin implements BaseJoin { private checkInputs(input: JoinInput, poolState: PoolState) { const tokensIn = input.tokenAmounts.map((t) => t.token.address); + const poolAssets = poolState.tokens.map((t) => t.address); if (input.tokenAmounts.length === 0) { throw new Error('Must specify at least one input'); - } else if (tokensIn.some((t) => !poolState.assets.includes(t))) { + } else if (tokensIn.some((t) => !poolAssets.includes(t))) { throw new Error('Input token not in pool'); } else if (tokensIn.includes(poolState.address)) { if (tokensIn.length > 1) { diff --git a/test/weightedJoin.test.ts b/test/weightedJoin.test.ts index 68f4be4c..e022210a 100644 --- a/test/weightedJoin.test.ts +++ b/test/weightedJoin.test.ts @@ -47,7 +47,10 @@ export class MockApi { id, address: getPoolAddress(id) as Address, type: 'Weighted', - assets: ['0xtoken1Addr', '0xtoken2Addr'], + tokens: [ + { address: '0xtoken1Addr', decimals: 18 }, + { address: '0xtoken2Addr', decimals: 18 }, + ], }; } } From cce91bfb55c271a88910fe6ba57a0b74b1a166d7 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 31 Aug 2023 15:23:11 -0300 Subject: [PATCH 009/199] Update integration test with TestClient from viem --- src/entities/join.ts | 2 +- src/entities/pools/index.ts | 4 +- src/entities/pools/weighted/weightedJoin.ts | 2 +- test/weightedJoin.integration.test.ts | 96 +++++++++++++++++++++ test/weightedJoin.test.ts | 58 ------------- 5 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 test/weightedJoin.integration.test.ts delete mode 100644 test/weightedJoin.test.ts diff --git a/src/entities/join.ts b/src/entities/join.ts index cee7f98c..b665de70 100644 --- a/src/entities/join.ts +++ b/src/entities/join.ts @@ -12,7 +12,7 @@ export class JoinParser { constructor(config?: JoinConfig) { const { customPoolJoins } = config || {}; this.poolJoins = { - weighted: new WeightedJoin(), + Weighted: new WeightedJoin(), // custom pool Joins take precedence over base Joins ...customPoolJoins, }; diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index b8ad1932..194577bb 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -75,8 +75,8 @@ export interface BaseJoin { query(input: JoinInput, poolState: PoolState): Promise; // TODO - Best way to represent slippage? buildCall(input: JoinCallInput): { - call: string; - to: string; + call: Address; + to: Address; value: string; }; } diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index 015892d0..e70e2648 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -105,7 +105,7 @@ export class WeightedJoin implements BaseJoin { } public buildCall(input: JoinCallInput): { - call: string; + call: Address; to: Address; value: string; } { diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts new file mode 100644 index 00000000..2f804127 --- /dev/null +++ b/test/weightedJoin.integration.test.ts @@ -0,0 +1,96 @@ +// pnpm test -- weightedJoin.integration.test.ts +import { describe, expect, test, beforeAll } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + JoinInput, + JoinParser, + PoolState, + Token, + TokenAmount, +} from '../src/entities'; +import { BALANCER_VAULT, CHAINS, ChainId, getPoolAddress } from '../src/utils'; +import { Address } from '../src/types'; +import { TestClient, createTestClient, http } from 'viem'; + +const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig + +describe('weighted join test', () => { + let api: MockApi; + let chainId: ChainId; + let rpcUrl: string; + let client: TestClient; + + beforeAll(() => { + api = new MockApi(); + chainId = ChainId.MAINNET; + rpcUrl = process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com'; + client = createTestClient({ + chain: CHAINS[chainId], + mode: 'anvil', + transport: http(rpcUrl), + }); + }); + test('should join', async () => { + const poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + // Calls API + const poolFromApi = await api.getPool(poolId); + const joinParser = new JoinParser(); + const weightedJoin = joinParser.getJoin(poolFromApi.type); + const tokenInRaw = poolFromApi.tokens[0]; + const tokenIn = new Token( + chainId, + tokenInRaw.address, + tokenInRaw.decimals, + ); + const queryInput: JoinInput = { + tokenAmounts: [TokenAmount.fromHumanAmount(tokenIn, '1')], + chainId, + rpcUrl, + }; + const queryResult = await weightedJoin.query(queryInput, poolFromApi); + + const { call, to } = weightedJoin.buildCall({ + ...queryResult, + slippage: '10', + sender: testAddress, + recipient: testAddress, + }); + + const result = await client.sendUnsignedTransaction({ + from: testAddress, + to, + data: call, + }); + + console.log('result', result); + + expect(to).toEqual(BALANCER_VAULT); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Address): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'Weighted', + tokens: [ + { + address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL + decimals: 18, + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH + decimals: 18, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/weightedJoin.test.ts b/test/weightedJoin.test.ts deleted file mode 100644 index e022210a..00000000 --- a/test/weightedJoin.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -// pnpm test -- weightedJoin.test.ts -import { describe, expect, test, beforeAll } from 'vitest'; -import { - JoinInput, - JoinParser, - PoolState, - Token, - TokenAmount, -} from '../src/entities'; -import { ChainId, getPoolAddress } from '../src/utils'; -import { Address } from '../src/types'; - -describe('weighted join test', () => { - let api: MockApi; - beforeAll(() => { - api = new MockApi(); - }); - test('should join', async () => { - const joins = new JoinParser(); - const poolId = '0xpoolId'; - // Calls API - const poolFromApi = await api.getPool(poolId); - const weightedJoin = joins.getJoin(poolFromApi.type); - const tokenIn = new Token(ChainId.MAINNET, '0xtoken1Addr', 18); - const queryInput: JoinInput = { - tokenAmounts: [TokenAmount.fromHumanAmount(tokenIn, '1')], - chainId: ChainId.MAINNET, - rpcUrl: '', - }; - const queryResult = await weightedJoin.query(queryInput, poolFromApi); - const call = weightedJoin.buildCall({ - ...queryResult, - slippage: '10', - sender: '0xsenderAddr', - recipient: '0xrecipientAddr', - }); - console.log(call); // Make call - expect(true).toEqual(true); - }); -}); - -/*********************** Mock To Represent API Requirements **********************/ - -export class MockApi { - public async getPool(id: Address): Promise { - return { - id, - address: getPoolAddress(id) as Address, - type: 'Weighted', - tokens: [ - { address: '0xtoken1Addr', decimals: 18 }, - { address: '0xtoken2Addr', decimals: 18 }, - ], - }; - } -} - -/******************************************************************************/ From eb34419b4035a3436f20ea03d0b879edd4b34d61 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 31 Aug 2023 20:40:05 -0300 Subject: [PATCH 010/199] Update viem to v1.9.3 --- package.json | 2 +- pnpm-lock.yaml | 53 +++++++++++++++++++++++++++----------------------- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 05862910..a2131f71 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "async-retry": "^1.3.3", "decimal.js-light": "^2.5.1", "pino": "^8.11.0", - "viem": "^1.4.1" + "viem": "^1.9.3" }, "devDependencies": { "@changesets/cli": "^2.26.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57ab0c1b..684c8d1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ dependencies: specifier: ^8.11.0 version: 8.11.0 viem: - specifier: ^1.4.1 - version: 1.4.1(typescript@5.1.3) + specifier: ^1.9.3 + version: 1.9.3(typescript@5.1.3) devDependencies: '@changesets/cli': @@ -694,10 +694,21 @@ packages: '@noble/hashes': 1.3.0 dev: false + /@noble/curves@1.1.0: + resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} + dependencies: + '@noble/hashes': 1.3.1 + dev: false + /@noble/hashes@1.3.0: resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} dev: false + /@noble/hashes@1.3.1: + resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} + engines: {node: '>= 16'} + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -818,7 +829,6 @@ packages: /@types/node@18.15.11: resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} - dev: true /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -832,6 +842,12 @@ packages: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true + /@types/ws@8.5.5: + resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} + dependencies: + '@types/node': 18.15.11 + dev: false + /@vitest/expect@0.30.1: resolution: {integrity: sha512-c3kbEtN8XXJSeN81iDGq29bUzSjQhjES2WR3aColsS4lPGbivwLtas4DNUe0jD9gg/FYGIteqOenfU95EFituw==} dependencies: @@ -871,17 +887,6 @@ packages: pretty-format: 27.5.1 dev: true - /@wagmi/chains@1.6.0(typescript@5.1.3): - resolution: {integrity: sha512-5FRlVxse5P4ZaHG3GTvxwVANSmYJas1eQrTBHhjxVtqXoorm0aLmCHbhmN8Xo1yu09PaWKlleEvfE98yH4AgIw==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - typescript: 5.1.3 - dev: false - /abitype@0.9.3(typescript@5.1.3): resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==} peerDependencies: @@ -1147,7 +1152,7 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /ci-info@3.8.0: @@ -1638,8 +1643,8 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true @@ -2738,7 +2743,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rollup@3.26.3: @@ -2746,7 +2751,7 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /rome@12.1.3: @@ -3238,8 +3243,8 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /viem@1.4.1(typescript@5.1.3): - resolution: {integrity: sha512-MtaoBHDSJDqa+QyXKG5d+S6EQSebRO0tzw6anSP4zC7AbC614vMeg9Y8LbkmEkWCw8swFYkort+H9l7GkWB0uA==} + /viem@1.9.3(typescript@5.1.3): + resolution: {integrity: sha512-5NcxPHkWCZIBYm8A8oOd77l2RQ/+VRm1p56mBxoRcZ+Mij5pA+5Y3cohz7MHt6gYmmoDWlLXUXxsdPLedeF2wg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -3247,11 +3252,11 @@ packages: optional: true dependencies: '@adraffy/ens-normalize': 1.9.0 - '@noble/curves': 1.0.0 + '@noble/curves': 1.1.0 '@noble/hashes': 1.3.0 '@scure/bip32': 1.3.0 '@scure/bip39': 1.2.0 - '@wagmi/chains': 1.6.0(typescript@5.1.3) + '@types/ws': 8.5.5 abitype: 0.9.3(typescript@5.1.3) isomorphic-ws: 5.0.0(ws@8.12.0) typescript: 5.1.3 @@ -3317,7 +3322,7 @@ packages: postcss: 8.4.27 rollup: 3.26.3 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 dev: true /vitest@0.30.1: From 41a7d6f2fd10cff83cfa29f589d474f1a1b1e5c2 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 31 Aug 2023 20:40:46 -0300 Subject: [PATCH 011/199] Add integration test (wip) --- test/weightedJoin.integration.test.ts | 35 ++++++++++++++++++--------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 2f804127..99a3b62c 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -10,9 +10,12 @@ import { Token, TokenAmount, } from '../src/entities'; -import { BALANCER_VAULT, CHAINS, ChainId, getPoolAddress } from '../src/utils'; +import { BALANCER_VAULT, ChainId, getPoolAddress } from '../src/utils'; import { Address } from '../src/types'; -import { TestClient, createTestClient, http } from 'viem'; +import { createTestClient, http, walletActions } from 'viem'; +import { mainnet } from 'viem/chains'; +import { writeContract } from 'viem/dist/types/actions/wallet/writeContract'; +import { erc20Abi } from '../src/abi'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -20,17 +23,11 @@ describe('weighted join test', () => { let api: MockApi; let chainId: ChainId; let rpcUrl: string; - let client: TestClient; beforeAll(() => { api = new MockApi(); chainId = ChainId.MAINNET; - rpcUrl = process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com'; - client = createTestClient({ - chain: CHAINS[chainId], - mode: 'anvil', - transport: http(rpcUrl), - }); + rpcUrl = 'http://127.0.0.1:8545/'; }); test('should join', async () => { const poolId = @@ -59,8 +56,24 @@ describe('weighted join test', () => { recipient: testAddress, }); - const result = await client.sendUnsignedTransaction({ - from: testAddress, + const client = createTestClient({ + chain: mainnet, + mode: 'hardhat', + transport: http(rpcUrl), + }).extend(walletActions); + + await client.impersonateAccount({ address: testAddress }); + + await client.writeContract({ + account: testAddress, + address: tokenIn.address, + abi: erc20Abi, + functionName: 'approve', + args: [to, queryResult.amountsIn[0].amount], + }); + + const result = await client.sendTransaction({ + account: testAddress, to, data: call, }); From 8747aa258962700f7a71554fd0acb732100453a9 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 1 Sep 2023 15:29:07 -0300 Subject: [PATCH 012/199] Add helpers to test --- src/entities/pools/index.ts | 2 +- src/entities/pools/weighted/weightedJoin.ts | 4 +- src/utils/constants.ts | 2 + test/lib/utils/helper.ts | 96 +++++++++++++++++++++ test/weightedJoin.integration.test.ts | 74 +++++++++++----- 5 files changed, 153 insertions(+), 25 deletions(-) create mode 100644 test/lib/utils/helper.ts diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index 194577bb..f5df4994 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -77,6 +77,6 @@ export interface BaseJoin { buildCall(input: JoinCallInput): { call: Address; to: Address; - value: string; + value: bigint; }; } diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index e70e2648..da6b4052 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -107,7 +107,7 @@ export class WeightedJoin implements BaseJoin { public buildCall(input: JoinCallInput): { call: Address; to: Address; - value: string; + value: bigint; } { let maxAmountsIn: bigint[]; let userData: Address; @@ -152,7 +152,7 @@ export class WeightedJoin implements BaseJoin { return { call, to: BALANCER_VAULT, - value: '0', // TODO: ETH value when joining with ETH + value: 0n, // TODO: ETH value when joining with ETH }; } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 3a05c86a..4514ec24 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -21,6 +21,8 @@ export const NATIVE_ADDRESS: Address = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'; export const MAX_UINT112 = 5192296858534827628530496329220095n; +export const MAX_UINT256 = + 115792089237316195423570985008687907853269984665640564039457584007913129639935n; export const PREMINTED_STABLE_BPT = 2596148429267413814265248164610048n; // 2**111 export const DECIMAL_SCALES = { diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts new file mode 100644 index 00000000..0a163951 --- /dev/null +++ b/test/lib/utils/helper.ts @@ -0,0 +1,96 @@ +import { + Address, + Client, + PublicActions, + TestActions, + TestClient, + TransactionReceipt, + WalletActions, +} from 'viem'; +import { erc20Abi } from '../../../src/abi'; +import { ZERO_ADDRESS } from '../../../src/utils'; + +export const getErc20Balance = ( + token: Address, + client: Client & PublicActions, + holder: Address, +): Promise => + client.readContract({ + address: token, + abi: erc20Abi, + functionName: 'balanceOf', + args: [holder], + }); + +export const getBalances = async ( + tokens: Address[], + client: Client & PublicActions, + holder: Address, +): Promise> => { + const balances: Promise[] = []; + for (let i = 0; i < tokens.length; i++) { + if (tokens[i] === ZERO_ADDRESS) { + balances[i] = client.getBalance({ + address: holder, + }); + } else { + balances[i] = getErc20Balance(tokens[i], client, holder); + } + } + return Promise.all(balances); +}; + +export async function sendTransactionGetBalances( + tokensForBalanceCheck: Address[], + client: Client & PublicActions & TestActions & WalletActions, + clientAddress: Address, + to: Address, + data: Address, + value?: bigint, +): Promise<{ + transactionReceipt: TransactionReceipt; + balanceDeltas: bigint[]; + gasUsed: bigint; +}> { + const balanceBefore = await getBalances( + tokensForBalanceCheck, + client, + clientAddress, + ); + + // Send transaction to local fork + const hash = await client.sendTransaction({ + account: clientAddress, + chain: client.chain, + data, + to, + value, + }); + const transactionReceipt = await client.getTransactionReceipt({ + hash, + }); + const { gasUsed, effectiveGasPrice } = transactionReceipt; + const gasPrice = gasUsed * effectiveGasPrice; + + const balancesAfter = await getBalances( + tokensForBalanceCheck, + client, + clientAddress, + ); + + const balanceDeltas = balancesAfter.map((balanceAfter, i) => { + let _balanceAfter = balanceAfter; + if (tokensForBalanceCheck[i] === ZERO_ADDRESS) { + // ignore ETH delta from gas cost + _balanceAfter = balanceAfter + gasPrice; + } + const delta = _balanceAfter - balanceBefore[i]; + return delta >= 0n ? delta : -delta; + }); + + return { + transactionReceipt, + balanceDeltas, + gasUsed, + }; +} diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 99a3b62c..e45567ee 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -10,12 +10,20 @@ import { Token, TokenAmount, } from '../src/entities'; -import { BALANCER_VAULT, ChainId, getPoolAddress } from '../src/utils'; +import { CHAINS, ChainId, MAX_UINT256, getPoolAddress } from '../src/utils'; import { Address } from '../src/types'; -import { createTestClient, http, walletActions } from 'viem'; -import { mainnet } from 'viem/chains'; -import { writeContract } from 'viem/dist/types/actions/wallet/writeContract'; +import { + Client, + createTestClient, + http, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, +} from 'viem'; import { erc20Abi } from '../src/abi'; +import { sendTransactionGetBalances } from './lib/utils/helper'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -23,11 +31,29 @@ describe('weighted join test', () => { let api: MockApi; let chainId: ChainId; let rpcUrl: string; + let blockNumber: bigint; + let client: Client & PublicActions & TestActions & WalletActions; - beforeAll(() => { + beforeAll(async () => { api = new MockApi(); chainId = ChainId.MAINNET; rpcUrl = 'http://127.0.0.1:8545/'; + blockNumber = 18043296n; + client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + await client.reset({ + blockNumber, + jsonRpcUrl: + process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com', + }); + + await client.impersonateAccount({ address: testAddress }); }); test('should join', async () => { const poolId = @@ -49,38 +75,42 @@ describe('weighted join test', () => { }; const queryResult = await weightedJoin.query(queryInput, poolFromApi); - const { call, to } = weightedJoin.buildCall({ + const { call, to, value } = weightedJoin.buildCall({ ...queryResult, slippage: '10', sender: testAddress, recipient: testAddress, }); - const client = createTestClient({ - chain: mainnet, - mode: 'hardhat', - transport: http(rpcUrl), - }).extend(walletActions); - - await client.impersonateAccount({ address: testAddress }); - + // approve token on the vault await client.writeContract({ account: testAddress, + chain: CHAINS[chainId], address: tokenIn.address, abi: erc20Abi, functionName: 'approve', - args: [to, queryResult.amountsIn[0].amount], + args: [to, MAX_UINT256], }); - const result = await client.sendTransaction({ - account: testAddress, - to, - data: call, - }); + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...queryResult.assets, queryResult.bptOut.token.address], + client, + testAddress, + to, + call, + value, + ); - console.log('result', result); + console.log('result', balanceDeltas); - expect(to).toEqual(BALANCER_VAULT); + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); }); }); From 67336474a6ee79161c5e1ee97d7609f73ffb2e3e Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 1 Sep 2023 16:20:38 -0300 Subject: [PATCH 013/199] Refactor test to improve readability --- test/lib/utils/helper.ts | 25 +++++- test/weightedJoin.integration.test.ts | 110 +++++++++++++------------- 2 files changed, 79 insertions(+), 56 deletions(-) diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 0a163951..ae2fb4e3 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -3,12 +3,33 @@ import { Client, PublicActions, TestActions, - TestClient, TransactionReceipt, WalletActions, } from 'viem'; import { erc20Abi } from '../../../src/abi'; -import { ZERO_ADDRESS } from '../../../src/utils'; +import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../src/utils'; + +export const approveToken = async ( + client: Client & PublicActions & WalletActions, + account: Address, + token: Address, + amount = MAX_UINT256, // approve max by default +): Promise => { + // approve token on the vault + const hash = await client.writeContract({ + account, + chain: client.chain, + address: token, + abi: erc20Abi, + functionName: 'approve', + args: [BALANCER_VAULT, amount], + }); + + const txReceipt = await client.getTransactionReceipt({ + hash, + }); + return txReceipt.status === 'success'; +}; export const getErc20Balance = ( token: Address, diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index e45567ee..1340bb6d 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -1,16 +1,17 @@ // pnpm test -- weightedJoin.integration.test.ts -import { describe, expect, test, beforeAll } from 'vitest'; +import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); import { + BaseJoin, JoinInput, JoinParser, PoolState, Token, TokenAmount, } from '../src/entities'; -import { CHAINS, ChainId, MAX_UINT256, getPoolAddress } from '../src/utils'; +import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { Address } from '../src/types'; import { Client, @@ -22,8 +23,7 @@ import { WalletActions, walletActions, } from 'viem'; -import { erc20Abi } from '../src/abi'; -import { sendTransactionGetBalances } from './lib/utils/helper'; +import { approveToken, sendTransactionGetBalances } from './lib/utils/helper'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -33,6 +33,10 @@ describe('weighted join test', () => { let rpcUrl: string; let blockNumber: bigint; let client: Client & PublicActions & TestActions & WalletActions; + let poolId: Address; + let poolFromApi: PoolState; + let tokenIn: Token; + let weightedJoin: BaseJoin; beforeAll(async () => { api = new MockApi(); @@ -46,71 +50,69 @@ describe('weighted join test', () => { }) .extend(publicActions) .extend(walletActions); + }); + beforeEach(async () => { await client.reset({ blockNumber, jsonRpcUrl: process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com', }); - await client.impersonateAccount({ address: testAddress }); + await approveToken(client, testAddress, tokenIn.address); + + poolFromApi = await api.getPool(poolId); + const joinParser = new JoinParser(); + weightedJoin = joinParser.getJoin(poolFromApi.type); }); - test('should join', async () => { - const poolId = + + describe('single token join', async () => { + poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH - // Calls API - const poolFromApi = await api.getPool(poolId); - const joinParser = new JoinParser(); - const weightedJoin = joinParser.getJoin(poolFromApi.type); - const tokenInRaw = poolFromApi.tokens[0]; - const tokenIn = new Token( + tokenIn = new Token( chainId, - tokenInRaw.address, - tokenInRaw.decimals, + '0xba100000625a3754423978a60c9317c58a424e3D', + 18, + 'BAL', ); - const queryInput: JoinInput = { - tokenAmounts: [TokenAmount.fromHumanAmount(tokenIn, '1')], - chainId, - rpcUrl, - }; - const queryResult = await weightedJoin.query(queryInput, poolFromApi); + const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - const { call, to, value } = weightedJoin.buildCall({ - ...queryResult, - slippage: '10', - sender: testAddress, - recipient: testAddress, - }); - - // approve token on the vault - await client.writeContract({ - account: testAddress, - chain: CHAINS[chainId], - address: tokenIn.address, - abi: erc20Abi, - functionName: 'approve', - args: [to, MAX_UINT256], - }); - - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...queryResult.assets, queryResult.bptOut.token.address], - client, - testAddress, - to, - call, - value, + test('should join', async () => { + const joinInput: JoinInput = { + tokenAmounts: [amountIn], + chainId, + rpcUrl, + }; + const queryResult = await weightedJoin.query( + joinInput, + poolFromApi, ); - console.log('result', balanceDeltas); + const { call, to, value } = weightedJoin.buildCall({ + ...queryResult, + slippage: '10', + sender: testAddress, + recipient: testAddress, + }); + + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...queryResult.assets, queryResult.bptOut.token.address], + client, + testAddress, + to, + call, + value, + ); - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + }); }); }); From feda776dfa1abc9d7913cd37710735dc9110fb2f Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 1 Sep 2023 16:58:54 -0300 Subject: [PATCH 014/199] Add Slippage helper class --- src/entities/pools/index.ts | 5 ++- src/entities/pools/weighted/weightedJoin.ts | 9 ++++- src/entities/slippage.ts | 44 +++++++++++++++++++++ test/weightedJoin.integration.test.ts | 10 ++++- 4 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 src/entities/slippage.ts diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index f5df4994..1be2941f 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -2,6 +2,7 @@ import { Hex } from 'viem'; import { Address, PoolType, SwapKind } from '../../types'; import { Token, TokenAmount } from '../'; import { RawPool } from '../../data/types'; +import { Slippage } from '../slippage'; export interface BasePool { readonly poolType: PoolType | string; @@ -65,7 +66,7 @@ export type JoinQueryResult = { }; export type JoinCallInput = JoinQueryResult & { - slippage: string; + slippage: Slippage; sender: Address; recipient: Address; }; @@ -73,10 +74,10 @@ export type JoinCallInput = JoinQueryResult & { export interface BaseJoin { getInstance(): BaseJoin; query(input: JoinInput, poolState: PoolState): Promise; - // TODO - Best way to represent slippage? buildCall(input: JoinCallInput): { call: Address; to: Address; value: bigint; + minBptOut: bigint; }; } diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index da6b4052..e068e8f4 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -108,9 +108,11 @@ export class WeightedJoin implements BaseJoin { call: Address; to: Address; value: bigint; + minBptOut: bigint; } { let maxAmountsIn: bigint[]; let userData: Address; + let minBptOut = input.bptOut.amount; switch (input.joinKind) { case 'Init': { @@ -120,12 +122,14 @@ export class WeightedJoin implements BaseJoin { } case 'GivenIn': { maxAmountsIn = input.amountsIn.map((a) => a.amount); - const minBptOut = input.bptOut.amount; // TODO sub slippage here + minBptOut = input.slippage.removeFrom(input.bptOut.amount); userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); break; } case 'GivenOut': { - maxAmountsIn = input.amountsIn.map((a) => a.amount); // TODO add slippage here + maxAmountsIn = input.amountsIn.map((a) => + input.slippage.applyTo(a.amount), + ); userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); break; } @@ -153,6 +157,7 @@ export class WeightedJoin implements BaseJoin { call, to: BALANCER_VAULT, value: 0n, // TODO: ETH value when joining with ETH + minBptOut, }; } diff --git a/src/entities/slippage.ts b/src/entities/slippage.ts new file mode 100644 index 00000000..7234ac0c --- /dev/null +++ b/src/entities/slippage.ts @@ -0,0 +1,44 @@ +import { formatEther, parseEther } from 'viem'; +import { BigintIsh } from './tokenAmount'; +import { MathSol, WAD } from '../utils'; + +export class Slippage { + public amount: bigint; + public decimal: number; + public percentage: number; + public bps: number; + + public static fromRawAmount(rawAmount: BigintIsh) { + return new Slippage(rawAmount); + } + + public static fromDecimal(decimalAmount: `${number}`) { + const rawAmount = parseEther(decimalAmount); + return Slippage.fromRawAmount(rawAmount); + } + + public static fromPercentage(percentageAmount: `${number}`) { + const decimalAmount = Number(percentageAmount) / 100; + return Slippage.fromDecimal(`${decimalAmount}`); + } + + public static fromBasisPoints(bpsAmount: `${number}`) { + const decimalAmount = Number(bpsAmount) / 10000; + return Slippage.fromDecimal(`${decimalAmount}`); + } + + protected constructor(amount: BigintIsh) { + this.amount = BigInt(amount); + this.decimal = parseFloat(formatEther(this.amount)); + this.percentage = this.decimal * 100; + this.bps = this.decimal * 10000; + } + + public applyTo(amount: bigint): bigint { + return MathSol.mulDownFixed(amount, this.amount + WAD); + } + + public removeFrom(amount: bigint): bigint { + return MathSol.divDownFixed(amount, this.amount + WAD); + } +} diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 1340bb6d..a688be75 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -24,6 +24,7 @@ import { walletActions, } from 'viem'; import { approveToken, sendTransactionGetBalances } from './lib/utils/helper'; +import { Slippage } from '../src/entities/slippage'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -88,9 +89,10 @@ describe('weighted join test', () => { poolFromApi, ); - const { call, to, value } = weightedJoin.buildCall({ + const slippage = Slippage.fromPercentage('1'); + const { call, to, value, minBptOut } = weightedJoin.buildCall({ ...queryResult, - slippage: '10', + slippage, sender: testAddress, recipient: testAddress, }); @@ -112,6 +114,10 @@ describe('weighted join test', () => { queryResult.bptOut.amount, ]; expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinBpt = slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); }); }); }); From dfb113faa2fed02a1837bd6268a107eb42a7d004 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 1 Sep 2023 16:59:39 -0300 Subject: [PATCH 015/199] Expose slippage entity --- src/entities/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entities/index.ts b/src/entities/index.ts index 853163fa..a42aaaa9 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,5 +1,6 @@ export * from './path'; export * from './swap'; +export * from './slippage'; export * from './token'; export * from './tokenAmount'; export * from './pools/'; From 443ec1666d23226da4564f85a5d0841e219ed352 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Mon, 4 Sep 2023 14:26:11 -0300 Subject: [PATCH 016/199] Add hardhat to dev dependencies so we're able to run local fork for testing --- .gitignore | 15 +- hardhat.config.ts | 12 + package.json | 6 +- pnpm-lock.yaml | 1956 ++++++++++++++++++++++++++++++++++++++++- tsconfig.testing.json | 6 + 5 files changed, 1984 insertions(+), 11 deletions(-) create mode 100644 hardhat.config.ts create mode 100644 tsconfig.testing.json diff --git a/.gitignore b/.gitignore index 7bbcc288..3e819cac 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,17 @@ jspm_packages/ .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz -.pnp.* \ No newline at end of file +.pnp.* + +# hardhat +node_modules +.env +coverage +coverage.json +typechain +typechain-types + +# Hardhat files +cache +artifacts + diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 00000000..6efa9583 --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,12 @@ +import '@nomiclabs/hardhat-ethers'; + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +export default { + networks: { + hardhat: { + chainId: 1, + }, + }, +}; diff --git a/package.json b/package.json index a2131f71..f6ccb0ba 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "test": "vitest dev", "test:ci": "vitest run", "changeset": "changeset", - "changeset:release": "pnpm build && changeset publish" + "changeset:release": "pnpm build && changeset publish", + "node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)" }, "dependencies": { "async-retry": "^1.3.3", @@ -34,11 +35,14 @@ }, "devDependencies": { "@changesets/cli": "^2.26.1", + "@nomiclabs/hardhat-ethers": "^2.2.3", "@types/async-retry": "^1.4.4", "@types/node": "^18.11.18", "dotenv": "^16.0.3", + "hardhat": "^2.17.2", "pino-pretty": "^10.0.0", "rome": "12.1.3", + "ts-node": "^10.9.1", "tsup": "^6.6.0", "typescript": "^5.0.4", "vite": "^4.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 684c8d1d..0a294b89 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,9 @@ devDependencies: '@changesets/cli': specifier: ^2.26.1 version: 2.26.1 + '@nomiclabs/hardhat-ethers': + specifier: ^2.2.3 + version: 2.2.3(ethers@5.7.2)(hardhat@2.17.2) '@types/async-retry': specifier: ^1.4.4 version: 1.4.5 @@ -31,15 +34,21 @@ devDependencies: dotenv: specifier: ^16.0.3 version: 16.0.3 + hardhat: + specifier: ^2.17.2 + version: 2.17.2(ts-node@10.9.1)(typescript@5.1.3) pino-pretty: specifier: ^10.0.0 version: 10.0.0 rome: specifier: 12.1.3 version: 12.1.3 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.15.11)(typescript@5.1.3) tsup: specifier: ^6.6.0 - version: 6.7.0(typescript@5.1.3) + version: 6.7.0(ts-node@10.9.1)(typescript@5.1.3) typescript: specifier: ^5.0.4 version: 5.1.3 @@ -84,6 +93,37 @@ packages: regenerator-runtime: 0.13.11 dev: true + /@chainsafe/as-sha256@0.3.1: + resolution: {integrity: sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==} + dev: true + + /@chainsafe/persistent-merkle-tree@0.4.2: + resolution: {integrity: sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + dev: true + + /@chainsafe/persistent-merkle-tree@0.5.0: + resolution: {integrity: sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + dev: true + + /@chainsafe/ssz@0.10.2: + resolution: {integrity: sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + '@chainsafe/persistent-merkle-tree': 0.5.0 + dev: true + + /@chainsafe/ssz@0.9.4: + resolution: {integrity: sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + '@chainsafe/persistent-merkle-tree': 0.4.2 + case: 1.6.3 + dev: true + /@changesets/apply-release-plan@6.1.3: resolution: {integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==} dependencies: @@ -268,6 +308,13 @@ packages: prettier: 2.8.8 dev: true + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@esbuild/android-arm64@0.17.15: resolution: {integrity: sha512-0kOB6Y7Br3KDVgHeg8PRcvfLkq+AccreK///B4Z6fNZGr/tNHX0z2VywCc7PTeWp+bPvjA5WMvNXltHw5QjAIA==} engines: {node: '>=12'} @@ -664,10 +711,337 @@ packages: dev: true optional: true + /@ethersproject/abi@5.7.0: + resolution: {integrity: sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==} + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + dev: true + + /@ethersproject/abstract-provider@5.7.0: + resolution: {integrity: sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/properties': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/web': 5.7.1 + dev: true + + /@ethersproject/abstract-signer@5.7.0: + resolution: {integrity: sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==} + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + dev: true + + /@ethersproject/address@5.7.0: + resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/rlp': 5.7.0 + dev: true + + /@ethersproject/base64@5.7.0: + resolution: {integrity: sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==} + dependencies: + '@ethersproject/bytes': 5.7.0 + dev: true + + /@ethersproject/basex@5.7.0: + resolution: {integrity: sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/properties': 5.7.0 + dev: true + + /@ethersproject/bignumber@5.7.0: + resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + bn.js: 5.2.1 + dev: true + + /@ethersproject/bytes@5.7.0: + resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} + dependencies: + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/constants@5.7.0: + resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + dev: true + + /@ethersproject/contracts@5.7.0: + resolution: {integrity: sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==} + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/transactions': 5.7.0 + dev: true + + /@ethersproject/hash@5.7.0: + resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + dev: true + + /@ethersproject/hdnode@5.7.0: + resolution: {integrity: sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==} + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wordlists': 5.7.0 + dev: true + + /@ethersproject/json-wallets@5.7.0: + resolution: {integrity: sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==} + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + aes-js: 3.0.0 + scrypt-js: 3.0.1 + dev: true + + /@ethersproject/keccak256@5.7.0: + resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} + dependencies: + '@ethersproject/bytes': 5.7.0 + js-sha3: 0.8.0 + dev: true + + /@ethersproject/logger@5.7.0: + resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} + dev: true + + /@ethersproject/networks@5.7.1: + resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} + dependencies: + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/pbkdf2@5.7.0: + resolution: {integrity: sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/sha2': 5.7.0 + dev: true + + /@ethersproject/properties@5.7.0: + resolution: {integrity: sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==} + dependencies: + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/providers@5.7.2: + resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/web': 5.7.1 + bech32: 1.1.4 + ws: 7.4.6 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@ethersproject/random@5.7.0: + resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/rlp@5.7.0: + resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/sha2@5.7.0: + resolution: {integrity: sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + hash.js: 1.1.7 + dev: true + + /@ethersproject/signing-key@5.7.0: + resolution: {integrity: sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + bn.js: 5.2.1 + elliptic: 6.5.4 + hash.js: 1.1.7 + dev: true + + /@ethersproject/solidity@5.7.0: + resolution: {integrity: sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + dev: true + + /@ethersproject/strings@5.7.0: + resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/transactions@5.7.0: + resolution: {integrity: sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==} + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + dev: true + + /@ethersproject/units@5.7.0: + resolution: {integrity: sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/wallet@5.7.0: + resolution: {integrity: sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==} + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wordlists': 5.7.0 + dev: true + + /@ethersproject/web@5.7.1: + resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} + dependencies: + '@ethersproject/base64': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + dev: true + + /@ethersproject/wordlists@5.7.0: + resolution: {integrity: sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: @@ -688,6 +1062,17 @@ packages: read-yaml-file: 1.1.0 dev: true + /@metamask/eth-sig-util@4.0.1: + resolution: {integrity: sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==} + engines: {node: '>=12.0.0'} + dependencies: + ethereumjs-abi: 0.6.8 + ethereumjs-util: 6.2.1 + ethjs-util: 0.1.6 + tweetnacl: 1.0.3 + tweetnacl-util: 0.15.1 + dev: true + /@noble/curves@1.0.0: resolution: {integrity: sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==} dependencies: @@ -700,6 +1085,10 @@ packages: '@noble/hashes': 1.3.1 dev: false + /@noble/hashes@1.2.0: + resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} + dev: true + /@noble/hashes@1.3.0: resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} dev: false @@ -709,6 +1098,10 @@ packages: engines: {node: '>= 16'} dev: false + /@noble/secp256k1@1.7.1: + resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} + dev: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -730,6 +1123,280 @@ packages: fastq: 1.15.0 dev: true + /@nomicfoundation/ethereumjs-block@5.0.2: + resolution: {integrity: sha512-hSe6CuHI4SsSiWWjHDIzWhSiAVpzMUcDRpWYzN0T9l8/Rz7xNn3elwVOJ/tAyS0LqL6vitUD78Uk7lQDXZun7Q==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-trie': 6.0.2 + '@nomicfoundation/ethereumjs-tx': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + ethereum-cryptography: 0.1.3 + ethers: 5.7.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@nomicfoundation/ethereumjs-blockchain@7.0.2: + resolution: {integrity: sha512-8UUsSXJs+MFfIIAKdh3cG16iNmWzWC/91P40sazNvrqhhdR/RtGDlFk2iFTGbBAZPs2+klZVzhRX8m2wvuvz3w==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-block': 5.0.2 + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-ethash': 3.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-trie': 6.0.2 + '@nomicfoundation/ethereumjs-tx': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + abstract-level: 1.0.3 + debug: 4.3.4(supports-color@8.1.1) + ethereum-cryptography: 0.1.3 + level: 8.0.0 + lru-cache: 5.1.1 + memory-level: 1.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /@nomicfoundation/ethereumjs-common@4.0.2: + resolution: {integrity: sha512-I2WGP3HMGsOoycSdOTSqIaES0ughQTueOsddJ36aYVpI3SN8YSusgRFLwzDJwRFVIYDKx/iJz0sQ5kBHVgdDwg==} + dependencies: + '@nomicfoundation/ethereumjs-util': 9.0.2 + crc-32: 1.2.2 + dev: true + + /@nomicfoundation/ethereumjs-ethash@3.0.2: + resolution: {integrity: sha512-8PfoOQCcIcO9Pylq0Buijuq/O73tmMVURK0OqdjhwqcGHYC2PwhbajDh7GZ55ekB0Px197ajK3PQhpKoiI/UPg==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-block': 5.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + abstract-level: 1.0.3 + bigint-crypto-utils: 3.3.0 + ethereum-cryptography: 0.1.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@nomicfoundation/ethereumjs-evm@2.0.2: + resolution: {integrity: sha512-rBLcUaUfANJxyOx9HIdMX6uXGin6lANCulIm/pjMgRqfiCRMZie3WKYxTSd8ZE/d+qT+zTedBF4+VHTdTSePmQ==} + engines: {node: '>=14'} + dependencies: + '@ethersproject/providers': 5.7.2 + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-tx': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + debug: 4.3.4(supports-color@8.1.1) + ethereum-cryptography: 0.1.3 + mcl-wasm: 0.7.9 + rustbn.js: 0.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /@nomicfoundation/ethereumjs-rlp@5.0.2: + resolution: {integrity: sha512-QwmemBc+MMsHJ1P1QvPl8R8p2aPvvVcKBbvHnQOKBpBztEo0omN0eaob6FeZS/e3y9NSe+mfu3nNFBHszqkjTA==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /@nomicfoundation/ethereumjs-statemanager@2.0.2: + resolution: {integrity: sha512-dlKy5dIXLuDubx8Z74sipciZnJTRSV/uHG48RSijhgm1V7eXYFC567xgKtsKiVZB1ViTP9iFL4B6Je0xD6X2OA==} + dependencies: + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + debug: 4.3.4(supports-color@8.1.1) + ethereum-cryptography: 0.1.3 + ethers: 5.7.2 + js-sdsl: 4.4.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /@nomicfoundation/ethereumjs-trie@6.0.2: + resolution: {integrity: sha512-yw8vg9hBeLYk4YNg5MrSJ5H55TLOv2FSWUTROtDtTMMmDGROsAu+0tBjiNGTnKRi400M6cEzoFfa89Fc5k8NTQ==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + '@types/readable-stream': 2.3.15 + ethereum-cryptography: 0.1.3 + readable-stream: 3.6.2 + dev: true + + /@nomicfoundation/ethereumjs-tx@5.0.2: + resolution: {integrity: sha512-T+l4/MmTp7VhJeNloMkM+lPU3YMUaXdcXgTGCf8+ZFvV9NYZTRLFekRwlG6/JMmVfIfbrW+dRRJ9A6H5Q/Z64g==} + engines: {node: '>=14'} + dependencies: + '@chainsafe/ssz': 0.9.4 + '@ethersproject/providers': 5.7.2 + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + ethereum-cryptography: 0.1.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@nomicfoundation/ethereumjs-util@9.0.2: + resolution: {integrity: sha512-4Wu9D3LykbSBWZo8nJCnzVIYGvGCuyiYLIJa9XXNVt1q1jUzHdB+sJvx95VGCpPkCT+IbLecW6yfzy3E1bQrwQ==} + engines: {node: '>=14'} + dependencies: + '@chainsafe/ssz': 0.10.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + ethereum-cryptography: 0.1.3 + dev: true + + /@nomicfoundation/ethereumjs-vm@7.0.2: + resolution: {integrity: sha512-Bj3KZT64j54Tcwr7Qm/0jkeZXJMfdcAtRBedou+Hx0dPOSIgqaIr0vvLwP65TpHbak2DmAq+KJbW2KNtIoFwvA==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-block': 5.0.2 + '@nomicfoundation/ethereumjs-blockchain': 7.0.2 + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-evm': 2.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-statemanager': 2.0.2 + '@nomicfoundation/ethereumjs-trie': 6.0.2 + '@nomicfoundation/ethereumjs-tx': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + debug: 4.3.4(supports-color@8.1.1) + ethereum-cryptography: 0.1.3 + mcl-wasm: 0.7.9 + rustbn.js: 0.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1: + resolution: {integrity: sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1: + resolution: {integrity: sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1: + resolution: {integrity: sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1: + resolution: {integrity: sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1: + resolution: {integrity: sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1: + resolution: {integrity: sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1: + resolution: {integrity: sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1: + resolution: {integrity: sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1: + resolution: {integrity: sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1: + resolution: {integrity: sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@nomicfoundation/solidity-analyzer@0.1.1: + resolution: {integrity: sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==} + engines: {node: '>= 12'} + optionalDependencies: + '@nomicfoundation/solidity-analyzer-darwin-arm64': 0.1.1 + '@nomicfoundation/solidity-analyzer-darwin-x64': 0.1.1 + '@nomicfoundation/solidity-analyzer-freebsd-x64': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-arm64-gnu': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-arm64-musl': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-x64-gnu': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.1 + '@nomicfoundation/solidity-analyzer-win32-arm64-msvc': 0.1.1 + '@nomicfoundation/solidity-analyzer-win32-ia32-msvc': 0.1.1 + '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 + dev: true + + /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.17.2): + resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} + peerDependencies: + ethers: ^5.0.0 + hardhat: ^2.0.0 + dependencies: + ethers: 5.7.2 + hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.1.3) + dev: true + /@rometools/cli-darwin-arm64@12.1.3: resolution: {integrity: sha512-AmFTUDYjBuEGQp/Wwps+2cqUr+qhR7gyXAUnkL5psCuNCz3807TrUq/ecOoct5MIavGJTH6R4aaSL6+f+VlBEg==} cpu: [arm64] @@ -780,7 +1447,14 @@ packages: /@scure/base@1.1.1: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} - dev: false + + /@scure/bip32@1.1.5: + resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} + dependencies: + '@noble/hashes': 1.2.0 + '@noble/secp256k1': 1.7.1 + '@scure/base': 1.1.1 + dev: true /@scure/bip32@1.3.0: resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==} @@ -790,6 +1464,13 @@ packages: '@scure/base': 1.1.1 dev: false + /@scure/bip39@1.1.1: + resolution: {integrity: sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==} + dependencies: + '@noble/hashes': 1.2.0 + '@scure/base': 1.1.1 + dev: true + /@scure/bip39@1.2.0: resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} dependencies: @@ -797,12 +1478,110 @@ packages: '@scure/base': 1.1.1 dev: false + /@sentry/core@5.30.0: + resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==} + engines: {node: '>=6'} + dependencies: + '@sentry/hub': 5.30.0 + '@sentry/minimal': 5.30.0 + '@sentry/types': 5.30.0 + '@sentry/utils': 5.30.0 + tslib: 1.14.1 + dev: true + + /@sentry/hub@5.30.0: + resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==} + engines: {node: '>=6'} + dependencies: + '@sentry/types': 5.30.0 + '@sentry/utils': 5.30.0 + tslib: 1.14.1 + dev: true + + /@sentry/minimal@5.30.0: + resolution: {integrity: sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==} + engines: {node: '>=6'} + dependencies: + '@sentry/hub': 5.30.0 + '@sentry/types': 5.30.0 + tslib: 1.14.1 + dev: true + + /@sentry/node@5.30.0: + resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==} + engines: {node: '>=6'} + dependencies: + '@sentry/core': 5.30.0 + '@sentry/hub': 5.30.0 + '@sentry/tracing': 5.30.0 + '@sentry/types': 5.30.0 + '@sentry/utils': 5.30.0 + cookie: 0.4.2 + https-proxy-agent: 5.0.1 + lru_map: 0.3.3 + tslib: 1.14.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@sentry/tracing@5.30.0: + resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==} + engines: {node: '>=6'} + dependencies: + '@sentry/hub': 5.30.0 + '@sentry/minimal': 5.30.0 + '@sentry/types': 5.30.0 + '@sentry/utils': 5.30.0 + tslib: 1.14.1 + dev: true + + /@sentry/types@5.30.0: + resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==} + engines: {node: '>=6'} + dev: true + + /@sentry/utils@5.30.0: + resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==} + engines: {node: '>=6'} + dependencies: + '@sentry/types': 5.30.0 + tslib: 1.14.1 + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + /@types/async-retry@1.4.5: resolution: {integrity: sha512-YrdjSD+yQv7h6d5Ip+PMxh3H6ZxKyQk0Ts+PvaNRInxneG9PFVZjFg77ILAN+N6qYf7g4giSJ1l+ZjQ1zeegvA==} dependencies: '@types/retry': 0.12.2 dev: true + /@types/bn.js@4.11.6: + resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} + dependencies: + '@types/node': 18.15.11 + dev: true + + /@types/bn.js@5.1.1: + resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} + dependencies: + '@types/node': 18.15.11 + dev: true + /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: @@ -819,6 +1598,10 @@ packages: ci-info: 3.8.0 dev: true + /@types/lru-cache@5.1.1: + resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==} + dev: true + /@types/minimist@1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true @@ -834,10 +1617,29 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/pbkdf2@3.1.0: + resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} + dependencies: + '@types/node': 18.15.11 + dev: true + + /@types/readable-stream@2.3.15: + resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} + dependencies: + '@types/node': 18.15.11 + safe-buffer: 5.1.2 + dev: true + /@types/retry@0.12.2: resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} dev: true + /@types/secp256k1@4.0.3: + resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} + dependencies: + '@types/node': 18.15.11 + dev: true + /@types/semver@6.2.3: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true @@ -907,6 +1709,19 @@ packages: dependencies: event-target-shim: 5.0.1 + /abstract-level@1.0.3: + resolution: {integrity: sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==} + engines: {node: '>=12'} + dependencies: + buffer: 6.0.3 + catering: 2.1.1 + is-buffer: 2.0.5 + level-supports: 4.0.1 + level-transcoder: 1.0.1 + module-error: 1.0.2 + queue-microtask: 1.2.3 + dev: true + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} @@ -918,11 +1733,49 @@ packages: hasBin: true dev: true + /adm-zip@0.4.16: + resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} + engines: {node: '>=0.3.0'} + dev: true + + /aes-js@3.0.0: + resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} + dev: true + + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + + /aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + + /ansi-colors@4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + dev: true + /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} dev: true + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -959,12 +1812,20 @@ packages: picomatch: 2.3.1 dev: true + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: sprintf-js: 1.0.3 dev: true + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} dependencies: @@ -1015,9 +1876,19 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /base-x@3.0.9: + resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + /bech32@1.1.4: + resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} + dev: true + /better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -1025,15 +1896,32 @@ packages: is-windows: 1.0.2 dev: true + /bigint-crypto-utils@3.3.0: + resolution: {integrity: sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg==} + engines: {node: '>=14.0.0'} + dev: true + /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: true + /blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + dev: true + /blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} dev: true + /bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + dev: true + + /bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + dev: true + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1060,6 +1948,56 @@ packages: wcwidth: 1.0.1 dev: true + /brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + dev: true + + /browser-level@1.0.1: + resolution: {integrity: sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==} + dependencies: + abstract-level: 1.0.3 + catering: 2.1.1 + module-error: 1.0.2 + run-parallel-limit: 1.1.0 + dev: true + + /browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dev: true + + /browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + dependencies: + base-x: 3.0.9 + dev: true + + /bs58check@2.1.2: + resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} + dependencies: + bs58: 4.0.1 + create-hash: 1.2.0 + safe-buffer: 5.2.1 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + dev: true + /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: @@ -1076,6 +2014,18 @@ packages: load-tsconfig: 0.2.5 dev: true + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: true + + /bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: true + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1102,6 +2052,21 @@ packages: engines: {node: '>=6'} dev: true + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /case@1.6.3: + resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /catering@2.1.1: + resolution: {integrity: sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==} + engines: {node: '>=6'} + dev: true + /chai@4.3.7: resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} engines: {node: '>=4'} @@ -1155,11 +2120,39 @@ packages: fsevents: 2.3.3 dev: true + /ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + dev: true + /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} dev: true + /cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /classic-level@1.3.0: + resolution: {integrity: sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==} + engines: {node: '>=12'} + requiresBuild: true + dependencies: + abstract-level: 1.0.3 + catering: 2.1.1 + module-error: 1.0.2 + napi-macros: 2.2.2 + node-gyp-build: 4.6.1 + dev: true + + /clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -1168,6 +2161,14 @@ packages: wrap-ansi: 6.2.0 dev: true + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1207,6 +2208,14 @@ packages: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} dev: true + /command-exists@1.2.9: + resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} + dev: true + + /commander@3.0.2: + resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} + dev: true + /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1230,6 +2239,42 @@ packages: well-known-symbols: 2.0.0 dev: true + /cookie@0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + dev: true + + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: true + + /create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: true + + /create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -1280,7 +2325,7 @@ packages: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: true - /debug@4.3.4: + /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -1290,6 +2335,7 @@ packages: optional: true dependencies: ms: 2.1.2 + supports-color: 8.1.1 dev: true /decamelize-keys@1.1.1: @@ -1305,6 +2351,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + dev: true + /decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} dev: false @@ -1330,11 +2381,26 @@ packages: object-keys: 1.1.1 dev: true + /depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: true + /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} dev: true + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /diff@5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} + engines: {node: '>=0.3.1'} + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -1347,6 +2413,18 @@ packages: engines: {node: '>=12'} dev: true + /elliptic@6.5.4: + resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: true + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -1364,6 +2442,11 @@ packages: ansi-colors: 4.1.3 dev: true + /env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: true + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -1504,6 +2587,11 @@ packages: engines: {node: '>=0.8.0'} dev: true + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -1515,6 +2603,100 @@ packages: engines: {node: '>=0.10.0'} dev: true + /ethereum-cryptography@0.1.3: + resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==} + dependencies: + '@types/pbkdf2': 3.1.0 + '@types/secp256k1': 4.0.3 + blakejs: 1.2.1 + browserify-aes: 1.2.0 + bs58check: 2.1.2 + create-hash: 1.2.0 + create-hmac: 1.1.7 + hash.js: 1.1.7 + keccak: 3.0.3 + pbkdf2: 3.1.2 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + scrypt-js: 3.0.1 + secp256k1: 4.0.3 + setimmediate: 1.0.5 + dev: true + + /ethereum-cryptography@1.2.0: + resolution: {integrity: sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==} + dependencies: + '@noble/hashes': 1.2.0 + '@noble/secp256k1': 1.7.1 + '@scure/bip32': 1.1.5 + '@scure/bip39': 1.1.1 + dev: true + + /ethereumjs-abi@0.6.8: + resolution: {integrity: sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==} + dependencies: + bn.js: 4.12.0 + ethereumjs-util: 6.2.1 + dev: true + + /ethereumjs-util@6.2.1: + resolution: {integrity: sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==} + dependencies: + '@types/bn.js': 4.11.6 + bn.js: 4.12.0 + create-hash: 1.2.0 + elliptic: 6.5.4 + ethereum-cryptography: 0.1.3 + ethjs-util: 0.1.6 + rlp: 2.2.7 + dev: true + + /ethers@5.7.2: + resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/contracts': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/solidity': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/units': 5.7.0 + '@ethersproject/wallet': 5.7.0 + '@ethersproject/web': 5.7.1 + '@ethersproject/wordlists': 5.7.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /ethjs-util@0.1.6: + resolution: {integrity: sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==} + engines: {node: '>=6.5.0', npm: '>=3'} + dependencies: + is-hex-prefixed: 1.0.0 + strip-hex-prefix: 1.0.0 + dev: true + /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -1523,6 +2705,13 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + /evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + dev: true + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -1592,6 +2781,13 @@ packages: to-regex-range: 5.0.1 dev: true + /find-up@2.1.0: + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} + dependencies: + locate-path: 2.0.0 + dev: true + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1615,12 +2811,43 @@ packages: pkg-dir: 4.2.0 dev: true + /flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + dev: true + + /follow-redirects@1.15.2(debug@4.3.4): + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dependencies: + debug: 4.3.4(supports-color@8.1.1) + dev: true + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: is-callable: 1.2.7 dev: true + /fp-ts@1.19.3: + resolution: {integrity: sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==} + dev: true + + /fs-extra@0.30.0: + resolution: {integrity: sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 2.4.0 + klaw: 1.3.1 + path-is-absolute: 1.0.1 + rimraf: 2.7.1 + dev: true + /fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -1665,6 +2892,10 @@ packages: functions-have-names: 1.2.3 dev: true + /functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: true + /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true @@ -1718,6 +2949,17 @@ packages: path-is-absolute: 1.0.1 dev: true + /glob@7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -1767,6 +3009,74 @@ packages: engines: {node: '>=6'} dev: true + /hardhat@2.17.2(ts-node@10.9.1)(typescript@5.1.3): + resolution: {integrity: sha512-oUv40jBeHw0dKpbyQ+iH9cmNMziweLoTW3MnkNxJ2Gc0KGLrQR/1n4vV4xY60zn2LdmRgnwPqy3CgtY0mfwIIA==} + hasBin: true + peerDependencies: + ts-node: '*' + typescript: '*' + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + dependencies: + '@ethersproject/abi': 5.7.0 + '@metamask/eth-sig-util': 4.0.1 + '@nomicfoundation/ethereumjs-block': 5.0.2 + '@nomicfoundation/ethereumjs-blockchain': 7.0.2 + '@nomicfoundation/ethereumjs-common': 4.0.2 + '@nomicfoundation/ethereumjs-evm': 2.0.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.2 + '@nomicfoundation/ethereumjs-statemanager': 2.0.2 + '@nomicfoundation/ethereumjs-trie': 6.0.2 + '@nomicfoundation/ethereumjs-tx': 5.0.2 + '@nomicfoundation/ethereumjs-util': 9.0.2 + '@nomicfoundation/ethereumjs-vm': 7.0.2 + '@nomicfoundation/solidity-analyzer': 0.1.1 + '@sentry/node': 5.30.0 + '@types/bn.js': 5.1.1 + '@types/lru-cache': 5.1.1 + adm-zip: 0.4.16 + aggregate-error: 3.1.0 + ansi-escapes: 4.3.2 + chalk: 2.4.2 + chokidar: 3.5.3 + ci-info: 2.0.0 + debug: 4.3.4(supports-color@8.1.1) + enquirer: 2.3.6 + env-paths: 2.2.1 + ethereum-cryptography: 1.2.0 + ethereumjs-abi: 0.6.8 + find-up: 2.1.0 + fp-ts: 1.19.3 + fs-extra: 7.0.1 + glob: 7.2.0 + immutable: 4.3.4 + io-ts: 1.10.4 + keccak: 3.0.3 + lodash: 4.17.21 + mnemonist: 0.38.5 + mocha: 10.2.0 + p-map: 4.0.0 + raw-body: 2.5.2 + resolve: 1.17.0 + semver: 6.3.1 + solc: 0.7.3(debug@4.3.4) + source-map-support: 0.5.21 + stacktrace-parser: 0.1.10 + ts-node: 10.9.1(@types/node@18.15.11)(typescript@5.1.3) + tsort: 0.0.1 + typescript: 5.1.3 + undici: 5.23.0 + uuid: 8.3.2 + ws: 7.5.9 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -1811,6 +3121,27 @@ packages: function-bind: 1.1.1 dev: true + /hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + dev: true + + /hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: true + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + /help-me@4.2.0: resolution: {integrity: sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==} dependencies: @@ -1818,10 +3149,39 @@ packages: readable-stream: 3.6.2 dev: true + /hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + dev: true + /hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: true + + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: true + /human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true @@ -1846,6 +3206,10 @@ packages: engines: {node: '>= 4'} dev: true + /immutable@4.3.4: + resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} + dev: true + /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -1871,6 +3235,12 @@ packages: side-channel: 1.0.4 dev: true + /io-ts@1.10.4: + resolution: {integrity: sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==} + dependencies: + fp-ts: 1.19.3 + dev: true + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -1904,6 +3274,11 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: true + /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -1946,6 +3321,11 @@ packages: is-extglob: 2.1.1 dev: true + /is-hex-prefixed@1.0.0: + resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} + engines: {node: '>=6.5.0', npm: '>=3'} + dev: true + /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -1968,6 +3348,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: true + /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -2019,6 +3404,11 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: @@ -2047,6 +3437,14 @@ packages: engines: {node: '>=10'} dev: true + /js-sdsl@4.4.2: + resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} + dev: true + + /js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + dev: true + /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} engines: {node: '>= 0.8'} @@ -2064,6 +3462,13 @@ packages: esprima: 4.0.1 dev: true + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -2072,22 +3477,65 @@ packages: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true + /jsonfile@2.4.0: + resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 dev: true + /keccak@3.0.3: + resolution: {integrity: sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.6.1 + readable-stream: 3.6.2 + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} dev: true + /klaw@1.3.1: + resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: true + /kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} dev: true + /level-supports@4.0.1: + resolution: {integrity: sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==} + engines: {node: '>=12'} + dev: true + + /level-transcoder@1.0.1: + resolution: {integrity: sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==} + engines: {node: '>=12'} + dependencies: + buffer: 6.0.3 + module-error: 1.0.2 + dev: true + + /level@8.0.0: + resolution: {integrity: sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==} + engines: {node: '>=12'} + dependencies: + browser-level: 1.0.1 + classic-level: 1.3.0 + dev: true + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -2117,6 +3565,14 @@ packages: engines: {node: '>=14'} dev: true + /locate-path@2.0.0: + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} + dependencies: + p-locate: 2.0.0 + path-exists: 3.0.0 + dev: true + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2143,6 +3599,14 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: @@ -2156,6 +3620,12 @@ packages: yallist: 2.1.2 dev: true + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -2163,6 +3633,10 @@ packages: yallist: 4.0.0 dev: true + /lru_map@0.3.3: + resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} + dev: true + /magic-string@0.30.1: resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} engines: {node: '>=12'} @@ -2170,6 +3644,10 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -2180,6 +3658,11 @@ packages: engines: {node: '>=8'} dev: true + /mcl-wasm@0.7.9: + resolution: {integrity: sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==} + engines: {node: '>=8.9.0'} + dev: true + /md5-hex@3.0.1: resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} engines: {node: '>=8'} @@ -2187,6 +3670,28 @@ packages: blueimp-md5: 2.19.0 dev: true + /md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /memory-level@1.0.0: + resolution: {integrity: sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==} + engines: {node: '>=12'} + dependencies: + abstract-level: 1.0.3 + functional-red-black-tree: 1.0.1 + module-error: 1.0.2 + dev: true + + /memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + dev: true + /meow@6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} @@ -2231,12 +3736,27 @@ packages: engines: {node: '>=4'} dev: true + /minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + dev: true + + /minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true + /minimatch@5.0.1: + resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -2271,10 +3791,53 @@ packages: ufo: 1.1.2 dev: true + /mnemonist@0.38.5: + resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} + dependencies: + obliterator: 2.0.4 + dev: true + + /mocha@10.2.0: + resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} + engines: {node: '>= 14.0.0'} + hasBin: true + dependencies: + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@8.1.1) + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.2.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.0.1 + ms: 2.1.3 + nanoid: 3.3.3 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.2.1 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + dev: true + + /module-error@1.0.2: + resolution: {integrity: sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==} + engines: {node: '>=10'} + dev: true + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -2283,12 +3846,31 @@ packages: thenify-all: 1.6.0 dev: true + /nanoid@3.3.3: + resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true + /napi-macros@2.2.2: + resolution: {integrity: sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==} + dev: true + + /node-addon-api@2.0.2: + resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} + dev: true + + /node-gyp-build@4.6.1: + resolution: {integrity: sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==} + hasBin: true + dev: true + /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -2334,6 +3916,10 @@ packages: object-keys: 1.1.1 dev: true + /obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + dev: true + /on-exit-leak-free@2.1.0: resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==} @@ -2366,6 +3952,13 @@ packages: p-map: 2.1.0 dev: true + /p-limit@1.3.0: + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} + dependencies: + p-try: 1.0.0 + dev: true + /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -2387,6 +3980,13 @@ packages: yocto-queue: 1.0.0 dev: true + /p-locate@2.0.0: + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} + dependencies: + p-limit: 1.3.0 + dev: true + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -2406,6 +4006,18 @@ packages: engines: {node: '>=6'} dev: true + /p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + + /p-try@1.0.0: + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} + dev: true + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -2421,6 +4033,11 @@ packages: lines-and-columns: 1.2.4 dev: true + /path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2453,6 +4070,17 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true + /pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: true + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -2534,7 +4162,7 @@ packages: pathe: 1.1.1 dev: true - /postcss-load-config@3.1.4: + /postcss-load-config@3.1.4(ts-node@10.9.1): resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} peerDependencies: @@ -2547,6 +4175,7 @@ packages: optional: true dependencies: lilconfig: 2.1.0 + ts-node: 10.9.1(@types/node@18.15.11)(typescript@5.1.3) yaml: 1.10.2 dev: true @@ -2621,6 +4250,22 @@ packages: engines: {node: '>=8'} dev: true + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: true + /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true @@ -2710,6 +4355,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true @@ -2719,6 +4369,12 @@ packages: engines: {node: '>=8'} dev: true + /resolve@1.17.0: + resolution: {integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==} + dependencies: + path-parse: 1.0.7 + dev: true + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -2738,6 +4394,27 @@ packages: engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.0 + dev: true + + /ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: true + + /rlp@2.2.7: + resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} + hasBin: true + dependencies: + bn.js: 5.2.1 + dev: true + /rollup@3.20.2: resolution: {integrity: sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -2768,12 +4445,26 @@ packages: '@rometools/cli-win32-x64': 12.1.3 dev: true + /run-parallel-limit@1.1.0: + resolution: {integrity: sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==} + dependencies: + queue-microtask: 1.2.3 + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true + /rustbn.js@0.2.0: + resolution: {integrity: sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==} + dev: true + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true @@ -2795,6 +4486,20 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /scrypt-js@3.0.1: + resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} + dev: true + + /secp256k1@4.0.3: + resolution: {integrity: sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + elliptic: 6.5.4 + node-addon-api: 2.0.2 + node-gyp-build: 4.6.1 + dev: true + /secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} dev: true @@ -2804,6 +4509,11 @@ packages: hasBin: true dev: true + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + /semver@7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} @@ -2812,10 +4522,32 @@ packages: lru-cache: 6.0.0 dev: true + /serialize-javascript@6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + dependencies: + randombytes: 2.1.0 + dev: true + /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true + /setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + dev: true + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: true + + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -2874,6 +4606,24 @@ packages: yargs: 15.4.1 dev: true + /solc@0.7.3(debug@4.3.4): + resolution: {integrity: sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + command-exists: 1.2.9 + commander: 3.0.2 + follow-redirects: 1.15.2(debug@4.3.4) + fs-extra: 0.30.0 + js-sha3: 0.8.0 + memorystream: 0.3.1 + require-from-string: 2.0.2 + semver: 5.7.1 + tmp: 0.0.33 + transitivePeerDependencies: + - debug + dev: true + /sonic-boom@3.3.0: resolution: {integrity: sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==} dependencies: @@ -2884,6 +4634,13 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -2937,6 +4694,18 @@ packages: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true + /stacktrace-parser@0.1.10: + resolution: {integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==} + engines: {node: '>=6'} + dependencies: + type-fest: 0.7.1 + dev: true + + /statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: true + /std-env@3.3.3: resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} dev: true @@ -2947,6 +4716,11 @@ packages: mixme: 0.5.9 dev: true + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3004,6 +4778,13 @@ packages: engines: {node: '>=6'} dev: true + /strip-hex-prefix@1.0.0: + resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} + engines: {node: '>=6.5.0', npm: '>=3'} + dependencies: + is-hex-prefixed: 1.0.0 + dev: true + /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -3049,6 +4830,13 @@ packages: has-flag: 4.0.0 dev: true + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -3111,6 +4899,11 @@ packages: is-number: 7.0.0 dev: true + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: true + /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} dependencies: @@ -3131,7 +4924,46 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /tsup@6.7.0(typescript@5.1.3): + /ts-node@10.9.1(@types/node@18.15.11)(typescript@5.1.3): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.15.11 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tsort@0.0.1: + resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} + dev: true + + /tsup@6.7.0(ts-node@10.9.1)(typescript@5.1.3): resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} engines: {node: '>=14.18'} hasBin: true @@ -3150,12 +4982,12 @@ packages: bundle-require: 4.0.1(esbuild@0.17.15) cac: 6.7.14 chokidar: 3.5.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) esbuild: 0.17.15 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 3.1.4 + postcss-load-config: 3.1.4(ts-node@10.9.1) resolve-from: 5.0.0 rollup: 3.20.2 source-map: 0.8.0-beta.0 @@ -3181,6 +5013,14 @@ packages: yargs: 17.7.1 dev: true + /tweetnacl-util@0.15.1: + resolution: {integrity: sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==} + dev: true + + /tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + dev: true + /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -3191,11 +5031,21 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} dev: true + /type-fest@0.7.1: + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} + dev: true + /type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -3227,15 +5077,36 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /undici@5.23.0: + resolution: {integrity: sha512-1D7w+fvRsqlQ9GscLBwcAJinqcZGHUKjbOmXdlE/v8BvEGXjeWAax+341q44EuTcHXXnfyKNbKRq4Lg7OzhMmg==} + engines: {node: '>=14.0'} + dependencies: + busboy: 1.6.0 + dev: true + /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} dev: true + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: true + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: true + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -3273,7 +5144,7 @@ packages: hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 @@ -3369,7 +5240,7 @@ packages: cac: 6.7.14 chai: 4.3.7 concordance: 5.0.4 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) local-pkg: 0.4.3 magic-string: 0.30.1 pathe: 1.1.1 @@ -3473,6 +5344,10 @@ packages: stackback: 0.0.2 dev: true + /workerpool@6.2.1: + resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} + dev: true + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -3495,6 +5370,32 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true + /ws@7.4.6: + resolution: {integrity: sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + + /ws@7.5.9: + resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /ws@8.12.0: resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} engines: {node: '>=10.0.0'} @@ -3521,6 +5422,10 @@ packages: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: true + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true @@ -3538,11 +5443,26 @@ packages: decamelize: 1.2.0 dev: true + /yargs-parser@20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + dev: true + /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} dev: true + /yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + dev: true + /yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} @@ -3560,6 +5480,19 @@ packages: yargs-parser: 18.1.3 dev: true + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.4 + dev: true + /yargs@17.7.1: resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} engines: {node: '>=12'} @@ -3573,6 +5506,11 @@ packages: yargs-parser: 21.1.1 dev: true + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/tsconfig.testing.json b/tsconfig.testing.json new file mode 100644 index 00000000..2c7b2841 --- /dev/null +++ b/tsconfig.testing.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs" + } +} From 29fa180149de4f50eff406cbd2047da79c24af26 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Mon, 4 Sep 2023 14:29:10 -0300 Subject: [PATCH 017/199] Add comments to test file --- test/weightedJoin.integration.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index a688be75..b73a39a6 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -40,7 +40,10 @@ describe('weighted join test', () => { let weightedJoin: BaseJoin; beforeAll(async () => { + // setup mock api api = new MockApi(); + + // setup chain and test client chainId = ChainId.MAINNET; rpcUrl = 'http://127.0.0.1:8545/'; blockNumber = 18043296n; @@ -54,20 +57,27 @@ describe('weighted join test', () => { }); beforeEach(async () => { + // reset local fork await client.reset({ blockNumber, jsonRpcUrl: process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com', }); + + // prepare test client with balance and token approvals await client.impersonateAccount({ address: testAddress }); await approveToken(client, testAddress, tokenIn.address); + // get pool state from api poolFromApi = await api.getPool(poolId); + + // setup join helper const joinParser = new JoinParser(); weightedJoin = joinParser.getJoin(poolFromApi.type); }); describe('single token join', async () => { + // set initial test conditions poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH tokenIn = new Token( @@ -79,6 +89,7 @@ describe('weighted join test', () => { const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); test('should join', async () => { + // perform join query to get expected bpt out const joinInput: JoinInput = { tokenAmounts: [amountIn], chainId, @@ -89,7 +100,8 @@ describe('weighted join test', () => { poolFromApi, ); - const slippage = Slippage.fromPercentage('1'); + // build join call with expected minBpOut based on slippage + const slippage = Slippage.fromPercentage('1'); // 1% const { call, to, value, minBptOut } = weightedJoin.buildCall({ ...queryResult, slippage, @@ -97,6 +109,7 @@ describe('weighted join test', () => { recipient: testAddress, }); + // send join transaction and check balance changes const { transactionReceipt, balanceDeltas } = await sendTransactionGetBalances( [...queryResult.assets, queryResult.bptOut.token.address], From 92438bf14db92ee6d7a43dc59b41907cb24efefb Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 5 Sep 2023 19:12:00 -0300 Subject: [PATCH 018/199] Fix encoders issues --- src/entities/encoders/weighted.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index 87d77a06..ccaa2227 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -30,7 +30,7 @@ export class WeightedEncoder { static joinInit = (amountsIn: bigint[]): Address => encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256[]' }], - [WeightedPoolJoinKind.INIT, amountsIn], + [BigInt(WeightedPoolJoinKind.INIT), amountsIn], ); /** @@ -42,7 +42,7 @@ export class WeightedEncoder { encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], [ - WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + BigInt(WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT), amountsIn, minimumBPT, ], @@ -62,7 +62,9 @@ export class WeightedEncoder { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ - WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, + BigInt( + WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, + ), bptAmountOut, ], ); @@ -71,9 +73,9 @@ export class WeightedEncoder { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], [ - WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT, + BigInt(WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), bptAmountOut, - enterTokenIndex, + BigInt(enterTokenIndex), ], ); } @@ -91,15 +93,18 @@ export class WeightedEncoder { if (exitTokenIndex === undefined) { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], - [WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn], + [ + BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT), + bptAmountIn, + ], ); } else { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], [ - WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, + BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT), bptAmountIn, - exitTokenIndex, + BigInt(exitTokenIndex), ], ); } @@ -115,9 +120,9 @@ export class WeightedEncoder { maxBPTAmountIn: bigint, ): Address => encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], [ - WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, + BigInt(WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT), amountsOut, maxBPTAmountIn, ], From 24adcc7a7777f3d77dd9095bd6da5c937c468b77 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 5 Sep 2023 21:17:00 -0300 Subject: [PATCH 019/199] Rename join inputs to make interface more intuitive (wip) --- src/entities/pools/index.ts | 38 +++++- src/entities/pools/weighted/weightedJoin.ts | 127 ++++++++++---------- 2 files changed, 99 insertions(+), 66 deletions(-) diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index 1be2941f..ec926d83 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -49,13 +49,43 @@ export type PoolState = { }; // This will be extended for each pools specific input requirements -export type JoinInput = { - tokenAmounts: TokenAmount[]; +export type BaseJoinInput = { chainId: number; - rpcUrl: string; - isInit?: boolean; + rpcUrl?: string; }; +export type InitJoinInput = BaseJoinInput & { + initAmountsIn: TokenAmount[]; + kind: 'init'; +}; + +export type ProportionalJoinInput = BaseJoinInput & { + refAmountIn: TokenAmount; + kind: 'proportional'; +}; + +export type UnbalancedJoinInput = BaseJoinInput & { + amountsIn: TokenAmount[]; + kind: 'unbalanced'; +}; + +export type SingleAssetJoinInput = BaseJoinInput & { + amountIn: TokenAmount; + kind: 'singleAsset'; +}; + +export type ExactOutJoinInput = BaseJoinInput & { + bptOut: TokenAmount; + kind: 'exactOut'; +}; + +export type JoinInput = + | InitJoinInput + | ProportionalJoinInput + | UnbalancedJoinInput + | SingleAssetJoinInput + | ExactOutJoinInput; + // Returned from a join query export type JoinQueryResult = { id: Address; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index e068e8f4..6f498fd3 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -33,29 +33,27 @@ export class WeightedJoin implements BaseJoin { this.checkInputs(input, poolState); - // infer join kind by input tokens - const joinKind = this.getJoinKind(input, poolState.address); - // Initialize join parameters let maxAmountsIn = Array(poolState.tokens.length).fill(0n); let userData: Address; const poolAssets = poolState.tokens.map((t) => t.address); - switch (joinKind) { - case 'Init': { + switch (input.kind) { + case 'init': { maxAmountsIn = this.getAmountsIn(input, poolAssets); userData = WeightedEncoder.joinInit(maxAmountsIn); break; } - case 'GivenIn': { + case 'proportional': + case 'unbalanced': + case 'singleAsset': { maxAmountsIn = this.getAmountsIn(input, poolAssets); const bptOut = 0n; userData = WeightedEncoder.joinGivenIn(maxAmountsIn, bptOut); break; } - case 'GivenOut': { - const bptOut = input.tokenAmounts[0].amount; - userData = WeightedEncoder.joinGivenOut(bptOut); + case 'exactOut': { + userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); break; } default: @@ -96,7 +94,7 @@ export class WeightedJoin implements BaseJoin { ); return { - joinKind, + joinKind: input.kind, id: poolState.id, assets: poolAssets, bptOut, @@ -110,32 +108,32 @@ export class WeightedJoin implements BaseJoin { value: bigint; minBptOut: bigint; } { - let maxAmountsIn: bigint[]; - let userData: Address; - let minBptOut = input.bptOut.amount; - - switch (input.joinKind) { - case 'Init': { - maxAmountsIn = input.amountsIn.map((a) => a.amount); - userData = WeightedEncoder.joinInit(maxAmountsIn); - break; - } - case 'GivenIn': { - maxAmountsIn = input.amountsIn.map((a) => a.amount); - minBptOut = input.slippage.removeFrom(input.bptOut.amount); - userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); - break; - } - case 'GivenOut': { - maxAmountsIn = input.amountsIn.map((a) => - input.slippage.applyTo(a.amount), - ); - userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); - break; - } - default: - throw new Error('Invalid join kind'); - } + const maxAmountsIn: bigint[] = []; + const userData: Address = ZERO_ADDRESS; + const minBptOut = input.bptOut.amount; + + // switch (input.joinKind) { + // case 'Init': { + // maxAmountsIn = input.amountsIn.map((a) => a.amount); + // userData = WeightedEncoder.joinInit(maxAmountsIn); + // break; + // } + // case 'GivenIn': { + // maxAmountsIn = input.amountsIn.map((a) => a.amount); + // minBptOut = input.slippage.removeFrom(input.bptOut.amount); + // userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); + // break; + // } + // case 'GivenOut': { + // maxAmountsIn = input.amountsIn.map((a) => + // input.slippage.applyTo(a.amount), + // ); + // userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); + // break; + // } + // default: + // throw new Error('Invalid join kind'); + // } const queryArgs = this.getJoinParameters({ poolId: input.id, @@ -187,40 +185,45 @@ export class WeightedJoin implements BaseJoin { } private checkInputs(input: JoinInput, poolState: PoolState) { - const tokensIn = input.tokenAmounts.map((t) => t.token.address); const poolAssets = poolState.tokens.map((t) => t.address); - if (input.tokenAmounts.length === 0) { - throw new Error('Must specify at least one input'); - } else if (tokensIn.some((t) => !poolAssets.includes(t))) { - throw new Error('Input token not in pool'); - } else if (tokensIn.includes(poolState.address)) { - if (tokensIn.length > 1) { - throw new Error('Cannot join with BPT and other tokens'); - } else if (input.isInit) { - throw new Error('Cannot init with BPT'); + switch (input.kind) { + case 'proportional': { + if (!poolAssets.includes(input.refAmountIn.token.address)) { + throw new Error('Reference token not in pool'); + } } - } - } - - private getJoinKind(input: JoinInput, poolAddress: Address): string { - const tokensIn = input.tokenAmounts.map((t) => t.token.address); - if (tokensIn.includes(poolAddress)) { - return 'GivenOut'; - } else { - return input.isInit ? 'Init' : 'GivenIn'; + break; + // TODO: think about a way to consolidate checks so this doesn't become uneccessarily hard to maintain + default: + break; } } private getAmountsIn(input: JoinInput, poolAssets: string[]): bigint[] { return poolAssets.map((asset) => { - let amountIn = 0n; - const tokenIn = input.tokenAmounts.find( - (t) => t.token.address === asset, - ); - if (tokenIn) { - amountIn = tokenIn.amount; + let tokenIn: TokenAmount | undefined; + switch (input.kind) { + case 'init': + tokenIn = input.initAmountsIn.find( + (t) => t.token.address === asset, + ); + break; + case 'proportional': + if (input.refAmountIn.token.address === asset) + tokenIn = input.refAmountIn; + // TODO: calculate proportional amounts based on reference token + break; + case 'unbalanced': + tokenIn = input.amountsIn.find( + (t) => t.token.address === asset, + ); + break; + case 'singleAsset': + if (input.amountIn.token.address === asset) + tokenIn = input.amountIn; + break; } - return amountIn; + return tokenIn?.amount ?? 0n; }); } } From c401a48ac4bf723bd26dcaaacdbe619b7478b431 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 6 Sep 2023 10:26:02 -0300 Subject: [PATCH 020/199] Rename join inputs to make interface more intuitive --- src/entities/join.ts | 8 +++ src/entities/pools/index.ts | 15 ++--- src/entities/pools/weighted/weightedJoin.ts | 75 +++++++++++---------- test/weightedJoin.integration.test.ts | 13 ++-- 4 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/entities/join.ts b/src/entities/join.ts index b665de70..7a4e3257 100644 --- a/src/entities/join.ts +++ b/src/entities/join.ts @@ -6,6 +6,14 @@ export type JoinConfig = { customPoolJoins: Record; }; +export enum JoinKind { + Init = 'Init', + Proportional = 'Proportional', + Unbalanced = 'Unbalanced', + SingleAsset = 'SingleAsset', + ExactOut = 'ExactOut', +} + export class JoinParser { private readonly poolJoins: Record = {}; diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index ec926d83..b3004aa2 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -1,6 +1,6 @@ import { Hex } from 'viem'; import { Address, PoolType, SwapKind } from '../../types'; -import { Token, TokenAmount } from '../'; +import { JoinKind, Token, TokenAmount } from '../'; import { RawPool } from '../../data/types'; import { Slippage } from '../slippage'; @@ -45,7 +45,6 @@ export type PoolState = { address: Address; decimals: number; }[]; // already properly sorted in case different versions sort them differently - // TODO - Possibly add encoding info here? }; // This will be extended for each pools specific input requirements @@ -56,27 +55,27 @@ export type BaseJoinInput = { export type InitJoinInput = BaseJoinInput & { initAmountsIn: TokenAmount[]; - kind: 'init'; + kind: JoinKind.Init; }; export type ProportionalJoinInput = BaseJoinInput & { refAmountIn: TokenAmount; - kind: 'proportional'; + kind: JoinKind.Proportional; }; export type UnbalancedJoinInput = BaseJoinInput & { amountsIn: TokenAmount[]; - kind: 'unbalanced'; + kind: JoinKind.Unbalanced; }; export type SingleAssetJoinInput = BaseJoinInput & { amountIn: TokenAmount; - kind: 'singleAsset'; + kind: JoinKind.SingleAsset; }; export type ExactOutJoinInput = BaseJoinInput & { bptOut: TokenAmount; - kind: 'exactOut'; + kind: JoinKind.ExactOut; }; export type JoinInput = @@ -90,7 +89,7 @@ export type JoinInput = export type JoinQueryResult = { id: Address; assets: Address[]; - joinKind: string; + joinKind: JoinKind; bptOut: TokenAmount; amountsIn: TokenAmount[]; }; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index 6f498fd3..fb618eac 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -18,6 +18,7 @@ import { TokenAmount } from '../../tokenAmount'; import { balancerHelpersAbi } from '../../../abi/balancerHelpers'; import { Token } from '../../token'; import { vaultAbi } from '../../../abi'; +import { JoinKind } from '../../join'; export class WeightedJoin implements BaseJoin { // TODO - Probably not needed @@ -39,20 +40,20 @@ export class WeightedJoin implements BaseJoin { const poolAssets = poolState.tokens.map((t) => t.address); switch (input.kind) { - case 'init': { + case JoinKind.Init: { maxAmountsIn = this.getAmountsIn(input, poolAssets); userData = WeightedEncoder.joinInit(maxAmountsIn); break; } - case 'proportional': - case 'unbalanced': - case 'singleAsset': { + case JoinKind.Proportional: + case JoinKind.Unbalanced: + case JoinKind.SingleAsset: { maxAmountsIn = this.getAmountsIn(input, poolAssets); const bptOut = 0n; userData = WeightedEncoder.joinGivenIn(maxAmountsIn, bptOut); break; } - case 'exactOut': { + case JoinKind.ExactOut: { userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); break; } @@ -108,32 +109,34 @@ export class WeightedJoin implements BaseJoin { value: bigint; minBptOut: bigint; } { - const maxAmountsIn: bigint[] = []; - const userData: Address = ZERO_ADDRESS; - const minBptOut = input.bptOut.amount; - - // switch (input.joinKind) { - // case 'Init': { - // maxAmountsIn = input.amountsIn.map((a) => a.amount); - // userData = WeightedEncoder.joinInit(maxAmountsIn); - // break; - // } - // case 'GivenIn': { - // maxAmountsIn = input.amountsIn.map((a) => a.amount); - // minBptOut = input.slippage.removeFrom(input.bptOut.amount); - // userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); - // break; - // } - // case 'GivenOut': { - // maxAmountsIn = input.amountsIn.map((a) => - // input.slippage.applyTo(a.amount), - // ); - // userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); - // break; - // } - // default: - // throw new Error('Invalid join kind'); - // } + let maxAmountsIn: bigint[]; + let userData: Address; + let minBptOut = input.bptOut.amount; + + switch (input.joinKind) { + case JoinKind.Init: { + maxAmountsIn = input.amountsIn.map((a) => a.amount); + userData = WeightedEncoder.joinInit(maxAmountsIn); + break; + } + case JoinKind.Proportional: + case JoinKind.Unbalanced: + case JoinKind.SingleAsset: { + maxAmountsIn = input.amountsIn.map((a) => a.amount); + minBptOut = input.slippage.removeFrom(input.bptOut.amount); + userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); + break; + } + case JoinKind.ExactOut: { + maxAmountsIn = input.amountsIn.map((a) => + input.slippage.applyTo(a.amount), + ); + userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); + break; + } + default: + throw new Error('Invalid join kind'); + } const queryArgs = this.getJoinParameters({ poolId: input.id, @@ -187,7 +190,7 @@ export class WeightedJoin implements BaseJoin { private checkInputs(input: JoinInput, poolState: PoolState) { const poolAssets = poolState.tokens.map((t) => t.address); switch (input.kind) { - case 'proportional': { + case JoinKind.Proportional: { if (!poolAssets.includes(input.refAmountIn.token.address)) { throw new Error('Reference token not in pool'); } @@ -203,22 +206,22 @@ export class WeightedJoin implements BaseJoin { return poolAssets.map((asset) => { let tokenIn: TokenAmount | undefined; switch (input.kind) { - case 'init': + case JoinKind.Init: tokenIn = input.initAmountsIn.find( (t) => t.token.address === asset, ); break; - case 'proportional': + case JoinKind.Proportional: if (input.refAmountIn.token.address === asset) tokenIn = input.refAmountIn; // TODO: calculate proportional amounts based on reference token break; - case 'unbalanced': + case JoinKind.Unbalanced: tokenIn = input.amountsIn.find( (t) => t.token.address === asset, ); break; - case 'singleAsset': + case JoinKind.SingleAsset: if (input.amountIn.token.address === asset) tokenIn = input.amountIn; break; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index b73a39a6..2616e66c 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -5,9 +5,10 @@ dotenv.config(); import { BaseJoin, - JoinInput, + JoinKind, JoinParser, PoolState, + SingleAssetJoinInput, Token, TokenAmount, } from '../src/entities'; @@ -60,8 +61,7 @@ describe('weighted join test', () => { // reset local fork await client.reset({ blockNumber, - jsonRpcUrl: - process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com', + jsonRpcUrl: process.env.ETHEREUM_RPC_URL, }); // prepare test client with balance and token approvals @@ -76,7 +76,7 @@ describe('weighted join test', () => { weightedJoin = joinParser.getJoin(poolFromApi.type); }); - describe('single token join', async () => { + describe('single asset join', async () => { // set initial test conditions poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH @@ -90,10 +90,11 @@ describe('weighted join test', () => { test('should join', async () => { // perform join query to get expected bpt out - const joinInput: JoinInput = { - tokenAmounts: [amountIn], + const joinInput: SingleAssetJoinInput = { + amountIn, chainId, rpcUrl, + kind: JoinKind.SingleAsset, }; const queryResult = await weightedJoin.query( joinInput, From 18af0b1bff4d31e94e3fbc52b1ab74baf73557c6 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 6 Sep 2023 11:04:58 -0300 Subject: [PATCH 021/199] Rearrange join related types --- src/abi/index.ts | 1 + src/entities/encoders/index.ts | 2 + src/entities/index.ts | 3 +- src/entities/join/index.ts | 2 + src/entities/{join.ts => join/parser.ts} | 12 +-- src/entities/join/types.ts | 87 +++++++++++++++++++++ src/entities/pools/index.ts | 81 +------------------ src/entities/pools/weighted/weightedJoin.ts | 13 ++- test/weightedJoin.integration.test.ts | 26 +++--- 9 files changed, 118 insertions(+), 109 deletions(-) create mode 100644 src/entities/join/index.ts rename src/entities/{join.ts => join/parser.ts} (71%) create mode 100644 src/entities/join/types.ts diff --git a/src/abi/index.ts b/src/abi/index.ts index db6ac5cb..e74c5a07 100644 --- a/src/abi/index.ts +++ b/src/abi/index.ts @@ -1,4 +1,5 @@ export * from './vault'; +export * from './balancerHelpers'; export * from './balancerQueries'; export * from './balancerPoolDataQueries'; export * from './erc20'; diff --git a/src/entities/encoders/index.ts b/src/entities/encoders/index.ts index b1b600de..2746a28e 100644 --- a/src/entities/encoders/index.ts +++ b/src/entities/encoders/index.ts @@ -1,6 +1,8 @@ import { SupportedRawPoolTypes } from '../../data/types'; import { WeightedEncoder } from './weighted'; +export * from './weighted'; + export const getEncoder = ( poolType: SupportedRawPoolTypes | string, ): typeof WeightedEncoder | undefined => { diff --git a/src/entities/index.ts b/src/entities/index.ts index a42aaaa9..3b61ca42 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,7 +1,8 @@ +export * from './encoders'; +export * from './join/'; export * from './path'; export * from './swap'; export * from './slippage'; export * from './token'; export * from './tokenAmount'; export * from './pools/'; -export * from './join'; diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts new file mode 100644 index 00000000..cddc130b --- /dev/null +++ b/src/entities/join/index.ts @@ -0,0 +1,2 @@ +export * from './parser'; +export * from './types'; diff --git a/src/entities/join.ts b/src/entities/join/parser.ts similarity index 71% rename from src/entities/join.ts rename to src/entities/join/parser.ts index 7a4e3257..8f15df30 100644 --- a/src/entities/join.ts +++ b/src/entities/join/parser.ts @@ -1,19 +1,11 @@ -import { BaseJoin } from './pools'; -import { WeightedJoin } from './pools/weighted'; +import { WeightedJoin } from '../pools/weighted'; +import { BaseJoin } from './types'; /*********************** Basic Helper to get join class from pool type *************/ export type JoinConfig = { customPoolJoins: Record; }; -export enum JoinKind { - Init = 'Init', - Proportional = 'Proportional', - Unbalanced = 'Unbalanced', - SingleAsset = 'SingleAsset', - ExactOut = 'ExactOut', -} - export class JoinParser { private readonly poolJoins: Record = {}; diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts new file mode 100644 index 00000000..c5ebae93 --- /dev/null +++ b/src/entities/join/types.ts @@ -0,0 +1,87 @@ +import { Address } from 'viem'; +import { TokenAmount } from '../tokenAmount'; +import { Slippage } from '../slippage'; + +export enum JoinKind { + Init = 'Init', + Proportional = 'Proportional', + Unbalanced = 'Unbalanced', + SingleAsset = 'SingleAsset', + ExactOut = 'ExactOut', +} + +// Returned from API and used as input +export type PoolState = { + id: Address; + address: Address; + type: string; + // TODO: is it ok to replace by Token here? Or should we stick to basic types? + tokens: { + address: Address; + decimals: number; + }[]; // already properly sorted in case different versions sort them differently +}; + +// This will be extended for each pools specific input requirements +export type BaseJoinInput = { + chainId: number; + rpcUrl?: string; +}; + +export type InitJoinInput = BaseJoinInput & { + initAmountsIn: TokenAmount[]; + kind: JoinKind.Init; +}; + +export type ProportionalJoinInput = BaseJoinInput & { + refAmountIn: TokenAmount; + kind: JoinKind.Proportional; +}; + +export type UnbalancedJoinInput = BaseJoinInput & { + amountsIn: TokenAmount[]; + kind: JoinKind.Unbalanced; +}; + +export type SingleAssetJoinInput = BaseJoinInput & { + amountIn: TokenAmount; + kind: JoinKind.SingleAsset; +}; + +export type ExactOutJoinInput = BaseJoinInput & { + bptOut: TokenAmount; + kind: JoinKind.ExactOut; +}; + +export type JoinInput = + | InitJoinInput + | ProportionalJoinInput + | UnbalancedJoinInput + | SingleAssetJoinInput + | ExactOutJoinInput; + +// Returned from a join query +export type JoinQueryResult = { + id: Address; + assets: Address[]; + joinKind: JoinKind; + bptOut: TokenAmount; + amountsIn: TokenAmount[]; +}; + +export type JoinCallInput = JoinQueryResult & { + slippage: Slippage; + sender: Address; + recipient: Address; +}; + +export interface BaseJoin { + getInstance(): BaseJoin; + query(input: JoinInput, poolState: PoolState): Promise; + buildCall(input: JoinCallInput): { + call: Address; + to: Address; + value: bigint; + minBptOut: bigint; + }; +} diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index b3004aa2..fc7d6e9a 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -1,8 +1,7 @@ import { Hex } from 'viem'; -import { Address, PoolType, SwapKind } from '../../types'; -import { JoinKind, Token, TokenAmount } from '../'; +import { PoolType, SwapKind } from '../../types'; +import { Token, TokenAmount } from '../'; import { RawPool } from '../../data/types'; -import { Slippage } from '../slippage'; export interface BasePool { readonly poolType: PoolType | string; @@ -34,79 +33,3 @@ export interface BasePoolFactory { isPoolForFactory(pool: RawPool): boolean; create(chainId: number, pool: RawPool): BasePool; } - -// Returned from API and used as input -export type PoolState = { - id: Address; - address: Address; - type: string; - // TODO: is it ok to replace by Token here? Or should we stick to basic types? - tokens: { - address: Address; - decimals: number; - }[]; // already properly sorted in case different versions sort them differently -}; - -// This will be extended for each pools specific input requirements -export type BaseJoinInput = { - chainId: number; - rpcUrl?: string; -}; - -export type InitJoinInput = BaseJoinInput & { - initAmountsIn: TokenAmount[]; - kind: JoinKind.Init; -}; - -export type ProportionalJoinInput = BaseJoinInput & { - refAmountIn: TokenAmount; - kind: JoinKind.Proportional; -}; - -export type UnbalancedJoinInput = BaseJoinInput & { - amountsIn: TokenAmount[]; - kind: JoinKind.Unbalanced; -}; - -export type SingleAssetJoinInput = BaseJoinInput & { - amountIn: TokenAmount; - kind: JoinKind.SingleAsset; -}; - -export type ExactOutJoinInput = BaseJoinInput & { - bptOut: TokenAmount; - kind: JoinKind.ExactOut; -}; - -export type JoinInput = - | InitJoinInput - | ProportionalJoinInput - | UnbalancedJoinInput - | SingleAssetJoinInput - | ExactOutJoinInput; - -// Returned from a join query -export type JoinQueryResult = { - id: Address; - assets: Address[]; - joinKind: JoinKind; - bptOut: TokenAmount; - amountsIn: TokenAmount[]; -}; - -export type JoinCallInput = JoinQueryResult & { - slippage: Slippage; - sender: Address; - recipient: Address; -}; - -export interface BaseJoin { - getInstance(): BaseJoin; - query(input: JoinInput, poolState: PoolState): Promise; - buildCall(input: JoinCallInput): { - call: Address; - to: Address; - value: bigint; - minBptOut: bigint; - }; -} diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts index fb618eac..1f59efef 100644 --- a/src/entities/pools/weighted/weightedJoin.ts +++ b/src/entities/pools/weighted/weightedJoin.ts @@ -3,9 +3,13 @@ import { BaseJoin, JoinCallInput, JoinInput, + JoinKind, JoinQueryResult, PoolState, -} from '..'; + Token, + TokenAmount, + WeightedEncoder, +} from '../../..'; import { Address } from '../../../types'; import { BALANCER_HELPERS, @@ -13,12 +17,7 @@ import { CHAINS, ZERO_ADDRESS, } from '../../../utils'; -import { WeightedEncoder } from '../../encoders/weighted'; -import { TokenAmount } from '../../tokenAmount'; -import { balancerHelpersAbi } from '../../../abi/balancerHelpers'; -import { Token } from '../../token'; -import { vaultAbi } from '../../../abi'; -import { JoinKind } from '../../join'; +import { balancerHelpersAbi, vaultAbi } from '../../../abi'; export class WeightedJoin implements BaseJoin { // TODO - Probably not needed diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 2616e66c..07bbd2a7 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -3,17 +3,6 @@ import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); -import { - BaseJoin, - JoinKind, - JoinParser, - PoolState, - SingleAssetJoinInput, - Token, - TokenAmount, -} from '../src/entities'; -import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; -import { Address } from '../src/types'; import { Client, createTestClient, @@ -24,8 +13,21 @@ import { WalletActions, walletActions, } from 'viem'; + +import { + BaseJoin, + JoinKind, + JoinParser, + PoolState, + SingleAssetJoinInput, + Slippage, + Token, + TokenAmount, +} from '../src/entities'; +import { Address } from '../src/types'; +import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; + import { approveToken, sendTransactionGetBalances } from './lib/utils/helper'; -import { Slippage } from '../src/entities/slippage'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig From 0995200ed3f0097a9a9357f707a63c46d3d353c9 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 6 Sep 2023 11:05:40 -0300 Subject: [PATCH 022/199] Run local fork in background before github checks --- .github/workflows/checks.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 83ce0682..f4ddbd19 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -42,6 +42,8 @@ jobs: - uses: actions/checkout@v3 - name: Setup uses: ./.github/actions/setup + - name: Run local fork in background for integration tests + run: npx hardhat --tsconfig tsconfig.testing.json node --hostname 127.0.0.1 --fork ${{ secrets.ETHEREUM_RPC_URL }} & - name: Test run: pnpm test:ci env: From ecbdf1f3299eb2d51ef0b2fc4cdb9772a6994877 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 6 Sep 2023 17:16:48 -0300 Subject: [PATCH 023/199] Add join with native assets --- src/entities/join/index.ts | 88 +++++++- src/entities/join/parser.ts | 4 +- src/entities/join/types.ts | 87 -------- src/entities/join/weighted/helpers.ts | 135 ++++++++++++ src/entities/join/weighted/weightedJoin.ts | 149 +++++++++++++ src/entities/pools/weighted/index.ts | 1 - src/entities/pools/weighted/weightedJoin.ts | 231 -------------------- src/entities/token.ts | 8 +- test/weightedJoin.integration.test.ts | 96 ++++++-- 9 files changed, 460 insertions(+), 339 deletions(-) delete mode 100644 src/entities/join/types.ts create mode 100644 src/entities/join/weighted/helpers.ts create mode 100644 src/entities/join/weighted/weightedJoin.ts delete mode 100644 src/entities/pools/weighted/weightedJoin.ts diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index cddc130b..3a9b71f1 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,2 +1,86 @@ -export * from './parser'; -export * from './types'; +import { Address } from 'viem'; +import { TokenAmount } from '../tokenAmount'; +import { Slippage } from '../slippage'; + +export enum JoinKind { + Init = 'Init', + Proportional = 'Proportional', + Unbalanced = 'Unbalanced', + SingleAsset = 'SingleAsset', + ExactOut = 'ExactOut', +} + +// Returned from API and used as input +export type PoolState = { + id: Address; + address: Address; + type: string; + // TODO: is it ok to replace by Token here? Or should we stick to basic types? + tokens: { + address: Address; + decimals: number; + }[]; // already properly sorted in case different versions sort them differently +}; + +// This will be extended for each pools specific input requirements +export type BaseJoinInput = { + chainId: number; + rpcUrl?: string; +}; + +export type InitJoinInput = BaseJoinInput & { + initAmountsIn: TokenAmount[]; + kind: JoinKind.Init; +}; + +export type ProportionalJoinInput = BaseJoinInput & { + refAmountIn: TokenAmount; + kind: JoinKind.Proportional; +}; + +export type UnbalancedJoinInput = BaseJoinInput & { + amountsIn: TokenAmount[]; + kind: JoinKind.Unbalanced; +}; + +export type SingleAssetJoinInput = BaseJoinInput & { + amountIn: TokenAmount; + kind: JoinKind.SingleAsset; +}; + +export type ExactOutJoinInput = BaseJoinInput & { + bptOut: TokenAmount; + kind: JoinKind.ExactOut; +}; + +export type JoinInput = + | InitJoinInput + | ProportionalJoinInput + | UnbalancedJoinInput + | SingleAssetJoinInput + | ExactOutJoinInput; + +// Returned from a join query +export type JoinQueryResult = { + id: Address; + joinKind: JoinKind; + bptOut: TokenAmount; + amountsIn: TokenAmount[]; +}; + +export type JoinCallInput = JoinQueryResult & { + slippage: Slippage; + sender: Address; + recipient: Address; +}; + +export interface BaseJoin { + getInstance(): BaseJoin; + query(input: JoinInput, poolState: PoolState): Promise; + buildCall(input: JoinCallInput): { + call: Address; + to: Address; + value: bigint | undefined; + minBptOut: bigint; + }; +} diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts index 8f15df30..38579527 100644 --- a/src/entities/join/parser.ts +++ b/src/entities/join/parser.ts @@ -1,5 +1,5 @@ -import { WeightedJoin } from '../pools/weighted'; -import { BaseJoin } from './types'; +import { BaseJoin } from '.'; +import { WeightedJoin } from './weighted/weightedJoin'; /*********************** Basic Helper to get join class from pool type *************/ export type JoinConfig = { diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts deleted file mode 100644 index c5ebae93..00000000 --- a/src/entities/join/types.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Address } from 'viem'; -import { TokenAmount } from '../tokenAmount'; -import { Slippage } from '../slippage'; - -export enum JoinKind { - Init = 'Init', - Proportional = 'Proportional', - Unbalanced = 'Unbalanced', - SingleAsset = 'SingleAsset', - ExactOut = 'ExactOut', -} - -// Returned from API and used as input -export type PoolState = { - id: Address; - address: Address; - type: string; - // TODO: is it ok to replace by Token here? Or should we stick to basic types? - tokens: { - address: Address; - decimals: number; - }[]; // already properly sorted in case different versions sort them differently -}; - -// This will be extended for each pools specific input requirements -export type BaseJoinInput = { - chainId: number; - rpcUrl?: string; -}; - -export type InitJoinInput = BaseJoinInput & { - initAmountsIn: TokenAmount[]; - kind: JoinKind.Init; -}; - -export type ProportionalJoinInput = BaseJoinInput & { - refAmountIn: TokenAmount; - kind: JoinKind.Proportional; -}; - -export type UnbalancedJoinInput = BaseJoinInput & { - amountsIn: TokenAmount[]; - kind: JoinKind.Unbalanced; -}; - -export type SingleAssetJoinInput = BaseJoinInput & { - amountIn: TokenAmount; - kind: JoinKind.SingleAsset; -}; - -export type ExactOutJoinInput = BaseJoinInput & { - bptOut: TokenAmount; - kind: JoinKind.ExactOut; -}; - -export type JoinInput = - | InitJoinInput - | ProportionalJoinInput - | UnbalancedJoinInput - | SingleAssetJoinInput - | ExactOutJoinInput; - -// Returned from a join query -export type JoinQueryResult = { - id: Address; - assets: Address[]; - joinKind: JoinKind; - bptOut: TokenAmount; - amountsIn: TokenAmount[]; -}; - -export type JoinCallInput = JoinQueryResult & { - slippage: Slippage; - sender: Address; - recipient: Address; -}; - -export interface BaseJoin { - getInstance(): BaseJoin; - query(input: JoinInput, poolState: PoolState): Promise; - buildCall(input: JoinCallInput): { - call: Address; - to: Address; - value: bigint; - minBptOut: bigint; - }; -} diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts new file mode 100644 index 00000000..a155a124 --- /dev/null +++ b/src/entities/join/weighted/helpers.ts @@ -0,0 +1,135 @@ +import { Address } from '../../../types'; +import { ZERO_ADDRESS } from '../../../utils'; +import { WeightedEncoder } from '../../encoders'; +import { TokenAmount } from '../../tokenAmount'; +import { JoinInput, JoinKind, PoolState } from '..'; + +export function getJoinParameters({ + poolId, + assets, + sender, + recipient, + maxAmountsIn, + userData, +}: { + poolId: Address; + assets: readonly Address[]; + sender: Address; + recipient: Address; + maxAmountsIn: readonly bigint[]; + userData: Address; +}) { + const joinPoolRequest = { + assets, // with BPT + maxAmountsIn, // with BPT + userData, // wihtout BPT + fromInternalBalance: false, + }; + + return [poolId, sender, recipient, joinPoolRequest] as const; +} + +export function checkInputs(input: JoinInput, poolState: PoolState) { + const poolAssets = poolState.tokens.map((t) => t.address); + switch (input.kind) { + case JoinKind.Proportional: { + if (!poolAssets.includes(input.refAmountIn.token.address)) { + throw new Error('Reference token not in pool'); + } + } + break; + // TODO: think about a way to consolidate checks so this doesn't become uneccessarily hard to maintain + default: + break; + } +} + +// TODO: amounts for native asset join relies on the fact that the user provided wrapped address for input tokens - is that a fair assumption? +export function getAmountsIn(input: JoinInput, poolAssets: string[]): bigint[] { + return poolAssets.map((asset) => { + let tokenIn: TokenAmount | undefined; + switch (input.kind) { + case JoinKind.Init: + tokenIn = input.initAmountsIn.find( + (t) => t.token.wrapped === asset, + ); + break; + case JoinKind.Proportional: + if (input.refAmountIn.token.wrapped === asset) + tokenIn = input.refAmountIn; + // TODO: calculate proportional amounts based on reference token + break; + case JoinKind.Unbalanced: + tokenIn = input.amountsIn.find( + (t) => t.token.wrapped === asset, + ); + break; + case JoinKind.SingleAsset: + if (input.amountIn.token.wrapped === asset) + tokenIn = input.amountIn; + break; + } + return tokenIn?.amount ?? 0n; + }); +} + +export function getUserData(input: JoinInput, amountsIn: bigint[]): Address { + let userData: Address; + switch (input.kind) { + case JoinKind.Init: { + userData = WeightedEncoder.joinInit(amountsIn); + break; + } + case JoinKind.Proportional: + case JoinKind.Unbalanced: + case JoinKind.SingleAsset: { + userData = WeightedEncoder.joinGivenIn(amountsIn, 0n); + break; + } + case JoinKind.ExactOut: { + userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); + break; + } + default: + throw new Error('Invalid join kind'); + } + return userData; +} + +export function getAssets(input: JoinInput, poolAssets: Address[]): Address[] { + let nativeTokenIn: TokenAmount | undefined; + switch (input.kind) { + case JoinKind.Init: + nativeTokenIn = input.initAmountsIn.find( + (t) => t.token.address === ZERO_ADDRESS, + ); + break; + case JoinKind.Proportional: + if (input.refAmountIn.token.address === ZERO_ADDRESS) + nativeTokenIn = input.refAmountIn; + break; + case JoinKind.Unbalanced: + nativeTokenIn = input.amountsIn.find( + (t) => t.token.address === ZERO_ADDRESS, + ); + break; + case JoinKind.SingleAsset: + if (input.amountIn.token.address === ZERO_ADDRESS) + nativeTokenIn = input.amountIn; + break; + } + let assets = [...poolAssets]; + + if (nativeTokenIn !== undefined) { + const wrappedAssetIndex = poolAssets.findIndex( + // TODO: this assumes that the user provided wrapped address for input tokens - is that a fair assumption? + (a) => a === (nativeTokenIn as TokenAmount).token.wrapped, + ); + assets = [ + ...poolAssets.slice(0, wrappedAssetIndex), + ZERO_ADDRESS, + ...poolAssets.slice(wrappedAssetIndex + 1, poolAssets.length), + ]; + } + return assets; +} diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts new file mode 100644 index 00000000..4982a28a --- /dev/null +++ b/src/entities/join/weighted/weightedJoin.ts @@ -0,0 +1,149 @@ +import { createPublicClient, encodeFunctionData, http } from 'viem'; +import { Token, TokenAmount, WeightedEncoder } from '../../..'; +import { Address } from '../../../types'; +import { + BALANCER_HELPERS, + BALANCER_VAULT, + CHAINS, + ZERO_ADDRESS, +} from '../../../utils'; +import { balancerHelpersAbi, vaultAbi } from '../../../abi'; +import { + checkInputs, + getAmountsIn, + getAssets, + getJoinParameters, + getUserData, +} from './helpers'; +import { + BaseJoin, + JoinCallInput, + JoinInput, + JoinKind, + JoinQueryResult, + PoolState, +} from '..'; + +export class WeightedJoin implements BaseJoin { + // TODO - Probably not needed + getInstance(): WeightedJoin { + return new WeightedJoin(); + } + + public async query( + input: JoinInput, + poolState: PoolState, + ): Promise { + // TODO - This would need extended to work with relayer + + checkInputs(input, poolState); + + const poolAssets = poolState.tokens.map((t) => t.address); + const assets = getAssets(input, poolAssets); + const maxAmountsIn = getAmountsIn(input, poolAssets); + const userData = getUserData(input, maxAmountsIn); + + const queryArgs = getJoinParameters({ + poolId: poolState.id, + assets, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + maxAmountsIn, + userData, + }); + + const client = createPublicClient({ + transport: http(input.rpcUrl), + chain: CHAINS[input.chainId], + }); + + const { + result: [queryBptOut, queryAmountsIn], + } = await client.simulateContract({ + address: BALANCER_HELPERS[input.chainId], + abi: balancerHelpersAbi, + functionName: 'queryJoin', + args: queryArgs, + }); + + const bpt = new Token(input.chainId, poolState.address, 18); + const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); + + const tokensIn = poolState.tokens.map( + (token, i) => new Token(input.chainId, assets[i], token.decimals), + ); + const amountsIn = queryAmountsIn.map((a, i) => + TokenAmount.fromRawAmount(tokensIn[i], a), + ); + + return { + joinKind: input.kind, + id: poolState.id, + bptOut, + amountsIn, + }; + } + + public buildCall(input: JoinCallInput): { + call: Address; + to: Address; + value: bigint | undefined; + minBptOut: bigint; + } { + let maxAmountsIn: bigint[]; + let userData: Address; + let minBptOut = input.bptOut.amount; + + switch (input.joinKind) { + case JoinKind.Init: { + maxAmountsIn = input.amountsIn.map((a) => a.amount); + userData = WeightedEncoder.joinInit(maxAmountsIn); + break; + } + case JoinKind.Proportional: + case JoinKind.Unbalanced: + case JoinKind.SingleAsset: { + maxAmountsIn = input.amountsIn.map((a) => a.amount); + minBptOut = input.slippage.removeFrom(input.bptOut.amount); + userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); + break; + } + case JoinKind.ExactOut: { + maxAmountsIn = input.amountsIn.map((a) => + input.slippage.applyTo(a.amount), + ); + userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); + break; + } + default: + throw new Error('Invalid join kind'); + } + + const queryArgs = getJoinParameters({ + poolId: input.id, + assets: input.amountsIn.map((a) => a.token.address), + sender: input.sender, + recipient: input.recipient, + maxAmountsIn, + userData, + }); + + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'joinPool', + args: queryArgs, + }); + + const value = input.amountsIn.find( + (a) => a.token.address === ZERO_ADDRESS, + )?.amount; + + // Encode data + return { + call, + to: BALANCER_VAULT, + value, + minBptOut, + }; + } +} diff --git a/src/entities/pools/weighted/index.ts b/src/entities/pools/weighted/index.ts index 36268989..f5256553 100644 --- a/src/entities/pools/weighted/index.ts +++ b/src/entities/pools/weighted/index.ts @@ -1,4 +1,3 @@ export * from './weightedFactory'; export * from './weightedPool'; export * from './weightedMath'; -export * from './weightedJoin'; diff --git a/src/entities/pools/weighted/weightedJoin.ts b/src/entities/pools/weighted/weightedJoin.ts deleted file mode 100644 index 1f59efef..00000000 --- a/src/entities/pools/weighted/weightedJoin.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { createPublicClient, encodeFunctionData, http } from 'viem'; -import { - BaseJoin, - JoinCallInput, - JoinInput, - JoinKind, - JoinQueryResult, - PoolState, - Token, - TokenAmount, - WeightedEncoder, -} from '../../..'; -import { Address } from '../../../types'; -import { - BALANCER_HELPERS, - BALANCER_VAULT, - CHAINS, - ZERO_ADDRESS, -} from '../../../utils'; -import { balancerHelpersAbi, vaultAbi } from '../../../abi'; - -export class WeightedJoin implements BaseJoin { - // TODO - Probably not needed - getInstance(): WeightedJoin { - return new WeightedJoin(); - } - - public async query( - input: JoinInput, - poolState: PoolState, - ): Promise { - // TODO - This would need extended to work with relayer - - this.checkInputs(input, poolState); - - // Initialize join parameters - let maxAmountsIn = Array(poolState.tokens.length).fill(0n); - let userData: Address; - const poolAssets = poolState.tokens.map((t) => t.address); - - switch (input.kind) { - case JoinKind.Init: { - maxAmountsIn = this.getAmountsIn(input, poolAssets); - userData = WeightedEncoder.joinInit(maxAmountsIn); - break; - } - case JoinKind.Proportional: - case JoinKind.Unbalanced: - case JoinKind.SingleAsset: { - maxAmountsIn = this.getAmountsIn(input, poolAssets); - const bptOut = 0n; - userData = WeightedEncoder.joinGivenIn(maxAmountsIn, bptOut); - break; - } - case JoinKind.ExactOut: { - userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); - break; - } - default: - throw new Error('Invalid join kind'); - } - - const queryArgs = this.getJoinParameters({ - poolId: poolState.id, - assets: poolAssets, - sender: ZERO_ADDRESS, - recipient: ZERO_ADDRESS, - maxAmountsIn, - userData, - }); - - const client = createPublicClient({ - transport: http(input.rpcUrl), - chain: CHAINS[input.chainId], - }); - - const { - result: [queryBptOut, queryAmountsIn], - } = await client.simulateContract({ - address: BALANCER_HELPERS[input.chainId], - abi: balancerHelpersAbi, - functionName: 'queryJoin', - args: queryArgs, - }); - - const bpt = new Token(input.chainId, poolState.address, 18); - const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); - - const poolTokens = poolState.tokens.map( - (token) => new Token(input.chainId, token.address, token.decimals), - ); - const amountsIn = queryAmountsIn.map((a, i) => - TokenAmount.fromRawAmount(poolTokens[i], a), - ); - - return { - joinKind: input.kind, - id: poolState.id, - assets: poolAssets, - bptOut, - amountsIn, - }; - } - - public buildCall(input: JoinCallInput): { - call: Address; - to: Address; - value: bigint; - minBptOut: bigint; - } { - let maxAmountsIn: bigint[]; - let userData: Address; - let minBptOut = input.bptOut.amount; - - switch (input.joinKind) { - case JoinKind.Init: { - maxAmountsIn = input.amountsIn.map((a) => a.amount); - userData = WeightedEncoder.joinInit(maxAmountsIn); - break; - } - case JoinKind.Proportional: - case JoinKind.Unbalanced: - case JoinKind.SingleAsset: { - maxAmountsIn = input.amountsIn.map((a) => a.amount); - minBptOut = input.slippage.removeFrom(input.bptOut.amount); - userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); - break; - } - case JoinKind.ExactOut: { - maxAmountsIn = input.amountsIn.map((a) => - input.slippage.applyTo(a.amount), - ); - userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); - break; - } - default: - throw new Error('Invalid join kind'); - } - - const queryArgs = this.getJoinParameters({ - poolId: input.id, - assets: input.assets, - sender: input.sender, - recipient: input.recipient, - maxAmountsIn, - userData, - }); - - const call = encodeFunctionData({ - abi: vaultAbi, - functionName: 'joinPool', - args: queryArgs, - }); - - // Encode data - return { - call, - to: BALANCER_VAULT, - value: 0n, // TODO: ETH value when joining with ETH - minBptOut, - }; - } - - private getJoinParameters({ - poolId, - assets, - sender, - recipient, - maxAmountsIn, - userData, - }: { - poolId: Address; - assets: readonly Address[]; - sender: Address; - recipient: Address; - maxAmountsIn: readonly bigint[]; - userData: Address; - }) { - const joinPoolRequest = { - assets, // with BPT - maxAmountsIn, // with BPT - userData, // wihtout BPT - fromInternalBalance: false, - }; - - return [poolId, sender, recipient, joinPoolRequest] as const; - } - - private checkInputs(input: JoinInput, poolState: PoolState) { - const poolAssets = poolState.tokens.map((t) => t.address); - switch (input.kind) { - case JoinKind.Proportional: { - if (!poolAssets.includes(input.refAmountIn.token.address)) { - throw new Error('Reference token not in pool'); - } - } - break; - // TODO: think about a way to consolidate checks so this doesn't become uneccessarily hard to maintain - default: - break; - } - } - - private getAmountsIn(input: JoinInput, poolAssets: string[]): bigint[] { - return poolAssets.map((asset) => { - let tokenIn: TokenAmount | undefined; - switch (input.kind) { - case JoinKind.Init: - tokenIn = input.initAmountsIn.find( - (t) => t.token.address === asset, - ); - break; - case JoinKind.Proportional: - if (input.refAmountIn.token.address === asset) - tokenIn = input.refAmountIn; - // TODO: calculate proportional amounts based on reference token - break; - case JoinKind.Unbalanced: - tokenIn = input.amountsIn.find( - (t) => t.token.address === asset, - ); - break; - case JoinKind.SingleAsset: - if (input.amountIn.token.address === asset) - tokenIn = input.amountIn; - break; - } - return tokenIn?.amount ?? 0n; - }); - } -} diff --git a/src/entities/token.ts b/src/entities/token.ts index 59dbdd9f..ea4f8d70 100644 --- a/src/entities/token.ts +++ b/src/entities/token.ts @@ -5,7 +5,7 @@ export class Token { public readonly decimals: number; public readonly symbol?: string; public readonly name?: string; - public readonly wrapped: string; + public readonly wrapped: Address; public constructor( chainId: number, @@ -13,7 +13,7 @@ export class Token { decimals: number, symbol?: string, name?: string, - wrapped?: string, + wrapped?: Address, ) { this.chainId = chainId; // Addresses are always lowercased for speed @@ -21,7 +21,9 @@ export class Token { this.decimals = decimals; this.symbol = symbol; this.name = name; - this.wrapped = wrapped ? wrapped.toLowerCase() : address.toLowerCase(); + this.wrapped = ( + wrapped ? wrapped.toLowerCase() : address.toLowerCase() + ) as Address; } public isEqual(token: Token) { diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 07bbd2a7..76ea9f67 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -17,15 +17,15 @@ import { import { BaseJoin, JoinKind, - JoinParser, PoolState, SingleAssetJoinInput, Slippage, Token, TokenAmount, } from '../src/entities'; +import { JoinParser } from '../src/entities/join/parser'; import { Address } from '../src/types'; -import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; +import { CHAINS, ChainId, ZERO_ADDRESS, getPoolAddress } from '../src/utils'; import { approveToken, sendTransactionGetBalances } from './lib/utils/helper'; @@ -79,18 +79,85 @@ describe('weighted join test', () => { }); describe('single asset join', async () => { - // set initial test conditions - poolId = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH - tokenIn = new Token( - chainId, - '0xba100000625a3754423978a60c9317c58a424e3D', - 18, - 'BAL', - ); - const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); + beforeAll(() => { + poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + tokenIn = new Token( + chainId, + '0xba100000625a3754423978a60c9317c58a424e3D', + 18, + 'BAL', + ); + }); + + test('should join', async () => { + const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); + + // perform join query to get expected bpt out + const joinInput: SingleAssetJoinInput = { + amountIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + const queryResult = await weightedJoin.query( + joinInput, + poolFromApi, + ); + + // build join call with expected minBpOut based on slippage + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, minBptOut } = weightedJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send join transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsIn.map((a) => a.token.address), + queryResult.bptOut.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinBpt = slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + }); + }); + + describe('native asset join', async () => { + beforeAll(() => { + poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + tokenIn = new Token( + chainId, + ZERO_ADDRESS, + 18, + 'ETH', + 'Ether', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + ); + }); test('should join', async () => { + const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); // perform join query to get expected bpt out const joinInput: SingleAssetJoinInput = { amountIn, @@ -115,7 +182,10 @@ describe('weighted join test', () => { // send join transaction and check balance changes const { transactionReceipt, balanceDeltas } = await sendTransactionGetBalances( - [...queryResult.assets, queryResult.bptOut.token.address], + [ + ...queryResult.amountsIn.map((a) => a.token.address), + queryResult.bptOut.token.address, + ], client, testAddress, to, From 9f9935bb08be92d9625a0b3e9ee4ff733e8e3d4e Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 8 Sep 2023 12:05:52 -0300 Subject: [PATCH 024/199] Refactor join with native asset interface --- src/entities/join/index.ts | 7 ++- src/entities/join/weighted/helpers.ts | 55 ++++------------------ src/entities/join/weighted/weightedJoin.ts | 23 +++++---- test/weightedJoin.integration.test.ts | 32 +++++++------ 4 files changed, 44 insertions(+), 73 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 3a9b71f1..00c97dec 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,6 +1,7 @@ import { Address } from 'viem'; import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; +import { Token } from '../token'; export enum JoinKind { Init = 'Init', @@ -16,16 +17,14 @@ export type PoolState = { address: Address; type: string; // TODO: is it ok to replace by Token here? Or should we stick to basic types? - tokens: { - address: Address; - decimals: number; - }[]; // already properly sorted in case different versions sort them differently + tokens: Token[]; // already properly sorted in case different versions sort them differently }; // This will be extended for each pools specific input requirements export type BaseJoinInput = { chainId: number; rpcUrl?: string; + joinWithNativeAsset?: boolean; }; export type InitJoinInput = BaseJoinInput & { diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts index a155a124..24ae1529 100644 --- a/src/entities/join/weighted/helpers.ts +++ b/src/entities/join/weighted/helpers.ts @@ -3,6 +3,7 @@ import { ZERO_ADDRESS } from '../../../utils'; import { WeightedEncoder } from '../../encoders'; import { TokenAmount } from '../../tokenAmount'; import { JoinInput, JoinKind, PoolState } from '..'; +import { Token } from '../../token'; export function getJoinParameters({ poolId, @@ -45,27 +46,25 @@ export function checkInputs(input: JoinInput, poolState: PoolState) { } // TODO: amounts for native asset join relies on the fact that the user provided wrapped address for input tokens - is that a fair assumption? -export function getAmountsIn(input: JoinInput, poolAssets: string[]): bigint[] { - return poolAssets.map((asset) => { +export function getAmountsIn(input: JoinInput, poolTokens: Token[]): bigint[] { + return poolTokens.map((token) => { let tokenIn: TokenAmount | undefined; switch (input.kind) { case JoinKind.Init: - tokenIn = input.initAmountsIn.find( - (t) => t.token.wrapped === asset, + tokenIn = input.initAmountsIn.find((t) => + t.token.isEqual(token), ); break; case JoinKind.Proportional: - if (input.refAmountIn.token.wrapped === asset) + if (input.refAmountIn.token.isEqual(token)) tokenIn = input.refAmountIn; // TODO: calculate proportional amounts based on reference token break; case JoinKind.Unbalanced: - tokenIn = input.amountsIn.find( - (t) => t.token.wrapped === asset, - ); + tokenIn = input.amountsIn.find((t) => t.token.isEqual(token)); break; case JoinKind.SingleAsset: - if (input.amountIn.token.wrapped === asset) + if (input.amountIn.token.isEqual(token)) tokenIn = input.amountIn; break; } @@ -95,41 +94,3 @@ export function getUserData(input: JoinInput, amountsIn: bigint[]): Address { } return userData; } - -export function getAssets(input: JoinInput, poolAssets: Address[]): Address[] { - let nativeTokenIn: TokenAmount | undefined; - switch (input.kind) { - case JoinKind.Init: - nativeTokenIn = input.initAmountsIn.find( - (t) => t.token.address === ZERO_ADDRESS, - ); - break; - case JoinKind.Proportional: - if (input.refAmountIn.token.address === ZERO_ADDRESS) - nativeTokenIn = input.refAmountIn; - break; - case JoinKind.Unbalanced: - nativeTokenIn = input.amountsIn.find( - (t) => t.token.address === ZERO_ADDRESS, - ); - break; - case JoinKind.SingleAsset: - if (input.amountIn.token.address === ZERO_ADDRESS) - nativeTokenIn = input.amountIn; - break; - } - let assets = [...poolAssets]; - - if (nativeTokenIn !== undefined) { - const wrappedAssetIndex = poolAssets.findIndex( - // TODO: this assumes that the user provided wrapped address for input tokens - is that a fair assumption? - (a) => a === (nativeTokenIn as TokenAmount).token.wrapped, - ); - assets = [ - ...poolAssets.slice(0, wrappedAssetIndex), - ZERO_ADDRESS, - ...poolAssets.slice(wrappedAssetIndex + 1, poolAssets.length), - ]; - } - return assets; -} diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 4982a28a..0dae70dc 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -5,13 +5,13 @@ import { BALANCER_HELPERS, BALANCER_VAULT, CHAINS, + NATIVE_ASSETS, ZERO_ADDRESS, } from '../../../utils'; import { balancerHelpersAbi, vaultAbi } from '../../../abi'; import { checkInputs, getAmountsIn, - getAssets, getJoinParameters, getUserData, } from './helpers'; @@ -38,14 +38,24 @@ export class WeightedJoin implements BaseJoin { checkInputs(input, poolState); - const poolAssets = poolState.tokens.map((t) => t.address); - const assets = getAssets(input, poolAssets); - const maxAmountsIn = getAmountsIn(input, poolAssets); + const maxAmountsIn = getAmountsIn(input, poolState.tokens); const userData = getUserData(input, maxAmountsIn); + // replace wrapped token with native asset if needed + const tokensIn = poolState.tokens.map((token) => { + if ( + input.joinWithNativeAsset && + token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId]) + ) { + return new Token(input.chainId, ZERO_ADDRESS, 18); + } else { + return token; + } + }); + const queryArgs = getJoinParameters({ poolId: poolState.id, - assets, + assets: tokensIn.map((t) => t.address), sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, maxAmountsIn, @@ -69,9 +79,6 @@ export class WeightedJoin implements BaseJoin { const bpt = new Token(input.chainId, poolState.address, 18); const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); - const tokensIn = poolState.tokens.map( - (token, i) => new Token(input.chainId, assets[i], token.decimals), - ); const amountsIn = queryAmountsIn.map((a, i) => TokenAmount.fromRawAmount(tokensIn[i], a), ); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 76ea9f67..1747658c 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -148,11 +148,9 @@ describe('weighted join test', () => { '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH tokenIn = new Token( chainId, - ZERO_ADDRESS, + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 18, - 'ETH', - 'Ether', - '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + 'WETH', ); }); @@ -164,6 +162,7 @@ describe('weighted join test', () => { chainId, rpcUrl, kind: JoinKind.SingleAsset, + joinWithNativeAsset: true, }; const queryResult = await weightedJoin.query( joinInput, @@ -212,20 +211,25 @@ describe('weighted join test', () => { export class MockApi { public async getPool(id: Address): Promise { + const tokens = [ + new Token( + ChainId.MAINNET, + '0xba100000625a3754423978a60c9317c58a424e3d', + 18, + 'BAL', + ), + new Token( + ChainId.MAINNET, + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + 18, + 'WETH', + ), + ]; return { id, address: getPoolAddress(id) as Address, type: 'Weighted', - tokens: [ - { - address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL - decimals: 18, - }, - { - address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH - decimals: 18, - }, - ], + tokens, }; } } From c95d21ea52439367d91ba395cc037fa0535a6ad6 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 8 Sep 2023 16:04:25 -0300 Subject: [PATCH 025/199] Refactor join types to match contract interface --- src/entities/encoders/weighted.ts | 2 + src/entities/join/index.ts | 35 +++--- src/entities/join/weighted/helpers.ts | 37 +----- src/entities/join/weighted/weightedJoin.ts | 49 ++++---- test/weightedJoin.integration.test.ts | 128 ++++++++++----------- 5 files changed, 110 insertions(+), 141 deletions(-) diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index ccaa2227..bc1dec28 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -53,6 +53,7 @@ export class WeightedEncoder { * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ + // TODO: refactor this into 2 separate functions static joinGivenOut = ( bptAmountOut: bigint, enterTokenIndex?: number, @@ -86,6 +87,7 @@ export class WeightedEncoder { * @param bptAmountIn - the amount of BPT to be burned * @param enterTokenIndex - the index of the token to removed from the pool */ + // TODO: refactor this into 2 separate functions static exitGivenIn = ( bptAmountIn: bigint, exitTokenIndex?: number, diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 00c97dec..000f6637 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -5,10 +5,9 @@ import { Token } from '../token'; export enum JoinKind { Init = 'Init', - Proportional = 'Proportional', - Unbalanced = 'Unbalanced', - SingleAsset = 'SingleAsset', - ExactOut = 'ExactOut', + ExactIn = 'ExactIn', + ExactOutSingleAsset = 'ExactOutSingleAsset', + ExactOutProportional = 'ExactOutProportional', } // Returned from API and used as input @@ -32,32 +31,27 @@ export type InitJoinInput = BaseJoinInput & { kind: JoinKind.Init; }; -export type ProportionalJoinInput = BaseJoinInput & { - refAmountIn: TokenAmount; - kind: JoinKind.Proportional; -}; - -export type UnbalancedJoinInput = BaseJoinInput & { +export type ExactInJoinInput = BaseJoinInput & { amountsIn: TokenAmount[]; - kind: JoinKind.Unbalanced; + kind: JoinKind.ExactIn; }; -export type SingleAssetJoinInput = BaseJoinInput & { - amountIn: TokenAmount; - kind: JoinKind.SingleAsset; +export type ExactOutSingleAssetJoinInput = BaseJoinInput & { + bptOut: TokenAmount; + tokenIn: Address; + kind: JoinKind.ExactOutSingleAsset; }; -export type ExactOutJoinInput = BaseJoinInput & { +export type ExactOutProportionalJoinInput = BaseJoinInput & { bptOut: TokenAmount; - kind: JoinKind.ExactOut; + kind: JoinKind.ExactOutProportional; }; export type JoinInput = | InitJoinInput - | ProportionalJoinInput - | UnbalancedJoinInput - | SingleAssetJoinInput - | ExactOutJoinInput; + | ExactInJoinInput + | ExactOutSingleAssetJoinInput + | ExactOutProportionalJoinInput; // Returned from a join query export type JoinQueryResult = { @@ -65,6 +59,7 @@ export type JoinQueryResult = { joinKind: JoinKind; bptOut: TokenAmount; amountsIn: TokenAmount[]; + tokenInIndex?: number; }; export type JoinCallInput = JoinQueryResult & { diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts index 24ae1529..cc455852 100644 --- a/src/entities/join/weighted/helpers.ts +++ b/src/entities/join/weighted/helpers.ts @@ -1,8 +1,7 @@ import { Address } from '../../../types'; -import { ZERO_ADDRESS } from '../../../utils'; import { WeightedEncoder } from '../../encoders'; import { TokenAmount } from '../../tokenAmount'; -import { JoinInput, JoinKind, PoolState } from '..'; +import { JoinInput, JoinKind } from '..'; import { Token } from '../../token'; export function getJoinParameters({ @@ -30,21 +29,6 @@ export function getJoinParameters({ return [poolId, sender, recipient, joinPoolRequest] as const; } -export function checkInputs(input: JoinInput, poolState: PoolState) { - const poolAssets = poolState.tokens.map((t) => t.address); - switch (input.kind) { - case JoinKind.Proportional: { - if (!poolAssets.includes(input.refAmountIn.token.address)) { - throw new Error('Reference token not in pool'); - } - } - break; - // TODO: think about a way to consolidate checks so this doesn't become uneccessarily hard to maintain - default: - break; - } -} - // TODO: amounts for native asset join relies on the fact that the user provided wrapped address for input tokens - is that a fair assumption? export function getAmountsIn(input: JoinInput, poolTokens: Token[]): bigint[] { return poolTokens.map((token) => { @@ -55,18 +39,9 @@ export function getAmountsIn(input: JoinInput, poolTokens: Token[]): bigint[] { t.token.isEqual(token), ); break; - case JoinKind.Proportional: - if (input.refAmountIn.token.isEqual(token)) - tokenIn = input.refAmountIn; - // TODO: calculate proportional amounts based on reference token - break; - case JoinKind.Unbalanced: + case JoinKind.ExactIn: tokenIn = input.amountsIn.find((t) => t.token.isEqual(token)); break; - case JoinKind.SingleAsset: - if (input.amountIn.token.isEqual(token)) - tokenIn = input.amountIn; - break; } return tokenIn?.amount ?? 0n; }); @@ -79,13 +54,13 @@ export function getUserData(input: JoinInput, amountsIn: bigint[]): Address { userData = WeightedEncoder.joinInit(amountsIn); break; } - case JoinKind.Proportional: - case JoinKind.Unbalanced: - case JoinKind.SingleAsset: { + case JoinKind.ExactIn: { userData = WeightedEncoder.joinGivenIn(amountsIn, 0n); break; } - case JoinKind.ExactOut: { + case JoinKind.ExactOutSingleAsset: + // TODO: add tokenInIndex after refactoring into switch/case into separate functions + case JoinKind.ExactOutProportional: { userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); break; } diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 0dae70dc..a403de1a 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -9,12 +9,7 @@ import { ZERO_ADDRESS, } from '../../../utils'; import { balancerHelpersAbi, vaultAbi } from '../../../abi'; -import { - checkInputs, - getAmountsIn, - getJoinParameters, - getUserData, -} from './helpers'; +import { getAmountsIn, getJoinParameters, getUserData } from './helpers'; import { BaseJoin, JoinCallInput, @@ -36,22 +31,22 @@ export class WeightedJoin implements BaseJoin { ): Promise { // TODO - This would need extended to work with relayer - checkInputs(input, poolState); + // TODO: check inputs after refactoring switch/case into separate functions const maxAmountsIn = getAmountsIn(input, poolState.tokens); const userData = getUserData(input, maxAmountsIn); + let tokensIn = poolState.tokens; // replace wrapped token with native asset if needed - const tokensIn = poolState.tokens.map((token) => { - if ( - input.joinWithNativeAsset && - token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId]) - ) { - return new Token(input.chainId, ZERO_ADDRESS, 18); - } else { - return token; - } - }); + if (input.joinWithNativeAsset) { + tokensIn = poolState.tokens.map((token) => { + if (token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId])) { + return new Token(input.chainId, ZERO_ADDRESS, 18); + } else { + return token; + } + }); + } const queryArgs = getJoinParameters({ poolId: poolState.id, @@ -83,11 +78,17 @@ export class WeightedJoin implements BaseJoin { TokenAmount.fromRawAmount(tokensIn[i], a), ); + const tokenInIndex = + input.kind === JoinKind.ExactOutSingleAsset + ? poolState.tokens.findIndex((t) => t.address === input.tokenIn) + : undefined; + return { joinKind: input.kind, id: poolState.id, bptOut, amountsIn, + tokenInIndex, }; } @@ -96,6 +97,7 @@ export class WeightedJoin implements BaseJoin { to: Address; value: bigint | undefined; minBptOut: bigint; + // TODO: add maxAmountsIn after creating test scenario for ExactOut joins } { let maxAmountsIn: bigint[]; let userData: Address; @@ -107,19 +109,22 @@ export class WeightedJoin implements BaseJoin { userData = WeightedEncoder.joinInit(maxAmountsIn); break; } - case JoinKind.Proportional: - case JoinKind.Unbalanced: - case JoinKind.SingleAsset: { + case JoinKind.ExactIn: { maxAmountsIn = input.amountsIn.map((a) => a.amount); minBptOut = input.slippage.removeFrom(input.bptOut.amount); userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); break; } - case JoinKind.ExactOut: { + case JoinKind.ExactOutSingleAsset: + case JoinKind.ExactOutProportional: { + // TODO: refactor this after splitting encoder into 2 methods maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ); - userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); + userData = WeightedEncoder.joinGivenOut( + input.bptOut.amount, + input.tokenInIndex, + ); break; } default: diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 1747658c..b309a7e6 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -16,16 +16,16 @@ import { import { BaseJoin, + ExactInJoinInput, JoinKind, PoolState, - SingleAssetJoinInput, Slippage, Token, TokenAmount, } from '../src/entities'; import { JoinParser } from '../src/entities/join/parser'; import { Address } from '../src/types'; -import { CHAINS, ChainId, ZERO_ADDRESS, getPoolAddress } from '../src/utils'; +import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { approveToken, sendTransactionGetBalances } from './lib/utils/helper'; @@ -68,7 +68,6 @@ describe('weighted join test', () => { // prepare test client with balance and token approvals await client.impersonateAccount({ address: testAddress }); - await approveToken(client, testAddress, tokenIn.address); // get pool state from api poolFromApi = await api.getPool(poolId); @@ -78,27 +77,29 @@ describe('weighted join test', () => { weightedJoin = joinParser.getJoin(poolFromApi.type); }); - describe('single asset join', async () => { + describe('exact in', async () => { beforeAll(() => { poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + }); + + test('single asset join', async () => { tokenIn = new Token( chainId, '0xba100000625a3754423978a60c9317c58a424e3D', 18, 'BAL', ); - }); - - test('should join', async () => { const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); + await approveToken(client, testAddress, tokenIn.address); + // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - amountIn, + const joinInput: ExactInJoinInput = { + amountsIn: [amountIn], chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: JoinKind.ExactIn, }; const queryResult = await weightedJoin.query( joinInput, @@ -142,68 +143,59 @@ describe('weighted join test', () => { }); }); - describe('native asset join', async () => { - beforeAll(() => { - poolId = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH - tokenIn = new Token( - chainId, - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - 18, - 'WETH', - ); - }); - - test('should join', async () => { - const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - amountIn, - chainId, - rpcUrl, - kind: JoinKind.SingleAsset, - joinWithNativeAsset: true, - }; - const queryResult = await weightedJoin.query( - joinInput, - poolFromApi, - ); + test('native asset join', async () => { + tokenIn = new Token( + chainId, + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + 18, + 'WETH', + ); + const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - // build join call with expected minBpOut based on slippage - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, minBptOut } = weightedJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); + await approveToken(client, testAddress, tokenIn.address); - // send join transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsIn.map((a) => a.token.address), - queryResult.bptOut.token.address, - ], - client, - testAddress, - to, - call, - value, - ); + // perform join query to get expected bpt out + const joinInput: ExactInJoinInput = { + amountsIn: [amountIn], + chainId, + rpcUrl, + kind: JoinKind.ExactIn, + joinWithNativeAsset: true, + }; + const queryResult = await weightedJoin.query(joinInput, poolFromApi); + + // build join call with expected minBpOut based on slippage + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, minBptOut } = weightedJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMinBpt = slippage.removeFrom( - queryResult.bptOut.amount, + // send join transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsIn.map((a) => a.token.address), + queryResult.bptOut.token.address, + ], + client, + testAddress, + to, + call, + value, ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - }); + + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinBpt = slippage.removeFrom(queryResult.bptOut.amount); + expect(expectedMinBpt).to.deep.eq(minBptOut); }); }); From 4745d3014ea74679f64da9daf58302d49bb7eecc Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 8 Sep 2023 16:53:43 -0300 Subject: [PATCH 026/199] Refactor encoder functions to make them more explicit --- src/entities/encoders/weighted.ts | 100 +++++++++++---------- src/entities/join/weighted/helpers.ts | 45 ---------- src/entities/join/weighted/weightedJoin.ts | 58 +++++++++--- 3 files changed, 100 insertions(+), 103 deletions(-) diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index bc1dec28..269bd0b2 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -15,6 +15,7 @@ export enum WeightedPoolExitKind { MANAGEMENT_FEE_TOKENS_OUT = 3, } +// TODO: rename functions after deciding on the naming convention export class WeightedEncoder { /** * Cannot be constructed. @@ -38,7 +39,7 @@ export class WeightedEncoder { * @param amountsIn - the amounts each of token to deposit in the pool as liquidity * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens */ - static joinGivenIn = (amountsIn: bigint[], minimumBPT: bigint): Address => + static joinExactIn = (amountsIn: bigint[], minimumBPT: bigint): Address => encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], [ @@ -53,33 +54,33 @@ export class WeightedEncoder { * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ - // TODO: refactor this into 2 separate functions - static joinGivenOut = ( + static joinExactOutSingleAsset = ( bptAmountOut: bigint, - enterTokenIndex?: number, + enterTokenIndex: number, ): Address => { - if (enterTokenIndex === undefined) { - // if enterTokenIndex is not provided, it's assumed to be an allTokensIn - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }], - [ - BigInt( - WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, - ), - bptAmountOut, - ], - ); - } else { - // if enterTokenIndex is provided, it's assumed to be an allTokensIn - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), - bptAmountOut, - BigInt(enterTokenIndex), - ], - ); - } + // if enterTokenIndex is provided, it's assumed to be an allTokensIn + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), + bptAmountOut, + BigInt(enterTokenIndex), + ], + ); + }; + + /** + * Encodes the userData parameter for joining a WeightedPool proportionally to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + */ + static joinExactOutProportional = (bptAmountOut: bigint): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT), + bptAmountOut, + ], + ); }; /** @@ -87,29 +88,32 @@ export class WeightedEncoder { * @param bptAmountIn - the amount of BPT to be burned * @param enterTokenIndex - the index of the token to removed from the pool */ - // TODO: refactor this into 2 separate functions - static exitGivenIn = ( + static exitExacInSingleAsset = ( bptAmountIn: bigint, - exitTokenIndex?: number, + exitTokenIndex: number, ): Address => { - if (exitTokenIndex === undefined) { - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT), - bptAmountIn, - ], - ); - } else { - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT), - bptAmountIn, - BigInt(exitTokenIndex), - ], - ); - } + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT), + bptAmountIn, + BigInt(exitTokenIndex), + ], + ); + }; + + /** + * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + */ + static exitExacInProportional = (bptAmountIn: bigint): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT), + bptAmountIn, + ], + ); }; /** @@ -117,7 +121,7 @@ export class WeightedEncoder { * @param amountsOut - the amounts of each token to be withdrawn from the pool * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens */ - static exitGivenOut = ( + static exitExactOut = ( amountsOut: bigint[], maxBPTAmountIn: bigint, ): Address => diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts index cc455852..2e2a209a 100644 --- a/src/entities/join/weighted/helpers.ts +++ b/src/entities/join/weighted/helpers.ts @@ -1,8 +1,4 @@ import { Address } from '../../../types'; -import { WeightedEncoder } from '../../encoders'; -import { TokenAmount } from '../../tokenAmount'; -import { JoinInput, JoinKind } from '..'; -import { Token } from '../../token'; export function getJoinParameters({ poolId, @@ -28,44 +24,3 @@ export function getJoinParameters({ return [poolId, sender, recipient, joinPoolRequest] as const; } - -// TODO: amounts for native asset join relies on the fact that the user provided wrapped address for input tokens - is that a fair assumption? -export function getAmountsIn(input: JoinInput, poolTokens: Token[]): bigint[] { - return poolTokens.map((token) => { - let tokenIn: TokenAmount | undefined; - switch (input.kind) { - case JoinKind.Init: - tokenIn = input.initAmountsIn.find((t) => - t.token.isEqual(token), - ); - break; - case JoinKind.ExactIn: - tokenIn = input.amountsIn.find((t) => t.token.isEqual(token)); - break; - } - return tokenIn?.amount ?? 0n; - }); -} - -export function getUserData(input: JoinInput, amountsIn: bigint[]): Address { - let userData: Address; - switch (input.kind) { - case JoinKind.Init: { - userData = WeightedEncoder.joinInit(amountsIn); - break; - } - case JoinKind.ExactIn: { - userData = WeightedEncoder.joinGivenIn(amountsIn, 0n); - break; - } - case JoinKind.ExactOutSingleAsset: - // TODO: add tokenInIndex after refactoring into switch/case into separate functions - case JoinKind.ExactOutProportional: { - userData = WeightedEncoder.joinGivenOut(input.bptOut.amount); - break; - } - default: - throw new Error('Invalid join kind'); - } - return userData; -} diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index a403de1a..7f07b979 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -9,7 +9,7 @@ import { ZERO_ADDRESS, } from '../../../utils'; import { balancerHelpersAbi, vaultAbi } from '../../../abi'; -import { getAmountsIn, getJoinParameters, getUserData } from './helpers'; +import { getJoinParameters } from './helpers'; import { BaseJoin, JoinCallInput, @@ -31,10 +31,40 @@ export class WeightedJoin implements BaseJoin { ): Promise { // TODO - This would need extended to work with relayer - // TODO: check inputs after refactoring switch/case into separate functions + let maxAmountsIn = Array(poolState.tokens.length).fill(0n); + let userData: Address; - const maxAmountsIn = getAmountsIn(input, poolState.tokens); - const userData = getUserData(input, maxAmountsIn); + switch (input.kind) { + case JoinKind.Init: + maxAmountsIn = poolState.tokens.map( + (t) => + input.initAmountsIn.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ); + userData = WeightedEncoder.joinInit(maxAmountsIn); + break; + case JoinKind.ExactIn: + maxAmountsIn = poolState.tokens.map( + (t) => + input.amountsIn.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ); + userData = WeightedEncoder.joinExactIn(maxAmountsIn, 0n); + break; + case JoinKind.ExactOutSingleAsset: + userData = WeightedEncoder.joinExactOutSingleAsset( + input.bptOut.amount, + poolState.tokens.findIndex( + (t) => t.address === input.tokenIn, + ), + ); + break; + case JoinKind.ExactOutProportional: + userData = WeightedEncoder.joinExactOutProportional( + input.bptOut.amount, + ); + break; + } let tokensIn = poolState.tokens; // replace wrapped token with native asset if needed @@ -112,23 +142,31 @@ export class WeightedJoin implements BaseJoin { case JoinKind.ExactIn: { maxAmountsIn = input.amountsIn.map((a) => a.amount); minBptOut = input.slippage.removeFrom(input.bptOut.amount); - userData = WeightedEncoder.joinGivenIn(maxAmountsIn, minBptOut); + userData = WeightedEncoder.joinExactIn(maxAmountsIn, minBptOut); break; } case JoinKind.ExactOutSingleAsset: - case JoinKind.ExactOutProportional: { - // TODO: refactor this after splitting encoder into 2 methods + if (input.tokenInIndex === undefined) { + throw new Error( + 'tokenInIndex must be defined for ExactOutSingleAsset joins', + ); + } maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ); - userData = WeightedEncoder.joinGivenOut( + userData = WeightedEncoder.joinExactOutSingleAsset( input.bptOut.amount, input.tokenInIndex, ); + case JoinKind.ExactOutProportional: { + maxAmountsIn = input.amountsIn.map((a) => + input.slippage.applyTo(a.amount), + ); + userData = WeightedEncoder.joinExactOutProportional( + input.bptOut.amount, + ); break; } - default: - throw new Error('Invalid join kind'); } const queryArgs = getJoinParameters({ From e20724d7aa6f4d5b39b263e76390395af0d292ff Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Mon, 11 Sep 2023 11:22:25 -0300 Subject: [PATCH 027/199] Refactor tokens to use basic types instead of Token class --- src/entities/join/index.ts | 9 +++++---- src/entities/join/weighted/weightedJoin.ts | 19 ++++++++++--------- test/weightedJoin.integration.test.ts | 22 +++++++++------------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 000f6637..d0cf2787 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,7 +1,6 @@ -import { Address } from 'viem'; import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; -import { Token } from '../token'; +import { Address } from '../../types'; export enum JoinKind { Init = 'Init', @@ -15,8 +14,10 @@ export type PoolState = { id: Address; address: Address; type: string; - // TODO: is it ok to replace by Token here? Or should we stick to basic types? - tokens: Token[]; // already properly sorted in case different versions sort them differently + tokens: { + address: Address; + decimals: number; + }[]; // already properly sorted in case different versions sort them differently }; // This will be extended for each pools specific input requirements diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 7f07b979..b7338740 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -31,12 +31,15 @@ export class WeightedJoin implements BaseJoin { ): Promise { // TODO - This would need extended to work with relayer - let maxAmountsIn = Array(poolState.tokens.length).fill(0n); + const poolTokens = poolState.tokens.map( + (t) => new Token(input.chainId, t.address, t.decimals), + ); + let maxAmountsIn = Array(poolTokens.length).fill(0n); let userData: Address; switch (input.kind) { case JoinKind.Init: - maxAmountsIn = poolState.tokens.map( + maxAmountsIn = poolTokens.map( (t) => input.initAmountsIn.find((a) => a.token.isEqual(t)) ?.amount ?? 0n, @@ -44,7 +47,7 @@ export class WeightedJoin implements BaseJoin { userData = WeightedEncoder.joinInit(maxAmountsIn); break; case JoinKind.ExactIn: - maxAmountsIn = poolState.tokens.map( + maxAmountsIn = poolTokens.map( (t) => input.amountsIn.find((a) => a.token.isEqual(t)) ?.amount ?? 0n, @@ -54,9 +57,7 @@ export class WeightedJoin implements BaseJoin { case JoinKind.ExactOutSingleAsset: userData = WeightedEncoder.joinExactOutSingleAsset( input.bptOut.amount, - poolState.tokens.findIndex( - (t) => t.address === input.tokenIn, - ), + poolTokens.findIndex((t) => t.address === input.tokenIn), ); break; case JoinKind.ExactOutProportional: @@ -66,10 +67,10 @@ export class WeightedJoin implements BaseJoin { break; } - let tokensIn = poolState.tokens; + let tokensIn = [...poolTokens]; // replace wrapped token with native asset if needed if (input.joinWithNativeAsset) { - tokensIn = poolState.tokens.map((token) => { + tokensIn = poolTokens.map((token) => { if (token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId])) { return new Token(input.chainId, ZERO_ADDRESS, 18); } else { @@ -110,7 +111,7 @@ export class WeightedJoin implements BaseJoin { const tokenInIndex = input.kind === JoinKind.ExactOutSingleAsset - ? poolState.tokens.findIndex((t) => t.address === input.tokenIn) + ? poolTokens.findIndex((t) => t.address === input.tokenIn) : undefined; return { diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index b309a7e6..e3f8f9b8 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -203,19 +203,15 @@ describe('weighted join test', () => { export class MockApi { public async getPool(id: Address): Promise { - const tokens = [ - new Token( - ChainId.MAINNET, - '0xba100000625a3754423978a60c9317c58a424e3d', - 18, - 'BAL', - ), - new Token( - ChainId.MAINNET, - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - 18, - 'WETH', - ), + const tokens: { address: Address; decimals: number }[] = [ + { + address: '0xba100000625a3754423978a60c9317c58a424e3d', + decimals: 18, + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + decimals: 18, + }, ]; return { id, From 03e3bff4651b890f30b7b445e0de01b1535dab41 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Mon, 11 Sep 2023 11:24:09 -0300 Subject: [PATCH 028/199] Remove unnecessary getInstance method --- src/entities/join/index.ts | 1 - src/entities/join/weighted/weightedJoin.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index d0cf2787..72eeb3b4 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -70,7 +70,6 @@ export type JoinCallInput = JoinQueryResult & { }; export interface BaseJoin { - getInstance(): BaseJoin; query(input: JoinInput, poolState: PoolState): Promise; buildCall(input: JoinCallInput): { call: Address; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index b7338740..4ed4d1c1 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -20,11 +20,6 @@ import { } from '..'; export class WeightedJoin implements BaseJoin { - // TODO - Probably not needed - getInstance(): WeightedJoin { - return new WeightedJoin(); - } - public async query( input: JoinInput, poolState: PoolState, From e6455935cf469b17b8d1df4c0887d10febedbeac Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 12 Sep 2023 09:28:19 -0300 Subject: [PATCH 029/199] Add setTokenBalance helper --- test/lib/utils/helper.ts | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index ae2fb4e3..7c7221b7 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -5,6 +5,11 @@ import { TestActions, TransactionReceipt, WalletActions, + concat, + keccak256, + pad, + toBytes, + toHex, } from 'viem'; import { erc20Abi } from '../../../src/abi'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../src/utils'; @@ -115,3 +120,41 @@ export async function sendTransactionGetBalances( gasUsed, }; } + +/** + * Set local ERC20 token balance for a given account address (used for testing) + * + * @param client client that will perform the setStorageAt call + * @param accountAddress Account address that will have token balance set + * @param token Token address which balance will be set + * @param slot Slot memory that stores balance - use npm package `slot20` to identify which slot to provide + * @param balance Balance in EVM amount + * @param isVyperMapping Whether the storage uses Vyper or Solidity mapping + */ +export const setTokenBalance = async ( + client: Client & TestActions, + accountAddress: Address, + token: Address, + slot: number, + balance: bigint, + isVyperMapping = false, +): Promise => { + // Get storage slot index + + const slotBytes = pad(toBytes(slot)); + const accountAddressBytes = pad(toBytes(accountAddress)); + + let index; + if (isVyperMapping) { + index = keccak256(concat([slotBytes, accountAddressBytes])); // slot, key + } else { + index = keccak256(concat([accountAddressBytes, slotBytes])); // key, slot + } + + // Manipulate local balance (needs to be bytes32 string) + await client.setStorageAt({ + address: token, + index, + value: toHex(balance, { size: 32 }), + }); +}; From 606c210b5b16b2e50e2f40fbed30f82817698242 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 12 Sep 2023 09:46:53 -0300 Subject: [PATCH 030/199] Add exactOut integration tests --- src/entities/join/index.ts | 1 + src/entities/join/weighted/weightedJoin.ts | 22 +- test/weightedJoin.integration.test.ts | 292 ++++++++++++++++----- 3 files changed, 250 insertions(+), 65 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 72eeb3b4..4a67705a 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -76,5 +76,6 @@ export interface BaseJoin { to: Address; value: bigint | undefined; minBptOut: bigint; + maxAmountsIn: bigint[]; }; } diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 4ed4d1c1..0bec8cd3 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -5,6 +5,7 @@ import { BALANCER_HELPERS, BALANCER_VAULT, CHAINS, + MAX_UINT256, NATIVE_ASSETS, ZERO_ADDRESS, } from '../../../utils'; @@ -26,10 +27,13 @@ export class WeightedJoin implements BaseJoin { ): Promise { // TODO - This would need extended to work with relayer + // TODO: check inputs + // joinExactOutProportional only works for Weighted v2+ -> should we handle at the SDK level or should we just let the query fail? + const poolTokens = poolState.tokens.map( (t) => new Token(input.chainId, t.address, t.decimals), ); - let maxAmountsIn = Array(poolTokens.length).fill(0n); + let maxAmountsIn = Array(poolTokens.length).fill(MAX_UINT256); let userData: Address; switch (input.kind) { @@ -52,7 +56,9 @@ export class WeightedJoin implements BaseJoin { case JoinKind.ExactOutSingleAsset: userData = WeightedEncoder.joinExactOutSingleAsset( input.bptOut.amount, - poolTokens.findIndex((t) => t.address === input.tokenIn), + poolTokens.findIndex( + (t) => t.address === input.tokenIn.toLowerCase(), + ), ); break; case JoinKind.ExactOutProportional: @@ -106,7 +112,9 @@ export class WeightedJoin implements BaseJoin { const tokenInIndex = input.kind === JoinKind.ExactOutSingleAsset - ? poolTokens.findIndex((t) => t.address === input.tokenIn) + ? poolTokens.findIndex( + (t) => t.address === input.tokenIn.toLowerCase(), + ) : undefined; return { @@ -123,11 +131,11 @@ export class WeightedJoin implements BaseJoin { to: Address; value: bigint | undefined; minBptOut: bigint; - // TODO: add maxAmountsIn after creating test scenario for ExactOut joins + maxAmountsIn: bigint[]; } { - let maxAmountsIn: bigint[]; - let userData: Address; + let maxAmountsIn = input.amountsIn.map((a) => a.amount); let minBptOut = input.bptOut.amount; + let userData: Address; switch (input.joinKind) { case JoinKind.Init: { @@ -154,6 +162,7 @@ export class WeightedJoin implements BaseJoin { input.bptOut.amount, input.tokenInIndex, ); + break; case JoinKind.ExactOutProportional: { maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), @@ -190,6 +199,7 @@ export class WeightedJoin implements BaseJoin { to: BALANCER_VAULT, value, minBptOut, + maxAmountsIn, }; } } diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index e3f8f9b8..65b3b70c 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -7,6 +7,7 @@ import { Client, createTestClient, http, + parseUnits, publicActions, PublicActions, TestActions, @@ -17,6 +18,8 @@ import { import { BaseJoin, ExactInJoinInput, + ExactOutProportionalJoinInput, + ExactOutSingleAssetJoinInput, JoinKind, PoolState, Slippage, @@ -27,7 +30,11 @@ import { JoinParser } from '../src/entities/join/parser'; import { Address } from '../src/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; -import { approveToken, sendTransactionGetBalances } from './lib/utils/helper'; +import { + approveToken, + sendTransactionGetBalances, + setTokenBalance, +} from './lib/utils/helper'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -39,7 +46,6 @@ describe('weighted join test', () => { let client: Client & PublicActions & TestActions & WalletActions; let poolId: Address; let poolFromApi: PoolState; - let tokenIn: Token; let weightedJoin: BaseJoin; beforeAll(async () => { @@ -78,6 +84,8 @@ describe('weighted join test', () => { }); describe('exact in', async () => { + let tokenIn: Token; + beforeAll(() => { poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH @@ -141,61 +149,207 @@ describe('weighted join test', () => { ); expect(expectedMinBpt).to.deep.eq(minBptOut); }); + + test('native asset join', async () => { + tokenIn = new Token( + chainId, + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + 18, + 'WETH', + ); + const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); + + await approveToken(client, testAddress, tokenIn.address); + + // perform join query to get expected bpt out + const joinInput: ExactInJoinInput = { + amountsIn: [amountIn], + chainId, + rpcUrl, + kind: JoinKind.ExactIn, + joinWithNativeAsset: true, + }; + const queryResult = await weightedJoin.query( + joinInput, + poolFromApi, + ); + + // build join call with expected minBpOut based on slippage + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, minBptOut } = weightedJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send join transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsIn.map((a) => a.token.address), + queryResult.bptOut.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinBpt = slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + }); }); - test('native asset join', async () => { - tokenIn = new Token( - chainId, - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - 18, - 'WETH', - ); - const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - - await approveToken(client, testAddress, tokenIn.address); - - // perform join query to get expected bpt out - const joinInput: ExactInJoinInput = { - amountsIn: [amountIn], - chainId, - rpcUrl, - kind: JoinKind.ExactIn, - joinWithNativeAsset: true, - }; - const queryResult = await weightedJoin.query(joinInput, poolFromApi); - - // build join call with expected minBpOut based on slippage - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, minBptOut } = weightedJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, + describe('exact out', async () => { + let tokenOut: Token; + + beforeAll(() => { + poolId = + '0x87a867f5d240a782d43d90b6b06dea470f3f8f22000200000000000000000516'; // Balancer 50COMP-50wstETH + tokenOut = new Token( + chainId, + '0x87a867f5d240a782d43d90b6b06dea470f3f8f22', + 18, + 'Balancer 50COMP-50wstETH', + ); }); - // send join transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsIn.map((a) => a.token.address), - queryResult.bptOut.token.address, - ], + test('single asset join', async () => { + const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); + const tokenIn = '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'; + + await approveToken(client, testAddress, tokenIn); + await setTokenBalance( client, testAddress, - to, - call, - value, + tokenIn, + 0, + parseUnits('100', 18), ); - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMinBpt = slippage.removeFrom(queryResult.bptOut.amount); - expect(expectedMinBpt).to.deep.eq(minBptOut); + // perform join query to get expected bpt out + const joinInput: ExactOutSingleAssetJoinInput = { + bptOut: amountOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.ExactOutSingleAsset, + }; + const queryResult = await weightedJoin.query( + joinInput, + poolFromApi, + ); + + // build join call with expected minBpOut based on slippage + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, maxAmountsIn } = weightedJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send join transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsIn.map((a) => a.token.address), + queryResult.bptOut.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + }); + + test('proportional join', async () => { + const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); + const slots = [0, 1]; + + for (let i = 0; i < poolFromApi.tokens.length; i++) { + const token = poolFromApi.tokens[i]; + await approveToken(client, testAddress, token.address); + await setTokenBalance( + client, + testAddress, + token.address, + slots[i], + parseUnits('100', token.decimals), + ); + } + + // perform join query to get expected bpt out + const joinInput: ExactOutProportionalJoinInput = { + bptOut: amountOut, + chainId, + rpcUrl, + kind: JoinKind.ExactOutProportional, + }; + const queryResult = await weightedJoin.query( + joinInput, + poolFromApi, + ); + + // build join call with expected minBpOut based on slippage + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, maxAmountsIn } = weightedJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send join transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsIn.map((a) => a.token.address), + queryResult.bptOut.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(queryResult.bptOut.amount > 0n).to.be.true; + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + }); }); }); @@ -203,16 +357,36 @@ describe('weighted join test', () => { export class MockApi { public async getPool(id: Address): Promise { - const tokens: { address: Address; decimals: number }[] = [ - { - address: '0xba100000625a3754423978a60c9317c58a424e3d', - decimals: 18, - }, - { - address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - decimals: 18, - }, - ]; + let tokens: { address: Address; decimals: number }[] = []; + if ( + id === + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014' + ) { + tokens = [ + { + address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL + decimals: 18, + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH + decimals: 18, + }, + ]; + } else if ( + id === + '0x87a867f5d240a782d43d90b6b06dea470f3f8f22000200000000000000000516' + ) { + tokens = [ + { + address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH slot 0 + decimals: 18, + }, + { + address: '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP slot 1 + decimals: 18, + }, + ]; + } return { id, address: getPoolAddress(id) as Address, From c2c8030a8f2f5639bf0fde541ba37f5ef52508a2 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 12 Sep 2023 10:23:14 -0300 Subject: [PATCH 031/199] Add findTokenBalanceSlot helper --- test/lib/utils/helper.ts | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 7c7221b7..4a4d897c 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -1,15 +1,19 @@ import { Address, Client, + Hex, PublicActions, TestActions, TransactionReceipt, WalletActions, concat, + encodeAbiParameters, + hexToBigInt, keccak256, pad, toBytes, toHex, + trim, } from 'viem'; import { erc20Abi } from '../../../src/abi'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../src/utils'; @@ -158,3 +162,57 @@ export const setTokenBalance = async ( value: toHex(balance, { size: 32 }), }); }; + +export async function findTokenBalanceSlot( + client: Client & PublicActions & TestActions, + accountAddress: Address, + tokenAddress: Address, + isVyperMapping = false, +): Promise { + const probeA = encodeAbiParameters( + [{ name: 'probeA', type: 'uint256' }], + [BigInt((Math.random() * 10000).toFixed())], + ); + const probeB = encodeAbiParameters( + [{ name: 'probeA', type: 'uint256' }], + [BigInt((Math.random() * 10000).toFixed())], + ); + for (let i = 0; i < 999; i++) { + const slotBytes = pad(toBytes(i)); + const accountAddressBytes = pad(toBytes(accountAddress)); + let probedSlot: Address; + if (isVyperMapping) { + probedSlot = keccak256(concat([slotBytes, accountAddressBytes])); // slot, key + } else { + probedSlot = keccak256(concat([accountAddressBytes, slotBytes])); // key, slot + } + // remove padding for JSON RPC + probedSlot = trim(probedSlot); + const prev = (await client.getStorageAt({ + address: tokenAddress, + slot: probedSlot, + })) as Hex; + // make sure the probe will change the slot value + const probe = prev === probeA ? probeB : probeA; + + await client.setStorageAt({ + address: tokenAddress, + index: probedSlot, + value: probe, + }); + + const balance = await getErc20Balance( + tokenAddress, + client, + accountAddress, + ); + // reset to previous value + await client.setStorageAt({ + address: tokenAddress, + index: probedSlot, + value: prev, + }); + if (balance === hexToBigInt(probe)) return i; + } + throw new Error('Balance slot not found!'); +} From c0edb275c51ed77ff54b5df7f4705b00b76750fa Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 12 Sep 2023 10:56:06 -0300 Subject: [PATCH 032/199] Add forkSetup to test helper --- test/lib/utils/helper.ts | 70 +++++++++++++++++++++++++++ test/weightedJoin.integration.test.ts | 51 +++++-------------- 2 files changed, 81 insertions(+), 40 deletions(-) diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 4a4d897c..2e3bf2a1 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -163,6 +163,14 @@ export const setTokenBalance = async ( }); }; +/** + * Find ERC20 token balance storage slot (to be used on setTokenBalance) + * + * @param client client that will perform contract calls + * @param accountAddress Account address to probe storage slot changes + * @param tokenAddress Token address which we're looking for the balance slot + * @param isVyperMapping Whether the storage uses Vyper or Solidity mapping + */ export async function findTokenBalanceSlot( client: Client & PublicActions & TestActions, accountAddress: Address, @@ -216,3 +224,65 @@ export async function findTokenBalanceSlot( } throw new Error('Balance slot not found!'); } + +/** + * Setup local fork with approved token balance for a given account address + * + * @param client Client that will perform transactions + * @param accountAddress Account address that will have token balance set and approved + * @param tokens Token addresses which balance will be set and approved + * @param slots Slot that stores token balance in memory - use npm package `slot20` to identify which slot to provide + * @param balances Balances in EVM amounts + * @param jsonRpcUrl Url with remote node to be forked locally + * @param blockNumber Number of the block that the fork will happen + * @param isVyperMapping Whether the storage uses Vyper or Solidity mapping + */ +export const forkSetup = async ( + client: Client & PublicActions & TestActions & WalletActions, + accountAddress: Address, + tokens: Address[], + slots: number[] | undefined, + balances: bigint[], + jsonRpcUrl: string, + blockNumber?: bigint, + isVyperMapping: boolean[] = Array(tokens.length).fill(false), +): Promise => { + await client.reset({ + blockNumber, + jsonRpcUrl, + }); + + await client.impersonateAccount({ address: accountAddress }); + + let _slots: number[]; + if (slots) { + _slots = slots; + } else { + _slots = await Promise.all( + tokens.map(async (token, i) => + findTokenBalanceSlot( + client, + accountAddress, + token, + isVyperMapping[i], + ), + ), + ); + console.log(`slots: ${_slots}`); + } + + for (let i = 0; i < tokens.length; i++) { + // Set initial account balance for each token that will be used to join pool + await setTokenBalance( + client, + accountAddress, + tokens[i], + _slots[i], + balances[i], + isVyperMapping[i], + ); + + // Approve appropriate allowances so that vault contract can move tokens + await approveToken(client, accountAddress, tokens[i]); + } +}; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 65b3b70c..c4fc13e4 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -30,11 +30,7 @@ import { JoinParser } from '../src/entities/join/parser'; import { Address } from '../src/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; -import { - approveToken, - sendTransactionGetBalances, - setTokenBalance, -} from './lib/utils/helper'; +import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -66,18 +62,19 @@ describe('weighted join test', () => { }); beforeEach(async () => { - // reset local fork - await client.reset({ - blockNumber, - jsonRpcUrl: process.env.ETHEREUM_RPC_URL, - }); - - // prepare test client with balance and token approvals - await client.impersonateAccount({ address: testAddress }); - // get pool state from api poolFromApi = await api.getPool(poolId); + await forkSetup( + client, + testAddress, + poolFromApi.tokens.map((t) => t.address), + undefined, // TODO: hardcode these values to improve test performance + poolFromApi.tokens.map((t) => parseUnits('100', t.decimals)), + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); + // setup join helper const joinParser = new JoinParser(); weightedJoin = joinParser.getJoin(poolFromApi.type); @@ -100,8 +97,6 @@ describe('weighted join test', () => { ); const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - await approveToken(client, testAddress, tokenIn.address); - // perform join query to get expected bpt out const joinInput: ExactInJoinInput = { amountsIn: [amountIn], @@ -159,8 +154,6 @@ describe('weighted join test', () => { ); const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - await approveToken(client, testAddress, tokenIn.address); - // perform join query to get expected bpt out const joinInput: ExactInJoinInput = { amountsIn: [amountIn], @@ -229,15 +222,6 @@ describe('weighted join test', () => { const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); const tokenIn = '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'; - await approveToken(client, testAddress, tokenIn); - await setTokenBalance( - client, - testAddress, - tokenIn, - 0, - parseUnits('100', 18), - ); - // perform join query to get expected bpt out const joinInput: ExactOutSingleAssetJoinInput = { bptOut: amountOut, @@ -289,19 +273,6 @@ describe('weighted join test', () => { test('proportional join', async () => { const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); - const slots = [0, 1]; - - for (let i = 0; i < poolFromApi.tokens.length; i++) { - const token = poolFromApi.tokens[i]; - await approveToken(client, testAddress, token.address); - await setTokenBalance( - client, - testAddress, - token.address, - slots[i], - parseUnits('100', token.decimals), - ); - } // perform join query to get expected bpt out const joinInput: ExactOutProportionalJoinInput = { From aef40261be60d24d1e03e9c2305a76eb69623a7e Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Tue, 12 Sep 2023 11:57:02 -0300 Subject: [PATCH 033/199] Add comments describing helper methods --- test/lib/utils/helper.ts | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 2e3bf2a1..9b3ffa22 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -70,6 +70,17 @@ export const getBalances = async ( return Promise.all(balances); }; +/** + * Helper function that sends a transaction and check for balance deltas + * + * @param tokensForBalanceCheck Token addresses to check balance deltas + * @param client Client that will perform transactions + * @param clientAddress Account address that will have token balance checked + * @param to Contract Address that will be called + * @param data Transaction encoded data + * @param value ETH value in case of ETH transfer + * @returns Transaction recepit, balance deltas and gas used + */ export async function sendTransactionGetBalances( tokensForBalanceCheck: Address[], client: Client & PublicActions & TestActions & WalletActions, @@ -148,7 +159,7 @@ export const setTokenBalance = async ( const slotBytes = pad(toBytes(slot)); const accountAddressBytes = pad(toBytes(accountAddress)); - let index; + let index: Address; if (isVyperMapping) { index = keccak256(concat([slotBytes, accountAddressBytes])); // slot, key } else { @@ -186,6 +197,7 @@ export async function findTokenBalanceSlot( [BigInt((Math.random() * 10000).toFixed())], ); for (let i = 0; i < 999; i++) { + // encode probed slot const slotBytes = pad(toBytes(i)); const accountAddressBytes = pad(toBytes(accountAddress)); let probedSlot: Address; @@ -194,32 +206,39 @@ export async function findTokenBalanceSlot( } else { probedSlot = keccak256(concat([accountAddressBytes, slotBytes])); // key, slot } + // remove padding for JSON RPC probedSlot = trim(probedSlot); + + // get storage value const prev = (await client.getStorageAt({ address: tokenAddress, slot: probedSlot, })) as Hex; - // make sure the probe will change the slot value - const probe = prev === probeA ? probeB : probeA; + // set storage slot to new probe + const probe = prev === probeA ? probeB : probeA; await client.setStorageAt({ address: tokenAddress, index: probedSlot, value: probe, }); + // check if balance changed const balance = await getErc20Balance( tokenAddress, client, accountAddress, ); + // reset to previous value await client.setStorageAt({ address: tokenAddress, index: probedSlot, value: prev, }); + + // return slot if balance changed if (balance === hexToBigInt(probe)) return i; } throw new Error('Balance slot not found!'); From 3cafa27921fd02250bbef8b44612856adb18124e Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 13 Sep 2023 11:11:11 +0100 Subject: [PATCH 034/199] refactor: Fix typos in weightedEncoder. --- src/entities/encoders/weighted.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index 269bd0b2..cc652503 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -88,7 +88,7 @@ export class WeightedEncoder { * @param bptAmountIn - the amount of BPT to be burned * @param enterTokenIndex - the index of the token to removed from the pool */ - static exitExacInSingleAsset = ( + static exitExactInSingleAsset = ( bptAmountIn: bigint, exitTokenIndex: number, ): Address => { @@ -106,7 +106,7 @@ export class WeightedEncoder { * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned */ - static exitExacInProportional = (bptAmountIn: bigint): Address => { + static exitExactInProportional = (bptAmountIn: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ From 294c0828665a221fd48294119fcf9fc0b9f36d41 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 13 Sep 2023 15:32:57 +0100 Subject: [PATCH 035/199] feat: Initial Exit functionality and Weighted Pool implementation. --- src/entities/common/index.ts | 2 + src/entities/common/replaceWrapped.ts | 12 + src/entities/common/types.ts | 12 + src/entities/exit/index.ts | 1 + src/entities/exit/parser.ts | 21 ++ src/entities/exit/types.ts | 77 +++++ src/entities/exit/weighted/helpers.ts | 29 ++ src/entities/exit/weighted/weightedExit.ts | 180 ++++++++++ src/entities/index.ts | 4 +- src/entities/join/index.ts | 12 +- src/entities/join/weighted/weightedJoin.ts | 14 +- test/weightedExit.integration.test.ts | 362 +++++++++++++++++++++ 12 files changed, 703 insertions(+), 23 deletions(-) create mode 100644 src/entities/common/index.ts create mode 100644 src/entities/common/replaceWrapped.ts create mode 100644 src/entities/common/types.ts create mode 100644 src/entities/exit/index.ts create mode 100644 src/entities/exit/parser.ts create mode 100644 src/entities/exit/types.ts create mode 100644 src/entities/exit/weighted/helpers.ts create mode 100644 src/entities/exit/weighted/weightedExit.ts create mode 100644 test/weightedExit.integration.test.ts diff --git a/src/entities/common/index.ts b/src/entities/common/index.ts new file mode 100644 index 00000000..171fe6b7 --- /dev/null +++ b/src/entities/common/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export * from './replaceWrapped'; diff --git a/src/entities/common/replaceWrapped.ts b/src/entities/common/replaceWrapped.ts new file mode 100644 index 00000000..2da576d4 --- /dev/null +++ b/src/entities/common/replaceWrapped.ts @@ -0,0 +1,12 @@ +import { Token } from '../token'; +import { NATIVE_ASSETS, ZERO_ADDRESS } from '../../utils'; + +export function replaceWrapped(tokens: Token[], chainId: number): Token[] { + return tokens.map((token) => { + if (token.isUnderlyingEqual(NATIVE_ASSETS[chainId])) { + return new Token(chainId, ZERO_ADDRESS, 18); + } else { + return token; + } + }); +} diff --git a/src/entities/common/types.ts b/src/entities/common/types.ts new file mode 100644 index 00000000..6c7d0e88 --- /dev/null +++ b/src/entities/common/types.ts @@ -0,0 +1,12 @@ +import { Address } from '../../types'; + +// Returned from API and used as input +export type PoolState = { + id: Address; + address: Address; + type: string; + tokens: { + address: Address; + decimals: number; + }[]; // already properly sorted in case different versions sort them differently +}; diff --git a/src/entities/exit/index.ts b/src/entities/exit/index.ts new file mode 100644 index 00000000..fcb073fe --- /dev/null +++ b/src/entities/exit/index.ts @@ -0,0 +1 @@ +export * from './types'; diff --git a/src/entities/exit/parser.ts b/src/entities/exit/parser.ts new file mode 100644 index 00000000..b2cf670b --- /dev/null +++ b/src/entities/exit/parser.ts @@ -0,0 +1,21 @@ +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 = {}; + + 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]; + } +} diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts new file mode 100644 index 00000000..bbff607e --- /dev/null +++ b/src/entities/exit/types.ts @@ -0,0 +1,77 @@ +import { TokenAmount } from '../tokenAmount'; +import { Slippage } from '../slippage'; +import { Address } from '../../types'; +import { PoolState } from '../common'; + +export enum ExitKind { + UNBALANCED = 'UNBALANCED', // exitExactOut + SINGLE_ASSET = 'SINGLE_ASSET', // exitExactInSingleAsset + PROPORTIONAL = 'PROPORTIONAL', // exitExactInProportional +} + +// This will be extended for each pools specific output requirements +export type BaseExitInput = { + chainId: number; + rpcUrl?: string; + exitWithNativeAsset?: boolean; +}; + +export type UnbalancedExitInput = BaseExitInput & { + amountsOut: TokenAmount[]; + kind: ExitKind.UNBALANCED; +}; + +export type SingleAssetExitInput = BaseExitInput & { + bptIn: TokenAmount; + tokenOut: Address; + kind: ExitKind.SINGLE_ASSET; +}; + +export type ProportionalExitInput = BaseExitInput & { + bptIn: TokenAmount; + kind: ExitKind.PROPORTIONAL; +}; + +export type ExitInput = + | UnbalancedExitInput + | SingleAssetExitInput + | ProportionalExitInput; + +// Returned from a exit query +export type ExitQueryResult = { + id: Address; + exitKind: ExitKind; + bptIn: TokenAmount; + amountsOut: TokenAmount[]; + tokenOutIndex?: number; +}; + +export type ExitCallInput = ExitQueryResult & { + slippage: Slippage; + sender: Address; + recipient: Address; +}; + +export type BuildOutput = { + call: Address; + to: Address; + value: bigint | undefined; + maxBptIn: bigint; + minAmountsOut: bigint[]; +}; + +export interface BaseExit { + query(input: ExitInput, poolState: PoolState): Promise; + buildCall(input: ExitCallInput): BuildOutput; +} + +export type ExitConfig = { + customPoolExits: Record; +}; + +export type ExitPoolRequest = { + assets: Address[]; + minAmountsOut: bigint[]; + userData: Address; + toInternalBalance: boolean; +}; diff --git a/src/entities/exit/weighted/helpers.ts b/src/entities/exit/weighted/helpers.ts new file mode 100644 index 00000000..d040f4fb --- /dev/null +++ b/src/entities/exit/weighted/helpers.ts @@ -0,0 +1,29 @@ +import { Address } from '../../../types'; +import { ExitPoolRequest } from '../types'; + +export function getExitParameters({ + poolId, + assets, + sender, + recipient, + minAmountsOut, + userData, + toInternalBalance, +}: { + poolId: Address; + assets: Address[]; + sender: Address; + recipient: Address; + minAmountsOut: bigint[]; + userData: Address; + toInternalBalance: boolean; +}) { + const exitPoolRequest: ExitPoolRequest = { + assets, // with BPT + minAmountsOut, // with BPT + userData, // wihtout BPT + toInternalBalance, + }; + + return [poolId, sender, recipient, exitPoolRequest] as const; +} diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts new file mode 100644 index 00000000..2f72dedb --- /dev/null +++ b/src/entities/exit/weighted/weightedExit.ts @@ -0,0 +1,180 @@ +import { createPublicClient, encodeFunctionData, http } from 'viem'; +import { Token, TokenAmount, WeightedEncoder } from '../../..'; +import { Address } from '../../../types'; +import { + BALANCER_HELPERS, + BALANCER_VAULT, + CHAINS, + MAX_UINT256, + ZERO_ADDRESS, +} from '../../../utils'; +import { balancerHelpersAbi, vaultAbi } from '../../../abi'; +import { getExitParameters } from './helpers'; +import { + BaseExit, + BuildOutput, + ExitCallInput, + ExitInput, + ExitKind, + ExitQueryResult, +} from '../types'; +import { PoolState, replaceWrapped } from '../../common'; + +export class WeightedExit implements BaseExit { + public async query( + input: ExitInput, + poolState: PoolState, + ): Promise { + // TODO - This would need extended to work with relayer + + const poolTokens = poolState.tokens.map( + (t) => new Token(input.chainId, t.address, t.decimals), + ); + let minAmountsOut = Array(poolTokens.length).fill(0n); + let userData: Address; + + switch (input.kind) { + case ExitKind.UNBALANCED: + minAmountsOut = poolTokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ); + userData = WeightedEncoder.exitExactOut( + minAmountsOut, + MAX_UINT256, + ); + break; + case ExitKind.SINGLE_ASSET: + userData = WeightedEncoder.exitExactInSingleAsset( + input.bptIn.amount, + poolTokens.findIndex( + (t) => t.address === input.tokenOut.toLowerCase(), + ), + ); + break; + case ExitKind.PROPORTIONAL: + userData = WeightedEncoder.exitExactInProportional( + input.bptIn.amount, + ); + break; + } + + let tokensOut = [...poolTokens]; + // replace wrapped token with native asset if needed + if (input.exitWithNativeAsset) + tokensOut = replaceWrapped(poolTokens, input.chainId); + + const queryArgs = getExitParameters({ + poolId: poolState.id, + assets: tokensOut.map((t) => t.address), + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + minAmountsOut, + userData, + toInternalBalance: false, // TODO - Should we make this part of input? + }); + + const client = createPublicClient({ + transport: http(input.rpcUrl), + chain: CHAINS[input.chainId], + }); + + const { + result: [queryBptIn, queryAmountsOut], + } = await client.simulateContract({ + address: BALANCER_HELPERS[input.chainId], + abi: balancerHelpersAbi, + functionName: 'queryExit', + args: queryArgs, + }); + + const bpt = new Token(input.chainId, poolState.address, 18); + const bptIn = TokenAmount.fromRawAmount(bpt, queryBptIn); + + const amountsOut = queryAmountsOut.map((a, i) => + TokenAmount.fromRawAmount(tokensOut[i], a), + ); + + const tokenOutIndex = + input.kind === ExitKind.SINGLE_ASSET + ? poolTokens.findIndex( + (t) => t.address === input.tokenOut.toLowerCase(), + ) + : undefined; + + return { + exitKind: input.kind, + id: poolState.id, + bptIn, + amountsOut, + tokenOutIndex, + }; + } + + public buildCall(input: ExitCallInput): BuildOutput { + let minAmountsOut: bigint[]; + let maxBptIn: bigint; + let userData: Address; + + switch (input.exitKind) { + case ExitKind.UNBALANCED: + minAmountsOut = input.amountsOut.map((a) => a.amount); + maxBptIn = input.slippage.applyTo(input.bptIn.amount); + userData = WeightedEncoder.exitExactOut( + minAmountsOut, + maxBptIn, + ); + break; + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + 'tokenOutIndex must be defined for SINGLE_ASSET exit', + ); + } + minAmountsOut = input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ); + maxBptIn = input.bptIn.amount; + userData = WeightedEncoder.exitExactInSingleAsset( + maxBptIn, + input.tokenOutIndex, + ); + break; + case ExitKind.PROPORTIONAL: + minAmountsOut = input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ); + maxBptIn = input.bptIn.amount; + userData = WeightedEncoder.exitExactInProportional( + input.bptIn.amount, + ); + break; + } + + const queryArgs = getExitParameters({ + poolId: input.id, + assets: input.amountsOut.map((a) => a.token.address), + sender: input.sender, + recipient: input.recipient, + minAmountsOut, + userData, + toInternalBalance: false, // TODO - Should we make this part of input? + }); + + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'exitPool', + args: queryArgs, + }); + + // Encode data + return { + call, + to: BALANCER_VAULT, + value: 0n, + maxBptIn, + minAmountsOut, + }; + } +} diff --git a/src/entities/index.ts b/src/entities/index.ts index 3b61ca42..24742462 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,8 +1,10 @@ export * from './encoders'; -export * from './join/'; +export * from './join'; +export * from './exit'; export * from './path'; export * from './swap'; export * from './slippage'; export * from './token'; export * from './tokenAmount'; export * from './pools/'; +export * from './common'; diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 4a67705a..71056477 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,6 +1,7 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; import { Address } from '../../types'; +import { PoolState } from '../common'; export enum JoinKind { Init = 'Init', @@ -9,17 +10,6 @@ export enum JoinKind { ExactOutProportional = 'ExactOutProportional', } -// Returned from API and used as input -export type PoolState = { - id: Address; - address: Address; - type: string; - tokens: { - address: Address; - decimals: number; - }[]; // already properly sorted in case different versions sort them differently -}; - // This will be extended for each pools specific input requirements export type BaseJoinInput = { chainId: number; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 0bec8cd3..7b6bc0fb 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -6,7 +6,6 @@ import { BALANCER_VAULT, CHAINS, MAX_UINT256, - NATIVE_ASSETS, ZERO_ADDRESS, } from '../../../utils'; import { balancerHelpersAbi, vaultAbi } from '../../../abi'; @@ -17,8 +16,8 @@ import { JoinInput, JoinKind, JoinQueryResult, - PoolState, } from '..'; +import { PoolState, replaceWrapped } from '../../common'; export class WeightedJoin implements BaseJoin { public async query( @@ -70,15 +69,8 @@ export class WeightedJoin implements BaseJoin { let tokensIn = [...poolTokens]; // replace wrapped token with native asset if needed - if (input.joinWithNativeAsset) { - tokensIn = poolTokens.map((token) => { - if (token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId])) { - return new Token(input.chainId, ZERO_ADDRESS, 18); - } else { - return token; - } - }); - } + if (input.joinWithNativeAsset) + tokensIn = replaceWrapped(poolTokens, input.chainId); const queryArgs = getJoinParameters({ poolId: poolState.id, diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts new file mode 100644 index 00000000..7cb85e45 --- /dev/null +++ b/test/weightedExit.integration.test.ts @@ -0,0 +1,362 @@ +// pnpm test -- weightedExit.integration.test.ts +import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + Client, + createTestClient, + http, + parseUnits, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, +} from 'viem'; + +import { + BaseExit, + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + PoolState, + Slippage, + Token, + TokenAmount, +} from '../src/entities'; +import { ExitParser } from '../src/entities/exit/parser'; +import { Address } from '../src/types'; +import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; + +import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; + +const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig + +describe('weighted exit test', () => { + let api: MockApi; + let chainId: ChainId; + let rpcUrl: string; + let blockNumber: bigint; + let client: Client & PublicActions & TestActions & WalletActions; + let poolId: Address; + let poolFromApi: PoolState; + let weightedExit: BaseExit; + let tokenBpt: Token; + + beforeAll(async () => { + // setup mock api + api = new MockApi(); + + // setup chain and test client + chainId = ChainId.MAINNET; + rpcUrl = 'http://127.0.0.1:8545/'; + blockNumber = 18043296n; + client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + }); + + beforeEach(async () => { + // get pool state from api + poolFromApi = await api.getPool(poolId); + + await forkSetup( + client, + testAddress, + [poolFromApi.address], + undefined, // TODO: hardcode these values to improve test performance + [parseUnits('1', 18)], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); + + // setup join helper + const exitParser = new ExitParser(); + weightedExit = exitParser.getExit(poolFromApi.type); + }); + + test('single asset exit', async () => { + tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); + const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); + const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL + + // perform join query to get expected bpt out + const exitInput: SingleAssetExitInput = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SINGLE_ASSET, + }; + const queryResult = await weightedExit.query(exitInput, poolFromApi); + + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + queryResult.amountsOut.forEach((a, i) => { + if (i === queryResult.tokenOutIndex) + expect(a.amount > 0n).to.be.true; + else expect(a.amount === 0n).to.be.true; + }); + + // build call with slippage applied + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, maxBptIn, minAmountsOut } = + weightedExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsOut.map((a) => a.token.address), + queryResult.bptIn.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(maxBptIn).to.eq(bptIn.amount); + const expectedDeltas = [ + ...queryResult.amountsOut.map((a) => a.amount), + queryResult.bptIn.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + }); + + test('proportional exit', async () => { + tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); + const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); + + // perform join query to get expected bpt out + const exitInput: ProportionalExitInput = { + chainId, + rpcUrl, + bptIn, + kind: ExitKind.PROPORTIONAL, + }; + const queryResult = await weightedExit.query(exitInput, poolFromApi); + + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + queryResult.amountsOut.forEach((a) => { + expect(a.amount > 0n).to.be.true; + }); + + // build call with slippage applied + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, maxBptIn, minAmountsOut } = + weightedExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsOut.map((a) => a.token.address), + queryResult.bptIn.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(maxBptIn).to.eq(bptIn.amount); + const expectedDeltas = [ + ...queryResult.amountsOut.map((a) => a.amount), + queryResult.bptIn.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + }); + + test('unbalanced exit', async () => { + const poolTokens = poolFromApi.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const amountsOut = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '0.001'), + ); + // perform join query to get expected bpt out + const exitInput: UnbalancedExitInput = { + chainId, + rpcUrl, + amountsOut, + kind: ExitKind.UNBALANCED, + }; + const queryResult = await weightedExit.query(exitInput, poolFromApi); + + expect(queryResult.bptIn.amount > 0n).to.be.true; + + queryResult.amountsOut.forEach((a, i) => { + expect(a.amount).to.eq(amountsOut[i].amount); + }); + + // build call with slippage applied + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, maxBptIn, minAmountsOut } = + weightedExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsOut.map((a) => a.token.address), + queryResult.bptIn.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + minAmountsOut.forEach((a, i) => { + expect(a).to.eq(amountsOut[i].amount); + }); + const expectedDeltas = [ + ...queryResult.amountsOut.map((a) => a.amount), + queryResult.bptIn.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); + expect(expectedMaxBptIn).to.deep.eq(maxBptIn); + }); + + test('exit with native asset', async () => { + tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); + const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); + + // perform join query to get expected bpt out + const exitInput: ProportionalExitInput = { + chainId, + rpcUrl, + bptIn, + kind: ExitKind.PROPORTIONAL, + exitWithNativeAsset: true, + }; + const queryResult = await weightedExit.query(exitInput, poolFromApi); + + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + queryResult.amountsOut.forEach((a) => { + expect(a.amount > 0n).to.be.true; + }); + + // build call with slippage applied + const slippage = Slippage.fromPercentage('1'); // 1% + const { call, to, value, maxBptIn, minAmountsOut } = + weightedExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...queryResult.amountsOut.map((a) => a.token.address), + queryResult.bptIn.token.address, + ], + client, + testAddress, + to, + call, + value, + ); + + expect(transactionReceipt.status).to.eq('success'); + expect(maxBptIn).to.eq(bptIn.amount); + const expectedDeltas = [ + ...queryResult.amountsOut.map((a) => a.amount), + queryResult.bptIn.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Address): Promise { + let tokens: { address: Address; decimals: number }[] = []; + if ( + id === + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014' + ) { + tokens = [ + { + address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL + decimals: 18, + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH + decimals: 18, + }, + ]; + } else if ( + id === + '0x87a867f5d240a782d43d90b6b06dea470f3f8f22000200000000000000000516' + ) { + tokens = [ + { + address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH slot 0 + decimals: 18, + }, + { + address: '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP slot 1 + decimals: 18, + }, + ]; + } + return { + id, + address: getPoolAddress(id) as Address, + type: 'Weighted', + tokens, + }; + } +} + +/******************************************************************************/ From 7a524268afad364d53e828b92d7fcb128e67f27d Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 13 Sep 2023 14:20:43 -0300 Subject: [PATCH 036/199] Rename join kind to follow convention decided by the team --- src/entities/encoders/weighted.ts | 15 ++++++---- src/entities/join/index.ts | 24 ++++++++-------- src/entities/join/weighted/weightedJoin.ts | 33 ++++++++++++---------- test/weightedJoin.integration.test.ts | 22 +++++++-------- 4 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index 269bd0b2..45aa9888 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -39,7 +39,10 @@ export class WeightedEncoder { * @param amountsIn - the amounts each of token to deposit in the pool as liquidity * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens */ - static joinExactIn = (amountsIn: bigint[], minimumBPT: bigint): Address => + static joinUnbalanced = ( + amountsIn: bigint[], + minimumBPT: bigint, + ): Address => encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], [ @@ -54,7 +57,7 @@ export class WeightedEncoder { * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ - static joinExactOutSingleAsset = ( + static joinSingleAsset = ( bptAmountOut: bigint, enterTokenIndex: number, ): Address => { @@ -73,7 +76,7 @@ export class WeightedEncoder { * Encodes the userData parameter for joining a WeightedPool proportionally to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted */ - static joinExactOutProportional = (bptAmountOut: bigint): Address => { + static joinProportional = (bptAmountOut: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ @@ -88,7 +91,7 @@ export class WeightedEncoder { * @param bptAmountIn - the amount of BPT to be burned * @param enterTokenIndex - the index of the token to removed from the pool */ - static exitExacInSingleAsset = ( + static exitSingleAsset = ( bptAmountIn: bigint, exitTokenIndex: number, ): Address => { @@ -106,7 +109,7 @@ export class WeightedEncoder { * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned */ - static exitExacInProportional = (bptAmountIn: bigint): Address => { + static exitProportional = (bptAmountIn: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ @@ -121,7 +124,7 @@ export class WeightedEncoder { * @param amountsOut - the amounts of each token to be withdrawn from the pool * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens */ - static exitExactOut = ( + static exitUnbalanced = ( amountsOut: bigint[], maxBPTAmountIn: bigint, ): Address => diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 4a67705a..cafa2217 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -4,9 +4,9 @@ import { Address } from '../../types'; export enum JoinKind { Init = 'Init', - ExactIn = 'ExactIn', - ExactOutSingleAsset = 'ExactOutSingleAsset', - ExactOutProportional = 'ExactOutProportional', + Unbalanced = 'Unbalanced', + SingleAsset = 'SingleAsset', + Proportional = 'Proportional', } // Returned from API and used as input @@ -32,27 +32,27 @@ export type InitJoinInput = BaseJoinInput & { kind: JoinKind.Init; }; -export type ExactInJoinInput = BaseJoinInput & { +export type UnbalancedJoinInput = BaseJoinInput & { amountsIn: TokenAmount[]; - kind: JoinKind.ExactIn; + kind: JoinKind.Unbalanced; }; -export type ExactOutSingleAssetJoinInput = BaseJoinInput & { +export type SingleAssetJoinInput = BaseJoinInput & { bptOut: TokenAmount; tokenIn: Address; - kind: JoinKind.ExactOutSingleAsset; + kind: JoinKind.SingleAsset; }; -export type ExactOutProportionalJoinInput = BaseJoinInput & { +export type ProportionalJoinInput = BaseJoinInput & { bptOut: TokenAmount; - kind: JoinKind.ExactOutProportional; + kind: JoinKind.Proportional; }; export type JoinInput = | InitJoinInput - | ExactInJoinInput - | ExactOutSingleAssetJoinInput - | ExactOutProportionalJoinInput; + | UnbalancedJoinInput + | SingleAssetJoinInput + | ProportionalJoinInput; // Returned from a join query export type JoinQueryResult = { diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 0bec8cd3..844dca42 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -28,7 +28,7 @@ export class WeightedJoin implements BaseJoin { // TODO - This would need extended to work with relayer // TODO: check inputs - // joinExactOutProportional only works for Weighted v2+ -> should we handle at the SDK level or should we just let the query fail? + // joinProportional only works for Weighted v2+ -> should we handle at the SDK level or should we just let the query fail? const poolTokens = poolState.tokens.map( (t) => new Token(input.chainId, t.address, t.decimals), @@ -45,24 +45,24 @@ export class WeightedJoin implements BaseJoin { ); userData = WeightedEncoder.joinInit(maxAmountsIn); break; - case JoinKind.ExactIn: + case JoinKind.Unbalanced: maxAmountsIn = poolTokens.map( (t) => input.amountsIn.find((a) => a.token.isEqual(t)) ?.amount ?? 0n, ); - userData = WeightedEncoder.joinExactIn(maxAmountsIn, 0n); + userData = WeightedEncoder.joinUnbalanced(maxAmountsIn, 0n); break; - case JoinKind.ExactOutSingleAsset: - userData = WeightedEncoder.joinExactOutSingleAsset( + case JoinKind.SingleAsset: + userData = WeightedEncoder.joinSingleAsset( input.bptOut.amount, poolTokens.findIndex( (t) => t.address === input.tokenIn.toLowerCase(), ), ); break; - case JoinKind.ExactOutProportional: - userData = WeightedEncoder.joinExactOutProportional( + case JoinKind.Proportional: + userData = WeightedEncoder.joinProportional( input.bptOut.amount, ); break; @@ -111,7 +111,7 @@ export class WeightedJoin implements BaseJoin { ); const tokenInIndex = - input.kind === JoinKind.ExactOutSingleAsset + input.kind === JoinKind.SingleAsset ? poolTokens.findIndex( (t) => t.address === input.tokenIn.toLowerCase(), ) @@ -143,31 +143,34 @@ export class WeightedJoin implements BaseJoin { userData = WeightedEncoder.joinInit(maxAmountsIn); break; } - case JoinKind.ExactIn: { + case JoinKind.Unbalanced: { maxAmountsIn = input.amountsIn.map((a) => a.amount); minBptOut = input.slippage.removeFrom(input.bptOut.amount); - userData = WeightedEncoder.joinExactIn(maxAmountsIn, minBptOut); + userData = WeightedEncoder.joinUnbalanced( + maxAmountsIn, + minBptOut, + ); break; } - case JoinKind.ExactOutSingleAsset: + case JoinKind.SingleAsset: if (input.tokenInIndex === undefined) { throw new Error( - 'tokenInIndex must be defined for ExactOutSingleAsset joins', + 'tokenInIndex must be defined for SingleAsset joins', ); } maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ); - userData = WeightedEncoder.joinExactOutSingleAsset( + userData = WeightedEncoder.joinSingleAsset( input.bptOut.amount, input.tokenInIndex, ); break; - case JoinKind.ExactOutProportional: { + case JoinKind.Proportional: { maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ); - userData = WeightedEncoder.joinExactOutProportional( + userData = WeightedEncoder.joinProportional( input.bptOut.amount, ); break; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index c4fc13e4..1267bb7c 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -17,9 +17,9 @@ import { import { BaseJoin, - ExactInJoinInput, - ExactOutProportionalJoinInput, - ExactOutSingleAssetJoinInput, + UnbalancedJoinInput, + ProportionalJoinInput, + SingleAssetJoinInput, JoinKind, PoolState, Slippage, @@ -98,11 +98,11 @@ describe('weighted join test', () => { const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); // perform join query to get expected bpt out - const joinInput: ExactInJoinInput = { + const joinInput: UnbalancedJoinInput = { amountsIn: [amountIn], chainId, rpcUrl, - kind: JoinKind.ExactIn, + kind: JoinKind.Unbalanced, }; const queryResult = await weightedJoin.query( joinInput, @@ -155,11 +155,11 @@ describe('weighted join test', () => { const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); // perform join query to get expected bpt out - const joinInput: ExactInJoinInput = { + const joinInput: UnbalancedJoinInput = { amountsIn: [amountIn], chainId, rpcUrl, - kind: JoinKind.ExactIn, + kind: JoinKind.Unbalanced, joinWithNativeAsset: true, }; const queryResult = await weightedJoin.query( @@ -223,12 +223,12 @@ describe('weighted join test', () => { const tokenIn = '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'; // perform join query to get expected bpt out - const joinInput: ExactOutSingleAssetJoinInput = { + const joinInput: SingleAssetJoinInput = { bptOut: amountOut, tokenIn, chainId, rpcUrl, - kind: JoinKind.ExactOutSingleAsset, + kind: JoinKind.SingleAsset, }; const queryResult = await weightedJoin.query( joinInput, @@ -275,11 +275,11 @@ describe('weighted join test', () => { const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); // perform join query to get expected bpt out - const joinInput: ExactOutProportionalJoinInput = { + const joinInput: ProportionalJoinInput = { bptOut: amountOut, chainId, rpcUrl, - kind: JoinKind.ExactOutProportional, + kind: JoinKind.Proportional, }; const queryResult = await weightedJoin.query( joinInput, From 2ece7206f53d3f36a3768faa2139214d5dac30e6 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 13 Sep 2023 14:59:17 -0300 Subject: [PATCH 037/199] Add initial structure to check inputs --- src/entities/join/weighted/helpers.ts | 38 ++++++++++++++++++++++ src/entities/join/weighted/weightedJoin.ts | 6 ++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts index 2e2a209a..94e5ee09 100644 --- a/src/entities/join/weighted/helpers.ts +++ b/src/entities/join/weighted/helpers.ts @@ -1,3 +1,4 @@ +import { JoinInput, JoinKind, PoolState } from '..'; import { Address } from '../../../types'; export function getJoinParameters({ @@ -24,3 +25,40 @@ export function getJoinParameters({ return [poolId, sender, recipient, joinPoolRequest] as const; } + +export function checkInputs(input: JoinInput, poolState: PoolState) { + switch (input.kind) { + case JoinKind.Init: + checkTokenMismatch( + input.initAmountsIn.map((a) => a.token.address), + poolState.tokens.map((t) => t.address), + ); + break; + case JoinKind.Unbalanced: + checkTokenMismatch( + input.amountsIn.map((a) => a.token.address), + poolState.tokens.map((t) => t.address), + ); + break; + case JoinKind.SingleAsset: + checkTokenMismatch( + [input.tokenIn], + poolState.tokens.map((t) => t.address), + ); + case JoinKind.Proportional: + checkTokenMismatch( + [input.bptOut.token.address.toLowerCase() as Address], + [poolState.address], + ); + default: + break; + } +} + +function checkTokenMismatch(tokensIn: Address[], poolTokens: Address[]) { + for (const tokenIn of tokensIn) { + if (!poolTokens.includes(tokenIn.toLowerCase() as Address)) { + throw new Error(`Token ${tokenIn} not found in pool`); + } + } +} diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 844dca42..7f446f46 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -10,7 +10,7 @@ import { ZERO_ADDRESS, } from '../../../utils'; import { balancerHelpersAbi, vaultAbi } from '../../../abi'; -import { getJoinParameters } from './helpers'; +import { checkInputs, getJoinParameters } from './helpers'; import { BaseJoin, JoinCallInput, @@ -27,8 +27,8 @@ export class WeightedJoin implements BaseJoin { ): Promise { // TODO - This would need extended to work with relayer - // TODO: check inputs - // joinProportional only works for Weighted v2+ -> should we handle at the SDK level or should we just let the query fail? + // TODO: Extend input validation for cases we'd like to check + checkInputs(input, poolState); const poolTokens = poolState.tokens.map( (t) => new Token(input.chainId, t.address, t.decimals), From 4d49b3f0c1d98da8667d62821fb776f796eb1571 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Wed, 13 Sep 2023 16:16:27 -0300 Subject: [PATCH 038/199] Use index provided by the api to sort pool tokens --- src/entities/join/index.ts | 3 ++- src/entities/join/weighted/weightedJoin.ts | 6 +++--- test/weightedJoin.integration.test.ts | 7 ++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index cafa2217..b448a52f 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -17,7 +17,8 @@ export type PoolState = { tokens: { address: Address; decimals: number; - }[]; // already properly sorted in case different versions sort them differently + index: number; + }[]; }; // This will be extended for each pools specific input requirements diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 7f446f46..7b34c7f3 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -30,9 +30,9 @@ export class WeightedJoin implements BaseJoin { // TODO: Extend input validation for cases we'd like to check checkInputs(input, poolState); - const poolTokens = poolState.tokens.map( - (t) => new Token(input.chainId, t.address, t.decimals), - ); + const poolTokens = poolState.tokens + .sort((a, b) => a.index - b.index) + .map((t) => new Token(input.chainId, t.address, t.decimals)); let maxAmountsIn = Array(poolTokens.length).fill(MAX_UINT256); let userData: Address; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 1267bb7c..b8b07453 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -328,7 +328,8 @@ describe('weighted join test', () => { export class MockApi { public async getPool(id: Address): Promise { - let tokens: { address: Address; decimals: number }[] = []; + let tokens: { address: Address; decimals: number; index: number }[] = + []; if ( id === '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014' @@ -337,10 +338,12 @@ export class MockApi { { address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL decimals: 18, + index: 0, }, { address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH decimals: 18, + index: 1, }, ]; } else if ( @@ -351,10 +354,12 @@ export class MockApi { { address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH slot 0 decimals: 18, + index: 0, }, { address: '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP slot 1 decimals: 18, + index: 1, }, ]; } From fb58e4cfc744759fe3119039c2cb6c0b0a5de7b2 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 14 Sep 2023 10:50:44 -0300 Subject: [PATCH 039/199] Apply code review suggestions --- src/entities/join/index.ts | 10 +++++---- src/entities/join/weighted/helpers.ts | 11 ++++++---- src/entities/join/weighted/weightedJoin.ts | 25 +++++++++++++--------- test/weightedJoin.integration.test.ts | 6 +++--- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index b448a52f..7a0af182 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,6 +1,6 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; -import { Address } from '../../types'; +import { Address, Hex } from '../../types'; export enum JoinKind { Init = 'Init', @@ -11,7 +11,7 @@ export enum JoinKind { // Returned from API and used as input export type PoolState = { - id: Address; + id: Hex; address: Address; type: string; tokens: { @@ -26,6 +26,7 @@ export type BaseJoinInput = { chainId: number; rpcUrl?: string; joinWithNativeAsset?: boolean; + fromInternalBalance?: boolean; }; export type InitJoinInput = BaseJoinInput & { @@ -57,10 +58,11 @@ export type JoinInput = // Returned from a join query export type JoinQueryResult = { - id: Address; + id: Hex; joinKind: JoinKind; bptOut: TokenAmount; amountsIn: TokenAmount[]; + fromInternalBalance: boolean; tokenInIndex?: number; }; @@ -73,7 +75,7 @@ export type JoinCallInput = JoinQueryResult & { export interface BaseJoin { query(input: JoinInput, poolState: PoolState): Promise; buildCall(input: JoinCallInput): { - call: Address; + call: Hex; to: Address; value: bigint | undefined; minBptOut: bigint; diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts index 94e5ee09..29144017 100644 --- a/src/entities/join/weighted/helpers.ts +++ b/src/entities/join/weighted/helpers.ts @@ -1,3 +1,4 @@ +import { Hex } from 'viem'; import { JoinInput, JoinKind, PoolState } from '..'; import { Address } from '../../../types'; @@ -8,25 +9,27 @@ export function getJoinParameters({ recipient, maxAmountsIn, userData, + fromInternalBalance, }: { - poolId: Address; + poolId: Hex; assets: readonly Address[]; sender: Address; recipient: Address; maxAmountsIn: readonly bigint[]; - userData: Address; + userData: Hex; + fromInternalBalance: boolean; }) { const joinPoolRequest = { assets, // with BPT maxAmountsIn, // with BPT userData, // wihtout BPT - fromInternalBalance: false, + fromInternalBalance, }; return [poolId, sender, recipient, joinPoolRequest] as const; } -export function checkInputs(input: JoinInput, poolState: PoolState) { +export function validateInputs(input: JoinInput, poolState: PoolState) { switch (input.kind) { case JoinKind.Init: checkTokenMismatch( diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 7b34c7f3..db6f36df 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -1,6 +1,6 @@ import { createPublicClient, encodeFunctionData, http } from 'viem'; import { Token, TokenAmount, WeightedEncoder } from '../../..'; -import { Address } from '../../../types'; +import { Address, Hex } from '../../../types'; import { BALANCER_HELPERS, BALANCER_VAULT, @@ -10,7 +10,7 @@ import { ZERO_ADDRESS, } from '../../../utils'; import { balancerHelpersAbi, vaultAbi } from '../../../abi'; -import { checkInputs, getJoinParameters } from './helpers'; +import { validateInputs, getJoinParameters } from './helpers'; import { BaseJoin, JoinCallInput, @@ -27,14 +27,13 @@ export class WeightedJoin implements BaseJoin { ): Promise { // TODO - This would need extended to work with relayer - // TODO: Extend input validation for cases we'd like to check - checkInputs(input, poolState); + validateInputs(input, poolState); const poolTokens = poolState.tokens .sort((a, b) => a.index - b.index) .map((t) => new Token(input.chainId, t.address, t.decimals)); let maxAmountsIn = Array(poolTokens.length).fill(MAX_UINT256); - let userData: Address; + let userData: Hex; switch (input.kind) { case JoinKind.Init: @@ -87,6 +86,7 @@ export class WeightedJoin implements BaseJoin { recipient: ZERO_ADDRESS, maxAmountsIn, userData, + fromInternalBalance: input.fromInternalBalance ?? false, }); const client = createPublicClient({ @@ -122,24 +122,26 @@ export class WeightedJoin implements BaseJoin { id: poolState.id, bptOut, amountsIn, + fromInternalBalance: input.fromInternalBalance ?? false, tokenInIndex, }; } public buildCall(input: JoinCallInput): { - call: Address; + call: Hex; to: Address; value: bigint | undefined; minBptOut: bigint; maxAmountsIn: bigint[]; } { - let maxAmountsIn = input.amountsIn.map((a) => a.amount); - let minBptOut = input.bptOut.amount; - let userData: Address; + let maxAmountsIn: bigint[]; + let minBptOut: bigint; + let userData: Hex; switch (input.joinKind) { case JoinKind.Init: { maxAmountsIn = input.amountsIn.map((a) => a.amount); + minBptOut = input.bptOut.amount; userData = WeightedEncoder.joinInit(maxAmountsIn); break; } @@ -161,8 +163,9 @@ export class WeightedJoin implements BaseJoin { maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ); + minBptOut = input.bptOut.amount; userData = WeightedEncoder.joinSingleAsset( - input.bptOut.amount, + minBptOut, input.tokenInIndex, ); break; @@ -170,6 +173,7 @@ export class WeightedJoin implements BaseJoin { maxAmountsIn = input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ); + minBptOut = input.bptOut.amount; userData = WeightedEncoder.joinProportional( input.bptOut.amount, ); @@ -184,6 +188,7 @@ export class WeightedJoin implements BaseJoin { recipient: input.recipient, maxAmountsIn, userData, + fromInternalBalance: input.fromInternalBalance, }); const call = encodeFunctionData({ diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index b8b07453..a9f6203e 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -27,7 +27,7 @@ import { TokenAmount, } from '../src/entities'; import { JoinParser } from '../src/entities/join/parser'; -import { Address } from '../src/types'; +import { Address, Hex } from '../src/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; @@ -40,7 +40,7 @@ describe('weighted join test', () => { let rpcUrl: string; let blockNumber: bigint; let client: Client & PublicActions & TestActions & WalletActions; - let poolId: Address; + let poolId: Hex; let poolFromApi: PoolState; let weightedJoin: BaseJoin; @@ -327,7 +327,7 @@ describe('weighted join test', () => { /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Address): Promise { + public async getPool(id: Hex): Promise { let tokens: { address: Address; decimals: number; index: number }[] = []; if ( From 39000671cc1ffd0d169065ea87befd42d35da4f2 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Thu, 14 Sep 2023 10:51:39 -0300 Subject: [PATCH 040/199] Remove unused comment --- src/entities/join/parser.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts index 38579527..7506e603 100644 --- a/src/entities/join/parser.ts +++ b/src/entities/join/parser.ts @@ -19,7 +19,6 @@ export class JoinParser { } public getJoin(poolType: string): BaseJoin { - // TODO - Need to parse return this.poolJoins[poolType]; } } From e0722d92bd3f67828034e342137b920bd2918158 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 14 Sep 2023 19:54:25 +0100 Subject: [PATCH 041/199] refactor: Separate functions that may be common to other pool types so they can be reusued. Simplify(?) encoding/amount logic. --- src/entities/join/index.ts | 19 +- src/entities/join/weighted/helpers.ts | 33 +-- src/entities/join/weighted/weightedJoin.ts | 268 ++++++++++----------- src/entities/types.ts | 21 ++ src/entities/utils/doQueryJoin.ts | 41 ++++ src/entities/utils/getAmounts.ts | 19 ++ src/entities/utils/getSortedTokens.ts | 8 + src/entities/utils/index.ts | 5 + src/entities/utils/parseJoinArgs.ts | 38 +++ src/entities/utils/replaceWrapped.ts | 12 + test/weightedJoin.integration.test.ts | 2 +- 11 files changed, 277 insertions(+), 189 deletions(-) create mode 100644 src/entities/types.ts create mode 100644 src/entities/utils/doQueryJoin.ts create mode 100644 src/entities/utils/getAmounts.ts create mode 100644 src/entities/utils/getSortedTokens.ts create mode 100644 src/entities/utils/index.ts create mode 100644 src/entities/utils/parseJoinArgs.ts create mode 100644 src/entities/utils/replaceWrapped.ts diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index b448a52f..12ea2e2f 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,6 +1,7 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; import { Address } from '../../types'; +import { PoolState } from '../types'; export enum JoinKind { Init = 'Init', @@ -9,27 +10,15 @@ export enum JoinKind { Proportional = 'Proportional', } -// Returned from API and used as input -export type PoolState = { - id: Address; - address: Address; - type: string; - tokens: { - address: Address; - decimals: number; - index: number; - }[]; -}; - // This will be extended for each pools specific input requirements export type BaseJoinInput = { chainId: number; - rpcUrl?: string; + rpcUrl: string; joinWithNativeAsset?: boolean; }; export type InitJoinInput = BaseJoinInput & { - initAmountsIn: TokenAmount[]; + amountsIn: TokenAmount[]; kind: JoinKind.Init; }; @@ -57,7 +46,7 @@ export type JoinInput = // Returned from a join query export type JoinQueryResult = { - id: Address; + poolId: Address; joinKind: JoinKind; bptOut: TokenAmount; amountsIn: TokenAmount[]; diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/helpers.ts index 94e5ee09..741a547c 100644 --- a/src/entities/join/weighted/helpers.ts +++ b/src/entities/join/weighted/helpers.ts @@ -1,39 +1,10 @@ -import { JoinInput, JoinKind, PoolState } from '..'; +import { JoinInput, JoinKind } from '..'; +import { PoolState } from '../../types'; import { Address } from '../../../types'; -export function getJoinParameters({ - poolId, - assets, - sender, - recipient, - maxAmountsIn, - userData, -}: { - poolId: Address; - assets: readonly Address[]; - sender: Address; - recipient: Address; - maxAmountsIn: readonly bigint[]; - userData: Address; -}) { - const joinPoolRequest = { - assets, // with BPT - maxAmountsIn, // with BPT - userData, // wihtout BPT - fromInternalBalance: false, - }; - - return [poolId, sender, recipient, joinPoolRequest] as const; -} - export function checkInputs(input: JoinInput, poolState: PoolState) { switch (input.kind) { case JoinKind.Init: - checkTokenMismatch( - input.initAmountsIn.map((a) => a.token.address), - poolState.tokens.map((t) => t.address), - ); - break; case JoinKind.Unbalanced: checkTokenMismatch( input.amountsIn.map((a) => a.token.address), diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 7b34c7f3..89decc12 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -1,128 +1,67 @@ -import { createPublicClient, encodeFunctionData, http } from 'viem'; +import { encodeFunctionData } from 'viem'; import { Token, TokenAmount, WeightedEncoder } from '../../..'; import { Address } from '../../../types'; -import { - BALANCER_HELPERS, - BALANCER_VAULT, - CHAINS, - MAX_UINT256, - NATIVE_ASSETS, - ZERO_ADDRESS, -} from '../../../utils'; -import { balancerHelpersAbi, vaultAbi } from '../../../abi'; -import { checkInputs, getJoinParameters } from './helpers'; +import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; +import { vaultAbi } from '../../../abi'; +import { checkInputs } from './helpers'; import { BaseJoin, JoinCallInput, JoinInput, JoinKind, JoinQueryResult, - PoolState, } from '..'; +import { PoolState, Amounts } from '../../types'; +import { + doQueryJoin, + getAmounts, + parseJoinArgs, + getSortedTokens, +} from '../../utils'; export class WeightedJoin implements BaseJoin { public async query( input: JoinInput, poolState: PoolState, ): Promise { - // TODO - This would need extended to work with relayer - - // TODO: Extend input validation for cases we'd like to check checkInputs(input, poolState); - const poolTokens = poolState.tokens - .sort((a, b) => a.index - b.index) - .map((t) => new Token(input.chainId, t.address, t.decimals)); - let maxAmountsIn = Array(poolTokens.length).fill(MAX_UINT256); - let userData: Address; + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); - switch (input.kind) { - case JoinKind.Init: - maxAmountsIn = poolTokens.map( - (t) => - input.initAmountsIn.find((a) => a.token.isEqual(t)) - ?.amount ?? 0n, - ); - userData = WeightedEncoder.joinInit(maxAmountsIn); - break; - case JoinKind.Unbalanced: - maxAmountsIn = poolTokens.map( - (t) => - input.amountsIn.find((a) => a.token.isEqual(t)) - ?.amount ?? 0n, - ); - userData = WeightedEncoder.joinUnbalanced(maxAmountsIn, 0n); - break; - case JoinKind.SingleAsset: - userData = WeightedEncoder.joinSingleAsset( - input.bptOut.amount, - poolTokens.findIndex( - (t) => t.address === input.tokenIn.toLowerCase(), - ), - ); - break; - case JoinKind.Proportional: - userData = WeightedEncoder.joinProportional( - input.bptOut.amount, - ); - break; - } + const amounts = this.getAmountsQuery(sortedTokens, input); - let tokensIn = [...poolTokens]; - // replace wrapped token with native asset if needed - if (input.joinWithNativeAsset) { - tokensIn = poolTokens.map((token) => { - if (token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId])) { - return new Token(input.chainId, ZERO_ADDRESS, 18); - } else { - return token; - } - }); - } + const userData = this.encodeUserData(input.kind, amounts); - const queryArgs = getJoinParameters({ + const queryArgs = parseJoinArgs({ + joinWithNativeAsset: !!input.joinWithNativeAsset, + chainId: input.chainId, + sortedTokens, poolId: poolState.id, - assets: tokensIn.map((t) => t.address), sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, - maxAmountsIn, + maxAmountsIn: amounts.maxAmountsIn, userData, }); - const client = createPublicClient({ - transport: http(input.rpcUrl), - chain: CHAINS[input.chainId], - }); - - const { - result: [queryBptOut, queryAmountsIn], - } = await client.simulateContract({ - address: BALANCER_HELPERS[input.chainId], - abi: balancerHelpersAbi, - functionName: 'queryJoin', - args: queryArgs, - }); + const queryResult = await doQueryJoin( + input.rpcUrl, + input.chainId, + queryArgs, + ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptOut = TokenAmount.fromRawAmount(bpt, queryBptOut); + const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); - const amountsIn = queryAmountsIn.map((a, i) => - TokenAmount.fromRawAmount(tokensIn[i], a), + const amountsIn = queryResult.amountsIn.map((a, i) => + TokenAmount.fromRawAmount(sortedTokens[i], a), ); - const tokenInIndex = - input.kind === JoinKind.SingleAsset - ? poolTokens.findIndex( - (t) => t.address === input.tokenIn.toLowerCase(), - ) - : undefined; - return { joinKind: input.kind, - id: poolState.id, + poolId: poolState.id, bptOut, amountsIn, - tokenInIndex, + tokenInIndex: amounts.tokenInIndex, }; } @@ -133,76 +72,121 @@ export class WeightedJoin implements BaseJoin { minBptOut: bigint; maxAmountsIn: bigint[]; } { - let maxAmountsIn = input.amountsIn.map((a) => a.amount); - let minBptOut = input.bptOut.amount; - let userData: Address; + const amounts = this.getAmountsCall(input); - switch (input.joinKind) { - case JoinKind.Init: { - maxAmountsIn = input.amountsIn.map((a) => a.amount); - userData = WeightedEncoder.joinInit(maxAmountsIn); - break; - } - case JoinKind.Unbalanced: { - maxAmountsIn = input.amountsIn.map((a) => a.amount); - minBptOut = input.slippage.removeFrom(input.bptOut.amount); - userData = WeightedEncoder.joinUnbalanced( - maxAmountsIn, - minBptOut, - ); - break; - } - case JoinKind.SingleAsset: - if (input.tokenInIndex === undefined) { - throw new Error( - 'tokenInIndex must be defined for SingleAsset joins', - ); - } - maxAmountsIn = input.amountsIn.map((a) => - input.slippage.applyTo(a.amount), - ); - userData = WeightedEncoder.joinSingleAsset( - input.bptOut.amount, - input.tokenInIndex, - ); - break; - case JoinKind.Proportional: { - maxAmountsIn = input.amountsIn.map((a) => - input.slippage.applyTo(a.amount), - ); - userData = WeightedEncoder.joinProportional( - input.bptOut.amount, - ); - break; - } - } + const userData = this.encodeUserData(input.joinKind, amounts); - const queryArgs = getJoinParameters({ - poolId: input.id, - assets: input.amountsIn.map((a) => a.token.address), - sender: input.sender, - recipient: input.recipient, - maxAmountsIn, + const args = parseJoinArgs({ + ...input, + sortedTokens: input.amountsIn.map((a) => a.token), + maxAmountsIn: amounts.maxAmountsIn, userData, }); const call = encodeFunctionData({ abi: vaultAbi, functionName: 'joinPool', - args: queryArgs, + args, }); const value = input.amountsIn.find( (a) => a.token.address === ZERO_ADDRESS, )?.amount; - // Encode data return { call, to: BALANCER_VAULT, value, - minBptOut, - maxAmountsIn, + minBptOut: amounts.minimumBpt, + maxAmountsIn: amounts.maxAmountsIn, }; } + + private getAmountsQuery(poolTokens: Token[], input: JoinInput): Amounts { + switch (input.kind) { + case JoinKind.Init: + case JoinKind.Unbalanced: { + return { + minimumBpt: 0n, + maxAmountsIn: getAmounts(poolTokens, input.amountsIn), + tokenInIndex: undefined, + }; + } + case JoinKind.SingleAsset: { + const tokenInIndex = poolTokens.findIndex( + (t) => t.address === input.tokenIn.toLowerCase(), + ); + if (tokenInIndex === -1) + throw Error("Can't find index of SingleAsset"); + const maxAmountsIn = Array(poolTokens.length).fill(0n); + maxAmountsIn[tokenInIndex] = MAX_UINT256; + return { + minimumBpt: input.bptOut.amount, + maxAmountsIn, + tokenInIndex, + }; + } + case JoinKind.Proportional: { + return { + minimumBpt: input.bptOut.amount, + maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), + tokenInIndex: undefined, + }; + } + default: + throw Error('Unsupported Join Type'); + } + } + + private getAmountsCall(input: JoinCallInput): Amounts { + switch (input.joinKind) { + case JoinKind.Init: + case JoinKind.Unbalanced: { + const minimumBpt = input.slippage.removeFrom( + input.bptOut.amount, + ); + return { + minimumBpt, + maxAmountsIn: input.amountsIn.map((a) => a.amount), + tokenInIndex: input.tokenInIndex, + }; + } + case JoinKind.SingleAsset: + case JoinKind.Proportional: { + return { + minimumBpt: input.bptOut.amount, + maxAmountsIn: input.amountsIn.map((a) => + input.slippage.applyTo(a.amount), + ), + tokenInIndex: input.tokenInIndex, + }; + } + default: + throw Error('Unsupported Join Type'); + } + } + + private encodeUserData(kind: JoinKind, amounts: Amounts): Address { + switch (kind) { + case JoinKind.Init: + return WeightedEncoder.joinInit(amounts.maxAmountsIn); + case JoinKind.Unbalanced: + return WeightedEncoder.joinUnbalanced( + amounts.maxAmountsIn, + amounts.minimumBpt, + ); + case JoinKind.SingleAsset: { + if (amounts.tokenInIndex === undefined) throw Error('No Index'); + return WeightedEncoder.joinSingleAsset( + amounts.minimumBpt, + amounts.tokenInIndex, + ); + } + case JoinKind.Proportional: { + return WeightedEncoder.joinProportional(amounts.minimumBpt); + } + default: + throw Error('Unsupported Join Type'); + } + } } diff --git a/src/entities/types.ts b/src/entities/types.ts new file mode 100644 index 00000000..e674ce63 --- /dev/null +++ b/src/entities/types.ts @@ -0,0 +1,21 @@ +import { Address } from '../types'; + +// Returned from API and used as input +export type PoolState = { + id: Address; + address: Address; + type: string; + tokens: TokenApi[]; +}; + +export type TokenApi = { + address: Address; + decimals: number; + index: number; +}; + +export type Amounts = { + maxAmountsIn: bigint[]; + tokenInIndex: number | undefined; + minimumBpt: bigint; +}; diff --git a/src/entities/utils/doQueryJoin.ts b/src/entities/utils/doQueryJoin.ts new file mode 100644 index 00000000..b2ed6635 --- /dev/null +++ b/src/entities/utils/doQueryJoin.ts @@ -0,0 +1,41 @@ +import { createPublicClient, http } from 'viem'; +import { Address } from '../../types'; +import { BALANCER_HELPERS, CHAINS } from '../../utils'; +import { balancerHelpersAbi } from '../../abi'; + +export async function doQueryJoin( + rpcUrl: string, + chainId: number, + args: readonly [ + Address, + Address, + Address, + { + assets: readonly Address[]; + maxAmountsIn: readonly bigint[]; + userData: Address; + fromInternalBalance: boolean; + }, + ], +): Promise<{ + bptOut: bigint; + amountsIn: readonly bigint[]; +}> { + const client = createPublicClient({ + transport: http(rpcUrl), + chain: CHAINS[chainId], + }); + + const { + result: [bptOut, amountsIn], + } = await client.simulateContract({ + address: BALANCER_HELPERS[chainId], + abi: balancerHelpersAbi, + functionName: 'queryJoin', + args, + }); + return { + bptOut, + amountsIn, + }; +} diff --git a/src/entities/utils/getAmounts.ts b/src/entities/utils/getAmounts.ts new file mode 100644 index 00000000..d2da549d --- /dev/null +++ b/src/entities/utils/getAmounts.ts @@ -0,0 +1,19 @@ +import { Token } from '../token'; +import { TokenAmount } from '../tokenAmount'; + +/** + * Get amounts from array of TokenAmounts returning default if not a value for tokens. + * @param tokens + * @param amounts + * @param defaultAmount + * @returns + */ +export function getAmounts( + tokens: Token[], + amounts: TokenAmount[], + defaultAmount = 0n, +): bigint[] { + return tokens.map( + (t) => amounts.find((a) => a.token.isEqual(t))?.amount ?? defaultAmount, + ); +} diff --git a/src/entities/utils/getSortedTokens.ts b/src/entities/utils/getSortedTokens.ts new file mode 100644 index 00000000..4da9d167 --- /dev/null +++ b/src/entities/utils/getSortedTokens.ts @@ -0,0 +1,8 @@ +import { Token } from '../token'; +import { TokenApi } from '../types'; + +export function getSortedTokens(tokens: TokenApi[], chainId: number): Token[] { + return tokens + .sort((a, b) => a.index - b.index) + .map((t) => new Token(chainId, t.address, t.decimals)); +} diff --git a/src/entities/utils/index.ts b/src/entities/utils/index.ts new file mode 100644 index 00000000..17362bd0 --- /dev/null +++ b/src/entities/utils/index.ts @@ -0,0 +1,5 @@ +export * from './doQueryJoin'; +export * from './getAmounts'; +export * from './getSortedTokens'; +export * from './parseJoinArgs'; +export * from './replaceWrapped'; diff --git a/src/entities/utils/parseJoinArgs.ts b/src/entities/utils/parseJoinArgs.ts new file mode 100644 index 00000000..3446d784 --- /dev/null +++ b/src/entities/utils/parseJoinArgs.ts @@ -0,0 +1,38 @@ +import { Address } from '../../types'; +import { Token } from '../token'; +import { replaceWrapped } from './replaceWrapped'; + +export function parseJoinArgs({ + joinWithNativeAsset, + chainId, + sortedTokens: poolTokens, + poolId, + sender, + recipient, + maxAmountsIn, + userData, +}: { + chainId?: number; + joinWithNativeAsset?: boolean; + sortedTokens: Token[]; + poolId: Address; + sender: Address; + recipient: Address; + maxAmountsIn: readonly bigint[]; + userData: Address; +}) { + // replace wrapped token with native asset if needed + const tokensIn = + joinWithNativeAsset && chainId + ? replaceWrapped([...poolTokens], chainId) + : [...poolTokens]; + + const joinPoolRequest = { + assets: tokensIn.map((t) => t.address), // with BPT + maxAmountsIn, // with BPT + userData, // wihtout BPT + fromInternalBalance: false, // TODO + }; + + return [poolId, sender, recipient, joinPoolRequest] as const; +} diff --git a/src/entities/utils/replaceWrapped.ts b/src/entities/utils/replaceWrapped.ts new file mode 100644 index 00000000..2da576d4 --- /dev/null +++ b/src/entities/utils/replaceWrapped.ts @@ -0,0 +1,12 @@ +import { Token } from '../token'; +import { NATIVE_ASSETS, ZERO_ADDRESS } from '../../utils'; + +export function replaceWrapped(tokens: Token[], chainId: number): Token[] { + return tokens.map((token) => { + if (token.isUnderlyingEqual(NATIVE_ASSETS[chainId])) { + return new Token(chainId, ZERO_ADDRESS, 18); + } else { + return token; + } + }); +} diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index b8b07453..094f5b38 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -21,13 +21,13 @@ import { ProportionalJoinInput, SingleAssetJoinInput, JoinKind, - PoolState, Slippage, Token, TokenAmount, } from '../src/entities'; import { JoinParser } from '../src/entities/join/parser'; import { Address } from '../src/types'; +import { PoolState } from '../src/entities/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; From 0d2271fa033458bbf9b722f95accdb2ff0f8b7a5 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Mon, 18 Sep 2023 13:36:18 -0300 Subject: [PATCH 042/199] Rename joinWtihNativeAsset to useNativeAssetAsWrappedAmountIn --- src/entities/join/index.ts | 2 +- src/entities/join/weighted/weightedJoin.ts | 2 +- test/weightedJoin.integration.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 7a0af182..6ce7ce3c 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -25,7 +25,7 @@ export type PoolState = { export type BaseJoinInput = { chainId: number; rpcUrl?: string; - joinWithNativeAsset?: boolean; + useNativeAssetAsWrappedAmountIn?: boolean; fromInternalBalance?: boolean; }; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index db6f36df..74cc7739 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -69,7 +69,7 @@ export class WeightedJoin implements BaseJoin { let tokensIn = [...poolTokens]; // replace wrapped token with native asset if needed - if (input.joinWithNativeAsset) { + if (input.useNativeAssetAsWrappedAmountIn) { tokensIn = poolTokens.map((token) => { if (token.isUnderlyingEqual(NATIVE_ASSETS[input.chainId])) { return new Token(input.chainId, ZERO_ADDRESS, 18); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index a9f6203e..f3c0b316 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -160,7 +160,7 @@ describe('weighted join test', () => { chainId, rpcUrl, kind: JoinKind.Unbalanced, - joinWithNativeAsset: true, + useNativeAssetAsWrappedAmountIn: true, }; const queryResult = await weightedJoin.query( joinInput, From c55ce8963c71981c78cd57a6d4be82327893ffb3 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 19 Sep 2023 11:48:19 +0100 Subject: [PATCH 043/199] refactor: Address review comments. --- src/data/types.ts | 7 +++++-- src/entities/join/index.ts | 2 +- .../join/weighted/{helpers.ts => validateInputs.ts} | 0 src/entities/join/weighted/weightedJoin.ts | 2 +- src/entities/types.ts | 9 ++------- src/entities/utils/getSortedTokens.ts | 7 +++++-- 6 files changed, 14 insertions(+), 13 deletions(-) rename src/entities/join/weighted/{helpers.ts => validateInputs.ts} (100%) diff --git a/src/data/types.ts b/src/data/types.ts index 34476dac..fa08eb3d 100644 --- a/src/data/types.ts +++ b/src/data/types.ts @@ -118,12 +118,15 @@ export interface RawFxPool extends RawBasePool { epsilon: HumanAmount; } -export interface RawPoolToken { +export interface MinimalToken { address: Address; + decimals: number; index: number; +} + +export interface RawPoolToken extends MinimalToken { symbol: string; name: string; - decimals: number; balance: HumanAmount; } diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 4af5c3c1..33449381 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -47,7 +47,7 @@ export type JoinInput = // Returned from a join query export type JoinQueryResult = { - poolId: Address; + poolId: Hex; joinKind: JoinKind; bptOut: TokenAmount; amountsIn: TokenAmount[]; diff --git a/src/entities/join/weighted/helpers.ts b/src/entities/join/weighted/validateInputs.ts similarity index 100% rename from src/entities/join/weighted/helpers.ts rename to src/entities/join/weighted/validateInputs.ts diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 196d4feb..7510be7a 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -3,7 +3,7 @@ import { Token, TokenAmount, WeightedEncoder } from '../../..'; import { Address, Hex } from '../../../types'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; -import { validateInputs } from './helpers'; +import { validateInputs } from './validateInputs'; import { BaseJoin, JoinCallInput, diff --git a/src/entities/types.ts b/src/entities/types.ts index 1e100edf..1419e60c 100644 --- a/src/entities/types.ts +++ b/src/entities/types.ts @@ -1,3 +1,4 @@ +import { MinimalToken } from '../data'; import { Address, Hex } from '../types'; // Returned from API and used as input @@ -5,13 +6,7 @@ export type PoolState = { id: Hex; address: Address; type: string; - tokens: TokenApi[]; -}; - -export type TokenApi = { - address: Address; - decimals: number; - index: number; + tokens: MinimalToken[]; }; export type Amounts = { diff --git a/src/entities/utils/getSortedTokens.ts b/src/entities/utils/getSortedTokens.ts index 4da9d167..3d28df09 100644 --- a/src/entities/utils/getSortedTokens.ts +++ b/src/entities/utils/getSortedTokens.ts @@ -1,7 +1,10 @@ +import { MinimalToken } from '../../data/types'; import { Token } from '../token'; -import { TokenApi } from '../types'; -export function getSortedTokens(tokens: TokenApi[], chainId: number): Token[] { +export function getSortedTokens( + tokens: MinimalToken[], + chainId: number, +): Token[] { return tokens .sort((a, b) => a.index - b.index) .map((t) => new Token(chainId, t.address, t.decimals)); From 8f64c6a789bce0e04954c9569d626378d190ecf0 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 19 Sep 2023 12:06:28 +0100 Subject: [PATCH 044/199] refactor: Single point to santise tokens in check. --- src/entities/join/weighted/validateInputs.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/entities/join/weighted/validateInputs.ts b/src/entities/join/weighted/validateInputs.ts index 43c2bfac..e6f40579 100644 --- a/src/entities/join/weighted/validateInputs.ts +++ b/src/entities/join/weighted/validateInputs.ts @@ -18,7 +18,7 @@ export function validateInputs(input: JoinInput, poolState: PoolState) { ); case JoinKind.Proportional: checkTokenMismatch( - [input.bptOut.token.address.toLowerCase() as Address], + [input.bptOut.token.address], [poolState.address], ); default: @@ -27,8 +27,10 @@ export function validateInputs(input: JoinInput, poolState: PoolState) { } function checkTokenMismatch(tokensIn: Address[], poolTokens: Address[]) { - for (const tokenIn of tokensIn) { - if (!poolTokens.includes(tokenIn.toLowerCase() as Address)) { + const sanitisedTokensIn = tokensIn.map((t) => t.toLowerCase() as Address); + const sanitisedPoolTokens = poolTokens.map((t) => t.toLowerCase()); + for (const tokenIn of sanitisedTokensIn) { + if (!sanitisedPoolTokens.includes(tokenIn)) { throw new Error(`Token ${tokenIn} not found in pool`); } } From 45d76dd0b9ecfcdea827d3f54f91fdf3594e90e9 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 19 Sep 2023 15:11:48 +0100 Subject: [PATCH 045/199] chore: Merge common refactor branch and fix conflicts. --- src/entities/encoders/weighted.ts | 1 - src/entities/exit/types.ts | 6 +- src/entities/exit/weighted/helpers.ts | 29 --- src/entities/exit/weighted/weightedExit.ts | 230 +++++++++++---------- src/entities/index.ts | 1 + src/entities/join/weighted/weightedJoin.ts | 11 +- src/entities/types.ts | 8 +- src/entities/utils/doQueryExit.ts | 33 +++ src/entities/utils/parseExitArgs.ts | 41 ++++ src/entities/utils/parseJoinArgs.ts | 6 +- test/weightedExit.integration.test.ts | 14 +- 11 files changed, 222 insertions(+), 158 deletions(-) delete mode 100644 src/entities/exit/weighted/helpers.ts create mode 100644 src/entities/utils/doQueryExit.ts create mode 100644 src/entities/utils/parseExitArgs.ts diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index 45aa9888..5882d43a 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -15,7 +15,6 @@ export enum WeightedPoolExitKind { MANAGEMENT_FEE_TOKENS_OUT = 3, } -// TODO: rename functions after deciding on the naming convention export class WeightedEncoder { /** * Cannot be constructed. diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index dde2123c..2e50da4d 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -1,7 +1,7 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; import { Address } from '../../types'; -import { PoolState } from '../utils'; +import { PoolState } from '../types'; export enum ExitKind { UNBALANCED = 'UNBALANCED', // exitExactOut @@ -12,8 +12,9 @@ export enum ExitKind { // This will be extended for each pools specific output requirements export type BaseExitInput = { chainId: number; - rpcUrl?: string; + rpcUrl: string; exitWithNativeAsset?: boolean; + toInternalBalance?: boolean; }; export type UnbalancedExitInput = BaseExitInput & { @@ -44,6 +45,7 @@ export type ExitQueryResult = { bptIn: TokenAmount; amountsOut: TokenAmount[]; tokenOutIndex?: number; + toInternalBalance?: boolean; }; export type ExitCallInput = ExitQueryResult & { diff --git a/src/entities/exit/weighted/helpers.ts b/src/entities/exit/weighted/helpers.ts deleted file mode 100644 index d040f4fb..00000000 --- a/src/entities/exit/weighted/helpers.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Address } from '../../../types'; -import { ExitPoolRequest } from '../types'; - -export function getExitParameters({ - poolId, - assets, - sender, - recipient, - minAmountsOut, - userData, - toInternalBalance, -}: { - poolId: Address; - assets: Address[]; - sender: Address; - recipient: Address; - minAmountsOut: bigint[]; - userData: Address; - toInternalBalance: boolean; -}) { - const exitPoolRequest: ExitPoolRequest = { - assets, // with BPT - minAmountsOut, // with BPT - userData, // wihtout BPT - toInternalBalance, - }; - - return [poolId, sender, recipient, exitPoolRequest] as const; -} diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index f8e93ad6..d26b3aef 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -1,15 +1,9 @@ -import { createPublicClient, encodeFunctionData, http } from 'viem'; +import { encodeFunctionData } from 'viem'; import { Token, TokenAmount, WeightedEncoder } from '../../..'; import { Address } from '../../../types'; -import { - BALANCER_HELPERS, - BALANCER_VAULT, - CHAINS, - MAX_UINT256, - ZERO_ADDRESS, -} from '../../../utils'; -import { balancerHelpersAbi, vaultAbi } from '../../../abi'; -import { getExitParameters } from './helpers'; +import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; +import { vaultAbi } from '../../../abi'; +import { parseExitArgs } from '../../utils/parseExitArgs'; import { BaseExit, BuildOutput, @@ -18,143 +12,98 @@ import { ExitKind, ExitQueryResult, } from '../types'; -import { replaceWrapped } from '../../utils'; -import { PoolState } from '../../types'; +import { getSortedTokens } from '../../utils'; +import { PoolState, AmountsExit } from '../../types'; +import { doQueryExit } from '../../utils/doQueryExit'; export class WeightedExit implements BaseExit { public async query( input: ExitInput, poolState: PoolState, ): Promise { - const poolTokens = poolState.tokens.map( - (t) => new Token(input.chainId, t.address, t.decimals), - ); - let minAmountsOut = Array(poolTokens.length).fill(0n); - let userData: Address; + // TODO - Validate inputs - switch (input.kind) { - case ExitKind.UNBALANCED: - minAmountsOut = poolTokens.map( - (t) => - input.amountsOut.find((a) => a.token.isEqual(t)) - ?.amount ?? 0n, - ); - userData = WeightedEncoder.exitUnbalanced( - minAmountsOut, - MAX_UINT256, - ); - break; - case ExitKind.SINGLE_ASSET: - userData = WeightedEncoder.exitSingleAsset( - input.bptIn.amount, - poolTokens.findIndex( - (t) => t.address === input.tokenOut.toLowerCase(), - ), - ); - break; - case ExitKind.PROPORTIONAL: - userData = WeightedEncoder.exitProportional(input.bptIn.amount); - break; - } + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); - let tokensOut = [...poolTokens]; - // replace wrapped token with native asset if needed - if (input.exitWithNativeAsset) - tokensOut = replaceWrapped(poolTokens, input.chainId); + const amounts = this.getAmountsQuery(sortedTokens, input); - const queryArgs = getExitParameters({ + const userData = this.encodeUserData(input.kind, amounts); + + const queryArgs = parseExitArgs({ + // TODO - Wrapped native exit? poolId: poolState.id, - assets: tokensOut.map((t) => t.address), + sortedTokens, sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, - minAmountsOut, + minAmountsOut: amounts.minAmountsOut, userData, - toInternalBalance: false, // TODO - Should we make this part of input? - }); - - const client = createPublicClient({ - transport: http(input.rpcUrl), - chain: CHAINS[input.chainId], + toInternalBalance: !!input.toInternalBalance, }); - const { - result: [queryBptIn, queryAmountsOut], - } = await client.simulateContract({ - address: BALANCER_HELPERS[input.chainId], - abi: balancerHelpersAbi, - functionName: 'queryExit', - args: queryArgs, - }); + const queryResult = await doQueryExit( + input.rpcUrl, + input.chainId, + queryArgs, + ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryBptIn); + const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); - const amountsOut = queryAmountsOut.map((a, i) => - TokenAmount.fromRawAmount(tokensOut[i], a), + const amountsOut = queryResult.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(sortedTokens[i], a), ); - const tokenOutIndex = - input.kind === ExitKind.SINGLE_ASSET - ? poolTokens.findIndex( - (t) => t.address === input.tokenOut.toLowerCase(), - ) - : undefined; - return { exitKind: input.kind, id: poolState.id, bptIn, amountsOut, - tokenOutIndex, + tokenOutIndex: amounts.tokenOutIndex, }; } - public buildCall(input: ExitCallInput): BuildOutput { - let minAmountsOut: bigint[]; - let maxBptIn: bigint; - let userData: Address; - - switch (input.exitKind) { + private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { + switch (input.kind) { case ExitKind.UNBALANCED: - minAmountsOut = input.amountsOut.map((a) => a.amount); - maxBptIn = input.slippage.applyTo(input.bptIn.amount); - userData = WeightedEncoder.exitUnbalanced( - minAmountsOut, - maxBptIn, - ); - break; + return { + minAmountsOut: tokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ), + tokenOutIndex: undefined, + maxBptAmountIn: MAX_UINT256, + }; case ExitKind.SINGLE_ASSET: - if (input.tokenOutIndex === undefined) { - throw new Error( - 'tokenOutIndex must be defined for SINGLE_ASSET exit', - ); - } - minAmountsOut = input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount), - ); - maxBptIn = input.bptIn.amount; - userData = WeightedEncoder.exitSingleAsset( - maxBptIn, - input.tokenOutIndex, - ); - break; + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: tokens.findIndex( + (t) => t.address === input.tokenOut.toLowerCase(), + ), + maxBptAmountIn: input.bptIn.amount, + }; case ExitKind.PROPORTIONAL: - minAmountsOut = input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount), - ); - maxBptIn = input.bptIn.amount; - userData = WeightedEncoder.exitProportional(input.bptIn.amount); - break; + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: undefined, + maxBptAmountIn: input.bptIn.amount, + }; } + } + + public buildCall(input: ExitCallInput): BuildOutput { + const amounts = this.getAmountsCall(input); + + const userData = this.encodeUserData(input.exitKind, amounts); - const queryArgs = getExitParameters({ + const queryArgs = parseExitArgs({ poolId: input.id, - assets: input.amountsOut.map((a) => a.token.address), + sortedTokens: input.amountsOut.map((a) => a.token), sender: input.sender, recipient: input.recipient, - minAmountsOut, + minAmountsOut: amounts.minAmountsOut, userData, - toInternalBalance: false, // TODO - Should we make this part of input? + toInternalBalance: !!input.toInternalBalance, }); const call = encodeFunctionData({ @@ -163,13 +112,68 @@ export class WeightedExit implements BaseExit { args: queryArgs, }); - // Encode data return { call, to: BALANCER_VAULT, value: 0n, - maxBptIn, - minAmountsOut, + maxBptIn: amounts.maxBptAmountIn, + minAmountsOut: amounts.minAmountsOut, }; } + + private getAmountsCall(input: ExitCallInput): AmountsExit { + switch (input.exitKind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: input.amountsOut.map((a) => a.amount), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), + }; + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + 'tokenOutIndex must be defined for SINGLE_ASSET exit', + ); + } + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + default: + throw Error('Unsupported Exit Type'); + } + } + + private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + switch (kind) { + case ExitKind.UNBALANCED: + return WeightedEncoder.exitUnbalanced( + amounts.minAmountsOut, + amounts.maxBptAmountIn, + ); + case ExitKind.SINGLE_ASSET: + if (amounts.tokenOutIndex === undefined) + throw Error('No Index'); + + return WeightedEncoder.exitSingleAsset( + amounts.maxBptAmountIn, + amounts.tokenOutIndex, + ); + case ExitKind.PROPORTIONAL: + return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); + default: + throw Error('Unsupported Exit Type'); + } + } } diff --git a/src/entities/index.ts b/src/entities/index.ts index 4064f6f5..c626c3bb 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -8,3 +8,4 @@ export * from './token'; export * from './tokenAmount'; export * from './pools/'; export * from './utils'; +export * from './types'; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index a82c38b4..465c824c 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -11,7 +11,7 @@ import { JoinKind, JoinQueryResult, } from '..'; -import { PoolState, Amounts } from '../../types'; +import { PoolState, AmountsJoin } from '../../types'; import { doQueryJoin, getAmounts, @@ -106,7 +106,10 @@ export class WeightedJoin implements BaseJoin { }; } - private getAmountsQuery(poolTokens: Token[], input: JoinInput): Amounts { + private getAmountsQuery( + poolTokens: Token[], + input: JoinInput, + ): AmountsJoin { switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: { @@ -142,7 +145,7 @@ export class WeightedJoin implements BaseJoin { } } - private getAmountsCall(input: JoinCallInput): Amounts { + private getAmountsCall(input: JoinCallInput): AmountsJoin { switch (input.joinKind) { case JoinKind.Init: case JoinKind.Unbalanced: { @@ -170,7 +173,7 @@ export class WeightedJoin implements BaseJoin { } } - private encodeUserData(kind: JoinKind, amounts: Amounts): Address { + private encodeUserData(kind: JoinKind, amounts: AmountsJoin): Address { switch (kind) { case JoinKind.Init: return WeightedEncoder.joinInit(amounts.maxAmountsIn); diff --git a/src/entities/types.ts b/src/entities/types.ts index 1419e60c..d1286ebf 100644 --- a/src/entities/types.ts +++ b/src/entities/types.ts @@ -9,8 +9,14 @@ export type PoolState = { tokens: MinimalToken[]; }; -export type Amounts = { +export type AmountsJoin = { maxAmountsIn: bigint[]; tokenInIndex: number | undefined; minimumBpt: bigint; }; + +export type AmountsExit = { + minAmountsOut: bigint[]; + tokenOutIndex: number | undefined; + maxBptAmountIn: bigint; +}; diff --git a/src/entities/utils/doQueryExit.ts b/src/entities/utils/doQueryExit.ts new file mode 100644 index 00000000..1cd1ecf2 --- /dev/null +++ b/src/entities/utils/doQueryExit.ts @@ -0,0 +1,33 @@ +import { createPublicClient, http } from 'viem'; +import { Address } from '../../types'; +import { BALANCER_HELPERS, CHAINS } from '../../utils'; +import { balancerHelpersAbi } from '../../abi'; +import { ExitPoolRequest } from '../exit'; + +export async function doQueryExit( + rpcUrl: string, + chainId: number, + args: readonly [Address, Address, Address, ExitPoolRequest], +): Promise<{ + bptIn: bigint; + amountsOut: readonly bigint[]; +}> { + const client = createPublicClient({ + transport: http(rpcUrl), + chain: CHAINS[chainId], + }); + + const { + result: [bptIn, amountsOut], + } = await client.simulateContract({ + address: BALANCER_HELPERS[chainId], + abi: balancerHelpersAbi, + functionName: 'queryExit', + args, + }); + + return { + bptIn, + amountsOut, + }; +} diff --git a/src/entities/utils/parseExitArgs.ts b/src/entities/utils/parseExitArgs.ts new file mode 100644 index 00000000..5d63e427 --- /dev/null +++ b/src/entities/utils/parseExitArgs.ts @@ -0,0 +1,41 @@ +import { Address } from '../../types'; +import { Token } from '../token'; +import { ExitPoolRequest } from '../exit/types'; +import { replaceWrapped } from './replaceWrapped'; + +export function parseExitArgs({ + chainId, + useNativeAssetAsWrappedAmountIn, + sortedTokens, + poolId, + sender, + recipient, + minAmountsOut, + userData, + toInternalBalance, +}: { + chainId?: number; + useNativeAssetAsWrappedAmountIn?: boolean; + sortedTokens: Token[]; + poolId: Address; + sender: Address; + recipient: Address; + minAmountsOut: bigint[]; + userData: Address; + toInternalBalance: boolean; +}) { + // replace wrapped token with native asset if needed + const tokensOut = + chainId && useNativeAssetAsWrappedAmountIn + ? replaceWrapped([...sortedTokens], chainId) + : [...sortedTokens]; + + const exitPoolRequest: ExitPoolRequest = { + assets: tokensOut.map((t) => t.address), // with BPT + minAmountsOut, // with BPT + userData, // wihtout BPT + toInternalBalance, + }; + + return [poolId, sender, recipient, exitPoolRequest] as const; +} diff --git a/src/entities/utils/parseJoinArgs.ts b/src/entities/utils/parseJoinArgs.ts index 6d44acea..78e5dd8c 100644 --- a/src/entities/utils/parseJoinArgs.ts +++ b/src/entities/utils/parseJoinArgs.ts @@ -5,7 +5,7 @@ import { replaceWrapped } from './replaceWrapped'; export function parseJoinArgs({ useNativeAssetAsWrappedAmountIn, chainId, - sortedTokens: poolTokens, + sortedTokens, poolId, sender, recipient, @@ -26,8 +26,8 @@ export function parseJoinArgs({ // replace wrapped token with native asset if needed const tokensIn = chainId && useNativeAssetAsWrappedAmountIn - ? replaceWrapped([...poolTokens], chainId) - : [...poolTokens]; + ? replaceWrapped([...sortedTokens], chainId) + : [...sortedTokens]; const joinPoolRequest = { assets: tokensIn.map((t) => t.address), // with BPT diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 7cb85e45..34e38ab0 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -14,7 +14,6 @@ import { WalletActions, walletActions, } from 'viem'; - import { BaseExit, SingleAssetExitInput, @@ -27,9 +26,8 @@ import { TokenAmount, } from '../src/entities'; import { ExitParser } from '../src/entities/exit/parser'; -import { Address } from '../src/types'; +import { Address, Hex } from '../src/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; - import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig @@ -259,6 +257,7 @@ describe('weighted exit test', () => { }); test('exit with native asset', async () => { + // TODO - This should be failing?? tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); @@ -319,8 +318,9 @@ describe('weighted exit test', () => { /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Address): Promise { - let tokens: { address: Address; decimals: number }[] = []; + public async getPool(id: Hex): Promise { + let tokens: { address: Address; decimals: number; index: number }[] = + []; if ( id === '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014' @@ -329,10 +329,12 @@ export class MockApi { { address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL decimals: 18, + index: 0, }, { address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH decimals: 18, + index: 1, }, ]; } else if ( @@ -343,10 +345,12 @@ export class MockApi { { address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH slot 0 decimals: 18, + index: 0, }, { address: '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP slot 1 decimals: 18, + index: 1, }, ]; } From 71d1872e4d4712bf15aee7d6923be23bd554dfcc Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 19 Sep 2023 16:21:12 +0100 Subject: [PATCH 046/199] fix: Native asset exit test and functionality. --- src/entities/exit/weighted/weightedExit.ts | 14 ++++++++------ src/entities/utils/parseExitArgs.ts | 11 +++++++---- test/weightedExit.integration.test.ts | 9 ++++++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index d26b3aef..362e5eea 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -29,8 +29,10 @@ export class WeightedExit implements BaseExit { const userData = this.encodeUserData(input.kind, amounts); - const queryArgs = parseExitArgs({ - // TODO - Wrapped native exit? + // tokensOut will have zero address if exit with native asset + const { args, tokensOut } = parseExitArgs({ + chainId: input.chainId, + exitWithNativeAsset: !!input.exitWithNativeAsset, poolId: poolState.id, sortedTokens, sender: ZERO_ADDRESS, @@ -43,14 +45,14 @@ export class WeightedExit implements BaseExit { const queryResult = await doQueryExit( input.rpcUrl, input.chainId, - queryArgs, + args, ); const bpt = new Token(input.chainId, poolState.address, 18); const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); const amountsOut = queryResult.amountsOut.map((a, i) => - TokenAmount.fromRawAmount(sortedTokens[i], a), + TokenAmount.fromRawAmount(tokensOut[i], a), ); return { @@ -96,7 +98,7 @@ export class WeightedExit implements BaseExit { const userData = this.encodeUserData(input.exitKind, amounts); - const queryArgs = parseExitArgs({ + const { args } = parseExitArgs({ poolId: input.id, sortedTokens: input.amountsOut.map((a) => a.token), sender: input.sender, @@ -109,7 +111,7 @@ export class WeightedExit implements BaseExit { const call = encodeFunctionData({ abi: vaultAbi, functionName: 'exitPool', - args: queryArgs, + args, }); return { diff --git a/src/entities/utils/parseExitArgs.ts b/src/entities/utils/parseExitArgs.ts index 5d63e427..0ba9de43 100644 --- a/src/entities/utils/parseExitArgs.ts +++ b/src/entities/utils/parseExitArgs.ts @@ -5,7 +5,7 @@ import { replaceWrapped } from './replaceWrapped'; export function parseExitArgs({ chainId, - useNativeAssetAsWrappedAmountIn, + exitWithNativeAsset, sortedTokens, poolId, sender, @@ -15,7 +15,7 @@ export function parseExitArgs({ toInternalBalance, }: { chainId?: number; - useNativeAssetAsWrappedAmountIn?: boolean; + exitWithNativeAsset?: boolean; sortedTokens: Token[]; poolId: Address; sender: Address; @@ -26,7 +26,7 @@ export function parseExitArgs({ }) { // replace wrapped token with native asset if needed const tokensOut = - chainId && useNativeAssetAsWrappedAmountIn + chainId && exitWithNativeAsset ? replaceWrapped([...sortedTokens], chainId) : [...sortedTokens]; @@ -37,5 +37,8 @@ export function parseExitArgs({ toInternalBalance, }; - return [poolId, sender, recipient, exitPoolRequest] as const; + return { + args: [poolId, sender, recipient, exitPoolRequest] as const, + tokensOut, + }; } diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 34e38ab0..b1a39240 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -24,6 +24,7 @@ import { Slippage, Token, TokenAmount, + replaceWrapped, } from '../src/entities'; import { ExitParser } from '../src/entities/exit/parser'; import { Address, Hex } from '../src/types'; @@ -287,11 +288,17 @@ describe('weighted exit test', () => { recipient: testAddress, }); + const poolTokens = poolFromApi.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + // send transaction and check balance changes const { transactionReceipt, balanceDeltas } = await sendTransactionGetBalances( [ - ...queryResult.amountsOut.map((a) => a.token.address), + ...replaceWrapped(poolTokens, chainId).map( + (a) => a.address, + ), queryResult.bptIn.token.address, ], client, From 2d755f86ae5f3e9db24421df835dd9a39e7655cb Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 20 Sep 2023 09:47:20 +0100 Subject: [PATCH 047/199] refactor: Add isSameAddress to Token to keep sanitising consistent. --- src/entities/exit/weighted/weightedExit.ts | 4 ++-- src/entities/join/weighted/weightedJoin.ts | 4 ++-- src/entities/token.ts | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 362e5eea..9f814350 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -79,8 +79,8 @@ export class WeightedExit implements BaseExit { case ExitKind.SINGLE_ASSET: return { minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: tokens.findIndex( - (t) => t.address === input.tokenOut.toLowerCase(), + tokenOutIndex: tokens.findIndex((t) => + t.isSameAddress(input.tokenOut), ), maxBptAmountIn: input.bptIn.amount, }; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 465c824c..d2a43f25 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -120,8 +120,8 @@ export class WeightedJoin implements BaseJoin { }; } case JoinKind.SingleAsset: { - const tokenInIndex = poolTokens.findIndex( - (t) => t.address === input.tokenIn.toLowerCase(), + const tokenInIndex = poolTokens.findIndex((t) => + t.isSameAddress(input.tokenIn), ); if (tokenInIndex === -1) throw Error("Can't find index of SingleAsset"); diff --git a/src/entities/token.ts b/src/entities/token.ts index ea4f8d70..c27bb98b 100644 --- a/src/entities/token.ts +++ b/src/entities/token.ts @@ -33,4 +33,8 @@ export class Token { public isUnderlyingEqual(token: Token) { return this.chainId === token.chainId && this.wrapped === token.wrapped; } + + public isSameAddress(address: Address) { + return this.address === address.toLowerCase(); + } } From 5ea7e26d09de1c92867ac2b8caddbabc8bff75ca Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 20 Sep 2023 10:23:39 +0100 Subject: [PATCH 048/199] feat: Add validation for weightedExit inputs. --- src/entities/exit/weighted/validateInputs.ts | 23 ++++++++++++++++++++ src/entities/exit/weighted/weightedExit.ts | 3 ++- src/entities/join/weighted/validateInputs.ts | 21 ++++-------------- src/entities/utils/areTokensInArray.ts | 11 ++++++++++ 4 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 src/entities/exit/weighted/validateInputs.ts create mode 100644 src/entities/utils/areTokensInArray.ts diff --git a/src/entities/exit/weighted/validateInputs.ts b/src/entities/exit/weighted/validateInputs.ts new file mode 100644 index 00000000..08f7b6fb --- /dev/null +++ b/src/entities/exit/weighted/validateInputs.ts @@ -0,0 +1,23 @@ +import { ExitInput, ExitKind } from '..'; +import { PoolState } from '../../types'; +import { areTokensInArray } from '../../utils/areTokensInArray'; + +export function validateInputs(input: ExitInput, poolState: PoolState) { + switch (input.kind) { + case ExitKind.UNBALANCED: + areTokensInArray( + input.amountsOut.map((a) => a.token.address), + poolState.tokens.map((t) => t.address), + ); + break; + case ExitKind.SINGLE_ASSET: + areTokensInArray( + [input.tokenOut], + poolState.tokens.map((t) => t.address), + ); + case ExitKind.PROPORTIONAL: + areTokensInArray([input.bptIn.token.address], [poolState.address]); + default: + break; + } +} diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 9f814350..58d54fe3 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -15,13 +15,14 @@ import { import { getSortedTokens } from '../../utils'; import { PoolState, AmountsExit } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; +import { validateInputs } from './validateInputs'; export class WeightedExit implements BaseExit { public async query( input: ExitInput, poolState: PoolState, ): Promise { - // TODO - Validate inputs + validateInputs(input, poolState); const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); diff --git a/src/entities/join/weighted/validateInputs.ts b/src/entities/join/weighted/validateInputs.ts index e6f40579..02916875 100644 --- a/src/entities/join/weighted/validateInputs.ts +++ b/src/entities/join/weighted/validateInputs.ts @@ -1,37 +1,24 @@ import { JoinInput, JoinKind } from '..'; import { PoolState } from '../../types'; -import { Address } from '../../../types'; +import { areTokensInArray } from '../../utils/areTokensInArray'; export function validateInputs(input: JoinInput, poolState: PoolState) { switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: - checkTokenMismatch( + areTokensInArray( input.amountsIn.map((a) => a.token.address), poolState.tokens.map((t) => t.address), ); break; case JoinKind.SingleAsset: - checkTokenMismatch( + areTokensInArray( [input.tokenIn], poolState.tokens.map((t) => t.address), ); case JoinKind.Proportional: - checkTokenMismatch( - [input.bptOut.token.address], - [poolState.address], - ); + areTokensInArray([input.bptOut.token.address], [poolState.address]); default: break; } } - -function checkTokenMismatch(tokensIn: Address[], poolTokens: Address[]) { - const sanitisedTokensIn = tokensIn.map((t) => t.toLowerCase() as Address); - const sanitisedPoolTokens = poolTokens.map((t) => t.toLowerCase()); - for (const tokenIn of sanitisedTokensIn) { - if (!sanitisedPoolTokens.includes(tokenIn)) { - throw new Error(`Token ${tokenIn} not found in pool`); - } - } -} diff --git a/src/entities/utils/areTokensInArray.ts b/src/entities/utils/areTokensInArray.ts new file mode 100644 index 00000000..568e2dce --- /dev/null +++ b/src/entities/utils/areTokensInArray.ts @@ -0,0 +1,11 @@ +import { Address } from '../../types'; + +export function areTokensInArray(tokens: Address[], tokenArray: Address[]) { + const sanitisedTokens = tokens.map((t) => t.toLowerCase() as Address); + const sanitisedTokenArray = tokenArray.map((t) => t.toLowerCase()); + for (const token of sanitisedTokens) { + if (!sanitisedTokenArray.includes(token)) { + throw new Error(`Token ${token} not found in array`); + } + } +} From 41b9569fedea688785032ce7284403a44eb0dd5d Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 20 Sep 2023 10:48:47 +0100 Subject: [PATCH 049/199] refactor: Move constants. --- test/weightedExit.integration.test.ts | 51 ++++++++++++--------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index b1a39240..44565e9f 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -31,27 +31,31 @@ import { Address, Hex } from '../src/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +const chainId = ChainId.MAINNET; +const rpcUrl = 'http://127.0.0.1:8545/'; +const blockNumber = 18043296n; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig +const poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH describe('weighted exit test', () => { let api: MockApi; - let chainId: ChainId; - let rpcUrl: string; - let blockNumber: bigint; let client: Client & PublicActions & TestActions & WalletActions; - let poolId: Address; let poolFromApi: PoolState; let weightedExit: BaseExit; - let tokenBpt: Token; + let bpt: Token; beforeAll(async () => { // setup mock api api = new MockApi(); - // setup chain and test client - chainId = ChainId.MAINNET; - rpcUrl = 'http://127.0.0.1:8545/'; - blockNumber = 18043296n; + // get pool state from api + poolFromApi = await api.getPool(poolId); + + // setup exit helper + const exitParser = new ExitParser(); + weightedExit = exitParser.getExit(poolFromApi.type); + client = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], @@ -60,14 +64,11 @@ describe('weighted exit test', () => { .extend(publicActions) .extend(walletActions); - poolId = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + // setup BPT token + bpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); }); beforeEach(async () => { - // get pool state from api - poolFromApi = await api.getPool(poolId); - await forkSetup( client, testAddress, @@ -77,18 +78,13 @@ describe('weighted exit test', () => { process.env.ETHEREUM_RPC_URL as string, blockNumber, ); - - // setup join helper - const exitParser = new ExitParser(); - weightedExit = exitParser.getExit(poolFromApi.type); }); test('single asset exit', async () => { - tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); - const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); + const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL - // perform join query to get expected bpt out + // perform exit query to get expected bpt out const exitInput: SingleAssetExitInput = { chainId, rpcUrl, @@ -143,10 +139,9 @@ describe('weighted exit test', () => { }); test('proportional exit', async () => { - tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); - const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); + const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); - // perform join query to get expected bpt out + // perform exit query to get expected bpt out const exitInput: ProportionalExitInput = { chainId, rpcUrl, @@ -205,7 +200,7 @@ describe('weighted exit test', () => { const amountsOut = poolTokens.map((t) => TokenAmount.fromHumanAmount(t, '0.001'), ); - // perform join query to get expected bpt out + // perform exit query to get expected bpt out const exitInput: UnbalancedExitInput = { chainId, rpcUrl, @@ -258,11 +253,9 @@ describe('weighted exit test', () => { }); test('exit with native asset', async () => { - // TODO - This should be failing?? - tokenBpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); - const bptIn = TokenAmount.fromHumanAmount(tokenBpt, '1'); + const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); - // perform join query to get expected bpt out + // perform exit query to get expected bpt out const exitInput: ProportionalExitInput = { chainId, rpcUrl, From 99fe3906c091682c25d4c059c53963e96662d694 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 20 Sep 2023 11:28:31 +0100 Subject: [PATCH 050/199] refactor: WeightedExit test. --- test/weightedExit.integration.test.ts | 196 ++++++++++---------------- 1 file changed, 78 insertions(+), 118 deletions(-) diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 44565e9f..95aeff1b 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -37,6 +37,7 @@ const blockNumber = 18043296n; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH +const slippage = Slippage.fromPercentage('1'); // 1% describe('weighted exit test', () => { let api: MockApi; @@ -92,50 +93,30 @@ describe('weighted exit test', () => { tokenOut, kind: ExitKind.SINGLE_ASSET, }; - const queryResult = await weightedExit.query(exitInput, poolFromApi); + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + exitInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); + // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We only expect single asset to have a value for exit + expect(queryResult.tokenOutIndex).to.be.toBeDefined; queryResult.amountsOut.forEach((a, i) => { if (i === queryResult.tokenOutIndex) expect(a.amount > 0n).to.be.true; else expect(a.amount === 0n).to.be.true; }); - // build call with slippage applied - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, maxBptIn, minAmountsOut } = - weightedExit.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsOut.map((a) => a.token.address), - queryResult.bptIn.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - expect(maxBptIn).to.eq(bptIn.amount); - const expectedDeltas = [ - ...queryResult.amountsOut.map((a) => a.amount), - queryResult.bptIn.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); + // Confirm slippage - only to amounts out not bpt in const expectedMinAmountsOut = queryResult.amountsOut.map((a) => slippage.removeFrom(a.amount), ); expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); }); test('proportional exit', async () => { @@ -148,49 +129,28 @@ describe('weighted exit test', () => { bptIn, kind: ExitKind.PROPORTIONAL, }; - const queryResult = await weightedExit.query(exitInput, poolFromApi); + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + exitInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); + // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + // We expect all assets to have a value for exit + expect(queryResult.tokenOutIndex).to.be.undefined; queryResult.amountsOut.forEach((a) => { expect(a.amount > 0n).to.be.true; }); - // build call with slippage applied - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, maxBptIn, minAmountsOut } = - weightedExit.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsOut.map((a) => a.token.address), - queryResult.bptIn.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - expect(maxBptIn).to.eq(bptIn.amount); - const expectedDeltas = [ - ...queryResult.amountsOut.map((a) => a.amount), - queryResult.bptIn.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); + // Confirm slippage - only to amounts out not bpt in const expectedMinAmountsOut = queryResult.amountsOut.map((a) => slippage.removeFrom(a.amount), ); expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); }); test('unbalanced exit', async () => { @@ -207,47 +167,25 @@ describe('weighted exit test', () => { amountsOut, kind: ExitKind.UNBALANCED, }; - const queryResult = await weightedExit.query(exitInput, poolFromApi); + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + exitInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); + // We expect a BPT input amount > 0 expect(queryResult.bptIn.amount > 0n).to.be.true; + // We expect assets to have same amount out as user defined + expect(queryResult.tokenOutIndex).to.be.undefined; queryResult.amountsOut.forEach((a, i) => { expect(a.amount).to.eq(amountsOut[i].amount); }); - // build call with slippage applied - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, maxBptIn, minAmountsOut } = - weightedExit.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsOut.map((a) => a.token.address), - queryResult.bptIn.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - minAmountsOut.forEach((a, i) => { - expect(a).to.eq(amountsOut[i].amount); - }); - const expectedDeltas = [ - ...queryResult.amountsOut.map((a) => a.amount), - queryResult.bptIn.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); + // Confirm slippage - only to bpt in, not amounts out + const expectedMinAmountsOut = amountsOut.map((a) => a.amount); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); expect(expectedMaxBptIn).to.deep.eq(maxBptIn); }); @@ -263,16 +201,45 @@ describe('weighted exit test', () => { kind: ExitKind.PROPORTIONAL, exitWithNativeAsset: true, }; - const queryResult = await weightedExit.query(exitInput, poolFromApi); + // We have to use zero address for balanceDeltas + const poolTokens = poolFromApi.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + exitInput, + replaceWrapped(poolTokens, chainId).map((a) => a.address), + bpt.address, + slippage, + ); + // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + // We expect all assets to have a value for exit + expect(queryResult.tokenOutIndex).to.be.undefined; queryResult.amountsOut.forEach((a) => { expect(a.amount > 0n).to.be.true; }); - // build call with slippage applied - const slippage = Slippage.fromPercentage('1'); // 1% + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); + }); + + async function doTransaction( + exitInput: + | SingleAssetExitInput + | ProportionalExitInput + | UnbalancedExitInput, + poolTokens: Address[], + bptToken: Address, + slippage: Slippage, + ) { + const queryResult = await weightedExit.query(exitInput, poolFromApi); + const { call, to, value, maxBptIn, minAmountsOut } = weightedExit.buildCall({ ...queryResult, @@ -281,38 +248,31 @@ describe('weighted exit test', () => { recipient: testAddress, }); - const poolTokens = poolFromApi.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - // send transaction and check balance changes const { transactionReceipt, balanceDeltas } = await sendTransactionGetBalances( - [ - ...replaceWrapped(poolTokens, chainId).map( - (a) => a.address, - ), - queryResult.bptIn.token.address, - ], + [...poolTokens, bptToken], client, testAddress, to, call, value, ); - expect(transactionReceipt.status).to.eq('success'); - expect(maxBptIn).to.eq(bptIn.amount); + + // Confirm final balance changes match query result const expectedDeltas = [ ...queryResult.amountsOut.map((a) => a.amount), queryResult.bptIn.amount, ]; expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - }); + + return { + queryResult, + maxBptIn, + minAmountsOut, + }; + } }); /*********************** Mock To Represent API Requirements **********************/ From bccfda9415bf043ad18ff3ec27154e79f11dd4f0 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 20 Sep 2023 14:05:48 +0100 Subject: [PATCH 051/199] refactor: Weighted Join tests. --- test/weightedJoin.integration.test.ts | 477 +++++++++++--------------- 1 file changed, 208 insertions(+), 269 deletions(-) diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 80c32ab7..878131fb 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -24,6 +24,7 @@ import { Slippage, Token, TokenAmount, + replaceWrapped, } from '../src/entities'; import { JoinParser } from '../src/entities/join/parser'; import { Address, Hex } from '../src/types'; @@ -32,26 +33,25 @@ import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +const chainId = ChainId.MAINNET; +const rpcUrl = 'http://127.0.0.1:8545/'; +const blockNumber = 18043296n; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig +const slippage = Slippage.fromPercentage('1'); // 1% +const poolId = + '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH describe('weighted join test', () => { let api: MockApi; - let chainId: ChainId; - let rpcUrl: string; - let blockNumber: bigint; let client: Client & PublicActions & TestActions & WalletActions; - let poolId: Hex; let poolFromApi: PoolState; let weightedJoin: BaseJoin; + let bpt: Token; beforeAll(async () => { // setup mock api api = new MockApi(); - // setup chain and test client - chainId = ChainId.MAINNET; - rpcUrl = 'http://127.0.0.1:8545/'; - blockNumber = 18043296n; client = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], @@ -59,310 +59,249 @@ describe('weighted join test', () => { }) .extend(publicActions) .extend(walletActions); - }); - beforeEach(async () => { // get pool state from api poolFromApi = await api.getPool(poolId); + // setup join helper + const joinParser = new JoinParser(); + weightedJoin = joinParser.getJoin(poolFromApi.type); + + // setup BPT token + bpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); + }); + + beforeEach(async () => { await forkSetup( client, testAddress, - poolFromApi.tokens.map((t) => t.address), + [...poolFromApi.tokens.map((t) => t.address), poolFromApi.address], undefined, // TODO: hardcode these values to improve test performance - poolFromApi.tokens.map((t) => parseUnits('100', t.decimals)), + [ + ...poolFromApi.tokens.map((t) => parseUnits('100', t.decimals)), + parseUnits('100', 18), + ], process.env.ETHEREUM_RPC_URL as string, blockNumber, ); + }); - // setup join helper - const joinParser = new JoinParser(); - weightedJoin = joinParser.getJoin(poolFromApi.type); + test('unbalanced join', async () => { + const poolTokens = poolFromApi.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const amountsIn = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + + const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( + joinInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); + + // Query should use same amountsIn as user sets + expect(queryResult.amountsIn).to.deep.eq(amountsIn); + expect(queryResult.tokenInIndex).to.be.undefined; + + // Expect some bpt amount + expect(queryResult.bptOut.amount > 0n).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = slippage.removeFrom(queryResult.bptOut.amount); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); - describe('exact in', async () => { - let tokenIn: Token; + test('native asset join', async () => { + const poolTokens = poolFromApi.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const amountsIn = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); - beforeAll(() => { - poolId = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH - }); + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + useNativeAssetAsWrappedAmountIn: true, + }; - test('single asset join', async () => { - tokenIn = new Token( - chainId, - '0xba100000625a3754423978a60c9317c58a424e3D', - 18, - 'BAL', - ); - const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn: [amountIn], - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; - const queryResult = await weightedJoin.query( - joinInput, - poolFromApi, - ); + // We have to use zero address for balanceDeltas + const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( + joinInput, + replaceWrapped(poolTokens, chainId).map((a) => a.address), + bpt.address, + slippage, + ); - // build join call with expected minBpOut based on slippage - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, minBptOut } = weightedJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); + // Query should use same amountsIn as user sets + expect(queryResult.amountsIn).to.deep.eq(amountsIn); + expect(queryResult.tokenInIndex).to.be.undefined; - // send join transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsIn.map((a) => a.token.address), - queryResult.bptOut.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMinBpt = slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - }); + // Expect some bpt amount + expect(queryResult.bptOut.amount > 0n).to.be.true; - test('native asset join', async () => { - tokenIn = new Token( - chainId, - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - 18, - 'WETH', - ); - const amountIn = TokenAmount.fromHumanAmount(tokenIn, '1'); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn: [amountIn], - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - useNativeAssetAsWrappedAmountIn: true, - }; - const queryResult = await weightedJoin.query( - joinInput, - poolFromApi, - ); + // Confirm slippage - only bpt out + const expectedMinBpt = slippage.removeFrom(queryResult.bptOut.amount); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + }); - // build join call with expected minBpOut based on slippage - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, minBptOut } = weightedJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); + test('single asset join', async () => { + const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); + const tokenIn = '0x198d7387fa97a73f05b8578cdeff8f2a1f34cd1f'; + + // perform join query to get expected bpt out + const joinInput: SingleAssetJoinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; - // send join transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsIn.map((a) => a.token.address), - queryResult.bptOut.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMinBpt = slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); + const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( + joinInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); + + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // We only expect single asset to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn.forEach((a, i) => { + if (i === queryResult.tokenInIndex) + expect(a.amount > 0n).to.be.true; + else expect(a.amount === 0n).to.be.true; }); + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); }); - describe('exact out', async () => { - let tokenOut: Token; - - beforeAll(() => { - poolId = - '0x87a867f5d240a782d43d90b6b06dea470f3f8f22000200000000000000000516'; // Balancer 50COMP-50wstETH - tokenOut = new Token( - chainId, - '0x87a867f5d240a782d43d90b6b06dea470f3f8f22', - 18, - 'Balancer 50COMP-50wstETH', - ); - }); + test('proportional join', async () => { + const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); - test('single asset join', async () => { - const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); - const tokenIn = '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'; - - // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - bptOut: amountOut, - tokenIn, - chainId, - rpcUrl, - kind: JoinKind.SingleAsset, - }; - const queryResult = await weightedJoin.query( - joinInput, - poolFromApi, - ); + // perform join query to get expected bpt out + const joinInput: ProportionalJoinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; - // build join call with expected minBpOut based on slippage - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, maxAmountsIn } = weightedJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); + const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( + joinInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); - // send join transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsIn.map((a) => a.token.address), - queryResult.bptOut.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // Expect all assets to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn.forEach((a) => { + expect(a.amount > 0n).to.be.true; }); + expect(queryResult.tokenInIndex).toBeUndefined; - test('proportional join', async () => { - const amountOut = TokenAmount.fromHumanAmount(tokenOut, '1'); - - // perform join query to get expected bpt out - const joinInput: ProportionalJoinInput = { - bptOut: amountOut, - chainId, - rpcUrl, - kind: JoinKind.Proportional, - }; - const queryResult = await weightedJoin.query( - joinInput, - poolFromApi, - ); + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); - // build join call with expected minBpOut based on slippage - const slippage = Slippage.fromPercentage('1'); // 1% - const { call, to, value, maxAmountsIn } = weightedJoin.buildCall({ + async function doTransaction( + exitInput: + | UnbalancedJoinInput + | ProportionalJoinInput + | SingleAssetJoinInput, + poolTokens: Address[], + bptToken: Address, + slippage: Slippage, + ) { + const queryResult = await weightedJoin.query(exitInput, poolFromApi); + + const { call, to, value, maxAmountsIn, minBptOut } = + weightedJoin.buildCall({ ...queryResult, slippage, sender: testAddress, recipient: testAddress, }); - // send join transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...queryResult.amountsIn.map((a) => a.token.address), - queryResult.bptOut.token.address, - ], - client, - testAddress, - to, - call, - value, - ); - - expect(transactionReceipt.status).to.eq('success'); - expect(queryResult.bptOut.amount > 0n).to.be.true; - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - slippage.applyTo(a.amount), + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolTokens, bptToken], + client, + testAddress, + to, + call, + value, ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - }); - }); + expect(transactionReceipt.status).to.eq('success'); + + // Confirm final balance changes match query result + const expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxAmountsIn, + minBptOut, + }; + } }); /*********************** Mock To Represent API Requirements **********************/ export class MockApi { public async getPool(id: Hex): Promise { - let tokens: { address: Address; decimals: number; index: number }[] = - []; - if ( - id === - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014' - ) { - tokens = [ - { - address: '0xba100000625a3754423978a60c9317c58a424e3d', // BAL - decimals: 18, - index: 0, - }, - { - address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH - decimals: 18, - index: 1, - }, - ]; - } else if ( - id === - '0x87a867f5d240a782d43d90b6b06dea470f3f8f22000200000000000000000516' - ) { - tokens = [ - { - address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH slot 0 - decimals: 18, - index: 0, - }, - { - address: '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP slot 1 - decimals: 18, - index: 1, - }, - ]; - } + const tokens = [ + { + address: + '0x198d7387fa97a73f05b8578cdeff8f2a1f34cd1f' as Address, // wjAURA + decimals: 18, + index: 0, + }, + { + address: + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH + decimals: 18, + index: 1, + }, + ]; + return { id, address: getPoolAddress(id) as Address, From 368f3d9cae920aa490b02e264fbefd2ec02d89a6 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 20 Sep 2023 14:27:23 +0100 Subject: [PATCH 052/199] fix: Handle native assets correctly on join. --- src/entities/join/weighted/weightedJoin.ts | 8 +-- src/entities/utils/parseJoinArgs.ts | 5 +- test/weightedJoin.integration.test.ts | 68 ++++++++++++++-------- 3 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index d2a43f25..cb963f33 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -32,7 +32,7 @@ export class WeightedJoin implements BaseJoin { const userData = this.encodeUserData(input.kind, amounts); - const queryArgs = parseJoinArgs({ + const { args, tokensIn } = parseJoinArgs({ useNativeAssetAsWrappedAmountIn: !!input.useNativeAssetAsWrappedAmountIn, chainId: input.chainId, @@ -48,14 +48,14 @@ export class WeightedJoin implements BaseJoin { const queryResult = await doQueryJoin( input.rpcUrl, input.chainId, - queryArgs, + args, ); const bpt = new Token(input.chainId, poolState.address, 18); const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); const amountsIn = queryResult.amountsIn.map((a, i) => - TokenAmount.fromRawAmount(sortedTokens[i], a), + TokenAmount.fromRawAmount(tokensIn[i], a), ); return { @@ -79,7 +79,7 @@ export class WeightedJoin implements BaseJoin { const userData = this.encodeUserData(input.joinKind, amounts); - const args = parseJoinArgs({ + const { args } = parseJoinArgs({ ...input, sortedTokens: input.amountsIn.map((a) => a.token), maxAmountsIn: amounts.maxAmountsIn, diff --git a/src/entities/utils/parseJoinArgs.ts b/src/entities/utils/parseJoinArgs.ts index 78e5dd8c..8611a2ff 100644 --- a/src/entities/utils/parseJoinArgs.ts +++ b/src/entities/utils/parseJoinArgs.ts @@ -36,5 +36,8 @@ export function parseJoinArgs({ fromInternalBalance, }; - return [poolId, sender, recipient, joinPoolRequest] as const; + return { + args: [poolId, sender, recipient, joinPoolRequest] as const, + tokensIn, + }; } diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 878131fb..723a9cef 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -102,17 +102,21 @@ describe('weighted join test', () => { kind: JoinKind.Unbalanced, }; - const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( - joinInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction( + joinInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); // Query should use same amountsIn as user sets expect(queryResult.amountsIn).to.deep.eq(amountsIn); expect(queryResult.tokenInIndex).to.be.undefined; + // Should be no native value + expect(value).toBeUndefined; + // Expect some bpt amount expect(queryResult.bptOut.amount > 0n).to.be.true; @@ -141,16 +145,21 @@ describe('weighted join test', () => { }; // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( - joinInput, - replaceWrapped(poolTokens, chainId).map((a) => a.address), - bpt.address, - slippage, - ); + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction( + joinInput, + replaceWrapped(poolTokens, chainId).map((a) => a.address), + bpt.address, + slippage, + ); // Query should use same amountsIn as user sets - expect(queryResult.amountsIn).to.deep.eq(amountsIn); + expect(queryResult.amountsIn.map((a) => a.amount)).to.deep.eq( + amountsIn.map((a) => a.amount), + ); expect(queryResult.tokenInIndex).to.be.undefined; + // Should have native value equal to input amount + expect(value).eq(amountsIn[0].amount); // Expect some bpt amount expect(queryResult.bptOut.amount > 0n).to.be.true; @@ -175,12 +184,13 @@ describe('weighted join test', () => { kind: JoinKind.SingleAsset, }; - const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( - joinInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction( + joinInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -193,6 +203,9 @@ describe('weighted join test', () => { else expect(a.amount === 0n).to.be.true; }); + // Should be no native value + expect(value).toBeUndefined; + // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => slippage.applyTo(a.amount), @@ -212,12 +225,13 @@ describe('weighted join test', () => { kind: JoinKind.Proportional, }; - const { queryResult, maxAmountsIn, minBptOut } = await doTransaction( - joinInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction( + joinInput, + poolFromApi.tokens.map((t) => t.address), + bpt.address, + slippage, + ); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -229,6 +243,9 @@ describe('weighted join test', () => { }); expect(queryResult.tokenInIndex).toBeUndefined; + // Should be no native value + expect(value).toBeUndefined; + // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => slippage.applyTo(a.amount), @@ -279,6 +296,7 @@ describe('weighted join test', () => { queryResult, maxAmountsIn, minBptOut, + value, }; } }); From fe3cc45d97281f5c1d7f72fa0440d60db3ced329 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 21 Sep 2023 09:54:36 +0200 Subject: [PATCH 053/199] Move input validation to a higher level to simplify the individual implementations. --- src/entities/exit/poolExit.ts | 53 ++++++++++++++++++++ src/entities/exit/weighted/validateInputs.ts | 4 +- src/entities/exit/weighted/weightedExit.ts | 12 ++--- src/entities/types.ts | 8 +++ 4 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 src/entities/exit/poolExit.ts diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts new file mode 100644 index 00000000..dd6ea4b5 --- /dev/null +++ b/src/entities/exit/poolExit.ts @@ -0,0 +1,53 @@ +import { + BaseExit, + BuildOutput, + ExitCallInput, + ExitConfig, + ExitInput, + ExitQueryResult, +} from './types'; +import { WeightedExit } from './weighted/weightedExit'; +import { PoolStateInput } from '../types'; +import { validateInputs } from './weighted/validateInputs'; +import { getSortedTokens } from '../utils'; + +export class PoolExit { + private readonly poolExits: Record = {}; + + 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( + poolType: string, + input: ExitInput, + poolState: PoolStateInput, + ): Promise { + validateInputs(input, poolState); + + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); + const mappedPoolState = { + ...poolState, + tokens: sortedTokens, + }; + + return this.getExit(poolType).query(input, mappedPoolState); + } + + public buildCall(poolType: string, input: ExitCallInput): BuildOutput { + return this.getExit(poolType).buildCall(input); + } +} diff --git a/src/entities/exit/weighted/validateInputs.ts b/src/entities/exit/weighted/validateInputs.ts index 08f7b6fb..335d98fb 100644 --- a/src/entities/exit/weighted/validateInputs.ts +++ b/src/entities/exit/weighted/validateInputs.ts @@ -1,8 +1,8 @@ import { ExitInput, ExitKind } from '..'; -import { PoolState } from '../../types'; +import { PoolState, 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( diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 58d54fe3..3a76bccd 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -12,21 +12,15 @@ import { 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 { - 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); @@ -35,7 +29,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, diff --git a/src/entities/types.ts b/src/entities/types.ts index d1286ebf..4be403fe 100644 --- a/src/entities/types.ts +++ b/src/entities/types.ts @@ -1,8 +1,16 @@ import { MinimalToken } from '../data'; import { Address, Hex } from '../types'; +import { Token } from './token'; // Returned from API and used as input export type PoolState = { + id: Hex; + address: Address; + type: string; + tokens: Token[]; +}; + +export type PoolStateInput = { id: Hex; address: Address; type: string; From 80c339b8c0d6d02dadb687502f4d2a9ae6468161 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 21 Sep 2023 10:00:33 +0200 Subject: [PATCH 054/199] remove parser --- src/entities/exit/parser.ts | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/entities/exit/parser.ts diff --git a/src/entities/exit/parser.ts b/src/entities/exit/parser.ts deleted file mode 100644 index b2cf670b..00000000 --- a/src/entities/exit/parser.ts +++ /dev/null @@ -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 = {}; - - 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]; - } -} From ccef3274914019ee69b6fc19bd8f0eeb64024e5d Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 09:10:34 +0100 Subject: [PATCH 055/199] refactor: Update naming. --- test/weightedJoin.integration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 723a9cef..7ee6ffa9 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -255,7 +255,7 @@ describe('weighted join test', () => { }); async function doTransaction( - exitInput: + joinInput: | UnbalancedJoinInput | ProportionalJoinInput | SingleAssetJoinInput, @@ -263,7 +263,7 @@ describe('weighted join test', () => { bptToken: Address, slippage: Slippage, ) { - const queryResult = await weightedJoin.query(exitInput, poolFromApi); + const queryResult = await weightedJoin.query(joinInput, poolFromApi); const { call, to, value, maxAmountsIn, minBptOut } = weightedJoin.buildCall({ From 78dc6dcac7e56f49f2a3b79542acd05e7deeb617 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 09:57:14 +0100 Subject: [PATCH 056/199] chore: Fix liniting. --- src/entities/exit/weighted/validateInputs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entities/exit/weighted/validateInputs.ts b/src/entities/exit/weighted/validateInputs.ts index 335d98fb..2b89aab7 100644 --- a/src/entities/exit/weighted/validateInputs.ts +++ b/src/entities/exit/weighted/validateInputs.ts @@ -1,5 +1,5 @@ import { ExitInput, ExitKind } from '..'; -import { PoolState, PoolStateInput } from '../../types'; +import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; export function validateInputs(input: ExitInput, poolState: PoolStateInput) { From e0e044b029bd4f0ee94ea64a14cffc75cc32e364 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 10:04:22 +0100 Subject: [PATCH 057/199] chore: Fix imports, types for joins. --- src/entities/join/index.ts | 7 +++++-- src/entities/join/weighted/validateInputs.ts | 4 ++-- src/entities/join/weighted/weightedJoin.ts | 4 ++-- test/weightedJoin.integration.test.ts | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index b968ca7a..386561fe 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,6 +1,6 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; -import { PoolState } from '../types'; +import { PoolStateInput } from '../types'; import { Address, Hex } from '../../types'; export enum JoinKind { @@ -62,7 +62,10 @@ export type JoinCallInput = JoinQueryResult & { }; export interface BaseJoin { - query(input: JoinInput, poolState: PoolState): Promise; + query( + input: JoinInput, + poolState: PoolStateInput, + ): Promise; buildCall(input: JoinCallInput): { call: Hex; to: Address; diff --git a/src/entities/join/weighted/validateInputs.ts b/src/entities/join/weighted/validateInputs.ts index 02916875..02a9b053 100644 --- a/src/entities/join/weighted/validateInputs.ts +++ b/src/entities/join/weighted/validateInputs.ts @@ -1,8 +1,8 @@ import { JoinInput, JoinKind } from '..'; -import { PoolState } from '../../types'; +import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; -export function validateInputs(input: JoinInput, poolState: PoolState) { +export function validateInputs(input: JoinInput, poolState: PoolStateInput) { switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index cb963f33..b451e393 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -11,7 +11,7 @@ import { JoinKind, JoinQueryResult, } from '..'; -import { PoolState, AmountsJoin } from '../../types'; +import { AmountsJoin, PoolStateInput } from '../../types'; import { doQueryJoin, getAmounts, @@ -22,7 +22,7 @@ import { export class WeightedJoin implements BaseJoin { public async query( input: JoinInput, - poolState: PoolState, + poolState: PoolStateInput, ): Promise { validateInputs(input, poolState); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 7ee6ffa9..982f9ff2 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -28,7 +28,7 @@ import { } from '../src/entities'; import { JoinParser } from '../src/entities/join/parser'; import { Address, Hex } from '../src/types'; -import { PoolState } from '../src/entities/types'; +import { PoolStateInput } from '../src/entities/types'; import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; @@ -44,7 +44,7 @@ const poolId = describe('weighted join test', () => { let api: MockApi; let client: Client & PublicActions & TestActions & WalletActions; - let poolFromApi: PoolState; + let poolFromApi: PoolStateInput; let weightedJoin: BaseJoin; let bpt: Token; @@ -304,7 +304,7 @@ describe('weighted join test', () => { /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Hex): Promise { + public async getPool(id: Hex): Promise { const tokens = [ { address: From 49be636bcd8c17e27fb7d6da2827457c60295d4a Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 11:07:46 +0100 Subject: [PATCH 058/199] fix: Circular dep issue when exporting PoolExit. --- src/entities/exit/index.ts | 1 + src/entities/exit/poolExit.ts | 2 +- src/entities/exit/weighted/weightedExit.ts | 10 ++++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/entities/exit/index.ts b/src/entities/exit/index.ts index fcb073fe..318c97dc 100644 --- a/src/entities/exit/index.ts +++ b/src/entities/exit/index.ts @@ -1 +1,2 @@ +export * from './poolExit'; export * from './types'; diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index dd6ea4b5..6a229a1a 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -9,7 +9,7 @@ import { import { WeightedExit } from './weighted/weightedExit'; import { PoolStateInput } from '../types'; import { validateInputs } from './weighted/validateInputs'; -import { getSortedTokens } from '../utils'; +import { getSortedTokens } from '../utils/getSortedTokens'; export class PoolExit { private readonly poolExits: Record = {}; diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 3a76bccd..2fee92b2 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -1,7 +1,13 @@ 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 { From d6674ab9cf0dfc2fcd6e62c4e0d796c713da22cb Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 11:55:28 +0100 Subject: [PATCH 059/199] fix: Change test to use new PoolExit instead of parser. --- test/weightedExit.integration.test.ts | 170 +++++++++++++++----------- 1 file changed, 97 insertions(+), 73 deletions(-) diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 95aeff1b..140576ab 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -15,49 +15,55 @@ import { walletActions, } from 'viem'; import { - BaseExit, SingleAssetExitInput, ProportionalExitInput, UnbalancedExitInput, ExitKind, - PoolState, Slippage, Token, TokenAmount, replaceWrapped, -} from '../src/entities'; -import { ExitParser } from '../src/entities/exit/parser'; -import { Address, Hex } from '../src/types'; -import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, +} from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +type TxInput = { + client: Client & PublicActions & TestActions & WalletActions; + poolExit: PoolExit; + exitInput: + | SingleAssetExitInput + | ProportionalExitInput + | UnbalancedExitInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; +}; + const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const blockNumber = 18043296n; -const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH -const slippage = Slippage.fromPercentage('1'); // 1% describe('weighted exit test', () => { - let api: MockApi; - let client: Client & PublicActions & TestActions & WalletActions; - let poolFromApi: PoolState; - let weightedExit: BaseExit; - let bpt: Token; - + let txInput: TxInput; + let bptToken: Token; beforeAll(async () => { // setup mock api - api = new MockApi(); + const api = new MockApi(); // get pool state from api - poolFromApi = await api.getPool(poolId); - - // setup exit helper - const exitParser = new ExitParser(); - weightedExit = exitParser.getExit(poolFromApi.type); + const poolInput = await api.getPool(poolId); - client = createTestClient({ + const client = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], transport: http(rpcUrl), @@ -66,14 +72,23 @@ describe('weighted exit test', () => { .extend(walletActions); // setup BPT token - bpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig + exitInput: {} as ExitInput, + checkNativeBalance: false, + }; + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); beforeEach(async () => { await forkSetup( - client, - testAddress, - [poolFromApi.address], + txInput.client, + txInput.testAddress, + [txInput.poolInput.address], undefined, // TODO: hardcode these values to improve test performance [parseUnits('1', 18)], process.env.ETHEREUM_RPC_URL as string, @@ -82,10 +97,10 @@ describe('weighted exit test', () => { }); test('single asset exit', async () => { - const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL - // perform exit query to get expected bpt out const exitInput: SingleAssetExitInput = { chainId, rpcUrl, @@ -93,12 +108,10 @@ describe('weighted exit test', () => { tokenOut, kind: ExitKind.SINGLE_ASSET, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, exitInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + }); // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); @@ -120,21 +133,19 @@ describe('weighted exit test', () => { }); test('proportional exit', async () => { - const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - // perform exit query to get expected bpt out const exitInput: ProportionalExitInput = { chainId, rpcUrl, bptIn, kind: ExitKind.PROPORTIONAL, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, exitInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + }); // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); @@ -154,25 +165,25 @@ describe('weighted exit test', () => { }); test('unbalanced exit', async () => { - const poolTokens = poolFromApi.tokens.map( + const { poolInput, slippage } = txInput; + + const poolTokens = poolInput.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); const amountsOut = poolTokens.map((t) => TokenAmount.fromHumanAmount(t, '0.001'), ); - // perform exit query to get expected bpt out + const exitInput: UnbalancedExitInput = { chainId, rpcUrl, amountsOut, kind: ExitKind.UNBALANCED, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, exitInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + }); // We expect a BPT input amount > 0 expect(queryResult.bptIn.amount > 0n).to.be.true; @@ -191,9 +202,9 @@ describe('weighted exit test', () => { }); test('exit with native asset', async () => { - const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - // perform exit query to get expected bpt out const exitInput: ProportionalExitInput = { chainId, rpcUrl, @@ -202,16 +213,13 @@ describe('weighted exit test', () => { exitWithNativeAsset: true, }; - // We have to use zero address for balanceDeltas - const poolTokens = poolFromApi.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( + // Note - checking native balance + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, exitInput, - replaceWrapped(poolTokens, chainId).map((a) => a.address), - bpt.address, - slippage, - ); + checkNativeBalance: true, + }); + // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); @@ -229,29 +237,45 @@ describe('weighted exit test', () => { expect(maxBptIn).to.eq(bptIn.amount); }); - async function doTransaction( - exitInput: - | SingleAssetExitInput - | ProportionalExitInput - | UnbalancedExitInput, - poolTokens: Address[], - bptToken: Address, - slippage: Slippage, - ) { - const queryResult = await weightedExit.query(exitInput, poolFromApi); - - const { call, to, value, maxBptIn, minAmountsOut } = - weightedExit.buildCall({ + async function doTransaction(txIp: TxInput) { + const { + poolExit, + poolInput, + exitInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolExit.query( + poolInput.type, + exitInput, + poolInput, + ); + + const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( + poolInput.type, + { ...queryResult, slippage, sender: testAddress, recipient: testAddress, - }); + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); // send transaction and check balance changes const { transactionReceipt, balanceDeltas } = await sendTransactionGetBalances( - [...poolTokens, bptToken], + [...poolTokensAddr, poolInput.address], client, testAddress, to, @@ -278,7 +302,7 @@ describe('weighted exit test', () => { /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Hex): Promise { + public async getPool(id: Hex): Promise { let tokens: { address: Address; decimals: number; index: number }[] = []; if ( From 34b6b9e56e0c79c15ccfcb8b176578541eeec62d Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 12:01:14 +0100 Subject: [PATCH 060/199] refactor: Remove poolType input param. Add poolType to query return. --- src/entities/exit/poolExit.ts | 7 +++---- src/entities/exit/types.ts | 1 + src/entities/exit/weighted/weightedExit.ts | 1 + test/weightedExit.integration.test.ts | 7 +------ 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 6a229a1a..09ecd3cb 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -32,7 +32,6 @@ export class PoolExit { } public async query( - poolType: string, input: ExitInput, poolState: PoolStateInput, ): Promise { @@ -44,10 +43,10 @@ export class PoolExit { tokens: sortedTokens, }; - return this.getExit(poolType).query(input, mappedPoolState); + return this.getExit(poolState.type).query(input, mappedPoolState); } - public buildCall(poolType: string, input: ExitCallInput): BuildOutput { - return this.getExit(poolType).buildCall(input); + public buildCall(input: ExitCallInput): BuildOutput { + return this.getExit(input.poolType).buildCall(input); } } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 2e50da4d..de352587 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -40,6 +40,7 @@ export type ExitInput = // Returned from a exit query export type ExitQueryResult = { + poolType: string; id: Address; exitKind: ExitKind; bptIn: TokenAmount; diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 2fee92b2..244bbeb1 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -57,6 +57,7 @@ export class WeightedExit implements BaseExit { ); return { + poolType: poolState.type, exitKind: input.kind, id: poolState.id, bptIn, diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 140576ab..73f2e94c 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -247,14 +247,9 @@ describe('weighted exit test', () => { slippage, checkNativeBalance, } = txIp; - const queryResult = await poolExit.query( - poolInput.type, - exitInput, - poolInput, - ); + const queryResult = await poolExit.query(exitInput, poolInput); const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( - poolInput.type, { ...queryResult, slippage, From dfe55777ebc33b8ee7c3007f4d96d7a234a3ffec Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 14:42:09 +0100 Subject: [PATCH 061/199] feat: Change Join to use PoolJoin instead of parser. Updated tests. --- src/entities/join/index.ts | 78 +--------- src/entities/join/parser.ts | 24 --- src/entities/join/poolJoin.ts | 52 +++++++ src/entities/join/types.ts | 86 +++++++++++ src/entities/join/weighted/weightedJoin.ts | 32 ++-- test/weightedExit.integration.test.ts | 6 +- test/weightedJoin.integration.test.ts | 166 ++++++++++++--------- 7 files changed, 246 insertions(+), 198 deletions(-) delete mode 100644 src/entities/join/parser.ts create mode 100644 src/entities/join/poolJoin.ts create mode 100644 src/entities/join/types.ts diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index 386561fe..d20b54f9 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,76 +1,2 @@ -import { TokenAmount } from '../tokenAmount'; -import { Slippage } from '../slippage'; -import { PoolStateInput } 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: PoolStateInput, - ): Promise; - buildCall(input: JoinCallInput): { - call: Hex; - to: Address; - value: bigint | undefined; - minBptOut: bigint; - maxAmountsIn: bigint[]; - }; -} +export * from './poolJoin'; +export * from './types'; diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts deleted file mode 100644 index 7506e603..00000000 --- a/src/entities/join/parser.ts +++ /dev/null @@ -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; -}; - -export class JoinParser { - private readonly poolJoins: Record = {}; - - 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]; - } -} diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts new file mode 100644 index 00000000..b0753a5d --- /dev/null +++ b/src/entities/join/poolJoin.ts @@ -0,0 +1,52 @@ +import { + BaseJoin, + BuildOutput, + JoinConfig, + JoinInput, + JoinQueryResult, + JoinCallInput, +} from './types'; +import { WeightedJoin } from './weighted/weightedJoin'; +import { PoolStateInput } from '../types'; +import { validateInputs } from './weighted/validateInputs'; +import { getSortedTokens } from '../utils/getSortedTokens'; + +export class PoolJoin { + private readonly poolJoins: Record = {}; + + 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 { + if (!this.poolJoins[poolType]) { + throw new Error('Unsupported pool type'); + } + + return this.poolJoins[poolType]; + } + + public async query( + input: JoinInput, + poolState: PoolStateInput, + ): Promise { + validateInputs(input, poolState); + + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); + const mappedPoolState = { + ...poolState, + tokens: sortedTokens, + }; + + return this.getJoin(poolState.type).query(input, mappedPoolState); + } + + public buildCall(input: JoinCallInput): BuildOutput { + return this.getJoin(input.poolType).buildCall(input); + } +} diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts new file mode 100644 index 00000000..ad206e25 --- /dev/null +++ b/src/entities/join/types.ts @@ -0,0 +1,86 @@ +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 = { + poolType: string; + 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; + buildCall(input: JoinCallInput): { + call: Hex; + to: Address; + value: bigint | undefined; + minBptOut: bigint; + maxAmountsIn: bigint[]; + }; +} + +export type BuildOutput = { + call: Hex; + to: Address; + value: bigint | undefined; + minBptOut: bigint; + maxAmountsIn: bigint[]; +}; + +export type JoinConfig = { + customPoolJoins: Record; +}; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index b451e393..7f2b79a2 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -1,34 +1,25 @@ import { encodeFunctionData } from 'viem'; import { Token, TokenAmount, WeightedEncoder } from '../../..'; -import { Address, Hex } from '../../../types'; +import { Address } from '../../../types'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; -import { validateInputs } from './validateInputs'; import { BaseJoin, + BuildOutput, JoinCallInput, JoinInput, JoinKind, JoinQueryResult, } from '..'; -import { AmountsJoin, PoolStateInput } from '../../types'; -import { - doQueryJoin, - getAmounts, - parseJoinArgs, - getSortedTokens, -} from '../../utils'; +import { AmountsJoin, PoolState } from '../../types'; +import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; export class WeightedJoin implements BaseJoin { public async query( input: JoinInput, - poolState: PoolStateInput, + poolState: PoolState, ): Promise { - 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); @@ -36,7 +27,7 @@ export class WeightedJoin implements BaseJoin { useNativeAssetAsWrappedAmountIn: !!input.useNativeAssetAsWrappedAmountIn, chainId: input.chainId, - sortedTokens, + sortedTokens: poolState.tokens, poolId: poolState.id, sender: ZERO_ADDRESS, recipient: ZERO_ADDRESS, @@ -59,6 +50,7 @@ export class WeightedJoin implements BaseJoin { ); return { + poolType: poolState.type, joinKind: input.kind, poolId: poolState.id, bptOut, @@ -68,13 +60,7 @@ export class WeightedJoin implements BaseJoin { }; } - public buildCall(input: JoinCallInput): { - call: Hex; - to: Address; - value: bigint | undefined; - minBptOut: bigint; - maxAmountsIn: bigint[]; - } { + public buildCall(input: JoinCallInput): BuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.joinKind, amounts); diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 73f2e94c..50cad9d7 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -37,10 +37,7 @@ import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; type TxInput = { client: Client & PublicActions & TestActions & WalletActions; poolExit: PoolExit; - exitInput: - | SingleAssetExitInput - | ProportionalExitInput - | UnbalancedExitInput; + exitInput: ExitInput; slippage: Slippage; poolInput: PoolStateInput; testAddress: Address; @@ -71,7 +68,6 @@ describe('weighted exit test', () => { .extend(publicActions) .extend(walletActions); - // setup BPT token txInput = { client, poolExit: new PoolExit(), diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 982f9ff2..afd6497a 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -16,7 +16,6 @@ import { } from 'viem'; import { - BaseJoin, UnbalancedJoinInput, ProportionalJoinInput, SingleAssetJoinInput, @@ -25,34 +24,45 @@ import { Token, TokenAmount, replaceWrapped, -} from '../src/entities'; -import { JoinParser } from '../src/entities/join/parser'; -import { Address, Hex } from '../src/types'; -import { PoolStateInput } from '../src/entities/types'; -import { CHAINS, ChainId, getPoolAddress } from '../src/utils'; - + Address, + Hex, + PoolStateInput, + CHAINS, + ChainId, + getPoolAddress, + PoolJoin, + JoinInput, +} from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +type TxInput = { + client: Client & PublicActions & TestActions & WalletActions; + poolJoin: PoolJoin; + joinInput: JoinInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; +}; + const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const blockNumber = 18043296n; -const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig -const slippage = Slippage.fromPercentage('1'); // 1% const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH describe('weighted join test', () => { - let api: MockApi; - let client: Client & PublicActions & TestActions & WalletActions; - let poolFromApi: PoolStateInput; - let weightedJoin: BaseJoin; - let bpt: Token; + let txInput: TxInput; + let bptToken: Token; beforeAll(async () => { // setup mock api - api = new MockApi(); + const api = new MockApi(); - client = createTestClient({ + // get pool state from api + const poolInput = await api.getPool(poolId); + + const client = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], transport: http(rpcUrl), @@ -60,25 +70,33 @@ describe('weighted join test', () => { .extend(publicActions) .extend(walletActions); - // get pool state from api - poolFromApi = await api.getPool(poolId); - - // setup join helper - const joinParser = new JoinParser(); - weightedJoin = joinParser.getJoin(poolFromApi.type); + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig + joinInput: {} as JoinInput, + checkNativeBalance: false, + }; // setup BPT token - bpt = new Token(chainId, poolFromApi.address, 18, 'BPT'); + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); beforeEach(async () => { await forkSetup( - client, - testAddress, - [...poolFromApi.tokens.map((t) => t.address), poolFromApi.address], + txInput.client, + txInput.testAddress, + [ + ...txInput.poolInput.tokens.map((t) => t.address), + txInput.poolInput.address, + ], undefined, // TODO: hardcode these values to improve test performance [ - ...poolFromApi.tokens.map((t) => parseUnits('100', t.decimals)), + ...txInput.poolInput.tokens.map((t) => + parseUnits('100', t.decimals), + ), parseUnits('100', 18), ], process.env.ETHEREUM_RPC_URL as string, @@ -87,7 +105,7 @@ describe('weighted join test', () => { }); test('unbalanced join', async () => { - const poolTokens = poolFromApi.tokens.map( + const poolTokens = txInput.poolInput.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); const amountsIn = poolTokens.map((t) => @@ -103,12 +121,10 @@ describe('weighted join test', () => { }; const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction( + await doTransaction({ + ...txInput, joinInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + }); // Query should use same amountsIn as user sets expect(queryResult.amountsIn).to.deep.eq(amountsIn); @@ -121,14 +137,16 @@ describe('weighted join test', () => { expect(queryResult.bptOut.amount > 0n).to.be.true; // Confirm slippage - only bpt out - const expectedMinBpt = slippage.removeFrom(queryResult.bptOut.amount); + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); expect(expectedMinBpt).to.deep.eq(minBptOut); const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); test('native asset join', async () => { - const poolTokens = poolFromApi.tokens.map( + const poolTokens = txInput.poolInput.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); const amountsIn = poolTokens.map((t) => @@ -146,12 +164,11 @@ describe('weighted join test', () => { // We have to use zero address for balanceDeltas const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction( + await doTransaction({ + ...txInput, joinInput, - replaceWrapped(poolTokens, chainId).map((a) => a.address), - bpt.address, - slippage, - ); + checkNativeBalance: true, + }); // Query should use same amountsIn as user sets expect(queryResult.amountsIn.map((a) => a.amount)).to.deep.eq( @@ -165,14 +182,16 @@ describe('weighted join test', () => { expect(queryResult.bptOut.amount > 0n).to.be.true; // Confirm slippage - only bpt out - const expectedMinBpt = slippage.removeFrom(queryResult.bptOut.amount); + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); expect(expectedMinBpt).to.deep.eq(minBptOut); const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); test('single asset join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); const tokenIn = '0x198d7387fa97a73f05b8578cdeff8f2a1f34cd1f'; // perform join query to get expected bpt out @@ -185,12 +204,10 @@ describe('weighted join test', () => { }; const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction( + await doTransaction({ + ...txInput, joinInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + }); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -208,14 +225,14 @@ describe('weighted join test', () => { // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - slippage.applyTo(a.amount), + txInput.slippage.applyTo(a.amount), ); expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); expect(minBptOut).to.eq(bptOut.amount); }); test('proportional join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); // perform join query to get expected bpt out const joinInput: ProportionalJoinInput = { @@ -226,12 +243,10 @@ describe('weighted join test', () => { }; const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction( + await doTransaction({ + ...txInput, joinInput, - poolFromApi.tokens.map((t) => t.address), - bpt.address, - slippage, - ); + }); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -248,35 +263,46 @@ describe('weighted join test', () => { // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - slippage.applyTo(a.amount), + txInput.slippage.applyTo(a.amount), ); expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); expect(minBptOut).to.eq(bptOut.amount); }); - async function doTransaction( - joinInput: - | UnbalancedJoinInput - | ProportionalJoinInput - | SingleAssetJoinInput, - poolTokens: Address[], - bptToken: Address, - slippage: Slippage, - ) { - const queryResult = await weightedJoin.query(joinInput, poolFromApi); - - const { call, to, value, maxAmountsIn, minBptOut } = - weightedJoin.buildCall({ + async function doTransaction(txIp: TxInput) { + const { + poolJoin, + poolInput, + joinInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolJoin.query(joinInput, poolInput); + + const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( + { ...queryResult, slippage, sender: testAddress, recipient: testAddress, - }); + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); // send transaction and check balance changes const { transactionReceipt, balanceDeltas } = await sendTransactionGetBalances( - [...poolTokens, bptToken], + [...poolTokensAddr, poolInput.address], client, testAddress, to, From 1cee5145de316275625c2d9485f038ecffd5b652 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 21 Sep 2023 19:57:03 +0100 Subject: [PATCH 062/199] fix: Change BuildOutput naming to fix build issue. --- src/entities/exit/poolExit.ts | 4 ++-- src/entities/exit/types.ts | 4 ++-- src/entities/exit/weighted/weightedExit.ts | 4 ++-- src/entities/join/poolJoin.ts | 4 ++-- src/entities/join/types.ts | 2 +- src/entities/join/weighted/weightedJoin.ts | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 09ecd3cb..6d2a373f 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -1,6 +1,6 @@ import { BaseExit, - BuildOutput, + ExitBuildOutput, ExitCallInput, ExitConfig, ExitInput, @@ -46,7 +46,7 @@ export class PoolExit { return this.getExit(poolState.type).query(input, mappedPoolState); } - public buildCall(input: ExitCallInput): BuildOutput { + public buildCall(input: ExitCallInput): ExitBuildOutput { return this.getExit(input.poolType).buildCall(input); } } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index de352587..3938bbc7 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -55,7 +55,7 @@ export type ExitCallInput = ExitQueryResult & { recipient: Address; }; -export type BuildOutput = { +export type ExitBuildOutput = { call: Address; to: Address; value: bigint | undefined; @@ -65,7 +65,7 @@ export type BuildOutput = { export interface BaseExit { query(input: ExitInput, poolState: PoolState): Promise; - buildCall(input: ExitCallInput): BuildOutput; + buildCall(input: ExitCallInput): ExitBuildOutput; } export type ExitConfig = { diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 244bbeb1..28fecb7d 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -12,7 +12,7 @@ import { vaultAbi } from '../../../abi'; import { parseExitArgs } from '../../utils/parseExitArgs'; import { BaseExit, - BuildOutput, + ExitBuildOutput, ExitCallInput, ExitInput, ExitKind, @@ -95,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); diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index b0753a5d..04d8ced5 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -1,6 +1,6 @@ import { BaseJoin, - BuildOutput, + JoinBuildOutput, JoinConfig, JoinInput, JoinQueryResult, @@ -46,7 +46,7 @@ export class PoolJoin { return this.getJoin(poolState.type).query(input, mappedPoolState); } - public buildCall(input: JoinCallInput): BuildOutput { + public buildCall(input: JoinCallInput): JoinBuildOutput { return this.getJoin(input.poolType).buildCall(input); } } diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index ad206e25..318b8ce6 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -73,7 +73,7 @@ export interface BaseJoin { }; } -export type BuildOutput = { +export type JoinBuildOutput = { call: Hex; to: Address; value: bigint | undefined; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 7f2b79a2..b10fe0fc 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -5,7 +5,7 @@ import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; import { BaseJoin, - BuildOutput, + JoinBuildOutput, JoinCallInput, JoinInput, JoinKind, @@ -60,7 +60,7 @@ export class WeightedJoin implements BaseJoin { }; } - public buildCall(input: JoinCallInput): BuildOutput { + public buildCall(input: JoinCallInput): JoinBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.joinKind, amounts); From b62ab16bab92e6fe6b830ef75310953faacc806a Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Thu, 21 Sep 2023 19:26:43 -0300 Subject: [PATCH 063/199] Creating a provider to fetch data from the Balancer API; --- .gitignore | 2 + .../balancer-api-provider/client/index.ts | 22 +++++ .../providers/balancer-api-provider/index.ts | 16 ++++ .../modules/join/index.ts | 88 +++++++++++++++++++ .../modules/join/types.ts | 14 +++ 5 files changed, 142 insertions(+) create mode 100644 src/data/providers/balancer-api-provider/client/index.ts create mode 100644 src/data/providers/balancer-api-provider/index.ts create mode 100644 src/data/providers/balancer-api-provider/modules/join/index.ts create mode 100644 src/data/providers/balancer-api-provider/modules/join/types.ts diff --git a/.gitignore b/.gitignore index 3e819cac..c1308de5 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,5 @@ typechain-types cache artifacts +#IDE +.idea diff --git a/src/data/providers/balancer-api-provider/client/index.ts b/src/data/providers/balancer-api-provider/client/index.ts new file mode 100644 index 00000000..d1bd5ab9 --- /dev/null +++ b/src/data/providers/balancer-api-provider/client/index.ts @@ -0,0 +1,22 @@ +export class BalancerApiClient { + subgraphUrl: string; + chainId: number; + constructor(subgraphUrl: string, chainId: number) { + this.subgraphUrl = subgraphUrl; + this.chainId = chainId; + } + + async fetch(operationName: string, query: any, variables: any) { + const requestQuery = { + operationName, + query, + variables, + }; + const response = await fetch(this.subgraphUrl, { + method: 'post', + body: JSON.stringify(requestQuery), + headers: { 'Content-Type': 'application/json', ChainId: this.chainId.toString() }, + }); + return response.json(); + } +} diff --git a/src/data/providers/balancer-api-provider/index.ts b/src/data/providers/balancer-api-provider/index.ts new file mode 100644 index 00000000..5468e7d1 --- /dev/null +++ b/src/data/providers/balancer-api-provider/index.ts @@ -0,0 +1,16 @@ +import { JoinData } from "./modules/join"; +import { BalancerApiClient } from "./client"; + +export default class BalancerApi { + + balancerApiClient: BalancerApiClient; + joinData: JoinData; + + + constructor(balancerApiUrl: string, chainId: number){ + this.balancerApiClient = new BalancerApiClient(balancerApiUrl, chainId); + this.joinData = new JoinData(this.balancerApiClient); + } + + +} \ No newline at end of file diff --git a/src/data/providers/balancer-api-provider/modules/join/index.ts b/src/data/providers/balancer-api-provider/modules/join/index.ts new file mode 100644 index 00000000..60a9c8cc --- /dev/null +++ b/src/data/providers/balancer-api-provider/modules/join/index.ts @@ -0,0 +1,88 @@ +import { BalancerApiClient } from '../../client'; +import { PoolState } from './types'; + +export class JoinData { + readonly poolStateQuery = `query GetPool($id: String!){ + poolGetPool(id:$id) { + id + address + name + type + version + ... on GqlPoolWeighted { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + ... on GqlPoolStable { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + ... on GqlPoolPhantomStable { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + ... on GqlPoolGyro { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + ... on GqlPoolLiquidityBootstrapping { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + ... on GqlPoolElement { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + ... on GqlPoolLiquidityBootstrapping { + tokens { + ... on GqlPoolTokenBase { + address + decimals + index + } + } + } + } +}`; + + constructor(private readonly balancerApiClient: BalancerApiClient) {} + + async fetchPoolState(id: string): Promise { + const { + data: { poolGetPool }, + } = await this.balancerApiClient.fetch('GetPool', this.poolStateQuery, { + id, + }); + return poolGetPool; + } +} diff --git a/src/data/providers/balancer-api-provider/modules/join/types.ts b/src/data/providers/balancer-api-provider/modules/join/types.ts new file mode 100644 index 00000000..5b23f242 --- /dev/null +++ b/src/data/providers/balancer-api-provider/modules/join/types.ts @@ -0,0 +1,14 @@ +import { Address } from "viem"; + +export type PoolState = { + id: Address; + address: Address; + type: string; + version: string; + tokens: { + address: Address; + decimals: number; + index: number; + }; +}[]; + From e1efe6b68f4528287410adb0a7d5782273959b7b Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 25 Sep 2023 11:35:45 +0100 Subject: [PATCH 064/199] Add changeset. --- .changeset/large-socks-exist.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/large-socks-exist.md diff --git a/.changeset/large-socks-exist.md b/.changeset/large-socks-exist.md new file mode 100644 index 00000000..bcff5dde --- /dev/null +++ b/.changeset/large-socks-exist.md @@ -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 From 5702561423d095a7ea1c535597498ec58d5e66ab Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 25 Sep 2023 10:22:23 -0300 Subject: [PATCH 065/199] Adding Example; Changing variable names; Changing class names; --- README.md | 60 +++++++++++++++++++ .../client/index.ts | 8 +-- .../index.ts | 6 +- .../modules/pool-state}/index.ts | 4 +- .../modules/pool-state}/types.ts | 0 src/entities/join/index.ts | 2 +- src/entities/join/parser.ts | 2 +- test/lib/utils/helper.ts | 2 +- test/weightedJoin.integration.test.ts | 20 +++---- 9 files changed, 82 insertions(+), 22 deletions(-) rename src/data/providers/{balancer-api-provider => balancer-api}/client/index.ts (72%) rename src/data/providers/{balancer-api-provider => balancer-api}/index.ts (69%) rename src/data/providers/{balancer-api-provider/modules/join => balancer-api/modules/pool-state}/index.ts (95%) rename src/data/providers/{balancer-api-provider/modules/join => balancer-api/modules/pool-state}/types.ts (100%) diff --git a/README.md b/README.md index 0c0e32ce..3e79f119 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,63 @@ If your platform does not support one of the required features, it is also possi Testing requires access to an archive node for onchain quote comparisons. This can be done using Infura. `pnpm test` + +## Examples + +### Balancer Api Provider + +Joining with pool state: +```ts + import BalancerApi from "@balancer/sdk/data/providers/balancer-api"; + ... + const joinInput: ProportionalJoinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + + const balancerApi = new BalancerApi('https://api-v3.balancer.fi/', 1); + const poolState = await balancerApi.pools.fetchSimplePoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); + const joinParser = new JoinParser(); + const poolJoin = joinParser.getJoin(poolState.type); + const queryResult = await weightedJoin.query(joinInput, poolState); + +``` + +Exiting with pool state: +```ts +import BalancerApi from "@balancer/sdk/data/providers/balancer-api"; +... +const joinInput: ProportionalJoinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, +}; + +const balancerApi = new BalancerApi('https://api-v3.balancer.fi/', 1); +const poolState = await balancerApi.pools.fetchSimplePoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); +const joinParser = new JoinParser(); +const poolJoin = joinParser.getJoin(poolState.type); +const queryResult = await weightedJoin.query(joinInput, poolState); +const slippage = Slippage.fromPercentage('1'); // 1% +const { call, to, value, maxAmountsIn, minBptOut } = + weightedJoin.buildCall({ + ...queryResult, + slippage, + sender, + recipient, + }); +const client = createClient({ + ... +}) + +await client.sendTransaction({ + account, + chain: client.chain, + data, + to, + value, +}); +``` \ No newline at end of file diff --git a/src/data/providers/balancer-api-provider/client/index.ts b/src/data/providers/balancer-api/client/index.ts similarity index 72% rename from src/data/providers/balancer-api-provider/client/index.ts rename to src/data/providers/balancer-api/client/index.ts index d1bd5ab9..40ac1ab3 100644 --- a/src/data/providers/balancer-api-provider/client/index.ts +++ b/src/data/providers/balancer-api/client/index.ts @@ -1,8 +1,8 @@ export class BalancerApiClient { - subgraphUrl: string; + apiUrl: string; chainId: number; - constructor(subgraphUrl: string, chainId: number) { - this.subgraphUrl = subgraphUrl; + constructor(apiUrl: string, chainId: number) { + this.apiUrl = apiUrl; this.chainId = chainId; } @@ -12,7 +12,7 @@ export class BalancerApiClient { query, variables, }; - const response = await fetch(this.subgraphUrl, { + const response = await fetch(this.apiUrl, { method: 'post', body: JSON.stringify(requestQuery), headers: { 'Content-Type': 'application/json', ChainId: this.chainId.toString() }, diff --git a/src/data/providers/balancer-api-provider/index.ts b/src/data/providers/balancer-api/index.ts similarity index 69% rename from src/data/providers/balancer-api-provider/index.ts rename to src/data/providers/balancer-api/index.ts index 5468e7d1..d854c6ba 100644 --- a/src/data/providers/balancer-api-provider/index.ts +++ b/src/data/providers/balancer-api/index.ts @@ -1,15 +1,15 @@ -import { JoinData } from "./modules/join"; +import { Pools } from "./modules/pool-state"; import { BalancerApiClient } from "./client"; export default class BalancerApi { balancerApiClient: BalancerApiClient; - joinData: JoinData; + pools: Pools; constructor(balancerApiUrl: string, chainId: number){ this.balancerApiClient = new BalancerApiClient(balancerApiUrl, chainId); - this.joinData = new JoinData(this.balancerApiClient); + this.pools = new Pools(this.balancerApiClient); } diff --git a/src/data/providers/balancer-api-provider/modules/join/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts similarity index 95% rename from src/data/providers/balancer-api-provider/modules/join/index.ts rename to src/data/providers/balancer-api/modules/pool-state/index.ts index 60a9c8cc..b78aa25b 100644 --- a/src/data/providers/balancer-api-provider/modules/join/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -1,7 +1,7 @@ import { BalancerApiClient } from '../../client'; import { PoolState } from './types'; -export class JoinData { +export class Pools { readonly poolStateQuery = `query GetPool($id: String!){ poolGetPool(id:$id) { id @@ -77,7 +77,7 @@ export class JoinData { constructor(private readonly balancerApiClient: BalancerApiClient) {} - async fetchPoolState(id: string): Promise { + async fetchSimplePoolState(id: string): Promise { const { data: { poolGetPool }, } = await this.balancerApiClient.fetch('GetPool', this.poolStateQuery, { diff --git a/src/data/providers/balancer-api-provider/modules/join/types.ts b/src/data/providers/balancer-api/modules/pool-state/types.ts similarity index 100% rename from src/data/providers/balancer-api-provider/modules/join/types.ts rename to src/data/providers/balancer-api/modules/pool-state/types.ts diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index b968ca7a..bff8b24e 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -45,7 +45,7 @@ export type JoinInput = | SingleAssetJoinInput | ProportionalJoinInput; -// Returned from a join query +// Returned from a pool-state query export type JoinQueryResult = { poolId: Hex; joinKind: JoinKind; diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts index 7506e603..c9f81a98 100644 --- a/src/entities/join/parser.ts +++ b/src/entities/join/parser.ts @@ -1,7 +1,7 @@ import { BaseJoin } from '.'; import { WeightedJoin } from './weighted/weightedJoin'; -/*********************** Basic Helper to get join class from pool type *************/ +/*********************** Basic Helper to get pool-state class from pool type *************/ export type JoinConfig = { customPoolJoins: Record; }; diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 9b3ffa22..1571aa18 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -291,7 +291,7 @@ export const forkSetup = async ( } for (let i = 0; i < tokens.length; i++) { - // Set initial account balance for each token that will be used to join pool + // Set initial account balance for each token that will be used to pool-state pool await setTokenBalance( client, accountAddress, diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 7ee6ffa9..26f91e6e 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -41,7 +41,7 @@ const slippage = Slippage.fromPercentage('1'); // 1% const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH -describe('weighted join test', () => { +describe('weighted pool-state test', () => { let api: MockApi; let client: Client & PublicActions & TestActions & WalletActions; let poolFromApi: PoolState; @@ -63,7 +63,7 @@ describe('weighted join test', () => { // get pool state from api poolFromApi = await api.getPool(poolId); - // setup join helper + // setup pool-state helper const joinParser = new JoinParser(); weightedJoin = joinParser.getJoin(poolFromApi.type); @@ -86,7 +86,7 @@ describe('weighted join test', () => { ); }); - test('unbalanced join', async () => { + test('unbalanced pool-state', async () => { const poolTokens = poolFromApi.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); @@ -94,7 +94,7 @@ describe('weighted join test', () => { TokenAmount.fromHumanAmount(t, '1'), ); - // perform join query to get expected bpt out + // perform pool-state query to get expected bpt out const joinInput: UnbalancedJoinInput = { amountsIn, chainId, @@ -127,7 +127,7 @@ describe('weighted join test', () => { expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); - test('native asset join', async () => { + test('native asset pool-state', async () => { const poolTokens = poolFromApi.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); @@ -135,7 +135,7 @@ describe('weighted join test', () => { TokenAmount.fromHumanAmount(t, '1'), ); - // perform join query to get expected bpt out + // perform pool-state query to get expected bpt out const joinInput: UnbalancedJoinInput = { amountsIn, chainId, @@ -171,11 +171,11 @@ describe('weighted join test', () => { expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); - test('single asset join', async () => { + test('single asset pool-state', async () => { const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); const tokenIn = '0x198d7387fa97a73f05b8578cdeff8f2a1f34cd1f'; - // perform join query to get expected bpt out + // perform pool-state query to get expected bpt out const joinInput: SingleAssetJoinInput = { bptOut, tokenIn, @@ -214,10 +214,10 @@ describe('weighted join test', () => { expect(minBptOut).to.eq(bptOut.amount); }); - test('proportional join', async () => { + test('proportional pool-state', async () => { const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); - // perform join query to get expected bpt out + // perform pool-state query to get expected bpt out const joinInput: ProportionalJoinInput = { bptOut, chainId, From 16376b789dcada6c0fa4d9bd6668c1122ad770eb Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 25 Sep 2023 10:35:31 -0300 Subject: [PATCH 066/199] Fixing wrong replacements --- .../client/index.ts | 0 .../index.ts | 0 .../modules/pool-state/index.ts | 0 .../modules/pool-state/types.ts | 0 src/entities/join/index.ts | 2 +- src/entities/join/parser.ts | 2 +- test/lib/utils/helper.ts | 2 +- test/weightedJoin.integration.test.ts | 20 +++++++++---------- 8 files changed, 13 insertions(+), 13 deletions(-) rename src/data/providers/{balancer-api => balancer-api-provider}/client/index.ts (100%) rename src/data/providers/{balancer-api => balancer-api-provider}/index.ts (100%) rename src/data/providers/{balancer-api => balancer-api-provider}/modules/pool-state/index.ts (100%) rename src/data/providers/{balancer-api => balancer-api-provider}/modules/pool-state/types.ts (100%) diff --git a/src/data/providers/balancer-api/client/index.ts b/src/data/providers/balancer-api-provider/client/index.ts similarity index 100% rename from src/data/providers/balancer-api/client/index.ts rename to src/data/providers/balancer-api-provider/client/index.ts diff --git a/src/data/providers/balancer-api/index.ts b/src/data/providers/balancer-api-provider/index.ts similarity index 100% rename from src/data/providers/balancer-api/index.ts rename to src/data/providers/balancer-api-provider/index.ts diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api-provider/modules/pool-state/index.ts similarity index 100% rename from src/data/providers/balancer-api/modules/pool-state/index.ts rename to src/data/providers/balancer-api-provider/modules/pool-state/index.ts diff --git a/src/data/providers/balancer-api/modules/pool-state/types.ts b/src/data/providers/balancer-api-provider/modules/pool-state/types.ts similarity index 100% rename from src/data/providers/balancer-api/modules/pool-state/types.ts rename to src/data/providers/balancer-api-provider/modules/pool-state/types.ts diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index bff8b24e..b968ca7a 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -45,7 +45,7 @@ export type JoinInput = | SingleAssetJoinInput | ProportionalJoinInput; -// Returned from a pool-state query +// Returned from a join query export type JoinQueryResult = { poolId: Hex; joinKind: JoinKind; diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts index c9f81a98..7506e603 100644 --- a/src/entities/join/parser.ts +++ b/src/entities/join/parser.ts @@ -1,7 +1,7 @@ import { BaseJoin } from '.'; import { WeightedJoin } from './weighted/weightedJoin'; -/*********************** Basic Helper to get pool-state class from pool type *************/ +/*********************** Basic Helper to get join class from pool type *************/ export type JoinConfig = { customPoolJoins: Record; }; diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 1571aa18..9b3ffa22 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -291,7 +291,7 @@ export const forkSetup = async ( } for (let i = 0; i < tokens.length; i++) { - // Set initial account balance for each token that will be used to pool-state pool + // Set initial account balance for each token that will be used to join pool await setTokenBalance( client, accountAddress, diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 26f91e6e..7ee6ffa9 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -41,7 +41,7 @@ const slippage = Slippage.fromPercentage('1'); // 1% const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH -describe('weighted pool-state test', () => { +describe('weighted join test', () => { let api: MockApi; let client: Client & PublicActions & TestActions & WalletActions; let poolFromApi: PoolState; @@ -63,7 +63,7 @@ describe('weighted pool-state test', () => { // get pool state from api poolFromApi = await api.getPool(poolId); - // setup pool-state helper + // setup join helper const joinParser = new JoinParser(); weightedJoin = joinParser.getJoin(poolFromApi.type); @@ -86,7 +86,7 @@ describe('weighted pool-state test', () => { ); }); - test('unbalanced pool-state', async () => { + test('unbalanced join', async () => { const poolTokens = poolFromApi.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); @@ -94,7 +94,7 @@ describe('weighted pool-state test', () => { TokenAmount.fromHumanAmount(t, '1'), ); - // perform pool-state query to get expected bpt out + // perform join query to get expected bpt out const joinInput: UnbalancedJoinInput = { amountsIn, chainId, @@ -127,7 +127,7 @@ describe('weighted pool-state test', () => { expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); - test('native asset pool-state', async () => { + test('native asset join', async () => { const poolTokens = poolFromApi.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ); @@ -135,7 +135,7 @@ describe('weighted pool-state test', () => { TokenAmount.fromHumanAmount(t, '1'), ); - // perform pool-state query to get expected bpt out + // perform join query to get expected bpt out const joinInput: UnbalancedJoinInput = { amountsIn, chainId, @@ -171,11 +171,11 @@ describe('weighted pool-state test', () => { expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); }); - test('single asset pool-state', async () => { + test('single asset join', async () => { const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); const tokenIn = '0x198d7387fa97a73f05b8578cdeff8f2a1f34cd1f'; - // perform pool-state query to get expected bpt out + // perform join query to get expected bpt out const joinInput: SingleAssetJoinInput = { bptOut, tokenIn, @@ -214,10 +214,10 @@ describe('weighted pool-state test', () => { expect(minBptOut).to.eq(bptOut.amount); }); - test('proportional pool-state', async () => { + test('proportional join', async () => { const bptOut = TokenAmount.fromHumanAmount(bpt, '1'); - // perform pool-state query to get expected bpt out + // perform join query to get expected bpt out const joinInput: ProportionalJoinInput = { bptOut, chainId, From 581478e5cdee26887d539764268b91201bf36c2d Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 25 Sep 2023 11:21:06 -0300 Subject: [PATCH 067/199] Changing folder name "balancer-api-provider" to "balancer-api --- package.json | 3 ++- .../{balancer-api-provider => balancer-api}/client/index.ts | 0 .../providers/{balancer-api-provider => balancer-api}/index.ts | 0 .../modules/pool-state/index.ts | 0 .../modules/pool-state/types.ts | 0 5 files changed, 2 insertions(+), 1 deletion(-) rename src/data/providers/{balancer-api-provider => balancer-api}/client/index.ts (100%) rename src/data/providers/{balancer-api-provider => balancer-api}/index.ts (100%) rename src/data/providers/{balancer-api-provider => balancer-api}/modules/pool-state/index.ts (100%) rename src/data/providers/{balancer-api-provider => balancer-api}/modules/pool-state/types.ts (100%) diff --git a/package.json b/package.json index f6ccb0ba..f04ddbd2 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "test:ci": "vitest run", "changeset": "changeset", "changeset:release": "pnpm build && changeset publish", - "node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)" + "node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)", + "example": "npx ts-node -P tsconfig.testing.json -r tsconfig-paths/register" }, "dependencies": { "async-retry": "^1.3.3", diff --git a/src/data/providers/balancer-api-provider/client/index.ts b/src/data/providers/balancer-api/client/index.ts similarity index 100% rename from src/data/providers/balancer-api-provider/client/index.ts rename to src/data/providers/balancer-api/client/index.ts diff --git a/src/data/providers/balancer-api-provider/index.ts b/src/data/providers/balancer-api/index.ts similarity index 100% rename from src/data/providers/balancer-api-provider/index.ts rename to src/data/providers/balancer-api/index.ts diff --git a/src/data/providers/balancer-api-provider/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts similarity index 100% rename from src/data/providers/balancer-api-provider/modules/pool-state/index.ts rename to src/data/providers/balancer-api/modules/pool-state/index.ts diff --git a/src/data/providers/balancer-api-provider/modules/pool-state/types.ts b/src/data/providers/balancer-api/modules/pool-state/types.ts similarity index 100% rename from src/data/providers/balancer-api-provider/modules/pool-state/types.ts rename to src/data/providers/balancer-api/modules/pool-state/types.ts From 8f52f44a2f68449ba3c9fdacedcd30131cca6110 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 25 Sep 2023 14:16:48 -0300 Subject: [PATCH 068/199] [WIP] Creating example for api integration; Changing function name; --- examples/join.ts | 12 ++++++++++++ package.json | 1 + src/data/providers/balancer-api/index.ts | 2 +- .../balancer-api/modules/pool-state/index.ts | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 examples/join.ts diff --git a/examples/join.ts b/examples/join.ts new file mode 100644 index 00000000..262f1df0 --- /dev/null +++ b/examples/join.ts @@ -0,0 +1,12 @@ + +import { BalancerApi } from "../src/data/providers/balancer-api"; + +const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; +const poolId = '0x481c5fc05d63a58aa2f0f2aa417c021b5d419cb200000000000000000000056a'; +const join = async () => { + const balancerApi = new BalancerApi(balancerApiUrl, 1); + const poolState = await balancerApi.pools.fetchPoolState(poolId); + console.log(poolState); +} + +join(); \ No newline at end of file diff --git a/package.json b/package.json index f04ddbd2..5418c9a7 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "pino-pretty": "^10.0.0", "rome": "12.1.3", "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", "tsup": "^6.6.0", "typescript": "^5.0.4", "vite": "^4.4.2", diff --git a/src/data/providers/balancer-api/index.ts b/src/data/providers/balancer-api/index.ts index d854c6ba..13f85a0a 100644 --- a/src/data/providers/balancer-api/index.ts +++ b/src/data/providers/balancer-api/index.ts @@ -1,7 +1,7 @@ import { Pools } from "./modules/pool-state"; import { BalancerApiClient } from "./client"; -export default class BalancerApi { +export class BalancerApi { balancerApiClient: BalancerApiClient; pools: Pools; diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index b78aa25b..2f3a273d 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -77,7 +77,7 @@ export class Pools { constructor(private readonly balancerApiClient: BalancerApiClient) {} - async fetchSimplePoolState(id: string): Promise { + async fetchPoolState(id: string): Promise { const { data: { poolGetPool }, } = await this.balancerApiClient.fetch('GetPool', this.poolStateQuery, { From 5e01345e1ddef86442583c504ddff0f34275db20 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 25 Sep 2023 23:43:41 -0300 Subject: [PATCH 069/199] Changing "Weighted" to "WEIGHTED" (format that comes from the API); Creating example for Join Weighted Pool; --- examples/join.ts | 12 --- examples/join/weighted.ts | 94 +++++++++++++++++++ .../balancer-api/modules/pool-state/index.ts | 2 +- .../balancer-api/modules/pool-state/types.ts | 4 +- src/entities/exit/parser.ts | 2 +- src/entities/join/parser.ts | 2 +- test/weightedExit.integration.test.ts | 3 +- test/weightedJoin.integration.test.ts | 2 +- 8 files changed, 102 insertions(+), 19 deletions(-) delete mode 100644 examples/join.ts create mode 100644 examples/join/weighted.ts diff --git a/examples/join.ts b/examples/join.ts deleted file mode 100644 index 262f1df0..00000000 --- a/examples/join.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import { BalancerApi } from "../src/data/providers/balancer-api"; - -const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; -const poolId = '0x481c5fc05d63a58aa2f0f2aa417c021b5d419cb200000000000000000000056a'; -const join = async () => { - const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState = await balancerApi.pools.fetchPoolState(poolId); - console.log(poolState); -} - -join(); \ No newline at end of file diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts new file mode 100644 index 00000000..7869847c --- /dev/null +++ b/examples/join/weighted.ts @@ -0,0 +1,94 @@ +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 { + Client, + createTestClient, + http, + parseUnits, + PublicActions, + publicActions, + TestActions, WalletActions, + walletActions +} 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 +const chainId = ChainId.MAINNET; +const rpcUrl = 'http://127.0.0.1:8545/'; +const blockNumber = BigInt(18043296); +const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig +const slippage = Slippage.fromPercentage('1'); // 1% + +const join = async () => { + const balancerApi = new BalancerApi(balancerApiUrl, 1); + const poolState: PoolState = await balancerApi.pools.fetchPoolState(poolId); + let client: Client & PublicActions & TestActions & WalletActions; + client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + await forkSetup( + client, + testAddress, + [...poolState.tokens.map((t) => t.address), poolState.address], + undefined, // TODO: hardcode these values to improve test performance + [ + ...poolState.tokens.map((t) => parseUnits('100', t.decimals)), + parseUnits('100', 18), + ], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); + + + const joinParser = new JoinParser(); + const weightedJoin = joinParser.getJoin(poolState.type); + + const poolTokens = poolState.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const amountsIn = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + + const queryResult = await weightedJoin.query(joinInput, poolState); + + const { call, to, value, maxAmountsIn, minBptOut } = + weightedJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolState.tokens.map(({address})=>address), poolState.address /*BPT*/], + client, + testAddress, + to, + call, + value, + ); + console.log(transactionReceipt); + console.log(balanceDeltas); +} + +join(); \ No newline at end of file diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index 2f3a273d..938c1329 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -77,7 +77,7 @@ export class Pools { constructor(private readonly balancerApiClient: BalancerApiClient) {} - async fetchPoolState(id: string): Promise { + async fetchPoolState(id: string): Promise { const { data: { poolGetPool }, } = await this.balancerApiClient.fetch('GetPool', this.poolStateQuery, { diff --git a/src/data/providers/balancer-api/modules/pool-state/types.ts b/src/data/providers/balancer-api/modules/pool-state/types.ts index 5b23f242..88f908da 100644 --- a/src/data/providers/balancer-api/modules/pool-state/types.ts +++ b/src/data/providers/balancer-api/modules/pool-state/types.ts @@ -9,6 +9,6 @@ export type PoolState = { address: Address; decimals: number; index: number; - }; -}[]; + }[]; +}; diff --git a/src/entities/exit/parser.ts b/src/entities/exit/parser.ts index b2cf670b..ff9627fa 100644 --- a/src/entities/exit/parser.ts +++ b/src/entities/exit/parser.ts @@ -9,7 +9,7 @@ export class ExitParser { constructor(config?: ExitConfig) { const { customPoolExits } = config || {}; this.poolExits = { - Weighted: new WeightedExit(), + WEIGHTED: new WeightedExit(), // custom pool Exits take precedence over base Exits ...customPoolExits, }; diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts index 7506e603..2dfd87b8 100644 --- a/src/entities/join/parser.ts +++ b/src/entities/join/parser.ts @@ -12,7 +12,7 @@ export class JoinParser { constructor(config?: JoinConfig) { const { customPoolJoins } = config || {}; this.poolJoins = { - Weighted: new WeightedJoin(), + WEIGHTED: new WeightedJoin(), // custom pool Joins take precedence over base Joins ...customPoolJoins, }; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 95aeff1b..c3fd4512 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -93,6 +93,7 @@ describe('weighted exit test', () => { tokenOut, kind: ExitKind.SINGLE_ASSET, }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction( exitInput, poolFromApi.tokens.map((t) => t.address), @@ -317,7 +318,7 @@ export class MockApi { return { id, address: getPoolAddress(id) as Address, - type: 'Weighted', + type: 'WEIGHTED', tokens, }; } diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 7ee6ffa9..d993f511 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -323,7 +323,7 @@ export class MockApi { return { id, address: getPoolAddress(id) as Address, - type: 'Weighted', + type: 'WEIGHTED', tokens, }; } From 1d54c4c2a7d13afcbdb42d7e56001a83f3404d3b Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 26 Sep 2023 15:11:37 +0100 Subject: [PATCH 070/199] fix: Circular deps. --- src/data/enrichers/onChainPoolDataEnricher.ts | 3 +- src/data/types.ts | 3 +- src/entities/exit/weighted/validateInputs.ts | 2 +- src/entities/join/weighted/validateInputs.ts | 2 +- src/entities/join/weighted/weightedJoin.ts | 6 +- src/entities/path.ts | 3 +- src/entities/pools/fx/fxFactory.ts | 2 +- src/entities/pools/fx/fxMath.ts | 4 +- src/entities/pools/fx/fxPool.ts | 107 +----------------- src/entities/pools/fx/fxPoolToken.ts | 90 +++++++++++++++ src/entities/pools/fx/helpers.ts | 2 +- src/entities/pools/fx/index.ts | 2 + src/entities/pools/fx/types.ts | 17 +++ src/entities/pools/gyro2/gyro2Factory.ts | 4 +- src/entities/pools/gyro2/gyro2Pool.ts | 3 +- src/entities/pools/gyro3/gyro3Factory.ts | 4 +- src/entities/pools/gyro3/gyro3Pool.ts | 3 +- src/entities/pools/gyroE/gyroEFactory.ts | 4 +- src/entities/pools/gyroE/gyroEMath.ts | 2 +- .../pools/gyroE/gyroEMathFunctions.ts | 2 +- src/entities/pools/gyroE/gyroEMathHelpers.ts | 2 +- src/entities/pools/gyroE/gyroEPool.ts | 29 +---- src/entities/pools/gyroE/index.ts | 1 + src/entities/pools/gyroE/types.ts | 22 ++++ src/entities/pools/index.ts | 3 +- src/entities/pools/linear/index.ts | 1 + src/entities/pools/linear/linearFactory.ts | 4 +- src/entities/pools/linear/linearMath.ts | 4 +- src/entities/pools/linear/linearPool.ts | 11 +- src/entities/pools/linear/types.ts | 6 + .../pools/metastable/metastableFactory.ts | 4 +- src/entities/pools/stable/stableFactory.ts | 4 +- src/entities/pools/stable/stablePool.ts | 3 +- .../pools/weighted/weightedFactory.ts | 4 +- src/entities/pools/weighted/weightedPool.ts | 3 +- src/entities/tokenAmount.ts | 3 +- src/entities/utils/doQueryExit.ts | 4 +- src/pathGraph/pathGraphTypes.ts | 11 +- src/types.ts | 12 +- src/utils/helpers.ts | 3 +- 40 files changed, 214 insertions(+), 185 deletions(-) create mode 100644 src/entities/pools/fx/fxPoolToken.ts create mode 100644 src/entities/pools/fx/types.ts create mode 100644 src/entities/pools/gyroE/types.ts create mode 100644 src/entities/pools/linear/types.ts diff --git a/src/data/enrichers/onChainPoolDataEnricher.ts b/src/data/enrichers/onChainPoolDataEnricher.ts index 3903e565..e7cb9773 100644 --- a/src/data/enrichers/onChainPoolDataEnricher.ts +++ b/src/data/enrichers/onChainPoolDataEnricher.ts @@ -6,6 +6,7 @@ import { RawPool, RawPoolTokenWithRate, RawWeightedPoolToken, + HumanAmount, } from '../types'; import { @@ -16,7 +17,7 @@ import { poolHasVirtualSupply, poolIsLinearPool, } from '../../utils'; -import { HumanAmount, SwapOptions } from '../../types'; +import { SwapOptions } from '../../types'; interface OnChainPoolData { id: string; diff --git a/src/data/types.ts b/src/data/types.ts index fa08eb3d..d322afab 100644 --- a/src/data/types.ts +++ b/src/data/types.ts @@ -1,5 +1,6 @@ import { Address, Hex } from 'viem'; -import { HumanAmount } from '../types'; + +export type HumanAmount = `${number}`; // These are only the known pool types, additional pool types can be added via // extension through custom PoolFactories and PoolDataProviders diff --git a/src/entities/exit/weighted/validateInputs.ts b/src/entities/exit/weighted/validateInputs.ts index 2b89aab7..aaf86f10 100644 --- a/src/entities/exit/weighted/validateInputs.ts +++ b/src/entities/exit/weighted/validateInputs.ts @@ -1,4 +1,4 @@ -import { ExitInput, ExitKind } from '..'; +import { ExitInput, ExitKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; diff --git a/src/entities/join/weighted/validateInputs.ts b/src/entities/join/weighted/validateInputs.ts index 02a9b053..733073c8 100644 --- a/src/entities/join/weighted/validateInputs.ts +++ b/src/entities/join/weighted/validateInputs.ts @@ -1,4 +1,4 @@ -import { JoinInput, JoinKind } from '..'; +import { JoinInput, JoinKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index b10fe0fc..262b52b8 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -1,5 +1,7 @@ import { encodeFunctionData } from 'viem'; -import { Token, TokenAmount, WeightedEncoder } from '../../..'; +import { Token } from '../../../entities/token'; +import { TokenAmount } from '../../../entities/tokenAmount'; +import { WeightedEncoder } from '../../../entities/encoders/weighted'; import { Address } from '../../../types'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; @@ -10,7 +12,7 @@ import { JoinInput, JoinKind, JoinQueryResult, -} from '..'; +} from '../types'; import { AmountsJoin, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; diff --git a/src/entities/path.ts b/src/entities/path.ts index 8055a2e2..d9053a8d 100644 --- a/src/entities/path.ts +++ b/src/entities/path.ts @@ -1,5 +1,6 @@ import { BasePool } from './pools/'; -import { Token, TokenAmount } from './'; +import { Token } from './token'; +import { TokenAmount } from './tokenAmount'; import { SwapKind } from '../types'; export class Path { diff --git a/src/entities/pools/fx/fxFactory.ts b/src/entities/pools/fx/fxFactory.ts index 04d17822..b2c9dca9 100644 --- a/src/entities/pools/fx/fxFactory.ts +++ b/src/entities/pools/fx/fxFactory.ts @@ -1,5 +1,5 @@ import { BasePool, BasePoolFactory } from '../'; -import { FxPool } from './'; +import { FxPool } from './fxPool'; import { RawFxPool, RawPool } from '../../../data/types'; export class FxPoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/fx/fxMath.ts b/src/entities/pools/fx/fxMath.ts index e73f6ead..70b15b21 100644 --- a/src/entities/pools/fx/fxMath.ts +++ b/src/entities/pools/fx/fxMath.ts @@ -1,6 +1,6 @@ import { parseUnits } from 'viem'; -import { RAY } from '../../../utils'; -import { FxPoolPairData } from './fxPool'; +import { RAY } from '../../../utils/math'; +import { FxPoolPairData } from './types'; import { SwapKind } from '../../../types'; export const CURVEMATH_MAX_DIFF = parseUnits('-0.000001000000000000024', 36); diff --git a/src/entities/pools/fx/fxPool.ts b/src/entities/pools/fx/fxPool.ts index afc163f9..d264efc5 100644 --- a/src/entities/pools/fx/fxPool.ts +++ b/src/entities/pools/fx/fxPool.ts @@ -1,11 +1,14 @@ import { Hex, parseEther, parseUnits } from 'viem'; -import { HumanAmount, PoolType, SwapKind } from '../../../types'; -import { BigintIsh, Token, TokenAmount } from '../../'; +import { PoolType, SwapKind } from '../../../types'; +import { Token } from '../../token'; +import { TokenAmount } from '../../tokenAmount'; import { BasePool } from '../../pools'; -import { RAY, WAD, getPoolAddress } from '../../../utils'; +import { RAY, getPoolAddress } from '../../../utils'; import { _calcInGivenOut, _calcOutGivenIn } from './fxMath'; import { RawFxPool } from '../../../data/types'; import { MathFx, parseFixedCurveParam } from './helpers'; +import { FxPoolPairData } from './types'; +import { FxPoolToken } from './fxPoolToken'; const isUSDC = (address: string): boolean => { return ( @@ -15,104 +18,6 @@ const isUSDC = (address: string): boolean => { ); }; -export type FxPoolPairData = { - tIn: FxPoolToken; - tOut: FxPoolToken; - alpha: bigint; - beta: bigint; - delta: bigint; - lambda: bigint; - _oGLiq: bigint; - _nGLiq: bigint; - _oBals: bigint[]; - _nBals: bigint[]; - givenToken: FxPoolToken; - swapKind: SwapKind; -}; - -export class FxPoolToken extends TokenAmount { - public readonly index: number; - public readonly latestFXPrice: HumanAmount; - public readonly fxOracleDecimals: number; - public numeraire: bigint; // in 36 decimals - private readonly scalar36 = this.scalar * WAD; - - public constructor( - token: Token, - amount: BigintIsh, - latestFXPrice: HumanAmount, - fxOracleDecimals: number, - index: number, - ) { - super(token, amount); - this.latestFXPrice = latestFXPrice; - this.fxOracleDecimals = fxOracleDecimals; - const truncatedNumeraire = MathFx.mulDownFixed( - this.amount, - parseUnits(this.latestFXPrice, this.fxOracleDecimals), - this.fxOracleDecimals, - ); - this.numeraire = truncatedNumeraire * this.scalar36; - this.index = index; - } - - public increase(amount: bigint): TokenAmount { - this.amount = this.amount + amount; - this.scale18 = this.amount * this.scalar; - const truncatedNumeraire = MathFx.mulDownFixed( - this.amount, - parseUnits(this.latestFXPrice, this.fxOracleDecimals), - this.fxOracleDecimals, - ); - this.numeraire = truncatedNumeraire * this.scalar36; - return this; - } - - public decrease(amount: bigint): TokenAmount { - this.amount = this.amount - amount; - this.scale18 = this.amount * this.scalar; - const truncatedNumeraire = MathFx.mulDownFixed( - this.amount, - parseUnits(this.latestFXPrice, this.fxOracleDecimals), - this.fxOracleDecimals, - ); - this.numeraire = truncatedNumeraire * this.scalar36; - return this; - } - - public static fromNumeraire( - poolToken: FxPoolToken, - numeraire: BigintIsh, - divUp?: boolean, - ): FxPoolToken { - const truncatedNumeraire = BigInt(numeraire) / poolToken.scalar36; // loss of precision required to match SC implementation - const amount = divUp - ? MathFx.divUpFixed( - BigInt(truncatedNumeraire), - parseUnits( - poolToken.latestFXPrice, - poolToken.fxOracleDecimals, - ), - poolToken.fxOracleDecimals, - ) - : MathFx.divDownFixed( - BigInt(truncatedNumeraire), - parseUnits( - poolToken.latestFXPrice, - poolToken.fxOracleDecimals, - ), - poolToken.fxOracleDecimals, - ); - return new FxPoolToken( - poolToken.token, - amount, - poolToken.latestFXPrice, - poolToken.fxOracleDecimals, - poolToken.index, - ); - } -} - export class FxPool implements BasePool { public readonly chainId: number; public readonly id: Hex; diff --git a/src/entities/pools/fx/fxPoolToken.ts b/src/entities/pools/fx/fxPoolToken.ts new file mode 100644 index 00000000..75f2d1f4 --- /dev/null +++ b/src/entities/pools/fx/fxPoolToken.ts @@ -0,0 +1,90 @@ +import { parseUnits } from 'viem'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; +import { WAD } from '../../../utils/math'; +import { _calcInGivenOut, _calcOutGivenIn } from './fxMath'; +import { HumanAmount } from '../../../data/types'; +import { MathFx } from './helpers'; + +export class FxPoolToken extends TokenAmount { + public readonly index: number; + public readonly latestFXPrice: HumanAmount; + public readonly fxOracleDecimals: number; + public numeraire: bigint; // in 36 decimals + private readonly scalar36 = this.scalar * WAD; + + public constructor( + token: Token, + amount: BigintIsh, + latestFXPrice: HumanAmount, + fxOracleDecimals: number, + index: number, + ) { + super(token, amount); + this.latestFXPrice = latestFXPrice; + this.fxOracleDecimals = fxOracleDecimals; + const truncatedNumeraire = MathFx.mulDownFixed( + this.amount, + parseUnits(this.latestFXPrice, this.fxOracleDecimals), + this.fxOracleDecimals, + ); + this.numeraire = truncatedNumeraire * this.scalar36; + this.index = index; + } + + public increase(amount: bigint): TokenAmount { + this.amount = this.amount + amount; + this.scale18 = this.amount * this.scalar; + const truncatedNumeraire = MathFx.mulDownFixed( + this.amount, + parseUnits(this.latestFXPrice, this.fxOracleDecimals), + this.fxOracleDecimals, + ); + this.numeraire = truncatedNumeraire * this.scalar36; + return this; + } + + public decrease(amount: bigint): TokenAmount { + this.amount = this.amount - amount; + this.scale18 = this.amount * this.scalar; + const truncatedNumeraire = MathFx.mulDownFixed( + this.amount, + parseUnits(this.latestFXPrice, this.fxOracleDecimals), + this.fxOracleDecimals, + ); + this.numeraire = truncatedNumeraire * this.scalar36; + return this; + } + + public static fromNumeraire( + poolToken: FxPoolToken, + numeraire: BigintIsh, + divUp?: boolean, + ): FxPoolToken { + const truncatedNumeraire = BigInt(numeraire) / poolToken.scalar36; // loss of precision required to match SC implementation + const amount = divUp + ? MathFx.divUpFixed( + BigInt(truncatedNumeraire), + parseUnits( + poolToken.latestFXPrice, + poolToken.fxOracleDecimals, + ), + poolToken.fxOracleDecimals, + ) + : MathFx.divDownFixed( + BigInt(truncatedNumeraire), + parseUnits( + poolToken.latestFXPrice, + poolToken.fxOracleDecimals, + ), + poolToken.fxOracleDecimals, + ); + return new FxPoolToken( + poolToken.token, + amount, + poolToken.latestFXPrice, + poolToken.fxOracleDecimals, + poolToken.index, + ); + } +} diff --git a/src/entities/pools/fx/helpers.ts b/src/entities/pools/fx/helpers.ts index ddd9bb52..cfedbb46 100644 --- a/src/entities/pools/fx/helpers.ts +++ b/src/entities/pools/fx/helpers.ts @@ -1,5 +1,5 @@ import { parseUnits } from 'viem'; -import { HumanAmount } from '../../../types'; +import { HumanAmount } from '../../../data/types'; export class MathFx { static mulDownFixed(a: bigint, b: bigint, decimals = 36): bigint { diff --git a/src/entities/pools/fx/index.ts b/src/entities/pools/fx/index.ts index 6686b03e..ca789301 100644 --- a/src/entities/pools/fx/index.ts +++ b/src/entities/pools/fx/index.ts @@ -1,3 +1,5 @@ export * from './fxFactory'; export * from './fxPool'; export * from './fxMath'; +export * from './types'; +export * from './fxPoolToken'; diff --git a/src/entities/pools/fx/types.ts b/src/entities/pools/fx/types.ts new file mode 100644 index 00000000..c1a28fb0 --- /dev/null +++ b/src/entities/pools/fx/types.ts @@ -0,0 +1,17 @@ +import { SwapKind } from '../../../types'; +import { FxPoolToken } from './fxPoolToken'; + +export type FxPoolPairData = { + tIn: FxPoolToken; + tOut: FxPoolToken; + alpha: bigint; + beta: bigint; + delta: bigint; + lambda: bigint; + _oGLiq: bigint; + _nGLiq: bigint; + _oBals: bigint[]; + _nBals: bigint[]; + givenToken: FxPoolToken; + swapKind: SwapKind; +}; diff --git a/src/entities/pools/gyro2/gyro2Factory.ts b/src/entities/pools/gyro2/gyro2Factory.ts index a57272fa..9014c7a4 100644 --- a/src/entities/pools/gyro2/gyro2Factory.ts +++ b/src/entities/pools/gyro2/gyro2Factory.ts @@ -1,5 +1,5 @@ -import { Gyro2Pool } from '.'; -import { BasePool, BasePoolFactory } from '..'; +import { Gyro2Pool } from './gyro2Pool'; +import { BasePool, BasePoolFactory } from '../index'; import { RawGyro2Pool, RawPool } from '../../../data/types'; export class Gyro2PoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/gyro2/gyro2Pool.ts b/src/entities/pools/gyro2/gyro2Pool.ts index 4e0711dd..bf9274a4 100644 --- a/src/entities/pools/gyro2/gyro2Pool.ts +++ b/src/entities/pools/gyro2/gyro2Pool.ts @@ -7,7 +7,8 @@ import { _findVirtualParams, } from './gyro2Math'; import { BasePool } from '..'; -import { BigintIsh, Token, TokenAmount } from '../..'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; import { RawGyro2Pool } from '../../../data/types'; import { PoolType, SwapKind } from '../../../types'; import { getPoolAddress, MathSol, WAD } from '../../../utils'; diff --git a/src/entities/pools/gyro3/gyro3Factory.ts b/src/entities/pools/gyro3/gyro3Factory.ts index e938f5e0..75d038a3 100644 --- a/src/entities/pools/gyro3/gyro3Factory.ts +++ b/src/entities/pools/gyro3/gyro3Factory.ts @@ -1,5 +1,5 @@ -import { Gyro3Pool } from '.'; -import { BasePool, BasePoolFactory } from '..'; +import { Gyro3Pool } from './gyro3Pool'; +import { BasePool, BasePoolFactory } from '../index'; import { RawGyro3Pool, RawPool } from '../../../data/types'; export class Gyro3PoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/gyro3/gyro3Pool.ts b/src/entities/pools/gyro3/gyro3Pool.ts index 0a7d3093..b4f7a6a2 100644 --- a/src/entities/pools/gyro3/gyro3Pool.ts +++ b/src/entities/pools/gyro3/gyro3Pool.ts @@ -6,7 +6,8 @@ import { _calculateInvariant, } from './gyro3Math'; import { BasePool } from '..'; -import { BigintIsh, Token, TokenAmount } from '../..'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; import { RawGyro3Pool } from '../../../data/types'; import { PoolType, SwapKind } from '../../../types'; import { getPoolAddress, MathSol, WAD } from '../../../utils'; diff --git a/src/entities/pools/gyroE/gyroEFactory.ts b/src/entities/pools/gyroE/gyroEFactory.ts index 98a527d6..63e0816e 100644 --- a/src/entities/pools/gyroE/gyroEFactory.ts +++ b/src/entities/pools/gyroE/gyroEFactory.ts @@ -1,5 +1,5 @@ -import { GyroEPool } from '.'; -import { BasePool, BasePoolFactory } from '..'; +import { GyroEPool } from './gyroEPool'; +import { BasePool, BasePoolFactory } from '../index'; import { RawGyroEPool, RawPool } from '../../../data/types'; export class GyroEPoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/gyroE/gyroEMath.ts b/src/entities/pools/gyroE/gyroEMath.ts index c5c909cc..690244ab 100644 --- a/src/entities/pools/gyroE/gyroEMath.ts +++ b/src/entities/pools/gyroE/gyroEMath.ts @@ -11,7 +11,7 @@ import { normalizedLiquidityXIn, normalizedLiquidityYIn, } from './gyroEMathFunctions'; -import { DerivedGyroEParams, GyroEParams, Vector2 } from './gyroEPool'; +import { DerivedGyroEParams, GyroEParams, Vector2 } from './types'; import { MathGyro, ONE_XP, SMALL } from '../../../utils/gyroHelpers/math'; export function calculateNormalizedLiquidity( diff --git a/src/entities/pools/gyroE/gyroEMathFunctions.ts b/src/entities/pools/gyroE/gyroEMathFunctions.ts index 48ad7bd7..2893c5d6 100644 --- a/src/entities/pools/gyroE/gyroEMathFunctions.ts +++ b/src/entities/pools/gyroE/gyroEMathFunctions.ts @@ -1,7 +1,7 @@ import { WAD } from '../../../utils'; import { MathGyro } from '../../../utils/gyroHelpers/math'; import { virtualOffset0, virtualOffset1 } from './gyroEMathHelpers'; -import { DerivedGyroEParams, GyroEParams, Vector2 } from './gyroEPool'; +import { DerivedGyroEParams, GyroEParams, Vector2 } from './types'; ///////// /// SPOT PRICE DERIVATIVE CALCULATIONS diff --git a/src/entities/pools/gyroE/gyroEMathHelpers.ts b/src/entities/pools/gyroE/gyroEMathHelpers.ts index 799d7970..7ba22411 100644 --- a/src/entities/pools/gyroE/gyroEMathHelpers.ts +++ b/src/entities/pools/gyroE/gyroEMathHelpers.ts @@ -1,5 +1,5 @@ import { MAX_BALANCES } from './constants'; -import { DerivedGyroEParams, GyroEParams, Vector2 } from './gyroEPool'; +import { DerivedGyroEParams, GyroEParams, Vector2 } from './types'; import { MathGyro, ONE_XP } from '../../../utils/gyroHelpers/math'; ///////// diff --git a/src/entities/pools/gyroE/gyroEPool.ts b/src/entities/pools/gyroE/gyroEPool.ts index 13868f64..4f165152 100644 --- a/src/entities/pools/gyroE/gyroEPool.ts +++ b/src/entities/pools/gyroE/gyroEPool.ts @@ -1,7 +1,8 @@ import { Hex, parseEther, parseUnits } from 'viem'; -import { BasePool } from '..'; -import { BigintIsh, Token, TokenAmount } from '../..'; +import { BasePool } from '../index'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; import { RawGyroEPool } from '../../../data/types'; import { PoolType, SwapKind } from '../../../types'; import { MathSol, WAD, getPoolAddress } from '../../../utils'; @@ -16,6 +17,7 @@ import { calculateInvariantWithError, } from './gyroEMath'; import { MathGyro, SWAP_LIMIT_FACTOR } from '../../../utils/gyroHelpers/math'; +import { GyroEParams, Vector2, DerivedGyroEParams } from './types'; export class GyroEPoolToken extends TokenAmount { public readonly rate: bigint; @@ -46,29 +48,6 @@ export class GyroEPoolToken extends TokenAmount { } } -export type GyroEParams = { - alpha: bigint; - beta: bigint; - c: bigint; - s: bigint; - lambda: bigint; -}; - -export type Vector2 = { - x: bigint; - y: bigint; -}; - -export type DerivedGyroEParams = { - tauAlpha: Vector2; - tauBeta: Vector2; - u: bigint; - v: bigint; - w: bigint; - z: bigint; - dSq: bigint; -}; - export class GyroEPool implements BasePool { public readonly chainId: number; public readonly id: Hex; diff --git a/src/entities/pools/gyroE/index.ts b/src/entities/pools/gyroE/index.ts index 0fabeb53..d9d4c7c7 100644 --- a/src/entities/pools/gyroE/index.ts +++ b/src/entities/pools/gyroE/index.ts @@ -1,3 +1,4 @@ export * from './gyroEFactory'; export * from './gyroEPool'; export * from './gyroEMath'; +export * from './types'; diff --git a/src/entities/pools/gyroE/types.ts b/src/entities/pools/gyroE/types.ts new file mode 100644 index 00000000..5fda0b2b --- /dev/null +++ b/src/entities/pools/gyroE/types.ts @@ -0,0 +1,22 @@ +export type GyroEParams = { + alpha: bigint; + beta: bigint; + c: bigint; + s: bigint; + lambda: bigint; +}; + +export type Vector2 = { + x: bigint; + y: bigint; +}; + +export type DerivedGyroEParams = { + tauAlpha: Vector2; + tauBeta: Vector2; + u: bigint; + v: bigint; + w: bigint; + z: bigint; + dSq: bigint; +}; diff --git a/src/entities/pools/index.ts b/src/entities/pools/index.ts index fc7d6e9a..23298f2f 100644 --- a/src/entities/pools/index.ts +++ b/src/entities/pools/index.ts @@ -1,6 +1,7 @@ import { Hex } from 'viem'; import { PoolType, SwapKind } from '../../types'; -import { Token, TokenAmount } from '../'; +import { Token } from '../token'; +import { TokenAmount } from '../tokenAmount'; import { RawPool } from '../../data/types'; export interface BasePool { diff --git a/src/entities/pools/linear/index.ts b/src/entities/pools/linear/index.ts index e64a3b8e..f2a02c35 100644 --- a/src/entities/pools/linear/index.ts +++ b/src/entities/pools/linear/index.ts @@ -1,3 +1,4 @@ export * from './linearFactory'; export * from './linearPool'; export * from './linearMath'; +export * from './types'; diff --git a/src/entities/pools/linear/linearFactory.ts b/src/entities/pools/linear/linearFactory.ts index e53c7abb..d7e3aac1 100644 --- a/src/entities/pools/linear/linearFactory.ts +++ b/src/entities/pools/linear/linearFactory.ts @@ -1,5 +1,5 @@ -import { BasePool, BasePoolFactory } from '../'; -import { LinearPool } from './'; +import { BasePool, BasePoolFactory } from '../index'; +import { LinearPool } from './linearPool'; import { RawLinearPool, RawPool } from '../../../data/types'; export class LinearPoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/linear/linearMath.ts b/src/entities/pools/linear/linearMath.ts index 56383314..8c18e0bb 100644 --- a/src/entities/pools/linear/linearMath.ts +++ b/src/entities/pools/linear/linearMath.ts @@ -1,5 +1,5 @@ -import { MathSol, WAD } from '../../../utils/'; -import { Params } from './'; +import { MathSol, WAD } from '../../../utils/math'; +import { Params } from './types'; export function _calcWrappedOutPerMainIn( mainIn: bigint, diff --git a/src/entities/pools/linear/linearPool.ts b/src/entities/pools/linear/linearPool.ts index 840831e4..2cefa1ec 100644 --- a/src/entities/pools/linear/linearPool.ts +++ b/src/entities/pools/linear/linearPool.ts @@ -1,6 +1,7 @@ import { Hex, parseEther } from 'viem'; import { PoolType, SwapKind } from '../../../types'; -import { BigintIsh, Token, TokenAmount } from '../../'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; import { BasePool } from '../../pools'; import { getPoolAddress, MAX_UINT112, WAD } from '../../../utils'; import { @@ -19,6 +20,7 @@ import { } from './linearMath'; import { StablePoolToken } from '../stable/stablePool'; import { RawLinearPool } from '../../../data/types'; +import { Params } from './types'; const MAX_RATIO = parseEther('10'); const MAX_TOKEN_BALANCE = MAX_UINT112 - 1n; @@ -52,13 +54,6 @@ class BPT extends TokenAmount { } } -export type Params = { - fee: bigint; - rate: bigint; - lowerTarget: bigint; - upperTarget: bigint; -}; - export class LinearPool implements BasePool { public readonly chainId: number; public readonly id: Hex; diff --git a/src/entities/pools/linear/types.ts b/src/entities/pools/linear/types.ts new file mode 100644 index 00000000..399c93c1 --- /dev/null +++ b/src/entities/pools/linear/types.ts @@ -0,0 +1,6 @@ +export type Params = { + fee: bigint; + rate: bigint; + lowerTarget: bigint; + upperTarget: bigint; +}; diff --git a/src/entities/pools/metastable/metastableFactory.ts b/src/entities/pools/metastable/metastableFactory.ts index bd86bd09..e5190d79 100644 --- a/src/entities/pools/metastable/metastableFactory.ts +++ b/src/entities/pools/metastable/metastableFactory.ts @@ -1,5 +1,5 @@ -import { BasePool, BasePoolFactory } from '..'; -import { MetaStablePool } from '.'; +import { BasePool, BasePoolFactory } from '../index'; +import { MetaStablePool } from './metastablePool'; import { RawMetaStablePool, RawPool } from '../../../data/types'; export class MetaStablePoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/stable/stableFactory.ts b/src/entities/pools/stable/stableFactory.ts index 3bc486af..1c313030 100644 --- a/src/entities/pools/stable/stableFactory.ts +++ b/src/entities/pools/stable/stableFactory.ts @@ -1,5 +1,5 @@ -import { BasePool, BasePoolFactory } from '../'; -import { StablePool } from './'; +import { BasePool, BasePoolFactory } from '../index'; +import { StablePool } from './stablePool'; import { RawComposableStablePool, RawPool } from '../../../data/types'; export class StablePoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/stable/stablePool.ts b/src/entities/pools/stable/stablePool.ts index 3d4b2bfd..c3b8ce94 100644 --- a/src/entities/pools/stable/stablePool.ts +++ b/src/entities/pools/stable/stablePool.ts @@ -1,6 +1,7 @@ import { Hex, parseEther, parseUnits } from 'viem'; import { PoolType, SwapKind } from '../../../types'; -import { BigintIsh, Token, TokenAmount } from '../../'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; import { BasePool } from '../'; import { getPoolAddress, MathSol, WAD } from '../../../utils'; import { diff --git a/src/entities/pools/weighted/weightedFactory.ts b/src/entities/pools/weighted/weightedFactory.ts index 53d5f300..5a36c0d8 100644 --- a/src/entities/pools/weighted/weightedFactory.ts +++ b/src/entities/pools/weighted/weightedFactory.ts @@ -1,5 +1,5 @@ -import { BasePool, BasePoolFactory } from '../'; -import { WeightedPool } from './'; +import { BasePool, BasePoolFactory } from '../index'; +import { WeightedPool } from './weightedPool'; import { RawPool, RawWeightedPool } from '../../../data/types'; export class WeightedPoolFactory implements BasePoolFactory { diff --git a/src/entities/pools/weighted/weightedPool.ts b/src/entities/pools/weighted/weightedPool.ts index baecc0f9..f47a5104 100644 --- a/src/entities/pools/weighted/weightedPool.ts +++ b/src/entities/pools/weighted/weightedPool.ts @@ -1,6 +1,7 @@ import { Hex, parseEther } from 'viem'; import { PoolType, SwapKind } from '../../../types'; -import { Token, TokenAmount, BigintIsh } from '../../'; +import { Token } from '../../token'; +import { TokenAmount, BigintIsh } from '../../tokenAmount'; import { BasePool } from '../'; import { MathSol, WAD, getPoolAddress } from '../../../utils'; import { _calcOutGivenIn, _calcInGivenOut } from './weightedMath'; diff --git a/src/entities/tokenAmount.ts b/src/entities/tokenAmount.ts index e41397bd..9b9a5cb0 100644 --- a/src/entities/tokenAmount.ts +++ b/src/entities/tokenAmount.ts @@ -1,7 +1,8 @@ import { Token } from './token'; import _Decimal from 'decimal.js-light'; import { parseUnits } from 'viem'; -import { DECIMAL_SCALES, WAD } from '../utils'; +import { DECIMAL_SCALES } from '../utils/constants'; +import { WAD } from '../utils/math'; export type BigintIsh = bigint | string | number; diff --git a/src/entities/utils/doQueryExit.ts b/src/entities/utils/doQueryExit.ts index 1cd1ecf2..7cc8124c 100644 --- a/src/entities/utils/doQueryExit.ts +++ b/src/entities/utils/doQueryExit.ts @@ -1,8 +1,8 @@ import { createPublicClient, http } from 'viem'; import { Address } from '../../types'; -import { BALANCER_HELPERS, CHAINS } from '../../utils'; +import { BALANCER_HELPERS, CHAINS } from '../../utils/constants'; import { balancerHelpersAbi } from '../../abi'; -import { ExitPoolRequest } from '../exit'; +import { ExitPoolRequest } from '../exit/types'; export async function doQueryExit( rpcUrl: string, diff --git a/src/pathGraph/pathGraphTypes.ts b/src/pathGraph/pathGraphTypes.ts index 673a4a99..4a08bbf2 100644 --- a/src/pathGraph/pathGraphTypes.ts +++ b/src/pathGraph/pathGraphTypes.ts @@ -1,5 +1,12 @@ -import { PoolTokenPair } from '../types'; -import { BasePool, Token } from '../entities'; +import { Token } from '../entities/token'; +import { BasePool } from '../entities/pools/index'; + +export interface PoolTokenPair { + id: string; + pool: BasePool; + tokenIn: Token; + tokenOut: Token; +} export type PoolAddressDictionary = { [address: string]: BasePool; diff --git a/src/types.ts b/src/types.ts index c7a66698..b89b0b93 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,7 @@ -import { BigintIsh, Token, BasePool, BasePoolFactory } from './entities'; import { PoolDataEnricher, PoolDataProvider } from './data/types'; import { PathGraphTraversalConfig } from './pathGraph/pathGraphTypes'; +import { BigintIsh } from './entities/tokenAmount'; +import { BasePoolFactory } from './entities/pools/index'; export type Address = `0x${string}`; export type Hex = `0x${string}`; @@ -45,13 +46,6 @@ export type SorConfig = { customPoolFactories?: BasePoolFactory[]; }; -export interface PoolTokenPair { - id: string; - pool: BasePool; - tokenIn: Token; - tokenOut: Token; -} - export interface SingleSwap { poolId: Hex; kind: SwapKind; @@ -68,5 +62,3 @@ export interface BatchSwapStep { amount: bigint; userData: Hex; } - -export type HumanAmount = `${number}`; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 02620be0..f895d370 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,4 +1,5 @@ -import { BigintIsh, Token, TokenAmount } from '../entities'; +import { Token } from '../entities/token'; +import { TokenAmount, BigintIsh } from '../entities/tokenAmount'; import { SwapKind } from '../types'; export function checkInputs( From d0c111623a080f6e7883c810ab6f33ec14aae1e4 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 26 Sep 2023 19:39:54 -0300 Subject: [PATCH 071/199] Added example for exit; --- examples/exit/weighted.ts | 99 +++++++++++++++++++++++++++++++++++++++ examples/join/weighted.ts | 6 +-- tsconfig.json | 2 +- 3 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 examples/exit/weighted.ts diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts new file mode 100644 index 00000000..9839e601 --- /dev/null +++ b/examples/exit/weighted.ts @@ -0,0 +1,99 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +import { BalancerApi } from "../../src/data/providers/balancer-api"; +import { + ChainId, + CHAINS, ExitKind, + SingleAssetExitInput, + Slippage, + Token, + TokenAmount, +} from "../../src"; +import { + Client, + createTestClient, + http, + parseUnits, + PublicActions, + publicActions, + TestActions, WalletActions, + walletActions +} 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 +const chainId = ChainId.MAINNET; +const rpcUrl = 'http://127.0.0.1:8545/'; +const blockNumber = BigInt(18043296); +const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig +const slippage = Slippage.fromPercentage('1'); // 1% + +const exit = async () => { + const balancerApi = new BalancerApi(balancerApiUrl, 1); + const poolState: PoolState = await balancerApi.pools.fetchPoolState(poolId); + let client: Client & PublicActions & TestActions & WalletActions; + let bpt: Token; + // setup BPT token + bpt = new Token(chainId, poolState.address, 18, 'BPT'); + client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + await forkSetup( + client, + testAddress, + [poolState.address], + [0], + [ + parseUnits('100', 18), + ], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); + + + const exitParser = new ExitParser(); + const weightedExit = exitParser.getExit(poolState.type); + + const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); + const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL + + const exitInput: SingleAssetExitInput = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SINGLE_ASSET, + }; + + const queryResult = await weightedExit.query(exitInput, poolState); + + const { call, to, value, maxBptIn, minAmountsOut } = + weightedExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolState.tokens.map(({address})=>address), bpt.address], + client, + testAddress, + to, + call, + value, + ); + console.log("transaction status: " + transactionReceipt.status); + console.log("token amounts deltas per token: " + balanceDeltas); +} + +exit(); \ No newline at end of file diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index 7869847c..d87a5431 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -41,7 +41,7 @@ const join = async () => { client, testAddress, [...poolState.tokens.map((t) => t.address), poolState.address], - undefined, // TODO: hardcode these values to improve test performance + [1,3,0], [ ...poolState.tokens.map((t) => parseUnits('100', t.decimals)), parseUnits('100', 18), @@ -87,8 +87,8 @@ const join = async () => { call, value, ); - console.log(transactionReceipt); - console.log(balanceDeltas); + console.log("transaction status: " + transactionReceipt.status); + console.log("token amounts deltas per token: " + balanceDeltas); } join(); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 21a749b5..1bbfcb59 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "include": ["src"], + "include": ["src", "examples"], "compilerOptions": { "target": "ESNext", "module": "ESNext", From 55eed3b68f98431984934d69d126389e42650821 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 27 Sep 2023 20:15:14 -0300 Subject: [PATCH 072/199] Removing Parser; Replacing Type PoolState for PoolStateInput; Updating README.md examples section; --- README.md | 76 ++++++++++++------- examples/exit/weighted.ts | 5 +- examples/join/weighted.ts | 16 +++- src/data/index.ts | 1 + src/data/providers/balancer-api/index.ts | 2 +- .../balancer-api/modules/pool-state/index.ts | 4 +- src/entities/exit/parser.ts | 0 src/entities/join/parser.ts | 0 8 files changed, 67 insertions(+), 37 deletions(-) delete mode 100644 src/entities/exit/parser.ts delete mode 100644 src/entities/join/parser.ts diff --git a/README.md b/README.md index 3e79f119..a94a8c8f 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,18 @@ Testing requires access to an archive node for onchain quote comparisons. This c `pnpm test` -## Examples +## Balancer Api Provider -### Balancer Api Provider +The Balancer API Provider is a provider that facilitates +data fetching from the Balancer API, +it can be used for: +- Fetch Pool State for Joins; +- Fetch Pool State for Exits. + +### Usage for Joining Pool -Joining with pool state: ```ts - import BalancerApi from "@balancer/sdk/data/providers/balancer-api"; + import { BalancerApi, PoolJoin } from "@balancer/sdk"; ... const joinInput: ProportionalJoinInput = { bptOut, @@ -37,47 +42,64 @@ Joining with pool state: kind: JoinKind.Proportional, }; - const balancerApi = new BalancerApi('https://api-v3.balancer.fi/', 1); - const poolState = await balancerApi.pools.fetchSimplePoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); - const joinParser = new JoinParser(); - const poolJoin = joinParser.getJoin(poolState.type); - const queryResult = await weightedJoin.query(joinInput, poolState); - + const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); + const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); + const poolJoin = new PoolJoin(); + const queryResult = await poolJoin.query(joinInput, poolState); + const { call, to, value, maxAmountsIn, minBptOut } = + poolJoin.buildCall({ + ...queryResult, + slippage, + sender: signerAddress, + recipient: signerAddress, + }); + const client = createClient({ + ... + }) + + await client.sendTransaction({ + account: signerAddress, + chain: client.chain, + data: call, + to, + value, + }); ``` +Full working join example: [examples/join/weighted.ts](./examples/join/weighted.ts) -Exiting with pool state: +### Usage for Exiting Pool ```ts -import BalancerApi from "@balancer/sdk/data/providers/balancer-api"; +import { BalancerApi, PoolExit } from "@balancer/sdk"; ... -const joinInput: ProportionalJoinInput = { - bptOut, +const exitInput: SingleAssetExitInput = { chainId, rpcUrl, - kind: JoinKind.Proportional, + bptIn, + tokenOut, + kind: ExitKind.SINGLE_ASSET, }; -const balancerApi = new BalancerApi('https://api-v3.balancer.fi/', 1); -const poolState = await balancerApi.pools.fetchSimplePoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); -const joinParser = new JoinParser(); -const poolJoin = joinParser.getJoin(poolState.type); -const queryResult = await weightedJoin.query(joinInput, poolState); -const slippage = Slippage.fromPercentage('1'); // 1% +const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); +const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); +const poolExit = new PoolExit(); +const queryResult = await poolExit.query(exitInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = - weightedJoin.buildCall({ + poolExit.buildCall({ ...queryResult, slippage, - sender, - recipient, + sender: signerAddress, + recipient: signerAddress, }); const client = createClient({ ... }) await client.sendTransaction({ - account, + account: signerAddress, chain: client.chain, - data, + data: call, to, value, }); -``` \ No newline at end of file +``` +Full working exit example: [examples/exit/weighted.ts](./examples/exit/weighted.ts) diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index a25857fe..e7691dda 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -4,7 +4,7 @@ dotenv.config(); import { BalancerApi } from "../../src/data/providers/balancer-api"; import { ChainId, - CHAINS, ExitKind, PoolExit, + CHAINS, ExitKind, PoolExit, PoolStateInput, SingleAssetExitInput, Slippage, Token, @@ -20,7 +20,6 @@ import { TestActions, WalletActions, walletActions } from "viem"; -import { PoolState } from "../../src/data/providers/balancer-api/modules/pool-state/types"; import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper"; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; @@ -33,7 +32,7 @@ const slippage = Slippage.fromPercentage('1'); // 1% const exit = async () => { const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState: PoolState = await balancerApi.pools.fetchPoolState(poolId); + const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); let client: Client & PublicActions & TestActions & WalletActions; let bpt: Token; // setup BPT token diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index f972aff3..b1aed8f5 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -1,8 +1,17 @@ import dotenv from 'dotenv'; dotenv.config(); -import { BalancerApi } from "../../src/data/providers/balancer-api"; -import { ChainId, CHAINS, JoinKind, PoolJoin, Slippage, Token, TokenAmount, UnbalancedJoinInput } from "../../src"; +import { + BalancerApi, + ChainId, + CHAINS, + JoinKind, + PoolJoin, PoolStateInput, + Slippage, + Token, + TokenAmount, + UnbalancedJoinInput +} from "../../src"; import { Client, createTestClient, @@ -13,7 +22,6 @@ import { TestActions, WalletActions, walletActions } from "viem"; -import { PoolState } from "../../src/data/providers/balancer-api/modules/pool-state/types"; import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper"; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; @@ -26,7 +34,7 @@ const slippage = Slippage.fromPercentage('1'); // 1% const join = async () => { const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState: PoolState = await balancerApi.pools.fetchPoolState(poolId); + const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); let client: Client & PublicActions & TestActions & WalletActions; client = createTestClient({ mode: 'hardhat', diff --git a/src/data/index.ts b/src/data/index.ts index e02a4aa2..ed1dbc6a 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -1,3 +1,4 @@ export * from './enrichers/onChainPoolDataEnricher'; export * from './providers/subgraphPoolProvider'; +export * from './providers/balancer-api'; export * from './types'; diff --git a/src/data/providers/balancer-api/index.ts b/src/data/providers/balancer-api/index.ts index 13f85a0a..bc18ae52 100644 --- a/src/data/providers/balancer-api/index.ts +++ b/src/data/providers/balancer-api/index.ts @@ -13,4 +13,4 @@ export class BalancerApi { } -} \ No newline at end of file +} diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index 938c1329..38ca9403 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -1,5 +1,5 @@ import { BalancerApiClient } from '../../client'; -import { PoolState } from './types'; +import { PoolStateInput } from "../../../../../entities"; export class Pools { readonly poolStateQuery = `query GetPool($id: String!){ @@ -77,7 +77,7 @@ export class Pools { constructor(private readonly balancerApiClient: BalancerApiClient) {} - async fetchPoolState(id: string): Promise { + async fetchPoolState(id: string): Promise { const { data: { poolGetPool }, } = await this.balancerApiClient.fetch('GetPool', this.poolStateQuery, { diff --git a/src/entities/exit/parser.ts b/src/entities/exit/parser.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/entities/join/parser.ts b/src/entities/join/parser.ts deleted file mode 100644 index e69de29b..00000000 From 36e684f43c46f7595f2bbb978cc72c5955a230c4 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 2 Oct 2023 21:07:09 +0200 Subject: [PATCH 073/199] fixing lint errors --- examples/exit/weighted.ts | 13 +++++-------- examples/join/weighted.ts | 9 ++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index e7691dda..0f7e4a92 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -33,17 +33,14 @@ const slippage = Slippage.fromPercentage('1'); // 1% const exit = async () => { const balancerApi = new BalancerApi(balancerApiUrl, 1); const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - let client: Client & PublicActions & TestActions & WalletActions; - let bpt: Token; - // setup BPT token - bpt = new Token(chainId, poolState.address, 18, 'BPT'); - client = createTestClient({ + const client: Client & PublicActions & TestActions & WalletActions = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], transport: http(rpcUrl), }) .extend(publicActions) .extend(walletActions); + const bpt = new Token(chainId, poolState.address, 18, 'BPT'); await forkSetup( client, @@ -72,7 +69,7 @@ const exit = async () => { const queryResult = await poolExit.query(exitInput, poolState); - const { call, to, value, maxBptIn, minAmountsOut } = + const { call, to, value } = poolExit.buildCall({ ...queryResult, slippage, @@ -88,8 +85,8 @@ const exit = async () => { call, value, ); - console.log("transaction status: " + transactionReceipt.status); - console.log("token amounts deltas per token: " + balanceDeltas); + console.log(`transaction status: ${transactionReceipt.status}`); + console.log(`token amounts deltas per token: ${balanceDeltas}`); } exit(); \ No newline at end of file diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index b1aed8f5..72d38c0e 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -35,8 +35,7 @@ const slippage = Slippage.fromPercentage('1'); // 1% const join = async () => { const balancerApi = new BalancerApi(balancerApiUrl, 1); const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - let client: Client & PublicActions & TestActions & WalletActions; - client = createTestClient({ + const client: Client & PublicActions & TestActions & WalletActions = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], transport: http(rpcUrl), @@ -76,7 +75,7 @@ const join = async () => { const queryResult = await poolJoin.query(joinInput, poolState); - const { call, to, value, maxAmountsIn, minBptOut } = + const { call, to, value } = poolJoin.buildCall({ ...queryResult, slippage, @@ -92,8 +91,8 @@ const join = async () => { call, value, ); - console.log("transaction status: " + transactionReceipt.status); - console.log("token amounts deltas per token: " + balanceDeltas); + console.log(`transaction status: ${transactionReceipt.status}`); + console.log(`token amounts deltas per token: ${balanceDeltas}`); } join(); \ No newline at end of file From 1fbd05e2be30a85b1adf027a7329fa364b0f071f Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 11 Oct 2023 17:33:04 -0300 Subject: [PATCH 074/199] Changing hardhat to anvil client in tests; Creating script to start anvil node; Adding docs about anvil node installation in the README.md; --- .gitignore | 3 +++ README.md | 10 ++++++++++ package.json | 3 ++- test/lib/utils/helper.ts | 4 ++-- test/weightedExit.integration.test.ts | 2 +- test/weightedJoin.integration.test.ts | 2 +- 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3e819cac..37f99c66 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ typechain-types cache artifacts +#IDE +.idea + diff --git a/README.md b/README.md index 0c0e32ce..ff055f91 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,13 @@ If your platform does not support one of the required features, it is also possi Testing requires access to an archive node for onchain quote comparisons. This can be done using Infura. `pnpm test` + +## Anvil client +To download and install the anvil client, run the following commands (MacOS): +- `curl -L https://foundry.paradigm.xyz | bash` +- `brew install libusb` +- `source /Users/$(whoami)/.zshenv` +- `foundryup` + +For other SO's check https://book.getfoundry.sh/getting-started/installation +``` diff --git a/package.json b/package.json index f6ccb0ba..b6fbf9c4 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "test:ci": "vitest run", "changeset": "changeset", "changeset:release": "pnpm build && changeset publish", - "node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)" + "node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)", + "node:anvil": "anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL)" }, "dependencies": { "async-retry": "^1.3.3", diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 9b3ffa22..7e0980d1 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -34,7 +34,7 @@ export const approveToken = async ( args: [BALANCER_VAULT, amount], }); - const txReceipt = await client.getTransactionReceipt({ + const txReceipt = await client.waitForTransactionReceipt({ hash, }); return txReceipt.status === 'success'; @@ -107,7 +107,7 @@ export async function sendTransactionGetBalances( to, value, }); - const transactionReceipt = await client.getTransactionReceipt({ + const transactionReceipt = await client.waitForTransactionReceipt({ hash, }); const { gasUsed, effectiveGasPrice } = transactionReceipt; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 50cad9d7..474effb8 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -61,7 +61,7 @@ describe('weighted exit test', () => { const poolInput = await api.getPool(poolId); const client = createTestClient({ - mode: 'hardhat', + mode: 'anvil', chain: CHAINS[chainId], transport: http(rpcUrl), }) diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index afd6497a..e96f99f2 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -63,7 +63,7 @@ describe('weighted join test', () => { const poolInput = await api.getPool(poolId); const client = createTestClient({ - mode: 'hardhat', + mode: 'anvil', chain: CHAINS[chainId], transport: http(rpcUrl), }) From 1948ff370db243ebc2b6e1c3ee7876a7df0eb4bb Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 16 Oct 2023 00:05:59 -0300 Subject: [PATCH 075/199] Created Join for Composable Stable Pools; --- .gitignore | 2 + src/entities/encoders/composableStable.ts | 139 +++++++ .../composable-stable/composableStableJoin.ts | 206 ++++++++++ .../join/composable-stable/validateInputs.ts | 24 ++ src/entities/join/poolJoin.ts | 2 + src/entities/join/types.ts | 1 + test/composableStableJoin.integration.test.ts | 371 ++++++++++++++++++ 7 files changed, 745 insertions(+) create mode 100644 src/entities/encoders/composableStable.ts create mode 100644 src/entities/join/composable-stable/composableStableJoin.ts create mode 100644 src/entities/join/composable-stable/validateInputs.ts create mode 100644 test/composableStableJoin.integration.test.ts diff --git a/.gitignore b/.gitignore index 3e819cac..c5052933 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,5 @@ typechain-types cache artifacts +#IDE +.idea/ \ No newline at end of file diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts new file mode 100644 index 00000000..f8432c8d --- /dev/null +++ b/src/entities/encoders/composableStable.ts @@ -0,0 +1,139 @@ +import { encodeAbiParameters } from 'viem'; +import { Address } from '../../types'; + + + +export enum ComposableStablePoolJoinKind { + INIT, + EXACT_TOKENS_IN_FOR_BPT_OUT, + TOKEN_IN_FOR_EXACT_BPT_OUT, + ALL_TOKENS_IN_FOR_EXACT_BPT_OUT +} + +export enum ComposableStablePoolExitKind { + EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, + BPT_IN_FOR_EXACT_TOKENS_OUT, + EXACT_BPT_IN_FOR_ALL_TOKENS_OUT +} + +export class ComposableStableEncoder { + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } + + /** + * Encodes the userData parameter for providing the initial liquidity to a ComposableStablePool + * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances + */ + static joinInit = (amountsIn: bigint[]): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }], + [BigInt(ComposableStablePoolJoinKind.INIT), amountsIn], + ); + + /** + * Encodes the userData parameter for joining a ComposableStablePool with exact token inputs + * @param amountsIn - the amounts each of token to deposit in the pool as liquidity + * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens + */ + static joinUnbalanced = ( + amountsIn: bigint[], + minimumBPT: bigint, + ): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT), + amountsIn, + minimumBPT, + ], + ); + + /** + * Encodes the userData parameter for joining a ComposableStablePool with a single token to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + * @param enterTokenIndex - the index of the token to be provided as liquidity + */ + static joinSingleAsset = ( + bptAmountOut: bigint, + enterTokenIndex: number, + ): Address => { + // if enterTokenIndex is provided, it's assumed to be an allTokensIn + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), + bptAmountOut, + BigInt(enterTokenIndex), + ], + ); + }; + + /** + * Encodes the userData parameter for joining a ComposableStablePool proportionally to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + */ + static joinProportional = (bptAmountOut: bigint): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT), + bptAmountOut, + ], + ); + }; + + /** + * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + * @param enterTokenIndex - the index of the token to removed from the pool + */ + static exitSingleAsset = ( + bptAmountIn: bigint, + exitTokenIndex: number, + ): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT), + bptAmountIn, + BigInt(exitTokenIndex), + ], + ); + }; + + /** + * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + */ + static exitProportional = (bptAmountIn: bigint): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ALL_TOKENS_OUT), + bptAmountIn, + ], + ); + }; + + /** + * Encodes the userData parameter for exiting a ComposableStablePool by removing exact amounts of tokens + * @param amountsOut - the amounts of each token to be withdrawn from the pool + * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens + */ + static exitUnbalanced = ( + amountsOut: bigint[], + maxBPTAmountIn: bigint, + ): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT), + amountsOut, + maxBPTAmountIn, + ], + ); +} diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts new file mode 100644 index 00000000..fc68790c --- /dev/null +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -0,0 +1,206 @@ +import { encodeFunctionData } from 'viem'; +import { Token } from '../../../entities/token'; +import { TokenAmount } from '../../../entities/tokenAmount'; +import { Address } from '../../../types'; +import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; +import { vaultAbi } from '../../../abi'; +import { + BaseJoin, + JoinBuildOutput, + JoinCallInput, + JoinInput, + JoinKind, + JoinQueryResult, +} from '../types'; +import { AmountsJoin, PoolState } from '../../types'; +import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; +import { ComposableStableEncoder } from "../../encoders/composableStable"; + +export class ComposableStableJoin implements BaseJoin { + public async query( + input: JoinInput, + poolState: PoolState, + ): Promise { + const bptIndex = poolState.tokens.findIndex((t)=> t.address == poolState.address); + const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); + const amountsWithoutBpt = { + ...amounts, + maxAmountsIn: [...amounts.maxAmountsIn.slice(0, bptIndex), ...amounts.maxAmountsIn.slice(bptIndex+1)] + }; + + const userData = this.encodeUserData(input.kind, amountsWithoutBpt); + + const { args, tokensIn } = parseJoinArgs({ + useNativeAssetAsWrappedAmountIn: + !!input.useNativeAssetAsWrappedAmountIn, + chainId: input.chainId, + sortedTokens: poolState.tokens, + poolId: poolState.id, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + maxAmountsIn: amounts.maxAmountsIn, + userData, + fromInternalBalance: input.fromInternalBalance ?? false, + }); + + const queryResult = await doQueryJoin( + input.rpcUrl, + input.chainId, + args, + ); + + const bpt = new Token(input.chainId, poolState.address, 18); + const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); + + const amountsIn = queryResult.amountsIn.map((a, i) => + TokenAmount.fromRawAmount(tokensIn[i], a), + ); + + return { + poolType: poolState.type, + joinKind: input.kind, + poolId: poolState.id, + bptOut, + amountsIn, + tokenInIndex: amounts.tokenInIndex, + fromInternalBalance: !!input.fromInternalBalance, + bptIndex, + }; + } + + public buildCall(input: JoinCallInput): JoinBuildOutput { + const amounts = this.getAmountsCall(input); + if(input.bptIndex === undefined){ + throw new Error("bptIndex is necessary"); + } + const amountsWithoutBpt = { + ...amounts, + maxAmountsIn: [...amounts.maxAmountsIn.slice(0, input.bptIndex), ...amounts.maxAmountsIn.slice(input.bptIndex+1)] + }; + + const userData = this.encodeUserData(input.joinKind, amountsWithoutBpt); + + const { args } = parseJoinArgs({ + ...input, + sortedTokens: input.amountsIn.map((a) => a.token), + maxAmountsIn: amounts.maxAmountsIn, + userData, + fromInternalBalance: input.fromInternalBalance, + }); + + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'joinPool', + args, + }); + + const value = input.amountsIn.find( + (a) => a.token.address === ZERO_ADDRESS, + )?.amount; + + return { + call, + to: BALANCER_VAULT, + value, + minBptOut: amounts.minimumBpt, + maxAmountsIn: amounts.maxAmountsIn, + }; + } + + private getAmountsQuery( + poolTokens: Token[], + input: JoinInput, + bptIndex?:number, + ): AmountsJoin { + switch (input.kind) { + case JoinKind.Init: + case JoinKind.Unbalanced: { + return { + minimumBpt: 0n, + maxAmountsIn: getAmounts(poolTokens, input.amountsIn, BigInt(0)), + tokenInIndex: undefined, + }; + } + case JoinKind.SingleAsset: { + if(bptIndex===undefined){ + throw new Error("bptIndex is necessary"); + } + const tokenInIndex = poolTokens + .filter((_, index)=> index!==bptIndex) // Need to remove Bpt + .findIndex((t) => + t.isSameAddress(input.tokenIn) + ); + if (tokenInIndex === -1) + throw Error("Can't find index of SingleAsset"); + const maxAmountsIn = Array(poolTokens.length).fill(0n); + maxAmountsIn[tokenInIndex] = MAX_UINT256; + return { + minimumBpt: input.bptOut.amount, + maxAmountsIn, + tokenInIndex + }; + } + case JoinKind.Proportional: { + return { + minimumBpt: input.bptOut.amount, + maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), + tokenInIndex: undefined, + }; + } + default: + throw Error('Unsupported Join Type'); + } + } + + private getAmountsCall(input: JoinCallInput): AmountsJoin { + switch (input.joinKind) { + case JoinKind.Init: + case JoinKind.Unbalanced: { + const minimumBpt = input.slippage.removeFrom( + input.bptOut.amount, + ); + return { + minimumBpt, + maxAmountsIn: input.amountsIn.map((a) => a.amount), + tokenInIndex: input.tokenInIndex, + }; + } + case JoinKind.SingleAsset: + case JoinKind.Proportional: { + return { + minimumBpt: input.bptOut.amount, + maxAmountsIn: input.amountsIn.map((a) => + input.slippage.applyTo(a.amount), + ), + tokenInIndex: input.tokenInIndex, + }; + } + default: + throw Error('Unsupported Join Type'); + } + } + + private encodeUserData(kind: JoinKind, amounts: AmountsJoin): Address { + switch (kind) { + case JoinKind.Init: + return ComposableStableEncoder.joinInit(amounts.maxAmountsIn); + case JoinKind.Unbalanced: + return ComposableStableEncoder.joinUnbalanced( + amounts.maxAmountsIn, + amounts.minimumBpt, + ); + case JoinKind.SingleAsset: { + if (amounts.tokenInIndex === undefined) throw Error('No Index'); + return ComposableStableEncoder.joinSingleAsset( + amounts.minimumBpt, + amounts.tokenInIndex, + ); + } + case JoinKind.Proportional: { + return ComposableStableEncoder.joinProportional(amounts.minimumBpt); + } + default: + throw Error('Unsupported Join Type'); + } + } +} diff --git a/src/entities/join/composable-stable/validateInputs.ts b/src/entities/join/composable-stable/validateInputs.ts new file mode 100644 index 00000000..733073c8 --- /dev/null +++ b/src/entities/join/composable-stable/validateInputs.ts @@ -0,0 +1,24 @@ +import { JoinInput, JoinKind } from '../types'; +import { PoolStateInput } from '../../types'; +import { areTokensInArray } from '../../utils/areTokensInArray'; + +export function validateInputs(input: JoinInput, poolState: PoolStateInput) { + switch (input.kind) { + case JoinKind.Init: + case JoinKind.Unbalanced: + areTokensInArray( + input.amountsIn.map((a) => a.token.address), + poolState.tokens.map((t) => t.address), + ); + break; + case JoinKind.SingleAsset: + areTokensInArray( + [input.tokenIn], + poolState.tokens.map((t) => t.address), + ); + case JoinKind.Proportional: + areTokensInArray([input.bptOut.token.address], [poolState.address]); + default: + break; + } +} diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 04d8ced5..df0b9002 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -10,6 +10,7 @@ import { WeightedJoin } from './weighted/weightedJoin'; import { PoolStateInput } from '../types'; import { validateInputs } from './weighted/validateInputs'; import { getSortedTokens } from '../utils/getSortedTokens'; +import { ComposableStableJoin } from "./composable-stable/composableStableJoin"; export class PoolJoin { private readonly poolJoins: Record = {}; @@ -18,6 +19,7 @@ export class PoolJoin { const { customPoolJoins } = config || {}; this.poolJoins = { Weighted: new WeightedJoin(), + PHANTOM_STABLE: new ComposableStableJoin(), // custom pool Joins take precedence over base Joins ...customPoolJoins, }; diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 318b8ce6..c5f62f8d 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -54,6 +54,7 @@ export type JoinQueryResult = { amountsIn: TokenAmount[]; fromInternalBalance: boolean; tokenInIndex?: number; + bptIndex?:number; }; export type JoinCallInput = JoinQueryResult & { diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts new file mode 100644 index 00000000..41c94851 --- /dev/null +++ b/test/composableStableJoin.integration.test.ts @@ -0,0 +1,371 @@ +// pnpm test -- weightedJoin.integration.test.ts +import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import { config } from 'dotenv'; +config(); + +import { + Client, + createTestClient, + http, + parseUnits, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, +} from 'viem'; + +import { + UnbalancedJoinInput, + ProportionalJoinInput, + SingleAssetJoinInput, + JoinKind, + Slippage, + Token, + TokenAmount, + replaceWrapped, + Address, + Hex, + PoolStateInput, + CHAINS, + ChainId, + getPoolAddress, + PoolJoin, + JoinInput, +} from '../src'; +import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; + +type TxInput = { + client: Client & PublicActions & TestActions & WalletActions; + poolJoin: PoolJoin; + joinInput: JoinInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; +}; + +const chainId = ChainId.MAINNET; +const rpcUrl = 'http://127.0.0.1:8545/'; +const blockNumber = BigInt(18043296); +const poolId = + '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool + +describe('composable stable join test', () => { + let txInput: TxInput; + let bptToken: Token; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + const poolInput = await api.getPool(poolId); + + const client: Client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions) as Client; + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig + joinInput: {} as JoinInput, + checkNativeBalance: false, + }; + + // setup BPT token + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [ + ...txInput.poolInput.tokens.map((t) => t.address), + ], + [0,0,3], + [ + ...txInput.poolInput.tokens.map((t) => + parseUnits('100', t.decimals), + ), + ], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); + }); + + test('unbalanced join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const poolTokensWithoutBpt = txInput.poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ).filter((_, index) => index !== bptIndex); + + const amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + // Query should use same amountsIn as user sets + expect(queryResult.amountsIn.filter((_, index)=>index!==bptIndex)).to.deep.eq(amountsIn); + expect(queryResult.tokenInIndex).to.be.undefined; + + // Should be no native value + expect(value).toBeUndefined; + + // Expect some bpt amount + expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn.filter((_, index)=>index!==bptIndex)); + }); + + test('native asset join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + + const poolTokensWithoutBpt = txInput.poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ).filter((_, index) => index !== bptIndex); + + const amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + useNativeAssetAsWrappedAmountIn: true, + }; + + // We have to use zero address for balanceDeltas + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + checkNativeBalance: true, + }); + + // Query should use same amountsIn as user sets + expect(queryResult.amountsIn + .filter((_, index)=>index!==bptIndex) + .map((a) => a.amount)).to.deep.eq( + amountsIn.map((a) => a.amount), + ); + expect(queryResult.tokenInIndex).to.be.undefined; + // Should have native value equal to input amount + expect(value).eq(amountsIn[1].amount); + + // Expect some bpt amount + expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn.filter((_, index)=>index!==bptIndex)); + }); + + test('single asset join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; + + // perform join query to get expected bpt out + const joinInput: SingleAssetJoinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // We only expect single asset to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn.filter((_, index) => index!==bptIndex) + .forEach((a, i) => { + if (i === queryResult.tokenInIndex) + expect(a.amount > BigInt(0)).to.be.true; + else expect(a.amount === BigInt(0)).to.be.true; + }); + + // Should be no native value + expect(value).toBeUndefined; + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + txInput.slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); + + test('proportional join', async () => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + + // perform join query to get expected bpt out + const joinInput: ProportionalJoinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // Expect all assets to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn.filter((_, index) => index!==bptIndex) + .forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + expect(queryResult.tokenInIndex).toBeUndefined; + + // Should be no native value + expect(value).toBeUndefined; + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + txInput.slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); + + async function doTransaction(txIp: TxInput) { + const { + poolJoin, + poolInput, + joinInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolJoin.query(joinInput, poolInput); + const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( + { + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolTokensAddr], + client, + testAddress, + to, + call, + value, + ); + const bptIndex = poolInput.tokens.findIndex((t) => t.address == poolInput.address); + expect(transactionReceipt.status).to.eq('success'); + // Confirm final balance changes match query result + const expectedDeltas = [ + ...queryResult.amountsIn.slice(0, bptIndex).map((a)=>a.amount), + queryResult.bptOut.amount, + ...queryResult.amountsIn.slice(bptIndex+1).map((a)=>a.amount) + ] + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxAmountsIn, + minBptOut, + value, + }; + } +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + const tokens = [ + { + address: + '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76' as Address, // vETH/WETH BPT + decimals: 18, + index: 0, + }, + { + address: + '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f' as Address, // VETH + decimals: 18, + index: 1, + }, + { + address: + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH + decimals: 18, + index: 2, + }, + ]; + + return { + id, + address: getPoolAddress(id) as Address, + type: 'PHANTOM_STABLE', + tokens, + }; + } +} + +/******************************************************************************/ From d8ae99bd4e455f7d28dca2f27059e85cb5b28a10 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 15:44:34 -0300 Subject: [PATCH 076/199] Installing foundry and running anvil in the github actions; --- .github/workflows/checks.yml | 15 ++++++++++++++- package.json | 3 +-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index f4ddbd19..127c36dc 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -29,7 +29,20 @@ jobs: uses: ./.github/actions/setup - name: Lint & format code run: pnpm format & pnpm lint + + foundry: + name: Foundry + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Foundry Up + run: foundryup + test: name: Test needs: install @@ -43,7 +56,7 @@ jobs: - name: Setup uses: ./.github/actions/setup - name: Run local fork in background for integration tests - run: npx hardhat --tsconfig tsconfig.testing.json node --hostname 127.0.0.1 --fork ${{ secrets.ETHEREUM_RPC_URL }} & + run: anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL) & - name: Test run: pnpm test:ci env: diff --git a/package.json b/package.json index b6fbf9c4..6eb9cd76 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,7 @@ "test:ci": "vitest run", "changeset": "changeset", "changeset:release": "pnpm build && changeset publish", - "node": "npx hardhat node --tsconfig tsconfig.testing.json --fork $(. ./.env && echo $ETHEREUM_RPC_URL)", - "node:anvil": "anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL)" + "node": "anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL)" }, "dependencies": { "async-retry": "^1.3.3", From c27a52686804d519d5203d5170c70fb38071c5d1 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 15:50:27 -0300 Subject: [PATCH 077/199] Fixing script to install foundry --- .github/workflows/checks.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 127c36dc..5f1e2e57 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -39,9 +39,8 @@ jobs: submodules: recursive - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - name: Foundry Up - run: foundryup + run: + curl -L https://foundry.paradigm.xyz | bash & source ~/.bashrc & foundryup test: name: Test From 78be59e183e6eaefbb56f342198996b830c86bda Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 15:52:28 -0300 Subject: [PATCH 078/199] Fixing commands to install foundry (2); --- .github/workflows/checks.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 5f1e2e57..fef488b8 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -38,9 +38,13 @@ jobs: with: submodules: recursive - - name: Install Foundry + - name: Download Foundry run: - curl -L https://foundry.paradigm.xyz | bash & source ~/.bashrc & foundryup + curl -L https://foundry.paradigm.xyz | bash + - name: Refresh environment + run: source ~/.bashrc + - name: Install Foundry + run: foundryup test: name: Test From ee49610974fbcca3a10a63065b502c6865841b22 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 15:55:48 -0300 Subject: [PATCH 079/199] Another try to install foundry; --- .github/workflows/checks.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index fef488b8..89202606 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -36,15 +36,11 @@ jobs: steps: - uses: actions/checkout@v3 with: - submodules: recursive + submodules: - - name: Download Foundry - run: - curl -L https://foundry.paradigm.xyz | bash - - name: Refresh environment - run: source ~/.bashrc - name: Install Foundry - run: foundryup + run: + curl -L https://foundry.paradigm.xyz | bash ; source ~/.bashrc ; foundryup test: name: Test From b8777fd2fdd7e3b65f7e73670b2dedd48ae3dd56 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:00:42 -0300 Subject: [PATCH 080/199] using custom bash to install foundry; --- .github/workflows/checks.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 89202606..15f3b445 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -33,11 +33,13 @@ jobs: foundry: name: Foundry runs-on: ubuntu-latest + defaults: + run: + shell: bash -ieo pipefail {0} steps: - uses: actions/checkout@v3 with: - submodules: - + submodules: recursive - name: Install Foundry run: curl -L https://foundry.paradigm.xyz | bash ; source ~/.bashrc ; foundryup From 741e78b989738ba06a1de2b24f44698aad9ca01f Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:09:53 -0300 Subject: [PATCH 081/199] no message --- .github/workflows/checks.yml | 3 +++ test/weightedExit.integration.test.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 15f3b445..1d0a69c6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -46,6 +46,9 @@ jobs: test: name: Test + defaults: + run: + shell: bash -ieo pipefail {0} needs: install runs-on: ubuntu-latest strategy: diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 474effb8..103a3788 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -59,7 +59,7 @@ describe('weighted exit test', () => { // get pool state from api const poolInput = await api.getPool(poolId); - + const client = createTestClient({ mode: 'anvil', chain: CHAINS[chainId], From 0621250c22121a469057dd11410f4d404e4b442b Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:12:01 -0300 Subject: [PATCH 082/199] refreshing bash before running anvil; --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 1d0a69c6..62719c3a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -60,7 +60,7 @@ jobs: - name: Setup uses: ./.github/actions/setup - name: Run local fork in background for integration tests - run: anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL) & + run: source ~/.bashrc ; anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL) & - name: Test run: pnpm test:ci env: From 876e9a68a4d20db9e41b29c0f37b85d29c565c29 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:21:56 -0300 Subject: [PATCH 083/199] adding foundry to test needs property; --- .github/workflows/checks.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 62719c3a..add542ad 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -46,10 +46,7 @@ jobs: test: name: Test - defaults: - run: - shell: bash -ieo pipefail {0} - needs: install + needs: [install, foundry] runs-on: ubuntu-latest strategy: matrix: From 6a960ca727a339b3a4fb63d5972b0869b8c8fdde Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:24:47 -0300 Subject: [PATCH 084/199] Yet Another Try to recognize anvil command; --- .github/workflows/checks.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index add542ad..eafc1b6c 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -48,6 +48,9 @@ jobs: name: Test needs: [install, foundry] runs-on: ubuntu-latest + defaults: + run: + shell: bash -ieo pipefail {0} strategy: matrix: node-version: [18] @@ -57,7 +60,7 @@ jobs: - name: Setup uses: ./.github/actions/setup - name: Run local fork in background for integration tests - run: source ~/.bashrc ; anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL) & + run: source ~/.bashrc ; anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - name: Test run: pnpm test:ci env: From 547984cff02ddfdebf6e54f7be8216eefa5b3187 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:34:33 -0300 Subject: [PATCH 085/199] Running the Anvil Client in the Foundry tab, hopefully it will recognize anvil cmd now; --- .github/workflows/checks.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index eafc1b6c..72361759 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -43,14 +43,15 @@ jobs: - name: Install Foundry run: curl -L https://foundry.paradigm.xyz | bash ; source ~/.bashrc ; foundryup - + - name: Run Anvil Client in background for integration tests + run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & + env: + ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} + POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} test: name: Test needs: [install, foundry] runs-on: ubuntu-latest - defaults: - run: - shell: bash -ieo pipefail {0} strategy: matrix: node-version: [18] @@ -59,14 +60,8 @@ jobs: - uses: actions/checkout@v3 - name: Setup uses: ./.github/actions/setup - - name: Run local fork in background for integration tests - run: source ~/.bashrc ; anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - name: Test run: pnpm test:ci - env: - ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} - POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} - build: name: Build needs: install From 3d68f79a98ae48fe4c3e33b0b4b0599f15298339 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:35:04 -0300 Subject: [PATCH 086/199] replacing wrongly removed environment variables; --- .github/workflows/checks.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 72361759..0a1c4ebe 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -62,6 +62,9 @@ jobs: uses: ./.github/actions/setup - name: Test run: pnpm test:ci + env: + ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} + POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} build: name: Build needs: install From a4981cafc010ff08fab25d52e9596472f60a5b1e Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:40:14 -0300 Subject: [PATCH 087/199] Putting the Foundry in github setup action; --- .github/actions/setup/action.yml | 16 ++++++++++++++++ .github/workflows/checks.yml | 19 ------------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 24322666..d9715aa0 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -27,3 +27,19 @@ runs: if: steps.cache-node-modules.outputs.cache-hit != 'true' run: pnpm i shell: bash + - name: Foundry + defaults: + run: + shell: bash -ieo pipefail {0} + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Install Foundry + run: + curl -L https://foundry.paradigm.xyz | bash ; source ~/.bashrc ; foundryup + - name: Run Anvil Client in background for integration tests + run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & + env: + ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} + POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0a1c4ebe..d1a9bcdf 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -29,25 +29,6 @@ jobs: uses: ./.github/actions/setup - name: Lint & format code run: pnpm format & pnpm lint - - foundry: - name: Foundry - runs-on: ubuntu-latest - defaults: - run: - shell: bash -ieo pipefail {0} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Install Foundry - run: - curl -L https://foundry.paradigm.xyz | bash ; source ~/.bashrc ; foundryup - - name: Run Anvil Client in background for integration tests - run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - env: - ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} - POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} test: name: Test needs: [install, foundry] From 746dd3f35921034eacae3ca690f6d8da63ca8ff5 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:44:47 -0300 Subject: [PATCH 088/199] Fixing foundry setup action; --- .github/actions/setup/action.yml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index d9715aa0..26d1c837 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -27,19 +27,10 @@ runs: if: steps.cache-node-modules.outputs.cache-hit != 'true' run: pnpm i shell: bash - - name: Foundry - defaults: - run: - shell: bash -ieo pipefail {0} - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Install Foundry - run: - curl -L https://foundry.paradigm.xyz | bash ; source ~/.bashrc ; foundryup - - name: Run Anvil Client in background for integration tests - run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - env: - ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} - POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Run Anvil Node + run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & + env: + ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} + POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} From 13b92f8da3f7d0146b7cb00e39152e4bfa5b58fe Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:46:25 -0300 Subject: [PATCH 089/199] fixing wrong parameter "test > needs", foundry no longer exists; --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d1a9bcdf..8a1d5ca7 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,7 +31,7 @@ jobs: run: pnpm format & pnpm lint test: name: Test - needs: [install, foundry] + needs: install runs-on: ubuntu-latest strategy: matrix: From ac375dee83f012c6c8f2c575a4921fedc86c271b Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 16:48:20 -0300 Subject: [PATCH 090/199] secrets.ETHEREUM_RPC_URL is not available in setup/action.yml, trying to run anvil in the workflow checks again; --- .github/actions/setup/action.yml | 7 ------- .github/workflows/checks.yml | 4 ++++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 26d1c837..24322666 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -27,10 +27,3 @@ runs: if: steps.cache-node-modules.outputs.cache-hit != 'true' run: pnpm i shell: bash - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - name: Run Anvil Node - run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - env: - ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} - POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 8a1d5ca7..c4850e15 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -41,6 +41,10 @@ jobs: - uses: actions/checkout@v3 - name: Setup uses: ./.github/actions/setup + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Run Anvil Node + run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - name: Test run: pnpm test:ci env: From 7ca52ad8c4cf8f22c143414b5e28ffd4cda934c6 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 20:04:48 -0300 Subject: [PATCH 091/199] Adding "ComposableStableJoinQueryResult" type; Removing validateInputs from pool-type folder and putting inside an "utils" folder; --- src/entities/join/poolJoin.ts | 2 +- src/entities/join/types.ts | 15 +++++++++--- .../validateInputs.ts | 0 src/entities/join/weighted/validateInputs.ts | 24 ------------------- 4 files changed, 13 insertions(+), 28 deletions(-) rename src/entities/join/{composable-stable => utils}/validateInputs.ts (100%) delete mode 100644 src/entities/join/weighted/validateInputs.ts diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index df0b9002..1d986674 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -8,7 +8,7 @@ import { } from './types'; import { WeightedJoin } from './weighted/weightedJoin'; import { PoolStateInput } from '../types'; -import { validateInputs } from './weighted/validateInputs'; +import { validateInputs } from './utils/validateInputs'; import { getSortedTokens } from '../utils/getSortedTokens'; import { ComposableStableJoin } from "./composable-stable/composableStableJoin"; diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index c5f62f8d..734b4c20 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -46,7 +46,11 @@ export type JoinInput = | ProportionalJoinInput; // Returned from a join query -export type JoinQueryResult = { +export type JoinQueryResult = + BaseJoinQueryResult + | ComposableStableJoinQueryResult; + +export type BaseJoinQueryResult = { poolType: string; poolId: Hex; joinKind: JoinKind; @@ -54,10 +58,15 @@ export type JoinQueryResult = { amountsIn: TokenAmount[]; fromInternalBalance: boolean; tokenInIndex?: number; +} + +export type ComposableStableJoinQueryResult = BaseJoinQueryResult & { bptIndex?:number; -}; +} -export type JoinCallInput = JoinQueryResult & { +export type JoinCallInput = JoinQueryResult + & ComposableStableJoinQueryResult + & { slippage: Slippage; sender: Address; recipient: Address; diff --git a/src/entities/join/composable-stable/validateInputs.ts b/src/entities/join/utils/validateInputs.ts similarity index 100% rename from src/entities/join/composable-stable/validateInputs.ts rename to src/entities/join/utils/validateInputs.ts diff --git a/src/entities/join/weighted/validateInputs.ts b/src/entities/join/weighted/validateInputs.ts deleted file mode 100644 index 733073c8..00000000 --- a/src/entities/join/weighted/validateInputs.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { JoinInput, JoinKind } from '../types'; -import { PoolStateInput } from '../../types'; -import { areTokensInArray } from '../../utils/areTokensInArray'; - -export function validateInputs(input: JoinInput, poolState: PoolStateInput) { - switch (input.kind) { - case JoinKind.Init: - case JoinKind.Unbalanced: - areTokensInArray( - input.amountsIn.map((a) => a.token.address), - poolState.tokens.map((t) => t.address), - ); - break; - case JoinKind.SingleAsset: - areTokensInArray( - [input.tokenIn], - poolState.tokens.map((t) => t.address), - ); - case JoinKind.Proportional: - areTokensInArray([input.bptOut.token.address], [poolState.address]); - default: - break; - } -} From 2691fdfbcd64560c8055e36e46768438f5ef98d4 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 21:23:40 -0300 Subject: [PATCH 092/199] Creating composable Stable Exit and integration test; --- .../composable-stable/composableStableExit.ts | 196 ++++++++++ src/entities/exit/poolExit.ts | 2 + src/entities/exit/types.ts | 13 +- test/composableStableExit.integration.test.ts | 336 ++++++++++++++++++ 4 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 src/entities/exit/composable-stable/composableStableExit.ts create mode 100644 test/composableStableExit.integration.test.ts diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts new file mode 100644 index 00000000..d07da0c1 --- /dev/null +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -0,0 +1,196 @@ +import { encodeFunctionData } from 'viem'; +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/constants'; +import { vaultAbi } from '../../../abi'; +import { parseExitArgs } from '../../utils/parseExitArgs'; +import { + BaseExit, + ExitBuildOutput, + ExitCallInput, + ExitInput, + ExitKind, + ExitQueryResult, +} from '../types'; +import { AmountsExit, PoolState } from '../../types'; +import { doQueryExit } from '../../utils/doQueryExit'; +import { ComposableStableEncoder } from "../../encoders/composableStable"; + +export class ComposableStableExit implements BaseExit { + public async query( + input: ExitInput, + poolState: PoolState, + ): Promise { + const bptIndex = poolState.tokens.findIndex((t)=> t.address == poolState.address); + const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); + const amountsWithoutBpt = { + ...amounts, + minAmountsOut: [...amounts.minAmountsOut.slice(0, bptIndex), ...amounts.minAmountsOut.slice(bptIndex+1)] + }; + const userData = this.encodeUserData(input.kind, amountsWithoutBpt); + + // tokensOut will have zero address if exit with native asset + const { args, tokensOut } = parseExitArgs({ + chainId: input.chainId, + exitWithNativeAsset: !!input.exitWithNativeAsset, + poolId: poolState.id, + sortedTokens: poolState.tokens, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + const queryResult = await doQueryExit( + input.rpcUrl, + input.chainId, + args, + ); + const bpt = new Token(input.chainId, poolState.address, 18); + const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + + const amountsOut = queryResult.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(tokensOut[i], a), + ); + + return { + poolType: poolState.type, + exitKind: input.kind, + id: poolState.id, + bptIn, + amountsOut, + tokenOutIndex: amounts.tokenOutIndex, + bptIndex + }; + } + + private getAmountsQuery(tokens: Token[], + input: ExitInput, + bptIndex?:number): AmountsExit { + switch (input.kind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: tokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ), + tokenOutIndex: undefined, + maxBptAmountIn: MAX_UINT256, + }; + case ExitKind.SINGLE_ASSET: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: tokens + .filter((_, index)=> index!==bptIndex) + .findIndex((t) => + t.isSameAddress(input.tokenOut), + ), + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: undefined, + maxBptAmountIn: input.bptIn.amount, + }; + } + } + + public buildCall(input: ExitCallInput): ExitBuildOutput { + const amounts = this.getAmountsCall(input); + if(input.bptIndex === undefined){ + throw new Error("bptIndex is necessary"); + } + const amountsWithoutBpt = { + ...amounts, + minAmountsOut: [...amounts.minAmountsOut.slice(0, input.bptIndex), ...amounts.minAmountsOut.slice(input.bptIndex+1)] + }; + const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); + + const { args } = parseExitArgs({ + poolId: input.id, + sortedTokens: input.amountsOut.map((a) => a.token), + sender: input.sender, + recipient: input.recipient, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'exitPool', + args, + }); + + return { + call, + to: BALANCER_VAULT, + value: 0n, + maxBptIn: amounts.maxBptAmountIn, + minAmountsOut: amounts.minAmountsOut, + }; + } + + private getAmountsCall(input: ExitCallInput): AmountsExit { + switch (input.exitKind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: input.amountsOut.map((a) => a.amount), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), + }; + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + 'tokenOutIndex must be defined for SINGLE_ASSET exit', + ); + } + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + default: + throw Error('Unsupported Exit Type'); + } + } + + private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + switch (kind) { + case ExitKind.UNBALANCED: + return ComposableStableEncoder.exitUnbalanced( + amounts.minAmountsOut, + amounts.maxBptAmountIn, + ); + case ExitKind.SINGLE_ASSET: + if (amounts.tokenOutIndex === undefined) + throw Error('No Index'); + + return ComposableStableEncoder.exitSingleAsset( + amounts.maxBptAmountIn, + amounts.tokenOutIndex, + ); + case ExitKind.PROPORTIONAL: + return ComposableStableEncoder.exitProportional(amounts.maxBptAmountIn); + default: + throw Error('Unsupported Exit Type'); + } + } +} diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 6d2a373f..47d12aeb 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -10,6 +10,7 @@ import { WeightedExit } from './weighted/weightedExit'; import { PoolStateInput } from '../types'; import { validateInputs } from './weighted/validateInputs'; import { getSortedTokens } from '../utils/getSortedTokens'; +import { ComposableStableExit } from "./composable-stable/composableStableExit"; export class PoolExit { private readonly poolExits: Record = {}; @@ -18,6 +19,7 @@ export class PoolExit { const { customPoolExits } = config || {}; this.poolExits = { Weighted: new WeightedExit(), + PHANTOM_STABLE: new ComposableStableExit(), // custom pool Exits take precedence over base Exits ...customPoolExits, }; diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 3938bbc7..166fd99a 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -2,6 +2,7 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; import { Address } from '../../types'; import { PoolState } from '../types'; +import { BaseJoinQueryResult, ComposableStableJoinQueryResult } from "../join"; export enum ExitKind { UNBALANCED = 'UNBALANCED', // exitExactOut @@ -38,8 +39,12 @@ export type ExitInput = | SingleAssetExitInput | ProportionalExitInput; +export type ExitQueryResult = + BaseExitQueryResult + | ComposableStableExitQueryResult; + // Returned from a exit query -export type ExitQueryResult = { +export type BaseExitQueryResult = { poolType: string; id: Address; exitKind: ExitKind; @@ -49,7 +54,11 @@ export type ExitQueryResult = { toInternalBalance?: boolean; }; -export type ExitCallInput = ExitQueryResult & { +export type ComposableStableExitQueryResult = BaseExitQueryResult & { + bptIndex?: number; +} + +export type ExitCallInput = ExitQueryResult & ComposableStableExitQueryResult & { slippage: Slippage; sender: Address; recipient: Address; diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts new file mode 100644 index 00000000..bca75958 --- /dev/null +++ b/test/composableStableExit.integration.test.ts @@ -0,0 +1,336 @@ +// pnpm test -- weightedExit.integration.test.ts +import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import { config } from 'dotenv'; +config(); + +import { + Client, + createTestClient, + http, + parseUnits, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, +} from 'viem'; +import { + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + Slippage, + Token, + TokenAmount, + replaceWrapped, + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, +} from '../src'; +import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; + +type TxInput = { + client: Client & PublicActions & TestActions & WalletActions; + poolExit: PoolExit; + exitInput: ExitInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; +}; + +const chainId = ChainId.MAINNET; +const rpcUrl = 'http://127.0.0.1:8545/'; +const blockNumber = BigInt(18043296); +const poolId = + '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool + +describe('composable stable exit test', () => { + let txInput: TxInput; + let bptToken: Token; + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + const poolInput = await api.getPool(poolId); + + const client: Client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions) as Client; + + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig + exitInput: {} as ExitInput, + checkNativeBalance: false, + }; + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [txInput.poolInput.address], + undefined, // TODO: hardcode these values to improve test performance + [parseUnits('1000', 18)], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); + }); + + test('single asset exit', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenOut = '0xf4edfad26ee0d23b69ca93112ecce52704e0006f'; // baoETH + + const exitInput: SingleAssetExitInput = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SINGLE_ASSET, + }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + }); + + // Query should use correct BPT amount + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We only expect single asset to have a value for exit + expect(queryResult.tokenOutIndex).to.be.toBeDefined; + queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a, i) => { + if (i === queryResult.tokenOutIndex) + expect(a.amount > BigInt(0)).to.be.true; + else expect(a.amount === BigInt(0)).to.be.true; + }); + + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); + }); + + test('proportional exit', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + + const exitInput: ProportionalExitInput = { + chainId, + rpcUrl, + bptIn, + kind: ExitKind.PROPORTIONAL, + }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + }); + + // Query should use correct BPT amount + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We expect all assets to have a value for exit + expect(queryResult.tokenOutIndex).to.be.undefined; + queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); + }); + + test('unbalanced exit', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const { poolInput, slippage } = txInput; + + const poolTokens = poolInput.tokens + .filter((_, index) => index!==bptIndex) + .map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const amountsOut = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '80'), + ); + + const exitInput: UnbalancedExitInput = { + chainId, + rpcUrl, + amountsOut, + kind: ExitKind.UNBALANCED, + }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + }); + + // We expect a BPT input amount > 0 + expect(queryResult.bptIn.amount > BigInt(0)).to.be.true; + + // We expect assets to have same amount out as user defined + expect(queryResult.tokenOutIndex).to.be.undefined; + queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a, i) => { + expect(a.amount).to.eq(amountsOut[i].amount); + }); + + // Confirm slippage - only to bpt in, not amounts out + const expectedMinAmountsOut = amountsOut.map((a) => a.amount); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut.filter((_, index) => index!==bptIndex)); + const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); + expect(expectedMaxBptIn).to.deep.eq(maxBptIn); + }); + + test('exit with native asset', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + + const exitInput: ProportionalExitInput = { + chainId, + rpcUrl, + bptIn, + kind: ExitKind.PROPORTIONAL, + exitWithNativeAsset: true, + }; + + // Note - checking native balance + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + checkNativeBalance: true, + }); + + // Query should use correct BPT amount + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We expect all assets to have a value for exit + expect(queryResult.tokenOutIndex).to.be.undefined; + queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut + .filter((_, index) => index!==bptIndex).map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut + .filter((_, index) => index!==bptIndex)); + expect(maxBptIn).to.eq(bptIn.amount); + }); + + async function doTransaction(txIp: TxInput) { + const { + poolExit, + poolInput, + exitInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolExit.query(exitInput, poolInput); + const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( + { + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolTokensAddr], + client, + testAddress, + to, + call, + value, + ); + expect(transactionReceipt.status).to.eq('success'); + const bptIndex = poolInput.tokens.findIndex((t) => t.address == poolInput.address); + // Confirm final balance changes match query result + const expectedDeltas = [ + ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), + queryResult.bptIn.amount, + ...queryResult.amountsOut.slice(bptIndex+1).map((a) => a.amount), + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxBptIn, + minAmountsOut, + }; + } +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + const tokens = [ + { + address: + '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d0' as Address, // B-baoETH-ETH-BPT + decimals: 18, + index: 0, + }, + { + address: + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH + decimals: 18, + index: 1, + }, + { + address: + '0xf4edfad26ee0d23b69ca93112ecce52704e0006f' as Address, // baoETH + decimals: 18, + index: 2, + }, + ]; + + return { + id, + address: getPoolAddress(id) as Address, + type: 'PHANTOM_STABLE', + tokens, + }; + } +} + +/******************************************************************************/ From 2a938d6caae10f7e9ea81a174df6b2b46683be70 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 21:29:47 -0300 Subject: [PATCH 093/199] Fixing Lint; --- src/entities/encoders/composableStable.ts | 14 +++++++------- .../join/composable-stable/composableStableJoin.ts | 2 +- test/composableStableJoin.integration.test.ts | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index f8432c8d..ca0e6c02 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -4,16 +4,16 @@ import { Address } from '../../types'; export enum ComposableStablePoolJoinKind { - INIT, - EXACT_TOKENS_IN_FOR_BPT_OUT, - TOKEN_IN_FOR_EXACT_BPT_OUT, - ALL_TOKENS_IN_FOR_EXACT_BPT_OUT + INIT = 0, + EXACT_TOKENS_IN_FOR_BPT_OUT = 1, + TOKEN_IN_FOR_EXACT_BPT_OUT = 2, + ALL_TOKENS_IN_FOR_EXACT_BPT_OUT = 3 } export enum ComposableStablePoolExitKind { - EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, - BPT_IN_FOR_EXACT_TOKENS_OUT, - EXACT_BPT_IN_FOR_ALL_TOKENS_OUT + EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, + BPT_IN_FOR_EXACT_TOKENS_OUT = 1, + EXACT_BPT_IN_FOR_ALL_TOKENS_OUT = 2 } export class ComposableStableEncoder { diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index fc68790c..d1523474 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -21,7 +21,7 @@ export class ComposableStableJoin implements BaseJoin { input: JoinInput, poolState: PoolState, ): Promise { - const bptIndex = poolState.tokens.findIndex((t)=> t.address == poolState.address); + const bptIndex = poolState.tokens.findIndex((t)=> t.address === poolState.address); const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); const amountsWithoutBpt = { ...amounts, diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 41c94851..7557d8ac 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -103,7 +103,7 @@ describe('composable stable join test', () => { }); test('unbalanced join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const poolTokensWithoutBpt = txInput.poolInput.tokens.map( (t) => new Token(chainId, t.address, t.decimals), ).filter((_, index) => index !== bptIndex); @@ -145,7 +145,7 @@ describe('composable stable join test', () => { }); test('native asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const poolTokensWithoutBpt = txInput.poolInput.tokens.map( (t) => new Token(chainId, t.address, t.decimals), @@ -195,7 +195,7 @@ describe('composable stable join test', () => { }); test('single asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; @@ -238,7 +238,7 @@ describe('composable stable join test', () => { test('proportional join', async () => { const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); // perform join query to get expected bpt out const joinInput: ProportionalJoinInput = { @@ -315,7 +315,7 @@ describe('composable stable join test', () => { call, value, ); - const bptIndex = poolInput.tokens.findIndex((t) => t.address == poolInput.address); + const bptIndex = poolInput.tokens.findIndex((t) => t.address === poolInput.address); expect(transactionReceipt.status).to.eq('success'); // Confirm final balance changes match query result const expectedDeltas = [ From 2fc96f70a92296f6d2ac37b768187b774a779abb Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 17 Oct 2023 21:32:53 -0300 Subject: [PATCH 094/199] Fixing Lint; --- .../exit/composable-stable/composableStableExit.ts | 3 +-- src/entities/exit/types.ts | 1 - test/composableStableExit.integration.test.ts | 10 +++++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index d07da0c1..b3a6b2e1 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -1,7 +1,6 @@ import { encodeFunctionData } from 'viem'; import { Token } from '../../token'; import { TokenAmount } from '../../tokenAmount'; -import { WeightedEncoder } from '../../encoders/weighted'; import { Address } from '../../../types'; import { BALANCER_VAULT, @@ -27,7 +26,7 @@ export class ComposableStableExit implements BaseExit { input: ExitInput, poolState: PoolState, ): Promise { - const bptIndex = poolState.tokens.findIndex((t)=> t.address == poolState.address); + const bptIndex = poolState.tokens.findIndex((t)=> t.address === poolState.address); const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); const amountsWithoutBpt = { ...amounts, diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 166fd99a..03173b03 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -2,7 +2,6 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; import { Address } from '../../types'; import { PoolState } from '../types'; -import { BaseJoinQueryResult, ComposableStableJoinQueryResult } from "../join"; export enum ExitKind { UNBALANCED = 'UNBALANCED', // exitExactOut diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index bca75958..f6e0a5f7 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -93,7 +93,7 @@ describe('composable stable exit test', () => { }); test('single asset exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const { slippage } = txInput; const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); const tokenOut = '0xf4edfad26ee0d23b69ca93112ecce52704e0006f'; // baoETH @@ -130,7 +130,7 @@ describe('composable stable exit test', () => { }); test('proportional exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const { slippage } = txInput; const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); @@ -163,7 +163,7 @@ describe('composable stable exit test', () => { }); test('unbalanced exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const { poolInput, slippage } = txInput; const poolTokens = poolInput.tokens @@ -203,7 +203,7 @@ describe('composable stable exit test', () => { }); test('exit with native asset', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address == txInput.poolInput.address); + const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); const { slippage } = txInput; const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); @@ -282,7 +282,7 @@ describe('composable stable exit test', () => { value, ); expect(transactionReceipt.status).to.eq('success'); - const bptIndex = poolInput.tokens.findIndex((t) => t.address == poolInput.address); + const bptIndex = poolInput.tokens.findIndex((t) => t.address === poolInput.address); // Confirm final balance changes match query result const expectedDeltas = [ ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), From ffeaa9d840b74cc82b28e03e33e46c38f5c44c8d Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 18 Oct 2023 10:02:12 +0100 Subject: [PATCH 095/199] fix: Client type in test. --- test/composableStableJoin.integration.test.ts | 696 +++++++++--------- 1 file changed, 356 insertions(+), 340 deletions(-) diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 7557d8ac..9fa60b92 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -1,371 +1,387 @@ -// pnpm test -- weightedJoin.integration.test.ts +// pnpm test -- composableStableJoin.integration.test.ts import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; import { config } from 'dotenv'; config(); import { - Client, - createTestClient, - http, - parseUnits, - publicActions, - PublicActions, - TestActions, - WalletActions, - walletActions, + Client, + createTestClient, + http, + parseUnits, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, } from 'viem'; import { - UnbalancedJoinInput, - ProportionalJoinInput, - SingleAssetJoinInput, - JoinKind, - Slippage, - Token, - TokenAmount, - replaceWrapped, - Address, - Hex, - PoolStateInput, - CHAINS, - ChainId, - getPoolAddress, - PoolJoin, - JoinInput, + UnbalancedJoinInput, + ProportionalJoinInput, + SingleAssetJoinInput, + JoinKind, + Slippage, + Token, + TokenAmount, + replaceWrapped, + Address, + Hex, + PoolStateInput, + CHAINS, + ChainId, + getPoolAddress, + PoolJoin, + JoinInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; - joinInput: JoinInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; + client: Client & PublicActions & TestActions & WalletActions; + poolJoin: PoolJoin; + joinInput: JoinInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; }; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const blockNumber = BigInt(18043296); const poolId = - '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool + '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool describe('composable stable join test', () => { - let txInput: TxInput; - let bptToken: Token; - - beforeAll(async () => { - // setup mock api - const api = new MockApi(); - - // get pool state from api - const poolInput = await api.getPool(poolId); - - const client: Client = createTestClient({ - mode: 'hardhat', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions) as Client; - - txInput = { - client, - poolJoin: new PoolJoin(), - slippage: Slippage.fromPercentage('1'), // 1% - poolInput, - testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - joinInput: {} as JoinInput, - checkNativeBalance: false, - }; - - // setup BPT token - bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); - }); - - beforeEach(async () => { - await forkSetup( - txInput.client, - txInput.testAddress, - [ - ...txInput.poolInput.tokens.map((t) => t.address), - ], - [0,0,3], - [ - ...txInput.poolInput.tokens.map((t) => - parseUnits('100', t.decimals), - ), - ], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, - ); - }); - - test('unbalanced join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const poolTokensWithoutBpt = txInput.poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ).filter((_, index) => index !== bptIndex); - - const amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); - // Query should use same amountsIn as user sets - expect(queryResult.amountsIn.filter((_, index)=>index!==bptIndex)).to.deep.eq(amountsIn); - expect(queryResult.tokenInIndex).to.be.undefined; - - // Should be no native value - expect(value).toBeUndefined; - - // Expect some bpt amount - expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn.filter((_, index)=>index!==bptIndex)); - }); - - test('native asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - - const poolTokensWithoutBpt = txInput.poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ).filter((_, index) => index !== bptIndex); - - const amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - useNativeAssetAsWrappedAmountIn: true, - }; - - // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - checkNativeBalance: true, - }); - - // Query should use same amountsIn as user sets - expect(queryResult.amountsIn - .filter((_, index)=>index!==bptIndex) - .map((a) => a.amount)).to.deep.eq( - amountsIn.map((a) => a.amount), - ); - expect(queryResult.tokenInIndex).to.be.undefined; - // Should have native value equal to input amount - expect(value).eq(amountsIn[1].amount); - - // Expect some bpt amount - expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn.filter((_, index)=>index!==bptIndex)); - }); - - test('single asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; - - // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - bptOut, - tokenIn, - chainId, - rpcUrl, - kind: JoinKind.SingleAsset, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - // We only expect single asset to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; - queryResult.amountsIn.filter((_, index) => index!==bptIndex) - .forEach((a, i) => { - if (i === queryResult.tokenInIndex) - expect(a.amount > BigInt(0)).to.be.true; - else expect(a.amount === BigInt(0)).to.be.true; + let txInput: TxInput; + let bptToken: Token; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + const poolInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig + joinInput: {} as JoinInput, + checkNativeBalance: false, + }; + + // setup BPT token + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); - // Should be no native value - expect(value).toBeUndefined; - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); - }); - - test('proportional join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - - // perform join query to get expected bpt out - const joinInput: ProportionalJoinInput = { - bptOut, - chainId, - rpcUrl, - kind: JoinKind.Proportional, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); - - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - // Expect all assets to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; - queryResult.amountsIn.filter((_, index) => index!==bptIndex) - .forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [...txInput.poolInput.tokens.map((t) => t.address)], + [0, 0, 3], + [ + ...txInput.poolInput.tokens.map((t) => + parseUnits('100', t.decimals), + ), + ], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); }); - expect(queryResult.tokenInIndex).toBeUndefined; - - // Should be no native value - expect(value).toBeUndefined; - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); - }); - - async function doTransaction(txIp: TxInput) { - const { - poolJoin, - poolInput, - joinInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolJoin.query(joinInput, poolInput); - const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr], - client, - testAddress, - to, - call, - value, - ); - const bptIndex = poolInput.tokens.findIndex((t) => t.address === poolInput.address); - expect(transactionReceipt.status).to.eq('success'); - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsIn.slice(0, bptIndex).map((a)=>a.amount), - queryResult.bptOut.amount, - ...queryResult.amountsIn.slice(bptIndex+1).map((a)=>a.amount) - ] - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxAmountsIn, - minBptOut, - value, - }; - } + + test('unbalanced join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const poolTokensWithoutBpt = txInput.poolInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + .filter((_, index) => index !== bptIndex); + + const amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + // Query should use same amountsIn as user sets + expect( + queryResult.amountsIn.filter((_, index) => index !== bptIndex), + ).to.deep.eq(amountsIn); + expect(queryResult.tokenInIndex).to.be.undefined; + + // Should be no native value + expect(value).toBeUndefined; + + // Expect some bpt amount + expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq( + maxAmountsIn.filter((_, index) => index !== bptIndex), + ); + }); + + test('native asset join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + + const poolTokensWithoutBpt = txInput.poolInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + .filter((_, index) => index !== bptIndex); + + const amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + useNativeAssetAsWrappedAmountIn: true, + }; + + // We have to use zero address for balanceDeltas + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + checkNativeBalance: true, + }); + + // Query should use same amountsIn as user sets + expect( + queryResult.amountsIn + .filter((_, index) => index !== bptIndex) + .map((a) => a.amount), + ).to.deep.eq(amountsIn.map((a) => a.amount)); + expect(queryResult.tokenInIndex).to.be.undefined; + // Should have native value equal to input amount + expect(value).eq(amountsIn[1].amount); + + // Expect some bpt amount + expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq( + maxAmountsIn.filter((_, index) => index !== bptIndex), + ); + }); + + test('single asset join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; + + // perform join query to get expected bpt out + const joinInput: SingleAssetJoinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // We only expect single asset to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn + .filter((_, index) => index !== bptIndex) + .forEach((a, i) => { + if (i === queryResult.tokenInIndex) + expect(a.amount > BigInt(0)).to.be.true; + else expect(a.amount === BigInt(0)).to.be.true; + }); + + // Should be no native value + expect(value).toBeUndefined; + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + txInput.slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); + + test('proportional join', async () => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + + // perform join query to get expected bpt out + const joinInput: ProportionalJoinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // Expect all assets to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn + .filter((_, index) => index !== bptIndex) + .forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + expect(queryResult.tokenInIndex).toBeUndefined; + + // Should be no native value + expect(value).toBeUndefined; + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + txInput.slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); + + async function doTransaction(txIp: TxInput) { + const { + poolJoin, + poolInput, + joinInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolJoin.query(joinInput, poolInput); + const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( + { + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolTokensAddr], + client, + testAddress, + to, + call, + value, + ); + const bptIndex = poolInput.tokens.findIndex( + (t) => t.address === poolInput.address, + ); + expect(transactionReceipt.status).to.eq('success'); + // Confirm final balance changes match query result + const expectedDeltas = [ + ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), + queryResult.bptOut.amount, + ...queryResult.amountsIn.slice(bptIndex + 1).map((a) => a.amount), + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxAmountsIn, + minBptOut, + value, + }; + } }); /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Hex): Promise { - const tokens = [ - { - address: - '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76' as Address, // vETH/WETH BPT - decimals: 18, - index: 0, - }, - { - address: - '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f' as Address, // VETH - decimals: 18, - index: 1, - }, - { - address: - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH - decimals: 18, - index: 2, - }, - ]; - - return { - id, - address: getPoolAddress(id) as Address, - type: 'PHANTOM_STABLE', - tokens, - }; - } + public async getPool(id: Hex): Promise { + const tokens = [ + { + address: + '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76' as Address, // vETH/WETH BPT + decimals: 18, + index: 0, + }, + { + address: + '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f' as Address, // VETH + decimals: 18, + index: 1, + }, + { + address: + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH + decimals: 18, + index: 2, + }, + ]; + + return { + id, + address: getPoolAddress(id) as Address, + type: 'PHANTOM_STABLE', + tokens, + }; + } } /******************************************************************************/ From 468702663abe20202e0ff3d1570c6a5213359db9 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 18 Oct 2023 10:04:06 +0100 Subject: [PATCH 096/199] chore: Add comment explaining PHANTOM_STABLE naming. --- src/entities/join/poolJoin.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 1d986674..0b84d2a3 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -10,7 +10,7 @@ import { WeightedJoin } from './weighted/weightedJoin'; import { PoolStateInput } from '../types'; import { validateInputs } from './utils/validateInputs'; import { getSortedTokens } from '../utils/getSortedTokens'; -import { ComposableStableJoin } from "./composable-stable/composableStableJoin"; +import { ComposableStableJoin } from './composable-stable/composableStableJoin'; export class PoolJoin { private readonly poolJoins: Record = {}; @@ -19,6 +19,7 @@ export class PoolJoin { const { customPoolJoins } = config || {}; this.poolJoins = { Weighted: new WeightedJoin(), + // PHANTOM_STABLE === ComposableStables in API PHANTOM_STABLE: new ComposableStableJoin(), // custom pool Joins take precedence over base Joins ...customPoolJoins, From 6a08b5fbbb36e98e57bcbf02c903feba6617430c Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 18 Oct 2023 10:23:14 +0100 Subject: [PATCH 097/199] Refactor: Join types. --- .../composable-stable/composableStableJoin.ts | 56 +++++++++++-------- src/entities/join/poolJoin.ts | 4 +- src/entities/join/types.ts | 32 ++++++----- src/entities/join/weighted/weightedJoin.ts | 10 ++-- 4 files changed, 59 insertions(+), 43 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index d1523474..d43d191a 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -7,25 +7,30 @@ import { vaultAbi } from '../../../abi'; import { BaseJoin, JoinBuildOutput, - JoinCallInput, JoinInput, JoinKind, - JoinQueryResult, + ComposableStableJoinQueryResult, + ComposableJoinCall, } from '../types'; import { AmountsJoin, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; -import { ComposableStableEncoder } from "../../encoders/composableStable"; +import { ComposableStableEncoder } from '../../encoders/composableStable'; export class ComposableStableJoin implements BaseJoin { public async query( input: JoinInput, poolState: PoolState, - ): Promise { - const bptIndex = poolState.tokens.findIndex((t)=> t.address === poolState.address); + ): Promise { + const bptIndex = poolState.tokens.findIndex( + (t) => t.address === poolState.address, + ); const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); const amountsWithoutBpt = { - ...amounts, - maxAmountsIn: [...amounts.maxAmountsIn.slice(0, bptIndex), ...amounts.maxAmountsIn.slice(bptIndex+1)] + ...amounts, + maxAmountsIn: [ + ...amounts.maxAmountsIn.slice(0, bptIndex), + ...amounts.maxAmountsIn.slice(bptIndex + 1), + ], }; const userData = this.encodeUserData(input.kind, amountsWithoutBpt); @@ -68,14 +73,17 @@ export class ComposableStableJoin implements BaseJoin { }; } - public buildCall(input: JoinCallInput): JoinBuildOutput { + public buildCall(input: ComposableJoinCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); - if(input.bptIndex === undefined){ - throw new Error("bptIndex is necessary"); + if (input.bptIndex === undefined) { + throw new Error('bptIndex is necessary'); } const amountsWithoutBpt = { ...amounts, - maxAmountsIn: [...amounts.maxAmountsIn.slice(0, input.bptIndex), ...amounts.maxAmountsIn.slice(input.bptIndex+1)] + maxAmountsIn: [ + ...amounts.maxAmountsIn.slice(0, input.bptIndex), + ...amounts.maxAmountsIn.slice(input.bptIndex + 1), + ], }; const userData = this.encodeUserData(input.joinKind, amountsWithoutBpt); @@ -110,26 +118,28 @@ export class ComposableStableJoin implements BaseJoin { private getAmountsQuery( poolTokens: Token[], input: JoinInput, - bptIndex?:number, + bptIndex?: number, ): AmountsJoin { switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: { return { minimumBpt: 0n, - maxAmountsIn: getAmounts(poolTokens, input.amountsIn, BigInt(0)), + maxAmountsIn: getAmounts( + poolTokens, + input.amountsIn, + BigInt(0), + ), tokenInIndex: undefined, }; } case JoinKind.SingleAsset: { - if(bptIndex===undefined){ - throw new Error("bptIndex is necessary"); + if (bptIndex === undefined) { + throw new Error('bptIndex is necessary'); } const tokenInIndex = poolTokens - .filter((_, index)=> index!==bptIndex) // Need to remove Bpt - .findIndex((t) => - t.isSameAddress(input.tokenIn) - ); + .filter((_, index) => index !== bptIndex) // Need to remove Bpt + .findIndex((t) => t.isSameAddress(input.tokenIn)); if (tokenInIndex === -1) throw Error("Can't find index of SingleAsset"); const maxAmountsIn = Array(poolTokens.length).fill(0n); @@ -137,7 +147,7 @@ export class ComposableStableJoin implements BaseJoin { return { minimumBpt: input.bptOut.amount, maxAmountsIn, - tokenInIndex + tokenInIndex, }; } case JoinKind.Proportional: { @@ -152,7 +162,7 @@ export class ComposableStableJoin implements BaseJoin { } } - private getAmountsCall(input: JoinCallInput): AmountsJoin { + private getAmountsCall(input: ComposableJoinCall): AmountsJoin { switch (input.joinKind) { case JoinKind.Init: case JoinKind.Unbalanced: { @@ -197,7 +207,9 @@ export class ComposableStableJoin implements BaseJoin { ); } case JoinKind.Proportional: { - return ComposableStableEncoder.joinProportional(amounts.minimumBpt); + return ComposableStableEncoder.joinProportional( + amounts.minimumBpt, + ); } default: throw Error('Unsupported Join Type'); diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 0b84d2a3..d85d8ff7 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -4,7 +4,7 @@ import { JoinConfig, JoinInput, JoinQueryResult, - JoinCallInput, + JoinCall, } from './types'; import { WeightedJoin } from './weighted/weightedJoin'; import { PoolStateInput } from '../types'; @@ -49,7 +49,7 @@ export class PoolJoin { return this.getJoin(poolState.type).query(input, mappedPoolState); } - public buildCall(input: JoinCallInput): JoinBuildOutput { + public buildCall(input: JoinCall): JoinBuildOutput { return this.getJoin(input.poolType).buildCall(input); } } diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 734b4c20..3c8f622f 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -11,7 +11,7 @@ export enum JoinKind { } // This will be extended for each pools specific input requirements -export type BaseJoinInput = { +type BaseJoinInput = { chainId: number; rpcUrl: string; useNativeAssetAsWrappedAmountIn?: boolean; @@ -45,12 +45,7 @@ export type JoinInput = | SingleAssetJoinInput | ProportionalJoinInput; -// Returned from a join query -export type JoinQueryResult = - BaseJoinQueryResult - | ComposableStableJoinQueryResult; - -export type BaseJoinQueryResult = { +type BaseJoinQueryResult = { poolType: string; poolId: Hex; joinKind: JoinKind; @@ -58,23 +53,32 @@ export type BaseJoinQueryResult = { amountsIn: TokenAmount[]; fromInternalBalance: boolean; tokenInIndex?: number; -} +}; + +export type WeightedJoinQueryResult = BaseJoinQueryResult; export type ComposableStableJoinQueryResult = BaseJoinQueryResult & { - bptIndex?:number; -} + bptIndex?: number; +}; + +export type JoinQueryResult = + | WeightedJoinQueryResult + | ComposableStableJoinQueryResult; -export type JoinCallInput = JoinQueryResult - & ComposableStableJoinQueryResult - & { +type BaseJoinCall = { slippage: Slippage; sender: Address; recipient: Address; }; +export type ComposableJoinCall = BaseJoinCall & ComposableStableJoinQueryResult; +export type WeightedJoinCall = BaseJoinCall & BaseJoinQueryResult; + +export type JoinCall = WeightedJoinCall | ComposableJoinCall; + export interface BaseJoin { query(input: JoinInput, poolState: PoolState): Promise; - buildCall(input: JoinCallInput): { + buildCall(input: JoinCall): { call: Hex; to: Address; value: bigint | undefined; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 262b52b8..9f4551b0 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -8,10 +8,10 @@ import { vaultAbi } from '../../../abi'; import { BaseJoin, JoinBuildOutput, - JoinCallInput, JoinInput, JoinKind, - JoinQueryResult, + WeightedJoinQueryResult, + WeightedJoinCall, } from '../types'; import { AmountsJoin, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; @@ -20,7 +20,7 @@ export class WeightedJoin implements BaseJoin { public async query( input: JoinInput, poolState: PoolState, - ): Promise { + ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); const userData = this.encodeUserData(input.kind, amounts); @@ -62,7 +62,7 @@ export class WeightedJoin implements BaseJoin { }; } - public buildCall(input: JoinCallInput): JoinBuildOutput { + public buildCall(input: WeightedJoinCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.joinKind, amounts); @@ -133,7 +133,7 @@ export class WeightedJoin implements BaseJoin { } } - private getAmountsCall(input: JoinCallInput): AmountsJoin { + private getAmountsCall(input: WeightedJoinCall): AmountsJoin { switch (input.joinKind) { case JoinKind.Init: case JoinKind.Unbalanced: { From cb58df9d6bf050cbcaaccd607f4662d552f953b1 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 18 Oct 2023 11:35:09 +0200 Subject: [PATCH 098/199] Setup @viem/anvil for automatic fork setup --- .changeset/large-socks-exist.md | 2 +- hardhat.config.ts | 12 - package.json | 4 +- pnpm-lock.yaml | 1931 ++---------------------------- test/anvil/anvil-global-setup.ts | 33 + test/lib/utils/helper.ts | 8 +- test/lib/utils/promises.ts | 3 + vitest.config.ts | 1 + 8 files changed, 146 insertions(+), 1848 deletions(-) delete mode 100644 hardhat.config.ts create mode 100644 test/anvil/anvil-global-setup.ts create mode 100644 test/lib/utils/promises.ts diff --git a/.changeset/large-socks-exist.md b/.changeset/large-socks-exist.md index bcff5dde..2768f766 100644 --- a/.changeset/large-socks-exist.md +++ b/.changeset/large-socks-exist.md @@ -5,4 +5,4 @@ - 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 +- Integration tests run against local viem fork diff --git a/hardhat.config.ts b/hardhat.config.ts deleted file mode 100644 index 6efa9583..00000000 --- a/hardhat.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import '@nomiclabs/hardhat-ethers'; - -/** - * @type import('hardhat/config').HardhatUserConfig - */ -export default { - networks: { - hardhat: { - chainId: 1, - }, - }, -}; diff --git a/package.json b/package.json index 6eb9cd76..f59ba444 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "test:ci": "vitest run", "changeset": "changeset", "changeset:release": "pnpm build && changeset publish", - "node": "anvil --fork-url $(. ./.env && echo $ETHEREUM_RPC_URL)" }, "dependencies": { "async-retry": "^1.3.3", @@ -35,11 +34,10 @@ }, "devDependencies": { "@changesets/cli": "^2.26.1", - "@nomiclabs/hardhat-ethers": "^2.2.3", "@types/async-retry": "^1.4.4", "@types/node": "^18.11.18", + "@viem/anvil": "^0.0.6", "dotenv": "^16.0.3", - "hardhat": "^2.17.2", "pino-pretty": "^10.0.0", "rome": "12.1.3", "ts-node": "^10.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a294b89..17a59130 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,21 +22,18 @@ devDependencies: '@changesets/cli': specifier: ^2.26.1 version: 2.26.1 - '@nomiclabs/hardhat-ethers': - specifier: ^2.2.3 - version: 2.2.3(ethers@5.7.2)(hardhat@2.17.2) '@types/async-retry': specifier: ^1.4.4 version: 1.4.5 '@types/node': specifier: ^18.11.18 version: 18.15.11 + '@viem/anvil': + specifier: ^0.0.6 + version: 0.0.6 dotenv: specifier: ^16.0.3 version: 16.0.3 - hardhat: - specifier: ^2.17.2 - version: 2.17.2(ts-node@10.9.1)(typescript@5.1.3) pino-pretty: specifier: ^10.0.0 version: 10.0.0 @@ -93,37 +90,6 @@ packages: regenerator-runtime: 0.13.11 dev: true - /@chainsafe/as-sha256@0.3.1: - resolution: {integrity: sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==} - dev: true - - /@chainsafe/persistent-merkle-tree@0.4.2: - resolution: {integrity: sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==} - dependencies: - '@chainsafe/as-sha256': 0.3.1 - dev: true - - /@chainsafe/persistent-merkle-tree@0.5.0: - resolution: {integrity: sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==} - dependencies: - '@chainsafe/as-sha256': 0.3.1 - dev: true - - /@chainsafe/ssz@0.10.2: - resolution: {integrity: sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==} - dependencies: - '@chainsafe/as-sha256': 0.3.1 - '@chainsafe/persistent-merkle-tree': 0.5.0 - dev: true - - /@chainsafe/ssz@0.9.4: - resolution: {integrity: sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==} - dependencies: - '@chainsafe/as-sha256': 0.3.1 - '@chainsafe/persistent-merkle-tree': 0.4.2 - case: 1.6.3 - dev: true - /@changesets/apply-release-plan@6.1.3: resolution: {integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==} dependencies: @@ -711,321 +677,6 @@ packages: dev: true optional: true - /@ethersproject/abi@5.7.0: - resolution: {integrity: sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==} - dependencies: - '@ethersproject/address': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: true - - /@ethersproject/abstract-provider@5.7.0: - resolution: {integrity: sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/networks': 5.7.1 - '@ethersproject/properties': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/web': 5.7.1 - dev: true - - /@ethersproject/abstract-signer@5.7.0: - resolution: {integrity: sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==} - dependencies: - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - dev: true - - /@ethersproject/address@5.7.0: - resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/rlp': 5.7.0 - dev: true - - /@ethersproject/base64@5.7.0: - resolution: {integrity: sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==} - dependencies: - '@ethersproject/bytes': 5.7.0 - dev: true - - /@ethersproject/basex@5.7.0: - resolution: {integrity: sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/properties': 5.7.0 - dev: true - - /@ethersproject/bignumber@5.7.0: - resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - bn.js: 5.2.1 - dev: true - - /@ethersproject/bytes@5.7.0: - resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} - dependencies: - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/constants@5.7.0: - resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - dev: true - - /@ethersproject/contracts@5.7.0: - resolution: {integrity: sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==} - dependencies: - '@ethersproject/abi': 5.7.0 - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/transactions': 5.7.0 - dev: true - - /@ethersproject/hash@5.7.0: - resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} - dependencies: - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/base64': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: true - - /@ethersproject/hdnode@5.7.0: - resolution: {integrity: sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==} - dependencies: - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/basex': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/pbkdf2': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/sha2': 5.7.0 - '@ethersproject/signing-key': 5.7.0 - '@ethersproject/strings': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/wordlists': 5.7.0 - dev: true - - /@ethersproject/json-wallets@5.7.0: - resolution: {integrity: sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==} - dependencies: - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/hdnode': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/pbkdf2': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/random': 5.7.0 - '@ethersproject/strings': 5.7.0 - '@ethersproject/transactions': 5.7.0 - aes-js: 3.0.0 - scrypt-js: 3.0.1 - dev: true - - /@ethersproject/keccak256@5.7.0: - resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} - dependencies: - '@ethersproject/bytes': 5.7.0 - js-sha3: 0.8.0 - dev: true - - /@ethersproject/logger@5.7.0: - resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} - dev: true - - /@ethersproject/networks@5.7.1: - resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} - dependencies: - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/pbkdf2@5.7.0: - resolution: {integrity: sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/sha2': 5.7.0 - dev: true - - /@ethersproject/properties@5.7.0: - resolution: {integrity: sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==} - dependencies: - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/providers@5.7.2: - resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} - dependencies: - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/base64': 5.7.0 - '@ethersproject/basex': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/networks': 5.7.1 - '@ethersproject/properties': 5.7.0 - '@ethersproject/random': 5.7.0 - '@ethersproject/rlp': 5.7.0 - '@ethersproject/sha2': 5.7.0 - '@ethersproject/strings': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/web': 5.7.1 - bech32: 1.1.4 - ws: 7.4.6 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: true - - /@ethersproject/random@5.7.0: - resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/rlp@5.7.0: - resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/sha2@5.7.0: - resolution: {integrity: sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - hash.js: 1.1.7 - dev: true - - /@ethersproject/signing-key@5.7.0: - resolution: {integrity: sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - bn.js: 5.2.1 - elliptic: 6.5.4 - hash.js: 1.1.7 - dev: true - - /@ethersproject/solidity@5.7.0: - resolution: {integrity: sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/sha2': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: true - - /@ethersproject/strings@5.7.0: - resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/transactions@5.7.0: - resolution: {integrity: sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==} - dependencies: - '@ethersproject/address': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/rlp': 5.7.0 - '@ethersproject/signing-key': 5.7.0 - dev: true - - /@ethersproject/units@5.7.0: - resolution: {integrity: sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==} - dependencies: - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/logger': 5.7.0 - dev: true - - /@ethersproject/wallet@5.7.0: - resolution: {integrity: sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==} - dependencies: - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/hdnode': 5.7.0 - '@ethersproject/json-wallets': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/random': 5.7.0 - '@ethersproject/signing-key': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/wordlists': 5.7.0 - dev: true - - /@ethersproject/web@5.7.1: - resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} - dependencies: - '@ethersproject/base64': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: true - - /@ethersproject/wordlists@5.7.0: - resolution: {integrity: sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==} - dependencies: - '@ethersproject/bytes': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 - dev: true - /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -1062,17 +713,6 @@ packages: read-yaml-file: 1.1.0 dev: true - /@metamask/eth-sig-util@4.0.1: - resolution: {integrity: sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==} - engines: {node: '>=12.0.0'} - dependencies: - ethereumjs-abi: 0.6.8 - ethereumjs-util: 6.2.1 - ethjs-util: 0.1.6 - tweetnacl: 1.0.3 - tweetnacl-util: 0.15.1 - dev: true - /@noble/curves@1.0.0: resolution: {integrity: sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==} dependencies: @@ -1085,10 +725,6 @@ packages: '@noble/hashes': 1.3.1 dev: false - /@noble/hashes@1.2.0: - resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} - dev: true - /@noble/hashes@1.3.0: resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} dev: false @@ -1098,10 +734,6 @@ packages: engines: {node: '>= 16'} dev: false - /@noble/secp256k1@1.7.1: - resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} - dev: true - /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1123,280 +755,6 @@ packages: fastq: 1.15.0 dev: true - /@nomicfoundation/ethereumjs-block@5.0.2: - resolution: {integrity: sha512-hSe6CuHI4SsSiWWjHDIzWhSiAVpzMUcDRpWYzN0T9l8/Rz7xNn3elwVOJ/tAyS0LqL6vitUD78Uk7lQDXZun7Q==} - engines: {node: '>=14'} - dependencies: - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-trie': 6.0.2 - '@nomicfoundation/ethereumjs-tx': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - ethereum-cryptography: 0.1.3 - ethers: 5.7.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: true - - /@nomicfoundation/ethereumjs-blockchain@7.0.2: - resolution: {integrity: sha512-8UUsSXJs+MFfIIAKdh3cG16iNmWzWC/91P40sazNvrqhhdR/RtGDlFk2iFTGbBAZPs2+klZVzhRX8m2wvuvz3w==} - engines: {node: '>=14'} - dependencies: - '@nomicfoundation/ethereumjs-block': 5.0.2 - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-ethash': 3.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-trie': 6.0.2 - '@nomicfoundation/ethereumjs-tx': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - abstract-level: 1.0.3 - debug: 4.3.4(supports-color@8.1.1) - ethereum-cryptography: 0.1.3 - level: 8.0.0 - lru-cache: 5.1.1 - memory-level: 1.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /@nomicfoundation/ethereumjs-common@4.0.2: - resolution: {integrity: sha512-I2WGP3HMGsOoycSdOTSqIaES0ughQTueOsddJ36aYVpI3SN8YSusgRFLwzDJwRFVIYDKx/iJz0sQ5kBHVgdDwg==} - dependencies: - '@nomicfoundation/ethereumjs-util': 9.0.2 - crc-32: 1.2.2 - dev: true - - /@nomicfoundation/ethereumjs-ethash@3.0.2: - resolution: {integrity: sha512-8PfoOQCcIcO9Pylq0Buijuq/O73tmMVURK0OqdjhwqcGHYC2PwhbajDh7GZ55ekB0Px197ajK3PQhpKoiI/UPg==} - engines: {node: '>=14'} - dependencies: - '@nomicfoundation/ethereumjs-block': 5.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - abstract-level: 1.0.3 - bigint-crypto-utils: 3.3.0 - ethereum-cryptography: 0.1.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: true - - /@nomicfoundation/ethereumjs-evm@2.0.2: - resolution: {integrity: sha512-rBLcUaUfANJxyOx9HIdMX6uXGin6lANCulIm/pjMgRqfiCRMZie3WKYxTSd8ZE/d+qT+zTedBF4+VHTdTSePmQ==} - engines: {node: '>=14'} - dependencies: - '@ethersproject/providers': 5.7.2 - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-tx': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - debug: 4.3.4(supports-color@8.1.1) - ethereum-cryptography: 0.1.3 - mcl-wasm: 0.7.9 - rustbn.js: 0.2.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /@nomicfoundation/ethereumjs-rlp@5.0.2: - resolution: {integrity: sha512-QwmemBc+MMsHJ1P1QvPl8R8p2aPvvVcKBbvHnQOKBpBztEo0omN0eaob6FeZS/e3y9NSe+mfu3nNFBHszqkjTA==} - engines: {node: '>=14'} - hasBin: true - dev: true - - /@nomicfoundation/ethereumjs-statemanager@2.0.2: - resolution: {integrity: sha512-dlKy5dIXLuDubx8Z74sipciZnJTRSV/uHG48RSijhgm1V7eXYFC567xgKtsKiVZB1ViTP9iFL4B6Je0xD6X2OA==} - dependencies: - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - debug: 4.3.4(supports-color@8.1.1) - ethereum-cryptography: 0.1.3 - ethers: 5.7.2 - js-sdsl: 4.4.2 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /@nomicfoundation/ethereumjs-trie@6.0.2: - resolution: {integrity: sha512-yw8vg9hBeLYk4YNg5MrSJ5H55TLOv2FSWUTROtDtTMMmDGROsAu+0tBjiNGTnKRi400M6cEzoFfa89Fc5k8NTQ==} - engines: {node: '>=14'} - dependencies: - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - '@types/readable-stream': 2.3.15 - ethereum-cryptography: 0.1.3 - readable-stream: 3.6.2 - dev: true - - /@nomicfoundation/ethereumjs-tx@5.0.2: - resolution: {integrity: sha512-T+l4/MmTp7VhJeNloMkM+lPU3YMUaXdcXgTGCf8+ZFvV9NYZTRLFekRwlG6/JMmVfIfbrW+dRRJ9A6H5Q/Z64g==} - engines: {node: '>=14'} - dependencies: - '@chainsafe/ssz': 0.9.4 - '@ethersproject/providers': 5.7.2 - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - ethereum-cryptography: 0.1.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: true - - /@nomicfoundation/ethereumjs-util@9.0.2: - resolution: {integrity: sha512-4Wu9D3LykbSBWZo8nJCnzVIYGvGCuyiYLIJa9XXNVt1q1jUzHdB+sJvx95VGCpPkCT+IbLecW6yfzy3E1bQrwQ==} - engines: {node: '>=14'} - dependencies: - '@chainsafe/ssz': 0.10.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - ethereum-cryptography: 0.1.3 - dev: true - - /@nomicfoundation/ethereumjs-vm@7.0.2: - resolution: {integrity: sha512-Bj3KZT64j54Tcwr7Qm/0jkeZXJMfdcAtRBedou+Hx0dPOSIgqaIr0vvLwP65TpHbak2DmAq+KJbW2KNtIoFwvA==} - engines: {node: '>=14'} - dependencies: - '@nomicfoundation/ethereumjs-block': 5.0.2 - '@nomicfoundation/ethereumjs-blockchain': 7.0.2 - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-evm': 2.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-statemanager': 2.0.2 - '@nomicfoundation/ethereumjs-trie': 6.0.2 - '@nomicfoundation/ethereumjs-tx': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - debug: 4.3.4(supports-color@8.1.1) - ethereum-cryptography: 0.1.3 - mcl-wasm: 0.7.9 - rustbn.js: 0.2.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1: - resolution: {integrity: sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1: - resolution: {integrity: sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1: - resolution: {integrity: sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1: - resolution: {integrity: sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1: - resolution: {integrity: sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1: - resolution: {integrity: sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1: - resolution: {integrity: sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1: - resolution: {integrity: sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1: - resolution: {integrity: sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1: - resolution: {integrity: sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@nomicfoundation/solidity-analyzer@0.1.1: - resolution: {integrity: sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==} - engines: {node: '>= 12'} - optionalDependencies: - '@nomicfoundation/solidity-analyzer-darwin-arm64': 0.1.1 - '@nomicfoundation/solidity-analyzer-darwin-x64': 0.1.1 - '@nomicfoundation/solidity-analyzer-freebsd-x64': 0.1.1 - '@nomicfoundation/solidity-analyzer-linux-arm64-gnu': 0.1.1 - '@nomicfoundation/solidity-analyzer-linux-arm64-musl': 0.1.1 - '@nomicfoundation/solidity-analyzer-linux-x64-gnu': 0.1.1 - '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.1 - '@nomicfoundation/solidity-analyzer-win32-arm64-msvc': 0.1.1 - '@nomicfoundation/solidity-analyzer-win32-ia32-msvc': 0.1.1 - '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 - dev: true - - /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.17.2): - resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} - peerDependencies: - ethers: ^5.0.0 - hardhat: ^2.0.0 - dependencies: - ethers: 5.7.2 - hardhat: 2.17.2(ts-node@10.9.1)(typescript@5.1.3) - dev: true - /@rometools/cli-darwin-arm64@12.1.3: resolution: {integrity: sha512-AmFTUDYjBuEGQp/Wwps+2cqUr+qhR7gyXAUnkL5psCuNCz3807TrUq/ecOoct5MIavGJTH6R4aaSL6+f+VlBEg==} cpu: [arm64] @@ -1447,14 +805,7 @@ packages: /@scure/base@1.1.1: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} - - /@scure/bip32@1.1.5: - resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} - dependencies: - '@noble/hashes': 1.2.0 - '@noble/secp256k1': 1.7.1 - '@scure/base': 1.1.1 - dev: true + dev: false /@scure/bip32@1.3.0: resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==} @@ -1464,13 +815,6 @@ packages: '@scure/base': 1.1.1 dev: false - /@scure/bip39@1.1.1: - resolution: {integrity: sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==} - dependencies: - '@noble/hashes': 1.2.0 - '@scure/base': 1.1.1 - dev: true - /@scure/bip39@1.2.0: resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} dependencies: @@ -1478,76 +822,6 @@ packages: '@scure/base': 1.1.1 dev: false - /@sentry/core@5.30.0: - resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==} - engines: {node: '>=6'} - dependencies: - '@sentry/hub': 5.30.0 - '@sentry/minimal': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 - dev: true - - /@sentry/hub@5.30.0: - resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==} - engines: {node: '>=6'} - dependencies: - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 - dev: true - - /@sentry/minimal@5.30.0: - resolution: {integrity: sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==} - engines: {node: '>=6'} - dependencies: - '@sentry/hub': 5.30.0 - '@sentry/types': 5.30.0 - tslib: 1.14.1 - dev: true - - /@sentry/node@5.30.0: - resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==} - engines: {node: '>=6'} - dependencies: - '@sentry/core': 5.30.0 - '@sentry/hub': 5.30.0 - '@sentry/tracing': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - cookie: 0.4.2 - https-proxy-agent: 5.0.1 - lru_map: 0.3.3 - tslib: 1.14.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@sentry/tracing@5.30.0: - resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==} - engines: {node: '>=6'} - dependencies: - '@sentry/hub': 5.30.0 - '@sentry/minimal': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 - dev: true - - /@sentry/types@5.30.0: - resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==} - engines: {node: '>=6'} - dev: true - - /@sentry/utils@5.30.0: - resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==} - engines: {node: '>=6'} - dependencies: - '@sentry/types': 5.30.0 - tslib: 1.14.1 - dev: true - /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -1570,18 +844,6 @@ packages: '@types/retry': 0.12.2 dev: true - /@types/bn.js@4.11.6: - resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} - dependencies: - '@types/node': 18.15.11 - dev: true - - /@types/bn.js@5.1.1: - resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} - dependencies: - '@types/node': 18.15.11 - dev: true - /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: @@ -1598,10 +860,6 @@ packages: ci-info: 3.8.0 dev: true - /@types/lru-cache@5.1.1: - resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==} - dev: true - /@types/minimist@1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true @@ -1617,29 +875,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true - /@types/pbkdf2@3.1.0: - resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} - dependencies: - '@types/node': 18.15.11 - dev: true - - /@types/readable-stream@2.3.15: - resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} - dependencies: - '@types/node': 18.15.11 - safe-buffer: 5.1.2 - dev: true - /@types/retry@0.12.2: resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} dev: true - /@types/secp256k1@4.0.3: - resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} - dependencies: - '@types/node': 18.15.11 - dev: true - /@types/semver@6.2.3: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true @@ -1650,6 +889,19 @@ packages: '@types/node': 18.15.11 dev: false + /@viem/anvil@0.0.6: + resolution: {integrity: sha512-OjKR/+FVwzuygXYFqP8MBal1SXG8bT2gbZwqqB0XuLw81LNBBvmE/Repm6+5kkBh4IUj0PhYdrqOsnayS14Gtg==} + dependencies: + execa: 7.2.0 + get-port: 6.1.2 + http-proxy: 1.18.1 + ws: 8.14.2 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + dev: true + /@vitest/expect@0.30.1: resolution: {integrity: sha512-c3kbEtN8XXJSeN81iDGq29bUzSjQhjES2WR3aColsS4lPGbivwLtas4DNUe0jD9gg/FYGIteqOenfU95EFituw==} dependencies: @@ -1709,19 +961,6 @@ packages: dependencies: event-target-shim: 5.0.1 - /abstract-level@1.0.3: - resolution: {integrity: sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==} - engines: {node: '>=12'} - dependencies: - buffer: 6.0.3 - catering: 2.1.1 - is-buffer: 2.0.5 - level-supports: 4.0.1 - level-transcoder: 1.0.1 - module-error: 1.0.2 - queue-microtask: 1.2.3 - dev: true - /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} @@ -1733,49 +972,11 @@ packages: hasBin: true dev: true - /adm-zip@0.4.16: - resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} - engines: {node: '>=0.3.0'} - dev: true - - /aes-js@3.0.0: - resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} - dev: true - - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - dev: true - - /aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - dev: true - - /ansi-colors@4.1.1: - resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} - engines: {node: '>=6'} - dev: true - /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} dev: true - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - dev: true - /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1822,10 +1023,6 @@ packages: sprintf-js: 1.0.3 dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} dependencies: @@ -1876,19 +1073,9 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /base-x@3.0.9: - resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} - dependencies: - safe-buffer: 5.2.1 - dev: true - /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - /bech32@1.1.4: - resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} - dev: true - /better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -1896,32 +1083,15 @@ packages: is-windows: 1.0.2 dev: true - /bigint-crypto-utils@3.3.0: - resolution: {integrity: sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg==} - engines: {node: '>=14.0.0'} - dev: true - /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} dev: true - /blakejs@1.2.1: - resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} - dev: true - /blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} dev: true - /bn.js@4.12.0: - resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - dev: true - - /bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: true - /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1948,56 +1118,6 @@ packages: wcwidth: 1.0.1 dev: true - /brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - dev: true - - /browser-level@1.0.1: - resolution: {integrity: sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==} - dependencies: - abstract-level: 1.0.3 - catering: 2.1.1 - module-error: 1.0.2 - run-parallel-limit: 1.1.0 - dev: true - - /browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: true - - /browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} - dependencies: - buffer-xor: 1.0.3 - cipher-base: 1.0.4 - create-hash: 1.2.0 - evp_bytestokey: 1.0.3 - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: true - - /bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - dependencies: - base-x: 3.0.9 - dev: true - - /bs58check@2.1.2: - resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} - dependencies: - bs58: 4.0.1 - create-hash: 1.2.0 - safe-buffer: 5.2.1 - dev: true - - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true - - /buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - dev: true - /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: @@ -2014,18 +1134,6 @@ packages: load-tsconfig: 0.2.5 dev: true - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - dependencies: - streamsearch: 1.1.0 - dev: true - - /bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - dev: true - /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -2052,21 +1160,6 @@ packages: engines: {node: '>=6'} dev: true - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true - - /case@1.6.3: - resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} - engines: {node: '>= 0.8.0'} - dev: true - - /catering@2.1.1: - resolution: {integrity: sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==} - engines: {node: '>=6'} - dev: true - /chai@4.3.7: resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} engines: {node: '>=4'} @@ -2120,39 +1213,11 @@ packages: fsevents: 2.3.3 dev: true - /ci-info@2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} - dev: true - /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} dev: true - /cipher-base@1.0.4: - resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: true - - /classic-level@1.3.0: - resolution: {integrity: sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==} - engines: {node: '>=12'} - requiresBuild: true - dependencies: - abstract-level: 1.0.3 - catering: 2.1.1 - module-error: 1.0.2 - napi-macros: 2.2.2 - node-gyp-build: 4.6.1 - dev: true - - /clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: true - /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -2161,14 +1226,6 @@ packages: wrap-ansi: 6.2.0 dev: true - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2208,14 +1265,6 @@ packages: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} dev: true - /command-exists@1.2.9: - resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} - dev: true - - /commander@3.0.2: - resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} - dev: true - /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -2239,38 +1288,6 @@ packages: well-known-symbols: 2.0.0 dev: true - /cookie@0.4.2: - resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} - engines: {node: '>= 0.6'} - dev: true - - /crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - dev: true - - /create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} - dependencies: - cipher-base: 1.0.4 - inherits: 2.0.4 - md5.js: 1.3.5 - ripemd160: 2.0.2 - sha.js: 2.4.11 - dev: true - - /create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} - dependencies: - cipher-base: 1.0.4 - create-hash: 1.2.0 - inherits: 2.0.4 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - dev: true - /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true @@ -2325,7 +1342,7 @@ packages: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: true - /debug@4.3.4(supports-color@8.1.1): + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -2335,7 +1352,6 @@ packages: optional: true dependencies: ms: 2.1.2 - supports-color: 8.1.1 dev: true /decamelize-keys@1.1.1: @@ -2351,11 +1367,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} - dev: true - /decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} dev: false @@ -2381,11 +1392,6 @@ packages: object-keys: 1.1.1 dev: true - /depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dev: true - /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -2396,11 +1402,6 @@ packages: engines: {node: '>=0.3.1'} dev: true - /diff@5.0.0: - resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} - engines: {node: '>=0.3.1'} - dev: true - /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2413,18 +1414,6 @@ packages: engines: {node: '>=12'} dev: true - /elliptic@6.5.4: - resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} - dependencies: - bn.js: 4.12.0 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - dev: true - /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -2442,11 +1431,6 @@ packages: ansi-colors: 4.1.3 dev: true - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true - /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -2587,11 +1571,6 @@ packages: engines: {node: '>=0.8.0'} dev: true - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true - /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -2603,115 +1582,18 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ethereum-cryptography@0.1.3: - resolution: {integrity: sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==} - dependencies: - '@types/pbkdf2': 3.1.0 - '@types/secp256k1': 4.0.3 - blakejs: 1.2.1 - browserify-aes: 1.2.0 - bs58check: 2.1.2 - create-hash: 1.2.0 - create-hmac: 1.1.7 - hash.js: 1.1.7 - keccak: 3.0.3 - pbkdf2: 3.1.2 - randombytes: 2.1.0 - safe-buffer: 5.2.1 - scrypt-js: 3.0.1 - secp256k1: 4.0.3 - setimmediate: 1.0.5 - dev: true - - /ethereum-cryptography@1.2.0: - resolution: {integrity: sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==} - dependencies: - '@noble/hashes': 1.2.0 - '@noble/secp256k1': 1.7.1 - '@scure/bip32': 1.1.5 - '@scure/bip39': 1.1.1 - dev: true - - /ethereumjs-abi@0.6.8: - resolution: {integrity: sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==} - dependencies: - bn.js: 4.12.0 - ethereumjs-util: 6.2.1 - dev: true - - /ethereumjs-util@6.2.1: - resolution: {integrity: sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==} - dependencies: - '@types/bn.js': 4.11.6 - bn.js: 4.12.0 - create-hash: 1.2.0 - elliptic: 6.5.4 - ethereum-cryptography: 0.1.3 - ethjs-util: 0.1.6 - rlp: 2.2.7 - dev: true - - /ethers@5.7.2: - resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} - dependencies: - '@ethersproject/abi': 5.7.0 - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/base64': 5.7.0 - '@ethersproject/basex': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/contracts': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/hdnode': 5.7.0 - '@ethersproject/json-wallets': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/networks': 5.7.1 - '@ethersproject/pbkdf2': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/providers': 5.7.2 - '@ethersproject/random': 5.7.0 - '@ethersproject/rlp': 5.7.0 - '@ethersproject/sha2': 5.7.0 - '@ethersproject/signing-key': 5.7.0 - '@ethersproject/solidity': 5.7.0 - '@ethersproject/strings': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/units': 5.7.0 - '@ethersproject/wallet': 5.7.0 - '@ethersproject/web': 5.7.1 - '@ethersproject/wordlists': 5.7.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - dev: true - - /ethjs-util@0.1.6: - resolution: {integrity: sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==} - engines: {node: '>=6.5.0', npm: '>=3'} - dependencies: - is-hex-prefixed: 1.0.0 - strip-hex-prefix: 1.0.0 - dev: true - /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + /eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + dev: true + /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - /evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} - dependencies: - md5.js: 1.3.5 - safe-buffer: 5.2.1 - dev: true - /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -2727,6 +1609,21 @@ packages: strip-final-newline: 2.0.0 dev: true + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + /extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} dev: true @@ -2781,13 +1678,6 @@ packages: to-regex-range: 5.0.1 dev: true - /find-up@2.1.0: - resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} - engines: {node: '>=4'} - dependencies: - locate-path: 2.0.0 - dev: true - /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -2811,12 +1701,7 @@ packages: pkg-dir: 4.2.0 dev: true - /flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - dev: true - - /follow-redirects@1.15.2(debug@4.3.4): + /follow-redirects@1.15.2: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: @@ -2824,8 +1709,6 @@ packages: peerDependenciesMeta: debug: optional: true - dependencies: - debug: 4.3.4(supports-color@8.1.1) dev: true /for-each@0.3.3: @@ -2834,20 +1717,6 @@ packages: is-callable: 1.2.7 dev: true - /fp-ts@1.19.3: - resolution: {integrity: sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==} - dev: true - - /fs-extra@0.30.0: - resolution: {integrity: sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 2.4.0 - klaw: 1.3.1 - path-is-absolute: 1.0.1 - rimraf: 2.7.1 - dev: true - /fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -2892,10 +1761,6 @@ packages: functions-have-names: 1.2.3 dev: true - /functional-red-black-tree@1.0.1: - resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - dev: true - /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true @@ -2918,6 +1783,11 @@ packages: has-symbols: 1.0.3 dev: true + /get-port@6.1.2: + resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -2949,17 +1819,6 @@ packages: path-is-absolute: 1.0.1 dev: true - /glob@7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - /glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -3009,74 +1868,6 @@ packages: engines: {node: '>=6'} dev: true - /hardhat@2.17.2(ts-node@10.9.1)(typescript@5.1.3): - resolution: {integrity: sha512-oUv40jBeHw0dKpbyQ+iH9cmNMziweLoTW3MnkNxJ2Gc0KGLrQR/1n4vV4xY60zn2LdmRgnwPqy3CgtY0mfwIIA==} - hasBin: true - peerDependencies: - ts-node: '*' - typescript: '*' - peerDependenciesMeta: - ts-node: - optional: true - typescript: - optional: true - dependencies: - '@ethersproject/abi': 5.7.0 - '@metamask/eth-sig-util': 4.0.1 - '@nomicfoundation/ethereumjs-block': 5.0.2 - '@nomicfoundation/ethereumjs-blockchain': 7.0.2 - '@nomicfoundation/ethereumjs-common': 4.0.2 - '@nomicfoundation/ethereumjs-evm': 2.0.2 - '@nomicfoundation/ethereumjs-rlp': 5.0.2 - '@nomicfoundation/ethereumjs-statemanager': 2.0.2 - '@nomicfoundation/ethereumjs-trie': 6.0.2 - '@nomicfoundation/ethereumjs-tx': 5.0.2 - '@nomicfoundation/ethereumjs-util': 9.0.2 - '@nomicfoundation/ethereumjs-vm': 7.0.2 - '@nomicfoundation/solidity-analyzer': 0.1.1 - '@sentry/node': 5.30.0 - '@types/bn.js': 5.1.1 - '@types/lru-cache': 5.1.1 - adm-zip: 0.4.16 - aggregate-error: 3.1.0 - ansi-escapes: 4.3.2 - chalk: 2.4.2 - chokidar: 3.5.3 - ci-info: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) - enquirer: 2.3.6 - env-paths: 2.2.1 - ethereum-cryptography: 1.2.0 - ethereumjs-abi: 0.6.8 - find-up: 2.1.0 - fp-ts: 1.19.3 - fs-extra: 7.0.1 - glob: 7.2.0 - immutable: 4.3.4 - io-ts: 1.10.4 - keccak: 3.0.3 - lodash: 4.17.21 - mnemonist: 0.38.5 - mocha: 10.2.0 - p-map: 4.0.0 - raw-body: 2.5.2 - resolve: 1.17.0 - semver: 6.3.1 - solc: 0.7.3(debug@4.3.4) - source-map-support: 0.5.21 - stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@5.1.3) - tsort: 0.0.1 - typescript: 5.1.3 - undici: 5.23.0 - uuid: 8.3.2 - ws: 7.5.9 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -3121,27 +1912,6 @@ packages: function-bind: 1.1.1 dev: true - /hash-base@3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} - dependencies: - inherits: 2.0.4 - readable-stream: 3.6.2 - safe-buffer: 5.2.1 - dev: true - - /hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - dev: true - - /he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - dev: true - /help-me@4.2.0: resolution: {integrity: sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==} dependencies: @@ -3149,37 +1919,19 @@ packages: readable-stream: 3.6.2 dev: true - /hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - dev: true - /hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true - /http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - dev: true - - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} + /http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} dependencies: - agent-base: 6.0.2 - debug: 4.3.4(supports-color@8.1.1) + eventemitter3: 4.0.7 + follow-redirects: 1.15.2 + requires-port: 1.0.0 transitivePeerDependencies: - - supports-color + - debug dev: true /human-id@1.0.2: @@ -3191,6 +1943,11 @@ packages: engines: {node: '>=10.17.0'} dev: true + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -3206,10 +1963,6 @@ packages: engines: {node: '>= 4'} dev: true - /immutable@4.3.4: - resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} - dev: true - /indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -3235,12 +1988,6 @@ packages: side-channel: 1.0.4 dev: true - /io-ts@1.10.4: - resolution: {integrity: sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==} - dependencies: - fp-ts: 1.19.3 - dev: true - /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -3274,11 +2021,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-buffer@2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - dev: true - /is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -3321,11 +2063,6 @@ packages: is-extglob: 2.1.1 dev: true - /is-hex-prefixed@1.0.0: - resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} - engines: {node: '>=6.5.0', npm: '>=3'} - dev: true - /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -3348,11 +2085,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - dev: true - /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -3372,6 +2104,11 @@ packages: engines: {node: '>=8'} dev: true + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -3404,11 +2141,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true - /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: @@ -3437,14 +2169,6 @@ packages: engines: {node: '>=10'} dev: true - /js-sdsl@4.4.2: - resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} - dev: true - - /js-sha3@0.8.0: - resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} - dev: true - /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} engines: {node: '>= 0.8'} @@ -3462,13 +2186,6 @@ packages: esprima: 4.0.1 dev: true - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -3477,63 +2194,20 @@ packages: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true - /jsonfile@2.4.0: - resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} - optionalDependencies: - graceful-fs: 4.2.11 - dev: true - /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 dev: true - /keccak@3.0.3: - resolution: {integrity: sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==} - engines: {node: '>=10.0.0'} - requiresBuild: true - dependencies: - node-addon-api: 2.0.2 - node-gyp-build: 4.6.1 - readable-stream: 3.6.2 - dev: true - /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} dev: true - /klaw@1.3.1: - resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} - optionalDependencies: - graceful-fs: 4.2.11 - dev: true - /kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - dev: true - - /level-supports@4.0.1: - resolution: {integrity: sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==} - engines: {node: '>=12'} - dev: true - - /level-transcoder@1.0.1: - resolution: {integrity: sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==} - engines: {node: '>=12'} - dependencies: - buffer: 6.0.3 - module-error: 1.0.2 - dev: true - - /level@8.0.0: - resolution: {integrity: sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==} - engines: {node: '>=12'} - dependencies: - browser-level: 1.0.1 - classic-level: 1.3.0 + engines: {node: '>=6'} dev: true /lilconfig@2.1.0: @@ -3565,14 +2239,6 @@ packages: engines: {node: '>=14'} dev: true - /locate-path@2.0.0: - resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} - engines: {node: '>=4'} - dependencies: - p-locate: 2.0.0 - path-exists: 3.0.0 - dev: true - /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -3599,14 +2265,6 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - dev: true - /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: @@ -3620,12 +2278,6 @@ packages: yallist: 2.1.2 dev: true - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - dev: true - /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -3633,10 +2285,6 @@ packages: yallist: 4.0.0 dev: true - /lru_map@0.3.3: - resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} - dev: true - /magic-string@0.30.1: resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} engines: {node: '>=12'} @@ -3658,11 +2306,6 @@ packages: engines: {node: '>=8'} dev: true - /mcl-wasm@0.7.9: - resolution: {integrity: sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==} - engines: {node: '>=8.9.0'} - dev: true - /md5-hex@3.0.1: resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} engines: {node: '>=8'} @@ -3670,28 +2313,6 @@ packages: blueimp-md5: 2.19.0 dev: true - /md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: true - - /memory-level@1.0.0: - resolution: {integrity: sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==} - engines: {node: '>=12'} - dependencies: - abstract-level: 1.0.3 - functional-red-black-tree: 1.0.1 - module-error: 1.0.2 - dev: true - - /memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - dev: true - /meow@6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} @@ -3731,32 +2352,22 @@ packages: engines: {node: '>=6'} dev: true + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} dev: true - /minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: true - - /minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - dev: true - /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true - /minimatch@5.0.1: - resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -3791,53 +2402,10 @@ packages: ufo: 1.1.2 dev: true - /mnemonist@0.38.5: - resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} - dependencies: - obliterator: 2.0.4 - dev: true - - /mocha@10.2.0: - resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} - engines: {node: '>= 14.0.0'} - hasBin: true - dependencies: - ansi-colors: 4.1.1 - browser-stdout: 1.3.1 - chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) - diff: 5.0.0 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 7.2.0 - he: 1.2.0 - js-yaml: 4.1.0 - log-symbols: 4.1.0 - minimatch: 5.0.1 - ms: 2.1.3 - nanoid: 3.3.3 - serialize-javascript: 6.0.0 - strip-json-comments: 3.1.1 - supports-color: 8.1.1 - workerpool: 6.2.1 - yargs: 16.2.0 - yargs-parser: 20.2.4 - yargs-unparser: 2.0.0 - dev: true - - /module-error@1.0.2: - resolution: {integrity: sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==} - engines: {node: '>=10'} - dev: true - /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true - /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -3846,31 +2414,12 @@ packages: thenify-all: 1.6.0 dev: true - /nanoid@3.3.3: - resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true - /napi-macros@2.2.2: - resolution: {integrity: sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==} - dev: true - - /node-addon-api@2.0.2: - resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} - dev: true - - /node-gyp-build@4.6.1: - resolution: {integrity: sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==} - hasBin: true - dev: true - /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -3892,6 +2441,13 @@ packages: path-key: 3.1.1 dev: true + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3916,10 +2472,6 @@ packages: object-keys: 1.1.1 dev: true - /obliterator@2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - dev: true - /on-exit-leak-free@2.1.0: resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==} @@ -3936,6 +2488,13 @@ packages: mimic-fn: 2.1.0 dev: true + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -3952,13 +2511,6 @@ packages: p-map: 2.1.0 dev: true - /p-limit@1.3.0: - resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} - engines: {node: '>=4'} - dependencies: - p-try: 1.0.0 - dev: true - /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3980,13 +2532,6 @@ packages: yocto-queue: 1.0.0 dev: true - /p-locate@2.0.0: - resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} - engines: {node: '>=4'} - dependencies: - p-limit: 1.3.0 - dev: true - /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -4006,18 +2551,6 @@ packages: engines: {node: '>=6'} dev: true - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - dependencies: - aggregate-error: 3.1.0 - dev: true - - /p-try@1.0.0: - resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} - engines: {node: '>=4'} - dev: true - /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -4033,11 +2566,6 @@ packages: lines-and-columns: 1.2.4 dev: true - /path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - dev: true - /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4053,6 +2581,11 @@ packages: engines: {node: '>=8'} dev: true + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true @@ -4070,17 +2603,6 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true - /pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} - dependencies: - create-hash: 1.2.0 - create-hmac: 1.1.7 - ripemd160: 2.0.2 - safe-buffer: 5.2.1 - sha.js: 2.4.11 - dev: true - /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} dev: true @@ -4250,22 +2772,6 @@ packages: engines: {node: '>=8'} dev: true - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - dev: true - - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - dev: true - /react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true @@ -4355,26 +2861,19 @@ packages: engines: {node: '>=0.10.0'} dev: true - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - dev: true - /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: true + /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} dev: true - /resolve@1.17.0: - resolution: {integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==} - dependencies: - path-parse: 1.0.7 - dev: true - /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -4394,27 +2893,6 @@ packages: engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.0 - dev: true - - /ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - dependencies: - hash-base: 3.1.0 - inherits: 2.0.4 - dev: true - - /rlp@2.2.7: - resolution: {integrity: sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==} - hasBin: true - dependencies: - bn.js: 5.2.1 - dev: true - /rollup@3.20.2: resolution: {integrity: sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -4445,26 +2923,12 @@ packages: '@rometools/cli-win32-x64': 12.1.3 dev: true - /run-parallel-limit@1.1.0: - resolution: {integrity: sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==} - dependencies: - queue-microtask: 1.2.3 - dev: true - /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /rustbn.js@0.2.0: - resolution: {integrity: sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==} - dev: true - - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true - /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true @@ -4486,20 +2950,6 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true - /scrypt-js@3.0.1: - resolution: {integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==} - dev: true - - /secp256k1@4.0.3: - resolution: {integrity: sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==} - engines: {node: '>=10.0.0'} - requiresBuild: true - dependencies: - elliptic: 6.5.4 - node-addon-api: 2.0.2 - node-gyp-build: 4.6.1 - dev: true - /secure-json-parse@2.7.0: resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} dev: true @@ -4509,11 +2959,6 @@ packages: hasBin: true dev: true - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - dev: true - /semver@7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} @@ -4522,32 +2967,10 @@ packages: lru-cache: 6.0.0 dev: true - /serialize-javascript@6.0.0: - resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} - dependencies: - randombytes: 2.1.0 - dev: true - /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true - /setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: true - - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: true - - /sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: true - /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -4606,24 +3029,6 @@ packages: yargs: 15.4.1 dev: true - /solc@0.7.3(debug@4.3.4): - resolution: {integrity: sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==} - engines: {node: '>=8.0.0'} - hasBin: true - dependencies: - command-exists: 1.2.9 - commander: 3.0.2 - follow-redirects: 1.15.2(debug@4.3.4) - fs-extra: 0.30.0 - js-sha3: 0.8.0 - memorystream: 0.3.1 - require-from-string: 2.0.2 - semver: 5.7.1 - tmp: 0.0.33 - transitivePeerDependencies: - - debug - dev: true - /sonic-boom@3.3.0: resolution: {integrity: sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==} dependencies: @@ -4634,13 +3039,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -4694,18 +3092,6 @@ packages: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true - /stacktrace-parser@0.1.10: - resolution: {integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==} - engines: {node: '>=6'} - dependencies: - type-fest: 0.7.1 - dev: true - - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - dev: true - /std-env@3.3.3: resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} dev: true @@ -4716,11 +3102,6 @@ packages: mixme: 0.5.9 dev: true - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: true - /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4778,11 +3159,9 @@ packages: engines: {node: '>=6'} dev: true - /strip-hex-prefix@1.0.0: - resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} - engines: {node: '>=6.5.0', npm: '>=3'} - dependencies: - is-hex-prefixed: 1.0.0 + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} dev: true /strip-indent@3.0.0: @@ -4830,13 +3209,6 @@ packages: has-flag: 4.0.0 dev: true - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -4899,11 +3271,6 @@ packages: is-number: 7.0.0 dev: true - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - dev: true - /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} dependencies: @@ -4955,14 +3322,6 @@ packages: yn: 3.1.1 dev: true - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true - - /tsort@0.0.1: - resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} - dev: true - /tsup@6.7.0(ts-node@10.9.1)(typescript@5.1.3): resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} engines: {node: '>=14.18'} @@ -4982,7 +3341,7 @@ packages: bundle-require: 4.0.1(esbuild@0.17.15) cac: 6.7.14 chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 esbuild: 0.17.15 execa: 5.1.1 globby: 11.1.0 @@ -5013,14 +3372,6 @@ packages: yargs: 17.7.1 dev: true - /tweetnacl-util@0.15.1: - resolution: {integrity: sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==} - dev: true - - /tweetnacl@1.0.3: - resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} - dev: true - /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -5031,21 +3382,11 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true - /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} dev: true - /type-fest@0.7.1: - resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} - engines: {node: '>=8'} - dev: true - /type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -5077,32 +3418,15 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /undici@5.23.0: - resolution: {integrity: sha512-1D7w+fvRsqlQ9GscLBwcAJinqcZGHUKjbOmXdlE/v8BvEGXjeWAax+341q44EuTcHXXnfyKNbKRq4Lg7OzhMmg==} - engines: {node: '>=14.0'} - dependencies: - busboy: 1.6.0 - dev: true - /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} dev: true - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - dev: true - /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - dev: true - /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true @@ -5144,7 +3468,7 @@ packages: hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 mlly: 1.4.0 pathe: 1.1.1 picocolors: 1.0.0 @@ -5240,7 +3564,7 @@ packages: cac: 6.7.14 chai: 4.3.7 concordance: 5.0.4 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 local-pkg: 0.4.3 magic-string: 0.30.1 pathe: 1.1.1 @@ -5344,10 +3668,6 @@ packages: stackback: 0.0.2 dev: true - /workerpool@6.2.1: - resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} - dev: true - /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -5370,34 +3690,21 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /ws@7.4.6: - resolution: {integrity: sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true - - /ws@7.5.9: - resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} - engines: {node: '>=8.3.0'} + /ws@8.12.0: + resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} + engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true - dev: true + dev: false - /ws@8.12.0: - resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} + /ws@8.14.2: + resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -5407,7 +3714,7 @@ packages: optional: true utf-8-validate: optional: true - dev: false + dev: true /y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -5422,10 +3729,6 @@ packages: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: true - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true - /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true @@ -5443,26 +3746,11 @@ packages: decamelize: 1.2.0 dev: true - /yargs-parser@20.2.4: - resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} - engines: {node: '>=10'} - dev: true - /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} dev: true - /yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} - dependencies: - camelcase: 6.3.0 - decamelize: 4.0.0 - flat: 5.0.2 - is-plain-obj: 2.1.0 - dev: true - /yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} @@ -5480,19 +3768,6 @@ packages: yargs-parser: 18.1.3 dev: true - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.4 - dev: true - /yargs@17.7.1: resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} engines: {node: '>=12'} diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts new file mode 100644 index 00000000..b7e1cb58 --- /dev/null +++ b/test/anvil/anvil-global-setup.ts @@ -0,0 +1,33 @@ +import { CreateAnvilOptions, createAnvil } from '@viem/anvil' +import { sleep } from '../lib/utils/promises' + +export let forkUrl: string +if (process.env.VITE_ANVIL_FORK_RPC_URL) { + forkUrl = process.env.VITE_ANVIL_FORK_RPC_URL +} else { + forkUrl = 'https://cloudflare-eth.com' + console.warn(`\`VITE_ANVIL_FORK_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`) +} + +const port = 8545 + +const anvilOptions: CreateAnvilOptions = { + forkUrl, + port, + forkBlockNumber: 18043296n, +} + +// https://www.npmjs.com/package/@viem/anvil +export const anvil = createAnvil(anvilOptions) + +export default async function startAnvil() { + if (process.env.VITE_SKIP_GLOBAL_SETUP === 'true') { + console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: +anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8555 --fork-block-number=17878719 +`) + await sleep(5000) + return + } + console.log(`🛠️ Starting anvil`, anvilOptions) + return await anvil.start() +} diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 7e0980d1..22ce9ebd 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -266,10 +266,10 @@ export const forkSetup = async ( blockNumber?: bigint, isVyperMapping: boolean[] = Array(tokens.length).fill(false), ): Promise => { - await client.reset({ - blockNumber, - jsonRpcUrl, - }); + // await client.reset({ + // blockNumber, + // jsonRpcUrl, + // }); await client.impersonateAccount({ address: accountAddress }); diff --git a/test/lib/utils/promises.ts b/test/lib/utils/promises.ts new file mode 100644 index 00000000..39068174 --- /dev/null +++ b/test/lib/utils/promises.ts @@ -0,0 +1,3 @@ +export function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)) +} diff --git a/vitest.config.ts b/vitest.config.ts index f49298d4..2157a9c9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,5 +4,6 @@ export default defineConfig({ test: { testTimeout: 10_000, hookTimeout: 20_000, + globalSetup: ['./test/anvil/anvil-global-setup.ts'], }, }); From 9d6f0acef8c78fbb31c205a080c702bb240e8733 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 18 Oct 2023 11:35:54 +0200 Subject: [PATCH 099/199] mend --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f59ba444..833ffd8e 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "test": "vitest dev", "test:ci": "vitest run", "changeset": "changeset", - "changeset:release": "pnpm build && changeset publish", + "changeset:release": "pnpm build && changeset publish" }, "dependencies": { "async-retry": "^1.3.3", From 3119a497c864e9c2e773041f525a1e6ab8b2f0bc Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 18 Oct 2023 11:41:25 +0200 Subject: [PATCH 100/199] Fix lint --- .github/workflows/checks.yml | 2 -- test/anvil/anvil-global-setup.ts | 42 +++++++++++++++++--------------- test/lib/utils/helper.ts | 7 ------ 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c4850e15..42b76fa6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -43,8 +43,6 @@ jobs: uses: ./.github/actions/setup - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 - - name: Run Anvil Node - run: anvil --fork-url ${{ secrets.ETHEREUM_RPC_URL }} & - name: Test run: pnpm test:ci env: diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index b7e1cb58..1066b4b0 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -1,33 +1,35 @@ -import { CreateAnvilOptions, createAnvil } from '@viem/anvil' -import { sleep } from '../lib/utils/promises' +import { CreateAnvilOptions, createAnvil } from '@viem/anvil'; +import { sleep } from '../lib/utils/promises'; -export let forkUrl: string +export let forkUrl: string; if (process.env.VITE_ANVIL_FORK_RPC_URL) { - forkUrl = process.env.VITE_ANVIL_FORK_RPC_URL + forkUrl = process.env.VITE_ANVIL_FORK_RPC_URL; } else { - forkUrl = 'https://cloudflare-eth.com' - console.warn(`\`VITE_ANVIL_FORK_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`) + forkUrl = 'https://cloudflare-eth.com'; + console.warn( + `\`VITE_ANVIL_FORK_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`, + ); } -const port = 8545 +const port = 8545; const anvilOptions: CreateAnvilOptions = { - forkUrl, - port, - forkBlockNumber: 18043296n, -} + forkUrl, + port, + forkBlockNumber: 18043296n, +}; // https://www.npmjs.com/package/@viem/anvil -export const anvil = createAnvil(anvilOptions) +export const anvil = createAnvil(anvilOptions); export default async function startAnvil() { - if (process.env.VITE_SKIP_GLOBAL_SETUP === 'true') { - console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: + if (process.env.VITE_SKIP_GLOBAL_SETUP === 'true') { + console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8555 --fork-block-number=17878719 -`) - await sleep(5000) - return - } - console.log(`🛠️ Starting anvil`, anvilOptions) - return await anvil.start() +`); + await sleep(5000); + return; + } + console.log('🛠️ Starting anvil', anvilOptions); + return await anvil.start(); } diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 22ce9ebd..5bdd6db5 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -262,15 +262,8 @@ export const forkSetup = async ( tokens: Address[], slots: number[] | undefined, balances: bigint[], - jsonRpcUrl: string, - blockNumber?: bigint, isVyperMapping: boolean[] = Array(tokens.length).fill(false), ): Promise => { - // await client.reset({ - // blockNumber, - // jsonRpcUrl, - // }); - await client.impersonateAccount({ address: accountAddress }); let _slots: number[]; From 457c50cacbf4189d06453aa32a5fabda61e12f87 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 18 Oct 2023 11:58:26 +0200 Subject: [PATCH 101/199] Refactor .env vars --- test/anvil/anvil-global-setup.ts | 10 ++++++---- tsconfig.json | 3 ++- vitest.config.ts | 14 ++++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 1066b4b0..c0299013 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -1,13 +1,15 @@ +/// + import { CreateAnvilOptions, createAnvil } from '@viem/anvil'; import { sleep } from '../lib/utils/promises'; export let forkUrl: string; -if (process.env.VITE_ANVIL_FORK_RPC_URL) { - forkUrl = process.env.VITE_ANVIL_FORK_RPC_URL; +if (import.meta.env.VITE_ETHEREUM_RPC_URL) { + forkUrl = import.meta.env.VITE_ETHEREUM_RPC_URL; } else { forkUrl = 'https://cloudflare-eth.com'; console.warn( - `\`VITE_ANVIL_FORK_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`, + `\`VITE_ETHEREUM_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`, ); } @@ -23,7 +25,7 @@ const anvilOptions: CreateAnvilOptions = { export const anvil = createAnvil(anvilOptions); export default async function startAnvil() { - if (process.env.VITE_SKIP_GLOBAL_SETUP === 'true') { + if (import.meta.env.VITE_SKIP_GLOBAL_SETUP === 'true') { console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8555 --fork-block-number=17878719 `); diff --git a/tsconfig.json b/tsconfig.json index 21a749b5..92be018f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "noImplicitReturns": true, "moduleResolution": "node", "esModuleInterop": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "types": ["vite/client"] } } diff --git a/vitest.config.ts b/vitest.config.ts index 2157a9c9..65bf4f71 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,9 +1,11 @@ import { defineConfig } from 'vitest/config'; -export default defineConfig({ - test: { - testTimeout: 10_000, - hookTimeout: 20_000, - globalSetup: ['./test/anvil/anvil-global-setup.ts'], - }, +export default defineConfig(() => { + return { + test: { + testTimeout: 10_000, + hookTimeout: 20_000, + globalSetup: ['./test/anvil/anvil-global-setup.ts'], + }, + }; }); From 1a7d990355beefaad521b3b33e87d4ac10884c08 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 18 Oct 2023 12:04:38 +0200 Subject: [PATCH 102/199] Fix VITE envs in checks.yml --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 42b76fa6..bf53fb5e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -46,8 +46,8 @@ jobs: - name: Test run: pnpm test:ci env: - ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} - POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} + VITE_ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} + VITE_POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} build: name: Build needs: install From 3c232b0bba2e71e71799f00d33ac313aa17637e5 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 18 Oct 2023 15:45:24 +0100 Subject: [PATCH 103/199] test: Fix undefined tests. --- test/weightedExit.integration.test.ts | 9 +++++---- test/weightedJoin.integration.test.ts | 16 ++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 50cad9d7..5a097162 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -113,7 +113,7 @@ describe('weighted exit test', () => { expect(queryResult.bptIn.amount).to.eq(bptIn.amount); // We only expect single asset to have a value for exit - expect(queryResult.tokenOutIndex).to.be.toBeDefined; + expect(queryResult.tokenOutIndex !== undefined).toEqual(true); queryResult.amountsOut.forEach((a, i) => { if (i === queryResult.tokenOutIndex) expect(a.amount > 0n).to.be.true; @@ -146,8 +146,8 @@ describe('weighted exit test', () => { // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + expect(queryResult.tokenOutIndex === undefined).toEqual(true); // We expect all assets to have a value for exit - expect(queryResult.tokenOutIndex).to.be.undefined; queryResult.amountsOut.forEach((a) => { expect(a.amount > 0n).to.be.true; }); @@ -184,8 +184,8 @@ describe('weighted exit test', () => { // We expect a BPT input amount > 0 expect(queryResult.bptIn.amount > 0n).to.be.true; + expect(queryResult.tokenOutIndex === undefined).toEqual(true); // We expect assets to have same amount out as user defined - expect(queryResult.tokenOutIndex).to.be.undefined; queryResult.amountsOut.forEach((a, i) => { expect(a.amount).to.eq(amountsOut[i].amount); }); @@ -219,8 +219,9 @@ describe('weighted exit test', () => { // Query should use correct BPT amount expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + expect(queryResult.tokenOutIndex === undefined).toEqual(true); + // We expect all assets to have a value for exit - expect(queryResult.tokenOutIndex).to.be.undefined; queryResult.amountsOut.forEach((a) => { expect(a.amount > 0n).to.be.true; }); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index afd6497a..2812f879 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -128,10 +128,10 @@ describe('weighted join test', () => { // Query should use same amountsIn as user sets expect(queryResult.amountsIn).to.deep.eq(amountsIn); - expect(queryResult.tokenInIndex).to.be.undefined; + expect(queryResult.tokenInIndex === undefined).toEqual(true); // Should be no native value - expect(value).toBeUndefined; + expect(value === undefined).toEqual(true); // Expect some bpt amount expect(queryResult.bptOut.amount > 0n).to.be.true; @@ -174,7 +174,8 @@ describe('weighted join test', () => { expect(queryResult.amountsIn.map((a) => a.amount)).to.deep.eq( amountsIn.map((a) => a.amount), ); - expect(queryResult.tokenInIndex).to.be.undefined; + expect(queryResult.tokenInIndex === undefined).toEqual(true); + // Should have native value equal to input amount expect(value).eq(amountsIn[0].amount); @@ -213,7 +214,7 @@ describe('weighted join test', () => { expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); // We only expect single asset to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; + expect(queryResult.tokenInIndex !== undefined).toEqual(true); queryResult.amountsIn.forEach((a, i) => { if (i === queryResult.tokenInIndex) expect(a.amount > 0n).to.be.true; @@ -221,7 +222,7 @@ describe('weighted join test', () => { }); // Should be no native value - expect(value).toBeUndefined; + expect(value === undefined).toEqual(true); // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => @@ -251,15 +252,14 @@ describe('weighted join test', () => { // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + expect(queryResult.tokenInIndex === undefined).toEqual(true); // Expect all assets to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; queryResult.amountsIn.forEach((a) => { expect(a.amount > 0n).to.be.true; }); - expect(queryResult.tokenInIndex).toBeUndefined; // Should be no native value - expect(value).toBeUndefined; + expect(value === undefined).toEqual(true); // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => From b5838d8fc52ebc00847bd0c06a00b2c5eebfa618 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 18 Oct 2023 19:14:58 -0300 Subject: [PATCH 104/199] Refactoring the types; Adding comment to PHANTOM_STABLE pool type; --- .../composable-stable/composableStableExit.ts | 343 +++++++++--------- src/entities/exit/poolExit.ts | 87 ++--- src/entities/exit/types.ts | 99 ++--- src/entities/exit/weighted/weightedExit.ts | 333 +++++++++-------- 4 files changed, 431 insertions(+), 431 deletions(-) diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index b3a6b2e1..080c88af 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -1,195 +1,194 @@ -import { encodeFunctionData } from 'viem'; -import { Token } from '../../token'; -import { TokenAmount } from '../../tokenAmount'; -import { Address } from '../../../types'; +import { encodeFunctionData } from "viem"; +import { Token } from "../../token"; +import { TokenAmount } from "../../tokenAmount"; +import { Address } from "../../../types"; import { - BALANCER_VAULT, - MAX_UINT256, - ZERO_ADDRESS, -} from '../../../utils/constants'; -import { vaultAbi } from '../../../abi'; -import { parseExitArgs } from '../../utils/parseExitArgs'; + BALANCER_VAULT, + MAX_UINT256, + ZERO_ADDRESS, +} from "../../../utils/constants"; +import { vaultAbi } from "../../../abi"; +import { parseExitArgs } from "../../utils/parseExitArgs"; import { - BaseExit, - ExitBuildOutput, - ExitCallInput, - ExitInput, - ExitKind, - ExitQueryResult, -} from '../types'; -import { AmountsExit, PoolState } from '../../types'; -import { doQueryExit } from '../../utils/doQueryExit'; + BaseExit, + ComposableStableExitCall, + ExitBuildOutput, + ExitInput, + ExitKind, + ExitQueryResult, +} from "../types"; +import { AmountsExit, PoolState } from "../../types"; +import { doQueryExit } from "../../utils/doQueryExit"; import { ComposableStableEncoder } from "../../encoders/composableStable"; export class ComposableStableExit implements BaseExit { - public async query( - input: ExitInput, - poolState: PoolState, - ): Promise { - const bptIndex = poolState.tokens.findIndex((t)=> t.address === poolState.address); - const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); - const amountsWithoutBpt = { - ...amounts, - minAmountsOut: [...amounts.minAmountsOut.slice(0, bptIndex), ...amounts.minAmountsOut.slice(bptIndex+1)] - }; - const userData = this.encodeUserData(input.kind, amountsWithoutBpt); + public async query( + input: ExitInput, + poolState: PoolState + ): Promise { + const bptIndex = poolState.tokens.findIndex( + (t) => t.address === poolState.address + ); + const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); + const amountsWithoutBpt = { + ...amounts, + minAmountsOut: [ + ...amounts.minAmountsOut.slice(0, bptIndex), + ...amounts.minAmountsOut.slice(bptIndex + 1), + ], + }; + const userData = this.encodeUserData(input.kind, amountsWithoutBpt); - // tokensOut will have zero address if exit with native asset - const { args, tokensOut } = parseExitArgs({ - chainId: input.chainId, - exitWithNativeAsset: !!input.exitWithNativeAsset, - poolId: poolState.id, - sortedTokens: poolState.tokens, - sender: ZERO_ADDRESS, - recipient: ZERO_ADDRESS, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - const queryResult = await doQueryExit( - input.rpcUrl, - input.chainId, - args, - ); - const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + // tokensOut will have zero address if exit with native asset + const { args, tokensOut } = parseExitArgs({ + chainId: input.chainId, + exitWithNativeAsset: !!input.exitWithNativeAsset, + poolId: poolState.id, + sortedTokens: poolState.tokens, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + const queryResult = await doQueryExit(input.rpcUrl, input.chainId, args); + const bpt = new Token(input.chainId, poolState.address, 18); + const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); - const amountsOut = queryResult.amountsOut.map((a, i) => - TokenAmount.fromRawAmount(tokensOut[i], a), - ); + const amountsOut = queryResult.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(tokensOut[i], a) + ); + return { + poolType: poolState.type, + exitKind: input.kind, + id: poolState.id, + bptIn, + amountsOut, + tokenOutIndex: amounts.tokenOutIndex, + bptIndex, + }; + } + + private getAmountsQuery( + tokens: Token[], + input: ExitInput, + bptIndex?: number + ): AmountsExit { + switch (input.kind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: tokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t))?.amount ?? 0n + ), + tokenOutIndex: undefined, + maxBptAmountIn: MAX_UINT256, + }; + case ExitKind.SINGLE_ASSET: return { - poolType: poolState.type, - exitKind: input.kind, - id: poolState.id, - bptIn, - amountsOut, - tokenOutIndex: amounts.tokenOutIndex, - bptIndex + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: tokens + .filter((_, index) => index !== bptIndex) + .findIndex((t) => t.isSameAddress(input.tokenOut)), + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: undefined, + maxBptAmountIn: input.bptIn.amount, }; } + } - private getAmountsQuery(tokens: Token[], - input: ExitInput, - bptIndex?:number): AmountsExit { - switch (input.kind) { - case ExitKind.UNBALANCED: - return { - minAmountsOut: tokens.map( - (t) => - input.amountsOut.find((a) => a.token.isEqual(t)) - ?.amount ?? 0n, - ), - tokenOutIndex: undefined, - maxBptAmountIn: MAX_UINT256, - }; - case ExitKind.SINGLE_ASSET: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: tokens - .filter((_, index)=> index!==bptIndex) - .findIndex((t) => - t.isSameAddress(input.tokenOut), - ), - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: undefined, - maxBptAmountIn: input.bptIn.amount, - }; - } - } + public buildCall(input: ComposableStableExitCall): ExitBuildOutput { + const amounts = this.getAmountsCall(input); + const amountsWithoutBpt = { + ...amounts, + minAmountsOut: [ + ...amounts.minAmountsOut.slice(0, input.bptIndex), + ...amounts.minAmountsOut.slice(input.bptIndex + 1), + ], + }; + const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); - public buildCall(input: ExitCallInput): ExitBuildOutput { - const amounts = this.getAmountsCall(input); - if(input.bptIndex === undefined){ - throw new Error("bptIndex is necessary"); - } - const amountsWithoutBpt = { - ...amounts, - minAmountsOut: [...amounts.minAmountsOut.slice(0, input.bptIndex), ...amounts.minAmountsOut.slice(input.bptIndex+1)] - }; - const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); + const { args } = parseExitArgs({ + poolId: input.id, + sortedTokens: input.amountsOut.map((a) => a.token), + sender: input.sender, + recipient: input.recipient, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: "exitPool", + args, + }); - const { args } = parseExitArgs({ - poolId: input.id, - sortedTokens: input.amountsOut.map((a) => a.token), - sender: input.sender, - recipient: input.recipient, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - const call = encodeFunctionData({ - abi: vaultAbi, - functionName: 'exitPool', - args, - }); + return { + call, + to: BALANCER_VAULT, + value: 0n, + maxBptIn: amounts.maxBptAmountIn, + minAmountsOut: amounts.minAmountsOut, + }; + } + private getAmountsCall(input: ComposableStableExitCall): AmountsExit { + switch (input.exitKind) { + case ExitKind.UNBALANCED: return { - call, - to: BALANCER_VAULT, - value: 0n, - maxBptIn: amounts.maxBptAmountIn, - minAmountsOut: amounts.minAmountsOut, + minAmountsOut: input.amountsOut.map((a) => a.amount), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - } - - private getAmountsCall(input: ExitCallInput): AmountsExit { - switch (input.exitKind) { - case ExitKind.UNBALANCED: - return { - minAmountsOut: input.amountsOut.map((a) => a.amount), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), - }; - case ExitKind.SINGLE_ASSET: - if (input.tokenOutIndex === undefined) { - throw new Error( - 'tokenOutIndex must be defined for SINGLE_ASSET exit', - ); - } - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount), - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount), - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - default: - throw Error('Unsupported Exit Type'); + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + "tokenOutIndex must be defined for SINGLE_ASSET exit" + ); } + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount) + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount) + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + default: + throw Error("Unsupported Exit Type"); } + } - private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { - switch (kind) { - case ExitKind.UNBALANCED: - return ComposableStableEncoder.exitUnbalanced( - amounts.minAmountsOut, - amounts.maxBptAmountIn, - ); - case ExitKind.SINGLE_ASSET: - if (amounts.tokenOutIndex === undefined) - throw Error('No Index'); + private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + switch (kind) { + case ExitKind.UNBALANCED: + return ComposableStableEncoder.exitUnbalanced( + amounts.minAmountsOut, + amounts.maxBptAmountIn + ); + case ExitKind.SINGLE_ASSET: + if (amounts.tokenOutIndex === undefined) throw Error("No Index"); - return ComposableStableEncoder.exitSingleAsset( - amounts.maxBptAmountIn, - amounts.tokenOutIndex, - ); - case ExitKind.PROPORTIONAL: - return ComposableStableEncoder.exitProportional(amounts.maxBptAmountIn); - default: - throw Error('Unsupported Exit Type'); - } + return ComposableStableEncoder.exitSingleAsset( + amounts.maxBptAmountIn, + amounts.tokenOutIndex + ); + case ExitKind.PROPORTIONAL: + return ComposableStableEncoder.exitProportional(amounts.maxBptAmountIn); + default: + throw Error("Unsupported Exit Type"); } + } } diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 47d12aeb..7555e446 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -1,54 +1,55 @@ 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'; + BaseExit, + ExitBuildOutput, + ExitCall, + ExitConfig, + ExitInput, + ExitQueryResult, +} from "./types"; +import { WeightedExit } from "./weighted/weightedExit"; +import { PoolStateInput } from "../types"; +import { validateInputs } from "./weighted/validateInputs"; +import { getSortedTokens } from "../utils/getSortedTokens"; import { ComposableStableExit } from "./composable-stable/composableStableExit"; export class PoolExit { - private readonly poolExits: Record = {}; - - constructor(config?: ExitConfig) { - const { customPoolExits } = config || {}; - this.poolExits = { - Weighted: new WeightedExit(), - PHANTOM_STABLE: new ComposableStableExit(), - // custom pool Exits take precedence over base Exits - ...customPoolExits, - }; + private readonly poolExits: Record = {}; + + constructor(config?: ExitConfig) { + const { customPoolExits } = config || {}; + this.poolExits = { + Weighted: new WeightedExit(), + // PHANTOM_STABLE === ComposableStables in API + PHANTOM_STABLE: new ComposableStableExit(), + // custom pool Exits take precedence over base Exits + ...customPoolExits, + }; + } + + public getExit(poolType: string): BaseExit { + if (!this.poolExits[poolType]) { + throw new Error("Unsupported pool type"); } - public getExit(poolType: string): BaseExit { - if (!this.poolExits[poolType]) { - throw new Error('Unsupported pool type'); - } + return this.poolExits[poolType]; + } - return this.poolExits[poolType]; - } - - public async query( - input: ExitInput, - poolState: PoolStateInput, - ): Promise { - validateInputs(input, poolState); + public async query( + input: ExitInput, + poolState: PoolStateInput + ): Promise { + validateInputs(input, poolState); - const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); - const mappedPoolState = { - ...poolState, - tokens: sortedTokens, - }; + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); + const mappedPoolState = { + ...poolState, + tokens: sortedTokens, + }; - return this.getExit(poolState.type).query(input, mappedPoolState); - } + return this.getExit(poolState.type).query(input, mappedPoolState); + } - public buildCall(input: ExitCallInput): ExitBuildOutput { - return this.getExit(input.poolType).buildCall(input); - } + public buildCall(input: ExitCall): ExitBuildOutput { + return this.getExit(input.poolType).buildCall(input); + } } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 03173b03..a3815cf6 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -1,88 +1,93 @@ -import { TokenAmount } from '../tokenAmount'; -import { Slippage } from '../slippage'; -import { Address } from '../../types'; -import { PoolState } from '../types'; +import { TokenAmount } from "../tokenAmount"; +import { Slippage } from "../slippage"; +import { Address } from "../../types"; +import { PoolState } from "../types"; export enum ExitKind { - UNBALANCED = 'UNBALANCED', // exitExactOut - SINGLE_ASSET = 'SINGLE_ASSET', // exitExactInSingleAsset - PROPORTIONAL = 'PROPORTIONAL', // exitExactInProportional + UNBALANCED = "UNBALANCED", // exitExactOut + SINGLE_ASSET = "SINGLE_ASSET", // exitExactInSingleAsset + PROPORTIONAL = "PROPORTIONAL", // exitExactInProportional } // This will be extended for each pools specific output requirements export type BaseExitInput = { - chainId: number; - rpcUrl: string; - exitWithNativeAsset?: boolean; - toInternalBalance?: boolean; + chainId: number; + rpcUrl: string; + exitWithNativeAsset?: boolean; + toInternalBalance?: boolean; }; export type UnbalancedExitInput = BaseExitInput & { - amountsOut: TokenAmount[]; - kind: ExitKind.UNBALANCED; + amountsOut: TokenAmount[]; + kind: ExitKind.UNBALANCED; }; export type SingleAssetExitInput = BaseExitInput & { - bptIn: TokenAmount; - tokenOut: Address; - kind: ExitKind.SINGLE_ASSET; + bptIn: TokenAmount; + tokenOut: Address; + kind: ExitKind.SINGLE_ASSET; }; export type ProportionalExitInput = BaseExitInput & { - bptIn: TokenAmount; - kind: ExitKind.PROPORTIONAL; + bptIn: TokenAmount; + kind: ExitKind.PROPORTIONAL; }; export type ExitInput = - | UnbalancedExitInput - | SingleAssetExitInput - | ProportionalExitInput; + | UnbalancedExitInput + | SingleAssetExitInput + | ProportionalExitInput; export type ExitQueryResult = - BaseExitQueryResult + | BaseExitQueryResult | ComposableStableExitQueryResult; // Returned from a exit query export type BaseExitQueryResult = { - poolType: string; - id: Address; - exitKind: ExitKind; - bptIn: TokenAmount; - amountsOut: TokenAmount[]; - tokenOutIndex?: number; - toInternalBalance?: boolean; + poolType: string; + id: Address; + exitKind: ExitKind; + bptIn: TokenAmount; + amountsOut: TokenAmount[]; + tokenOutIndex?: number; + toInternalBalance?: boolean; }; export type ComposableStableExitQueryResult = BaseExitQueryResult & { - bptIndex?: number; -} + bptIndex: number; +}; -export type ExitCallInput = ExitQueryResult & ComposableStableExitQueryResult & { - slippage: Slippage; - sender: Address; - recipient: Address; +type BaseExitCall = { + slippage: Slippage; + sender: Address; + recipient: Address; }; +export type ComposableStableExitCall = BaseExitCall & + ComposableStableExitQueryResult; +export type WeightedExitCall = BaseExitCall & BaseExitQueryResult; + +export type ExitCall = ComposableStableExitCall | WeightedExitCall; export type ExitBuildOutput = { - call: Address; - to: Address; - value: bigint | undefined; - maxBptIn: bigint; - minAmountsOut: bigint[]; + call: Address; + to: Address; + value: bigint | undefined; + maxBptIn: bigint; + minAmountsOut: bigint[]; }; export interface BaseExit { - query(input: ExitInput, poolState: PoolState): Promise; - buildCall(input: ExitCallInput): ExitBuildOutput; + query(input: ExitInput, poolState: PoolState): Promise; + buildCall(input: ExitCall): ExitBuildOutput; } export type ExitConfig = { - customPoolExits: Record; + customPoolExits: Record; }; export type ExitPoolRequest = { - assets: Address[]; - minAmountsOut: bigint[]; - userData: Address; - toInternalBalance: boolean; + assets: Address[]; + minAmountsOut: bigint[]; + userData: Address; + toInternalBalance: boolean; }; diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 28fecb7d..13d72126 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -1,183 +1,178 @@ -import { encodeFunctionData } from 'viem'; -import { Token } from '../../token'; -import { TokenAmount } from '../../tokenAmount'; -import { WeightedEncoder } from '../../encoders/weighted'; -import { Address } from '../../../types'; +import { encodeFunctionData } from "viem"; +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/constants'; -import { vaultAbi } from '../../../abi'; -import { parseExitArgs } from '../../utils/parseExitArgs'; + BALANCER_VAULT, + MAX_UINT256, + ZERO_ADDRESS, +} from "../../../utils/constants"; +import { vaultAbi } from "../../../abi"; +import { parseExitArgs } from "../../utils/parseExitArgs"; import { - BaseExit, - ExitBuildOutput, - ExitCallInput, - ExitInput, - ExitKind, - ExitQueryResult, -} from '../types'; -import { AmountsExit, PoolState } from '../../types'; -import { doQueryExit } from '../../utils/doQueryExit'; + BaseExit, + ExitBuildOutput, + ExitCall, + ExitInput, + ExitKind, + ExitQueryResult, + WeightedExitCall, +} from "../types"; +import { AmountsExit, PoolState } from "../../types"; +import { doQueryExit } from "../../utils/doQueryExit"; export class WeightedExit implements BaseExit { - public async query( - input: ExitInput, - poolState: PoolState, - ): Promise { - const amounts = this.getAmountsQuery(poolState.tokens, input); - - const userData = this.encodeUserData(input.kind, amounts); - - // tokensOut will have zero address if exit with native asset - const { args, tokensOut } = parseExitArgs({ - chainId: input.chainId, - exitWithNativeAsset: !!input.exitWithNativeAsset, - poolId: poolState.id, - sortedTokens: poolState.tokens, - sender: ZERO_ADDRESS, - recipient: ZERO_ADDRESS, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - - const queryResult = await doQueryExit( - input.rpcUrl, - input.chainId, - args, - ); - - const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); - - const amountsOut = queryResult.amountsOut.map((a, i) => - TokenAmount.fromRawAmount(tokensOut[i], a), - ); - + public async query( + input: ExitInput, + poolState: PoolState + ): Promise { + const amounts = this.getAmountsQuery(poolState.tokens, input); + + const userData = this.encodeUserData(input.kind, amounts); + + // tokensOut will have zero address if exit with native asset + const { args, tokensOut } = parseExitArgs({ + chainId: input.chainId, + exitWithNativeAsset: !!input.exitWithNativeAsset, + poolId: poolState.id, + sortedTokens: poolState.tokens, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + + const queryResult = await doQueryExit(input.rpcUrl, input.chainId, args); + + const bpt = new Token(input.chainId, poolState.address, 18); + const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + + const amountsOut = queryResult.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(tokensOut[i], a) + ); + + return { + poolType: poolState.type, + exitKind: input.kind, + id: poolState.id, + bptIn, + amountsOut, + tokenOutIndex: amounts.tokenOutIndex, + }; + } + + private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { + switch (input.kind) { + case ExitKind.UNBALANCED: return { - poolType: poolState.type, - exitKind: input.kind, - id: poolState.id, - bptIn, - amountsOut, - tokenOutIndex: amounts.tokenOutIndex, + minAmountsOut: tokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t))?.amount ?? 0n + ), + tokenOutIndex: undefined, + maxBptAmountIn: MAX_UINT256, }; - } - - private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { - switch (input.kind) { - case ExitKind.UNBALANCED: - return { - minAmountsOut: tokens.map( - (t) => - input.amountsOut.find((a) => a.token.isEqual(t)) - ?.amount ?? 0n, - ), - tokenOutIndex: undefined, - maxBptAmountIn: MAX_UINT256, - }; - case ExitKind.SINGLE_ASSET: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: tokens.findIndex((t) => - t.isSameAddress(input.tokenOut), - ), - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: undefined, - maxBptAmountIn: input.bptIn.amount, - }; - } - } - - public buildCall(input: ExitCallInput): ExitBuildOutput { - const amounts = this.getAmountsCall(input); - - const userData = this.encodeUserData(input.exitKind, amounts); - - const { args } = parseExitArgs({ - poolId: input.id, - sortedTokens: input.amountsOut.map((a) => a.token), - sender: input.sender, - recipient: input.recipient, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - - const call = encodeFunctionData({ - abi: vaultAbi, - functionName: 'exitPool', - args, - }); - + case ExitKind.SINGLE_ASSET: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: tokens.findIndex((t) => + t.isSameAddress(input.tokenOut) + ), + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: return { - call, - to: BALANCER_VAULT, - value: 0n, - maxBptIn: amounts.maxBptAmountIn, - minAmountsOut: amounts.minAmountsOut, + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: undefined, + maxBptAmountIn: input.bptIn.amount, }; } - - private getAmountsCall(input: ExitCallInput): AmountsExit { - switch (input.exitKind) { - case ExitKind.UNBALANCED: - return { - minAmountsOut: input.amountsOut.map((a) => a.amount), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), - }; - case ExitKind.SINGLE_ASSET: - if (input.tokenOutIndex === undefined) { - throw new Error( - 'tokenOutIndex must be defined for SINGLE_ASSET exit', - ); - } - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount), - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount), - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - default: - throw Error('Unsupported Exit Type'); + } + + public buildCall(input: WeightedExitCall): ExitBuildOutput { + const amounts = this.getAmountsCall(input); + + const userData = this.encodeUserData(input.exitKind, amounts); + + const { args } = parseExitArgs({ + poolId: input.id, + sortedTokens: input.amountsOut.map((a) => a.token), + sender: input.sender, + recipient: input.recipient, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: "exitPool", + args, + }); + + return { + call, + to: BALANCER_VAULT, + value: 0n, + maxBptIn: amounts.maxBptAmountIn, + minAmountsOut: amounts.minAmountsOut, + }; + } + + private getAmountsCall(input: ExitCall): AmountsExit { + switch (input.exitKind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: input.amountsOut.map((a) => a.amount), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), + }; + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + "tokenOutIndex must be defined for SINGLE_ASSET exit" + ); } + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount) + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount) + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + default: + throw Error("Unsupported Exit Type"); } + } + + private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + switch (kind) { + case ExitKind.UNBALANCED: + return WeightedEncoder.exitUnbalanced( + amounts.minAmountsOut, + amounts.maxBptAmountIn + ); + case ExitKind.SINGLE_ASSET: + if (amounts.tokenOutIndex === undefined) throw Error("No Index"); - private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { - switch (kind) { - case ExitKind.UNBALANCED: - return WeightedEncoder.exitUnbalanced( - amounts.minAmountsOut, - amounts.maxBptAmountIn, - ); - case ExitKind.SINGLE_ASSET: - if (amounts.tokenOutIndex === undefined) - throw Error('No Index'); - - return WeightedEncoder.exitSingleAsset( - amounts.maxBptAmountIn, - amounts.tokenOutIndex, - ); - case ExitKind.PROPORTIONAL: - return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); - default: - throw Error('Unsupported Exit Type'); - } + return WeightedEncoder.exitSingleAsset( + amounts.maxBptAmountIn, + amounts.tokenOutIndex + ); + case ExitKind.PROPORTIONAL: + return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); + default: + throw Error("Unsupported Exit Type"); } + } } From 422f42b5a90015f46f208d183fab79fd1590d1c0 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 18 Oct 2023 19:24:32 -0300 Subject: [PATCH 105/199] bptIndex is mandatory for composable stable pools; --- src/entities/join/composable-stable/composableStableJoin.ts | 3 --- src/entities/join/types.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index d43d191a..305a7e81 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -75,9 +75,6 @@ export class ComposableStableJoin implements BaseJoin { public buildCall(input: ComposableJoinCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); - if (input.bptIndex === undefined) { - throw new Error('bptIndex is necessary'); - } const amountsWithoutBpt = { ...amounts, maxAmountsIn: [ diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 3c8f622f..6ef3b866 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -58,7 +58,7 @@ type BaseJoinQueryResult = { export type WeightedJoinQueryResult = BaseJoinQueryResult; export type ComposableStableJoinQueryResult = BaseJoinQueryResult & { - bptIndex?: number; + bptIndex: number; }; export type JoinQueryResult = From e0016abc4dfd176f184a3a4c8285b530c6d029f7 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 18 Oct 2023 19:25:57 -0300 Subject: [PATCH 106/199] Running rome lint on all files; --- .../composable-stable/composableStableExit.ts | 352 ++++----- src/entities/exit/poolExit.ts | 90 +-- src/entities/exit/types.ts | 94 +-- src/entities/exit/weighted/weightedExit.ts | 334 ++++----- .../composable-stable/composableStableJoin.ts | 46 +- src/entities/join/poolJoin.ts | 2 +- src/entities/join/types.ts | 23 +- test/composableStableExit.integration.test.ts | 610 +++++++-------- test/composableStableJoin.integration.test.ts | 694 +++++++++--------- 9 files changed, 1152 insertions(+), 1093 deletions(-) diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index 080c88af..457cf98b 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -1,194 +1,202 @@ -import { encodeFunctionData } from "viem"; -import { Token } from "../../token"; -import { TokenAmount } from "../../tokenAmount"; -import { Address } from "../../../types"; +import { encodeFunctionData } from 'viem'; +import { Token } from '../../token'; +import { TokenAmount } from '../../tokenAmount'; +import { Address } from '../../../types'; import { - BALANCER_VAULT, - MAX_UINT256, - ZERO_ADDRESS, -} from "../../../utils/constants"; -import { vaultAbi } from "../../../abi"; -import { parseExitArgs } from "../../utils/parseExitArgs"; + BALANCER_VAULT, + MAX_UINT256, + ZERO_ADDRESS, +} from '../../../utils/constants'; +import { vaultAbi } from '../../../abi'; +import { parseExitArgs } from '../../utils/parseExitArgs'; import { - BaseExit, - ComposableStableExitCall, - ExitBuildOutput, - ExitInput, - ExitKind, - ExitQueryResult, -} from "../types"; -import { AmountsExit, PoolState } from "../../types"; -import { doQueryExit } from "../../utils/doQueryExit"; -import { ComposableStableEncoder } from "../../encoders/composableStable"; + BaseExit, + ComposableStableExitCall, + ExitBuildOutput, + ExitInput, + ExitKind, + ExitQueryResult, +} from '../types'; +import { AmountsExit, PoolState } from '../../types'; +import { doQueryExit } from '../../utils/doQueryExit'; +import { ComposableStableEncoder } from '../../encoders/composableStable'; export class ComposableStableExit implements BaseExit { - public async query( - input: ExitInput, - poolState: PoolState - ): Promise { - const bptIndex = poolState.tokens.findIndex( - (t) => t.address === poolState.address - ); - const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); - const amountsWithoutBpt = { - ...amounts, - minAmountsOut: [ - ...amounts.minAmountsOut.slice(0, bptIndex), - ...amounts.minAmountsOut.slice(bptIndex + 1), - ], - }; - const userData = this.encodeUserData(input.kind, amountsWithoutBpt); - - // tokensOut will have zero address if exit with native asset - const { args, tokensOut } = parseExitArgs({ - chainId: input.chainId, - exitWithNativeAsset: !!input.exitWithNativeAsset, - poolId: poolState.id, - sortedTokens: poolState.tokens, - sender: ZERO_ADDRESS, - recipient: ZERO_ADDRESS, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - const queryResult = await doQueryExit(input.rpcUrl, input.chainId, args); - const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + public async query( + input: ExitInput, + poolState: PoolState, + ): Promise { + const bptIndex = poolState.tokens.findIndex( + (t) => t.address === poolState.address, + ); + const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); + const amountsWithoutBpt = { + ...amounts, + minAmountsOut: [ + ...amounts.minAmountsOut.slice(0, bptIndex), + ...amounts.minAmountsOut.slice(bptIndex + 1), + ], + }; + const userData = this.encodeUserData(input.kind, amountsWithoutBpt); - const amountsOut = queryResult.amountsOut.map((a, i) => - TokenAmount.fromRawAmount(tokensOut[i], a) - ); + // tokensOut will have zero address if exit with native asset + const { args, tokensOut } = parseExitArgs({ + chainId: input.chainId, + exitWithNativeAsset: !!input.exitWithNativeAsset, + poolId: poolState.id, + sortedTokens: poolState.tokens, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + const queryResult = await doQueryExit( + input.rpcUrl, + input.chainId, + args, + ); + const bpt = new Token(input.chainId, poolState.address, 18); + const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); - return { - poolType: poolState.type, - exitKind: input.kind, - id: poolState.id, - bptIn, - amountsOut, - tokenOutIndex: amounts.tokenOutIndex, - bptIndex, - }; - } + const amountsOut = queryResult.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(tokensOut[i], a), + ); - private getAmountsQuery( - tokens: Token[], - input: ExitInput, - bptIndex?: number - ): AmountsExit { - switch (input.kind) { - case ExitKind.UNBALANCED: return { - minAmountsOut: tokens.map( - (t) => - input.amountsOut.find((a) => a.token.isEqual(t))?.amount ?? 0n - ), - tokenOutIndex: undefined, - maxBptAmountIn: MAX_UINT256, - }; - case ExitKind.SINGLE_ASSET: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: tokens - .filter((_, index) => index !== bptIndex) - .findIndex((t) => t.isSameAddress(input.tokenOut)), - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: undefined, - maxBptAmountIn: input.bptIn.amount, + poolType: poolState.type, + exitKind: input.kind, + id: poolState.id, + bptIn, + amountsOut, + tokenOutIndex: amounts.tokenOutIndex, + bptIndex, }; } - } - public buildCall(input: ComposableStableExitCall): ExitBuildOutput { - const amounts = this.getAmountsCall(input); - const amountsWithoutBpt = { - ...amounts, - minAmountsOut: [ - ...amounts.minAmountsOut.slice(0, input.bptIndex), - ...amounts.minAmountsOut.slice(input.bptIndex + 1), - ], - }; - const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); + private getAmountsQuery( + tokens: Token[], + input: ExitInput, + bptIndex?: number, + ): AmountsExit { + switch (input.kind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: tokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ), + tokenOutIndex: undefined, + maxBptAmountIn: MAX_UINT256, + }; + case ExitKind.SINGLE_ASSET: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: tokens + .filter((_, index) => index !== bptIndex) + .findIndex((t) => t.isSameAddress(input.tokenOut)), + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: undefined, + maxBptAmountIn: input.bptIn.amount, + }; + } + } - const { args } = parseExitArgs({ - poolId: input.id, - sortedTokens: input.amountsOut.map((a) => a.token), - sender: input.sender, - recipient: input.recipient, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - const call = encodeFunctionData({ - abi: vaultAbi, - functionName: "exitPool", - args, - }); + public buildCall(input: ComposableStableExitCall): ExitBuildOutput { + const amounts = this.getAmountsCall(input); + const amountsWithoutBpt = { + ...amounts, + minAmountsOut: [ + ...amounts.minAmountsOut.slice(0, input.bptIndex), + ...amounts.minAmountsOut.slice(input.bptIndex + 1), + ], + }; + const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); - return { - call, - to: BALANCER_VAULT, - value: 0n, - maxBptIn: amounts.maxBptAmountIn, - minAmountsOut: amounts.minAmountsOut, - }; - } + const { args } = parseExitArgs({ + poolId: input.id, + sortedTokens: input.amountsOut.map((a) => a.token), + sender: input.sender, + recipient: input.recipient, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'exitPool', + args, + }); - private getAmountsCall(input: ComposableStableExitCall): AmountsExit { - switch (input.exitKind) { - case ExitKind.UNBALANCED: return { - minAmountsOut: input.amountsOut.map((a) => a.amount), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), + call, + to: BALANCER_VAULT, + value: 0n, + maxBptIn: amounts.maxBptAmountIn, + minAmountsOut: amounts.minAmountsOut, }; - case ExitKind.SINGLE_ASSET: - if (input.tokenOutIndex === undefined) { - throw new Error( - "tokenOutIndex must be defined for SINGLE_ASSET exit" - ); + } + + private getAmountsCall(input: ComposableStableExitCall): AmountsExit { + switch (input.exitKind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: input.amountsOut.map((a) => a.amount), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), + }; + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + 'tokenOutIndex must be defined for SINGLE_ASSET exit', + ); + } + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + default: + throw Error('Unsupported Exit Type'); } - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount) - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount) - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - default: - throw Error("Unsupported Exit Type"); } - } - private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { - switch (kind) { - case ExitKind.UNBALANCED: - return ComposableStableEncoder.exitUnbalanced( - amounts.minAmountsOut, - amounts.maxBptAmountIn - ); - case ExitKind.SINGLE_ASSET: - if (amounts.tokenOutIndex === undefined) throw Error("No Index"); + private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + switch (kind) { + case ExitKind.UNBALANCED: + return ComposableStableEncoder.exitUnbalanced( + amounts.minAmountsOut, + amounts.maxBptAmountIn, + ); + case ExitKind.SINGLE_ASSET: + if (amounts.tokenOutIndex === undefined) + throw Error('No Index'); - return ComposableStableEncoder.exitSingleAsset( - amounts.maxBptAmountIn, - amounts.tokenOutIndex - ); - case ExitKind.PROPORTIONAL: - return ComposableStableEncoder.exitProportional(amounts.maxBptAmountIn); - default: - throw Error("Unsupported Exit Type"); + return ComposableStableEncoder.exitSingleAsset( + amounts.maxBptAmountIn, + amounts.tokenOutIndex, + ); + case ExitKind.PROPORTIONAL: + return ComposableStableEncoder.exitProportional( + amounts.maxBptAmountIn, + ); + default: + throw Error('Unsupported Exit Type'); + } } - } } diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 7555e446..a6cf885a 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -1,55 +1,55 @@ import { - BaseExit, - ExitBuildOutput, - ExitCall, - ExitConfig, - ExitInput, - ExitQueryResult, -} from "./types"; -import { WeightedExit } from "./weighted/weightedExit"; -import { PoolStateInput } from "../types"; -import { validateInputs } from "./weighted/validateInputs"; -import { getSortedTokens } from "../utils/getSortedTokens"; -import { ComposableStableExit } from "./composable-stable/composableStableExit"; + BaseExit, + ExitBuildOutput, + ExitCall, + ExitConfig, + ExitInput, + ExitQueryResult, +} from './types'; +import { WeightedExit } from './weighted/weightedExit'; +import { PoolStateInput } from '../types'; +import { validateInputs } from './weighted/validateInputs'; +import { getSortedTokens } from '../utils/getSortedTokens'; +import { ComposableStableExit } from './composable-stable/composableStableExit'; export class PoolExit { - private readonly poolExits: Record = {}; - - constructor(config?: ExitConfig) { - const { customPoolExits } = config || {}; - this.poolExits = { - Weighted: new WeightedExit(), - // PHANTOM_STABLE === ComposableStables in API - PHANTOM_STABLE: new ComposableStableExit(), - // custom pool Exits take precedence over base Exits - ...customPoolExits, - }; - } - - public getExit(poolType: string): BaseExit { - if (!this.poolExits[poolType]) { - throw new Error("Unsupported pool type"); + private readonly poolExits: Record = {}; + + constructor(config?: ExitConfig) { + const { customPoolExits } = config || {}; + this.poolExits = { + Weighted: new WeightedExit(), + // PHANTOM_STABLE === ComposableStables in API + PHANTOM_STABLE: new ComposableStableExit(), + // custom pool Exits take precedence over base Exits + ...customPoolExits, + }; } - return this.poolExits[poolType]; - } + public getExit(poolType: string): BaseExit { + if (!this.poolExits[poolType]) { + throw new Error('Unsupported pool type'); + } - public async query( - input: ExitInput, - poolState: PoolStateInput - ): Promise { - validateInputs(input, poolState); + return this.poolExits[poolType]; + } + + public async query( + input: ExitInput, + poolState: PoolStateInput, + ): Promise { + validateInputs(input, poolState); - const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); - const mappedPoolState = { - ...poolState, - tokens: sortedTokens, - }; + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); + const mappedPoolState = { + ...poolState, + tokens: sortedTokens, + }; - return this.getExit(poolState.type).query(input, mappedPoolState); - } + return this.getExit(poolState.type).query(input, mappedPoolState); + } - public buildCall(input: ExitCall): ExitBuildOutput { - return this.getExit(input.poolType).buildCall(input); - } + public buildCall(input: ExitCall): ExitBuildOutput { + return this.getExit(input.poolType).buildCall(input); + } } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index a3815cf6..493e11e5 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -1,93 +1,93 @@ -import { TokenAmount } from "../tokenAmount"; -import { Slippage } from "../slippage"; -import { Address } from "../../types"; -import { PoolState } from "../types"; +import { TokenAmount } from '../tokenAmount'; +import { Slippage } from '../slippage'; +import { Address } from '../../types'; +import { PoolState } from '../types'; export enum ExitKind { - UNBALANCED = "UNBALANCED", // exitExactOut - SINGLE_ASSET = "SINGLE_ASSET", // exitExactInSingleAsset - PROPORTIONAL = "PROPORTIONAL", // exitExactInProportional + UNBALANCED = 'UNBALANCED', // exitExactOut + SINGLE_ASSET = 'SINGLE_ASSET', // exitExactInSingleAsset + PROPORTIONAL = 'PROPORTIONAL', // exitExactInProportional } // This will be extended for each pools specific output requirements export type BaseExitInput = { - chainId: number; - rpcUrl: string; - exitWithNativeAsset?: boolean; - toInternalBalance?: boolean; + chainId: number; + rpcUrl: string; + exitWithNativeAsset?: boolean; + toInternalBalance?: boolean; }; export type UnbalancedExitInput = BaseExitInput & { - amountsOut: TokenAmount[]; - kind: ExitKind.UNBALANCED; + amountsOut: TokenAmount[]; + kind: ExitKind.UNBALANCED; }; export type SingleAssetExitInput = BaseExitInput & { - bptIn: TokenAmount; - tokenOut: Address; - kind: ExitKind.SINGLE_ASSET; + bptIn: TokenAmount; + tokenOut: Address; + kind: ExitKind.SINGLE_ASSET; }; export type ProportionalExitInput = BaseExitInput & { - bptIn: TokenAmount; - kind: ExitKind.PROPORTIONAL; + bptIn: TokenAmount; + kind: ExitKind.PROPORTIONAL; }; export type ExitInput = - | UnbalancedExitInput - | SingleAssetExitInput - | ProportionalExitInput; + | UnbalancedExitInput + | SingleAssetExitInput + | ProportionalExitInput; export type ExitQueryResult = - | BaseExitQueryResult - | ComposableStableExitQueryResult; + | BaseExitQueryResult + | ComposableStableExitQueryResult; // Returned from a exit query export type BaseExitQueryResult = { - poolType: string; - id: Address; - exitKind: ExitKind; - bptIn: TokenAmount; - amountsOut: TokenAmount[]; - tokenOutIndex?: number; - toInternalBalance?: boolean; + poolType: string; + id: Address; + exitKind: ExitKind; + bptIn: TokenAmount; + amountsOut: TokenAmount[]; + tokenOutIndex?: number; + toInternalBalance?: boolean; }; export type ComposableStableExitQueryResult = BaseExitQueryResult & { - bptIndex: number; + bptIndex: number; }; type BaseExitCall = { - slippage: Slippage; - sender: Address; - recipient: Address; + slippage: Slippage; + sender: Address; + recipient: Address; }; export type ComposableStableExitCall = BaseExitCall & - ComposableStableExitQueryResult; + ComposableStableExitQueryResult; export type WeightedExitCall = BaseExitCall & BaseExitQueryResult; export type ExitCall = ComposableStableExitCall | WeightedExitCall; export type ExitBuildOutput = { - call: Address; - to: Address; - value: bigint | undefined; - maxBptIn: bigint; - minAmountsOut: bigint[]; + call: Address; + to: Address; + value: bigint | undefined; + maxBptIn: bigint; + minAmountsOut: bigint[]; }; export interface BaseExit { - query(input: ExitInput, poolState: PoolState): Promise; - buildCall(input: ExitCall): ExitBuildOutput; + query(input: ExitInput, poolState: PoolState): Promise; + buildCall(input: ExitCall): ExitBuildOutput; } export type ExitConfig = { - customPoolExits: Record; + customPoolExits: Record; }; export type ExitPoolRequest = { - assets: Address[]; - minAmountsOut: bigint[]; - userData: Address; - toInternalBalance: boolean; + assets: Address[]; + minAmountsOut: bigint[]; + userData: Address; + toInternalBalance: boolean; }; diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 13d72126..63c1c617 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -1,178 +1,184 @@ -import { encodeFunctionData } from "viem"; -import { Token } from "../../token"; -import { TokenAmount } from "../../tokenAmount"; -import { WeightedEncoder } from "../../encoders/weighted"; -import { Address } from "../../../types"; +import { encodeFunctionData } from 'viem'; +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/constants"; -import { vaultAbi } from "../../../abi"; -import { parseExitArgs } from "../../utils/parseExitArgs"; + BALANCER_VAULT, + MAX_UINT256, + ZERO_ADDRESS, +} from '../../../utils/constants'; +import { vaultAbi } from '../../../abi'; +import { parseExitArgs } from '../../utils/parseExitArgs'; import { - BaseExit, - ExitBuildOutput, - ExitCall, - ExitInput, - ExitKind, - ExitQueryResult, - WeightedExitCall, -} from "../types"; -import { AmountsExit, PoolState } from "../../types"; -import { doQueryExit } from "../../utils/doQueryExit"; + BaseExit, + ExitBuildOutput, + ExitCall, + ExitInput, + ExitKind, + ExitQueryResult, + WeightedExitCall, +} from '../types'; +import { AmountsExit, PoolState } from '../../types'; +import { doQueryExit } from '../../utils/doQueryExit'; export class WeightedExit implements BaseExit { - public async query( - input: ExitInput, - poolState: PoolState - ): Promise { - const amounts = this.getAmountsQuery(poolState.tokens, input); - - const userData = this.encodeUserData(input.kind, amounts); - - // tokensOut will have zero address if exit with native asset - const { args, tokensOut } = parseExitArgs({ - chainId: input.chainId, - exitWithNativeAsset: !!input.exitWithNativeAsset, - poolId: poolState.id, - sortedTokens: poolState.tokens, - sender: ZERO_ADDRESS, - recipient: ZERO_ADDRESS, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - - const queryResult = await doQueryExit(input.rpcUrl, input.chainId, args); - - const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); - - const amountsOut = queryResult.amountsOut.map((a, i) => - TokenAmount.fromRawAmount(tokensOut[i], a) - ); - - return { - poolType: poolState.type, - exitKind: input.kind, - id: poolState.id, - bptIn, - amountsOut, - tokenOutIndex: amounts.tokenOutIndex, - }; - } - - private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { - switch (input.kind) { - case ExitKind.UNBALANCED: - return { - minAmountsOut: tokens.map( - (t) => - input.amountsOut.find((a) => a.token.isEqual(t))?.amount ?? 0n - ), - tokenOutIndex: undefined, - maxBptAmountIn: MAX_UINT256, - }; - case ExitKind.SINGLE_ASSET: - return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: tokens.findIndex((t) => - t.isSameAddress(input.tokenOut) - ), - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: + public async query( + input: ExitInput, + poolState: PoolState, + ): Promise { + const amounts = this.getAmountsQuery(poolState.tokens, input); + + const userData = this.encodeUserData(input.kind, amounts); + + // tokensOut will have zero address if exit with native asset + const { args, tokensOut } = parseExitArgs({ + chainId: input.chainId, + exitWithNativeAsset: !!input.exitWithNativeAsset, + poolId: poolState.id, + sortedTokens: poolState.tokens, + sender: ZERO_ADDRESS, + recipient: ZERO_ADDRESS, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + + const queryResult = await doQueryExit( + input.rpcUrl, + input.chainId, + args, + ); + + const bpt = new Token(input.chainId, poolState.address, 18); + const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + + const amountsOut = queryResult.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(tokensOut[i], a), + ); + return { - minAmountsOut: Array(tokens.length).fill(0n), - tokenOutIndex: undefined, - maxBptAmountIn: input.bptIn.amount, + poolType: poolState.type, + exitKind: input.kind, + id: poolState.id, + bptIn, + amountsOut, + tokenOutIndex: amounts.tokenOutIndex, }; } - } - - public buildCall(input: WeightedExitCall): ExitBuildOutput { - const amounts = this.getAmountsCall(input); - - const userData = this.encodeUserData(input.exitKind, amounts); - - const { args } = parseExitArgs({ - poolId: input.id, - sortedTokens: input.amountsOut.map((a) => a.token), - sender: input.sender, - recipient: input.recipient, - minAmountsOut: amounts.minAmountsOut, - userData, - toInternalBalance: !!input.toInternalBalance, - }); - - const call = encodeFunctionData({ - abi: vaultAbi, - functionName: "exitPool", - args, - }); - - return { - call, - to: BALANCER_VAULT, - value: 0n, - maxBptIn: amounts.maxBptAmountIn, - minAmountsOut: amounts.minAmountsOut, - }; - } - - private getAmountsCall(input: ExitCall): AmountsExit { - switch (input.exitKind) { - case ExitKind.UNBALANCED: - return { - minAmountsOut: input.amountsOut.map((a) => a.amount), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), - }; - case ExitKind.SINGLE_ASSET: - if (input.tokenOutIndex === undefined) { - throw new Error( - "tokenOutIndex must be defined for SINGLE_ASSET exit" - ); + + private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { + switch (input.kind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: tokens.map( + (t) => + input.amountsOut.find((a) => a.token.isEqual(t)) + ?.amount ?? 0n, + ), + tokenOutIndex: undefined, + maxBptAmountIn: MAX_UINT256, + }; + case ExitKind.SINGLE_ASSET: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: tokens.findIndex((t) => + t.isSameAddress(input.tokenOut), + ), + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: Array(tokens.length).fill(0n), + tokenOutIndex: undefined, + maxBptAmountIn: input.bptIn.amount, + }; } + } + + public buildCall(input: WeightedExitCall): ExitBuildOutput { + const amounts = this.getAmountsCall(input); + + const userData = this.encodeUserData(input.exitKind, amounts); + + const { args } = parseExitArgs({ + poolId: input.id, + sortedTokens: input.amountsOut.map((a) => a.token), + sender: input.sender, + recipient: input.recipient, + minAmountsOut: amounts.minAmountsOut, + userData, + toInternalBalance: !!input.toInternalBalance, + }); + + const call = encodeFunctionData({ + abi: vaultAbi, + functionName: 'exitPool', + args, + }); + return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount) - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, - }; - case ExitKind.PROPORTIONAL: - return { - minAmountsOut: input.amountsOut.map((a) => - input.slippage.removeFrom(a.amount) - ), - tokenOutIndex: input.tokenOutIndex, - maxBptAmountIn: input.bptIn.amount, + call, + to: BALANCER_VAULT, + value: 0n, + maxBptIn: amounts.maxBptAmountIn, + minAmountsOut: amounts.minAmountsOut, }; - default: - throw Error("Unsupported Exit Type"); } - } - - private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { - switch (kind) { - case ExitKind.UNBALANCED: - return WeightedEncoder.exitUnbalanced( - amounts.minAmountsOut, - amounts.maxBptAmountIn - ); - case ExitKind.SINGLE_ASSET: - if (amounts.tokenOutIndex === undefined) throw Error("No Index"); - return WeightedEncoder.exitSingleAsset( - amounts.maxBptAmountIn, - amounts.tokenOutIndex - ); - case ExitKind.PROPORTIONAL: - return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); - default: - throw Error("Unsupported Exit Type"); + private getAmountsCall(input: ExitCall): AmountsExit { + switch (input.exitKind) { + case ExitKind.UNBALANCED: + return { + minAmountsOut: input.amountsOut.map((a) => a.amount), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), + }; + case ExitKind.SINGLE_ASSET: + if (input.tokenOutIndex === undefined) { + throw new Error( + 'tokenOutIndex must be defined for SINGLE_ASSET exit', + ); + } + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + case ExitKind.PROPORTIONAL: + return { + minAmountsOut: input.amountsOut.map((a) => + input.slippage.removeFrom(a.amount), + ), + tokenOutIndex: input.tokenOutIndex, + maxBptAmountIn: input.bptIn.amount, + }; + default: + throw Error('Unsupported Exit Type'); + } + } + + private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + switch (kind) { + case ExitKind.UNBALANCED: + return WeightedEncoder.exitUnbalanced( + amounts.minAmountsOut, + amounts.maxBptAmountIn, + ); + case ExitKind.SINGLE_ASSET: + if (amounts.tokenOutIndex === undefined) + throw Error('No Index'); + + return WeightedEncoder.exitSingleAsset( + amounts.maxBptAmountIn, + amounts.tokenOutIndex, + ); + case ExitKind.PROPORTIONAL: + return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); + default: + throw Error('Unsupported Exit Type'); + } } - } } diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index d1523474..b1312125 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -14,18 +14,23 @@ import { } from '../types'; import { AmountsJoin, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; -import { ComposableStableEncoder } from "../../encoders/composableStable"; +import { ComposableStableEncoder } from '../../encoders/composableStable'; export class ComposableStableJoin implements BaseJoin { public async query( input: JoinInput, poolState: PoolState, ): Promise { - const bptIndex = poolState.tokens.findIndex((t)=> t.address === poolState.address); + const bptIndex = poolState.tokens.findIndex( + (t) => t.address === poolState.address, + ); const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); const amountsWithoutBpt = { - ...amounts, - maxAmountsIn: [...amounts.maxAmountsIn.slice(0, bptIndex), ...amounts.maxAmountsIn.slice(bptIndex+1)] + ...amounts, + maxAmountsIn: [ + ...amounts.maxAmountsIn.slice(0, bptIndex), + ...amounts.maxAmountsIn.slice(bptIndex + 1), + ], }; const userData = this.encodeUserData(input.kind, amountsWithoutBpt); @@ -70,12 +75,15 @@ export class ComposableStableJoin implements BaseJoin { public buildCall(input: JoinCallInput): JoinBuildOutput { const amounts = this.getAmountsCall(input); - if(input.bptIndex === undefined){ - throw new Error("bptIndex is necessary"); + if (input.bptIndex === undefined) { + throw new Error('bptIndex is necessary'); } const amountsWithoutBpt = { ...amounts, - maxAmountsIn: [...amounts.maxAmountsIn.slice(0, input.bptIndex), ...amounts.maxAmountsIn.slice(input.bptIndex+1)] + maxAmountsIn: [ + ...amounts.maxAmountsIn.slice(0, input.bptIndex), + ...amounts.maxAmountsIn.slice(input.bptIndex + 1), + ], }; const userData = this.encodeUserData(input.joinKind, amountsWithoutBpt); @@ -110,26 +118,28 @@ export class ComposableStableJoin implements BaseJoin { private getAmountsQuery( poolTokens: Token[], input: JoinInput, - bptIndex?:number, + bptIndex?: number, ): AmountsJoin { switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: { return { minimumBpt: 0n, - maxAmountsIn: getAmounts(poolTokens, input.amountsIn, BigInt(0)), + maxAmountsIn: getAmounts( + poolTokens, + input.amountsIn, + BigInt(0), + ), tokenInIndex: undefined, }; } case JoinKind.SingleAsset: { - if(bptIndex===undefined){ - throw new Error("bptIndex is necessary"); + if (bptIndex === undefined) { + throw new Error('bptIndex is necessary'); } const tokenInIndex = poolTokens - .filter((_, index)=> index!==bptIndex) // Need to remove Bpt - .findIndex((t) => - t.isSameAddress(input.tokenIn) - ); + .filter((_, index) => index !== bptIndex) // Need to remove Bpt + .findIndex((t) => t.isSameAddress(input.tokenIn)); if (tokenInIndex === -1) throw Error("Can't find index of SingleAsset"); const maxAmountsIn = Array(poolTokens.length).fill(0n); @@ -137,7 +147,7 @@ export class ComposableStableJoin implements BaseJoin { return { minimumBpt: input.bptOut.amount, maxAmountsIn, - tokenInIndex + tokenInIndex, }; } case JoinKind.Proportional: { @@ -197,7 +207,9 @@ export class ComposableStableJoin implements BaseJoin { ); } case JoinKind.Proportional: { - return ComposableStableEncoder.joinProportional(amounts.minimumBpt); + return ComposableStableEncoder.joinProportional( + amounts.minimumBpt, + ); } default: throw Error('Unsupported Join Type'); diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 1d986674..872de52d 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -10,7 +10,7 @@ import { WeightedJoin } from './weighted/weightedJoin'; import { PoolStateInput } from '../types'; import { validateInputs } from './utils/validateInputs'; import { getSortedTokens } from '../utils/getSortedTokens'; -import { ComposableStableJoin } from "./composable-stable/composableStableJoin"; +import { ComposableStableJoin } from './composable-stable/composableStableJoin'; export class PoolJoin { private readonly poolJoins: Record = {}; diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 734b4c20..8f3dcf32 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -47,8 +47,8 @@ export type JoinInput = // Returned from a join query export type JoinQueryResult = - BaseJoinQueryResult - | ComposableStableJoinQueryResult; + | BaseJoinQueryResult + | ComposableStableJoinQueryResult; export type BaseJoinQueryResult = { poolType: string; @@ -58,20 +58,19 @@ export type BaseJoinQueryResult = { amountsIn: TokenAmount[]; fromInternalBalance: boolean; tokenInIndex?: number; -} +}; export type ComposableStableJoinQueryResult = BaseJoinQueryResult & { - bptIndex?:number; -} - -export type JoinCallInput = JoinQueryResult - & ComposableStableJoinQueryResult - & { - slippage: Slippage; - sender: Address; - recipient: Address; + bptIndex?: number; }; +export type JoinCallInput = JoinQueryResult & + ComposableStableJoinQueryResult & { + slippage: Slippage; + sender: Address; + recipient: Address; + }; + export interface BaseJoin { query(input: JoinInput, poolState: PoolState): Promise; buildCall(input: JoinCallInput): { diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index f6e0a5f7..0dc8de02 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -4,333 +4,351 @@ import { config } from 'dotenv'; config(); import { - Client, - createTestClient, - http, - parseUnits, - publicActions, - PublicActions, - TestActions, - WalletActions, - walletActions, + Client, + createTestClient, + http, + parseUnits, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, - ExitKind, - Slippage, - Token, - TokenAmount, - replaceWrapped, - PoolStateInput, - PoolExit, - Address, - Hex, - CHAINS, - ChainId, - getPoolAddress, - ExitInput, + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + Slippage, + Token, + TokenAmount, + replaceWrapped, + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolExit: PoolExit; - exitInput: ExitInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; + client: Client & PublicActions & TestActions & WalletActions; + poolExit: PoolExit; + exitInput: ExitInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; }; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const blockNumber = BigInt(18043296); const poolId = - '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool + '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool describe('composable stable exit test', () => { - let txInput: TxInput; - let bptToken: Token; - beforeAll(async () => { - // setup mock api - const api = new MockApi(); - - // get pool state from api - const poolInput = await api.getPool(poolId); - - const client: Client = createTestClient({ - mode: 'hardhat', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions) as Client; - - txInput = { - client, - poolExit: new PoolExit(), - slippage: Slippage.fromPercentage('1'), // 1% - poolInput, - testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - exitInput: {} as ExitInput, - checkNativeBalance: false, - }; - bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); - }); - - beforeEach(async () => { - await forkSetup( - txInput.client, - txInput.testAddress, - [txInput.poolInput.address], - undefined, // TODO: hardcode these values to improve test performance - [parseUnits('1000', 18)], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, - ); - }); - - test('single asset exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenOut = '0xf4edfad26ee0d23b69ca93112ecce52704e0006f'; // baoETH - - const exitInput: SingleAssetExitInput = { - chainId, - rpcUrl, - bptIn, - tokenOut, - kind: ExitKind.SINGLE_ASSET, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ - ...txInput, - exitInput, + let txInput: TxInput; + let bptToken: Token; + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + const poolInput = await api.getPool(poolId); + + const client: Client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions) as Client; + + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig + exitInput: {} as ExitInput, + checkNativeBalance: false, + }; + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We only expect single asset to have a value for exit - expect(queryResult.tokenOutIndex).to.be.toBeDefined; - queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a, i) => { - if (i === queryResult.tokenOutIndex) - expect(a.amount > BigInt(0)).to.be.true; - else expect(a.amount === BigInt(0)).to.be.true; - }); - - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); - }); - - test('proportional exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - - const exitInput: ProportionalExitInput = { - chainId, - rpcUrl, - bptIn, - kind: ExitKind.PROPORTIONAL, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ - ...txInput, - exitInput, - }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We expect all assets to have a value for exit - expect(queryResult.tokenOutIndex).to.be.undefined; - queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [txInput.poolInput.address], + undefined, // TODO: hardcode these values to improve test performance + [parseUnits('1000', 18)], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); }); - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); - }); - - test('unbalanced exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const { poolInput, slippage } = txInput; - - const poolTokens = poolInput.tokens - .filter((_, index) => index!==bptIndex) - .map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsOut = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '80'), - ); - - const exitInput: UnbalancedExitInput = { - chainId, - rpcUrl, - amountsOut, - kind: ExitKind.UNBALANCED, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ - ...txInput, - exitInput, + test('single asset exit', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenOut = '0xf4edfad26ee0d23b69ca93112ecce52704e0006f'; // baoETH + + const exitInput: SingleAssetExitInput = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SINGLE_ASSET, + }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + }); + + // Query should use correct BPT amount + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We only expect single asset to have a value for exit + expect(queryResult.tokenOutIndex).to.be.toBeDefined; + queryResult.amountsOut + .filter((_, index) => index !== bptIndex) + .forEach((a, i) => { + if (i === queryResult.tokenOutIndex) + expect(a.amount > BigInt(0)).to.be.true; + else expect(a.amount === BigInt(0)).to.be.true; + }); + + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); }); - // We expect a BPT input amount > 0 - expect(queryResult.bptIn.amount > BigInt(0)).to.be.true; - - // We expect assets to have same amount out as user defined - expect(queryResult.tokenOutIndex).to.be.undefined; - queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a, i) => { - expect(a.amount).to.eq(amountsOut[i].amount); + test('proportional exit', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + + const exitInput: ProportionalExitInput = { + chainId, + rpcUrl, + bptIn, + kind: ExitKind.PROPORTIONAL, + }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + }); + + // Query should use correct BPT amount + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We expect all assets to have a value for exit + expect(queryResult.tokenOutIndex).to.be.undefined; + queryResult.amountsOut + .filter((_, index) => index !== bptIndex) + .forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut.map((a) => + slippage.removeFrom(a.amount), + ); + expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); + expect(maxBptIn).to.eq(bptIn.amount); }); - // Confirm slippage - only to bpt in, not amounts out - const expectedMinAmountsOut = amountsOut.map((a) => a.amount); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut.filter((_, index) => index!==bptIndex)); - const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); - expect(expectedMaxBptIn).to.deep.eq(maxBptIn); - }); - - test('exit with native asset', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - - const exitInput: ProportionalExitInput = { - chainId, - rpcUrl, - bptIn, - kind: ExitKind.PROPORTIONAL, - exitWithNativeAsset: true, - }; - - // Note - checking native balance - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ - ...txInput, - exitInput, - checkNativeBalance: true, + test('unbalanced exit', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const { poolInput, slippage } = txInput; + + const poolTokens = poolInput.tokens + .filter((_, index) => index !== bptIndex) + .map((t) => new Token(chainId, t.address, t.decimals)); + const amountsOut = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '80'), + ); + + const exitInput: UnbalancedExitInput = { + chainId, + rpcUrl, + amountsOut, + kind: ExitKind.UNBALANCED, + }; + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + }); + + // We expect a BPT input amount > 0 + expect(queryResult.bptIn.amount > BigInt(0)).to.be.true; + + // We expect assets to have same amount out as user defined + expect(queryResult.tokenOutIndex).to.be.undefined; + queryResult.amountsOut + .filter((_, index) => index !== bptIndex) + .forEach((a, i) => { + expect(a.amount).to.eq(amountsOut[i].amount); + }); + + // Confirm slippage - only to bpt in, not amounts out + const expectedMinAmountsOut = amountsOut.map((a) => a.amount); + expect(expectedMinAmountsOut).to.deep.eq( + minAmountsOut.filter((_, index) => index !== bptIndex), + ); + const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); + expect(expectedMaxBptIn).to.deep.eq(maxBptIn); }); - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We expect all assets to have a value for exit - expect(queryResult.tokenOutIndex).to.be.undefined; - queryResult.amountsOut.filter((_, index) => index!==bptIndex).forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + test('exit with native asset', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + + const { slippage } = txInput; + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + + const exitInput: ProportionalExitInput = { + chainId, + rpcUrl, + bptIn, + kind: ExitKind.PROPORTIONAL, + exitWithNativeAsset: true, + }; + + // Note - checking native balance + const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + ...txInput, + exitInput, + checkNativeBalance: true, + }); + + // Query should use correct BPT amount + expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + + // We expect all assets to have a value for exit + expect(queryResult.tokenOutIndex).to.be.undefined; + queryResult.amountsOut + .filter((_, index) => index !== bptIndex) + .forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + + // Confirm slippage - only to amounts out not bpt in + const expectedMinAmountsOut = queryResult.amountsOut + .filter((_, index) => index !== bptIndex) + .map((a) => slippage.removeFrom(a.amount)); + expect(expectedMinAmountsOut).to.deep.eq( + minAmountsOut.filter((_, index) => index !== bptIndex), + ); + expect(maxBptIn).to.eq(bptIn.amount); }); - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut - .filter((_, index) => index!==bptIndex).map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut - .filter((_, index) => index!==bptIndex)); - expect(maxBptIn).to.eq(bptIn.amount); - }); - - async function doTransaction(txIp: TxInput) { - const { - poolExit, - poolInput, - exitInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolExit.query(exitInput, poolInput); - const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr], - client, - testAddress, - to, - call, - value, - ); - expect(transactionReceipt.status).to.eq('success'); - const bptIndex = poolInput.tokens.findIndex((t) => t.address === poolInput.address); - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), - queryResult.bptIn.amount, - ...queryResult.amountsOut.slice(bptIndex+1).map((a) => a.amount), - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxBptIn, - minAmountsOut, - }; - } + async function doTransaction(txIp: TxInput) { + const { + poolExit, + poolInput, + exitInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolExit.query(exitInput, poolInput); + const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( + { + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolTokensAddr], + client, + testAddress, + to, + call, + value, + ); + expect(transactionReceipt.status).to.eq('success'); + const bptIndex = poolInput.tokens.findIndex( + (t) => t.address === poolInput.address, + ); + // Confirm final balance changes match query result + const expectedDeltas = [ + ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), + queryResult.bptIn.amount, + ...queryResult.amountsOut.slice(bptIndex + 1).map((a) => a.amount), + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxBptIn, + minAmountsOut, + }; + } }); /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Hex): Promise { - const tokens = [ - { - address: - '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d0' as Address, // B-baoETH-ETH-BPT - decimals: 18, - index: 0, - }, - { - address: - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH - decimals: 18, - index: 1, - }, - { - address: - '0xf4edfad26ee0d23b69ca93112ecce52704e0006f' as Address, // baoETH - decimals: 18, - index: 2, - }, - ]; - - return { - id, - address: getPoolAddress(id) as Address, - type: 'PHANTOM_STABLE', - tokens, - }; - } + public async getPool(id: Hex): Promise { + const tokens = [ + { + address: + '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d0' as Address, // B-baoETH-ETH-BPT + decimals: 18, + index: 0, + }, + { + address: + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH + decimals: 18, + index: 1, + }, + { + address: + '0xf4edfad26ee0d23b69ca93112ecce52704e0006f' as Address, // baoETH + decimals: 18, + index: 2, + }, + ]; + + return { + id, + address: getPoolAddress(id) as Address, + type: 'PHANTOM_STABLE', + tokens, + }; + } } /******************************************************************************/ diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 7557d8ac..fd33fdd2 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -4,368 +4,384 @@ import { config } from 'dotenv'; config(); import { - Client, - createTestClient, - http, - parseUnits, - publicActions, - PublicActions, - TestActions, - WalletActions, - walletActions, + Client, + createTestClient, + http, + parseUnits, + publicActions, + PublicActions, + TestActions, + WalletActions, + walletActions, } from 'viem'; import { - UnbalancedJoinInput, - ProportionalJoinInput, - SingleAssetJoinInput, - JoinKind, - Slippage, - Token, - TokenAmount, - replaceWrapped, - Address, - Hex, - PoolStateInput, - CHAINS, - ChainId, - getPoolAddress, - PoolJoin, - JoinInput, + UnbalancedJoinInput, + ProportionalJoinInput, + SingleAssetJoinInput, + JoinKind, + Slippage, + Token, + TokenAmount, + replaceWrapped, + Address, + Hex, + PoolStateInput, + CHAINS, + ChainId, + getPoolAddress, + PoolJoin, + JoinInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; - joinInput: JoinInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; + client: Client & PublicActions & TestActions & WalletActions; + poolJoin: PoolJoin; + joinInput: JoinInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; }; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const blockNumber = BigInt(18043296); const poolId = - '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool + '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool describe('composable stable join test', () => { - let txInput: TxInput; - let bptToken: Token; - - beforeAll(async () => { - // setup mock api - const api = new MockApi(); - - // get pool state from api - const poolInput = await api.getPool(poolId); - - const client: Client = createTestClient({ - mode: 'hardhat', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions) as Client; - - txInput = { - client, - poolJoin: new PoolJoin(), - slippage: Slippage.fromPercentage('1'), // 1% - poolInput, - testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - joinInput: {} as JoinInput, - checkNativeBalance: false, - }; - - // setup BPT token - bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); - }); - - beforeEach(async () => { - await forkSetup( - txInput.client, - txInput.testAddress, - [ - ...txInput.poolInput.tokens.map((t) => t.address), - ], - [0,0,3], - [ - ...txInput.poolInput.tokens.map((t) => - parseUnits('100', t.decimals), - ), - ], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, - ); - }); - - test('unbalanced join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const poolTokensWithoutBpt = txInput.poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ).filter((_, index) => index !== bptIndex); - - const amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); - // Query should use same amountsIn as user sets - expect(queryResult.amountsIn.filter((_, index)=>index!==bptIndex)).to.deep.eq(amountsIn); - expect(queryResult.tokenInIndex).to.be.undefined; - - // Should be no native value - expect(value).toBeUndefined; - - // Expect some bpt amount - expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn.filter((_, index)=>index!==bptIndex)); - }); - - test('native asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - - const poolTokensWithoutBpt = txInput.poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ).filter((_, index) => index !== bptIndex); - - const amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - useNativeAssetAsWrappedAmountIn: true, - }; - - // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - checkNativeBalance: true, - }); - - // Query should use same amountsIn as user sets - expect(queryResult.amountsIn - .filter((_, index)=>index!==bptIndex) - .map((a) => a.amount)).to.deep.eq( - amountsIn.map((a) => a.amount), - ); - expect(queryResult.tokenInIndex).to.be.undefined; - // Should have native value equal to input amount - expect(value).eq(amountsIn[1].amount); - - // Expect some bpt amount - expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn.filter((_, index)=>index!==bptIndex)); - }); - - test('single asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; - - // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - bptOut, - tokenIn, - chainId, - rpcUrl, - kind: JoinKind.SingleAsset, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - // We only expect single asset to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; - queryResult.amountsIn.filter((_, index) => index!==bptIndex) - .forEach((a, i) => { - if (i === queryResult.tokenInIndex) - expect(a.amount > BigInt(0)).to.be.true; - else expect(a.amount === BigInt(0)).to.be.true; + let txInput: TxInput; + let bptToken: Token; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + const poolInput = await api.getPool(poolId); + + const client: Client = createTestClient({ + mode: 'hardhat', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions) as Client; + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig + joinInput: {} as JoinInput, + checkNativeBalance: false, + }; + + // setup BPT token + bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); - // Should be no native value - expect(value).toBeUndefined; - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); - }); - - test('proportional join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const bptIndex = txInput.poolInput.tokens.findIndex((t) => t.address === txInput.poolInput.address); - - // perform join query to get expected bpt out - const joinInput: ProportionalJoinInput = { - bptOut, - chainId, - rpcUrl, - kind: JoinKind.Proportional, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); - - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - // Expect all assets to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; - queryResult.amountsIn.filter((_, index) => index!==bptIndex) - .forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [...txInput.poolInput.tokens.map((t) => t.address)], + [0, 0, 3], + [ + ...txInput.poolInput.tokens.map((t) => + parseUnits('100', t.decimals), + ), + ], + process.env.ETHEREUM_RPC_URL as string, + blockNumber, + ); }); - expect(queryResult.tokenInIndex).toBeUndefined; - - // Should be no native value - expect(value).toBeUndefined; - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); - }); - - async function doTransaction(txIp: TxInput) { - const { - poolJoin, - poolInput, - joinInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolJoin.query(joinInput, poolInput); - const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr], - client, - testAddress, - to, - call, - value, - ); - const bptIndex = poolInput.tokens.findIndex((t) => t.address === poolInput.address); - expect(transactionReceipt.status).to.eq('success'); - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsIn.slice(0, bptIndex).map((a)=>a.amount), - queryResult.bptOut.amount, - ...queryResult.amountsIn.slice(bptIndex+1).map((a)=>a.amount) - ] - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxAmountsIn, - minBptOut, - value, - }; - } + + test('unbalanced join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const poolTokensWithoutBpt = txInput.poolInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + .filter((_, index) => index !== bptIndex); + + const amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + // Query should use same amountsIn as user sets + expect( + queryResult.amountsIn.filter((_, index) => index !== bptIndex), + ).to.deep.eq(amountsIn); + expect(queryResult.tokenInIndex).to.be.undefined; + + // Should be no native value + expect(value).toBeUndefined; + + // Expect some bpt amount + expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq( + maxAmountsIn.filter((_, index) => index !== bptIndex), + ); + }); + + test('native asset join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + + const poolTokensWithoutBpt = txInput.poolInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + .filter((_, index) => index !== bptIndex); + + const amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + useNativeAssetAsWrappedAmountIn: true, + }; + + // We have to use zero address for balanceDeltas + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + checkNativeBalance: true, + }); + + // Query should use same amountsIn as user sets + expect( + queryResult.amountsIn + .filter((_, index) => index !== bptIndex) + .map((a) => a.amount), + ).to.deep.eq(amountsIn.map((a) => a.amount)); + expect(queryResult.tokenInIndex).to.be.undefined; + // Should have native value equal to input amount + expect(value).eq(amountsIn[1].amount); + + // Expect some bpt amount + expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; + + // Confirm slippage - only bpt out + const expectedMinBpt = txInput.slippage.removeFrom( + queryResult.bptOut.amount, + ); + expect(expectedMinBpt).to.deep.eq(minBptOut); + const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); + expect(expectedMaxAmountsIn).to.deep.eq( + maxAmountsIn.filter((_, index) => index !== bptIndex), + ); + }); + + test('single asset join', async () => { + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; + + // perform join query to get expected bpt out + const joinInput: SingleAssetJoinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // We only expect single asset to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn + .filter((_, index) => index !== bptIndex) + .forEach((a, i) => { + if (i === queryResult.tokenInIndex) + expect(a.amount > BigInt(0)).to.be.true; + else expect(a.amount === BigInt(0)).to.be.true; + }); + + // Should be no native value + expect(value).toBeUndefined; + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + txInput.slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); + + test('proportional join', async () => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptIndex = txInput.poolInput.tokens.findIndex( + (t) => t.address === txInput.poolInput.address, + ); + + // perform join query to get expected bpt out + const joinInput: ProportionalJoinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + + const { queryResult, maxAmountsIn, minBptOut, value } = + await doTransaction({ + ...txInput, + joinInput, + }); + + // Query should use same bpt out as user sets + expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); + + // Expect all assets to have a value for amount in + expect(queryResult.tokenInIndex).toBeDefined; + queryResult.amountsIn + .filter((_, index) => index !== bptIndex) + .forEach((a) => { + expect(a.amount > BigInt(0)).to.be.true; + }); + expect(queryResult.tokenInIndex).toBeUndefined; + + // Should be no native value + expect(value).toBeUndefined; + + // Confirm slippage - only to amount in not bpt out + const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => + txInput.slippage.applyTo(a.amount), + ); + expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + expect(minBptOut).to.eq(bptOut.amount); + }); + + async function doTransaction(txIp: TxInput) { + const { + poolJoin, + poolInput, + joinInput, + testAddress, + client, + slippage, + checkNativeBalance, + } = txIp; + const queryResult = await poolJoin.query(joinInput, poolInput); + const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( + { + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }, + ); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolTokensAddr], + client, + testAddress, + to, + call, + value, + ); + const bptIndex = poolInput.tokens.findIndex( + (t) => t.address === poolInput.address, + ); + expect(transactionReceipt.status).to.eq('success'); + // Confirm final balance changes match query result + const expectedDeltas = [ + ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), + queryResult.bptOut.amount, + ...queryResult.amountsIn.slice(bptIndex + 1).map((a) => a.amount), + ]; + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxAmountsIn, + minBptOut, + value, + }; + } }); /*********************** Mock To Represent API Requirements **********************/ export class MockApi { - public async getPool(id: Hex): Promise { - const tokens = [ - { - address: - '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76' as Address, // vETH/WETH BPT - decimals: 18, - index: 0, - }, - { - address: - '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f' as Address, // VETH - decimals: 18, - index: 1, - }, - { - address: - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH - decimals: 18, - index: 2, - }, - ]; - - return { - id, - address: getPoolAddress(id) as Address, - type: 'PHANTOM_STABLE', - tokens, - }; - } + public async getPool(id: Hex): Promise { + const tokens = [ + { + address: + '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76' as Address, // vETH/WETH BPT + decimals: 18, + index: 0, + }, + { + address: + '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f' as Address, // VETH + decimals: 18, + index: 1, + }, + { + address: + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' as Address, // WETH + decimals: 18, + index: 2, + }, + ]; + + return { + id, + address: getPoolAddress(id) as Address, + type: 'PHANTOM_STABLE', + tokens, + }; + } } /******************************************************************************/ From 357d8bebec3882de30c4d9749c6db2c63f35ad2c Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 10:55:01 +0200 Subject: [PATCH 107/199] Debug: exclude weighted integration tests --- vitest.config.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vitest.config.ts b/vitest.config.ts index 65bf4f71..d062e415 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,9 +3,11 @@ import { defineConfig } from 'vitest/config'; export default defineConfig(() => { return { test: { - testTimeout: 10_000, - hookTimeout: 20_000, + testTimeout: 20_000, + hookTimeout: 30_000, globalSetup: ['./test/anvil/anvil-global-setup.ts'], + // Uncomment to debug suite excluding some tests + exclude: ['test/*weighted*.integration.*', 'node_modules', 'dist'], }, }; }); From be6bf80f16bc92355de34bd44ab9501149055f5d Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 13:33:45 +0200 Subject: [PATCH 108/199] add threads false to test setup --- vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.ts b/vitest.config.ts index d062e415..5e3d26dc 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -8,6 +8,7 @@ export default defineConfig(() => { globalSetup: ['./test/anvil/anvil-global-setup.ts'], // Uncomment to debug suite excluding some tests exclude: ['test/*weighted*.integration.*', 'node_modules', 'dist'], + threads: false, }, }; }); From 1ff65fd1818e64c979b9d556838346c8c7c4d419 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 13:53:02 +0200 Subject: [PATCH 109/199] Avoid viem prefix in CI envs --- .github/workflows/checks.yml | 5 +++-- test/sor.test.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index bf53fb5e..522f727a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -46,8 +46,9 @@ jobs: - name: Test run: pnpm test:ci env: - VITE_ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} - VITE_POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} + ETHEREUM_RPC_URL: ${{ secrets.ETHEREUM_RPC_URL }} + POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }} + # TODO: add FANTOM and ARBITRUM secrets build: name: Build needs: install diff --git a/test/sor.test.ts b/test/sor.test.ts index 2ac4e9a5..2b001042 100644 --- a/test/sor.test.ts +++ b/test/sor.test.ts @@ -20,7 +20,8 @@ describe('SmartOrderRouter', () => { describe('Mainnet', () => { const chainId = ChainId.MAINNET; const rpcUrl = - process.env['ETHEREUM_RPC_URL'] || 'https://eth.llamarpc.com'; + process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com'; + const subgraphPoolDataService = new SubgraphPoolProvider(chainId); const onChainPoolDataEnricher = new OnChainPoolDataEnricher( chainId, From 1e7c4943d492db076d6bef0131c3a665f5f912a5 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 14:02:51 +0200 Subject: [PATCH 110/199] Clean integration tests --- test/anvil/anvil-global-setup.ts | 50 +++++++++++++-------------- test/weightedExit.integration.test.ts | 5 +-- test/weightedJoin.integration.test.ts | 3 -- vitest.config.ts | 15 +++++++- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index c0299013..2e723ff0 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -1,37 +1,35 @@ -/// - import { CreateAnvilOptions, createAnvil } from '@viem/anvil'; import { sleep } from '../lib/utils/promises'; -export let forkUrl: string; -if (import.meta.env.VITE_ETHEREUM_RPC_URL) { - forkUrl = import.meta.env.VITE_ETHEREUM_RPC_URL; -} else { - forkUrl = 'https://cloudflare-eth.com'; - console.warn( - `\`VITE_ETHEREUM_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`, - ); -} - -const port = 8545; - -const anvilOptions: CreateAnvilOptions = { - forkUrl, - port, - forkBlockNumber: 18043296n, -}; - -// https://www.npmjs.com/package/@viem/anvil -export const anvil = createAnvil(anvilOptions); - -export default async function startAnvil() { - if (import.meta.env.VITE_SKIP_GLOBAL_SETUP === 'true') { +export default async function () { + let forkUrl: string; + if (process.env.ETHEREUM_RPC_URL) { + forkUrl = process.env.ETHEREUM_RPC_URL; + } else { + forkUrl = 'https://cloudflare-eth.com'; + console.warn( + `\`ETHEREUM_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`, + ); + } + const port = 8545; + const forkBlockNumber = 18043296n; + // https://www.npmjs.com/package/@viem/anvil + const anvilOptions: CreateAnvilOptions = { + forkUrl, + port, + forkBlockNumber, + }; + const anvil = createAnvil(anvilOptions); + if (process.env.SKIP_GLOBAL_SETUP === 'true') { console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8555 --fork-block-number=17878719 `); await sleep(5000); return; } - console.log('🛠️ Starting anvil', anvilOptions); + console.log('🛠️ Starting anvil', { + port, + forkBlockNumber, + }); return await anvil.start(); } diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 103a3788..d39f52cb 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -46,7 +46,6 @@ type TxInput = { const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; -const blockNumber = 18043296n; const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH @@ -59,7 +58,7 @@ describe('weighted exit test', () => { // get pool state from api const poolInput = await api.getPool(poolId); - + const client = createTestClient({ mode: 'anvil', chain: CHAINS[chainId], @@ -87,8 +86,6 @@ describe('weighted exit test', () => { [txInput.poolInput.address], undefined, // TODO: hardcode these values to improve test performance [parseUnits('1', 18)], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, ); }); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index e96f99f2..47cce72e 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -47,7 +47,6 @@ type TxInput = { const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; -const blockNumber = 18043296n; const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH @@ -99,8 +98,6 @@ describe('weighted join test', () => { ), parseUnits('100', 18), ], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, ); }); diff --git a/vitest.config.ts b/vitest.config.ts index 5e3d26dc..18410bdb 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,7 +1,20 @@ import { defineConfig } from 'vitest/config'; +import { loadEnv } from 'vite'; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ''); -export default defineConfig(() => { return { + define: { + 'process.env.ETHEREUM_RPC_URL': JSON.stringify( + env.ETHEREUM_RPC_URL, + ), + 'process.env.POLYGON_RPC_URL': JSON.stringify(env.POLYGON_RPC_URL), + 'process.env.ARBITRUM_RPC_URL': JSON.stringify( + env.ARBITRUM_RPC_URL, + ), + 'process.env.FANTOM_RPC_URL': JSON.stringify(env.FANTOM_RPC_URL), + }, test: { testTimeout: 20_000, hookTimeout: 30_000, From 18151a8aacdd62b11757287693aadf17a16c9e47 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 16:53:55 +0200 Subject: [PATCH 111/199] Add weighted tests back --- vitest.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vitest.config.ts b/vitest.config.ts index 18410bdb..c321bcc0 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -20,7 +20,8 @@ export default defineConfig(({ mode }) => { hookTimeout: 30_000, globalSetup: ['./test/anvil/anvil-global-setup.ts'], // Uncomment to debug suite excluding some tests - exclude: ['test/*weighted*.integration.*', 'node_modules', 'dist'], + // exclude: ['test/*weighted*.integration.*', 'node_modules', 'dist'], + // Avoid testing threads to avoid problems with shared fork data threads: false, }, }; From 89ded1b2488d1eb25d194b48d46c9ccb19bc79e7 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 17:01:28 +0200 Subject: [PATCH 112/199] Bump viem and vitest --- package.json | 8 +- pnpm-lock.yaml | 293 ++++++++++++++++++------------------------------- 2 files changed, 109 insertions(+), 192 deletions(-) diff --git a/package.json b/package.json index 833ffd8e..ee3183d6 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "typings": "dist/index.d.ts", - "files": [ - "dist/" - ], + "files": ["dist/"], "scripts": { "build": "tsup", "format": "rome format .", @@ -30,7 +28,7 @@ "async-retry": "^1.3.3", "decimal.js-light": "^2.5.1", "pino": "^8.11.0", - "viem": "^1.9.3" + "viem": "^1.16.6" }, "devDependencies": { "@changesets/cli": "^2.26.1", @@ -44,7 +42,7 @@ "tsup": "^6.6.0", "typescript": "^5.0.4", "vite": "^4.4.2", - "vitest": "~0.30.1" + "vitest": "^0.34.6" }, "packageManager": "^pnpm@8.6.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17a59130..c3fd7201 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ dependencies: specifier: ^8.11.0 version: 8.11.0 viem: - specifier: ^1.9.3 - version: 1.9.3(typescript@5.1.3) + specifier: ^1.16.6 + version: 1.16.6(typescript@5.1.3) devDependencies: '@changesets/cli': @@ -53,13 +53,13 @@ devDependencies: specifier: ^4.4.2 version: 4.4.7(@types/node@18.15.11) vitest: - specifier: ~0.30.1 - version: 0.30.1 + specifier: ^0.34.6 + version: 0.34.6 packages: - /@adraffy/ens-normalize@1.9.0: - resolution: {integrity: sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==} + /@adraffy/ens-normalize@1.9.4: + resolution: {integrity: sha512-UK0bHA7hh9cR39V+4gl2/NnBBjoXIxkuWAPCaY4X7fbH4L/azIi7ilWOCjMUYfpJgraLUAqkRi2BqrjME8Rynw==} dev: false /@babel/code-frame@7.21.4: @@ -677,6 +677,13 @@ packages: dev: true optional: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -713,24 +720,14 @@ packages: read-yaml-file: 1.1.0 dev: true - /@noble/curves@1.0.0: - resolution: {integrity: sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==} - dependencies: - '@noble/hashes': 1.3.0 - dev: false - - /@noble/curves@1.1.0: - resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} + /@noble/curves@1.2.0: + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} dependencies: - '@noble/hashes': 1.3.1 - dev: false - - /@noble/hashes@1.3.0: - resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} + '@noble/hashes': 1.3.2 dev: false - /@noble/hashes@1.3.1: - resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} dev: false @@ -807,21 +804,29 @@ packages: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} dev: false - /@scure/bip32@1.3.0: - resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==} + /@scure/base@1.1.3: + resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} + dev: false + + /@scure/bip32@1.3.2: + resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==} dependencies: - '@noble/curves': 1.0.0 - '@noble/hashes': 1.3.0 - '@scure/base': 1.1.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.3 dev: false - /@scure/bip39@1.2.0: - resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} + /@scure/bip39@1.2.1: + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} dependencies: - '@noble/hashes': 1.3.0 + '@noble/hashes': 1.3.2 '@scure/base': 1.1.1 dev: false + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -870,6 +875,7 @@ packages: /@types/node@18.15.11: resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} + dev: true /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -883,12 +889,6 @@ packages: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true - /@types/ws@8.5.5: - resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} - dependencies: - '@types/node': 18.15.11 - dev: false - /@viem/anvil@0.0.6: resolution: {integrity: sha512-OjKR/+FVwzuygXYFqP8MBal1SXG8bT2gbZwqqB0XuLw81LNBBvmE/Repm6+5kkBh4IUj0PhYdrqOsnayS14Gtg==} dependencies: @@ -902,47 +902,46 @@ packages: - utf-8-validate dev: true - /@vitest/expect@0.30.1: - resolution: {integrity: sha512-c3kbEtN8XXJSeN81iDGq29bUzSjQhjES2WR3aColsS4lPGbivwLtas4DNUe0jD9gg/FYGIteqOenfU95EFituw==} + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} dependencies: - '@vitest/spy': 0.30.1 - '@vitest/utils': 0.30.1 - chai: 4.3.7 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.3.10 dev: true - /@vitest/runner@0.30.1: - resolution: {integrity: sha512-W62kT/8i0TF1UBCNMRtRMOBWJKRnNyv9RrjIgdUryEe0wNpGZvvwPDLuzYdxvgSckzjp54DSpv1xUbv4BQ0qVA==} + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} dependencies: - '@vitest/utils': 0.30.1 - concordance: 5.0.4 + '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.1 dev: true - /@vitest/snapshot@0.30.1: - resolution: {integrity: sha512-fJZqKrE99zo27uoZA/azgWyWbFvM1rw2APS05yB0JaLwUIg9aUtvvnBf4q7JWhEcAHmSwbrxKFgyBUga6tq9Tw==} + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: magic-string: 0.30.1 pathe: 1.1.1 - pretty-format: 27.5.1 + pretty-format: 29.7.0 dev: true - /@vitest/spy@0.30.1: - resolution: {integrity: sha512-YfJeIf37GvTZe04ZKxzJfnNNuNSmTEGnla2OdL60C8od16f3zOfv9q9K0nNii0NfjDJRt/CVN/POuY5/zTS+BA==} + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} dependencies: tinyspy: 2.1.1 dev: true - /@vitest/utils@0.30.1: - resolution: {integrity: sha512-/c8Xv2zUVc+rnNt84QF0Y0zkfxnaGhp87K2dYJMLtLOIckPzuxLVzAtFCicGFdB4NeBHNzTRr1tNn7rCtQcWFA==} + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: - concordance: 5.0.4 + diff-sequences: 29.6.3 loupe: 2.3.6 - pretty-format: 27.5.1 + pretty-format: 29.7.0 dev: true - /abitype@0.9.3(typescript@5.1.3): - resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==} + /abitype@0.9.8(typescript@5.1.3): + resolution: {integrity: sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ==} peerDependencies: typescript: '>=5.0.4' zod: ^3 >=3.19.1 @@ -1088,10 +1087,6 @@ packages: engines: {node: '>=8'} dev: true - /blueimp-md5@2.19.0: - resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} - dev: true - /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1160,14 +1155,14 @@ packages: engines: {node: '>=6'} dev: true - /chai@4.3.7: - resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 - check-error: 1.0.2 + check-error: 1.0.3 deep-eql: 4.1.3 - get-func-name: 2.0.0 + get-func-name: 2.0.2 loupe: 2.3.6 pathval: 1.1.1 type-detect: 4.0.8 @@ -1194,8 +1189,10 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true - /check-error@1.0.2: - resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 dev: true /chokidar@3.5.3: @@ -1274,20 +1271,6 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /concordance@5.0.4: - resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} - engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'} - dependencies: - date-time: 3.1.0 - esutils: 2.0.3 - fast-diff: 1.3.0 - js-string-escape: 1.0.1 - lodash: 4.17.21 - md5-hex: 3.0.1 - semver: 7.3.8 - well-known-symbols: 2.0.0 - dev: true - /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true @@ -1331,13 +1314,6 @@ packages: stream-transform: 2.1.3 dev: true - /date-time@3.1.0: - resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} - engines: {node: '>=6'} - dependencies: - time-zone: 1.0.0 - dev: true - /dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: true @@ -1397,6 +1373,11 @@ packages: engines: {node: '>=8'} dev: true + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -1577,11 +1558,6 @@ packages: hasBin: true dev: true - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -1641,10 +1617,6 @@ packages: resolution: {integrity: sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==} dev: true - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true - /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -1770,8 +1742,8 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true - /get-func-name@2.0.0: - resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true /get-intrinsic@1.2.1: @@ -2156,12 +2128,12 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /isomorphic-ws@5.0.0(ws@8.12.0): - resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + /isows@1.0.3(ws@8.13.0): + resolution: {integrity: sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg==} peerDependencies: ws: '*' dependencies: - ws: 8.12.0 + ws: 8.13.0 dev: false /joycon@3.1.1: @@ -2169,11 +2141,6 @@ packages: engines: {node: '>=10'} dev: true - /js-string-escape@1.0.1: - resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} - engines: {node: '>= 0.8'} - dev: true - /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true @@ -2261,14 +2228,10 @@ packages: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} dev: true - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true - /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: - get-func-name: 2.0.0 + get-func-name: 2.0.2 dev: true /lru-cache@4.1.5: @@ -2278,13 +2241,6 @@ packages: yallist: 2.1.2 dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - dev: true - /magic-string@0.30.1: resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==} engines: {node: '>=12'} @@ -2306,13 +2262,6 @@ packages: engines: {node: '>=8'} dev: true - /md5-hex@3.0.1: - resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} - engines: {node: '>=8'} - dependencies: - blueimp-md5: 2.19.0 - dev: true - /meow@6.1.1: resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} engines: {node: '>=8'} @@ -2726,13 +2675,13 @@ packages: hasBin: true dev: true - /pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - ansi-regex: 5.0.1 + '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 - react-is: 17.0.2 + react-is: 18.2.0 dev: true /process-warning@2.1.0: @@ -2772,8 +2721,8 @@ packages: engines: {node: '>=8'} dev: true - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true /read-pkg-up@7.0.1: @@ -2959,14 +2908,6 @@ packages: hasBin: true dev: true - /semver@7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true @@ -3039,11 +2980,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true - /source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -3238,17 +3174,12 @@ packages: real-require: 0.2.0 dev: false - /time-zone@1.0.0: - resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==} - engines: {node: '>=4'} - dev: true - /tinybench@2.5.0: resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} dev: true - /tinypool@0.4.0: - resolution: {integrity: sha512-2ksntHOKf893wSAH4z/+JbPpi92esw8Gn9N2deXX+B0EO92hexAVI9GIZZPx7P5aYo5KULfeOSt3kMOmSOy6uA==} + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} engines: {node: '>=14.0.0'} dev: true @@ -3438,32 +3369,31 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /viem@1.9.3(typescript@5.1.3): - resolution: {integrity: sha512-5NcxPHkWCZIBYm8A8oOd77l2RQ/+VRm1p56mBxoRcZ+Mij5pA+5Y3cohz7MHt6gYmmoDWlLXUXxsdPLedeF2wg==} + /viem@1.16.6(typescript@5.1.3): + resolution: {integrity: sha512-jcWcFQ+xzIfDwexwPJRvCuCRJKEkK9iHTStG7mpU5MmuSBpACs4nATBDyXNFtUiyYTFzLlVEwWkt68K0nCSImg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: typescript: optional: true dependencies: - '@adraffy/ens-normalize': 1.9.0 - '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.0 - '@scure/bip32': 1.3.0 - '@scure/bip39': 1.2.0 - '@types/ws': 8.5.5 - abitype: 0.9.3(typescript@5.1.3) - isomorphic-ws: 5.0.0(ws@8.12.0) + '@adraffy/ens-normalize': 1.9.4 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/bip32': 1.3.2 + '@scure/bip39': 1.2.1 + abitype: 0.9.8(typescript@5.1.3) + isows: 1.0.3(ws@8.13.0) typescript: 5.1.3 - ws: 8.12.0 + ws: 8.13.0 transitivePeerDependencies: - bufferutil - utf-8-validate - zod dev: false - /vite-node@0.30.1(@types/node@18.15.11): - resolution: {integrity: sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg==} + /vite-node@0.34.6(@types/node@18.15.11): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: @@ -3520,8 +3450,8 @@ packages: fsevents: 2.3.3 dev: true - /vitest@0.30.1: - resolution: {integrity: sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==} + /vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} hasBin: true peerDependencies: @@ -3554,28 +3484,26 @@ packages: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 '@types/node': 18.15.11 - '@vitest/expect': 0.30.1 - '@vitest/runner': 0.30.1 - '@vitest/snapshot': 0.30.1 - '@vitest/spy': 0.30.1 - '@vitest/utils': 0.30.1 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 acorn: 8.10.0 acorn-walk: 8.2.0 cac: 6.7.14 - chai: 4.3.7 - concordance: 5.0.4 + chai: 4.3.10 debug: 4.3.4 local-pkg: 0.4.3 magic-string: 0.30.1 pathe: 1.1.1 picocolors: 1.0.0 - source-map: 0.6.1 std-env: 3.3.3 strip-literal: 1.0.1 tinybench: 2.5.0 - tinypool: 0.4.0 + tinypool: 0.7.0 vite: 4.4.7(@types/node@18.15.11) - vite-node: 0.30.1(@types/node@18.15.11) + vite-node: 0.34.6(@types/node@18.15.11) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -3597,11 +3525,6 @@ packages: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true - /well-known-symbols@2.0.0: - resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==} - engines: {node: '>=6'} - dev: true - /whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} dependencies: @@ -3690,8 +3613,8 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /ws@8.12.0: - resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} + /ws@8.13.0: + resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3729,10 +3652,6 @@ packages: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: true - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true - /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} From 8b7744a488ab459f1a1437aca030043b3e7e3c3e Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 19 Oct 2023 17:10:46 +0200 Subject: [PATCH 113/199] Keep old viem version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ee3183d6..b1066826 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "async-retry": "^1.3.3", "decimal.js-light": "^2.5.1", "pino": "^8.11.0", - "viem": "^1.16.6" + "viem": "^1.9.3" }, "devDependencies": { "@changesets/cli": "^2.26.1", From 7cf3fd5990a398fa90af97ccc4c3cdd3bdb41435 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Thu, 19 Oct 2023 18:28:44 -0300 Subject: [PATCH 114/199] Adapting doTransaction to a doJoin function usable for both pool types "Weighted" and "Composable Stable"; --- test/composableStableJoin.integration.test.ts | 117 ++++-------------- test/lib/utils/joinHelper.ts | 74 +++++++++++ test/lib/utils/types.ts | 13 ++ test/weightedJoin.integration.test.ts | 110 ++++------------ tsconfig.json | 2 +- 5 files changed, 130 insertions(+), 186 deletions(-) create mode 100644 test/lib/utils/joinHelper.ts create mode 100644 test/lib/utils/types.ts diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 9fa60b92..7a7fd622 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -9,9 +9,6 @@ import { http, parseUnits, publicActions, - PublicActions, - TestActions, - WalletActions, walletActions, } from 'viem'; @@ -23,7 +20,6 @@ import { Slippage, Token, TokenAmount, - replaceWrapped, Address, Hex, PoolStateInput, @@ -34,16 +30,8 @@ import { JoinInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; - -type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; - joinInput: JoinInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; -}; +import { JoinTxInput } from './lib/utils/types'; +import { doJoin } from './lib/utils/joinHelper'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -52,7 +40,7 @@ const poolId = '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool describe('composable stable join test', () => { - let txInput: TxInput; + let txInput: JoinTxInput; let bptToken: Token; beforeAll(async () => { @@ -78,6 +66,7 @@ describe('composable stable join test', () => { testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig joinInput: {} as JoinInput, checkNativeBalance: false, + chainId, }; // setup BPT token @@ -120,11 +109,10 @@ describe('composable stable join test', () => { kind: JoinKind.Unbalanced, }; - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + }); // Query should use same amountsIn as user sets expect( queryResult.amountsIn.filter((_, index) => index !== bptIndex), @@ -171,13 +159,11 @@ describe('composable stable join test', () => { }; // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - checkNativeBalance: true, - }); - + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + checkNativeBalance: true, + }); // Query should use same amountsIn as user sets expect( queryResult.amountsIn @@ -218,11 +204,10 @@ describe('composable stable join test', () => { kind: JoinKind.SingleAsset, }; - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + }); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -261,11 +246,10 @@ describe('composable stable join test', () => { kind: JoinKind.Proportional, }; - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + }); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -289,65 +273,6 @@ describe('composable stable join test', () => { expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); expect(minBptOut).to.eq(bptOut.amount); }); - - async function doTransaction(txIp: TxInput) { - const { - poolJoin, - poolInput, - joinInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolJoin.query(joinInput, poolInput); - const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr], - client, - testAddress, - to, - call, - value, - ); - const bptIndex = poolInput.tokens.findIndex( - (t) => t.address === poolInput.address, - ); - expect(transactionReceipt.status).to.eq('success'); - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), - queryResult.bptOut.amount, - ...queryResult.amountsIn.slice(bptIndex + 1).map((a) => a.amount), - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxAmountsIn, - minBptOut, - value, - }; - } }); /*********************** Mock To Represent API Requirements **********************/ diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts new file mode 100644 index 00000000..afbb5453 --- /dev/null +++ b/test/lib/utils/joinHelper.ts @@ -0,0 +1,74 @@ +import { replaceWrapped, Token } from '../../../src'; +import { sendTransactionGetBalances } from './helper'; +import { expect } from 'vitest'; +import { JoinTxInput } from './types'; + +export const doJoin = async (txInput: JoinTxInput) => { + const { + poolJoin, + poolInput, + joinInput, + testAddress, + client, + slippage, + checkNativeBalance, + chainId, + } = txInput; + const queryResult = await poolJoin.query(joinInput, poolInput); + const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const bptIndex = poolInput.tokens.findIndex( + (t) => t.address === poolInput.address, + ); + let tokensForBalanceCheck; // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + if (bptIndex >= 0) { + tokensForBalanceCheck = [...poolTokensAddr]; + } else { + tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; + } + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + tokensForBalanceCheck, + client, + testAddress, + to, + call, + value, + ); + expect(transactionReceipt.status).to.eq('success'); + let expectedDeltas; + if (bptIndex >= 0) { + expectedDeltas = [ + ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), + queryResult.bptOut.amount, + ...queryResult.amountsIn.slice(bptIndex + 1).map((a) => a.amount), + ]; + } else { + expectedDeltas = [ + ...queryResult.amountsIn.map((a) => a.amount), + queryResult.bptOut.amount, + ]; + } + // Confirm final balance changes match query result + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxAmountsIn, + minBptOut, + value, + }; +}; diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts new file mode 100644 index 00000000..74bb7219 --- /dev/null +++ b/test/lib/utils/types.ts @@ -0,0 +1,13 @@ +import { Client, PublicActions, TestActions, WalletActions } from "viem"; +import { Address, ChainId, JoinInput, PoolJoin, PoolStateInput, Slippage } from "../../../src"; + +export type JoinTxInput = { + client: Client & PublicActions & TestActions & WalletActions; + poolJoin: PoolJoin; + joinInput: JoinInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; + chainId: ChainId; +} \ No newline at end of file diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index afd6497a..44985a54 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -34,16 +34,8 @@ import { JoinInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; - -type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; - joinInput: JoinInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; -}; +import { doJoin } from './lib/utils/joinHelper'; +import { JoinTxInput } from './lib/utils/types'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -52,7 +44,7 @@ const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH describe('weighted join test', () => { - let txInput: TxInput; + let txInput: JoinTxInput; let bptToken: Token; beforeAll(async () => { @@ -78,6 +70,7 @@ describe('weighted join test', () => { testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig joinInput: {} as JoinInput, checkNativeBalance: false, + chainId, }; // setup BPT token @@ -120,11 +113,10 @@ describe('weighted join test', () => { kind: JoinKind.Unbalanced, }; - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + }); // Query should use same amountsIn as user sets expect(queryResult.amountsIn).to.deep.eq(amountsIn); @@ -163,12 +155,11 @@ describe('weighted join test', () => { }; // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - checkNativeBalance: true, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + checkNativeBalance: true, + }); // Query should use same amountsIn as user sets expect(queryResult.amountsIn.map((a) => a.amount)).to.deep.eq( @@ -203,11 +194,10 @@ describe('weighted join test', () => { kind: JoinKind.SingleAsset, }; - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + }); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -242,11 +232,10 @@ describe('weighted join test', () => { kind: JoinKind.Proportional, }; - const { queryResult, maxAmountsIn, minBptOut, value } = - await doTransaction({ - ...txInput, - joinInput, - }); + const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ + ...txInput, + joinInput, + }); // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); @@ -268,63 +257,6 @@ describe('weighted join test', () => { expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); expect(minBptOut).to.eq(bptOut.amount); }); - - async function doTransaction(txIp: TxInput) { - const { - poolJoin, - poolInput, - joinInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolJoin.query(joinInput, poolInput); - - const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr, poolInput.address], - client, - testAddress, - to, - call, - value, - ); - expect(transactionReceipt.status).to.eq('success'); - - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxAmountsIn, - minBptOut, - value, - }; - } }); /*********************** Mock To Represent API Requirements **********************/ diff --git a/tsconfig.json b/tsconfig.json index 21a749b5..a606299b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "include": ["src"], + "include": ["src", "test"], "compilerOptions": { "target": "ESNext", "module": "ESNext", From 72301a354228a60e3be2a6b678ec60533f6362d1 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Thu, 19 Oct 2023 19:07:37 -0300 Subject: [PATCH 115/199] Merging Weighted and ComposableStable doTransactions into one single doExit function; --- test/composableStableExit.integration.test.ts | 18 ++-- test/lib/utils/exitHelper.ts | 79 +++++++++++++++++ test/lib/utils/types.ts | 41 ++++++--- test/weightedExit.integration.test.ts | 86 ++----------------- 4 files changed, 122 insertions(+), 102 deletions(-) create mode 100644 test/lib/utils/exitHelper.ts diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index 0dc8de02..e489b589 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -33,16 +33,7 @@ import { ExitInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; - -type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolExit: PoolExit; - exitInput: ExitInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; -}; +import { ExitTxInput } from './lib/utils/types'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -51,7 +42,7 @@ const poolId = '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool describe('composable stable exit test', () => { - let txInput: TxInput; + let txInput: ExitTxInput; let bptToken: Token; beforeAll(async () => { // setup mock api @@ -60,13 +51,13 @@ describe('composable stable exit test', () => { // get pool state from api const poolInput = await api.getPool(poolId); - const client: Client = createTestClient({ + const client = createTestClient({ mode: 'hardhat', chain: CHAINS[chainId], transport: http(rpcUrl), }) .extend(publicActions) - .extend(walletActions) as Client; + .extend(walletActions); txInput = { client, @@ -76,6 +67,7 @@ describe('composable stable exit test', () => { testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig exitInput: {} as ExitInput, checkNativeBalance: false, + chainId, }; bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts new file mode 100644 index 00000000..bd248934 --- /dev/null +++ b/test/lib/utils/exitHelper.ts @@ -0,0 +1,79 @@ +import { ExitTxInput } from './types'; +import { replaceWrapped, Token } from '../../../src'; +import { sendTransactionGetBalances } from './helper'; +import { expect } from 'vitest'; + +export const doExit = async (txInput: ExitTxInput) => { + const { + poolExit, + poolInput, + exitInput, + testAddress, + client, + slippage, + checkNativeBalance, + chainId, + } = txInput; + const queryResult = await poolExit.query(exitInput, poolInput); + const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + const poolTokens = poolInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + + const bptIndex = poolInput.tokens.findIndex( + (t) => t.address === poolInput.address, + ); + // Replace with native asset if required + const poolTokensAddr = checkNativeBalance + ? replaceWrapped(poolTokens, chainId).map((t) => t.address) + : poolTokens.map((t) => t.address); + + let tokensForBalanceCheck; + if (bptIndex < 0) { + tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; + } else { + tokensForBalanceCheck = [...poolTokensAddr]; + } + + // send transaction and check balance changes + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + tokensForBalanceCheck, + client, + testAddress, + to, + call, + value, + ); + expect(transactionReceipt.status).to.eq('success'); + + let expectedDeltas; + + if (bptIndex < 0) { + expectedDeltas = [ + ...queryResult.amountsOut.map((a) => a.amount), + queryResult.bptIn.amount, + ]; + } else { + expectedDeltas = [ + ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), + queryResult.bptIn.amount, + ...queryResult.amountsOut.slice(bptIndex + 1).map((a) => a.amount), + ]; + } + + // Confirm final balance changes match query result + expect(expectedDeltas).to.deep.eq(balanceDeltas); + + return { + queryResult, + maxBptIn, + minAmountsOut, + }; +}; diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index 74bb7219..e8761457 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -1,13 +1,30 @@ -import { Client, PublicActions, TestActions, WalletActions } from "viem"; -import { Address, ChainId, JoinInput, PoolJoin, PoolStateInput, Slippage } from "../../../src"; +import { Client, PublicActions, TestActions, WalletActions } from 'viem'; +import { + Address, + ChainId, + ExitInput, + JoinInput, + PoolExit, + PoolJoin, + PoolStateInput, + Slippage, +} from '../../../src'; -export type JoinTxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; - joinInput: JoinInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; - chainId: ChainId; -} \ No newline at end of file +type TxInputBase = { + client: Client & PublicActions & TestActions & WalletActions; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; + chainId: ChainId; +}; + +export type JoinTxInput = TxInputBase & { + poolJoin: PoolJoin; + joinInput: JoinInput; +}; + +export type ExitTxInput = TxInputBase & { + poolExit: PoolExit; + exitInput: ExitInput; +}; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 50cad9d7..fe66049f 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -4,14 +4,10 @@ import dotenv from 'dotenv'; dotenv.config(); import { - Client, createTestClient, http, parseUnits, publicActions, - PublicActions, - TestActions, - WalletActions, walletActions, } from 'viem'; import { @@ -22,7 +18,6 @@ import { Slippage, Token, TokenAmount, - replaceWrapped, PoolStateInput, PoolExit, Address, @@ -32,17 +27,9 @@ import { getPoolAddress, ExitInput, } from '../src'; -import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; - -type TxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolExit: PoolExit; - exitInput: ExitInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; -}; +import { forkSetup } from './lib/utils/helper'; +import { doExit } from './lib/utils/exitHelper'; +import { ExitTxInput } from './lib/utils/types'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -51,7 +38,7 @@ const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH describe('weighted exit test', () => { - let txInput: TxInput; + let txInput: ExitTxInput; let bptToken: Token; beforeAll(async () => { // setup mock api @@ -76,6 +63,7 @@ describe('weighted exit test', () => { testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig exitInput: {} as ExitInput, checkNativeBalance: false, + chainId, }; bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); @@ -104,7 +92,7 @@ describe('weighted exit test', () => { tokenOut, kind: ExitKind.SINGLE_ASSET, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, }); @@ -138,7 +126,7 @@ describe('weighted exit test', () => { bptIn, kind: ExitKind.PROPORTIONAL, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, }); @@ -176,7 +164,7 @@ describe('weighted exit test', () => { amountsOut, kind: ExitKind.UNBALANCED, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, }); @@ -210,7 +198,7 @@ describe('weighted exit test', () => { }; // Note - checking native balance - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, checkNativeBalance: true, @@ -232,62 +220,6 @@ describe('weighted exit test', () => { expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); expect(maxBptIn).to.eq(bptIn.amount); }); - - async function doTransaction(txIp: TxInput) { - const { - poolExit, - poolInput, - exitInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolExit.query(exitInput, poolInput); - - const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr, poolInput.address], - client, - testAddress, - to, - call, - value, - ); - expect(transactionReceipt.status).to.eq('success'); - - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsOut.map((a) => a.amount), - queryResult.bptIn.amount, - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxBptIn, - minAmountsOut, - }; - } }); /*********************** Mock To Represent API Requirements **********************/ From ff2132dfc439deb9d0555e5061612e1e4cd2a454 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Thu, 19 Oct 2023 19:09:32 -0300 Subject: [PATCH 116/199] Fixing Lint --- test/composableStableExit.integration.test.ts | 74 ++----------------- 1 file changed, 6 insertions(+), 68 deletions(-) diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index e489b589..542629b8 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -4,14 +4,10 @@ import { config } from 'dotenv'; config(); import { - Client, createTestClient, http, parseUnits, publicActions, - PublicActions, - TestActions, - WalletActions, walletActions, } from 'viem'; import { @@ -22,7 +18,6 @@ import { Slippage, Token, TokenAmount, - replaceWrapped, PoolStateInput, PoolExit, Address, @@ -32,8 +27,9 @@ import { getPoolAddress, ExitInput, } from '../src'; -import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +import { forkSetup } from './lib/utils/helper'; import { ExitTxInput } from './lib/utils/types'; +import { doExit } from './lib/utils/exitHelper'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -99,7 +95,7 @@ describe('composable stable exit test', () => { tokenOut, kind: ExitKind.SINGLE_ASSET, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, }); @@ -138,7 +134,7 @@ describe('composable stable exit test', () => { bptIn, kind: ExitKind.PROPORTIONAL, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, }); @@ -181,7 +177,7 @@ describe('composable stable exit test', () => { amountsOut, kind: ExitKind.UNBALANCED, }; - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, }); @@ -223,7 +219,7 @@ describe('composable stable exit test', () => { }; // Note - checking native balance - const { queryResult, maxBptIn, minAmountsOut } = await doTransaction({ + const { queryResult, maxBptIn, minAmountsOut } = await doExit({ ...txInput, exitInput, checkNativeBalance: true, @@ -249,64 +245,6 @@ describe('composable stable exit test', () => { ); expect(maxBptIn).to.eq(bptIn.amount); }); - - async function doTransaction(txIp: TxInput) { - const { - poolExit, - poolInput, - exitInput, - testAddress, - client, - slippage, - checkNativeBalance, - } = txIp; - const queryResult = await poolExit.query(exitInput, poolInput); - const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall( - { - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }, - ); - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolTokensAddr], - client, - testAddress, - to, - call, - value, - ); - expect(transactionReceipt.status).to.eq('success'); - const bptIndex = poolInput.tokens.findIndex( - (t) => t.address === poolInput.address, - ); - // Confirm final balance changes match query result - const expectedDeltas = [ - ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), - queryResult.bptIn.amount, - ...queryResult.amountsOut.slice(bptIndex + 1).map((a) => a.amount), - ]; - expect(expectedDeltas).to.deep.eq(balanceDeltas); - - return { - queryResult, - maxBptIn, - minAmountsOut, - }; - } }); /*********************** Mock To Represent API Requirements **********************/ From 0cb5b957379f607f8289d4ff741f7110ab61f767 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Thu, 19 Oct 2023 19:11:09 -0300 Subject: [PATCH 117/199] Fixing Lint; --- test/composableStableJoin.integration.test.ts | 3 +-- test/weightedJoin.integration.test.ts | 7 +------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 7a7fd622..01aae107 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -4,7 +4,6 @@ import { config } from 'dotenv'; config(); import { - Client, createTestClient, http, parseUnits, @@ -29,7 +28,7 @@ import { PoolJoin, JoinInput, } from '../src'; -import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +import { forkSetup } from './lib/utils/helper'; import { JoinTxInput } from './lib/utils/types'; import { doJoin } from './lib/utils/joinHelper'; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 44985a54..4f80d4d8 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -4,14 +4,10 @@ import dotenv from 'dotenv'; dotenv.config(); import { - Client, createTestClient, http, parseUnits, publicActions, - PublicActions, - TestActions, - WalletActions, walletActions, } from 'viem'; @@ -23,7 +19,6 @@ import { Slippage, Token, TokenAmount, - replaceWrapped, Address, Hex, PoolStateInput, @@ -33,7 +28,7 @@ import { PoolJoin, JoinInput, } from '../src'; -import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +import { forkSetup } from './lib/utils/helper'; import { doJoin } from './lib/utils/joinHelper'; import { JoinTxInput } from './lib/utils/types'; From a5bc0da4ec1b0146246c466fd423cf8d69d304c8 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 20 Oct 2023 10:32:35 +0200 Subject: [PATCH 118/199] Fix pnpm loclkfile --- pnpm-lock.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3fd7201..01d6fe62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,7 +15,7 @@ dependencies: specifier: ^8.11.0 version: 8.11.0 viem: - specifier: ^1.16.6 + specifier: ^1.9.3 version: 1.16.6(typescript@5.1.3) devDependencies: @@ -800,10 +800,6 @@ packages: dev: true optional: true - /@scure/base@1.1.1: - resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} - dev: false - /@scure/base@1.1.3: resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} dev: false @@ -820,7 +816,7 @@ packages: resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} dependencies: '@noble/hashes': 1.3.2 - '@scure/base': 1.1.1 + '@scure/base': 1.1.3 dev: false /@sinclair/typebox@0.27.8: From e1b71c7ab1af58837bffd45b8ecf3c53c9b40dd2 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 22 Oct 2023 20:05:10 -0300 Subject: [PATCH 119/199] Changing "chainId" type to ChainId instead of number; clarifying query and variables parameters type on fetch function; --- .../providers/balancer-api/client/index.ts | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/data/providers/balancer-api/client/index.ts b/src/data/providers/balancer-api/client/index.ts index 40ac1ab3..a9502be8 100644 --- a/src/data/providers/balancer-api/client/index.ts +++ b/src/data/providers/balancer-api/client/index.ts @@ -1,22 +1,31 @@ +import { ChainId } from '../../../../utils'; + export class BalancerApiClient { - apiUrl: string; - chainId: number; - constructor(apiUrl: string, chainId: number) { - this.apiUrl = apiUrl; - this.chainId = chainId; - } + apiUrl: string; + chainId: ChainId; + constructor(apiUrl: string, chainId: number) { + this.apiUrl = apiUrl; + this.chainId = chainId; + } - async fetch(operationName: string, query: any, variables: any) { - const requestQuery = { - operationName, - query, - variables, - }; - const response = await fetch(this.apiUrl, { - method: 'post', - body: JSON.stringify(requestQuery), - headers: { 'Content-Type': 'application/json', ChainId: this.chainId.toString() }, - }); - return response.json(); - } + async fetch( + operationName: string, + query: string, + variables: { id: string }, + ) { + const requestQuery = { + operationName, + query, + variables, + }; + const response = await fetch(this.apiUrl, { + method: 'post', + body: JSON.stringify(requestQuery), + headers: { + 'Content-Type': 'application/json', + ChainId: this.chainId.toString(), + }, + }); + return response.json(); + } } From e9dde1ce1c07bc01161a80f533215094f467b3a3 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 22 Oct 2023 20:21:21 -0300 Subject: [PATCH 120/199] Removing unused types file; --- .../balancer-api/modules/pool-state/types.ts | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/data/providers/balancer-api/modules/pool-state/types.ts diff --git a/src/data/providers/balancer-api/modules/pool-state/types.ts b/src/data/providers/balancer-api/modules/pool-state/types.ts deleted file mode 100644 index 88f908da..00000000 --- a/src/data/providers/balancer-api/modules/pool-state/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Address } from "viem"; - -export type PoolState = { - id: Address; - address: Address; - type: string; - version: string; - tokens: { - address: Address; - decimals: number; - index: number; - }[]; -}; - From 8a3b8d6cfcf118c3c9b1b7cd2ba92e2f05fd27c8 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 22 Oct 2023 20:22:56 -0300 Subject: [PATCH 121/199] Updating pnpm-lock.yaml after merge; --- pnpm-lock.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 01d6fe62..1b4858ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,6 +43,9 @@ devDependencies: ts-node: specifier: ^10.9.1 version: 10.9.1(@types/node@18.15.11)(typescript@5.1.3) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 tsup: specifier: ^6.6.0 version: 6.7.0(ts-node@10.9.1)(typescript@5.1.3) @@ -2153,6 +2156,12 @@ packages: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true @@ -3249,6 +3258,15 @@ packages: yn: 3.1.1 dev: true + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tsup@6.7.0(ts-node@10.9.1)(typescript@5.1.3): resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} engines: {node: '>=14.18'} From b3128a7767c3c73b1e485268dd6c8cca993cc67d Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 22 Oct 2023 23:14:45 -0300 Subject: [PATCH 122/199] Adding comments in the doJoin function; --- test/composableStableJoin.integration.test.ts | 15 +++++---- test/lib/utils/joinHelper.ts | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 01aae107..49f69d90 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -119,7 +119,7 @@ describe('composable stable join test', () => { expect(queryResult.tokenInIndex).to.be.undefined; // Should be no native value - expect(value).toBeUndefined; + expect(value).to.be.undefined // Expect some bpt amount expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; @@ -211,7 +211,7 @@ describe('composable stable join test', () => { expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); // We only expect single asset to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; + expect(queryResult.tokenInIndex).to.not.be.undefined; queryResult.amountsIn .filter((_, index) => index !== bptIndex) .forEach((a, i) => { @@ -221,7 +221,7 @@ describe('composable stable join test', () => { }); // Should be no native value - expect(value).toBeUndefined; + expect(value).to.be.undefined; // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => @@ -252,18 +252,19 @@ describe('composable stable join test', () => { // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - + + console.log(queryResult.tokenInIndex); + // Expect all assets to have a value for amount in - expect(queryResult.tokenInIndex).toBeDefined; + expect(queryResult.tokenInIndex).to.be.undefined queryResult.amountsIn .filter((_, index) => index !== bptIndex) .forEach((a) => { expect(a.amount > BigInt(0)).to.be.true; }); - expect(queryResult.tokenInIndex).toBeUndefined; // Should be no native value - expect(value).toBeUndefined; + expect(value).to.be.undefined // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index afbb5453..cbcd1c31 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -3,6 +3,19 @@ import { sendTransactionGetBalances } from './helper'; import { expect } from 'vitest'; import { JoinTxInput } from './types'; +/** + * Helper function that sends a join transaction and check for balance deltas + * @param txInput + * @param poolJoin: PoolJoin - The pool join class, used to query the join and build the join call + * @param poolInput: PoolStateInput - The state of the pool being joined + * @param joinInput: JoinInput - The parameters of the join transaction, example: bptOut, amountsIn, etc. + * @param testAddress: Address - The address to send the transaction from + * @param client: Client & PublicActions & WalletActions - The RPC client + * @param slippage: Slippage - The slippage tolerance for the join transaction + * @param checkNativeBalance: boolean - Whether to check the native asset balance or not + * @param chainId: ChainId - The Chain Id of the pool being joined, for example: 1 for Mainnet, 137 for Polygon, etc. + */ + export const doJoin = async (txInput: JoinTxInput) => { const { poolJoin, @@ -32,9 +45,19 @@ export const doJoin = async (txInput: JoinTxInput) => { const poolTokensAddr = checkNativeBalance ? replaceWrapped(poolTokens, chainId).map((t) => t.address) : poolTokens.map((t) => t.address); + if (bptIndex >= 0) { + /* + * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr + * will already have the bpt address, so we don't need to add it again. + * */ tokensForBalanceCheck = [...poolTokensAddr]; } else { + /** + * If the pool type tokens list does not contains BPT (for example: Weighted), the poolTokensAddr + * will not have the bpt address, so we need to add it, so the balance delta of the bpt will be + * checked in the tests. + */ tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; } @@ -51,12 +74,22 @@ export const doJoin = async (txInput: JoinTxInput) => { expect(transactionReceipt.status).to.eq('success'); let expectedDeltas; if (bptIndex >= 0) { + /* + * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsIn + * includes the amount in of the bptIn, which is always 0, so we need to remove it from the + * expectedDeltas and put the bptOut.amount in its place. + */ expectedDeltas = [ ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), queryResult.bptOut.amount, ...queryResult.amountsIn.slice(bptIndex + 1).map((a) => a.amount), ]; } else { + /** + * If the pool type tokens list does not contains BPT (for example: Weighted), the queryResult.amountsIn + * does not include the amount of the bptIn, so we just need to add the bptOut.amount in the end + * of the expectedDeltas. + */ expectedDeltas = [ ...queryResult.amountsIn.map((a) => a.amount), queryResult.bptOut.amount, From 1e7392764544cf320f8672d2feec6ae475cf5d3b Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 22 Oct 2023 23:17:19 -0300 Subject: [PATCH 123/199] changing toBeDefined to to.not.be.undefined --- test/composableStableExit.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index 542629b8..62ae30d8 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -104,7 +104,7 @@ describe('composable stable exit test', () => { expect(queryResult.bptIn.amount).to.eq(bptIn.amount); // We only expect single asset to have a value for exit - expect(queryResult.tokenOutIndex).to.be.toBeDefined; + expect(queryResult.tokenOutIndex).to.not.be.undefined; queryResult.amountsOut .filter((_, index) => index !== bptIndex) .forEach((a, i) => { From 32541923e66e8d6b16219e5f0b41f2809ba25cec Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 22 Oct 2023 23:21:36 -0300 Subject: [PATCH 124/199] Adding comments for the doExit test helper function --- test/lib/utils/exitHelper.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index bd248934..adf9c4fc 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -3,6 +3,18 @@ import { replaceWrapped, Token } from '../../../src'; import { sendTransactionGetBalances } from './helper'; import { expect } from 'vitest'; +/** + * Helper function that sends a exit transaction and check for balance deltas + * @param txInput + * @param poolExit: PoolExit - The pool exit class, used to query the exit and build the exit call + * @param poolInput: PoolStateInput - The state of the pool being exited + * @param exitInput: ExitInput - The parameters of the exit transaction, example: bptIn, amountsOut, etc. + * @param testAddress: Address - The address to send the transaction from + * @param client: Client & PublicActions & WalletActions - The RPC client + * @param slippage: Slippage - The slippage tolerance for the exit transaction + * @param checkNativeBalance: boolean - Whether to check the native asset balance or not + * @param chainId: ChainId - The Chain Id of the pool being exited, for example: 1 for Mainnet, 137 for Polygon, etc. + */ export const doExit = async (txInput: ExitTxInput) => { const { poolExit, @@ -36,8 +48,17 @@ export const doExit = async (txInput: ExitTxInput) => { let tokensForBalanceCheck; if (bptIndex < 0) { + /** + * If the pool type tokens list does not contains BPT (for example: Weighted), the poolTokensAddr + * will not have the bpt address, so we need to add it, so the balance delta of the bpt will be + * checked in the tests. + */ tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; } else { + /* + * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr + * will already have the bpt address, so we don't need to add it again. + */ tokensForBalanceCheck = [...poolTokensAddr]; } @@ -56,11 +77,20 @@ export const doExit = async (txInput: ExitTxInput) => { let expectedDeltas; if (bptIndex < 0) { + /* + * If the pool type tokens list does not contains BPT (for example: Weighted), the queryResult.amountsOut + * does not include any amount out of bpt, so we just need to add the bptIn.amount to the expectedDeltas. + */ expectedDeltas = [ ...queryResult.amountsOut.map((a) => a.amount), queryResult.bptIn.amount, ]; } else { + /* + * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsOut + * includes the amount out of the bpt, which is always 0, so we need to remove it from the + * expectedDeltas and put the bptIn.amount in its place. + */ expectedDeltas = [ ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), queryResult.bptIn.amount, From e9b3b32939f103933d9ab82b74b7a7f87e70f179 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 23 Oct 2023 19:00:23 -0300 Subject: [PATCH 125/199] Changing examples to anvil; Changing chainId types to ChainId instead of number; Adding a resolution to install get-port in the version 5.1.2, to accept non ESM imports, since @viem/anvil import this package using require; --- examples/exit/weighted.ts | 9 ++++----- examples/join/weighted.ts | 13 +++++++------ package.json | 12 +++++++++--- pnpm-lock.yaml | 18 ++++++++++++++---- .../providers/balancer-api/client/index.ts | 11 ++--------- src/data/providers/balancer-api/index.ts | 3 ++- .../balancer-api/modules/pool-state/index.ts | 6 ++++-- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index 0f7e4a92..07901d23 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -21,20 +21,21 @@ import { walletActions } from "viem"; import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper"; +import anvilGlobalSetup from "../../test/anvil/anvil-global-setup"; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; -const blockNumber = BigInt(18043296); const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const slippage = Slippage.fromPercentage('1'); // 1% const exit = async () => { + await anvilGlobalSetup(); const balancerApi = new BalancerApi(balancerApiUrl, 1); const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); const client: Client & PublicActions & TestActions & WalletActions = createTestClient({ - mode: 'hardhat', + mode: 'anvil', chain: CHAINS[chainId], transport: http(rpcUrl), }) @@ -49,9 +50,7 @@ const exit = async () => { [0], [ parseUnits('100', 18), - ], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, + ] ); const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index 72d38c0e..f55af9ff 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -1,5 +1,5 @@ -import dotenv from 'dotenv'; -dotenv.config(); +import { config } from 'dotenv'; +config(); import { BalancerApi, @@ -23,6 +23,7 @@ import { walletActions } from "viem"; import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper"; +import anvilGlobalSetup from "../../test/anvil/anvil-global-setup"; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH @@ -33,10 +34,11 @@ const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DA const slippage = Slippage.fromPercentage('1'); // 1% const join = async () => { + await anvilGlobalSetup(); const balancerApi = new BalancerApi(balancerApiUrl, 1); const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); const client: Client & PublicActions & TestActions & WalletActions = createTestClient({ - mode: 'hardhat', + mode: 'anvil', chain: CHAINS[chainId], transport: http(rpcUrl), }) @@ -52,8 +54,6 @@ const join = async () => { ...poolState.tokens.map((t) => parseUnits('100', t.decimals)), parseUnits('100', 18), ], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, ); @@ -93,6 +93,7 @@ const join = async () => { ); console.log(`transaction status: ${transactionReceipt.status}`); console.log(`token amounts deltas per token: ${balanceDeltas}`); + return; } -join(); \ No newline at end of file +join().then(()=>{}); \ No newline at end of file diff --git a/package.json b/package.json index 9dd091f8..1add6c53 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "typings": "dist/index.d.ts", - "files": ["dist/"], + "files": [ + "dist/" + ], "scripts": { "build": "tsup", "format": "rome format .", @@ -22,7 +24,8 @@ "test": "vitest dev", "test:ci": "vitest run", "changeset": "changeset", - "changeset:release": "pnpm build && changeset publish" + "changeset:release": "pnpm build && changeset publish", + "example": "npx ts-node -P tsconfig.testing.json -r tsconfig-paths/register" }, "dependencies": { "async-retry": "^1.3.3", @@ -45,5 +48,8 @@ "vite": "^4.4.2", "vitest": "^0.34.6" }, - "packageManager": "^pnpm@8.6.0" + "packageManager": "^pnpm@8.6.0", + "resolutions": { + "get-port": "5.1.2" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b4858ef..23436a11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + get-port: 5.0.0 + dependencies: async-retry: specifier: ^1.3.3 @@ -892,7 +895,7 @@ packages: resolution: {integrity: sha512-OjKR/+FVwzuygXYFqP8MBal1SXG8bT2gbZwqqB0XuLw81LNBBvmE/Repm6+5kkBh4IUj0PhYdrqOsnayS14Gtg==} dependencies: execa: 7.2.0 - get-port: 6.1.2 + get-port: 5.0.0 http-proxy: 1.18.1 ws: 8.14.2 transitivePeerDependencies: @@ -1754,9 +1757,11 @@ packages: has-symbols: 1.0.3 dev: true - /get-port@6.1.2: - resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + /get-port@5.0.0: + resolution: {integrity: sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.3.1 dev: true /get-stream@6.0.1: @@ -3327,6 +3332,11 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@0.3.1: + resolution: {integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==} + engines: {node: '>=6'} + dev: true + /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} diff --git a/src/data/providers/balancer-api/client/index.ts b/src/data/providers/balancer-api/client/index.ts index a9502be8..aeb6218a 100644 --- a/src/data/providers/balancer-api/client/index.ts +++ b/src/data/providers/balancer-api/client/index.ts @@ -3,21 +3,14 @@ import { ChainId } from '../../../../utils'; export class BalancerApiClient { apiUrl: string; chainId: ChainId; - constructor(apiUrl: string, chainId: number) { + constructor(apiUrl: string, chainId: ChainId) { this.apiUrl = apiUrl; this.chainId = chainId; } async fetch( - operationName: string, - query: string, - variables: { id: string }, + requestQuery: {operationName?:string, query:string, variables?:any} ) { - const requestQuery = { - operationName, - query, - variables, - }; const response = await fetch(this.apiUrl, { method: 'post', body: JSON.stringify(requestQuery), diff --git a/src/data/providers/balancer-api/index.ts b/src/data/providers/balancer-api/index.ts index bc18ae52..a00216e3 100644 --- a/src/data/providers/balancer-api/index.ts +++ b/src/data/providers/balancer-api/index.ts @@ -1,5 +1,6 @@ import { Pools } from "./modules/pool-state"; import { BalancerApiClient } from "./client"; +import { ChainId } from "../../../utils"; export class BalancerApi { @@ -7,7 +8,7 @@ export class BalancerApi { pools: Pools; - constructor(balancerApiUrl: string, chainId: number){ + constructor(balancerApiUrl: string, chainId: ChainId){ this.balancerApiClient = new BalancerApiClient(balancerApiUrl, chainId); this.pools = new Pools(this.balancerApiClient); } diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index 38ca9403..3941bae0 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -80,9 +80,11 @@ export class Pools { async fetchPoolState(id: string): Promise { const { data: { poolGetPool }, - } = await this.balancerApiClient.fetch('GetPool', this.poolStateQuery, { + } = await this.balancerApiClient.fetch({ + query: this.poolStateQuery, + variables: { id, - }); + }}); return poolGetPool; } } From eee4ac144c9d32ce9daf2f29b4c87394cd6237d8 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 23 Oct 2023 19:10:06 -0300 Subject: [PATCH 126/199] removed unused blockNumber variable; --- examples/join/weighted.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index f55af9ff..5d660056 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -29,7 +29,6 @@ const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; -const blockNumber = BigInt(18043296); const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const slippage = Slippage.fromPercentage('1'); // 1% From 400f4baf16f69dd957c8d68b37568e7bafd44bec Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 23 Oct 2023 19:21:53 -0300 Subject: [PATCH 127/199] Adapting test for anvil; --- test/composableStableJoin.integration.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 49f69d90..2f98f27b 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -34,7 +34,6 @@ import { doJoin } from './lib/utils/joinHelper'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; -const blockNumber = BigInt(18043296); const poolId = '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool @@ -50,7 +49,7 @@ describe('composable stable join test', () => { const poolInput = await api.getPool(poolId); const client = createTestClient({ - mode: 'hardhat', + mode: 'anvil', chain: CHAINS[chainId], transport: http(rpcUrl), }) @@ -82,9 +81,7 @@ describe('composable stable join test', () => { ...txInput.poolInput.tokens.map((t) => parseUnits('100', t.decimals), ), - ], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, + ] ); }); From c368c94edbf2e0e6ca2c450814a2a74efd07c037 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 23 Oct 2023 19:23:15 -0300 Subject: [PATCH 128/199] Adapting test to anvil; --- test/composableStableExit.integration.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index 62ae30d8..f4968b9e 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -33,7 +33,6 @@ import { doExit } from './lib/utils/exitHelper'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; -const blockNumber = BigInt(18043296); const poolId = '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool @@ -48,7 +47,7 @@ describe('composable stable exit test', () => { const poolInput = await api.getPool(poolId); const client = createTestClient({ - mode: 'hardhat', + mode: 'anvil', chain: CHAINS[chainId], transport: http(rpcUrl), }) @@ -74,9 +73,7 @@ describe('composable stable exit test', () => { txInput.testAddress, [txInput.poolInput.address], undefined, // TODO: hardcode these values to improve test performance - [parseUnits('1000', 18)], - process.env.ETHEREUM_RPC_URL as string, - blockNumber, + [parseUnits('1000', 18)] ); }); From 7789d6d6a4e4d918691f1b5ca0418819ed6a110e Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 23 Oct 2023 19:28:00 -0300 Subject: [PATCH 129/199] Fixing get-port version and pnpm-lock file; --- package.json | 2 +- pnpm-lock.yaml | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 1add6c53..eac9bb04 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,6 @@ }, "packageManager": "^pnpm@8.6.0", "resolutions": { - "get-port": "5.1.2" + "get-port": "5.1.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23436a11..cbf0e776 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - get-port: 5.0.0 + get-port: 5.1.1 dependencies: async-retry: @@ -895,7 +895,7 @@ packages: resolution: {integrity: sha512-OjKR/+FVwzuygXYFqP8MBal1SXG8bT2gbZwqqB0XuLw81LNBBvmE/Repm6+5kkBh4IUj0PhYdrqOsnayS14Gtg==} dependencies: execa: 7.2.0 - get-port: 5.0.0 + get-port: 5.1.1 http-proxy: 1.18.1 ws: 8.14.2 transitivePeerDependencies: @@ -1757,11 +1757,9 @@ packages: has-symbols: 1.0.3 dev: true - /get-port@5.0.0: - resolution: {integrity: sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==} + /get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} - dependencies: - type-fest: 0.3.1 dev: true /get-stream@6.0.1: @@ -3332,11 +3330,6 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@0.3.1: - resolution: {integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==} - engines: {node: '>=6'} - dev: true - /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} From 82e507fa1606d1bb33b63997fbae4936d44a3f2e Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 24 Oct 2023 11:36:15 -0300 Subject: [PATCH 130/199] Running rome in the client/index.ts file; replacing ts-node by tsx; removing tsconfig-paths; removing resolutions; updating pnpm-lock.yaml; --- package.json | 8 ++------ pnpm-lock.yaml | 11 ++++------- src/data/providers/balancer-api/client/index.ts | 8 +++++--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index eac9bb04..561a2913 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "test:ci": "vitest run", "changeset": "changeset", "changeset:release": "pnpm build && changeset publish", - "example": "npx ts-node -P tsconfig.testing.json -r tsconfig-paths/register" + "example": "npx tsx" }, "dependencies": { "async-retry": "^1.3.3", @@ -42,14 +42,10 @@ "pino-pretty": "^10.0.0", "rome": "12.1.3", "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", "tsup": "^6.6.0", "typescript": "^5.0.4", "vite": "^4.4.2", "vitest": "^0.34.6" }, - "packageManager": "^pnpm@8.6.0", - "resolutions": { - "get-port": "5.1.1" - } + "packageManager": "^pnpm@8.6.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbf0e776..1b4858ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - get-port: 5.1.1 - dependencies: async-retry: specifier: ^1.3.3 @@ -895,7 +892,7 @@ packages: resolution: {integrity: sha512-OjKR/+FVwzuygXYFqP8MBal1SXG8bT2gbZwqqB0XuLw81LNBBvmE/Repm6+5kkBh4IUj0PhYdrqOsnayS14Gtg==} dependencies: execa: 7.2.0 - get-port: 5.1.1 + get-port: 6.1.2 http-proxy: 1.18.1 ws: 8.14.2 transitivePeerDependencies: @@ -1757,9 +1754,9 @@ packages: has-symbols: 1.0.3 dev: true - /get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} + /get-port@6.1.2: + resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true /get-stream@6.0.1: diff --git a/src/data/providers/balancer-api/client/index.ts b/src/data/providers/balancer-api/client/index.ts index aeb6218a..91cd0d2d 100644 --- a/src/data/providers/balancer-api/client/index.ts +++ b/src/data/providers/balancer-api/client/index.ts @@ -8,9 +8,11 @@ export class BalancerApiClient { this.chainId = chainId; } - async fetch( - requestQuery: {operationName?:string, query:string, variables?:any} - ) { + async fetch(requestQuery: { + operationName?: string; + query: string; + variables?: any; + }) { const response = await fetch(this.apiUrl, { method: 'post', body: JSON.stringify(requestQuery), From c5285e3378f44dad78d8a5099e157c4a90d4a0af Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 24 Oct 2023 19:24:49 -0300 Subject: [PATCH 131/199] Linting files; Adding separated function to assert join transaction; Adding bptIndex verification in the validateInputs function; --- .../composable-stable/composableStableJoin.ts | 5 +-- src/entities/join/utils/validateInputs.ts | 6 ++++ test/composableStableJoin.integration.test.ts | 12 +++---- test/lib/utils/joinHelper.ts | 33 ++++++++++++------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 305a7e81..4d4e9395 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -115,7 +115,7 @@ export class ComposableStableJoin implements BaseJoin { private getAmountsQuery( poolTokens: Token[], input: JoinInput, - bptIndex?: number, + bptIndex: number, ): AmountsJoin { switch (input.kind) { case JoinKind.Init: @@ -131,9 +131,6 @@ export class ComposableStableJoin implements BaseJoin { }; } case JoinKind.SingleAsset: { - if (bptIndex === undefined) { - throw new Error('bptIndex is necessary'); - } const tokenInIndex = poolTokens .filter((_, index) => index !== bptIndex) // Need to remove Bpt .findIndex((t) => t.isSameAddress(input.tokenIn)); diff --git a/src/entities/join/utils/validateInputs.ts b/src/entities/join/utils/validateInputs.ts index 733073c8..aa56ac1e 100644 --- a/src/entities/join/utils/validateInputs.ts +++ b/src/entities/join/utils/validateInputs.ts @@ -3,6 +3,12 @@ import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; export function validateInputs(input: JoinInput, poolState: PoolStateInput) { + const bptIndex = poolState.tokens.findIndex( + (t) => t.address === poolState.address, + ); + if (['PHANTOM_STABLE'].includes(poolState.type) && bptIndex < 0) { + throw new Error('Pool Tokens does not contain BPT'); + } switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 2f98f27b..88ff3d36 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -81,7 +81,7 @@ describe('composable stable join test', () => { ...txInput.poolInput.tokens.map((t) => parseUnits('100', t.decimals), ), - ] + ], ); }); @@ -116,7 +116,7 @@ describe('composable stable join test', () => { expect(queryResult.tokenInIndex).to.be.undefined; // Should be no native value - expect(value).to.be.undefined + expect(value).to.be.undefined; // Expect some bpt amount expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; @@ -249,11 +249,11 @@ describe('composable stable join test', () => { // Query should use same bpt out as user sets expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - + console.log(queryResult.tokenInIndex); - + // Expect all assets to have a value for amount in - expect(queryResult.tokenInIndex).to.be.undefined + expect(queryResult.tokenInIndex).to.be.undefined; queryResult.amountsIn .filter((_, index) => index !== bptIndex) .forEach((a) => { @@ -261,7 +261,7 @@ describe('composable stable join test', () => { }); // Should be no native value - expect(value).to.be.undefined + expect(value).to.be.undefined; // Confirm slippage - only to amount in not bpt out const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index cbcd1c31..e4b4aace 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -45,17 +45,17 @@ export const doJoin = async (txInput: JoinTxInput) => { const poolTokensAddr = checkNativeBalance ? replaceWrapped(poolTokens, chainId).map((t) => t.address) : poolTokens.map((t) => t.address); - + if (bptIndex >= 0) { /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr - * will already have the bpt address, so we don't need to add it again. - * */ + * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr + * will already have the bpt address, so we don't need to add it again. + * */ tokensForBalanceCheck = [...poolTokensAddr]; } else { /** * If the pool type tokens list does not contains BPT (for example: Weighted), the poolTokensAddr - * will not have the bpt address, so we need to add it, so the balance delta of the bpt will be + * will not have the bpt address, so we need to add it, so the balance delta of the bpt will be * checked in the tests. */ tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; @@ -71,14 +71,13 @@ export const doJoin = async (txInput: JoinTxInput) => { call, value, ); - expect(transactionReceipt.status).to.eq('success'); let expectedDeltas; if (bptIndex >= 0) { /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsIn - * includes the amount in of the bptIn, which is always 0, so we need to remove it from the + * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsIn + * includes the amount in of the bptIn, which is always 0, so we need to remove it from the * expectedDeltas and put the bptOut.amount in its place. - */ + */ expectedDeltas = [ ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), queryResult.bptOut.amount, @@ -96,8 +95,11 @@ export const doJoin = async (txInput: JoinTxInput) => { ]; } // Confirm final balance changes match query result - expect(expectedDeltas).to.deep.eq(balanceDeltas); - + assertJoinTransaction( + expectedDeltas, + balanceDeltas, + transactionReceipt.status, + ); return { queryResult, maxAmountsIn, @@ -105,3 +107,12 @@ export const doJoin = async (txInput: JoinTxInput) => { value, }; }; + +const assertJoinTransaction = ( + expectedDeltas: bigint[], + balanceDeltas: bigint[], + transactionReceiptStatus: string, +) => { + expect(expectedDeltas).to.deep.eq(balanceDeltas); + expect(transactionReceiptStatus).to.eq('success'); +}; From 40288dd638a3db84648f9846207d8ca7c5a47a21 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 24 Oct 2023 19:28:00 -0300 Subject: [PATCH 132/199] Moving assertJoinTransaction to the general helper file and renaming to assertTransaction; --- test/lib/utils/helper.ts | 13 +++++++++++++ test/lib/utils/joinHelper.ts | 18 ++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 5bdd6db5..22d5ae6e 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -17,6 +17,7 @@ import { } from 'viem'; import { erc20Abi } from '../../../src/abi'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../src/utils'; +import { expect } from 'vitest'; export const approveToken = async ( client: Client & PublicActions & WalletActions, @@ -298,3 +299,15 @@ export const forkSetup = async ( await approveToken(client, accountAddress, tokens[i]); } }; + +/* + * A function that asserts the transaction balance deltas and status + * */ +export const assertTransaction = ( + expectedDeltas: bigint[], + balanceDeltas: bigint[], + transactionReceiptStatus: string, +) => { + expect(expectedDeltas).to.deep.eq(balanceDeltas); + expect(transactionReceiptStatus).to.eq('success'); +}; diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index e4b4aace..4addd66a 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -1,6 +1,5 @@ import { replaceWrapped, Token } from '../../../src'; -import { sendTransactionGetBalances } from './helper'; -import { expect } from 'vitest'; +import { assertTransaction, sendTransactionGetBalances } from './helper'; import { JoinTxInput } from './types'; /** @@ -95,11 +94,7 @@ export const doJoin = async (txInput: JoinTxInput) => { ]; } // Confirm final balance changes match query result - assertJoinTransaction( - expectedDeltas, - balanceDeltas, - transactionReceipt.status, - ); + assertTransaction(expectedDeltas, balanceDeltas, transactionReceipt.status); return { queryResult, maxAmountsIn, @@ -107,12 +102,3 @@ export const doJoin = async (txInput: JoinTxInput) => { value, }; }; - -const assertJoinTransaction = ( - expectedDeltas: bigint[], - balanceDeltas: bigint[], - transactionReceiptStatus: string, -) => { - expect(expectedDeltas).to.deep.eq(balanceDeltas); - expect(transactionReceiptStatus).to.eq('success'); -}; From 7ad13df73ea82452c4e1697a3e825f03ed57b9f8 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 24 Oct 2023 19:34:18 -0300 Subject: [PATCH 133/199] Moving validateInputs to utils folder; Adding bptIndex verification in the validateInputs function; Linting files; Adding assertTransaction function in the exitHelper doExit function; --- src/entities/exit/poolExit.ts | 2 +- .../{weighted => utils}/validateInputs.ts | 6 ++++++ test/lib/utils/exitHelper.ts | 21 ++++++++----------- 3 files changed, 16 insertions(+), 13 deletions(-) rename src/entities/exit/{weighted => utils}/validateInputs.ts (77%) diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index a6cf885a..0ccf7e16 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -8,7 +8,7 @@ import { } from './types'; import { WeightedExit } from './weighted/weightedExit'; import { PoolStateInput } from '../types'; -import { validateInputs } from './weighted/validateInputs'; +import { validateInputs } from './utils/validateInputs'; import { getSortedTokens } from '../utils/getSortedTokens'; import { ComposableStableExit } from './composable-stable/composableStableExit'; diff --git a/src/entities/exit/weighted/validateInputs.ts b/src/entities/exit/utils/validateInputs.ts similarity index 77% rename from src/entities/exit/weighted/validateInputs.ts rename to src/entities/exit/utils/validateInputs.ts index aaf86f10..93e2b179 100644 --- a/src/entities/exit/weighted/validateInputs.ts +++ b/src/entities/exit/utils/validateInputs.ts @@ -3,6 +3,12 @@ import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; export function validateInputs(input: ExitInput, poolState: PoolStateInput) { + const bptIndex = poolState.tokens.findIndex( + (t) => t.address === poolState.address, + ); + if (['PHANTOM_STABLE'].includes(poolState.type) && bptIndex < 0) { + throw new Error('Pool Tokens does not contain BPT'); + } switch (input.kind) { case ExitKind.UNBALANCED: areTokensInArray( diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index adf9c4fc..7601944a 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -1,6 +1,6 @@ import { ExitTxInput } from './types'; import { replaceWrapped, Token } from '../../../src'; -import { sendTransactionGetBalances } from './helper'; +import { assertTransaction, sendTransactionGetBalances } from './helper'; import { expect } from 'vitest'; /** @@ -56,8 +56,8 @@ export const doExit = async (txInput: ExitTxInput) => { tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; } else { /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr - * will already have the bpt address, so we don't need to add it again. + * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr + * will already have the bpt address, so we don't need to add it again. */ tokensForBalanceCheck = [...poolTokensAddr]; } @@ -72,14 +72,12 @@ export const doExit = async (txInput: ExitTxInput) => { call, value, ); - expect(transactionReceipt.status).to.eq('success'); - let expectedDeltas; if (bptIndex < 0) { /* - * If the pool type tokens list does not contains BPT (for example: Weighted), the queryResult.amountsOut - * does not include any amount out of bpt, so we just need to add the bptIn.amount to the expectedDeltas. + * If the pool type tokens list does not contains BPT (for example: Weighted), the queryResult.amountsOut + * does not include any amount out of bpt, so we just need to add the bptIn.amount to the expectedDeltas. */ expectedDeltas = [ ...queryResult.amountsOut.map((a) => a.amount), @@ -87,10 +85,10 @@ export const doExit = async (txInput: ExitTxInput) => { ]; } else { /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsOut - * includes the amount out of the bpt, which is always 0, so we need to remove it from the + * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsOut + * includes the amount out of the bpt, which is always 0, so we need to remove it from the * expectedDeltas and put the bptIn.amount in its place. - */ + */ expectedDeltas = [ ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), queryResult.bptIn.amount, @@ -99,8 +97,7 @@ export const doExit = async (txInput: ExitTxInput) => { } // Confirm final balance changes match query result - expect(expectedDeltas).to.deep.eq(balanceDeltas); - + assertTransaction(expectedDeltas, balanceDeltas, transactionReceipt.status); return { queryResult, maxBptIn, From 097ac9e5fde2fca967c130c45075731d9e4d7d96 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 25 Oct 2023 13:00:37 +0100 Subject: [PATCH 134/199] refactor: Fix formatting. --- examples/exit/weighted.ts | 152 ++++++++-------- examples/join/weighted.ts | 166 ++++++++++-------- src/data/providers/balancer-api/index.ts | 24 ++- .../balancer-api/modules/pool-state/index.ts | 27 +-- 4 files changed, 192 insertions(+), 177 deletions(-) diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index 07901d23..e7e4650e 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -1,91 +1,99 @@ import dotenv from 'dotenv'; dotenv.config(); -import { BalancerApi } from "../../src/data/providers/balancer-api"; +import { BalancerApi } from '../../src/data/providers/balancer-api'; import { - ChainId, - CHAINS, ExitKind, PoolExit, PoolStateInput, - SingleAssetExitInput, - Slippage, - Token, - TokenAmount, -} from "../../src"; + ChainId, + CHAINS, + ExitKind, + PoolExit, + PoolStateInput, + SingleAssetExitInput, + Slippage, + Token, + TokenAmount, +} from '../../src'; import { - Client, - createTestClient, - http, - parseUnits, - PublicActions, - publicActions, - TestActions, WalletActions, - walletActions -} from "viem"; -import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper"; -import anvilGlobalSetup from "../../test/anvil/anvil-global-setup"; + Client, + createTestClient, + http, + parseUnits, + PublicActions, + publicActions, + TestActions, + WalletActions, + walletActions, +} from 'viem'; +import { + forkSetup, + sendTransactionGetBalances, +} from '../../test/lib/utils/helper'; +import anvilGlobalSetup from '../../test/anvil/anvil-global-setup'; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; -const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH +const poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const slippage = Slippage.fromPercentage('1'); // 1% const exit = async () => { - await anvilGlobalSetup(); - const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - const client: Client & PublicActions & TestActions & WalletActions = createTestClient({ - mode: 'anvil', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions); - const bpt = new Token(chainId, poolState.address, 18, 'BPT'); + await anvilGlobalSetup(); + const balancerApi = new BalancerApi(balancerApiUrl, 1); + const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState( + poolId, + ); + const client: Client & PublicActions & TestActions & WalletActions = + createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + const bpt = new Token(chainId, poolState.address, 18, 'BPT'); + + await forkSetup( + client, + testAddress, + [poolState.address], + [0], + [parseUnits('100', 18)], + ); + + const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); + const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL - await forkSetup( - client, - testAddress, - [poolState.address], - [0], - [ - parseUnits('100', 18), - ] - ); + const poolExit = new PoolExit(); - const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); - const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL - - const poolExit = new PoolExit(); - - const exitInput: SingleAssetExitInput = { - chainId, - rpcUrl, - bptIn, - tokenOut, - kind: ExitKind.SINGLE_ASSET, - }; + const exitInput: SingleAssetExitInput = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SINGLE_ASSET, + }; - const queryResult = await poolExit.query(exitInput, poolState); + const queryResult = await poolExit.query(exitInput, poolState); - const { call, to, value } = - poolExit.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, + const { call, to, value } = poolExit.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, }); - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolState.tokens.map(({address})=>address), bpt.address], - client, - testAddress, - to, - call, - value, - ); - console.log(`transaction status: ${transactionReceipt.status}`); - console.log(`token amounts deltas per token: ${balanceDeltas}`); -} + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [...poolState.tokens.map(({ address }) => address), bpt.address], + client, + testAddress, + to, + call, + value, + ); + console.log(`transaction status: ${transactionReceipt.status}`); + console.log(`token amounts deltas per token: ${balanceDeltas}`); +}; -exit(); \ No newline at end of file +exit(); diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index 5d660056..51b3dd42 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -2,97 +2,107 @@ import { config } from 'dotenv'; config(); import { - BalancerApi, - ChainId, - CHAINS, - JoinKind, - PoolJoin, PoolStateInput, - Slippage, - Token, - TokenAmount, - UnbalancedJoinInput -} from "../../src"; + BalancerApi, + ChainId, + CHAINS, + JoinKind, + PoolJoin, + PoolStateInput, + Slippage, + Token, + TokenAmount, + UnbalancedJoinInput, +} from '../../src'; import { - Client, - createTestClient, - http, - parseUnits, - PublicActions, - publicActions, - TestActions, WalletActions, - walletActions -} from "viem"; -import { forkSetup, sendTransactionGetBalances } from "../../test/lib/utils/helper"; -import anvilGlobalSetup from "../../test/anvil/anvil-global-setup"; + Client, + createTestClient, + http, + parseUnits, + PublicActions, + publicActions, + TestActions, + WalletActions, + walletActions, +} from 'viem'; +import { + forkSetup, + sendTransactionGetBalances, +} from '../../test/lib/utils/helper'; +import anvilGlobalSetup from '../../test/anvil/anvil-global-setup'; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; -const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH +const poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const slippage = Slippage.fromPercentage('1'); // 1% const join = async () => { - await anvilGlobalSetup(); - const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - const client: Client & PublicActions & TestActions & WalletActions = createTestClient({ - mode: 'anvil', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions); - - await forkSetup( - client, - testAddress, - [...poolState.tokens.map((t) => t.address), poolState.address], - [1,3,0], - [ - ...poolState.tokens.map((t) => parseUnits('100', t.decimals)), - parseUnits('100', 18), - ], - ); + await anvilGlobalSetup(); + const balancerApi = new BalancerApi(balancerApiUrl, 1); + const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState( + poolId, + ); + const client: Client & PublicActions & TestActions & WalletActions = + createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + await forkSetup( + client, + testAddress, + [...poolState.tokens.map((t) => t.address), poolState.address], + [1, 3, 0], + [ + ...poolState.tokens.map((t) => parseUnits('100', t.decimals)), + parseUnits('100', 18), + ], + ); - const poolJoin = new PoolJoin(); - const poolTokens = poolState.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsIn = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); + const poolJoin = new PoolJoin(); + const poolTokens = poolState.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + const amountsIn = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; + // perform join query to get expected bpt out + const joinInput: UnbalancedJoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; - const queryResult = await poolJoin.query(joinInput, poolState); + const queryResult = await poolJoin.query(joinInput, poolState); - const { call, to, value } = - poolJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, + const { call, to, value } = poolJoin.buildCall({ + ...queryResult, + slippage, + sender: testAddress, + recipient: testAddress, }); - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolState.tokens.map(({address})=>address), poolState.address /*BPT*/], - client, - testAddress, - to, - call, - value, - ); - console.log(`transaction status: ${transactionReceipt.status}`); - console.log(`token amounts deltas per token: ${balanceDeltas}`); - return; -} + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + [ + ...poolState.tokens.map(({ address }) => address), + poolState.address /*BPT*/, + ], + client, + testAddress, + to, + call, + value, + ); + console.log(`transaction status: ${transactionReceipt.status}`); + console.log(`token amounts deltas per token: ${balanceDeltas}`); + return; +}; -join().then(()=>{}); \ No newline at end of file +join().then(() => {}); diff --git a/src/data/providers/balancer-api/index.ts b/src/data/providers/balancer-api/index.ts index a00216e3..1dc16831 100644 --- a/src/data/providers/balancer-api/index.ts +++ b/src/data/providers/balancer-api/index.ts @@ -1,17 +1,13 @@ -import { Pools } from "./modules/pool-state"; -import { BalancerApiClient } from "./client"; -import { ChainId } from "../../../utils"; +import { Pools } from './modules/pool-state'; +import { BalancerApiClient } from './client'; +import { ChainId } from '../../../utils'; export class BalancerApi { - - balancerApiClient: BalancerApiClient; - pools: Pools; - - - constructor(balancerApiUrl: string, chainId: ChainId){ - this.balancerApiClient = new BalancerApiClient(balancerApiUrl, chainId); - this.pools = new Pools(this.balancerApiClient); - } - - + balancerApiClient: BalancerApiClient; + pools: Pools; + + constructor(balancerApiUrl: string, chainId: ChainId) { + this.balancerApiClient = new BalancerApiClient(balancerApiUrl, chainId); + this.pools = new Pools(this.balancerApiClient); + } } diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index 3941bae0..eeab4cd7 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -1,8 +1,8 @@ import { BalancerApiClient } from '../../client'; -import { PoolStateInput } from "../../../../../entities"; +import { PoolStateInput } from '../../../../../entities'; export class Pools { - readonly poolStateQuery = `query GetPool($id: String!){ + readonly poolStateQuery = `query GetPool($id: String!){ poolGetPool(id:$id) { id address @@ -75,16 +75,17 @@ export class Pools { } }`; - constructor(private readonly balancerApiClient: BalancerApiClient) {} + constructor(private readonly balancerApiClient: BalancerApiClient) {} - async fetchPoolState(id: string): Promise { - const { - data: { poolGetPool }, - } = await this.balancerApiClient.fetch({ - query: this.poolStateQuery, - variables: { - id, - }}); - return poolGetPool; - } + async fetchPoolState(id: string): Promise { + const { + data: { poolGetPool }, + } = await this.balancerApiClient.fetch({ + query: this.poolStateQuery, + variables: { + id, + }, + }); + return poolGetPool; + } } From 73b19fccec020095c544341003b3bcadca9007e6 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 25 Oct 2023 13:11:19 +0100 Subject: [PATCH 135/199] chore: Add changeset. --- .changeset/neat-glasses-brush.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/neat-glasses-brush.md diff --git a/.changeset/neat-glasses-brush.md b/.changeset/neat-glasses-brush.md new file mode 100644 index 00000000..bad55acf --- /dev/null +++ b/.changeset/neat-glasses-brush.md @@ -0,0 +1,5 @@ +--- +"@balancer/sdk": minor +--- + +Adds Balancer API Provider. A utility module designed to fetch pool data from [API](https://github.com/beethovenxfi/beethovenx-backend/blob/v3-main/README.md#branching-and-deployment-environments). From b33cda82ce16330c47fd1fdf14c87e32bfec4998 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 25 Oct 2023 13:22:31 +0100 Subject: [PATCH 136/199] chore: Fix formatting. --- src/entities/encoders/composableStable.ts | 250 +++++++++++----------- test/lib/utils/types.ts | 29 ++- 2 files changed, 147 insertions(+), 132 deletions(-) diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index ca0e6c02..1d4e2ebb 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -1,139 +1,147 @@ import { encodeAbiParameters } from 'viem'; import { Address } from '../../types'; - - export enum ComposableStablePoolJoinKind { - INIT = 0, - EXACT_TOKENS_IN_FOR_BPT_OUT = 1, - TOKEN_IN_FOR_EXACT_BPT_OUT = 2, - ALL_TOKENS_IN_FOR_EXACT_BPT_OUT = 3 + INIT = 0, + EXACT_TOKENS_IN_FOR_BPT_OUT = 1, + TOKEN_IN_FOR_EXACT_BPT_OUT = 2, + ALL_TOKENS_IN_FOR_EXACT_BPT_OUT = 3, } export enum ComposableStablePoolExitKind { - EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, - BPT_IN_FOR_EXACT_TOKENS_OUT = 1, - EXACT_BPT_IN_FOR_ALL_TOKENS_OUT = 2 + EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, + BPT_IN_FOR_EXACT_TOKENS_OUT = 1, + EXACT_BPT_IN_FOR_ALL_TOKENS_OUT = 2, } export class ComposableStableEncoder { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - } + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } - /** - * Encodes the userData parameter for providing the initial liquidity to a ComposableStablePool - * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances - */ - static joinInit = (amountsIn: bigint[]): Address => - encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256[]' }], - [BigInt(ComposableStablePoolJoinKind.INIT), amountsIn], - ); + /** + * Encodes the userData parameter for providing the initial liquidity to a ComposableStablePool + * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances + */ + static joinInit = (amountsIn: bigint[]): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }], + [BigInt(ComposableStablePoolJoinKind.INIT), amountsIn], + ); - /** - * Encodes the userData parameter for joining a ComposableStablePool with exact token inputs - * @param amountsIn - the amounts each of token to deposit in the pool as liquidity - * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens - */ - static joinUnbalanced = ( - amountsIn: bigint[], - minimumBPT: bigint, - ): Address => - encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], - [ - BigInt(ComposableStablePoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT), - amountsIn, - minimumBPT, - ], - ); + /** + * Encodes the userData parameter for joining a ComposableStablePool with exact token inputs + * @param amountsIn - the amounts each of token to deposit in the pool as liquidity + * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens + */ + static joinUnbalanced = ( + amountsIn: bigint[], + minimumBPT: bigint, + ): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], + [ + BigInt( + ComposableStablePoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, + ), + amountsIn, + minimumBPT, + ], + ); - /** - * Encodes the userData parameter for joining a ComposableStablePool with a single token to receive an exact amount of BPT - * @param bptAmountOut - the amount of BPT to be minted - * @param enterTokenIndex - the index of the token to be provided as liquidity - */ - static joinSingleAsset = ( - bptAmountOut: bigint, - enterTokenIndex: number, - ): Address => { - // if enterTokenIndex is provided, it's assumed to be an allTokensIn - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(ComposableStablePoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), - bptAmountOut, - BigInt(enterTokenIndex), - ], - ); - }; + /** + * Encodes the userData parameter for joining a ComposableStablePool with a single token to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + * @param enterTokenIndex - the index of the token to be provided as liquidity + */ + static joinSingleAsset = ( + bptAmountOut: bigint, + enterTokenIndex: number, + ): Address => { + // if enterTokenIndex is provided, it's assumed to be an allTokensIn + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + BigInt(ComposableStablePoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), + bptAmountOut, + BigInt(enterTokenIndex), + ], + ); + }; - /** - * Encodes the userData parameter for joining a ComposableStablePool proportionally to receive an exact amount of BPT - * @param bptAmountOut - the amount of BPT to be minted - */ - static joinProportional = (bptAmountOut: bigint): Address => { - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(ComposableStablePoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT), - bptAmountOut, - ], - ); - }; + /** + * Encodes the userData parameter for joining a ComposableStablePool proportionally to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + */ + static joinProportional = (bptAmountOut: bigint): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + BigInt( + ComposableStablePoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, + ), + bptAmountOut, + ], + ); + }; - /** - * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT - * @param bptAmountIn - the amount of BPT to be burned - * @param enterTokenIndex - the index of the token to removed from the pool - */ - static exitSingleAsset = ( - bptAmountIn: bigint, - exitTokenIndex: number, - ): Address => { - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT), - bptAmountIn, - BigInt(exitTokenIndex), - ], - ); - }; + /** + * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + * @param enterTokenIndex - the index of the token to removed from the pool + */ + static exitSingleAsset = ( + bptAmountIn: bigint, + exitTokenIndex: number, + ): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], + [ + BigInt( + ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, + ), + bptAmountIn, + BigInt(exitTokenIndex), + ], + ); + }; - /** - * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT - * @param bptAmountIn - the amount of BPT to be burned - */ - static exitProportional = (bptAmountIn: bigint): Address => { - return encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256' }], - [ - BigInt(ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ALL_TOKENS_OUT), - bptAmountIn, - ], - ); - }; + /** + * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + */ + static exitProportional = (bptAmountIn: bigint): Address => { + return encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256' }], + [ + BigInt( + ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ALL_TOKENS_OUT, + ), + bptAmountIn, + ], + ); + }; - /** - * Encodes the userData parameter for exiting a ComposableStablePool by removing exact amounts of tokens - * @param amountsOut - the amounts of each token to be withdrawn from the pool - * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens - */ - static exitUnbalanced = ( - amountsOut: bigint[], - maxBPTAmountIn: bigint, - ): Address => - encodeAbiParameters( - [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], - [ - BigInt(ComposableStablePoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT), - amountsOut, - maxBPTAmountIn, - ], - ); + /** + * Encodes the userData parameter for exiting a ComposableStablePool by removing exact amounts of tokens + * @param amountsOut - the amounts of each token to be withdrawn from the pool + * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens + */ + static exitUnbalanced = ( + amountsOut: bigint[], + maxBPTAmountIn: bigint, + ): Address => + encodeAbiParameters( + [{ type: 'uint256' }, { type: 'uint256[]' }, { type: 'uint256' }], + [ + BigInt( + ComposableStablePoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, + ), + amountsOut, + maxBPTAmountIn, + ], + ); } diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index 74bb7219..f2003801 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -1,13 +1,20 @@ -import { Client, PublicActions, TestActions, WalletActions } from "viem"; -import { Address, ChainId, JoinInput, PoolJoin, PoolStateInput, Slippage } from "../../../src"; +import { Client, PublicActions, TestActions, WalletActions } from 'viem'; +import { + Address, + ChainId, + JoinInput, + PoolJoin, + PoolStateInput, + Slippage, +} from '../../../src'; export type JoinTxInput = { - client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; - joinInput: JoinInput; - slippage: Slippage; - poolInput: PoolStateInput; - testAddress: Address; - checkNativeBalance: boolean; - chainId: ChainId; -} \ No newline at end of file + client: Client & PublicActions & TestActions & WalletActions; + poolJoin: PoolJoin; + joinInput: JoinInput; + slippage: Slippage; + poolInput: PoolStateInput; + testAddress: Address; + checkNativeBalance: boolean; + chainId: ChainId; +}; From 1f5b579f058e8aac68cf6dd6a87f7e835bd10bd0 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 25 Oct 2023 13:28:48 +0100 Subject: [PATCH 137/199] chore: Fix lock file. --- pnpm-lock.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b4858ef..01d6fe62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,9 +43,6 @@ devDependencies: ts-node: specifier: ^10.9.1 version: 10.9.1(@types/node@18.15.11)(typescript@5.1.3) - tsconfig-paths: - specifier: ^4.2.0 - version: 4.2.0 tsup: specifier: ^6.6.0 version: 6.7.0(ts-node@10.9.1)(typescript@5.1.3) @@ -2156,12 +2153,6 @@ packages: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true - /jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: true @@ -3258,15 +3249,6 @@ packages: yn: 3.1.1 dev: true - /tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - dev: true - /tsup@6.7.0(ts-node@10.9.1)(typescript@5.1.3): resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} engines: {node: '>=14.18'} From 8a13f6ef7c7d9f3618765b6042fe779046f2ceae Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 25 Oct 2023 14:04:10 +0100 Subject: [PATCH 138/199] refactor: getAmountsQuery & getAmountsCall return maxAmountsInNoBpt. --- .../composable-stable/composableStableJoin.ts | 62 ++++++++++++------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 4d4e9395..3c9fb124 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -12,10 +12,14 @@ import { ComposableStableJoinQueryResult, ComposableJoinCall, } from '../types'; -import { AmountsJoin, PoolState } from '../../types'; +import { AmountsJoin as AmountsJoinBase, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; import { ComposableStableEncoder } from '../../encoders/composableStable'; +type AmountsJoin = AmountsJoinBase & { + maxAmountsInNoBpt: bigint[]; +}; + export class ComposableStableJoin implements BaseJoin { public async query( input: JoinInput, @@ -25,15 +29,8 @@ export class ComposableStableJoin implements BaseJoin { (t) => t.address === poolState.address, ); const amounts = this.getAmountsQuery(poolState.tokens, input, bptIndex); - const amountsWithoutBpt = { - ...amounts, - maxAmountsIn: [ - ...amounts.maxAmountsIn.slice(0, bptIndex), - ...amounts.maxAmountsIn.slice(bptIndex + 1), - ], - }; - const userData = this.encodeUserData(input.kind, amountsWithoutBpt); + const userData = this.encodeUserData(input.kind, amounts); const { args, tokensIn } = parseJoinArgs({ useNativeAssetAsWrappedAmountIn: @@ -75,15 +72,8 @@ export class ComposableStableJoin implements BaseJoin { public buildCall(input: ComposableJoinCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); - const amountsWithoutBpt = { - ...amounts, - maxAmountsIn: [ - ...amounts.maxAmountsIn.slice(0, input.bptIndex), - ...amounts.maxAmountsIn.slice(input.bptIndex + 1), - ], - }; - const userData = this.encodeUserData(input.joinKind, amountsWithoutBpt); + const userData = this.encodeUserData(input.joinKind, amounts); const { args } = parseJoinArgs({ ...input, @@ -117,10 +107,11 @@ export class ComposableStableJoin implements BaseJoin { input: JoinInput, bptIndex: number, ): AmountsJoin { + let amountsJoin: AmountsJoinBase; switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: { - return { + amountsJoin = { minimumBpt: 0n, maxAmountsIn: getAmounts( poolTokens, @@ -129,6 +120,7 @@ export class ComposableStableJoin implements BaseJoin { ), tokenInIndex: undefined, }; + break; } case JoinKind.SingleAsset: { const tokenInIndex = poolTokens @@ -138,59 +130,81 @@ export class ComposableStableJoin implements BaseJoin { throw Error("Can't find index of SingleAsset"); const maxAmountsIn = Array(poolTokens.length).fill(0n); maxAmountsIn[tokenInIndex] = MAX_UINT256; - return { + amountsJoin = { minimumBpt: input.bptOut.amount, maxAmountsIn, tokenInIndex, }; + break; } case JoinKind.Proportional: { - return { + amountsJoin = { minimumBpt: input.bptOut.amount, maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), tokenInIndex: undefined, }; + break; } default: throw Error('Unsupported Join Type'); } + + return { + ...amountsJoin, + maxAmountsInNoBpt: [ + ...amountsJoin.maxAmountsIn.slice(0, bptIndex), + ...amountsJoin.maxAmountsIn.slice(bptIndex + 1), + ], + }; } private getAmountsCall(input: ComposableJoinCall): AmountsJoin { + let amountsJoin: AmountsJoinBase; switch (input.joinKind) { case JoinKind.Init: case JoinKind.Unbalanced: { const minimumBpt = input.slippage.removeFrom( input.bptOut.amount, ); - return { + amountsJoin = { minimumBpt, maxAmountsIn: input.amountsIn.map((a) => a.amount), tokenInIndex: input.tokenInIndex, }; + break; } case JoinKind.SingleAsset: case JoinKind.Proportional: { - return { + amountsJoin = { minimumBpt: input.bptOut.amount, maxAmountsIn: input.amountsIn.map((a) => input.slippage.applyTo(a.amount), ), tokenInIndex: input.tokenInIndex, }; + break; } default: throw Error('Unsupported Join Type'); } + return { + ...amountsJoin, + maxAmountsInNoBpt: [ + ...amountsJoin.maxAmountsIn.slice(0, input.bptIndex), + ...amountsJoin.maxAmountsIn.slice(input.bptIndex + 1), + ], + }; } private encodeUserData(kind: JoinKind, amounts: AmountsJoin): Address { switch (kind) { case JoinKind.Init: - return ComposableStableEncoder.joinInit(amounts.maxAmountsIn); + return ComposableStableEncoder.joinInit( + amounts.maxAmountsInNoBpt, + ); case JoinKind.Unbalanced: return ComposableStableEncoder.joinUnbalanced( - amounts.maxAmountsIn, + amounts.maxAmountsInNoBpt, amounts.minimumBpt, ); case JoinKind.SingleAsset: { From 40f3427e8896c723422acc25be6cfbe727e23da0 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Fri, 27 Oct 2023 10:42:37 +0100 Subject: [PATCH 139/199] refactor: Join tests to use common helpers. --- .../composable-stable/composableStableJoin.ts | 2 +- test/composableStableJoin.integration.test.ts | 326 ++++++------- test/lib/utils/helper.ts | 27 +- test/lib/utils/joinHelper.ts | 448 +++++++++++++++--- test/lib/utils/types.ts | 5 +- test/weightedJoin.integration.test.ts | 309 ++++++------ 6 files changed, 685 insertions(+), 432 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 3c9fb124..e46ef46e 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -211,7 +211,7 @@ export class ComposableStableJoin implements BaseJoin { if (amounts.tokenInIndex === undefined) throw Error('No Index'); return ComposableStableEncoder.joinSingleAsset( amounts.minimumBpt, - amounts.tokenInIndex, + amounts.tokenInIndex, // Has to be index without BPT ); } case JoinKind.Proportional: { diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 88ff3d36..f9a36b8b 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -1,5 +1,5 @@ // pnpm test -- composableStableJoin.integration.test.ts -import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import { describe, test, beforeAll, beforeEach } from 'vitest'; import { config } from 'dotenv'; config(); @@ -30,7 +30,12 @@ import { } from '../src'; import { forkSetup } from './lib/utils/helper'; import { JoinTxInput } from './lib/utils/types'; -import { doJoin } from './lib/utils/joinHelper'; +import { + doJoin, + assertUnbalancedJoin, + assertSingleToken, + assertProportional, +} from './lib/utils/joinHelper'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -60,11 +65,9 @@ describe('composable stable join test', () => { client, poolJoin: new PoolJoin(), slippage: Slippage.fromPercentage('1'), // 1% - poolInput, + poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig joinInput: {} as JoinInput, - checkNativeBalance: false, - chainId, }; // setup BPT token @@ -75,200 +78,171 @@ describe('composable stable join test', () => { await forkSetup( txInput.client, txInput.testAddress, - [...txInput.poolInput.tokens.map((t) => t.address)], + [...txInput.poolStateInput.tokens.map((t) => t.address)], [0, 0, 3], [ - ...txInput.poolInput.tokens.map((t) => + ...txInput.poolStateInput.tokens.map((t) => parseUnits('100', t.decimals), ), ], ); }); - test('unbalanced join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - const poolTokensWithoutBpt = txInput.poolInput.tokens - .map((t) => new Token(chainId, t.address, t.decimals)) - .filter((_, index) => index !== bptIndex); - - const amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, + describe('unbalanced join', () => { + let input: Omit; + let amountsIn: TokenAmount[]; + beforeAll(() => { + const bptIndex = txInput.poolStateInput.tokens.findIndex( + (t) => t.address === txInput.poolStateInput.address, + ); + const poolTokensWithoutBpt = txInput.poolStateInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + .filter((_, index) => index !== bptIndex); + + amountsIn = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + input = { + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; }); - // Query should use same amountsIn as user sets - expect( - queryResult.amountsIn.filter((_, index) => index !== bptIndex), - ).to.deep.eq(amountsIn); - expect(queryResult.tokenInIndex).to.be.undefined; - - // Should be no native value - expect(value).to.be.undefined; - - // Expect some bpt amount - expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq( - maxAmountsIn.filter((_, index) => index !== bptIndex), - ); - }); - - test('native asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - - const poolTokensWithoutBpt = txInput.poolInput.tokens - .map((t) => new Token(chainId, t.address, t.decimals)) - .filter((_, index) => index !== bptIndex); - - const amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - useNativeAssetAsWrappedAmountIn: true, - }; - - // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, - checkNativeBalance: true, + test('token inputs', async () => { + const joinInput = { + ...input, + amountsIn: [...amountsIn.splice(0, 1)], + }; + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + assertUnbalancedJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); }); - // Query should use same amountsIn as user sets - expect( - queryResult.amountsIn - .filter((_, index) => index !== bptIndex) - .map((a) => a.amount), - ).to.deep.eq(amountsIn.map((a) => a.amount)); - expect(queryResult.tokenInIndex).to.be.undefined; - // Should have native value equal to input amount - expect(value).eq(amountsIn[1].amount); - - // Expect some bpt amount - expect(queryResult.bptOut.amount > BigInt(0)).to.be.true; - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq( - maxAmountsIn.filter((_, index) => index !== bptIndex), - ); + test('with native', async () => { + const joinInput = { + ...input, + amountsIn, + useNativeAssetAsWrappedAmountIn: true, + }; + const joinResult = await doJoin({ + ...txInput, + joinInput: { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + }); + assertUnbalancedJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + { ...joinInput, useNativeAssetAsWrappedAmountIn: true }, + joinResult, + txInput.slippage, + ); + }); }); - test('single asset join', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenIn = '0x4bc3263eb5bb2ef7ad9ab6fb68be80e43b43801f'; - - // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - bptOut, - tokenIn, - chainId, - rpcUrl, - kind: JoinKind.SingleAsset, - }; + describe('single asset join', () => { + let joinInput: SingleAssetJoinInput; + beforeAll(() => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + joinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + }); + test('with token', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, + assertSingleToken( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); }); - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - // We only expect single asset to have a value for amount in - expect(queryResult.tokenInIndex).to.not.be.undefined; - queryResult.amountsIn - .filter((_, index) => index !== bptIndex) - .forEach((a, i) => { - if (i === queryResult.tokenInIndex) - expect(a.amount > BigInt(0)).to.be.true; - else expect(a.amount === BigInt(0)).to.be.true; + test('with native', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput: { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, }); - // Should be no native value - expect(value).to.be.undefined; - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); + assertSingleToken( + txInput.client.chain?.id as number, + txInput.poolStateInput, + { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + joinResult, + txInput.slippage, + ); + }); }); - test('proportional join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - - // perform join query to get expected bpt out - const joinInput: ProportionalJoinInput = { - bptOut, - chainId, - rpcUrl, - kind: JoinKind.Proportional, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, + describe('proportional join', () => { + let joinInput: ProportionalJoinInput; + beforeAll(() => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + joinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; }); - - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - console.log(queryResult.tokenInIndex); - - // Expect all assets to have a value for amount in - expect(queryResult.tokenInIndex).to.be.undefined; - queryResult.amountsIn - .filter((_, index) => index !== bptIndex) - .forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + test('with tokens', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, }); - // Should be no native value - expect(value).to.be.undefined; - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); + assertProportional( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); + test('with native', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput: { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + }); + assertProportional( + txInput.client.chain?.id as number, + txInput.poolStateInput, + { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + joinResult, + txInput.slippage, + ); + }); }); }); diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 22d5ae6e..a9b8a197 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -17,7 +17,12 @@ import { } from 'viem'; import { erc20Abi } from '../../../src/abi'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../src/utils'; -import { expect } from 'vitest'; + +export type TxResult = { + transactionReceipt: TransactionReceipt; + balanceDeltas: bigint[]; + gasUsed: bigint; +}; export const approveToken = async ( client: Client & PublicActions & WalletActions, @@ -72,7 +77,7 @@ export const getBalances = async ( }; /** - * Helper function that sends a transaction and check for balance deltas + * Helper function that sends a transaction and calculates balance changes * * @param tokensForBalanceCheck Token addresses to check balance deltas * @param client Client that will perform transactions @@ -89,11 +94,7 @@ export async function sendTransactionGetBalances( to: Address, data: Address, value?: bigint, -): Promise<{ - transactionReceipt: TransactionReceipt; - balanceDeltas: bigint[]; - gasUsed: bigint; -}> { +): Promise { const balanceBefore = await getBalances( tokensForBalanceCheck, client, @@ -299,15 +300,3 @@ export const forkSetup = async ( await approveToken(client, accountAddress, tokens[i]); } }; - -/* - * A function that asserts the transaction balance deltas and status - * */ -export const assertTransaction = ( - expectedDeltas: bigint[], - balanceDeltas: bigint[], - transactionReceiptStatus: string, -) => { - expect(expectedDeltas).to.deep.eq(balanceDeltas); - expect(transactionReceiptStatus).to.eq('success'); -}; diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 4addd66a..a505ecbe 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -1,9 +1,106 @@ -import { replaceWrapped, Token } from '../../../src'; -import { assertTransaction, sendTransactionGetBalances } from './helper'; +import { expect } from 'vitest'; +import { + PoolJoin, + JoinInput, + PoolStateInput, + Slippage, + Address, + JoinBuildOutput, + JoinQueryResult, + ZERO_ADDRESS, + UnbalancedJoinInput, + BALANCER_VAULT, + SingleAssetJoinInput, + ProportionalJoinInput, + Token, + ChainId, + TokenAmount, + ComposableStableJoinQueryResult, + NATIVE_ASSETS, +} from '../../../src'; +import { TxResult, sendTransactionGetBalances } from './helper'; import { JoinTxInput } from './types'; +import { zeroAddress } from 'viem'; + +type JoinResult = { + joinQueryResult: JoinQueryResult; + joinBuildOutput: JoinBuildOutput; + txOutput: TxResult; +}; + +async function sdkJoin({ + poolJoin, + joinInput, + poolStateInput, + slippage, + testAddress, +}: { + poolJoin: PoolJoin; + joinInput: JoinInput; + poolStateInput: PoolStateInput; + slippage: Slippage; + testAddress: Address; +}): Promise<{ + joinBuildOutput: JoinBuildOutput; + joinQueryResult: JoinQueryResult; +}> { + const joinQueryResult = await poolJoin.query(joinInput, poolStateInput); + const joinBuildOutput = poolJoin.buildCall({ + ...joinQueryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + return { + joinBuildOutput, + joinQueryResult, + }; +} + +function getTokens(poolStateInput: PoolStateInput): Address[] { + // pool tokens, bpt, eth + const tokens = poolStateInput.tokens + .filter((t) => t.address !== poolStateInput.address) + .map((t) => t.address); + tokens.push(poolStateInput.address); + tokens.push(ZERO_ADDRESS); + return tokens; +} + +function isComposableStableJoinQueryResult(result: JoinQueryResult): boolean { + return (result as ComposableStableJoinQueryResult).bptIndex !== undefined; +} + +function getCheck(result: JoinQueryResult, isExactIn: boolean) { + if (isComposableStableJoinQueryResult(result)) { + if (isExactIn) { + // Using this destructuring to return only the fields of interest + // rome-ignore lint/correctness/noUnusedVariables: + const { bptOut, bptIndex, ...check } = + result as ComposableStableJoinQueryResult; + return check; + } else { + // rome-ignore lint/correctness/noUnusedVariables: + const { amountsIn, bptIndex, ...check } = + result as ComposableStableJoinQueryResult; + return check; + } + } else { + if (isExactIn) { + // rome-ignore lint/correctness/noUnusedVariables: + const { bptOut, ...check } = result; + return check; + } else { + // rome-ignore lint/correctness/noUnusedVariables: + const { amountsIn, ...check } = result; + return check; + } + } +} /** - * Helper function that sends a join transaction and check for balance deltas + * Create and submit join transaction. * @param txInput * @param poolJoin: PoolJoin - The pool join class, used to query the join and build the join call * @param poolInput: PoolStateInput - The state of the pool being joined @@ -11,94 +108,295 @@ import { JoinTxInput } from './types'; * @param testAddress: Address - The address to send the transaction from * @param client: Client & PublicActions & WalletActions - The RPC client * @param slippage: Slippage - The slippage tolerance for the join transaction - * @param checkNativeBalance: boolean - Whether to check the native asset balance or not - * @param chainId: ChainId - The Chain Id of the pool being joined, for example: 1 for Mainnet, 137 for Polygon, etc. */ - -export const doJoin = async (txInput: JoinTxInput) => { +export async function doJoin(txInput: JoinTxInput) { const { poolJoin, - poolInput, + poolStateInput, joinInput, testAddress, client, slippage, - checkNativeBalance, - chainId, } = txInput; - const queryResult = await poolJoin.query(joinInput, poolInput); - const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall({ - ...queryResult, + + const { joinQueryResult, joinBuildOutput } = await sdkJoin({ + poolJoin, + joinInput, + poolStateInput, slippage, - sender: testAddress, - recipient: testAddress, + testAddress, }); - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), + // get tokens for balance change - pool tokens, BPT, native + const tokens = getTokens(poolStateInput); + + // send transaction and calculate balance changes + const txOutput = await sendTransactionGetBalances( + tokens, + client, + testAddress, + joinBuildOutput.to, + joinBuildOutput.call, + joinBuildOutput.value, ); - const bptIndex = poolInput.tokens.findIndex( - (t) => t.address === poolInput.address, + + return { + joinQueryResult, + joinBuildOutput, + txOutput, + }; +} + +export function assertUnbalancedJoin( + chainId: ChainId, + poolStateInput: PoolStateInput, + joinInput: UnbalancedJoinInput, + joinResult: JoinResult, + slippage: Slippage, +) { + const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; + + // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) + const expectedAmountsIn = poolStateInput.tokens.map((t) => { + let token; + if ( + joinInput.useNativeAssetAsWrappedAmountIn && + t.address === NATIVE_ASSETS[chainId].wrapped + ) + token = new Token(chainId, zeroAddress, t.decimals); + else token = new Token(chainId, t.address, t.decimals); + const input = joinInput.amountsIn.find( + (a) => a.token.address === t.address, + ); + if (input === undefined) return TokenAmount.fromRawAmount(token, 0n); + else return TokenAmount.fromRawAmount(token, input.amount); + }); + + const expectedQueryResult: Omit = { + // Query should use same amountsIn as input + amountsIn: expectedAmountsIn, + tokenInIndex: undefined, + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + fromInternalBalance: !!joinInput.fromInternalBalance, + joinKind: joinInput.kind, + }; + + const queryCheck = getCheck(joinQueryResult, true); + + expect(queryCheck).to.deep.eq(expectedQueryResult); + + // Expect some bpt amount + expect(joinQueryResult.bptOut.amount > 0n).to.be.true; + + assertJoinBuildOutput( + joinInput, + joinQueryResult, + joinBuildOutput, + true, + slippage, ); - let tokensForBalanceCheck; // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - if (bptIndex >= 0) { - /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr - * will already have the bpt address, so we don't need to add it again. - * */ - tokensForBalanceCheck = [...poolTokensAddr]; - } else { - /** - * If the pool type tokens list does not contains BPT (for example: Weighted), the poolTokensAddr - * will not have the bpt address, so we need to add it, so the balance delta of the bpt will be - * checked in the tests. - */ - tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; - } - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - tokensForBalanceCheck, - client, - testAddress, - to, - call, - value, + assertTokenDeltas( + poolStateInput, + joinInput, + joinQueryResult, + joinBuildOutput, + txOutput, + ); +} + +export function assertSingleToken( + chainId: ChainId, + poolStateInput: PoolStateInput, + joinInput: SingleAssetJoinInput, + joinResult: JoinResult, + slippage: Slippage, +) { + const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; + + if (!joinQueryResult.tokenInIndex) throw Error('No index'); + + const bptToken = new Token(chainId, poolStateInput.address, 18); + + const tokensWithoutBpt = poolStateInput.tokens.filter( + (t) => t.address !== poolStateInput.address, + ); + + const expectedQueryResult: Omit = + { + // Query should use same bpt out as user sets + bptOut: TokenAmount.fromRawAmount( + bptToken, + joinInput.bptOut.amount, + ), + tokenInIndex: tokensWithoutBpt.findIndex( + (t) => t.address === joinInput.tokenIn, + ), + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + fromInternalBalance: !!joinInput.fromInternalBalance, + joinKind: joinInput.kind, + }; + + const queryCheck = getCheck(joinQueryResult, false); + + expect(queryCheck).to.deep.eq(expectedQueryResult); + + // Expect only tokenIn to have amount > 0 + // (Note joinQueryResult also has value for bpt if pre-minted) + joinQueryResult.amountsIn.forEach((a) => { + if ( + !joinInput.useNativeAssetAsWrappedAmountIn && + a.token.address === joinInput.tokenIn + ) + expect(a.amount > 0n).to.be.true; + else if ( + joinInput.useNativeAssetAsWrappedAmountIn && + a.token.address === zeroAddress + ) + expect(a.amount > 0n).to.be.true; + else expect(a.amount).toEqual(0n); + }); + + assertJoinBuildOutput( + joinInput, + joinQueryResult, + joinBuildOutput, + false, + slippage, + ); + + assertTokenDeltas( + poolStateInput, + joinInput, + joinQueryResult, + joinBuildOutput, + txOutput, + ); +} + +export function assertProportional( + chainId: ChainId, + poolStateInput: PoolStateInput, + joinInput: ProportionalJoinInput, + joinResult: JoinResult, + slippage: Slippage, +) { + const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; + + const bptToken = new Token(chainId, poolStateInput.address, 18); + + const expectedQueryResult: Omit = + { + // Query should use same bpt out as user sets + bptOut: TokenAmount.fromRawAmount( + bptToken, + joinInput.bptOut.amount, + ), + // Only expect tokenInIndex for SingleAssetJoin + tokenInIndex: undefined, + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + fromInternalBalance: !!joinInput.fromInternalBalance, + joinKind: joinInput.kind, + }; + + const queryCheck = getCheck(joinQueryResult, false); + + expect(queryCheck).to.deep.eq(expectedQueryResult); + + // Expect all assets in to have an amount > 0 apart from BPT if it exists + joinQueryResult.amountsIn.forEach((a) => { + if (a.token.address === poolStateInput.address) + expect(a.amount).toEqual(0n); + else expect(a.amount > 0n).to.be.true; + }); + + assertJoinBuildOutput( + joinInput, + joinQueryResult, + joinBuildOutput, + false, + slippage, + ); + + assertTokenDeltas( + poolStateInput, + joinInput, + joinQueryResult, + joinBuildOutput, + txOutput, + ); +} + +function assertTokenDeltas( + poolStateInput: PoolStateInput, + joinInput: JoinInput, + joinQueryResult: JoinQueryResult, + joinBuildOutput: JoinBuildOutput, + txOutput: TxResult, +) { + expect(txOutput.transactionReceipt.status).to.eq('success'); + + // joinQueryResult amountsIn will have a value for the BPT token if it is a pre-minted pool + const amountsWithoutBpt = [...joinQueryResult.amountsIn].filter( + (t) => t.token.address !== poolStateInput.address, + ); + + // Matching order of getTokens helper: [poolTokens, BPT, native] + const expectedDeltas = [ + ...amountsWithoutBpt.map((a) => a.amount), + joinQueryResult.bptOut.amount, + 0n, + ]; + + // If input is wrapped native we must replace it with 0 and update native value instead + if (joinInput.useNativeAssetAsWrappedAmountIn) { + const index = amountsWithoutBpt.findIndex( + (a) => a.token.address === zeroAddress, ); - let expectedDeltas; - if (bptIndex >= 0) { - /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsIn - * includes the amount in of the bptIn, which is always 0, so we need to remove it from the - * expectedDeltas and put the bptOut.amount in its place. - */ - expectedDeltas = [ - ...queryResult.amountsIn.slice(0, bptIndex).map((a) => a.amount), - queryResult.bptOut.amount, - ...queryResult.amountsIn.slice(bptIndex + 1).map((a) => a.amount), - ]; - } else { - /** - * If the pool type tokens list does not contains BPT (for example: Weighted), the queryResult.amountsIn - * does not include the amount of the bptIn, so we just need to add the bptOut.amount in the end - * of the expectedDeltas. - */ - expectedDeltas = [ - ...queryResult.amountsIn.map((a) => a.amount), - queryResult.bptOut.amount, - ]; + expectedDeltas[index] = 0n; + expectedDeltas[expectedDeltas.length - 1] = + joinBuildOutput.value as bigint; } - // Confirm final balance changes match query result - assertTransaction(expectedDeltas, balanceDeltas, transactionReceipt.status); - return { - queryResult, + + expect(txOutput.balanceDeltas).to.deep.eq(expectedDeltas); +} + +function assertJoinBuildOutput( + joinInput: JoinInput, + joinQueryResult: JoinQueryResult, + joinBuildOutput: JoinBuildOutput, + isExactIn: boolean, + slippage: Slippage, +) { + // if exactIn maxAmountsIn should use same amountsIn as input else slippage should be applied + const maxAmountsIn = isExactIn + ? joinQueryResult.amountsIn.map((a) => a.amount) + : joinQueryResult.amountsIn.map((a) => slippage.applyTo(a.amount)); + + // if exactIn slippage should be applied to bptOut else should use same bptOut as input + const minBptOut = isExactIn + ? slippage.removeFrom(joinQueryResult.bptOut.amount) + : joinQueryResult.bptOut.amount; + + const expectedBuildOutput: Omit = { maxAmountsIn, minBptOut, - value, + to: BALANCER_VAULT, + // Value should equal value of any wrapped asset if using native + value: joinInput.useNativeAssetAsWrappedAmountIn + ? joinQueryResult.amountsIn.find( + (a) => a.token.address === zeroAddress, + )?.amount + : undefined, }; -}; + + // rome-ignore lint/correctness/noUnusedVariables: + const { call, ...buildCheck } = joinBuildOutput; + expect(buildCheck).to.deep.eq(expectedBuildOutput); +} diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index f2003801..787cf2d9 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -1,7 +1,6 @@ import { Client, PublicActions, TestActions, WalletActions } from 'viem'; import { Address, - ChainId, JoinInput, PoolJoin, PoolStateInput, @@ -13,8 +12,6 @@ export type JoinTxInput = { poolJoin: PoolJoin; joinInput: JoinInput; slippage: Slippage; - poolInput: PoolStateInput; + poolStateInput: PoolStateInput; testAddress: Address; - checkNativeBalance: boolean; - chainId: ChainId; }; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 0a5a1a89..d4dff628 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -1,5 +1,5 @@ // pnpm test -- weightedJoin.integration.test.ts -import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import { describe, test, beforeAll, beforeEach } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -29,13 +29,18 @@ import { JoinInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { doJoin } from './lib/utils/joinHelper'; +import { + assertProportional, + assertSingleToken, + assertUnbalancedJoin, + doJoin, +} from './lib/utils/joinHelper'; import { JoinTxInput } from './lib/utils/types'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; const poolId = - '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // Balancer 50COMP-50wstETH + '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // 80wjAURA-20WETH describe('weighted join test', () => { let txInput: JoinTxInput; @@ -46,7 +51,7 @@ describe('weighted join test', () => { const api = new MockApi(); // get pool state from api - const poolInput = await api.getPool(poolId); + const poolStateInput = await api.getPool(poolId); const client = createTestClient({ mode: 'anvil', @@ -60,15 +65,13 @@ describe('weighted join test', () => { client, poolJoin: new PoolJoin(), slippage: Slippage.fromPercentage('1'), // 1% - poolInput, + poolStateInput, testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig joinInput: {} as JoinInput, - checkNativeBalance: false, - chainId, }; // setup BPT token - bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); + bptToken = new Token(chainId, poolStateInput.address, 18, 'BPT'); }); beforeEach(async () => { @@ -76,12 +79,12 @@ describe('weighted join test', () => { txInput.client, txInput.testAddress, [ - ...txInput.poolInput.tokens.map((t) => t.address), - txInput.poolInput.address, + ...txInput.poolStateInput.tokens.map((t) => t.address), + txInput.poolStateInput.address, ], undefined, // TODO: hardcode these values to improve test performance [ - ...txInput.poolInput.tokens.map((t) => + ...txInput.poolStateInput.tokens.map((t) => parseUnits('100', t.decimals), ), parseUnits('100', 18), @@ -89,165 +92,157 @@ describe('weighted join test', () => { ); }); - test('unbalanced join', async () => { - const poolTokens = txInput.poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsIn = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, + describe('unbalanced join', () => { + let input: Omit; + let amountsIn: TokenAmount[]; + beforeAll(() => { + const poolTokens = txInput.poolStateInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); + amountsIn = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + input = { + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; }); - - // Query should use same amountsIn as user sets - expect(queryResult.amountsIn).to.deep.eq(amountsIn); - expect(queryResult.tokenInIndex === undefined).toEqual(true); - - // Should be no native value - expect(value === undefined).toEqual(true); - - // Expect some bpt amount - expect(queryResult.bptOut.amount > 0n).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - }); - - test('native asset join', async () => { - const poolTokens = txInput.poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsIn = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - useNativeAssetAsWrappedAmountIn: true, - }; - - // We have to use zero address for balanceDeltas - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, - checkNativeBalance: true, + test('with tokens', async () => { + const joinInput = { + ...input, + amountsIn: [...amountsIn.splice(0, 1)], + }; + + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + assertUnbalancedJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); }); - // Query should use same amountsIn as user sets - expect(queryResult.amountsIn.map((a) => a.amount)).to.deep.eq( - amountsIn.map((a) => a.amount), - ); - expect(queryResult.tokenInIndex === undefined).toEqual(true); - - // Should have native value equal to input amount - expect(value).eq(amountsIn[0].amount); - - // Expect some bpt amount - expect(queryResult.bptOut.amount > 0n).to.be.true; - - // Confirm slippage - only bpt out - const expectedMinBpt = txInput.slippage.removeFrom( - queryResult.bptOut.amount, - ); - expect(expectedMinBpt).to.deep.eq(minBptOut); - const expectedMaxAmountsIn = amountsIn.map((a) => a.amount); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); + test('with native', async () => { + const joinInput = { + ...input, + amountsIn, + useNativeAssetAsWrappedAmountIn: true, + }; + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + assertUnbalancedJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); }); - test('single asset join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenIn = '0x198d7387fa97a73f05b8578cdeff8f2a1f34cd1f'; - - // perform join query to get expected bpt out - const joinInput: SingleAssetJoinInput = { - bptOut, - tokenIn, - chainId, - rpcUrl, - kind: JoinKind.SingleAsset, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, + describe('single asset join', () => { + let joinInput: SingleAssetJoinInput; + beforeAll(() => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + joinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; }); - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - // We only expect single asset to have a value for amount in - expect(queryResult.tokenInIndex !== undefined).toEqual(true); - queryResult.amountsIn.forEach((a, i) => { - if (i === queryResult.tokenInIndex) - expect(a.amount > 0n).to.be.true; - else expect(a.amount === 0n).to.be.true; + test('with token', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + + assertSingleToken( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); }); - // Should be no native value - expect(value === undefined).toEqual(true); - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); + test('with native', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput: { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + }); + + assertSingleToken( + txInput.client.chain?.id as number, + txInput.poolStateInput, + { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + joinResult, + txInput.slippage, + ); + }); }); - test('proportional join', async () => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - - // perform join query to get expected bpt out - const joinInput: ProportionalJoinInput = { - bptOut, - chainId, - rpcUrl, - kind: JoinKind.Proportional, - }; - - const { queryResult, maxAmountsIn, minBptOut, value } = await doJoin({ - ...txInput, - joinInput, + describe('proportional join', () => { + let joinInput: ProportionalJoinInput; + beforeAll(() => { + const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + joinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; }); - - // Query should use same bpt out as user sets - expect(queryResult.bptOut.amount).to.deep.eq(bptOut.amount); - - expect(queryResult.tokenInIndex === undefined).toEqual(true); - // Expect all assets to have a value for amount in - queryResult.amountsIn.forEach((a) => { - expect(a.amount > 0n).to.be.true; + test('with tokens', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + + assertProportional( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); + test('with native', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput: { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + }); + + assertProportional( + txInput.client.chain?.id as number, + txInput.poolStateInput, + { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + joinResult, + txInput.slippage, + ); }); - - // Should be no native value - expect(value === undefined).toEqual(true); - - // Confirm slippage - only to amount in not bpt out - const expectedMaxAmountsIn = queryResult.amountsIn.map((a) => - txInput.slippage.applyTo(a.amount), - ); - expect(expectedMaxAmountsIn).to.deep.eq(maxAmountsIn); - expect(minBptOut).to.eq(bptOut.amount); }); }); From ebccf76a41d6af99b7afda2b4029490999393e62 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Fri, 27 Oct 2023 10:47:01 +0100 Subject: [PATCH 140/199] refactor: Change value to never be undefined. --- .../join/composable-stable/composableStableJoin.ts | 2 +- src/entities/join/types.ts | 4 ++-- src/entities/join/weighted/weightedJoin.ts | 2 +- test/lib/utils/joinHelper.ts | 9 ++++----- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index e46ef46e..a45b47c4 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -96,7 +96,7 @@ export class ComposableStableJoin implements BaseJoin { return { call, to: BALANCER_VAULT, - value, + value: value === undefined ? 0n : value, minBptOut: amounts.minimumBpt, maxAmountsIn: amounts.maxAmountsIn, }; diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 6ef3b866..61f26964 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -81,7 +81,7 @@ export interface BaseJoin { buildCall(input: JoinCall): { call: Hex; to: Address; - value: bigint | undefined; + value: bigint; minBptOut: bigint; maxAmountsIn: bigint[]; }; @@ -90,7 +90,7 @@ export interface BaseJoin { export type JoinBuildOutput = { call: Hex; to: Address; - value: bigint | undefined; + value: bigint; minBptOut: bigint; maxAmountsIn: bigint[]; }; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 9f4551b0..b7ba584b 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -88,7 +88,7 @@ export class WeightedJoin implements BaseJoin { return { call, to: BALANCER_VAULT, - value, + value: value === undefined ? 0n : value, minBptOut: amounts.minimumBpt, maxAmountsIn: amounts.maxAmountsIn, }; diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index a505ecbe..10b585ad 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -360,8 +360,7 @@ function assertTokenDeltas( (a) => a.token.address === zeroAddress, ); expectedDeltas[index] = 0n; - expectedDeltas[expectedDeltas.length - 1] = - joinBuildOutput.value as bigint; + expectedDeltas[expectedDeltas.length - 1] = joinBuildOutput.value; } expect(txOutput.balanceDeltas).to.deep.eq(expectedDeltas); @@ -390,10 +389,10 @@ function assertJoinBuildOutput( to: BALANCER_VAULT, // Value should equal value of any wrapped asset if using native value: joinInput.useNativeAssetAsWrappedAmountIn - ? joinQueryResult.amountsIn.find( + ? (joinQueryResult.amountsIn.find( (a) => a.token.address === zeroAddress, - )?.amount - : undefined, + )?.amount as bigint) + : 0n, }; // rome-ignore lint/correctness/noUnusedVariables: From ecba974235958008f640ef63fd0e57e36dc60522 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 30 Oct 2023 10:39:32 +0000 Subject: [PATCH 141/199] refactor: Based off PR code review. --- test/composableStableJoin.integration.test.ts | 51 ++++++++----------- test/lib/utils/joinHelper.ts | 2 +- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index f9a36b8b..4a2583b4 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -134,15 +134,12 @@ describe('composable stable join test', () => { }; const joinResult = await doJoin({ ...txInput, - joinInput: { - ...joinInput, - useNativeAssetAsWrappedAmountIn: true, - }, + joinInput, }); assertUnbalancedJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - { ...joinInput, useNativeAssetAsWrappedAmountIn: true }, + joinInput, joinResult, txInput.slippage, ); @@ -150,11 +147,11 @@ describe('composable stable join test', () => { }); describe('single asset join', () => { - let joinInput: SingleAssetJoinInput; + let input: SingleAssetJoinInput; beforeAll(() => { const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - joinInput = { + input = { bptOut, tokenIn, chainId, @@ -165,34 +162,32 @@ describe('composable stable join test', () => { test('with token', async () => { const joinResult = await doJoin({ ...txInput, - joinInput, + joinInput: input, }); assertSingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + input, joinResult, txInput.slippage, ); }); test('with native', async () => { + const joinInput = { + ...input, + useNativeAssetAsWrappedAmountIn: true, + }; const joinResult = await doJoin({ ...txInput, - joinInput: { - ...joinInput, - useNativeAssetAsWrappedAmountIn: true, - }, + joinInput, }); assertSingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, - { - ...joinInput, - useNativeAssetAsWrappedAmountIn: true, - }, + joinInput, joinResult, txInput.slippage, ); @@ -200,10 +195,10 @@ describe('composable stable join test', () => { }); describe('proportional join', () => { - let joinInput: ProportionalJoinInput; + let input: ProportionalJoinInput; beforeAll(() => { const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); - joinInput = { + input = { bptOut, chainId, rpcUrl, @@ -213,32 +208,30 @@ describe('composable stable join test', () => { test('with tokens', async () => { const joinResult = await doJoin({ ...txInput, - joinInput, + joinInput: input, }); assertProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + input, joinResult, txInput.slippage, ); }); test('with native', async () => { + const joinInput = { + ...input, + useNativeAssetAsWrappedAmountIn: true, + }; const joinResult = await doJoin({ ...txInput, - joinInput: { - ...joinInput, - useNativeAssetAsWrappedAmountIn: true, - }, + joinInput, }); assertProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - { - ...joinInput, - useNativeAssetAsWrappedAmountIn: true, - }, + joinInput, joinResult, txInput.slippage, ); diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 10b585ad..feaac91f 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -216,7 +216,7 @@ export function assertSingleToken( ) { const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; - if (!joinQueryResult.tokenInIndex) throw Error('No index'); + if (joinQueryResult.tokenInIndex === undefined) throw Error('No index'); const bptToken = new Token(chainId, poolStateInput.address, 18); From 0e24dc5c530b4a591b7cebf81e735fbc298e2626 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 31 Oct 2023 10:17:39 +0000 Subject: [PATCH 142/199] feat: Update anvil setup to work with multiple networks. --- test/anvil/anvil-global-setup.ts | 59 ++++++++++++++++++++++++++------ test/lib/utils/helper.ts | 1 - 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 2e723ff0..16c7b2d0 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -1,24 +1,63 @@ import { CreateAnvilOptions, createAnvil } from '@viem/anvil'; import { sleep } from '../lib/utils/promises'; +type NetworkSetup = { + rpcEnv: string; + fallBackRpc: string | undefined; + port: number; + forkBlockNumber: bigint; +}; + +type Networks = NetworkSetup[]; + +const NETWORKS: Networks = [ + { + rpcEnv: 'ETHEREUM_RPC_URL', + fallBackRpc: 'https://cloudflare-eth.com', + port: 8545, + forkBlockNumber: 18043296n, + }, + { + rpcEnv: 'POLYGON_RPC_URL', + // Public Polygon RPCs are usually unreliable + fallBackRpc: undefined, + port: 8137, + forkBlockNumber: 43878700n, + }, +]; + export default async function () { + for (const network of NETWORKS) { + const config = getAnvilOptions(network); + await startFork(config); + } +} + +function getAnvilOptions(network: NetworkSetup): CreateAnvilOptions { let forkUrl: string; - if (process.env.ETHEREUM_RPC_URL) { - forkUrl = process.env.ETHEREUM_RPC_URL; + if (process.env[network.rpcEnv]) { + forkUrl = process.env[network.rpcEnv] as string; } else { - forkUrl = 'https://cloudflare-eth.com'; + if (!network.fallBackRpc) + throw Error( + `Please add a environment variable for: ${network.rpcEnv}`, + ); + forkUrl = network.fallBackRpc; console.warn( - `\`ETHEREUM_RPC_URL\` not found. Falling back to \`${forkUrl}\`.`, + `\`${network.rpcEnv}\` not found. Falling back to \`${forkUrl}\`.`, ); } - const port = 8545; - const forkBlockNumber = 18043296n; - // https://www.npmjs.com/package/@viem/anvil - const anvilOptions: CreateAnvilOptions = { + const port = network.port; + const forkBlockNumber = network.forkBlockNumber; + return { forkUrl, port, forkBlockNumber, }; +} + +async function startFork(anvilOptions: CreateAnvilOptions) { + // https://www.npmjs.com/package/@viem/anvil const anvil = createAnvil(anvilOptions); if (process.env.SKIP_GLOBAL_SETUP === 'true') { console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: @@ -28,8 +67,8 @@ anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8555 --f return; } console.log('🛠️ Starting anvil', { - port, - forkBlockNumber, + port: anvilOptions.port, + forkBlockNumber: anvilOptions.forkBlockNumber, }); return await anvil.start(); } diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index a9b8a197..32c42b38 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -255,7 +255,6 @@ export async function findTokenBalanceSlot( * @param slots Slot that stores token balance in memory - use npm package `slot20` to identify which slot to provide * @param balances Balances in EVM amounts * @param jsonRpcUrl Url with remote node to be forked locally - * @param blockNumber Number of the block that the fork will happen * @param isVyperMapping Whether the storage uses Vyper or Solidity mapping */ export const forkSetup = async ( From c5f9b499e48747140af35fd8e428da2c5893d424 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 31 Oct 2023 11:48:46 +0000 Subject: [PATCH 143/199] chore: Update rpcs to local fork. --- test/anvil/anvil-global-setup.ts | 3 ++- test/fxPool.integration.test.ts | 2 +- test/gyro3Pool.integration.test.ts | 2 +- test/gyroEV2Pool.integration.test.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 16c7b2d0..19c370d3 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -22,7 +22,8 @@ const NETWORKS: Networks = [ // Public Polygon RPCs are usually unreliable fallBackRpc: undefined, port: 8137, - forkBlockNumber: 43878700n, + // Note - this has to be >= highest blockNo used in tests + forkBlockNumber: 44215395n, }, ]; diff --git a/test/fxPool.integration.test.ts b/test/fxPool.integration.test.ts index a4ef6217..60c58eb2 100644 --- a/test/fxPool.integration.test.ts +++ b/test/fxPool.integration.test.ts @@ -18,7 +18,7 @@ import { describe('fx integration tests', () => { const chainId = ChainId.POLYGON; - const rpcUrl = process.env.POLYGON_RPC_URL || 'https://polygon-rpc.com'; + const rpcUrl = 'http://127.0.0.1:8137/'; const USDC = new Token( chainId, diff --git a/test/gyro3Pool.integration.test.ts b/test/gyro3Pool.integration.test.ts index 103053f8..831a9f7f 100644 --- a/test/gyro3Pool.integration.test.ts +++ b/test/gyro3Pool.integration.test.ts @@ -18,7 +18,7 @@ import { describe('gyro3 integration tests', () => { const chainId = ChainId.POLYGON; - const rpcUrl = process.env['POLYGON_RPC_URL'] || 'https://polygon-rpc.com'; + const rpcUrl = 'http://127.0.0.1:8137/'; const rawPool = { ...testPools }.pools[1] as RawGyro3Pool; const USDC = new Token( chainId, diff --git a/test/gyroEV2Pool.integration.test.ts b/test/gyroEV2Pool.integration.test.ts index fa07611e..9e577e51 100644 --- a/test/gyroEV2Pool.integration.test.ts +++ b/test/gyroEV2Pool.integration.test.ts @@ -21,7 +21,7 @@ import { MockPoolProvider } from './lib/utils/mockPoolProvider'; describe('gyroEV2: WMATIC-stMATIC integration tests', () => { const chainId = ChainId.POLYGON; - const rpcUrl = process.env.POLYGON_RPC_URL || 'https://polygon-rpc.com'; + const rpcUrl = 'http://127.0.0.1:8137/'; const WMATIC = new Token( chainId, '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270', From bdd1c55d91245ab236bdd8c99906c8795a415ae9 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 31 Oct 2023 11:56:02 +0000 Subject: [PATCH 144/199] refactor: FxPool tests use test pool json to avoid SG call. --- test/fxPool.integration.test.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/test/fxPool.integration.test.ts b/test/fxPool.integration.test.ts index 60c58eb2..bb962798 100644 --- a/test/fxPool.integration.test.ts +++ b/test/fxPool.integration.test.ts @@ -7,14 +7,17 @@ import { BATCHSIZE, ChainId, VAULT } from '../src/utils'; import { BasePool, OnChainPoolDataEnricher, + RawFxPool, SmartOrderRouter, - SubgraphPoolProvider, SwapKind, SwapOptions, Token, TokenAmount, sorGetSwapsWithPools, } from '../src'; +import { MockPoolProvider } from './lib/utils/mockPoolProvider'; + +import testPools from './lib/testData/fxPool_43667355.json'; describe('fx integration tests', () => { const chainId = ChainId.POLYGON; @@ -33,19 +36,15 @@ describe('fx integration tests', () => { 'XSGD', ); const swapOptions: SwapOptions = { - block: 43878700n, + block: 43667355n, }; let sor: SmartOrderRouter; beforeAll(() => { - const subgraphPoolDataService = new SubgraphPoolProvider( - chainId, - undefined, - { - poolTypeIn: ['FX'], - }, - ); + const pools = testPools.pools as RawFxPool[]; + const mockPoolProvider = new MockPoolProvider(pools); + const onChainPoolDataEnricher = new OnChainPoolDataEnricher( chainId, rpcUrl, @@ -55,7 +54,7 @@ describe('fx integration tests', () => { sor = new SmartOrderRouter({ chainId, - poolDataProviders: subgraphPoolDataService, + poolDataProviders: mockPoolProvider, poolDataEnrichers: onChainPoolDataEnricher, rpcUrl: rpcUrl, }); From c089a5ff282fc5ad47d1d633d21b73b6162138a2 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 31 Oct 2023 12:30:41 +0000 Subject: [PATCH 145/199] refactor: Split Weighted & CS tests and use local pool files instead of SG call to speed up. --- test/composableStable.integration.test.ts | 107 ++++++++++++++++++++ test/lib/testData/composableStable.json | 94 ++++++++++++++++++ test/lib/testData/weighted.json | 70 +++++++++++++ test/weightedPool.integration.test.ts | 115 ++++++++++++++++++++++ 4 files changed, 386 insertions(+) create mode 100644 test/composableStable.integration.test.ts create mode 100644 test/lib/testData/composableStable.json create mode 100644 test/lib/testData/weighted.json create mode 100644 test/weightedPool.integration.test.ts diff --git a/test/composableStable.integration.test.ts b/test/composableStable.integration.test.ts new file mode 100644 index 00000000..72981605 --- /dev/null +++ b/test/composableStable.integration.test.ts @@ -0,0 +1,107 @@ +// pnpm test -- test/composableStable.integration.test.ts +import { beforeEach, describe, expect, test } from "vitest"; +import dotenv from "dotenv"; +dotenv.config(); + +import { SmartOrderRouter } from "../src/sor"; +import { sorGetSwapsWithPools } from "../src/static"; +import { ChainId, BATCHSIZE, VAULT } from "../src/utils"; +import { Token, TokenAmount } from "../src/entities"; +import { OnChainPoolDataEnricher } from "../src/data/enrichers/onChainPoolDataEnricher"; +import { SwapKind, SwapOptions } from "../src/types"; +import { BasePool } from "../src/entities/pools"; +import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; + +import testPools from "./lib/testData/composableStable.json"; +import { RawStablePool } from "../src"; + +describe("ComposableStable Swap tests", () => { + const chainId = ChainId.MAINNET; + const rpcUrl = "http://127.0.0.1:8545"; + const mockPoolProvider = new MockPoolProvider( + testPools.pools as RawStablePool[] + ); + const onChainPoolDataEnricher = new OnChainPoolDataEnricher( + chainId, + rpcUrl, + BATCHSIZE[chainId], + VAULT[chainId] + ); + + const sor = new SmartOrderRouter({ + chainId, + poolDataProviders: mockPoolProvider, + poolDataEnrichers: onChainPoolDataEnricher, + rpcUrl: rpcUrl, + }); + + const USDC = new Token( + chainId, + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + 6, + "USDC" + ); + const USDT = new Token( + chainId, + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + 6, + "USDT" + ); + const DAI = new Token( + chainId, + "0x6B175474E89094C44Da98b954EedeAC495271d0F", + 18, + "DAI" + ); + + const swapOptions: SwapOptions = { + block: 17473810n, + }; + + let pools: BasePool[]; + // Since constructing a Swap mutates the pool balances, we refetch for each test + // May be a better way to deep clone a BasePool[] class instead + beforeEach(async () => { + pools = await sor.fetchAndCachePools(swapOptions.block); + }); + + test("DAI -> USDT givenIn ComposableStable", async () => { + const inputAmount = TokenAmount.fromHumanAmount(DAI, "100000"); + + const swap = await sorGetSwapsWithPools( + DAI, + USDT, + SwapKind.GivenIn, + inputAmount, + pools, + swapOptions + ); + + if (!swap) throw new Error("Swap is undefined"); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(inputAmount.amount); + expect(swap.outputAmount.amount).toEqual(swap.quote.amount); + expect(swap.paths.length).toEqual(1); + expect(swap.paths[0].pools.length).toEqual(1); + }); + + test("USDC -> DAI givenOut ComposableStable", async () => { + const outputAmount = TokenAmount.fromHumanAmount(DAI, "1000000"); + + const swap = await sorGetSwapsWithPools( + USDC, + DAI, + SwapKind.GivenOut, + outputAmount, + pools, + swapOptions + ); + + if (!swap) throw new Error("Swap is undefined"); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + expect(swap.quote.amount).toEqual(onchain.amount); + }); +}); diff --git a/test/lib/testData/composableStable.json b/test/lib/testData/composableStable.json new file mode 100644 index 00000000..c7d21491 --- /dev/null +++ b/test/lib/testData/composableStable.json @@ -0,0 +1,94 @@ +{ + "pools": [ + { + "id": "0x79c58f70905f734641735bc61e45c19dd9ad60bc0000000000000000000004e7", + "address": "0x79c58f70905f734641735bc61e45c19dd9ad60bc", + "poolType": "ComposableStable", + "poolTypeVersion": 3, + "name": "Balancer USDC-DAI-USDT Stable Pool", + "tokens": [ + { + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", + "balance": "1634347.131341290124462272", + "weight": null, + "priceRate": "1", + "decimals": 18, + "name": "Dai Stablecoin", + "index": 0, + "symbol": "DAI", + "token": { "latestFXPrice": "1.00011" } + }, + { + "address": "0x79c58f70905f734641735bc61e45c19dd9ad60bc", + "balance": "2596148429321223.432121172010498472", + "weight": null, + "priceRate": "1", + "decimals": 18, + "name": "Balancer USDC-DAI-USDT Stable Pool", + "index": 1, + "symbol": "USDC-DAI-USDT", + "token": { "latestFXPrice": null } + }, + { + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "balance": "1628729.447793", + "weight": null, + "priceRate": "1", + "decimals": 6, + "name": "USD Coin", + "index": 2, + "symbol": "USDC", + "token": { "latestFXPrice": "1" } + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "balance": "2217712.39191", + "weight": null, + "priceRate": "1", + "decimals": 6, + "name": "Tether USD", + "index": 3, + "symbol": "USDT", + "token": { "latestFXPrice": null } + } + ], + "tokensList": [ + "0x6b175474e89094c44da98b954eedeac495271d0f", + "0x79c58f70905f734641735bc61e45c19dd9ad60bc", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0xdac17f958d2ee523a2206206994597c13d831ec7" + ], + "swapEnabled": true, + "swapFee": "0.00005", + "amp": "2000", + "totalLiquidity": "5480788.971044290124462272", + "totalShares": "5476334.783405922127632546", + "mainIndex": null, + "wrappedIndex": null, + "lowerTarget": null, + "upperTarget": null, + "alpha": null, + "beta": null, + "c": null, + "delta": null, + "dSq": null, + "epsilon": null, + "lambda": null, + "root3Alpha": null, + "s": null, + "sqrtAlpha": null, + "sqrtBeta": null, + "tauAlphaX": null, + "tauAlphaY": null, + "tauBetaX": null, + "tauBetaY": null, + "u": null, + "v": null, + "w": null, + "z": null, + "inRecoveryMode": false, + "isPaused": false, + "liquidity": "0" + } + ] +} diff --git a/test/lib/testData/weighted.json b/test/lib/testData/weighted.json new file mode 100644 index 00000000..a771001f --- /dev/null +++ b/test/lib/testData/weighted.json @@ -0,0 +1,70 @@ +{ + "pools": [ + { + "id": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014", + "address": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", + "poolType": "Weighted", + "poolTypeVersion": 1, + "name": "Balancer 80 BAL 20 WETH", + "tokens": [ + { + "address": "0xba100000625a3754423978a60c9317c58a424e3d", + "balance": "31701973.223639147427614953", + "weight": "0.8", + "priceRate": "1", + "decimals": 18, + "name": "Balancer", + "index": 0, + "symbol": "BAL", + "token": { "latestFXPrice": null } + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "balance": "20294.720800533482038095", + "weight": "0.2", + "priceRate": "1", + "decimals": 18, + "name": "Wrapped Ether", + "index": 1, + "symbol": "WETH", + "token": { "latestFXPrice": null } + } + ], + "tokensList": [ + "0xba100000625a3754423978a60c9317c58a424e3d", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ], + "swapEnabled": true, + "swapFee": "0.01", + "amp": null, + "totalLiquidity": "173627239.2541944837545942771966962", + "totalShares": "14221698.569579854728322716", + "mainIndex": null, + "wrappedIndex": null, + "lowerTarget": null, + "upperTarget": null, + "alpha": null, + "beta": null, + "c": null, + "delta": null, + "dSq": null, + "epsilon": null, + "lambda": null, + "root3Alpha": null, + "s": null, + "sqrtAlpha": null, + "sqrtBeta": null, + "tauAlphaX": null, + "tauAlphaY": null, + "tauBetaX": null, + "tauBetaY": null, + "u": null, + "v": null, + "w": null, + "z": null, + "inRecoveryMode": false, + "isPaused": false, + "liquidity": "0" + } + ] +} diff --git a/test/weightedPool.integration.test.ts b/test/weightedPool.integration.test.ts new file mode 100644 index 00000000..6e76c7c9 --- /dev/null +++ b/test/weightedPool.integration.test.ts @@ -0,0 +1,115 @@ +// pnpm test -- test/weightedPool.integration.test.ts +import { beforeEach, describe, expect, test } from "vitest"; +import dotenv from "dotenv"; +dotenv.config(); + +import { SmartOrderRouter } from "../src/sor"; +import { sorGetSwapsWithPools } from "../src/static"; +import { ChainId, ETH, BATCHSIZE, VAULT } from "../src/utils"; +import { Token, TokenAmount } from "../src/entities"; +import { OnChainPoolDataEnricher } from "../src/data/enrichers/onChainPoolDataEnricher"; +import { SwapKind, SwapOptions } from "../src/types"; +import { BasePool } from "../src/entities/pools"; +import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; + +import testPools from "./lib/testData/weighted.json"; +import { RawWeightedPool } from "../src"; + +describe( + "Weighted Swap tests", + () => { + const chainId = ChainId.MAINNET; + const rpcUrl = "http://127.0.0.1:8545"; + const mockPoolProvider = new MockPoolProvider( + testPools.pools as RawWeightedPool[] + ); + const onChainPoolDataEnricher = new OnChainPoolDataEnricher( + chainId, + rpcUrl, + BATCHSIZE[chainId], + VAULT[chainId] + ); + + const sor = new SmartOrderRouter({ + chainId, + poolDataProviders: mockPoolProvider, + poolDataEnrichers: onChainPoolDataEnricher, + rpcUrl: rpcUrl, + }); + + const BAL = new Token( + chainId, + "0xba100000625a3754423978a60c9317c58a424e3D", + 18, + "BAL" + ); + + const swapOptions: SwapOptions = { + block: 17473810n, + }; + + let pools: BasePool[]; + // Since constructing a Swap mutates the pool balances, we refetch for each test + // May be a better way to deep clone a BasePool[] class instead + beforeEach(async () => { + pools = await sor.fetchAndCachePools(swapOptions.block); + }); + + // ETH -> BAL swapGivenIn single hop + // Weighted pool + // 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014 + test("Native ETH -> Token givenIn single hop", async () => { + const inputAmount = TokenAmount.fromHumanAmount(ETH, "1"); + + const swap = await sorGetSwapsWithPools( + ETH, + BAL, + SwapKind.GivenIn, + inputAmount, + pools, + swapOptions + ); + + if (!swap) throw new Error("Swap is undefined"); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(inputAmount.amount); + expect(swap.outputAmount.amount).toEqual(swap.quote.amount); + expect(swap.paths.length).toEqual(1); + expect(swap.paths[0].pools.length).toEqual(1); + expect(swap.paths[0].pools[0].id).toEqual( + "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014" + ); + }); + + // ETH -> BAL swapGivenOut single hop + // Weighted pool + // 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014 + test("Native ETH -> Token givenOut single hop", async () => { + const outputAmount = TokenAmount.fromHumanAmount(BAL, "100"); + + const swap = await sorGetSwapsWithPools( + ETH, + BAL, + SwapKind.GivenOut, + outputAmount, + pools, + swapOptions + ); + + if (!swap) throw new Error("Swap is undefined"); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(swap.quote.amount); + expect(swap.outputAmount.amount).toEqual(outputAmount.amount); + expect(swap.paths.length).toEqual(1); + expect(swap.paths[0].pools.length).toEqual(1); + expect(swap.paths[0].pools[0].id).toEqual( + "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014" + ); + }); + } +); From fc26998899973ecd87522f185d1054500d8750e3 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 31 Oct 2023 14:45:11 +0000 Subject: [PATCH 146/199] test: Add Fantom Anvil config and Fantom integration test. --- test/anvil/anvil-global-setup.ts | 7 + test/fantom.test.ts | 103 +++++++++ test/lib/testData/fantom_65313450.json | 70 ++++++ test/sor.test.ts | 287 ------------------------- 4 files changed, 180 insertions(+), 287 deletions(-) create mode 100644 test/fantom.test.ts create mode 100644 test/lib/testData/fantom_65313450.json delete mode 100644 test/sor.test.ts diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 19c370d3..4fc37a2b 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -25,6 +25,13 @@ const NETWORKS: Networks = [ // Note - this has to be >= highest blockNo used in tests forkBlockNumber: 44215395n, }, + { + rpcEnv: 'FANTOM_RPC_URL', + // Public Fantom RPCs are usually unreliable + fallBackRpc: undefined, + port: 8138, + forkBlockNumber: 65313450n, + }, ]; export default async function () { diff --git a/test/fantom.test.ts b/test/fantom.test.ts new file mode 100644 index 00000000..f6234ff5 --- /dev/null +++ b/test/fantom.test.ts @@ -0,0 +1,103 @@ +// pnpm test -- test/fantom.test.ts +import { beforeEach, describe, expect, test } from "vitest"; +import dotenv from "dotenv"; +dotenv.config(); + +import { SmartOrderRouter } from "../src/sor"; +import { sorGetSwapsWithPools } from "../src/static"; +import { ChainId, NATIVE_ASSETS, BATCHSIZE, VAULT } from "../src/utils"; +import { Token, TokenAmount } from "../src/entities"; +import { OnChainPoolDataEnricher } from "../src/data/enrichers/onChainPoolDataEnricher"; +import { SwapKind, SwapOptions } from "../src/types"; +import { BasePool } from "../src/entities/pools"; +import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; + +import testPools from "./lib/testData/fantom_65313450.json"; +import { RawBasePool } from "../src"; + +describe( + "Fantom SOR", + () => { + const chainId = ChainId.FANTOM; + const inputToken = NATIVE_ASSETS[chainId]; + const rpcUrl = "http://127.0.0.1:8138/"; + const mockPoolProvider = new MockPoolProvider( + testPools.pools as RawBasePool[] + ); + const onChainPoolDataEnricher = new OnChainPoolDataEnricher( + chainId, + rpcUrl, + BATCHSIZE[chainId], + VAULT[chainId] + ); + + const sor = new SmartOrderRouter({ + chainId, + poolDataProviders: mockPoolProvider, + poolDataEnrichers: onChainPoolDataEnricher, + rpcUrl: rpcUrl, + }); + + const BEETS = new Token( + chainId, + "0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e", + 18, + "BEETS" + ); + + const swapOptions: SwapOptions = { + block: 65313450n, + }; + + let pools: BasePool[]; + // Since constructing a Swap mutates the pool balances, we refetch for each test + // May be a better way to deep clone a BasePool[] class instead + beforeEach(async () => { + pools = await sor.fetchAndCachePools(swapOptions.block); + }); + + describe("Native Swaps", () => { + test("Native -> Token givenIn", async () => { + const inputAmount = TokenAmount.fromHumanAmount(inputToken, "100"); + + const swap = await sorGetSwapsWithPools( + inputToken, + BEETS, + SwapKind.GivenIn, + inputAmount, + pools, + swapOptions + ); + + if (!swap) throw new Error("Swap is undefined"); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(inputAmount.amount); + expect(swap.outputAmount.amount).toEqual(swap.quote.amount); + }); + + test("Native ETH -> Token givenOut", async () => { + const outputAmount = TokenAmount.fromHumanAmount(BEETS, "100000"); + + const swap = await sorGetSwapsWithPools( + inputToken, + BEETS, + SwapKind.GivenOut, + outputAmount, + pools, + swapOptions + ); + + if (!swap) throw new Error("Swap is undefined"); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(swap.quote.amount); + expect(swap.outputAmount.amount).toEqual(outputAmount.amount); + }); + }); + } +); diff --git a/test/lib/testData/fantom_65313450.json b/test/lib/testData/fantom_65313450.json new file mode 100644 index 00000000..558d9145 --- /dev/null +++ b/test/lib/testData/fantom_65313450.json @@ -0,0 +1,70 @@ +{ + "pools": [ + { + "id": "0x9e4341acef4147196e99d648c5e43b3fc9d026780002000000000000000005ec", + "address": "0x9e4341acef4147196e99d648c5e43b3fc9d02678", + "poolType": "Weighted", + "poolTypeVersion": 2, + "name": "Fresh BEETS", + "tokens": [ + { + "address": "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83", + "balance": "1529628.909214834943938725", + "weight": "0.2", + "priceRate": "1", + "decimals": 18, + "name": "Wrapped Fantom", + "index": 0, + "symbol": "WFTM", + "token": { "latestFXPrice": null } + }, + { + "address": "0xf24bcf4d1e507740041c9cfd2dddb29585adce1e", + "balance": "81750834.894524234143091925", + "weight": "0.8", + "priceRate": "1", + "decimals": 18, + "name": "BeethovenxToken", + "index": 1, + "symbol": "BEETS", + "token": { "latestFXPrice": null } + } + ], + "tokensList": [ + "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83", + "0xf24bcf4d1e507740041c9cfd2dddb29585adce1e" + ], + "swapEnabled": true, + "swapFee": "0.01", + "amp": null, + "totalLiquidity": "2679688.778562431478805364412296083", + "totalShares": "72890712.800024961804215653", + "mainIndex": null, + "wrappedIndex": null, + "lowerTarget": null, + "upperTarget": null, + "alpha": null, + "beta": null, + "c": null, + "delta": null, + "dSq": null, + "epsilon": null, + "lambda": null, + "root3Alpha": null, + "s": null, + "sqrtAlpha": null, + "sqrtBeta": null, + "tauAlphaX": null, + "tauAlphaY": null, + "tauBetaX": null, + "tauBetaY": null, + "u": null, + "v": null, + "w": null, + "z": null, + "inRecoveryMode": true, + "isPaused": false, + "liquidity": "0" + } + ] +} diff --git a/test/sor.test.ts b/test/sor.test.ts deleted file mode 100644 index c3ca0667..00000000 --- a/test/sor.test.ts +++ /dev/null @@ -1,287 +0,0 @@ -// pnpm test -- test/sor.test.ts -import { beforeEach, describe, expect, test } from 'vitest'; -import dotenv from 'dotenv'; -dotenv.config(); - -import { SmartOrderRouter } from '../src/sor'; -import { sorGetSwapsWithPools } from '../src/static'; -import { SubgraphPoolProvider } from '../src/data/providers/subgraphPoolProvider'; -import { ChainId, ETH, NATIVE_ASSETS, BATCHSIZE, VAULT } from '../src/utils'; -import { Token, TokenAmount } from '../src/entities'; -import { OnChainPoolDataEnricher } from '../src/data/enrichers/onChainPoolDataEnricher'; -import { SwapKind, SwapOptions } from '../src/types'; -import { BasePool } from '../src/entities/pools'; - -describe( - 'SmartOrderRouter', - () => { - describe('Mainnet', () => { - const chainId = ChainId.MAINNET; - const rpcUrl = - process.env.ETHEREUM_RPC_URL || 'https://eth.llamarpc.com'; - const subgraphPoolDataService = new SubgraphPoolProvider(chainId); - const onChainPoolDataEnricher = new OnChainPoolDataEnricher( - chainId, - rpcUrl, - BATCHSIZE[chainId], - VAULT[chainId], - ); - - const sor = new SmartOrderRouter({ - chainId, - poolDataProviders: subgraphPoolDataService, - poolDataEnrichers: onChainPoolDataEnricher, - rpcUrl: rpcUrl, - }); - - const BAL = new Token( - chainId, - '0xba100000625a3754423978a60c9317c58a424e3D', - 18, - 'BAL', - ); - const USDC = new Token( - chainId, - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - 6, - 'USDC', - ); - const USDT = new Token( - chainId, - '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 6, - 'USDT', - ); - const DAI = new Token( - chainId, - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - 18, - 'DAI', - ); - - const swapOptions: SwapOptions = { - block: 17473810n, - }; - - let pools: BasePool[]; - // Since constructing a Swap mutates the pool balances, we refetch for each test - // May be a better way to deep clone a BasePool[] class instead - beforeEach(async () => { - pools = await sor.fetchAndCachePools(swapOptions.block); - }); - - describe('Weighted Pools', () => { - // ETH -> BAL swapGivenIn single hop - // Weighted pool - // 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014 - test('Native ETH -> Token givenIn single hop', async () => { - const inputAmount = TokenAmount.fromHumanAmount(ETH, '1'); - - const swap = await sorGetSwapsWithPools( - ETH, - BAL, - SwapKind.GivenIn, - inputAmount, - pools, - swapOptions, - ); - - if (!swap) throw new Error('Swap is undefined'); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(inputAmount.amount); - expect(swap.outputAmount.amount).toEqual(swap.quote.amount); - expect(swap.paths.length).toEqual(1); - expect(swap.paths[0].pools.length).toEqual(1); - expect(swap.paths[0].pools[0].id).toEqual( - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', - ); - }); - - // ETH -> BAL swapGivenOut single hop - // Weighted pool - // 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014 - test('Native ETH -> Token givenOut single hop', async () => { - const outputAmount = TokenAmount.fromHumanAmount( - BAL, - '100', - ); - - const swap = await sorGetSwapsWithPools( - ETH, - BAL, - SwapKind.GivenOut, - outputAmount, - pools, - swapOptions, - ); - - if (!swap) throw new Error('Swap is undefined'); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(swap.quote.amount); - expect(swap.outputAmount.amount).toEqual( - outputAmount.amount, - ); - expect(swap.paths.length).toEqual(1); - expect(swap.paths[0].pools.length).toEqual(1); - expect(swap.paths[0].pools[0].id).toEqual( - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', - ); - }); - }); - - describe('Stable Pools', () => { - test('DAI -> USDT givenIn ComposableStable', async () => { - const inputAmount = TokenAmount.fromHumanAmount( - DAI, - '100000', - ); - - const swap = await sorGetSwapsWithPools( - DAI, - USDT, - SwapKind.GivenIn, - inputAmount, - pools, - swapOptions, - ); - - if (!swap) throw new Error('Swap is undefined'); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(inputAmount.amount); - expect(swap.outputAmount.amount).toEqual(swap.quote.amount); - expect(swap.paths.length).toEqual(1); - expect(swap.paths[0].pools.length).toEqual(1); - }); - - test('USDC -> DAI givenOut ComposableStable', async () => { - const outputAmount = TokenAmount.fromHumanAmount( - DAI, - '1000000', - ); - - const swap = await sorGetSwapsWithPools( - USDC, - DAI, - SwapKind.GivenOut, - outputAmount, - pools, - swapOptions, - ); - - if (!swap) throw new Error('Swap is undefined'); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - expect(swap.quote.amount).toEqual(onchain.amount); - }); - }); - }); - describe('Fantom', () => { - const chainId = ChainId.FANTOM; - const inputToken = NATIVE_ASSETS[chainId]; - const rpcUrl = process.env.FANTOM_RPC_URL || ''; - const subgraphPoolDataService = new SubgraphPoolProvider( - chainId, - undefined, - { - poolIdIn: [ - '0x9e4341acef4147196e99d648c5e43b3fc9d026780002000000000000000005ec', - ], - }, - ); - const onChainPoolDataEnricher = new OnChainPoolDataEnricher( - chainId, - rpcUrl, - BATCHSIZE[chainId], - VAULT[chainId], - ); - - const sor = new SmartOrderRouter({ - chainId, - poolDataProviders: subgraphPoolDataService, - poolDataEnrichers: onChainPoolDataEnricher, - rpcUrl: rpcUrl, - }); - - const BEETS = new Token( - chainId, - '0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e', - 18, - 'BEETS', - ); - - const swapOptions: SwapOptions = { - block: 65313450n, - }; - - let pools: BasePool[]; - // Since constructing a Swap mutates the pool balances, we refetch for each test - // May be a better way to deep clone a BasePool[] class instead - beforeEach(async () => { - pools = await sor.fetchAndCachePools(swapOptions.block); - }); - - describe('Native Swaps', () => { - test('Native -> Token givenIn', async () => { - const inputAmount = TokenAmount.fromHumanAmount( - inputToken, - '100', - ); - - const swap = await sorGetSwapsWithPools( - inputToken, - BEETS, - SwapKind.GivenIn, - inputAmount, - pools, - swapOptions, - ); - - if (!swap) throw new Error('Swap is undefined'); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(inputAmount.amount); - expect(swap.outputAmount.amount).toEqual(swap.quote.amount); - }); - - test('Native ETH -> Token givenOut', async () => { - const outputAmount = TokenAmount.fromHumanAmount( - BEETS, - '100000', - ); - - const swap = await sorGetSwapsWithPools( - inputToken, - BEETS, - SwapKind.GivenOut, - outputAmount, - pools, - swapOptions, - ); - - if (!swap) throw new Error('Swap is undefined'); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(swap.quote.amount); - expect(swap.outputAmount.amount).toEqual( - outputAmount.amount, - ); - }); - }); - }); - }, - { - timeout: 120000, - }, -); From 6cdd24121cab1cecabb4078e248336959f33b550 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 31 Oct 2023 14:54:01 +0000 Subject: [PATCH 147/199] refactor: Reorg and rename pool test files. --- test/composableStable.integration.test.ts | 2 +- test/fantom.test.ts | 2 +- test/fxPool.integration.test.ts | 2 +- test/fxPool.test.ts | 2 +- test/gyro2Math.test.ts | 2 +- test/gyro2Pool.test.ts | 2 +- test/gyro3Pool.integration.test.ts | 2 +- test/gyro3Pool.test.ts | 2 +- test/gyroEMath.test.ts | 5 +++-- test/gyroEPool.test.ts | 2 +- test/gyroEV2Pool.integration.test.ts | 2 +- test/gyroEV2Pool.test.ts | 2 +- .../composableStable_17473810.json} | 0 test/lib/testData/{ => testPools}/fantom_65313450.json | 0 .../fx_43667355.json} | 0 .../{gyro2TestPool.json => testPools/gyro2.json} | 10 ---------- .../gyro3_44133130.json} | 10 ---------- .../gyroE_44215395.json} | 10 ---------- .../weighted_17473810.json} | 0 test/weightedPool.integration.test.ts | 2 +- 20 files changed, 15 insertions(+), 44 deletions(-) rename test/lib/testData/{composableStable.json => testPools/composableStable_17473810.json} (100%) rename test/lib/testData/{ => testPools}/fantom_65313450.json (100%) rename test/lib/testData/{fxPool_43667355.json => testPools/fx_43667355.json} (100%) rename test/lib/testData/{gyro2TestPool.json => testPools/gyro2.json} (85%) rename test/lib/testData/{gyro3TestPool.json => testPools/gyro3_44133130.json} (93%) rename test/lib/testData/{gyroETestPool.json => testPools/gyroE_44215395.json} (96%) rename test/lib/testData/{weighted.json => testPools/weighted_17473810.json} (100%) diff --git a/test/composableStable.integration.test.ts b/test/composableStable.integration.test.ts index 72981605..1a91dc25 100644 --- a/test/composableStable.integration.test.ts +++ b/test/composableStable.integration.test.ts @@ -12,7 +12,7 @@ import { SwapKind, SwapOptions } from "../src/types"; import { BasePool } from "../src/entities/pools"; import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; -import testPools from "./lib/testData/composableStable.json"; +import testPools from "./lib/testData/testPools/composableStable_17473810.json"; import { RawStablePool } from "../src"; describe("ComposableStable Swap tests", () => { diff --git a/test/fantom.test.ts b/test/fantom.test.ts index f6234ff5..d5845848 100644 --- a/test/fantom.test.ts +++ b/test/fantom.test.ts @@ -12,7 +12,7 @@ import { SwapKind, SwapOptions } from "../src/types"; import { BasePool } from "../src/entities/pools"; import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; -import testPools from "./lib/testData/fantom_65313450.json"; +import testPools from "./lib/testData/testPools/fantom_65313450.json"; import { RawBasePool } from "../src"; describe( diff --git a/test/fxPool.integration.test.ts b/test/fxPool.integration.test.ts index bb962798..37c01c89 100644 --- a/test/fxPool.integration.test.ts +++ b/test/fxPool.integration.test.ts @@ -17,7 +17,7 @@ import { } from '../src'; import { MockPoolProvider } from './lib/utils/mockPoolProvider'; -import testPools from './lib/testData/fxPool_43667355.json'; +import testPools from './lib/testData/testPools/fx_43667355.json'; describe('fx integration tests', () => { const chainId = ChainId.POLYGON; diff --git a/test/fxPool.test.ts b/test/fxPool.test.ts index 8f73aad5..51040f23 100644 --- a/test/fxPool.test.ts +++ b/test/fxPool.test.ts @@ -1,7 +1,7 @@ // pnpm test -- fxPool.test.ts import { describe, expect, test } from 'vitest'; import { ChainId, RawFxPool, SwapKind, Token } from '../src'; -import testPools from './lib/testData/fxPool_43667355.json'; +import testPools from './lib/testData/testPools/fx_43667355.json'; import { CurveMathRevert, FxPool, FxPoolToken } from '../src/entities/pools/fx'; import { parseFixedCurveParam } from '../src/entities/pools/fx/helpers'; import { parseUnits } from 'viem'; diff --git a/test/gyro2Math.test.ts b/test/gyro2Math.test.ts index 746a4f77..b982bad9 100644 --- a/test/gyro2Math.test.ts +++ b/test/gyro2Math.test.ts @@ -1,6 +1,6 @@ // pnpm test -- gyro2Math.test.ts import { describe, expect, test } from 'vitest'; -import testPools from './lib/testData/gyro2TestPool.json'; +import testPools from './lib/testData/testPools/gyro2.json'; import { ChainId, RawGyro2Pool, Token, TokenAmount, WAD } from '../src'; import { _calculateQuadratic, diff --git a/test/gyro2Pool.test.ts b/test/gyro2Pool.test.ts index 809b2403..2cd7f032 100644 --- a/test/gyro2Pool.test.ts +++ b/test/gyro2Pool.test.ts @@ -5,7 +5,7 @@ dotenv.config(); import { parseEther } from 'viem'; -import testPools from './lib/testData/gyro2TestPool.json'; +import testPools from './lib/testData/testPools/gyro2.json'; import { MockPoolDataEnricher } from './lib/utils/mockPoolEnricher'; import { MockPoolProvider } from './lib/utils/mockPoolProvider'; import { diff --git a/test/gyro3Pool.integration.test.ts b/test/gyro3Pool.integration.test.ts index 831a9f7f..8d276310 100644 --- a/test/gyro3Pool.integration.test.ts +++ b/test/gyro3Pool.integration.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, test } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); -import testPools from './lib/testData/gyro3TestPool.json'; +import testPools from './lib/testData/testPools/gyro3_44133130.json'; import { ChainId } from '../src/utils'; import { RawGyro3Pool } from '../src/data/types'; import { diff --git a/test/gyro3Pool.test.ts b/test/gyro3Pool.test.ts index 98170121..baaedb07 100644 --- a/test/gyro3Pool.test.ts +++ b/test/gyro3Pool.test.ts @@ -1,6 +1,6 @@ // pnpm test -- gyro3Pool.test.ts import { describe, expect, test } from 'vitest'; -import testPools from './lib/testData/gyro3TestPool.json'; +import testPools from './lib/testData/testPools/gyro3_44133130.json'; import { ChainId, RawGyro3Pool, SwapKind, Token, TokenAmount } from '../src'; import { Gyro3Pool } from '../src/entities/pools/gyro3'; diff --git a/test/gyroEMath.test.ts b/test/gyroEMath.test.ts index bb98a973..a441f570 100644 --- a/test/gyroEMath.test.ts +++ b/test/gyroEMath.test.ts @@ -1,9 +1,10 @@ // pnpm test -- gyroEMath.test.ts import { describe, expect, test } from 'vitest'; -import testPools from './lib/testData/gyroETestPool.json'; +import testPools from './lib/testData/testPools/gyroE_44215395.json'; import { RawGyroEPool } from '../src/data/types'; import { ChainId } from '../src/utils'; -import { GyroEPool, Vector2 } from '../src/entities/pools/gyroE/gyroEPool'; +import { GyroEPool } from '../src/entities/pools/gyroE/gyroEPool'; +import { Vector2 } from '../src/entities/pools/gyroE/types'; import { Token, TokenAmount } from '../src/entities'; import { calculateInvariantWithError } from '../src/entities/pools/gyroE/gyroEMath'; import { diff --git a/test/gyroEPool.test.ts b/test/gyroEPool.test.ts index 1ada9153..1f4f042d 100644 --- a/test/gyroEPool.test.ts +++ b/test/gyroEPool.test.ts @@ -1,6 +1,6 @@ // pnpm test -- test/gyroEPool.test.ts import { describe, expect, test } from 'vitest'; -import testPools from './lib/testData/gyroETestPool.json'; +import testPools from './lib/testData/testPools/gyroE_44215395.json'; import { ChainId, SwapKind, Token, TokenAmount } from '../src'; import { RawGyroEPool } from '../src/data/types'; import { GyroEPool } from '../src/entities/pools/gyroE'; diff --git a/test/gyroEV2Pool.integration.test.ts b/test/gyroEV2Pool.integration.test.ts index 9e577e51..e94c4fc4 100644 --- a/test/gyroEV2Pool.integration.test.ts +++ b/test/gyroEV2Pool.integration.test.ts @@ -3,7 +3,7 @@ import { beforeAll, beforeEach, describe, expect, test } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); -import testPools from './lib/testData/gyroETestPool.json'; +import testPools from './lib/testData/testPools/gyroE_44215395.json'; import { BATCHSIZE, ChainId, VAULT } from '../src/utils'; import { BasePool, diff --git a/test/gyroEV2Pool.test.ts b/test/gyroEV2Pool.test.ts index 2dd3d11d..ea05dc58 100644 --- a/test/gyroEV2Pool.test.ts +++ b/test/gyroEV2Pool.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test } from 'vitest'; import { ChainId, RawGyroEPool, SwapKind, Token, TokenAmount } from '../src'; import { GyroEPool } from '../src/entities/pools/gyroE'; -import testPools from './lib/testData/gyroETestPool.json'; +import testPools from './lib/testData/testPools/gyroE_44215395.json'; describe('gyroEPool tests', () => { const testPool = { ...testPools }.pools[1] as RawGyroEPool; diff --git a/test/lib/testData/composableStable.json b/test/lib/testData/testPools/composableStable_17473810.json similarity index 100% rename from test/lib/testData/composableStable.json rename to test/lib/testData/testPools/composableStable_17473810.json diff --git a/test/lib/testData/fantom_65313450.json b/test/lib/testData/testPools/fantom_65313450.json similarity index 100% rename from test/lib/testData/fantom_65313450.json rename to test/lib/testData/testPools/fantom_65313450.json diff --git a/test/lib/testData/fxPool_43667355.json b/test/lib/testData/testPools/fx_43667355.json similarity index 100% rename from test/lib/testData/fxPool_43667355.json rename to test/lib/testData/testPools/fx_43667355.json diff --git a/test/lib/testData/gyro2TestPool.json b/test/lib/testData/testPools/gyro2.json similarity index 85% rename from test/lib/testData/gyro2TestPool.json rename to test/lib/testData/testPools/gyro2.json index 09b37799..404bf9c6 100644 --- a/test/lib/testData/gyro2TestPool.json +++ b/test/lib/testData/testPools/gyro2.json @@ -1,14 +1,4 @@ { - "tradeInfo": { - "SwapType": "n/a", - "TokenIn": "n/a", - "TokenOut": "n/a", - "NoPools": 1, - "SwapAmount": "n/a", - "GasPrice": "n/a", - "SwapAmountDecimals": "n/a", - "ReturnAmountDecimals": "n/a" - }, "pools": [ { "id": "0xebfed10e11dc08fcda1af1fda146945e8710f22e0000000000000000000000ff", diff --git a/test/lib/testData/gyro3TestPool.json b/test/lib/testData/testPools/gyro3_44133130.json similarity index 93% rename from test/lib/testData/gyro3TestPool.json rename to test/lib/testData/testPools/gyro3_44133130.json index dec03547..a9bdc58b 100644 --- a/test/lib/testData/gyro3TestPool.json +++ b/test/lib/testData/testPools/gyro3_44133130.json @@ -1,14 +1,4 @@ { - "tradeInfo": { - "SwapType": "n/a", - "TokenIn": "n/a", - "TokenOut": "n/a", - "NoPools": 1, - "SwapAmount": "n/a", - "GasPrice": "n/a", - "SwapAmountDecimals": "n/a", - "ReturnAmountDecimals": "n/a" - }, "pools": [ { "id": "0xebfed10e11dc08fcda1af1fda146945e8710f22e0000000000000000000000ff", diff --git a/test/lib/testData/gyroETestPool.json b/test/lib/testData/testPools/gyroE_44215395.json similarity index 96% rename from test/lib/testData/gyroETestPool.json rename to test/lib/testData/testPools/gyroE_44215395.json index 81cdf43a..b684212f 100644 --- a/test/lib/testData/gyroETestPool.json +++ b/test/lib/testData/testPools/gyroE_44215395.json @@ -1,14 +1,4 @@ { - "tradeInfo": { - "SwapType": "n/a", - "TokenIn": "n/a", - "TokenOut": "n/a", - "NoPools": 1, - "SwapAmount": "n/a", - "GasPrice": "n/a", - "SwapAmountDecimals": "n/a", - "ReturnAmountDecimals": "n/a" - }, "pools": [ { "id": "0x12311573a96806182c01ef6c349948edc6635b040002000000000000000002ab", diff --git a/test/lib/testData/weighted.json b/test/lib/testData/testPools/weighted_17473810.json similarity index 100% rename from test/lib/testData/weighted.json rename to test/lib/testData/testPools/weighted_17473810.json diff --git a/test/weightedPool.integration.test.ts b/test/weightedPool.integration.test.ts index 6e76c7c9..8a6b554b 100644 --- a/test/weightedPool.integration.test.ts +++ b/test/weightedPool.integration.test.ts @@ -12,7 +12,7 @@ import { SwapKind, SwapOptions } from "../src/types"; import { BasePool } from "../src/entities/pools"; import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; -import testPools from "./lib/testData/weighted.json"; +import testPools from "./lib/testData/testPools/weighted_17473810.json"; import { RawWeightedPool } from "../src"; describe( From 4f1186e40141fc4f2e74b02b9efe3d25b4dece7f Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 3 Nov 2023 09:23:37 +0100 Subject: [PATCH 148/199] setup vitest jobids for anvil tests --- examples/exit/weighted.ts | 5 +- examples/join/weighted.ts | 5 +- test/anvil/anvil-global-setup.ts | 84 ++++++-- test/composableStable.integration.test.ts | 196 +++++++++--------- test/composableStableJoin.integration.test.ts | 3 +- test/fantom.integration.test.ts | 103 +++++++++ test/fantom.test.ts | 103 --------- test/fxPool.integration.test.ts | 8 +- test/gyro3Pool.integration.test.ts | 6 +- test/gyroEV2Pool.integration.test.ts | 6 +- test/vitest-setup.ts | 6 + test/weightedExit.integration.test.ts | 3 +- test/weightedJoin.integration.test.ts | 3 +- test/weightedPool.integration.test.ts | 165 ++++++++------- vitest.config.ts | 6 +- 15 files changed, 384 insertions(+), 318 deletions(-) create mode 100644 test/fantom.integration.test.ts delete mode 100644 test/fantom.test.ts create mode 100644 test/vitest-setup.ts diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index e7e4650e..10e092d4 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -28,18 +28,17 @@ import { forkSetup, sendTransactionGetBalances, } from '../../test/lib/utils/helper'; -import anvilGlobalSetup from '../../test/anvil/anvil-global-setup'; +import { ANVIL_NETWORKS, startFork } from '../../test/anvil/anvil-global-setup'; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH const chainId = ChainId.MAINNET; -const rpcUrl = 'http://127.0.0.1:8545/'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const slippage = Slippage.fromPercentage('1'); // 1% const exit = async () => { - await anvilGlobalSetup(); + const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const balancerApi = new BalancerApi(balancerApiUrl, 1); const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState( poolId, diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index 51b3dd42..bc0534f6 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -28,18 +28,17 @@ import { forkSetup, sendTransactionGetBalances, } from '../../test/lib/utils/helper'; -import anvilGlobalSetup from '../../test/anvil/anvil-global-setup'; +import { ANVIL_NETWORKS, startFork } from '../../test/anvil/anvil-global-setup'; const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH const chainId = ChainId.MAINNET; -const rpcUrl = 'http://127.0.0.1:8545/'; const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig const slippage = Slippage.fromPercentage('1'); // 1% const join = async () => { - await anvilGlobalSetup(); + const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const balancerApi = new BalancerApi(balancerApiUrl, 1); const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState( poolId, diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 4fc37a2b..7d0b0062 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -1,5 +1,7 @@ -import { CreateAnvilOptions, createAnvil } from '@viem/anvil'; +import { Anvil, CreateAnvilOptions, createAnvil } from '@viem/anvil'; import { sleep } from '../lib/utils/promises'; +import { ChainId } from '../../src/utils/constants'; +import os from 'os'; type NetworkSetup = { rpcEnv: string; @@ -8,16 +10,19 @@ type NetworkSetup = { forkBlockNumber: bigint; }; -type Networks = NetworkSetup[]; +type NetworksWithFork = Extract< + keyof typeof ChainId, + 'MAINNET' | 'POLYGON' | 'FANTOM' +>; -const NETWORKS: Networks = [ - { +export const ANVIL_NETWORKS: Record = { + MAINNET: { rpcEnv: 'ETHEREUM_RPC_URL', fallBackRpc: 'https://cloudflare-eth.com', port: 8545, forkBlockNumber: 18043296n, }, - { + POLYGON: { rpcEnv: 'POLYGON_RPC_URL', // Public Polygon RPCs are usually unreliable fallBackRpc: undefined, @@ -25,19 +30,22 @@ const NETWORKS: Networks = [ // Note - this has to be >= highest blockNo used in tests forkBlockNumber: 44215395n, }, - { + FANTOM: { rpcEnv: 'FANTOM_RPC_URL', // Public Fantom RPCs are usually unreliable fallBackRpc: undefined, port: 8138, forkBlockNumber: 65313450n, }, -]; +}; export default async function () { - for (const network of NETWORKS) { + const numberOfCpus = os.cpus().length; + for (const network of Object.values(ANVIL_NETWORKS)) { const config = getAnvilOptions(network); - await startFork(config); + for (let jobId = 0; jobId < numberOfCpus; jobId++) { + await startFork(config, jobId); + } } } @@ -64,19 +72,65 @@ function getAnvilOptions(network: NetworkSetup): CreateAnvilOptions { }; } -async function startFork(anvilOptions: CreateAnvilOptions) { +// Controls the current running forks to avoid starting the same fork twice +let runningForks: Record = {}; + +// Make sure that forks are stopped after each test suite +export async function stopAnvilForks() { + await Promise.all( + Object.values(runningForks).map(async (anvil) => { + // console.log('Stopping anvil fork', anvil.options); + return anvil.stop(); + }), + ); + runningForks = {}; +} + +/* + Starts an anvil fork with the given options. + In vitest, each thread is assigned a unique, numerical id (`process.env.VITEST_POOL_ID`). + When jobId is provided, the fork uses this id to create a different local rpc url (e.g. `http://127.0.0.1:<8545+jobId>/` + so that tests can be run in parallel (depending on the number of threads of the host machine) +*/ +export async function startFork( + network: NetworkSetup, + jobId = Number(process.env.VITEST_POOL_ID) || 0, +) { + const anvilOptions = getAnvilOptions(network); + + const defaultAnvilPort = 8545; + let port = (anvilOptions.port || defaultAnvilPort) + jobId; + //Skip 8550, 8551 ports because they are already used + if ([8550, 8551].includes(port)) port = 8552; + + if (!anvilOptions.forkUrl) { + throw Error( + 'Anvil forkUrl must have a value. Please review your anvil setup', + ); + } + const rpcUrl = `http://127.0.0.1:${port}`; + + // Avoid starting fork if it was running already + if (runningForks[rpcUrl]) return { rpcUrl }; + // https://www.npmjs.com/package/@viem/anvil - const anvil = createAnvil(anvilOptions); + const anvil = createAnvil({ ...anvilOptions, port }); + // Save reference to running fork + runningForks[rpcUrl] = anvil; + if (process.env.SKIP_GLOBAL_SETUP === 'true') { console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: -anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8555 --fork-block-number=17878719 +anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/ --port 8545 --fork-block-number=17878719 `); await sleep(5000); - return; + return { rpcUrl }; } console.log('🛠️ Starting anvil', { - port: anvilOptions.port, + port, forkBlockNumber: anvilOptions.forkBlockNumber, }); - return await anvil.start(); + await anvil.start(); + return { + rpcUrl, + }; } diff --git a/test/composableStable.integration.test.ts b/test/composableStable.integration.test.ts index 1a91dc25..afc47a09 100644 --- a/test/composableStable.integration.test.ts +++ b/test/composableStable.integration.test.ts @@ -1,107 +1,109 @@ // pnpm test -- test/composableStable.integration.test.ts -import { beforeEach, describe, expect, test } from "vitest"; -import dotenv from "dotenv"; +import { beforeEach, describe, expect, test } from 'vitest'; +import dotenv from 'dotenv'; dotenv.config(); -import { SmartOrderRouter } from "../src/sor"; -import { sorGetSwapsWithPools } from "../src/static"; -import { ChainId, BATCHSIZE, VAULT } from "../src/utils"; -import { Token, TokenAmount } from "../src/entities"; -import { OnChainPoolDataEnricher } from "../src/data/enrichers/onChainPoolDataEnricher"; -import { SwapKind, SwapOptions } from "../src/types"; -import { BasePool } from "../src/entities/pools"; -import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; - -import testPools from "./lib/testData/testPools/composableStable_17473810.json"; -import { RawStablePool } from "../src"; - -describe("ComposableStable Swap tests", () => { - const chainId = ChainId.MAINNET; - const rpcUrl = "http://127.0.0.1:8545"; - const mockPoolProvider = new MockPoolProvider( - testPools.pools as RawStablePool[] +import { SmartOrderRouter } from '../src/sor'; +import { sorGetSwapsWithPools } from '../src/static'; +import { ChainId, BATCHSIZE, VAULT } from '../src/utils'; +import { Token, TokenAmount } from '../src/entities'; +import { OnChainPoolDataEnricher } from '../src/data/enrichers/onChainPoolDataEnricher'; +import { SwapKind, SwapOptions } from '../src/types'; +import { BasePool } from '../src/entities/pools'; +import { MockPoolProvider } from './lib/utils/mockPoolProvider'; + +import testPools from './lib/testData/testPools/composableStable_17473810.json'; +import { RawStablePool } from '../src'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; + +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); + +describe('ComposableStable Swap tests', () => { + const chainId = ChainId.MAINNET; + const mockPoolProvider = new MockPoolProvider( + testPools.pools as RawStablePool[], ); - const onChainPoolDataEnricher = new OnChainPoolDataEnricher( - chainId, - rpcUrl, - BATCHSIZE[chainId], - VAULT[chainId] - ); - - const sor = new SmartOrderRouter({ - chainId, - poolDataProviders: mockPoolProvider, - poolDataEnrichers: onChainPoolDataEnricher, - rpcUrl: rpcUrl, - }); - - const USDC = new Token( - chainId, - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - 6, - "USDC" - ); - const USDT = new Token( - chainId, - "0xdAC17F958D2ee523a2206206994597C13D831ec7", - 6, - "USDT" - ); - const DAI = new Token( - chainId, - "0x6B175474E89094C44Da98b954EedeAC495271d0F", - 18, - "DAI" - ); - - const swapOptions: SwapOptions = { - block: 17473810n, - }; - - let pools: BasePool[]; - // Since constructing a Swap mutates the pool balances, we refetch for each test - // May be a better way to deep clone a BasePool[] class instead - beforeEach(async () => { - pools = await sor.fetchAndCachePools(swapOptions.block); - }); - - test("DAI -> USDT givenIn ComposableStable", async () => { - const inputAmount = TokenAmount.fromHumanAmount(DAI, "100000"); - - const swap = await sorGetSwapsWithPools( - DAI, - USDT, - SwapKind.GivenIn, - inputAmount, - pools, - swapOptions + const onChainPoolDataEnricher = new OnChainPoolDataEnricher( + chainId, + rpcUrl, + BATCHSIZE[chainId], + VAULT[chainId], ); - if (!swap) throw new Error("Swap is undefined"); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(inputAmount.amount); - expect(swap.outputAmount.amount).toEqual(swap.quote.amount); - expect(swap.paths.length).toEqual(1); - expect(swap.paths[0].pools.length).toEqual(1); - }); - - test("USDC -> DAI givenOut ComposableStable", async () => { - const outputAmount = TokenAmount.fromHumanAmount(DAI, "1000000"); - - const swap = await sorGetSwapsWithPools( - USDC, - DAI, - SwapKind.GivenOut, - outputAmount, - pools, - swapOptions + const sor = new SmartOrderRouter({ + chainId, + poolDataProviders: mockPoolProvider, + poolDataEnrichers: onChainPoolDataEnricher, + rpcUrl: rpcUrl, + }); + + const USDC = new Token( + chainId, + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + 6, + 'USDC', ); + const USDT = new Token( + chainId, + '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 6, + 'USDT', + ); + const DAI = new Token( + chainId, + '0x6B175474E89094C44Da98b954EedeAC495271d0F', + 18, + 'DAI', + ); + + const swapOptions: SwapOptions = { + block: 17473810n, + }; + + let pools: BasePool[]; + // Since constructing a Swap mutates the pool balances, we refetch for each test + // May be a better way to deep clone a BasePool[] class instead + beforeEach(async () => { + pools = await sor.fetchAndCachePools(swapOptions.block); + }); + + test('DAI -> USDT givenIn ComposableStable', async () => { + const inputAmount = TokenAmount.fromHumanAmount(DAI, '100000'); + + const swap = await sorGetSwapsWithPools( + DAI, + USDT, + SwapKind.GivenIn, + inputAmount, + pools, + swapOptions, + ); + + if (!swap) throw new Error('Swap is undefined'); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(inputAmount.amount); + expect(swap.outputAmount.amount).toEqual(swap.quote.amount); + expect(swap.paths.length).toEqual(1); + expect(swap.paths[0].pools.length).toEqual(1); + }); + + test('USDC -> DAI givenOut ComposableStable', async () => { + const outputAmount = TokenAmount.fromHumanAmount(DAI, '1000000'); + + const swap = await sorGetSwapsWithPools( + USDC, + DAI, + SwapKind.GivenOut, + outputAmount, + pools, + swapOptions, + ); - if (!swap) throw new Error("Swap is undefined"); + if (!swap) throw new Error('Swap is undefined'); - const onchain = await swap.query(rpcUrl, swapOptions.block); - expect(swap.quote.amount).toEqual(onchain.amount); - }); + const onchain = await swap.query(rpcUrl, swapOptions.block); + expect(swap.quote.amount).toEqual(onchain.amount); + }); }); diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 4a2583b4..127df7b7 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -36,9 +36,10 @@ import { assertSingleToken, assertProportional, } from './lib/utils/joinHelper'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const chainId = ChainId.MAINNET; -const rpcUrl = 'http://127.0.0.1:8545/'; const poolId = '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool diff --git a/test/fantom.integration.test.ts b/test/fantom.integration.test.ts new file mode 100644 index 00000000..d09c211b --- /dev/null +++ b/test/fantom.integration.test.ts @@ -0,0 +1,103 @@ +// pnpm test -- test/fantom.test.ts +import { beforeEach, describe, expect, test } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { SmartOrderRouter } from '../src/sor'; +import { sorGetSwapsWithPools } from '../src/static'; +import { ChainId, NATIVE_ASSETS, BATCHSIZE, VAULT } from '../src/utils'; +import { Token, TokenAmount } from '../src/entities'; +import { OnChainPoolDataEnricher } from '../src/data/enrichers/onChainPoolDataEnricher'; +import { SwapKind, SwapOptions } from '../src/types'; +import { BasePool } from '../src/entities/pools'; +import { MockPoolProvider } from './lib/utils/mockPoolProvider'; + +import testPools from './lib/testData/testPools/fantom_65313450.json'; +import { RawBasePool } from '../src'; + +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; + +const chainId = ChainId.FANTOM; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.FANTOM); + +describe('Fantom SOR', () => { + const inputToken = NATIVE_ASSETS[chainId]; + const mockPoolProvider = new MockPoolProvider( + testPools.pools as RawBasePool[], + ); + const onChainPoolDataEnricher = new OnChainPoolDataEnricher( + chainId, + rpcUrl, + BATCHSIZE[chainId], + VAULT[chainId], + ); + + const sor = new SmartOrderRouter({ + chainId, + poolDataProviders: mockPoolProvider, + poolDataEnrichers: onChainPoolDataEnricher, + rpcUrl: rpcUrl, + }); + + const BEETS = new Token( + chainId, + '0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e', + 18, + 'BEETS', + ); + + const swapOptions: SwapOptions = { + block: 65313450n, + }; + + let pools: BasePool[]; + // Since constructing a Swap mutates the pool balances, we refetch for each test + // May be a better way to deep clone a BasePool[] class instead + beforeEach(async () => { + pools = await sor.fetchAndCachePools(swapOptions.block); + }); + + describe('Native Swaps', () => { + test('Native -> Token givenIn', async () => { + const inputAmount = TokenAmount.fromHumanAmount(inputToken, '100'); + + const swap = await sorGetSwapsWithPools( + inputToken, + BEETS, + SwapKind.GivenIn, + inputAmount, + pools, + swapOptions, + ); + + if (!swap) throw new Error('Swap is undefined'); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(inputAmount.amount); + expect(swap.outputAmount.amount).toEqual(swap.quote.amount); + }); + + test('Native ETH -> Token givenOut', async () => { + const outputAmount = TokenAmount.fromHumanAmount(BEETS, '100000'); + + const swap = await sorGetSwapsWithPools( + inputToken, + BEETS, + SwapKind.GivenOut, + outputAmount, + pools, + swapOptions, + ); + + if (!swap) throw new Error('Swap is undefined'); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(swap.quote.amount); + expect(swap.outputAmount.amount).toEqual(outputAmount.amount); + }); + }); +}); diff --git a/test/fantom.test.ts b/test/fantom.test.ts deleted file mode 100644 index d5845848..00000000 --- a/test/fantom.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -// pnpm test -- test/fantom.test.ts -import { beforeEach, describe, expect, test } from "vitest"; -import dotenv from "dotenv"; -dotenv.config(); - -import { SmartOrderRouter } from "../src/sor"; -import { sorGetSwapsWithPools } from "../src/static"; -import { ChainId, NATIVE_ASSETS, BATCHSIZE, VAULT } from "../src/utils"; -import { Token, TokenAmount } from "../src/entities"; -import { OnChainPoolDataEnricher } from "../src/data/enrichers/onChainPoolDataEnricher"; -import { SwapKind, SwapOptions } from "../src/types"; -import { BasePool } from "../src/entities/pools"; -import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; - -import testPools from "./lib/testData/testPools/fantom_65313450.json"; -import { RawBasePool } from "../src"; - -describe( - "Fantom SOR", - () => { - const chainId = ChainId.FANTOM; - const inputToken = NATIVE_ASSETS[chainId]; - const rpcUrl = "http://127.0.0.1:8138/"; - const mockPoolProvider = new MockPoolProvider( - testPools.pools as RawBasePool[] - ); - const onChainPoolDataEnricher = new OnChainPoolDataEnricher( - chainId, - rpcUrl, - BATCHSIZE[chainId], - VAULT[chainId] - ); - - const sor = new SmartOrderRouter({ - chainId, - poolDataProviders: mockPoolProvider, - poolDataEnrichers: onChainPoolDataEnricher, - rpcUrl: rpcUrl, - }); - - const BEETS = new Token( - chainId, - "0xF24Bcf4d1e507740041C9cFd2DddB29585aDCe1e", - 18, - "BEETS" - ); - - const swapOptions: SwapOptions = { - block: 65313450n, - }; - - let pools: BasePool[]; - // Since constructing a Swap mutates the pool balances, we refetch for each test - // May be a better way to deep clone a BasePool[] class instead - beforeEach(async () => { - pools = await sor.fetchAndCachePools(swapOptions.block); - }); - - describe("Native Swaps", () => { - test("Native -> Token givenIn", async () => { - const inputAmount = TokenAmount.fromHumanAmount(inputToken, "100"); - - const swap = await sorGetSwapsWithPools( - inputToken, - BEETS, - SwapKind.GivenIn, - inputAmount, - pools, - swapOptions - ); - - if (!swap) throw new Error("Swap is undefined"); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(inputAmount.amount); - expect(swap.outputAmount.amount).toEqual(swap.quote.amount); - }); - - test("Native ETH -> Token givenOut", async () => { - const outputAmount = TokenAmount.fromHumanAmount(BEETS, "100000"); - - const swap = await sorGetSwapsWithPools( - inputToken, - BEETS, - SwapKind.GivenOut, - outputAmount, - pools, - swapOptions - ); - - if (!swap) throw new Error("Swap is undefined"); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(swap.quote.amount); - expect(swap.outputAmount.amount).toEqual(outputAmount.amount); - }); - }); - } -); diff --git a/test/fxPool.integration.test.ts b/test/fxPool.integration.test.ts index 37c01c89..fce87191 100644 --- a/test/fxPool.integration.test.ts +++ b/test/fxPool.integration.test.ts @@ -16,13 +16,13 @@ import { sorGetSwapsWithPools, } from '../src'; import { MockPoolProvider } from './lib/utils/mockPoolProvider'; - import testPools from './lib/testData/testPools/fx_43667355.json'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -describe('fx integration tests', () => { - const chainId = ChainId.POLYGON; - const rpcUrl = 'http://127.0.0.1:8137/'; +const chainId = ChainId.POLYGON; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +describe('fx integration tests', () => { const USDC = new Token( chainId, '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', diff --git a/test/gyro3Pool.integration.test.ts b/test/gyro3Pool.integration.test.ts index 8d276310..6cad933c 100644 --- a/test/gyro3Pool.integration.test.ts +++ b/test/gyro3Pool.integration.test.ts @@ -15,10 +15,12 @@ import { sorGetSwapsWithPools, sorParseRawPools, } from '../src'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; + +const chainId = ChainId.POLYGON; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); describe('gyro3 integration tests', () => { - const chainId = ChainId.POLYGON; - const rpcUrl = 'http://127.0.0.1:8137/'; const rawPool = { ...testPools }.pools[1] as RawGyro3Pool; const USDC = new Token( chainId, diff --git a/test/gyroEV2Pool.integration.test.ts b/test/gyroEV2Pool.integration.test.ts index e94c4fc4..85710e9a 100644 --- a/test/gyroEV2Pool.integration.test.ts +++ b/test/gyroEV2Pool.integration.test.ts @@ -18,10 +18,12 @@ import { import { parseEther } from 'viem'; import { GyroEPool, GyroEPoolToken } from '../src/entities/pools/gyroE'; import { MockPoolProvider } from './lib/utils/mockPoolProvider'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; + +const chainId = ChainId.POLYGON; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); describe('gyroEV2: WMATIC-stMATIC integration tests', () => { - const chainId = ChainId.POLYGON; - const rpcUrl = 'http://127.0.0.1:8137/'; const WMATIC = new Token( chainId, '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270', diff --git a/test/vitest-setup.ts b/test/vitest-setup.ts new file mode 100644 index 00000000..cc17ca61 --- /dev/null +++ b/test/vitest-setup.ts @@ -0,0 +1,6 @@ +import { afterAll } from 'vitest'; +import { stopAnvilForks } from './anvil/anvil-global-setup'; + +afterAll(async () => { + await stopAnvilForks(); +}); diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 44db114c..7a9e597b 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -33,6 +33,7 @@ import { ExitInput, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; type TxInput = { client: Client & PublicActions & TestActions & WalletActions; @@ -45,7 +46,7 @@ type TxInput = { }; const chainId = ChainId.MAINNET; -const rpcUrl = 'http://127.0.0.1:8545/'; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index d4dff628..e466fcf1 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -36,9 +36,10 @@ import { doJoin, } from './lib/utils/joinHelper'; import { JoinTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const chainId = ChainId.MAINNET; -const rpcUrl = 'http://127.0.0.1:8545/'; const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // 80wjAURA-20WETH diff --git a/test/weightedPool.integration.test.ts b/test/weightedPool.integration.test.ts index 8a6b554b..a58e9b54 100644 --- a/test/weightedPool.integration.test.ts +++ b/test/weightedPool.integration.test.ts @@ -1,115 +1,114 @@ // pnpm test -- test/weightedPool.integration.test.ts -import { beforeEach, describe, expect, test } from "vitest"; -import dotenv from "dotenv"; +import { beforeEach, describe, expect, test } from 'vitest'; +import dotenv from 'dotenv'; dotenv.config(); -import { SmartOrderRouter } from "../src/sor"; -import { sorGetSwapsWithPools } from "../src/static"; -import { ChainId, ETH, BATCHSIZE, VAULT } from "../src/utils"; -import { Token, TokenAmount } from "../src/entities"; -import { OnChainPoolDataEnricher } from "../src/data/enrichers/onChainPoolDataEnricher"; -import { SwapKind, SwapOptions } from "../src/types"; -import { BasePool } from "../src/entities/pools"; -import { MockPoolProvider } from "./lib/utils/mockPoolProvider"; - -import testPools from "./lib/testData/testPools/weighted_17473810.json"; -import { RawWeightedPool } from "../src"; - -describe( - "Weighted Swap tests", - () => { - const chainId = ChainId.MAINNET; - const rpcUrl = "http://127.0.0.1:8545"; +import { SmartOrderRouter } from '../src/sor'; +import { sorGetSwapsWithPools } from '../src/static'; +import { ChainId, ETH, BATCHSIZE, VAULT } from '../src/utils'; +import { Token, TokenAmount } from '../src/entities'; +import { OnChainPoolDataEnricher } from '../src/data/enrichers/onChainPoolDataEnricher'; +import { SwapKind, SwapOptions } from '../src/types'; +import { BasePool } from '../src/entities/pools'; +import { MockPoolProvider } from './lib/utils/mockPoolProvider'; + +import testPools from './lib/testData/testPools/weighted_17473810.json'; +import { RawWeightedPool } from '../src'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; + +const chainId = ChainId.MAINNET; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); + +describe('Weighted Swap tests', () => { const mockPoolProvider = new MockPoolProvider( - testPools.pools as RawWeightedPool[] + testPools.pools as RawWeightedPool[], ); const onChainPoolDataEnricher = new OnChainPoolDataEnricher( - chainId, - rpcUrl, - BATCHSIZE[chainId], - VAULT[chainId] + chainId, + rpcUrl, + BATCHSIZE[chainId], + VAULT[chainId], ); const sor = new SmartOrderRouter({ - chainId, - poolDataProviders: mockPoolProvider, - poolDataEnrichers: onChainPoolDataEnricher, - rpcUrl: rpcUrl, + chainId, + poolDataProviders: mockPoolProvider, + poolDataEnrichers: onChainPoolDataEnricher, + rpcUrl: rpcUrl, }); const BAL = new Token( - chainId, - "0xba100000625a3754423978a60c9317c58a424e3D", - 18, - "BAL" + chainId, + '0xba100000625a3754423978a60c9317c58a424e3D', + 18, + 'BAL', ); const swapOptions: SwapOptions = { - block: 17473810n, + block: 17473810n, }; let pools: BasePool[]; // Since constructing a Swap mutates the pool balances, we refetch for each test // May be a better way to deep clone a BasePool[] class instead beforeEach(async () => { - pools = await sor.fetchAndCachePools(swapOptions.block); + pools = await sor.fetchAndCachePools(swapOptions.block); }); // ETH -> BAL swapGivenIn single hop // Weighted pool // 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014 - test("Native ETH -> Token givenIn single hop", async () => { - const inputAmount = TokenAmount.fromHumanAmount(ETH, "1"); - - const swap = await sorGetSwapsWithPools( - ETH, - BAL, - SwapKind.GivenIn, - inputAmount, - pools, - swapOptions - ); - - if (!swap) throw new Error("Swap is undefined"); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(inputAmount.amount); - expect(swap.outputAmount.amount).toEqual(swap.quote.amount); - expect(swap.paths.length).toEqual(1); - expect(swap.paths[0].pools.length).toEqual(1); - expect(swap.paths[0].pools[0].id).toEqual( - "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014" - ); + test('Native ETH -> Token givenIn single hop', async () => { + const inputAmount = TokenAmount.fromHumanAmount(ETH, '1'); + + const swap = await sorGetSwapsWithPools( + ETH, + BAL, + SwapKind.GivenIn, + inputAmount, + pools, + swapOptions, + ); + + if (!swap) throw new Error('Swap is undefined'); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(inputAmount.amount); + expect(swap.outputAmount.amount).toEqual(swap.quote.amount); + expect(swap.paths.length).toEqual(1); + expect(swap.paths[0].pools.length).toEqual(1); + expect(swap.paths[0].pools[0].id).toEqual( + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', + ); }); // ETH -> BAL swapGivenOut single hop // Weighted pool // 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014 - test("Native ETH -> Token givenOut single hop", async () => { - const outputAmount = TokenAmount.fromHumanAmount(BAL, "100"); - - const swap = await sorGetSwapsWithPools( - ETH, - BAL, - SwapKind.GivenOut, - outputAmount, - pools, - swapOptions - ); - - if (!swap) throw new Error("Swap is undefined"); - - const onchain = await swap.query(rpcUrl, swapOptions.block); - - expect(swap.quote.amount).toEqual(onchain.amount); - expect(swap.inputAmount.amount).toEqual(swap.quote.amount); - expect(swap.outputAmount.amount).toEqual(outputAmount.amount); - expect(swap.paths.length).toEqual(1); - expect(swap.paths[0].pools.length).toEqual(1); - expect(swap.paths[0].pools[0].id).toEqual( - "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014" - ); + test('Native ETH -> Token givenOut single hop', async () => { + const outputAmount = TokenAmount.fromHumanAmount(BAL, '100'); + + const swap = await sorGetSwapsWithPools( + ETH, + BAL, + SwapKind.GivenOut, + outputAmount, + pools, + swapOptions, + ); + + if (!swap) throw new Error('Swap is undefined'); + + const onchain = await swap.query(rpcUrl, swapOptions.block); + + expect(swap.quote.amount).toEqual(onchain.amount); + expect(swap.inputAmount.amount).toEqual(swap.quote.amount); + expect(swap.outputAmount.amount).toEqual(outputAmount.amount); + expect(swap.paths.length).toEqual(1); + expect(swap.paths[0].pools.length).toEqual(1); + expect(swap.paths[0].pools[0].id).toEqual( + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', + ); }); - } -); +}); diff --git a/vitest.config.ts b/vitest.config.ts index c321bcc0..d03b2f8b 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -18,11 +18,11 @@ export default defineConfig(({ mode }) => { test: { testTimeout: 20_000, hookTimeout: 30_000, - globalSetup: ['./test/anvil/anvil-global-setup.ts'], + setupFiles: ['/test/vitest-setup.ts'], // Uncomment to debug suite excluding some tests // exclude: ['test/*weighted*.integration.*', 'node_modules', 'dist'], - // Avoid testing threads to avoid problems with shared fork data - threads: false, + // Uncomment to run integration tests sequentially + // threads: false, }, }; }); From a073afc4706385c9d0900efd0a53cdf8387a3fc1 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 3 Nov 2023 16:08:18 +0100 Subject: [PATCH 149/199] Refactor anvil ports to be separated by 100 --- test/anvil/anvil-global-setup.ts | 26 +++++++++++++++++--------- test/subgraph.test.ts | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 7d0b0062..7e42f946 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -2,6 +2,7 @@ import { Anvil, CreateAnvilOptions, createAnvil } from '@viem/anvil'; import { sleep } from '../lib/utils/promises'; import { ChainId } from '../../src/utils/constants'; import os from 'os'; +import { av } from 'vitest/dist/reporters-5f784f42'; type NetworkSetup = { rpcEnv: string; @@ -15,18 +16,25 @@ type NetworksWithFork = Extract< 'MAINNET' | 'POLYGON' | 'FANTOM' >; +const ANVIL_PORTS: Record = { + //Ports separated by 100 to avoid port collision when running tests in parallel + MAINNET: 8545, + POLYGON: 8645, + FANTOM: 8745, +}; + export const ANVIL_NETWORKS: Record = { MAINNET: { rpcEnv: 'ETHEREUM_RPC_URL', fallBackRpc: 'https://cloudflare-eth.com', - port: 8545, + port: ANVIL_PORTS.MAINNET, forkBlockNumber: 18043296n, }, POLYGON: { rpcEnv: 'POLYGON_RPC_URL', // Public Polygon RPCs are usually unreliable fallBackRpc: undefined, - port: 8137, + port: ANVIL_PORTS.POLYGON, // Note - this has to be >= highest blockNo used in tests forkBlockNumber: 44215395n, }, @@ -34,7 +42,7 @@ export const ANVIL_NETWORKS: Record = { rpcEnv: 'FANTOM_RPC_URL', // Public Fantom RPCs are usually unreliable fallBackRpc: undefined, - port: 8138, + port: ANVIL_PORTS.FANTOM, forkBlockNumber: 65313450n, }, }; @@ -73,7 +81,7 @@ function getAnvilOptions(network: NetworkSetup): CreateAnvilOptions { } // Controls the current running forks to avoid starting the same fork twice -let runningForks: Record = {}; +let runningForks: Record = {}; // Make sure that forks are stopped after each test suite export async function stopAnvilForks() { @@ -99,9 +107,7 @@ export async function startFork( const anvilOptions = getAnvilOptions(network); const defaultAnvilPort = 8545; - let port = (anvilOptions.port || defaultAnvilPort) + jobId; - //Skip 8550, 8551 ports because they are already used - if ([8550, 8551].includes(port)) port = 8552; + const port = (anvilOptions.port || defaultAnvilPort) + jobId; if (!anvilOptions.forkUrl) { throw Error( @@ -110,13 +116,15 @@ export async function startFork( } const rpcUrl = `http://127.0.0.1:${port}`; + console.log('checking rpcUrl', port, runningForks); + // Avoid starting fork if it was running already - if (runningForks[rpcUrl]) return { rpcUrl }; + if (runningForks[port]) return { rpcUrl }; // https://www.npmjs.com/package/@viem/anvil const anvil = createAnvil({ ...anvilOptions, port }); // Save reference to running fork - runningForks[rpcUrl] = anvil; + runningForks[port] = anvil; if (process.env.SKIP_GLOBAL_SETUP === 'true') { console.warn(`🛠️ Skipping global anvil setup. You must run the anvil fork manually. Example: diff --git a/test/subgraph.test.ts b/test/subgraph.test.ts index 470c4c8e..6cfaa61d 100644 --- a/test/subgraph.test.ts +++ b/test/subgraph.test.ts @@ -3,7 +3,7 @@ import { SubgraphPoolProvider } from '../src/data/providers/subgraphPoolProvider import { ChainId } from '../src/utils'; import { ProviderSwapOptions } from '../src/data/types'; -describe( +describe.skip( 'SubgraphPoolProvider', () => { test('getPools mainnet', async () => { From 2612b131cacdc36a2ad17cfb5907ecc452fc2c1a Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 3 Nov 2023 16:11:53 +0100 Subject: [PATCH 150/199] Remove old code --- test/anvil/anvil-global-setup.ts | 16 +++------------- test/subgraph.test.ts | 2 +- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 7e42f946..5d226ee9 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -18,9 +18,9 @@ type NetworksWithFork = Extract< const ANVIL_PORTS: Record = { //Ports separated by 100 to avoid port collision when running tests in parallel - MAINNET: 8545, - POLYGON: 8645, - FANTOM: 8745, + MAINNET: 8645, + POLYGON: 8745, + FANTOM: 8845, }; export const ANVIL_NETWORKS: Record = { @@ -47,16 +47,6 @@ export const ANVIL_NETWORKS: Record = { }, }; -export default async function () { - const numberOfCpus = os.cpus().length; - for (const network of Object.values(ANVIL_NETWORKS)) { - const config = getAnvilOptions(network); - for (let jobId = 0; jobId < numberOfCpus; jobId++) { - await startFork(config, jobId); - } - } -} - function getAnvilOptions(network: NetworkSetup): CreateAnvilOptions { let forkUrl: string; if (process.env[network.rpcEnv]) { diff --git a/test/subgraph.test.ts b/test/subgraph.test.ts index 6cfaa61d..470c4c8e 100644 --- a/test/subgraph.test.ts +++ b/test/subgraph.test.ts @@ -3,7 +3,7 @@ import { SubgraphPoolProvider } from '../src/data/providers/subgraphPoolProvider import { ChainId } from '../src/utils'; import { ProviderSwapOptions } from '../src/data/types'; -describe.skip( +describe( 'SubgraphPoolProvider', () => { test('getPools mainnet', async () => { From b1a0f7d226a77c2431fc391526df00e8130ca0c4 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 3 Nov 2023 16:16:04 +0100 Subject: [PATCH 151/199] Fix lint --- test/anvil/anvil-global-setup.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index 5d226ee9..e90602e7 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -1,8 +1,6 @@ import { Anvil, CreateAnvilOptions, createAnvil } from '@viem/anvil'; import { sleep } from '../lib/utils/promises'; import { ChainId } from '../../src/utils/constants'; -import os from 'os'; -import { av } from 'vitest/dist/reporters-5f784f42'; type NetworkSetup = { rpcEnv: string; From 498a3d05bc017cae0058df92e2e8102f4a1be950 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Sun, 5 Nov 2023 20:18:10 -0300 Subject: [PATCH 152/199] Refactoring all exit tests, exit enums and parameters names; --- README.md | 2 +- examples/exit/weighted.ts | 2 +- .../composable-stable/composableStableExit.ts | 25 +- src/entities/exit/types.ts | 18 +- src/entities/exit/utils/validateInputs.ts | 6 +- src/entities/exit/weighted/weightedExit.ts | 25 +- test/composableStableExit.integration.test.ts | 293 ++++++------ test/composableStableJoin.integration.test.ts | 12 +- test/lib/utils/exitHelper.ts | 440 ++++++++++++++---- test/lib/utils/joinHelper.ts | 4 +- test/lib/utils/types.ts | 11 + test/weightedExit.integration.test.ts | 268 ++++++----- 12 files changed, 691 insertions(+), 415 deletions(-) diff --git a/README.md b/README.md index a3318993..d3f781dd 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ const exitInput: SingleAssetExitInput = { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SINGLE_ASSET, + kind: ExitKind.SingleAsset, }; const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index e7e4650e..8dc79fb8 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -72,7 +72,7 @@ const exit = async () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SINGLE_ASSET, + kind: ExitKind.SingleAsset, }; const queryResult = await poolExit.query(exitInput, poolState); diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index 457cf98b..fb39c01c 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -66,10 +66,11 @@ export class ComposableStableExit implements BaseExit { return { poolType: poolState.type, exitKind: input.kind, - id: poolState.id, + poolId: poolState.id, bptIn, amountsOut, tokenOutIndex: amounts.tokenOutIndex, + toInternalBalance: !!input.toInternalBalance, bptIndex, }; } @@ -80,7 +81,7 @@ export class ComposableStableExit implements BaseExit { bptIndex?: number, ): AmountsExit { switch (input.kind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: return { minAmountsOut: tokens.map( (t) => @@ -90,7 +91,7 @@ export class ComposableStableExit implements BaseExit { tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: tokens @@ -98,7 +99,7 @@ export class ComposableStableExit implements BaseExit { .findIndex((t) => t.isSameAddress(input.tokenOut)), maxBptAmountIn: input.bptIn.amount, }; - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: undefined, @@ -119,7 +120,7 @@ export class ComposableStableExit implements BaseExit { const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); const { args } = parseExitArgs({ - poolId: input.id, + poolId: input.poolId, sortedTokens: input.amountsOut.map((a) => a.token), sender: input.sender, recipient: input.recipient, @@ -144,16 +145,16 @@ export class ComposableStableExit implements BaseExit { private getAmountsCall(input: ComposableStableExitCall): AmountsExit { switch (input.exitKind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: return { minAmountsOut: input.amountsOut.map((a) => a.amount), tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: if (input.tokenOutIndex === undefined) { throw new Error( - 'tokenOutIndex must be defined for SINGLE_ASSET exit', + 'tokenOutIndex must be defined for SingleAsset exit', ); } return { @@ -163,7 +164,7 @@ export class ComposableStableExit implements BaseExit { tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.bptIn.amount, }; - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: return { minAmountsOut: input.amountsOut.map((a) => input.slippage.removeFrom(a.amount), @@ -178,12 +179,12 @@ export class ComposableStableExit implements BaseExit { private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { switch (kind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: return ComposableStableEncoder.exitUnbalanced( amounts.minAmountsOut, amounts.maxBptAmountIn, ); - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: if (amounts.tokenOutIndex === undefined) throw Error('No Index'); @@ -191,7 +192,7 @@ export class ComposableStableExit implements BaseExit { amounts.maxBptAmountIn, amounts.tokenOutIndex, ); - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: return ComposableStableEncoder.exitProportional( amounts.maxBptAmountIn, ); diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 493e11e5..08c7bbbd 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -4,9 +4,9 @@ import { Address } from '../../types'; import { PoolState } from '../types'; export enum ExitKind { - UNBALANCED = 'UNBALANCED', // exitExactOut - SINGLE_ASSET = 'SINGLE_ASSET', // exitExactInSingleAsset - PROPORTIONAL = 'PROPORTIONAL', // exitExactInProportional + Unbalanced = 'Unbalanced', // exitExactOut + SingleAsset = 'SingleAsset', // exitExactInSingleAsset + Proportional = 'Proportional', // exitExactInProportional } // This will be extended for each pools specific output requirements @@ -19,18 +19,18 @@ export type BaseExitInput = { export type UnbalancedExitInput = BaseExitInput & { amountsOut: TokenAmount[]; - kind: ExitKind.UNBALANCED; + kind: ExitKind.Unbalanced; }; export type SingleAssetExitInput = BaseExitInput & { bptIn: TokenAmount; tokenOut: Address; - kind: ExitKind.SINGLE_ASSET; + kind: ExitKind.SingleAsset; }; export type ProportionalExitInput = BaseExitInput & { bptIn: TokenAmount; - kind: ExitKind.PROPORTIONAL; + kind: ExitKind.Proportional; }; export type ExitInput = @@ -45,12 +45,12 @@ export type ExitQueryResult = // Returned from a exit query export type BaseExitQueryResult = { poolType: string; - id: Address; + poolId: Address; exitKind: ExitKind; bptIn: TokenAmount; amountsOut: TokenAmount[]; tokenOutIndex?: number; - toInternalBalance?: boolean; + toInternalBalance: boolean; }; export type ComposableStableExitQueryResult = BaseExitQueryResult & { @@ -71,7 +71,7 @@ export type ExitCall = ComposableStableExitCall | WeightedExitCall; export type ExitBuildOutput = { call: Address; to: Address; - value: bigint | undefined; + value: bigint; maxBptIn: bigint; minAmountsOut: bigint[]; }; diff --git a/src/entities/exit/utils/validateInputs.ts b/src/entities/exit/utils/validateInputs.ts index 93e2b179..1cfe8c00 100644 --- a/src/entities/exit/utils/validateInputs.ts +++ b/src/entities/exit/utils/validateInputs.ts @@ -10,18 +10,18 @@ export function validateInputs(input: ExitInput, poolState: PoolStateInput) { throw new Error('Pool Tokens does not contain BPT'); } switch (input.kind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: areTokensInArray( input.amountsOut.map((a) => a.token.address), poolState.tokens.map((t) => t.address), ); break; - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: areTokensInArray( [input.tokenOut], poolState.tokens.map((t) => t.address), ); - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: areTokensInArray([input.bptIn.token.address], [poolState.address]); default: break; diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 63c1c617..bf04ff23 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -60,16 +60,17 @@ export class WeightedExit implements BaseExit { return { poolType: poolState.type, exitKind: input.kind, - id: poolState.id, + poolId: poolState.id, bptIn, amountsOut, tokenOutIndex: amounts.tokenOutIndex, + toInternalBalance: !!input.toInternalBalance, }; } private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { switch (input.kind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: return { minAmountsOut: tokens.map( (t) => @@ -79,7 +80,7 @@ export class WeightedExit implements BaseExit { tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: tokens.findIndex((t) => @@ -87,7 +88,7 @@ export class WeightedExit implements BaseExit { ), maxBptAmountIn: input.bptIn.amount, }; - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: undefined, @@ -102,7 +103,7 @@ export class WeightedExit implements BaseExit { const userData = this.encodeUserData(input.exitKind, amounts); const { args } = parseExitArgs({ - poolId: input.id, + poolId: input.poolId, sortedTokens: input.amountsOut.map((a) => a.token), sender: input.sender, recipient: input.recipient, @@ -128,16 +129,16 @@ export class WeightedExit implements BaseExit { private getAmountsCall(input: ExitCall): AmountsExit { switch (input.exitKind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: return { minAmountsOut: input.amountsOut.map((a) => a.amount), tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: if (input.tokenOutIndex === undefined) { throw new Error( - 'tokenOutIndex must be defined for SINGLE_ASSET exit', + 'tokenOutIndex must be defined for SingleAsset exit', ); } return { @@ -147,7 +148,7 @@ export class WeightedExit implements BaseExit { tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.bptIn.amount, }; - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: return { minAmountsOut: input.amountsOut.map((a) => input.slippage.removeFrom(a.amount), @@ -162,12 +163,12 @@ export class WeightedExit implements BaseExit { private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { switch (kind) { - case ExitKind.UNBALANCED: + case ExitKind.Unbalanced: return WeightedEncoder.exitUnbalanced( amounts.minAmountsOut, amounts.maxBptAmountIn, ); - case ExitKind.SINGLE_ASSET: + case ExitKind.SingleAsset: if (amounts.tokenOutIndex === undefined) throw Error('No Index'); @@ -175,7 +176,7 @@ export class WeightedExit implements BaseExit { amounts.maxBptAmountIn, amounts.tokenOutIndex, ); - case ExitKind.PROPORTIONAL: + case ExitKind.Proportional: return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); default: throw Error('Unsupported Exit Type'); diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index f4968b9e..a889fa9a 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -1,5 +1,5 @@ // pnpm test -- weightedExit.integration.test.ts -import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import { describe, test, beforeAll, beforeEach } from 'vitest'; import { config } from 'dotenv'; config(); @@ -29,7 +29,12 @@ import { } from '../src'; import { forkSetup } from './lib/utils/helper'; import { ExitTxInput } from './lib/utils/types'; -import { doExit } from './lib/utils/exitHelper'; +import { + assertProportionalExit, + assertSingleTokenExit, + assertUnbalancedExit, + doExit, +} from './lib/utils/exitHelper'; const chainId = ChainId.MAINNET; const rpcUrl = 'http://127.0.0.1:8545/'; @@ -58,11 +63,9 @@ describe('composable stable exit test', () => { client, poolExit: new PoolExit(), slippage: Slippage.fromPercentage('1'), // 1% - poolInput, + poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig exitInput: {} as ExitInput, - checkNativeBalance: false, - chainId, }; bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); @@ -71,176 +74,156 @@ describe('composable stable exit test', () => { await forkSetup( txInput.client, txInput.testAddress, - [txInput.poolInput.address], + [txInput.poolStateInput.address], undefined, // TODO: hardcode these values to improve test performance - [parseUnits('1000', 18)] + [parseUnits('1000', 18)], ); }); - test('single asset exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenOut = '0xf4edfad26ee0d23b69ca93112ecce52704e0006f'; // baoETH - - const exitInput: SingleAssetExitInput = { - chainId, - rpcUrl, - bptIn, - tokenOut, - kind: ExitKind.SINGLE_ASSET, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, + describe('unbalanced exit', async () => { + let input: Omit; + let amountsOut: TokenAmount[]; + beforeAll(() => { + const bptIndex = txInput.poolStateInput.tokens.findIndex( + (t) => t.address === txInput.poolStateInput.address, + ); + const poolTokensWithoutBpt = txInput.poolStateInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + .filter((_, index) => index !== bptIndex); + + amountsOut = poolTokensWithoutBpt.map((t) => + TokenAmount.fromHumanAmount(t, '20'), + ); + input = { + chainId, + rpcUrl, + kind: ExitKind.Unbalanced, + }; }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We only expect single asset to have a value for exit - expect(queryResult.tokenOutIndex).to.not.be.undefined; - queryResult.amountsOut - .filter((_, index) => index !== bptIndex) - .forEach((a, i) => { - if (i === queryResult.tokenOutIndex) - expect(a.amount > BigInt(0)).to.be.true; - else expect(a.amount === BigInt(0)).to.be.true; + test('exiting with wrapped', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + }; + const exitResult = await doExit({ ...txInput, exitInput }); + assertUnbalancedExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); + }); + test('exiting with native', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + exitWithNativeAsset: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, }); - - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); + assertUnbalancedExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); + }); }); - test('proportional exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - - const exitInput: ProportionalExitInput = { - chainId, - rpcUrl, - bptIn, - kind: ExitKind.PROPORTIONAL, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, + describe('single asset exit', () => { + let input: SingleAssetExitInput; + beforeAll(() => { + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenOut = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH + input = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We expect all assets to have a value for exit - expect(queryResult.tokenOutIndex).to.be.undefined; - queryResult.amountsOut - .filter((_, index) => index !== bptIndex) - .forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + test('exiting with wrapped', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, }); - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); - }); - - test('unbalanced exit', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - const { poolInput, slippage } = txInput; - - const poolTokens = poolInput.tokens - .filter((_, index) => index !== bptIndex) - .map((t) => new Token(chainId, t.address, t.decimals)); - const amountsOut = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '80'), - ); - - const exitInput: UnbalancedExitInput = { - chainId, - rpcUrl, - amountsOut, - kind: ExitKind.UNBALANCED, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, + assertSingleTokenExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); }); - // We expect a BPT input amount > 0 - expect(queryResult.bptIn.amount > BigInt(0)).to.be.true; - - // We expect assets to have same amount out as user defined - expect(queryResult.tokenOutIndex).to.be.undefined; - queryResult.amountsOut - .filter((_, index) => index !== bptIndex) - .forEach((a, i) => { - expect(a.amount).to.eq(amountsOut[i].amount); + test('exiting with native', async () => { + const exitInput = { + ...input, + exitWithNativeAsset: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, }); - // Confirm slippage - only to bpt in, not amounts out - const expectedMinAmountsOut = amountsOut.map((a) => a.amount); - expect(expectedMinAmountsOut).to.deep.eq( - minAmountsOut.filter((_, index) => index !== bptIndex), - ); - const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); - expect(expectedMaxBptIn).to.deep.eq(maxBptIn); + assertSingleTokenExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); + }); }); - test('exit with native asset', async () => { - const bptIndex = txInput.poolInput.tokens.findIndex( - (t) => t.address === txInput.poolInput.address, - ); - - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - - const exitInput: ProportionalExitInput = { - chainId, - rpcUrl, - bptIn, - kind: ExitKind.PROPORTIONAL, - exitWithNativeAsset: true, - }; - - // Note - checking native balance - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, - checkNativeBalance: true, + describe('proportional exit', () => { + let input: ProportionalExitInput; + beforeAll(() => { + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + input = { + bptIn, + chainId, + rpcUrl, + kind: ExitKind.Proportional, + }; }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We expect all assets to have a value for exit - expect(queryResult.tokenOutIndex).to.be.undefined; - queryResult.amountsOut - .filter((_, index) => index !== bptIndex) - .forEach((a) => { - expect(a.amount > BigInt(0)).to.be.true; + test('with tokens', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, }); - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut - .filter((_, index) => index !== bptIndex) - .map((a) => slippage.removeFrom(a.amount)); - expect(expectedMinAmountsOut).to.deep.eq( - minAmountsOut.filter((_, index) => index !== bptIndex), - ); - expect(maxBptIn).to.eq(bptIn.amount); + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); + }); + test('with native', async () => { + const exitInput = { + ...input, + useNativeAssetAsWrappedAmountIn: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, + }); + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); + }); }); }); diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 4a2583b4..a2450880 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -33,8 +33,8 @@ import { JoinTxInput } from './lib/utils/types'; import { doJoin, assertUnbalancedJoin, - assertSingleToken, - assertProportional, + assertSingleTokenJoin, + assertProportionalJoin, } from './lib/utils/joinHelper'; const chainId = ChainId.MAINNET; @@ -165,7 +165,7 @@ describe('composable stable join test', () => { joinInput: input, }); - assertSingleToken( + assertSingleTokenJoin( txInput.client.chain?.id as number, txInput.poolStateInput, input, @@ -184,7 +184,7 @@ describe('composable stable join test', () => { joinInput, }); - assertSingleToken( + assertSingleTokenJoin( txInput.client.chain?.id as number, txInput.poolStateInput, joinInput, @@ -211,7 +211,7 @@ describe('composable stable join test', () => { joinInput: input, }); - assertProportional( + assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, input, @@ -228,7 +228,7 @@ describe('composable stable join test', () => { ...txInput, joinInput, }); - assertProportional( + assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, joinInput, diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 7601944a..3948723c 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -1,106 +1,388 @@ import { ExitTxInput } from './types'; -import { replaceWrapped, Token } from '../../../src'; -import { assertTransaction, sendTransactionGetBalances } from './helper'; +import { + Address, + ChainId, + ComposableStableExitQueryResult, + ExitBuildOutput, + ExitQueryResult, + NATIVE_ASSETS, + PoolStateInput, + TokenAmount, + Slippage, + Token, + UnbalancedExitInput, + ZERO_ADDRESS, + SingleAssetExitInput, + BALANCER_VAULT, + ExitInput, + ProportionalExitInput, +} from '../../../src'; +import { sendTransactionGetBalances, TxResult } from './helper'; import { expect } from 'vitest'; +import { zeroAddress } from 'viem'; + +type ExitResult = { + exitQueryResult: ExitQueryResult; + exitBuildOutput: ExitBuildOutput; + txOutput: TxResult; +}; + +export const sdkExit = async ({ + poolExit, + exitInput, + poolStateInput, + slippage, + testAddress, +}: Omit): Promise<{ + exitBuildOutput: ExitBuildOutput; + exitQueryResult: ExitQueryResult; +}> => { + const exitQueryResult = await poolExit.query(exitInput, poolStateInput); + const exitBuildOutput = poolExit.buildCall({ + ...exitQueryResult, + slippage, + sender: testAddress, + recipient: testAddress, + }); + + return { + exitBuildOutput, + exitQueryResult, + }; +}; + +function getTokens(poolStateInput: PoolStateInput): Address[] { + // pool tokens, bpt, eth + const tokens = poolStateInput.tokens + .filter((t) => t.address !== poolStateInput.address) + .map((t) => t.address); + tokens.push(poolStateInput.address); + tokens.push(ZERO_ADDRESS); + return tokens; +} + +function isComposableStableExitQueryResult(result: ExitQueryResult): boolean { + return (result as ComposableStableExitQueryResult).bptIndex !== undefined; +} + +function getCheck(result: ExitQueryResult, isExactIn: boolean) { + if (isComposableStableExitQueryResult(result)) { + if (isExactIn) { + // Using this destructuring to return only the fields of interest + // rome-ignore lint/correctness/noUnusedVariables: + const { amountsOut, bptIndex, ...check } = + result as ComposableStableExitQueryResult; + return check; + } else { + // rome-ignore lint/correctness/noUnusedVariables: + const { bptIn, bptIndex, ...check } = + result as ComposableStableExitQueryResult; + return check; + } + } else { + if (isExactIn) { + // rome-ignore lint/correctness/noUnusedVariables: + const { amountsOut, ...check } = result; + return check; + } else { + // rome-ignore lint/correctness/noUnusedVariables: + const { bptIn, ...check } = result; + return check; + } + } +} /** - * Helper function that sends a exit transaction and check for balance deltas + * Create and submit exit transaction. * @param txInput + * @param client: Client & PublicActions & WalletActions - The RPC client * @param poolExit: PoolExit - The pool exit class, used to query the exit and build the exit call - * @param poolInput: PoolStateInput - The state of the pool being exited * @param exitInput: ExitInput - The parameters of the exit transaction, example: bptIn, amountsOut, etc. - * @param testAddress: Address - The address to send the transaction from - * @param client: Client & PublicActions & WalletActions - The RPC client * @param slippage: Slippage - The slippage tolerance for the exit transaction - * @param checkNativeBalance: boolean - Whether to check the native asset balance or not - * @param chainId: ChainId - The Chain Id of the pool being exited, for example: 1 for Mainnet, 137 for Polygon, etc. - */ -export const doExit = async (txInput: ExitTxInput) => { + * @param poolStateInput: PoolStateInput - The state of the pool being exited + * @param testAddress: Address - The address to send the transaction from + * */ +export async function doExit(txInput: ExitTxInput) { const { poolExit, - poolInput, + poolStateInput, exitInput, testAddress, client, slippage, - checkNativeBalance, - chainId, } = txInput; - const queryResult = await poolExit.query(exitInput, poolInput); - const { call, to, value, maxBptIn, minAmountsOut } = poolExit.buildCall({ - ...queryResult, + + const { exitQueryResult, exitBuildOutput } = await sdkExit({ + poolExit, + exitInput, + poolStateInput, slippage, - sender: testAddress, - recipient: testAddress, + testAddress, }); - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), + // get tokens for balance change - pool tokens, BPT, native + const tokens = getTokens(poolStateInput); + + // send transaction and calculate balance changes + const txOutput = await sendTransactionGetBalances( + tokens, + client, + testAddress, + exitBuildOutput.to, + exitBuildOutput.call, + exitBuildOutput.value, ); - const bptIndex = poolInput.tokens.findIndex( - (t) => t.address === poolInput.address, + return { + exitQueryResult, + exitBuildOutput, + txOutput, + }; +} + +export function assertUnbalancedExit( + chainId: ChainId, + poolStateInput: PoolStateInput, + exitInput: UnbalancedExitInput, + exitResult: ExitResult, + slippage: Slippage, +) { + const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + + // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) + const expectedAmountsOut = poolStateInput.tokens.map((t) => { + let token; + if ( + exitInput.exitWithNativeAsset && + t.address === NATIVE_ASSETS[chainId].wrapped + ) + token = new Token(chainId, zeroAddress, t.decimals); + else token = new Token(chainId, t.address, t.decimals); + const input = exitInput.amountsOut.find( + (a) => a.token.address === t.address, + ); + if (input === undefined) return TokenAmount.fromRawAmount(token, 0n); + else return TokenAmount.fromRawAmount(token, input.amount); + }); + + const expectedQueryResult: Omit = { + // Query should use same amountsOut as input + amountsOut: expectedAmountsOut, + tokenOutIndex: undefined, + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + toInternalBalance: !!exitInput.toInternalBalance, + exitKind: exitInput.kind, + }; + + const queryCheck = getCheck(exitQueryResult, false); + + expect(queryCheck).to.deep.eq(expectedQueryResult); + + // Expect some bpt amount + expect(exitQueryResult.bptIn.amount > 0n).to.be.true; + + assertExitBuildOutput( + exitInput, + exitQueryResult, + exitBuildOutput, + false, + slippage, ); - // Replace with native asset if required - const poolTokensAddr = checkNativeBalance - ? replaceWrapped(poolTokens, chainId).map((t) => t.address) - : poolTokens.map((t) => t.address); - - let tokensForBalanceCheck; - if (bptIndex < 0) { - /** - * If the pool type tokens list does not contains BPT (for example: Weighted), the poolTokensAddr - * will not have the bpt address, so we need to add it, so the balance delta of the bpt will be - * checked in the tests. - */ - tokensForBalanceCheck = [...poolTokensAddr, poolInput.address]; - } else { - /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the poolTokensAddr - * will already have the bpt address, so we don't need to add it again. - */ - tokensForBalanceCheck = [...poolTokensAddr]; - } - // send transaction and check balance changes - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - tokensForBalanceCheck, - client, - testAddress, - to, - call, - value, + assertTokenDeltas( + poolStateInput, + exitInput, + exitQueryResult, + exitBuildOutput, + txOutput, + ); +} + +export function assertSingleTokenExit( + chainId: ChainId, + poolStateInput: PoolStateInput, + exitInput: SingleAssetExitInput, + exitResult: ExitResult, + slippage: Slippage, +) { + const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + + if (exitQueryResult.tokenOutIndex === undefined) throw Error('No index'); + + const bptToken = new Token(chainId, poolStateInput.address, 18); + + const tokensWithoutBpt = poolStateInput.tokens.filter( + (t) => t.address !== poolStateInput.address, + ); + + const expectedQueryResult: Omit< + ExitQueryResult, + 'amountsOut' | 'bptIndex' + > = { + // Query should use same bpt out as user sets + bptIn: TokenAmount.fromRawAmount(bptToken, exitInput.bptIn.amount), + tokenOutIndex: tokensWithoutBpt.findIndex( + (t) => t.address === exitInput.tokenOut, + ), + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + toInternalBalance: !!exitInput.toInternalBalance, + exitKind: exitInput.kind, + }; + + const queryCheck = getCheck(exitQueryResult, true); + + expect(queryCheck).to.deep.eq(expectedQueryResult); + + // Expect only tokenOut to have amount > 0 + // (Note exitQueryResult also has value for bpt if pre-minted) + exitQueryResult.amountsOut.forEach((a) => { + if ( + !exitInput.exitWithNativeAsset && + a.token.address === exitInput.tokenOut + ) + expect(a.amount > 0n).to.be.true; + else if ( + exitInput.exitWithNativeAsset && + a.token.address === zeroAddress + ) + expect(a.amount > 0n).to.be.true; + else expect(a.amount).toEqual(0n); + }); + + assertExitBuildOutput( + exitInput, + exitQueryResult, + exitBuildOutput, + true, + slippage, + ); + + assertTokenDeltas( + poolStateInput, + exitInput, + exitQueryResult, + exitBuildOutput, + txOutput, + ); +} + +export function assertProportionalExit( + chainId: ChainId, + poolStateInput: PoolStateInput, + exitInput: ProportionalExitInput, + exitResult: ExitResult, + slippage: Slippage, +) { + const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + + const bptToken = new Token(chainId, poolStateInput.address, 18); + + const expectedQueryResult: Omit< + ExitQueryResult, + 'amountsOut' | 'bptIndex' + > = { + // Query should use same bpt out as user sets + bptIn: TokenAmount.fromRawAmount(bptToken, exitInput.bptIn.amount), + // Only expect tokenInIndex for SingleAssetJoin + tokenOutIndex: undefined, + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + toInternalBalance: !!exitInput.toInternalBalance, + exitKind: exitInput.kind, + }; + + const queryCheck = getCheck(exitQueryResult, true); + + expect(queryCheck).to.deep.eq(expectedQueryResult); + + // Expect all assets in to have an amount > 0 apart from BPT if it exists + exitQueryResult.amountsOut.forEach((a) => { + if (a.token.address === poolStateInput.address) + expect(a.amount).toEqual(0n); + else expect(a.amount > 0n).to.be.true; + }); + + assertExitBuildOutput( + exitInput, + exitQueryResult, + exitBuildOutput, + true, + slippage, + ); + + assertTokenDeltas( + poolStateInput, + exitInput, + exitQueryResult, + exitBuildOutput, + txOutput, + ); +} + +function assertTokenDeltas( + poolStateInput: PoolStateInput, + exitInput: ExitInput, + exitQueryResult: ExitQueryResult, + exitBuildOutput: ExitBuildOutput, + txOutput: TxResult, +) { + expect(txOutput.transactionReceipt.status).to.eq('success'); + + // exitQueryResult amountsOut will have a value for the BPT token if it is a pre-minted pool + const amountsWithoutBpt = [...exitQueryResult.amountsOut].filter( + (t) => t.token.address !== poolStateInput.address, + ); + + // Matching order of getTokens helper: [poolTokens, BPT, native] + const expectedDeltas = [ + ...amountsWithoutBpt.map((a) => a.amount), + exitQueryResult.bptIn.amount, + 0n, + ]; + + // If input is exit with native we must replace it with 0 and update native value instead + if (exitInput.exitWithNativeAsset) { + const index = amountsWithoutBpt.findIndex( + (a) => a.token.address === zeroAddress, ); - let expectedDeltas; - - if (bptIndex < 0) { - /* - * If the pool type tokens list does not contains BPT (for example: Weighted), the queryResult.amountsOut - * does not include any amount out of bpt, so we just need to add the bptIn.amount to the expectedDeltas. - */ - expectedDeltas = [ - ...queryResult.amountsOut.map((a) => a.amount), - queryResult.bptIn.amount, - ]; - } else { - /* - * If the pool type tokens list contains BPT (for example: Composable Stable), the queryResult.amountsOut - * includes the amount out of the bpt, which is always 0, so we need to remove it from the - * expectedDeltas and put the bptIn.amount in its place. - */ - expectedDeltas = [ - ...queryResult.amountsOut.slice(0, bptIndex).map((a) => a.amount), - queryResult.bptIn.amount, - ...queryResult.amountsOut.slice(bptIndex + 1).map((a) => a.amount), - ]; + expectedDeltas[expectedDeltas.length - 1] = expectedDeltas[index]; + expectedDeltas[index] = 0n; } - // Confirm final balance changes match query result - assertTransaction(expectedDeltas, balanceDeltas, transactionReceipt.status); - return { - queryResult, - maxBptIn, + expect(txOutput.balanceDeltas).to.deep.eq(expectedDeltas); +} + +function assertExitBuildOutput( + exitInput: ExitInput, + exitQueryResult: ExitQueryResult, + exitBuildOutput: ExitBuildOutput, + isExactIn: boolean, + slippage: Slippage, +) { + // if exactIn minAmountsOut should use amountsOut with slippage applied, else should use same amountsOut as input + const minAmountsOut = isExactIn + ? exitQueryResult.amountsOut.map((a) => slippage.removeFrom(a.amount)) + : exitQueryResult.amountsOut.map((a) => a.amount); + + // if exactIn slippage cannot be applied to bptIn, else should use bptIn with slippage applied + const maxBptIn = isExactIn + ? exitQueryResult.bptIn.amount + : slippage.applyTo(exitQueryResult.bptIn.amount); + + const expectedBuildOutput: Omit = { minAmountsOut, + maxBptIn, + to: BALANCER_VAULT, + // Value should equal value of any wrapped asset if using native + value: 0n, }; -}; + + // rome-ignore lint/correctness/noUnusedVariables: + const { call, ...buildCheck } = exitBuildOutput; + expect(buildCheck).to.deep.eq(expectedBuildOutput); +} diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index feaac91f..15e183f1 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -207,7 +207,7 @@ export function assertUnbalancedJoin( ); } -export function assertSingleToken( +export function assertSingleTokenJoin( chainId: ChainId, poolStateInput: PoolStateInput, joinInput: SingleAssetJoinInput, @@ -278,7 +278,7 @@ export function assertSingleToken( ); } -export function assertProportional( +export function assertProportionalJoin( chainId: ChainId, poolStateInput: PoolStateInput, joinInput: ProportionalJoinInput, diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index 787cf2d9..e9c9803b 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -2,6 +2,8 @@ import { Client, PublicActions, TestActions, WalletActions } from 'viem'; import { Address, JoinInput, + ExitInput, + PoolExit, PoolJoin, PoolStateInput, Slippage, @@ -15,3 +17,12 @@ export type JoinTxInput = { poolStateInput: PoolStateInput; testAddress: Address; }; + +export type ExitTxInput = { + client: Client & PublicActions & TestActions & WalletActions; + poolExit: PoolExit; + exitInput: ExitInput; + slippage: Slippage; + poolStateInput: PoolStateInput; + testAddress: Address; +}; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 6ef06c29..be869c6d 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -28,7 +28,7 @@ import { ExitInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { doExit } from './lib/utils/exitHelper'; +import { assertProportionalExit, assertSingleTokenExit, assertUnbalancedExit, doExit } from './lib/utils/exitHelper'; import { ExitTxInput } from './lib/utils/types'; const chainId = ChainId.MAINNET; @@ -58,11 +58,9 @@ describe('weighted exit test', () => { client, poolExit: new PoolExit(), slippage: Slippage.fromPercentage('1'), // 1% - poolInput, - testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig + poolStateInput: poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig exitInput: {} as ExitInput, - checkNativeBalance: false, - chainId, }; bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); @@ -71,152 +69,152 @@ describe('weighted exit test', () => { await forkSetup( txInput.client, txInput.testAddress, - [txInput.poolInput.address], + [txInput.poolStateInput.address], undefined, // TODO: hardcode these values to improve test performance - [parseUnits('1', 18)], + [parseUnits('1000', 18)], ); }); - test('single asset exit', async () => { - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL - - const exitInput: SingleAssetExitInput = { - chainId, - rpcUrl, - bptIn, - tokenOut, - kind: ExitKind.SINGLE_ASSET, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, - }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - // We only expect single asset to have a value for exit - expect(queryResult.tokenOutIndex !== undefined).toEqual(true); - queryResult.amountsOut.forEach((a, i) => { - if (i === queryResult.tokenOutIndex) - expect(a.amount > 0n).to.be.true; - else expect(a.amount === 0n).to.be.true; + describe('unbalanced exit', async () => { + let input: Omit; + let amountsOut: TokenAmount[]; + beforeAll(() => { + const poolTokens = txInput.poolStateInput.tokens + .map((t) => new Token(chainId, t.address, t.decimals)) + + amountsOut = poolTokens.map((t) => + TokenAmount.fromHumanAmount(t, '1'), + ); + input = { + chainId, + rpcUrl, + kind: ExitKind.Unbalanced, + }; }); - - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); - }); - - test('proportional exit', async () => { - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - - const exitInput: ProportionalExitInput = { - chainId, - rpcUrl, - bptIn, - kind: ExitKind.PROPORTIONAL, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, + test('exiting with wrapped', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + }; + const exitResult = await doExit({ ...txInput, exitInput }); + assertUnbalancedExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - expect(queryResult.tokenOutIndex === undefined).toEqual(true); - // We expect all assets to have a value for exit - queryResult.amountsOut.forEach((a) => { - expect(a.amount > 0n).to.be.true; + test('exiting with native', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + exitWithNativeAsset: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, + }); + assertUnbalancedExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); }); - - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); }); - test('unbalanced exit', async () => { - const { poolInput, slippage } = txInput; - - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsOut = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '0.001'), - ); - - const exitInput: UnbalancedExitInput = { - chainId, - rpcUrl, - amountsOut, - kind: ExitKind.UNBALANCED, - }; - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, + describe('single asset exit', () => { + let input: SingleAssetExitInput; + beforeAll(() => { + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const tokenOut = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH + input = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; }); - - // We expect a BPT input amount > 0 - expect(queryResult.bptIn.amount > 0n).to.be.true; - - expect(queryResult.tokenOutIndex === undefined).toEqual(true); - // We expect assets to have same amount out as user defined - queryResult.amountsOut.forEach((a, i) => { - expect(a.amount).to.eq(amountsOut[i].amount); + test('exiting with wrapped', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, + }); + + assertSingleTokenExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); }); - // Confirm slippage - only to bpt in, not amounts out - const expectedMinAmountsOut = amountsOut.map((a) => a.amount); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); - expect(expectedMaxBptIn).to.deep.eq(maxBptIn); + test('exiting with native', async () => { + const exitInput = { + ...input, + exitWithNativeAsset: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, + }); + + assertSingleTokenExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); + }); }); - test('exit with native asset', async () => { - const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); - - const exitInput: ProportionalExitInput = { - chainId, - rpcUrl, - bptIn, - kind: ExitKind.PROPORTIONAL, - exitWithNativeAsset: true, - }; - - // Note - checking native balance - const { queryResult, maxBptIn, minAmountsOut } = await doExit({ - ...txInput, - exitInput, - checkNativeBalance: true, + describe('proportional exit', () => { + let input: ProportionalExitInput; + beforeAll(() => { + const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + input = { + bptIn, + chainId, + rpcUrl, + kind: ExitKind.Proportional, + }; }); - - // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); - - expect(queryResult.tokenOutIndex === undefined).toEqual(true); - - // We expect all assets to have a value for exit - queryResult.amountsOut.forEach((a) => { - expect(a.amount > 0n).to.be.true; + test('with tokens', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, + }); + + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); + }); + test('with native', async () => { + const exitInput = { + ...input, + useNativeAssetAsWrappedAmountIn: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, + }); + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); }); - - // Confirm slippage - only to amounts out not bpt in - const expectedMinAmountsOut = queryResult.amountsOut.map((a) => - slippage.removeFrom(a.amount), - ); - expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); }); }); From 4340eb20e1b6f671514ddc6e6151b04ce498ea1b Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 6 Nov 2023 09:52:36 +0000 Subject: [PATCH 153/199] fix: Check for undefined in .env. --- test/anvil/anvil-global-setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/anvil/anvil-global-setup.ts b/test/anvil/anvil-global-setup.ts index e90602e7..1d1e93e4 100644 --- a/test/anvil/anvil-global-setup.ts +++ b/test/anvil/anvil-global-setup.ts @@ -47,7 +47,7 @@ export const ANVIL_NETWORKS: Record = { function getAnvilOptions(network: NetworkSetup): CreateAnvilOptions { let forkUrl: string; - if (process.env[network.rpcEnv]) { + if (process.env[network.rpcEnv] !== 'undefined') { forkUrl = process.env[network.rpcEnv] as string; } else { if (!network.fallBackRpc) From aebbc403be43e79aa0570e2842d6cf09700f69ea Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 6 Nov 2023 11:45:56 +0000 Subject: [PATCH 154/199] feat: Add InputAmount type. --- src/types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/types.ts b/src/types.ts index b89b0b93..b0159b2d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,3 +62,9 @@ export interface BatchSwapStep { amount: bigint; userData: Hex; } + +export type InputAmount = { + address: Address; + decimals: number; + rawAmount: bigint; +}; From 70cd9e449b187a5187bff33aa70f8d54ed06a7c5 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 6 Nov 2023 11:47:29 +0000 Subject: [PATCH 155/199] refactor: Change exit inputs to use InputAmount type instead of TokenAmount. --- examples/exit/weighted.ts | 10 +++- src/entities/exit/types.ts | 8 +-- src/entities/exit/weighted/validateInputs.ts | 4 +- src/entities/exit/weighted/weightedExit.ts | 9 ++-- test/weightedExit.integration.test.ts | 53 ++++++++++++-------- 5 files changed, 51 insertions(+), 33 deletions(-) diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts index 10e092d4..c10bc536 100644 --- a/examples/exit/weighted.ts +++ b/examples/exit/weighted.ts @@ -1,3 +1,4 @@ +// Run with - pnpm example ./examples/exit/weighted.ts import dotenv from 'dotenv'; dotenv.config(); @@ -11,7 +12,7 @@ import { SingleAssetExitInput, Slippage, Token, - TokenAmount, + InputAmount, } from '../../src'; import { Client, @@ -23,6 +24,7 @@ import { TestActions, WalletActions, walletActions, + parseEther, } from 'viem'; import { forkSetup, @@ -61,7 +63,11 @@ const exit = async () => { [parseUnits('100', 18)], ); - const bptIn = TokenAmount.fromHumanAmount(bpt, '1'); + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolState.address, + }; const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL const poolExit = new PoolExit(); diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 3938bbc7..72507150 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -1,6 +1,6 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; -import { Address } from '../../types'; +import { Address, InputAmount } from '../../types'; import { PoolState } from '../types'; export enum ExitKind { @@ -18,18 +18,18 @@ export type BaseExitInput = { }; export type UnbalancedExitInput = BaseExitInput & { - amountsOut: TokenAmount[]; + amountsOut: InputAmount[]; kind: ExitKind.UNBALANCED; }; export type SingleAssetExitInput = BaseExitInput & { - bptIn: TokenAmount; + bptIn: InputAmount; tokenOut: Address; kind: ExitKind.SINGLE_ASSET; }; export type ProportionalExitInput = BaseExitInput & { - bptIn: TokenAmount; + bptIn: InputAmount; kind: ExitKind.PROPORTIONAL; }; diff --git a/src/entities/exit/weighted/validateInputs.ts b/src/entities/exit/weighted/validateInputs.ts index aaf86f10..b65dffb6 100644 --- a/src/entities/exit/weighted/validateInputs.ts +++ b/src/entities/exit/weighted/validateInputs.ts @@ -6,7 +6,7 @@ export function validateInputs(input: ExitInput, poolState: PoolStateInput) { switch (input.kind) { case ExitKind.UNBALANCED: areTokensInArray( - input.amountsOut.map((a) => a.token.address), + input.amountsOut.map((a) => a.address), poolState.tokens.map((t) => t.address), ); break; @@ -16,7 +16,7 @@ export function validateInputs(input: ExitInput, poolState: PoolStateInput) { poolState.tokens.map((t) => t.address), ); case ExitKind.PROPORTIONAL: - areTokensInArray([input.bptIn.token.address], [poolState.address]); + areTokensInArray([input.bptIn.address], [poolState.address]); default: break; } diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 28fecb7d..8e5aedc3 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -72,8 +72,9 @@ export class WeightedExit implements BaseExit { return { minAmountsOut: tokens.map( (t) => - input.amountsOut.find((a) => a.token.isEqual(t)) - ?.amount ?? 0n, + input.amountsOut.find((a) => + t.isSameAddress(a.address), + )?.rawAmount ?? 0n, ), tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, @@ -84,13 +85,13 @@ export class WeightedExit implements BaseExit { tokenOutIndex: tokens.findIndex((t) => t.isSameAddress(input.tokenOut), ), - maxBptAmountIn: input.bptIn.amount, + maxBptAmountIn: input.bptIn.rawAmount, }; case ExitKind.PROPORTIONAL: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: undefined, - maxBptAmountIn: input.bptIn.amount, + maxBptAmountIn: input.bptIn.rawAmount, }; } } diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 7a9e597b..154a9ca4 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -7,6 +7,7 @@ import { Client, createTestClient, http, + parseEther, parseUnits, publicActions, PublicActions, @@ -21,7 +22,6 @@ import { ExitKind, Slippage, Token, - TokenAmount, replaceWrapped, PoolStateInput, PoolExit, @@ -31,6 +31,7 @@ import { ChainId, getPoolAddress, ExitInput, + InputAmount, } from '../src'; import { forkSetup, sendTransactionGetBalances } from './lib/utils/helper'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; @@ -52,13 +53,13 @@ const poolId = describe('weighted exit test', () => { let txInput: TxInput; - let bptToken: Token; + let poolInput: PoolStateInput; beforeAll(async () => { // setup mock api const api = new MockApi(); // get pool state from api - const poolInput = await api.getPool(poolId); + poolInput = await api.getPool(poolId); const client = createTestClient({ mode: 'anvil', @@ -77,7 +78,6 @@ describe('weighted exit test', () => { exitInput: {} as ExitInput, checkNativeBalance: false, }; - bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); beforeEach(async () => { @@ -92,7 +92,11 @@ describe('weighted exit test', () => { test('single asset exit', async () => { const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL const exitInput: SingleAssetExitInput = { @@ -108,7 +112,7 @@ describe('weighted exit test', () => { }); // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + expect(queryResult.bptIn.amount).to.eq(bptIn.rawAmount); // We only expect single asset to have a value for exit expect(queryResult.tokenOutIndex !== undefined).toEqual(true); @@ -123,12 +127,16 @@ describe('weighted exit test', () => { slippage.removeFrom(a.amount), ); expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); + expect(maxBptIn).to.eq(bptIn.rawAmount); }); test('proportional exit', async () => { const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; const exitInput: ProportionalExitInput = { chainId, @@ -142,7 +150,7 @@ describe('weighted exit test', () => { }); // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + expect(queryResult.bptIn.amount).to.eq(bptIn.rawAmount); expect(queryResult.tokenOutIndex === undefined).toEqual(true); // We expect all assets to have a value for exit @@ -155,18 +163,17 @@ describe('weighted exit test', () => { slippage.removeFrom(a.amount), ); expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); + expect(maxBptIn).to.eq(bptIn.rawAmount); }); test('unbalanced exit', async () => { const { poolInput, slippage } = txInput; - const poolTokens = poolInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsOut = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '0.001'), - ); + const amountsOut = poolInput.tokens.map((t) => ({ + rawAmount: parseUnits('0.001', t.decimals), + decimals: t.decimals, + address: t.address, + })); const exitInput: UnbalancedExitInput = { chainId, @@ -185,11 +192,11 @@ describe('weighted exit test', () => { expect(queryResult.tokenOutIndex === undefined).toEqual(true); // We expect assets to have same amount out as user defined queryResult.amountsOut.forEach((a, i) => { - expect(a.amount).to.eq(amountsOut[i].amount); + expect(a.amount).to.eq(amountsOut[i].rawAmount); }); // Confirm slippage - only to bpt in, not amounts out - const expectedMinAmountsOut = amountsOut.map((a) => a.amount); + const expectedMinAmountsOut = amountsOut.map((a) => a.rawAmount); expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); const expectedMaxBptIn = slippage.applyTo(queryResult.bptIn.amount); expect(expectedMaxBptIn).to.deep.eq(maxBptIn); @@ -197,7 +204,11 @@ describe('weighted exit test', () => { test('exit with native asset', async () => { const { slippage } = txInput; - const bptIn = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; const exitInput: ProportionalExitInput = { chainId, @@ -215,7 +226,7 @@ describe('weighted exit test', () => { }); // Query should use correct BPT amount - expect(queryResult.bptIn.amount).to.eq(bptIn.amount); + expect(queryResult.bptIn.amount).to.eq(bptIn.rawAmount); expect(queryResult.tokenOutIndex === undefined).toEqual(true); @@ -229,7 +240,7 @@ describe('weighted exit test', () => { slippage.removeFrom(a.amount), ); expect(expectedMinAmountsOut).to.deep.eq(minAmountsOut); - expect(maxBptIn).to.eq(bptIn.amount); + expect(maxBptIn).to.eq(bptIn.rawAmount); }); async function doTransaction(txIp: TxInput) { From 00533a831bb7cefd86313796b49cd2f64435b63c Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 6 Nov 2023 12:08:36 +0000 Subject: [PATCH 156/199] refactor: Change join inputs to use InputAmount type instead of TokenAmount. --- examples/join/weighted.ts | 15 ++++--- .../composable-stable/composableStableJoin.ts | 4 +- src/entities/join/types.ts | 10 ++--- src/entities/join/utils/validateInputs.ts | 4 +- src/entities/join/weighted/weightedJoin.ts | 4 +- src/entities/utils/getAmounts.ts | 8 ++-- test/composableStableJoin.integration.test.ts | 39 +++++++++++-------- test/lib/utils/joinHelper.ts | 10 ++--- test/weightedJoin.integration.test.ts | 36 +++++++++-------- 9 files changed, 69 insertions(+), 61 deletions(-) diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts index bc0534f6..7b95ed81 100644 --- a/examples/join/weighted.ts +++ b/examples/join/weighted.ts @@ -1,3 +1,4 @@ +// Run with - pnpm example ./examples/join/weighted.ts import { config } from 'dotenv'; config(); @@ -9,8 +10,6 @@ import { PoolJoin, PoolStateInput, Slippage, - Token, - TokenAmount, UnbalancedJoinInput, } from '../../src'; import { @@ -64,12 +63,12 @@ const join = async () => { ); const poolJoin = new PoolJoin(); - const poolTokens = poolState.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - const amountsIn = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); + + const amountsIn = poolState.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); // perform join query to get expected bpt out const joinInput: UnbalancedJoinInput = { diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index a45b47c4..88e6d731 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -131,7 +131,7 @@ export class ComposableStableJoin implements BaseJoin { const maxAmountsIn = Array(poolTokens.length).fill(0n); maxAmountsIn[tokenInIndex] = MAX_UINT256; amountsJoin = { - minimumBpt: input.bptOut.amount, + minimumBpt: input.bptOut.rawAmount, maxAmountsIn, tokenInIndex, }; @@ -139,7 +139,7 @@ export class ComposableStableJoin implements BaseJoin { } case JoinKind.Proportional: { amountsJoin = { - minimumBpt: input.bptOut.amount, + minimumBpt: input.bptOut.rawAmount, maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), tokenInIndex: undefined, }; diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 61f26964..204db979 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -1,7 +1,7 @@ import { TokenAmount } from '../tokenAmount'; import { Slippage } from '../slippage'; import { PoolState } from '../types'; -import { Address, Hex } from '../../types'; +import { Address, Hex, InputAmount } from '../../types'; export enum JoinKind { Init = 'Init', @@ -19,23 +19,23 @@ type BaseJoinInput = { }; export type InitJoinInput = BaseJoinInput & { - amountsIn: TokenAmount[]; + amountsIn: InputAmount[]; kind: JoinKind.Init; }; export type UnbalancedJoinInput = BaseJoinInput & { - amountsIn: TokenAmount[]; + amountsIn: InputAmount[]; kind: JoinKind.Unbalanced; }; export type SingleAssetJoinInput = BaseJoinInput & { - bptOut: TokenAmount; + bptOut: InputAmount; tokenIn: Address; kind: JoinKind.SingleAsset; }; export type ProportionalJoinInput = BaseJoinInput & { - bptOut: TokenAmount; + bptOut: InputAmount; kind: JoinKind.Proportional; }; diff --git a/src/entities/join/utils/validateInputs.ts b/src/entities/join/utils/validateInputs.ts index aa56ac1e..89d577ab 100644 --- a/src/entities/join/utils/validateInputs.ts +++ b/src/entities/join/utils/validateInputs.ts @@ -13,7 +13,7 @@ export function validateInputs(input: JoinInput, poolState: PoolStateInput) { case JoinKind.Init: case JoinKind.Unbalanced: areTokensInArray( - input.amountsIn.map((a) => a.token.address), + input.amountsIn.map((a) => a.address), poolState.tokens.map((t) => t.address), ); break; @@ -23,7 +23,7 @@ export function validateInputs(input: JoinInput, poolState: PoolStateInput) { poolState.tokens.map((t) => t.address), ); case JoinKind.Proportional: - areTokensInArray([input.bptOut.token.address], [poolState.address]); + areTokensInArray([input.bptOut.address], [poolState.address]); default: break; } diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index b7ba584b..5a434932 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -116,14 +116,14 @@ export class WeightedJoin implements BaseJoin { const maxAmountsIn = Array(poolTokens.length).fill(0n); maxAmountsIn[tokenInIndex] = MAX_UINT256; return { - minimumBpt: input.bptOut.amount, + minimumBpt: input.bptOut.rawAmount, maxAmountsIn, tokenInIndex, }; } case JoinKind.Proportional: { return { - minimumBpt: input.bptOut.amount, + minimumBpt: input.bptOut.rawAmount, maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), tokenInIndex: undefined, }; diff --git a/src/entities/utils/getAmounts.ts b/src/entities/utils/getAmounts.ts index d2da549d..7fc1d351 100644 --- a/src/entities/utils/getAmounts.ts +++ b/src/entities/utils/getAmounts.ts @@ -1,5 +1,5 @@ +import { InputAmount } from '../../types'; import { Token } from '../token'; -import { TokenAmount } from '../tokenAmount'; /** * Get amounts from array of TokenAmounts returning default if not a value for tokens. @@ -10,10 +10,12 @@ import { TokenAmount } from '../tokenAmount'; */ export function getAmounts( tokens: Token[], - amounts: TokenAmount[], + amounts: InputAmount[], defaultAmount = 0n, ): bigint[] { return tokens.map( - (t) => amounts.find((a) => a.token.isEqual(t))?.amount ?? defaultAmount, + (t) => + amounts.find((a) => t.isSameAddress(a.address))?.rawAmount ?? + defaultAmount, ); } diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 127df7b7..9de13828 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -9,6 +9,7 @@ import { parseUnits, publicActions, walletActions, + parseEther, } from 'viem'; import { @@ -17,8 +18,6 @@ import { SingleAssetJoinInput, JoinKind, Slippage, - Token, - TokenAmount, Address, Hex, PoolStateInput, @@ -27,6 +26,7 @@ import { getPoolAddress, PoolJoin, JoinInput, + InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; import { JoinTxInput } from './lib/utils/types'; @@ -45,14 +45,14 @@ const poolId = describe('composable stable join test', () => { let txInput: JoinTxInput; - let bptToken: Token; + let poolStateInput: PoolStateInput; beforeAll(async () => { // setup mock api const api = new MockApi(); // get pool state from api - const poolInput = await api.getPool(poolId); + poolStateInput = await api.getPool(poolId); const client = createTestClient({ mode: 'anvil', @@ -66,13 +66,10 @@ describe('composable stable join test', () => { client, poolJoin: new PoolJoin(), slippage: Slippage.fromPercentage('1'), // 1% - poolStateInput: poolInput, + poolStateInput: poolStateInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig joinInput: {} as JoinInput, }; - - // setup BPT token - bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); beforeEach(async () => { @@ -91,18 +88,18 @@ describe('composable stable join test', () => { describe('unbalanced join', () => { let input: Omit; - let amountsIn: TokenAmount[]; + let amountsIn: InputAmount[]; beforeAll(() => { const bptIndex = txInput.poolStateInput.tokens.findIndex( (t) => t.address === txInput.poolStateInput.address, ); - const poolTokensWithoutBpt = txInput.poolStateInput.tokens - .map((t) => new Token(chainId, t.address, t.decimals)) + amountsIn = txInput.poolStateInput.tokens + .map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })) .filter((_, index) => index !== bptIndex); - - amountsIn = poolTokensWithoutBpt.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); input = { chainId, rpcUrl, @@ -150,7 +147,11 @@ describe('composable stable join test', () => { describe('single asset join', () => { let input: SingleAssetJoinInput; beforeAll(() => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; input = { bptOut, @@ -198,7 +199,11 @@ describe('composable stable join test', () => { describe('proportional join', () => { let input: ProportionalJoinInput; beforeAll(() => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; input = { bptOut, chainId, diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index feaac91f..e25c7139 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -165,11 +165,9 @@ export function assertUnbalancedJoin( ) token = new Token(chainId, zeroAddress, t.decimals); else token = new Token(chainId, t.address, t.decimals); - const input = joinInput.amountsIn.find( - (a) => a.token.address === t.address, - ); + const input = joinInput.amountsIn.find((a) => a.address === t.address); if (input === undefined) return TokenAmount.fromRawAmount(token, 0n); - else return TokenAmount.fromRawAmount(token, input.amount); + else return TokenAmount.fromRawAmount(token, input.rawAmount); }); const expectedQueryResult: Omit = { @@ -229,7 +227,7 @@ export function assertSingleToken( // Query should use same bpt out as user sets bptOut: TokenAmount.fromRawAmount( bptToken, - joinInput.bptOut.amount, + joinInput.bptOut.rawAmount, ), tokenInIndex: tokensWithoutBpt.findIndex( (t) => t.address === joinInput.tokenIn, @@ -294,7 +292,7 @@ export function assertProportional( // Query should use same bpt out as user sets bptOut: TokenAmount.fromRawAmount( bptToken, - joinInput.bptOut.amount, + joinInput.bptOut.rawAmount, ), // Only expect tokenInIndex for SingleAssetJoin tokenInIndex: undefined, diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index e466fcf1..03c026ad 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -9,6 +9,7 @@ import { parseUnits, publicActions, walletActions, + parseEther, } from 'viem'; import { @@ -17,8 +18,6 @@ import { SingleAssetJoinInput, JoinKind, Slippage, - Token, - TokenAmount, Address, Hex, PoolStateInput, @@ -27,6 +26,7 @@ import { getPoolAddress, PoolJoin, JoinInput, + InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; import { @@ -45,14 +45,14 @@ const poolId = describe('weighted join test', () => { let txInput: JoinTxInput; - let bptToken: Token; + let poolStateInput: PoolStateInput; beforeAll(async () => { // setup mock api const api = new MockApi(); // get pool state from api - const poolStateInput = await api.getPool(poolId); + poolStateInput = await api.getPool(poolId); const client = createTestClient({ mode: 'anvil', @@ -70,9 +70,6 @@ describe('weighted join test', () => { testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig joinInput: {} as JoinInput, }; - - // setup BPT token - bptToken = new Token(chainId, poolStateInput.address, 18, 'BPT'); }); beforeEach(async () => { @@ -95,14 +92,13 @@ describe('weighted join test', () => { describe('unbalanced join', () => { let input: Omit; - let amountsIn: TokenAmount[]; + let amountsIn: InputAmount[]; beforeAll(() => { - const poolTokens = txInput.poolStateInput.tokens.map( - (t) => new Token(chainId, t.address, t.decimals), - ); - amountsIn = poolTokens.map((t) => - TokenAmount.fromHumanAmount(t, '1'), - ); + amountsIn = txInput.poolStateInput.tokens.map((t) => ({ + rawAmount: parseUnits('0.001', t.decimals), + decimals: t.decimals, + address: t.address, + })); input = { chainId, rpcUrl, @@ -151,7 +147,11 @@ describe('weighted join test', () => { describe('single asset join', () => { let joinInput: SingleAssetJoinInput; beforeAll(() => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; joinInput = { bptOut, @@ -202,7 +202,11 @@ describe('weighted join test', () => { describe('proportional join', () => { let joinInput: ProportionalJoinInput; beforeAll(() => { - const bptOut = TokenAmount.fromHumanAmount(bptToken, '1'); + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; joinInput = { bptOut, chainId, From 8f4ca80476995c9e84f87a43a7638739acb0d789 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Mon, 6 Nov 2023 12:10:01 +0000 Subject: [PATCH 157/199] refactor: Use helper method to get amounts in exit. --- src/entities/exit/weighted/weightedExit.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 8e5aedc3..928b5f9d 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -20,6 +20,7 @@ import { } from '../types'; import { AmountsExit, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; +import { getAmounts } from '../../utils'; export class WeightedExit implements BaseExit { public async query( @@ -70,12 +71,7 @@ export class WeightedExit implements BaseExit { switch (input.kind) { case ExitKind.UNBALANCED: return { - minAmountsOut: tokens.map( - (t) => - input.amountsOut.find((a) => - t.isSameAddress(a.address), - )?.rawAmount ?? 0n, - ), + minAmountsOut: getAmounts(tokens, input.amountsOut), tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; From 394896ff49a70eafc06704c1933ae4339412614a Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 6 Nov 2023 09:57:30 -0300 Subject: [PATCH 158/199] Fixing Lint Errors --- test/lib/utils/exitHelper.ts | 50 ++++----------------------- test/weightedExit.integration.test.ts | 14 +++++--- 2 files changed, 16 insertions(+), 48 deletions(-) diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 3948723c..2c626719 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -183,21 +183,9 @@ export function assertUnbalancedExit( // Expect some bpt amount expect(exitQueryResult.bptIn.amount > 0n).to.be.true; - assertExitBuildOutput( - exitInput, - exitQueryResult, - exitBuildOutput, - false, - slippage, - ); + assertExitBuildOutput(exitQueryResult, exitBuildOutput, false, slippage); - assertTokenDeltas( - poolStateInput, - exitInput, - exitQueryResult, - exitBuildOutput, - txOutput, - ); + assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); } export function assertSingleTokenExit( @@ -253,21 +241,9 @@ export function assertSingleTokenExit( else expect(a.amount).toEqual(0n); }); - assertExitBuildOutput( - exitInput, - exitQueryResult, - exitBuildOutput, - true, - slippage, - ); + assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); - assertTokenDeltas( - poolStateInput, - exitInput, - exitQueryResult, - exitBuildOutput, - txOutput, - ); + assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); } export function assertProportionalExit( @@ -307,28 +283,15 @@ export function assertProportionalExit( else expect(a.amount > 0n).to.be.true; }); - assertExitBuildOutput( - exitInput, - exitQueryResult, - exitBuildOutput, - true, - slippage, - ); + assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); - assertTokenDeltas( - poolStateInput, - exitInput, - exitQueryResult, - exitBuildOutput, - txOutput, - ); + assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); } function assertTokenDeltas( poolStateInput: PoolStateInput, exitInput: ExitInput, exitQueryResult: ExitQueryResult, - exitBuildOutput: ExitBuildOutput, txOutput: TxResult, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); @@ -358,7 +321,6 @@ function assertTokenDeltas( } function assertExitBuildOutput( - exitInput: ExitInput, exitQueryResult: ExitQueryResult, exitBuildOutput: ExitBuildOutput, isExactIn: boolean, diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index be869c6d..6b1731dd 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -1,5 +1,5 @@ // pnpm test -- weightedExit.integration.test.ts -import { describe, expect, test, beforeAll, beforeEach } from 'vitest'; +import { describe, test, beforeAll, beforeEach } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -28,7 +28,12 @@ import { ExitInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalExit, assertSingleTokenExit, assertUnbalancedExit, doExit } from './lib/utils/exitHelper'; +import { + assertProportionalExit, + assertSingleTokenExit, + assertUnbalancedExit, + doExit, +} from './lib/utils/exitHelper'; import { ExitTxInput } from './lib/utils/types'; const chainId = ChainId.MAINNET; @@ -79,8 +84,9 @@ describe('weighted exit test', () => { let input: Omit; let amountsOut: TokenAmount[]; beforeAll(() => { - const poolTokens = txInput.poolStateInput.tokens - .map((t) => new Token(chainId, t.address, t.decimals)) + const poolTokens = txInput.poolStateInput.tokens.map( + (t) => new Token(chainId, t.address, t.decimals), + ); amountsOut = poolTokens.map((t) => TokenAmount.fromHumanAmount(t, '1'), From 851c161b8364844dcc072c6e659b075521446b98 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 6 Nov 2023 10:01:57 -0300 Subject: [PATCH 159/199] Fixing weightedJoin assert function names; --- test/composableStableExit.integration.test.ts | 2 +- test/weightedExit.integration.test.ts | 2 +- test/weightedJoin.integration.test.ts | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index a889fa9a..2d7cda1c 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -75,7 +75,7 @@ describe('composable stable exit test', () => { txInput.client, txInput.testAddress, [txInput.poolStateInput.address], - undefined, // TODO: hardcode these values to improve test performance + [0], // TODO: hardcode these values to improve test performance [parseUnits('1000', 18)], ); }); diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 6b1731dd..47786412 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -75,7 +75,7 @@ describe('weighted exit test', () => { txInput.client, txInput.testAddress, [txInput.poolStateInput.address], - undefined, // TODO: hardcode these values to improve test performance + [0], // TODO: hardcode these values to improve test performance [parseUnits('1000', 18)], ); }); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index d4dff628..3bf7ee54 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -31,7 +31,9 @@ import { import { forkSetup } from './lib/utils/helper'; import { assertProportional, + assertProportionalJoin, assertSingleToken, + assertSingleTokenJoin, assertUnbalancedJoin, doJoin, } from './lib/utils/joinHelper'; @@ -167,7 +169,7 @@ describe('weighted join test', () => { joinInput, }); - assertSingleToken( + assertSingleTokenJoin( txInput.client.chain?.id as number, txInput.poolStateInput, joinInput, @@ -185,7 +187,7 @@ describe('weighted join test', () => { }, }); - assertSingleToken( + assertSingleTokenJoin( txInput.client.chain?.id as number, txInput.poolStateInput, { @@ -215,7 +217,7 @@ describe('weighted join test', () => { joinInput, }); - assertProportional( + assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, joinInput, @@ -232,7 +234,7 @@ describe('weighted join test', () => { }, }); - assertProportional( + assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, { From 94633e2ef1e489e68a52671e262e36b5ec8cec84 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Mon, 6 Nov 2023 10:06:30 -0300 Subject: [PATCH 160/199] removing unused imports; --- test/weightedJoin.integration.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 3bf7ee54..28f46dbf 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -30,9 +30,7 @@ import { } from '../src'; import { forkSetup } from './lib/utils/helper'; import { - assertProportional, assertProportionalJoin, - assertSingleToken, assertSingleTokenJoin, assertUnbalancedJoin, doJoin, From 826bd07350e5685773d13c26dc1167d7019a01ce Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem de Liz <50002794+lgahdl@users.noreply.github.com> Date: Tue, 7 Nov 2023 10:24:33 -0300 Subject: [PATCH 161/199] Update test/lib/utils/exitHelper.ts Co-authored-by: John Grant --- test/lib/utils/exitHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 9dab99d3..24d7ea03 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -338,7 +338,7 @@ function assertExitBuildOutput( minAmountsOut, maxBptIn, to: BALANCER_VAULT, - // Value should equal value of any wrapped asset if using native + // Value should always be 0 for exits value: 0n, }; From 42771ee1e3982580ca0d43b80985f586fec3ec65 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Tue, 7 Nov 2023 10:34:04 -0300 Subject: [PATCH 162/199] making bptIndex mandatory for getAmountsQuery function; making getTokens reusable for join and exit helper as "getTokensForBalanceCheck" --- .../composable-stable/composableStableExit.ts | 2 +- test/lib/utils/exitHelper.ts | 15 +++------------ test/lib/utils/getTokensForBalanceCheck.ts | 17 +++++++++++++++++ test/lib/utils/joinHelper.ts | 15 ++------------- 4 files changed, 23 insertions(+), 26 deletions(-) create mode 100644 test/lib/utils/getTokensForBalanceCheck.ts diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index eef8d1c2..d85a15e3 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -79,7 +79,7 @@ export class ComposableStableExit implements BaseExit { private getAmountsQuery( tokens: Token[], input: ExitInput, - bptIndex?: number, + bptIndex: number, ): AmountsExit { switch (input.kind) { case ExitKind.Unbalanced: diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 24d7ea03..5dd72cec 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -1,6 +1,5 @@ import { ExitTxInput } from './types'; import { - Address, ChainId, ComposableStableExitQueryResult, ExitBuildOutput, @@ -11,7 +10,6 @@ import { Slippage, Token, UnbalancedExitInput, - ZERO_ADDRESS, SingleAssetExitInput, BALANCER_VAULT, ExitInput, @@ -20,6 +18,7 @@ import { import { sendTransactionGetBalances, TxResult } from './helper'; import { expect } from 'vitest'; import { zeroAddress } from 'viem'; +import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type ExitResult = { exitQueryResult: ExitQueryResult; @@ -51,15 +50,7 @@ export const sdkExit = async ({ }; }; -function getTokens(poolStateInput: PoolStateInput): Address[] { - // pool tokens, bpt, eth - const tokens = poolStateInput.tokens - .filter((t) => t.address !== poolStateInput.address) - .map((t) => t.address); - tokens.push(poolStateInput.address); - tokens.push(ZERO_ADDRESS); - return tokens; -} + function isComposableStableExitQueryResult(result: ExitQueryResult): boolean { return (result as ComposableStableExitQueryResult).bptIndex !== undefined; @@ -121,7 +112,7 @@ export async function doExit(txInput: ExitTxInput) { }); // get tokens for balance change - pool tokens, BPT, native - const tokens = getTokens(poolStateInput); + const tokens = getTokensForBalanceCheck(poolStateInput); // send transaction and calculate balance changes const txOutput = await sendTransactionGetBalances( diff --git a/test/lib/utils/getTokensForBalanceCheck.ts b/test/lib/utils/getTokensForBalanceCheck.ts new file mode 100644 index 00000000..f828f461 --- /dev/null +++ b/test/lib/utils/getTokensForBalanceCheck.ts @@ -0,0 +1,17 @@ +import { Address, PoolStateInput, ZERO_ADDRESS } from "../../../src"; + + +/** + * Get tokens from the pool formatted for balance check, i.e. [...poolTokens, bpt, eth] + * @param poolStateInput PoolStateInput + * @returns Address[] + */ +export function getTokensForBalanceCheck(poolStateInput: PoolStateInput): Address[] { + // pool tokens, bpt, eth + const tokens = poolStateInput.tokens + .filter((t) => t.address !== poolStateInput.address) + .map((t) => t.address); + tokens.push(poolStateInput.address); + tokens.push(ZERO_ADDRESS); + return tokens; +} \ No newline at end of file diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index dd995659..ea778d69 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -7,7 +7,6 @@ import { Address, JoinBuildOutput, JoinQueryResult, - ZERO_ADDRESS, UnbalancedJoinInput, BALANCER_VAULT, SingleAssetJoinInput, @@ -21,6 +20,7 @@ import { import { TxResult, sendTransactionGetBalances } from './helper'; import { JoinTxInput } from './types'; import { zeroAddress } from 'viem'; +import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type JoinResult = { joinQueryResult: JoinQueryResult; @@ -58,16 +58,6 @@ async function sdkJoin({ }; } -function getTokens(poolStateInput: PoolStateInput): Address[] { - // pool tokens, bpt, eth - const tokens = poolStateInput.tokens - .filter((t) => t.address !== poolStateInput.address) - .map((t) => t.address); - tokens.push(poolStateInput.address); - tokens.push(ZERO_ADDRESS); - return tokens; -} - function isComposableStableJoinQueryResult(result: JoinQueryResult): boolean { return (result as ComposableStableJoinQueryResult).bptIndex !== undefined; } @@ -127,8 +117,7 @@ export async function doJoin(txInput: JoinTxInput) { testAddress, }); - // get tokens for balance change - pool tokens, BPT, native - const tokens = getTokens(poolStateInput); + const tokens = getTokensForBalanceCheck(poolStateInput); // send transaction and calculate balance changes const txOutput = await sendTransactionGetBalances( From ab9933aaa7c60220090808bcf131c07cf7366d8f Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Wed, 8 Nov 2023 15:01:25 +0000 Subject: [PATCH 163/199] refactor: join/exit examples. Separate fork logic to keep focus on example. Add more comments and console logs. --- examples/exit/weighted.ts | 104 ---------------------------------- examples/exitPool.ts | 100 +++++++++++++++++++++++++++++++++ examples/join/weighted.ts | 106 ----------------------------------- examples/joinPool.ts | 97 ++++++++++++++++++++++++++++++++ examples/utils/makeForkTx.ts | 83 +++++++++++++++++++++++++++ 5 files changed, 280 insertions(+), 210 deletions(-) delete mode 100644 examples/exit/weighted.ts create mode 100644 examples/exitPool.ts delete mode 100644 examples/join/weighted.ts create mode 100644 examples/joinPool.ts create mode 100644 examples/utils/makeForkTx.ts diff --git a/examples/exit/weighted.ts b/examples/exit/weighted.ts deleted file mode 100644 index 6dfc85ea..00000000 --- a/examples/exit/weighted.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Run with - pnpm example ./examples/exit/weighted.ts -import dotenv from 'dotenv'; -dotenv.config(); - -import { BalancerApi } from '../../src/data/providers/balancer-api'; -import { - ChainId, - CHAINS, - ExitKind, - PoolExit, - PoolStateInput, - SingleAssetExitInput, - Slippage, - Token, - InputAmount, -} from '../../src'; -import { - Client, - createTestClient, - http, - parseUnits, - PublicActions, - publicActions, - TestActions, - WalletActions, - walletActions, - parseEther, -} from 'viem'; -import { - forkSetup, - sendTransactionGetBalances, -} from '../../test/lib/utils/helper'; -import { ANVIL_NETWORKS, startFork } from '../../test/anvil/anvil-global-setup'; - -const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; -const poolId = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH -const chainId = ChainId.MAINNET; -const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig -const slippage = Slippage.fromPercentage('1'); // 1% - -const exit = async () => { - const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); - const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState( - poolId, - ); - const client: Client & PublicActions & TestActions & WalletActions = - createTestClient({ - mode: 'anvil', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions); - const bpt = new Token(chainId, poolState.address, 18, 'BPT'); - - await forkSetup( - client, - testAddress, - [poolState.address], - [0], - [parseUnits('100', 18)], - ); - - const bptIn: InputAmount = { - rawAmount: parseEther('1'), - decimals: 18, - address: poolState.address, - }; - const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL - - const poolExit = new PoolExit(); - - const exitInput: SingleAssetExitInput = { - chainId, - rpcUrl, - bptIn, - tokenOut, - kind: ExitKind.SingleAsset, - }; - - const queryResult = await poolExit.query(exitInput, poolState); - - const { call, to, value } = poolExit.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [...poolState.tokens.map(({ address }) => address), bpt.address], - client, - testAddress, - to, - call, - value, - ); - console.log(`transaction status: ${transactionReceipt.status}`); - console.log(`token amounts deltas per token: ${balanceDeltas}`); -}; - -exit(); diff --git a/examples/exitPool.ts b/examples/exitPool.ts new file mode 100644 index 00000000..9e4744cd --- /dev/null +++ b/examples/exitPool.ts @@ -0,0 +1,100 @@ +/** + * Example showing how to exit a pool. + * (Runs against a local Anvil fork) + * + * Run with: + * pnpm example ./examples/exitPool.ts + */ +import dotenv from 'dotenv'; +dotenv.config(); + +import { + ChainId, + ExitKind, + PoolExit, + PoolStateInput, + Slippage, + InputAmount, + ExitInput, + BalancerApi, +} from '../src'; +import { parseEther } from 'viem'; +import { ANVIL_NETWORKS, startFork } from '../test/anvil/anvil-global-setup'; +import { makeForkTx } from './utils/makeForkTx'; + +const exit = async () => { + // User defined: + const chainId = ChainId.MAINNET; + const userAccount = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; + const poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + const tokenOut = '0xba100000625a3754423978a60c9317c58a424e3D'; // BAL + const slippage = Slippage.fromPercentage('1'); // 1% + + // Start a local anvil fork that will be used to query/tx against + const { rpcUrl } = await startFork(ANVIL_NETWORKS[ChainId[chainId]]); + + // API is used to fetch relevant pool data + const balancerApi = new BalancerApi( + 'https://backend-v3-canary.beets-ftm-node.com/graphql', + chainId, + ); + const poolStateInput: PoolStateInput = + await balancerApi.pools.fetchPoolState(poolId); + + // Construct the ExitInput, in this case a SingleAsset exit + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + const exitInput: ExitInput = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; + + // Simulate the exit to get the tokens out + const poolExit = new PoolExit(); + const queryResult = await poolExit.query(exitInput, poolStateInput); + + console.log('\nExit Query Result:'); + console.log(`BPT In: ${queryResult.bptIn.amount.toString()}\nTokens Out:`); + queryResult.amountsOut.map((a) => + console.log(a.token.address, a.amount.toString()), + ); + + // Apply slippage to the tokens out received from the query and construct the call + const call = poolExit.buildCall({ + ...queryResult, + slippage, + sender: userAccount, + recipient: userAccount, + }); + + console.log('\nWith slippage applied:'); + console.log(`Max BPT In: ${call.maxBptIn.toString()}`); // TODO these should be InputAmounts or TokenAmounts? + console.log(`Min amounts out: ${call.minAmountsOut}`); // TODO these should be InputAmounts or TokenAmounts? + + // Make the tx against the local fork and print the result + await makeForkTx( + call, + { + rpcUrl, + chainId, + impersonateAccount: userAccount, + forkTokens: [ + { + address: bptIn.address, + slot: 0, + rawBalance: bptIn.rawAmount, + }, + ], + }, + poolStateInput, + ); +}; + +exit(); diff --git a/examples/join/weighted.ts b/examples/join/weighted.ts deleted file mode 100644 index 7b95ed81..00000000 --- a/examples/join/weighted.ts +++ /dev/null @@ -1,106 +0,0 @@ -// Run with - pnpm example ./examples/join/weighted.ts -import { config } from 'dotenv'; -config(); - -import { - BalancerApi, - ChainId, - CHAINS, - JoinKind, - PoolJoin, - PoolStateInput, - Slippage, - UnbalancedJoinInput, -} from '../../src'; -import { - Client, - createTestClient, - http, - parseUnits, - PublicActions, - publicActions, - TestActions, - WalletActions, - walletActions, -} from 'viem'; -import { - forkSetup, - sendTransactionGetBalances, -} from '../../test/lib/utils/helper'; -import { ANVIL_NETWORKS, startFork } from '../../test/anvil/anvil-global-setup'; - -const balancerApiUrl = 'https://backend-v3-canary.beets-ftm-node.com/graphql'; -const poolId = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH -const chainId = ChainId.MAINNET; -const testAddress = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; // Balancer DAO Multisig -const slippage = Slippage.fromPercentage('1'); // 1% - -const join = async () => { - const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); - const balancerApi = new BalancerApi(balancerApiUrl, 1); - const poolState: PoolStateInput = await balancerApi.pools.fetchPoolState( - poolId, - ); - const client: Client & PublicActions & TestActions & WalletActions = - createTestClient({ - mode: 'anvil', - chain: CHAINS[chainId], - transport: http(rpcUrl), - }) - .extend(publicActions) - .extend(walletActions); - - await forkSetup( - client, - testAddress, - [...poolState.tokens.map((t) => t.address), poolState.address], - [1, 3, 0], - [ - ...poolState.tokens.map((t) => parseUnits('100', t.decimals)), - parseUnits('100', 18), - ], - ); - - const poolJoin = new PoolJoin(); - - const amountsIn = poolState.tokens.map((t) => ({ - rawAmount: parseUnits('1', t.decimals), - decimals: t.decimals, - address: t.address, - })); - - // perform join query to get expected bpt out - const joinInput: UnbalancedJoinInput = { - amountsIn, - chainId, - rpcUrl, - kind: JoinKind.Unbalanced, - }; - - const queryResult = await poolJoin.query(joinInput, poolState); - - const { call, to, value } = poolJoin.buildCall({ - ...queryResult, - slippage, - sender: testAddress, - recipient: testAddress, - }); - const { transactionReceipt, balanceDeltas } = - await sendTransactionGetBalances( - [ - ...poolState.tokens.map(({ address }) => address), - poolState.address /*BPT*/, - ], - client, - testAddress, - to, - call, - value, - ); - console.log(`transaction status: ${transactionReceipt.status}`); - console.log(`token amounts deltas per token: ${balanceDeltas}`); - return; -}; - -join().then(() => {}); diff --git a/examples/joinPool.ts b/examples/joinPool.ts new file mode 100644 index 00000000..390d1e6d --- /dev/null +++ b/examples/joinPool.ts @@ -0,0 +1,97 @@ +/** + * Example showing how to join a pool. + * (Runs against a local Anvil fork) + * + * Run with: + * pnpm example ./examples/joinPool.ts + */ +import { config } from 'dotenv'; +config(); + +import { + BalancerApi, + ChainId, + JoinInput, + JoinKind, + PoolJoin, + Slippage, +} from '../src'; +import { parseUnits } from 'viem'; +import { ANVIL_NETWORKS, startFork } from '../test/anvil/anvil-global-setup'; +import { makeForkTx } from './utils/makeForkTx'; + +const join = async () => { + // User defined + const chainId = ChainId.MAINNET; + const userAccount = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; + const poolId = + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH + const slippage = Slippage.fromPercentage('1'); // 1% + + // Start a local anvil fork that will be used to query/tx against + const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); + + // API is used to fetch relevant pool data + const balancerApi = new BalancerApi( + 'https://backend-v3-canary.beets-ftm-node.com/graphql', + chainId, + ); + const poolStateInput = await balancerApi.pools.fetchPoolState(poolId); + + // We create arbitrary amounts in but these would usually be set by user + const amountsIn = poolStateInput.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); + + // Construct the JoinInput, in this case an Unbalanced join + const joinInput: JoinInput = { + amountsIn, + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + + // Simulate the join to get the amount of BPT out + const poolJoin = new PoolJoin(); + const queryResult = await poolJoin.query(joinInput, poolStateInput); + + console.log('\nJoin Query Result:'); + console.log('Tokens In:'); + queryResult.amountsIn.map((a) => + console.log(a.token.address, a.amount.toString()), + ); + console.log(`BPT Out: ${queryResult.bptOut.amount.toString()}`); + + // Apply slippage to the BPT amount received from the query and construct the call + const call = poolJoin.buildCall({ + ...queryResult, + slippage, + sender: userAccount, + recipient: userAccount, + }); + + console.log('\nWith slippage applied:'); + console.log(`Max tokens in: ${call.maxAmountsIn}`); // TODO these should be InputAmounts or TokenAmounts? + console.log(`Min BPT Out: ${call.minBptOut.toString()}`); // TODO these should be InputAmounts or TokenAmounts? + + // Make the tx against the local fork and print the result + const slots = [1, 3, 0]; + await makeForkTx( + call, + { + rpcUrl, + chainId, + impersonateAccount: userAccount, + forkTokens: amountsIn.map((a, i) => ({ + address: a.address, + slot: slots[i], + rawBalance: a.rawAmount, + })), + }, + poolStateInput, + ); +}; + +join().then(() => {}); diff --git a/examples/utils/makeForkTx.ts b/examples/utils/makeForkTx.ts new file mode 100644 index 00000000..338a430d --- /dev/null +++ b/examples/utils/makeForkTx.ts @@ -0,0 +1,83 @@ +import { CHAINS, PoolStateInput } from '../../src'; +import { + createTestClient, + http, + publicActions, + walletActions, + Address, + Hex, +} from 'viem'; +import { + forkSetup, + sendTransactionGetBalances, +} from '../../test/lib/utils/helper'; + +type Tx = { + to: Address; + call: Hex; + value: bigint; +}; + +type ForkToken = { + address: Address; + slot: number; + rawBalance: bigint; +}; + +/** + * Sets balances for forkTokens, send tx to Anvil fork and print pool token deltas for account + * @param tx + * @param impersonateAccount + * @param rpcUrl + * @param poolStateInput + * @param forkTokens + */ +export async function makeForkTx( + tx: Tx, + forkConfig: { + rpcUrl: string; + chainId: number; + impersonateAccount: Address; + forkTokens: ForkToken[]; + }, + poolStateInput: PoolStateInput, +) { + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[forkConfig.chainId], + transport: http(forkConfig.rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + await forkSetup( + client, + forkConfig.impersonateAccount, + forkConfig.forkTokens.map((t) => t.address), + forkConfig.forkTokens.map((t) => t.slot), + forkConfig.forkTokens.map((t) => t.rawBalance), + ); + + console.log('\nSending tx...'); + + const tokensForBalanceCheck = [ + ...poolStateInput.tokens.map(({ address }) => address), + poolStateInput.address, + ]; + const { transactionReceipt, balanceDeltas } = + await sendTransactionGetBalances( + tokensForBalanceCheck, + client, + forkConfig.impersonateAccount, + tx.to, + tx.call, + tx.value, + ); + if (transactionReceipt.status === 'reverted') + throw Error('Transaction reverted'); + + console.log('Token balance deltas:'); + tokensForBalanceCheck.forEach((t, i) => { + console.log(`${t} ${balanceDeltas[i]}`); + }); +} From ef3f07af3ff5b4472451beb04b92f2a445676299 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 8 Nov 2023 19:29:27 -0300 Subject: [PATCH 164/199] Adding support to gyro joins and exits. --- src/entities/exit/poolExit.ts | 3 + src/entities/exit/utils/validateInputs.ts | 40 +++- src/entities/join/poolJoin.ts | 7 +- src/entities/join/utils/validateInputs.ts | 39 +++- test/gyroEV2Exit.integration.test.ts | 201 ++++++++++++++++++++ test/gyroEV2Join.integration.test.ts | 222 ++++++++++++++++++++++ 6 files changed, 500 insertions(+), 12 deletions(-) create mode 100644 test/gyroEV2Exit.integration.test.ts create mode 100644 test/gyroEV2Join.integration.test.ts diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 51d46df0..2142de7d 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -18,6 +18,9 @@ export class PoolExit { constructor(config?: ExitConfig) { const { customPoolExits } = config || {}; this.poolExits = { + GYRO2: new WeightedExit(), + GYRO3: new WeightedExit(), + GYROE: new WeightedExit(), WEIGHTED: new WeightedExit(), // PHANTOM_STABLE === ComposableStables in API PHANTOM_STABLE: new ComposableStableExit(), diff --git a/src/entities/exit/utils/validateInputs.ts b/src/entities/exit/utils/validateInputs.ts index 50f0286c..72bca12e 100644 --- a/src/entities/exit/utils/validateInputs.ts +++ b/src/entities/exit/utils/validateInputs.ts @@ -1,14 +1,16 @@ import { ExitInput, ExitKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; +import { Address } from 'viem'; +import { MinimalToken } from '../../../data'; export function validateInputs(input: ExitInput, poolState: PoolStateInput) { - const bptIndex = poolState.tokens.findIndex( - (t) => t.address === poolState.address, + validateComposableStableWithoutBPT( + poolState.type, + poolState.address, + poolState.tokens, ); - if (['PHANTOM_STABLE'].includes(poolState.type) && bptIndex < 0) { - throw new Error('Pool Tokens does not contain BPT'); - } + validateGyroPoolJoinIsNotProportional(input.kind, poolState.type); switch (input.kind) { case ExitKind.Unbalanced: areTokensInArray( @@ -27,3 +29,31 @@ export function validateInputs(input: ExitInput, poolState: PoolStateInput) { break; } } + + +export const gyroExitKindNotSupported = 'INPUT_ERROR: Gyro pools do not implement this exit kind, only Proportional Exits(1 - EXACT_BPT_IN_FOR_TOKENS_OUT) are supported'; + +function validateGyroPoolJoinIsNotProportional( + kind: ExitKind, + poolType: string, +) { + if ( + ['GYROE', 'GYRO2', 'GYRO3'].includes(poolType) && + kind !== ExitKind.Proportional + ) { + throw new Error(gyroExitKindNotSupported); + } +} + +function validateComposableStableWithoutBPT( + poolType: string, + poolAddress: Address, + poolTokens: MinimalToken[], +) { + const bptIndex = poolTokens.findIndex((t) => t.address === poolAddress); + if (['PHANTOM_STABLE'].includes(poolType) && bptIndex < 0) { + throw new Error( + 'INPUT_ERROR: Composable Stable Pool State without BPT token included', + ); + } +} diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index d80e6e3a..9bd9434f 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -18,10 +18,13 @@ export class PoolJoin { constructor(config?: JoinConfig) { const { customPoolJoins } = config || {}; this.poolJoins = { - WEIGHTED: new WeightedJoin(), // PHANTOM_STABLE === ComposableStables in API - PHANTOM_STABLE: new ComposableStableJoin(), // custom pool Joins take precedence over base Joins + PHANTOM_STABLE: new ComposableStableJoin(), + GYRO2: new WeightedJoin(), + GYRO3: new WeightedJoin(), + GYROE: new WeightedJoin(), + WEIGHTED: new WeightedJoin(), ...customPoolJoins, }; } diff --git a/src/entities/join/utils/validateInputs.ts b/src/entities/join/utils/validateInputs.ts index 89d577ab..143054b2 100644 --- a/src/entities/join/utils/validateInputs.ts +++ b/src/entities/join/utils/validateInputs.ts @@ -1,14 +1,16 @@ import { JoinInput, JoinKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; +import { Address } from 'viem'; +import { MinimalToken } from '../../../data'; export function validateInputs(input: JoinInput, poolState: PoolStateInput) { - const bptIndex = poolState.tokens.findIndex( - (t) => t.address === poolState.address, + validateComposableStableWithoutBPT( + poolState.type, + poolState.address, + poolState.tokens, ); - if (['PHANTOM_STABLE'].includes(poolState.type) && bptIndex < 0) { - throw new Error('Pool Tokens does not contain BPT'); - } + validateGyroPoolJoinIsNotProportional(input.kind, poolState.type); switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: @@ -28,3 +30,30 @@ export function validateInputs(input: JoinInput, poolState: PoolStateInput) { break; } } + +export const gyroJoinKindNotSupported = 'INPUT_ERROR: Gyro pools do not implement this join kind, only Proportional Joins(3 - ALL_TOKENS_IN_FOR_BPT_OUT) are supported' + +function validateGyroPoolJoinIsNotProportional( + kind: JoinKind, + poolType: string, +) { + if ( + ['GYROE', 'GYRO2', 'GYRO3'].includes(poolType) && + kind !== JoinKind.Proportional + ) { + throw new Error(gyroJoinKindNotSupported); + } +} + +function validateComposableStableWithoutBPT( + poolType: string, + poolAddress: Address, + poolTokens: MinimalToken[], +) { + const bptIndex = poolTokens.findIndex((t) => t.address === poolAddress); + if (['PHANTOM_STABLE'].includes(poolType) && bptIndex < 0) { + throw new Error( + 'INPUT_ERROR: Composable Stable Pool State without BPT token included', + ); + } +} diff --git a/test/gyroEV2Exit.integration.test.ts b/test/gyroEV2Exit.integration.test.ts new file mode 100644 index 00000000..5b480bcf --- /dev/null +++ b/test/gyroEV2Exit.integration.test.ts @@ -0,0 +1,201 @@ +// pnpm test -- weightedExit.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseEther, + parseUnits, + publicActions, + walletActions, +} from 'viem'; +import { + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + Slippage, + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, + InputAmount, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; +import { ExitTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; + +const chainId = ChainId.MAINNET; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); +const poolId = + '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // 80BAL-20WETH + +describe('weighted exit test', () => { + let txInput: ExitTxInput; + let poolInput: PoolStateInput; + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput: poolInput, + testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig + exitInput: {} as ExitInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [txInput.poolStateInput.address], + [0], // TODO: hardcode these values to improve test performance + [parseUnits('1000', 18)], + ); + }); + + describe('proportional exit', () => { + let input: ProportionalExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('0.01'), + decimals: 18, + address: poolInput.address, + }; + input = { + bptIn, + chainId, + rpcUrl, + kind: ExitKind.Proportional, + }; + }); + test('with tokens', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, + }); + + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); + }); + test('with native', async () => { + const exitInput = { + ...input, + useNativeAssetAsWrappedAmountIn: true, + }; + const exitResult = await doExit({ + ...txInput, + exitInput, + }); + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + exitInput, + exitResult, + txInput.slippage, + ); + }); + }); + + describe('unbalanced exit', async () => { + let input: Omit; + let amountsOut: InputAmount[]; + beforeAll(() => { + amountsOut = poolInput.tokens.map((t) => ({ + rawAmount: parseUnits('0.001', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: ExitKind.Unbalanced, + }; + }); + test('must throw error, exit kind not supported', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + }; + await expect(() => + doExit({ ...txInput, exitInput }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); + + describe('single asset exit', () => { + let input: SingleAssetExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; + const tokenOut = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH + input = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; + }); + test('must throw error, exit kind not supported', async () => { + await expect(() => + doExit({ ...txInput, exitInput: input }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYROE', + tokens: [ + { + address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH + decimals: 18, + index: 0, + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH + decimals: 18, + index: 1, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyroEV2Join.integration.test.ts b/test/gyroEV2Join.integration.test.ts new file mode 100644 index 00000000..f90a8257 --- /dev/null +++ b/test/gyroEV2Join.integration.test.ts @@ -0,0 +1,222 @@ +// pnpm test -- weightedJoin.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseUnits, + publicActions, + walletActions, + parseEther, + Address, +} from 'viem'; + +import { + ProportionalJoinInput, + JoinKind, + Slippage, + Hex, + PoolStateInput, + CHAINS, + ChainId, + PoolJoin, + JoinInput, + InputAmount, + getPoolAddress, + UnbalancedJoinInput, + SingleAssetJoinInput, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; +import { JoinTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; + +const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); +const chainId = ChainId.MAINNET; +const poolId = + '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // ECLP-wstETH-wETH + +describe('gyro join test', () => { + let txInput: JoinTxInput; + let poolStateInput: PoolStateInput; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolStateInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput, + testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig + joinInput: {} as JoinInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [ + ...txInput.poolStateInput.tokens.map((t) => t.address), + txInput.poolStateInput.address, + ], + [0, 98, 0], + [ + ...txInput.poolStateInput.tokens.map((t) => + parseUnits('100', t.decimals), + ), + parseUnits('100', 18), + ], + ); + }); + + describe('proportional join', () => { + let joinInput: ProportionalJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('2'), + decimals: 18, + address: poolStateInput.address, + }; + joinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + }); + test('with tokens', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + + assertProportionalJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); + test('with native', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput: { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + }); + console.log(joinResult.joinBuildOutput.value); + assertProportionalJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + { + ...joinInput, + useNativeAssetAsWrappedAmountIn: true, + }, + joinResult, + txInput.slippage, + ); + }); + }); + + describe('unbalanced join', () => { + let input: Omit; + let amountsIn: InputAmount[]; + beforeAll(() => { + amountsIn = txInput.poolStateInput.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + }); + test('with tokens', async () => { + const joinInput = { + ...input, + amountsIn: [...amountsIn.splice(0, 1)], + }; + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); + + describe('single asset join', () => { + let joinInput: SingleAssetJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + joinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + }); + + test('must throw unsupported single asset join error', async () => { + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYROE', + tokens: [ + { + address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', // wstETH + decimals: 18, + index: 0, + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // wETH + decimals: 18, + index: 1, + }, + ], + }; + } +} + +/******************************************************************************/ From 9699a6450e9748669f43e495ea004dcd6707799f Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Wed, 8 Nov 2023 19:32:05 -0300 Subject: [PATCH 165/199] Fixing/Removing comments; --- test/gyroEV2Exit.integration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/gyroEV2Exit.integration.test.ts b/test/gyroEV2Exit.integration.test.ts index 5b480bcf..2dc41dd5 100644 --- a/test/gyroEV2Exit.integration.test.ts +++ b/test/gyroEV2Exit.integration.test.ts @@ -36,7 +36,7 @@ import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInp const chainId = ChainId.MAINNET; const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const poolId = - '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // 80BAL-20WETH + '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // ECLP-wstETH-wETH describe('weighted exit test', () => { let txInput: ExitTxInput; @@ -71,7 +71,7 @@ describe('weighted exit test', () => { txInput.client, txInput.testAddress, [txInput.poolStateInput.address], - [0], // TODO: hardcode these values to improve test performance + [0], [parseUnits('1000', 18)], ); }); From fbb917a3188be64e4c569e26712bae6e717138a2 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 9 Nov 2023 10:45:19 +0000 Subject: [PATCH 166/199] fix: We are working on assumption that API will return BPT token in token list (as current SG does) It does not currently do this so we have to add it manually --- .../balancer-api/modules/pool-state/index.ts | 25 +++++++++++++-- test/balancerApi.test.ts | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 test/balancerApi.test.ts diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index eeab4cd7..f333ad97 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -78,14 +78,33 @@ export class Pools { constructor(private readonly balancerApiClient: BalancerApiClient) {} async fetchPoolState(id: string): Promise { - const { - data: { poolGetPool }, - } = await this.balancerApiClient.fetch({ + const { data } = await this.balancerApiClient.fetch({ query: this.poolStateQuery, variables: { id, }, }); + const poolGetPool: PoolStateInput = data.poolGetPool; + /** + * TODO: + * We are working on assumption that API will return BPT token in token list (as current SG does) + * It does not currently do this so we have to add it manually + */ + if (poolGetPool.type === 'PHANTOM_STABLE') { + let missingBPTIndex = 0; + const sortedIndexes = poolGetPool.tokens.map((t) => t.index).sort(); + for (let i = 0; i < poolGetPool.tokens.length + 1; i++) { + if (i === poolGetPool.tokens.length || sortedIndexes[i] !== i) { + missingBPTIndex = i; + break; + } + } + poolGetPool.tokens.splice(missingBPTIndex, 0, { + index: missingBPTIndex, + address: poolGetPool.address, + decimals: 18, + }); + } return poolGetPool; } } diff --git a/test/balancerApi.test.ts b/test/balancerApi.test.ts new file mode 100644 index 00000000..f9d65360 --- /dev/null +++ b/test/balancerApi.test.ts @@ -0,0 +1,31 @@ +// pnpm test -- balancerApi.test.ts +import { describe, expect, test } from 'vitest'; +import { BalancerApi, PoolStateInput, ChainId } from '../src'; + +describe( + 'BalancerApi Provider', + () => { + test('CS Pool - Should add BPT to tokens', async () => { + const chainId = ChainId.MAINNET; + // wstEth/WETH CS Pool + const poolId = + '0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2'; + + // API is used to fetch relevant pool data + const balancerApi = new BalancerApi( + 'https://backend-v3-canary.beets-ftm-node.com/graphql', + chainId, + ); + const poolStateInput: PoolStateInput = + await balancerApi.pools.fetchPoolState(poolId); + + expect(poolStateInput.tokens.length).toEqual(3); + expect(poolStateInput.tokens[1].address).toEqual( + poolStateInput.address, + ); + }); + }, + { + timeout: 60000, + }, +); From 0e580a8ef35de6a6cd8f36c216ae3d02cd2343de Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 9 Nov 2023 13:48:14 +0000 Subject: [PATCH 167/199] feat: Make CS BPT addition more resilient by only adding if it doesnt already exist. --- .../balancer-api/modules/pool-state/index.ts | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/data/providers/balancer-api/modules/pool-state/index.ts b/src/data/providers/balancer-api/modules/pool-state/index.ts index f333ad97..5cfca48d 100644 --- a/src/data/providers/balancer-api/modules/pool-state/index.ts +++ b/src/data/providers/balancer-api/modules/pool-state/index.ts @@ -85,25 +85,37 @@ export class Pools { }, }); const poolGetPool: PoolStateInput = data.poolGetPool; - /** - * TODO: - * We are working on assumption that API will return BPT token in token list (as current SG does) - * It does not currently do this so we have to add it manually - */ + if (poolGetPool.type === 'PHANTOM_STABLE') { - let missingBPTIndex = 0; - const sortedIndexes = poolGetPool.tokens.map((t) => t.index).sort(); - for (let i = 0; i < poolGetPool.tokens.length + 1; i++) { - if (i === poolGetPool.tokens.length || sortedIndexes[i] !== i) { - missingBPTIndex = i; - break; + if ( + !poolGetPool.tokens.some( + (t) => t.address === poolGetPool.address, + ) + ) { + /** + * TODO: + * We are working on assumption that API will return BPT token in token list (as current SG does) + * If it doesn't (as of 09/11/23) we have to add it manually + */ + let missingBPTIndex = 0; + const sortedIndexes = poolGetPool.tokens + .map((t) => t.index) + .sort(); + for (let i = 0; i < poolGetPool.tokens.length + 1; i++) { + if ( + i === poolGetPool.tokens.length || + sortedIndexes[i] !== i + ) { + missingBPTIndex = i; + break; + } } + poolGetPool.tokens.splice(missingBPTIndex, 0, { + index: missingBPTIndex, + address: poolGetPool.address, + decimals: 18, + }); } - poolGetPool.tokens.splice(missingBPTIndex, 0, { - index: missingBPTIndex, - address: poolGetPool.address, - decimals: 18, - }); } return poolGetPool; } From 4ef701db221df2062415a44fda2d8c4aeb30e0e4 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 9 Nov 2023 13:56:30 +0000 Subject: [PATCH 168/199] feat: Add toInputAmount helper on TokenAmount. --- src/entities/tokenAmount.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/entities/tokenAmount.ts b/src/entities/tokenAmount.ts index 9b9a5cb0..580098ee 100644 --- a/src/entities/tokenAmount.ts +++ b/src/entities/tokenAmount.ts @@ -3,6 +3,7 @@ import _Decimal from 'decimal.js-light'; import { parseUnits } from 'viem'; import { DECIMAL_SCALES } from '../utils/constants'; import { WAD } from '../utils/math'; +import { InputAmount } from '../types'; export type BigintIsh = bigint | string | number; @@ -77,4 +78,12 @@ export class TokenAmount { .toDecimalPlaces(significantDigits) .toString(); } + + public toInputAmount(): InputAmount { + return { + address: this.token.address, + decimals: this.token.decimals, + rawAmount: this.amount, + }; + } } From 1192f573d6b25d1d18eaeef60abcec1541a74038 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Thu, 9 Nov 2023 20:50:13 -0300 Subject: [PATCH 169/199] Adding tests for other gyro pool types (no test with native token, cause there are no gyro pools from other versions with wrapped native asset); Adding comments to explain that the gyro pool types only accept Proportional Join/Exit; --- src/entities/exit/poolExit.ts | 1 + src/entities/join/poolJoin.ts | 1 + test/gyro2Exit.integration.test.ts | 186 ++++++++++++++++++++++++ test/gyro2Join.integration.test.ts | 204 ++++++++++++++++++++++++++ test/gyro3Exit.integration.test.ts | 191 ++++++++++++++++++++++++ test/gyro3Join.integration.test.ts | 208 +++++++++++++++++++++++++++ test/gyroEExit.integration.test.ts | 186 ++++++++++++++++++++++++ test/gyroEJoin.integration.test.ts | 203 ++++++++++++++++++++++++++ test/gyroEV2Exit.integration.test.ts | 2 +- test/gyroEV2Join.integration.test.ts | 3 +- 10 files changed, 1182 insertions(+), 3 deletions(-) create mode 100644 test/gyro2Exit.integration.test.ts create mode 100644 test/gyro2Join.integration.test.ts create mode 100644 test/gyro3Exit.integration.test.ts create mode 100644 test/gyro3Join.integration.test.ts create mode 100644 test/gyroEExit.integration.test.ts create mode 100644 test/gyroEJoin.integration.test.ts diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 2142de7d..3ea4a31e 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -18,6 +18,7 @@ export class PoolExit { constructor(config?: ExitConfig) { const { customPoolExits } = config || {}; this.poolExits = { + //GYRO2, GYRO3, GYROE only support Proportional Exit(1 - EXACT_BPT_IN_FOR_TOKENS_OUT) GYRO2: new WeightedExit(), GYRO3: new WeightedExit(), GYROE: new WeightedExit(), diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 9bd9434f..4c81f4cf 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -21,6 +21,7 @@ export class PoolJoin { // PHANTOM_STABLE === ComposableStables in API // custom pool Joins take precedence over base Joins PHANTOM_STABLE: new ComposableStableJoin(), + //GYRO2, GYRO3, GYROE pool types only support Proportional Join(3 - ALL_TOKENS_IN_FOR_BPT_OUT) GYRO2: new WeightedJoin(), GYRO3: new WeightedJoin(), GYROE: new WeightedJoin(), diff --git a/test/gyro2Exit.integration.test.ts b/test/gyro2Exit.integration.test.ts new file mode 100644 index 00000000..23b6870d --- /dev/null +++ b/test/gyro2Exit.integration.test.ts @@ -0,0 +1,186 @@ +// pnpm test -- weightedExit.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseEther, + parseUnits, + publicActions, + walletActions, +} from 'viem'; +import { + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + Slippage, + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, + InputAmount, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; +import { ExitTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; + +const chainId = ChainId.POLYGON; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +const poolId = + '0xdac42eeb17758daa38caf9a3540c808247527ae3000200000000000000000a2b'; // 2CLP-USDC-DAI + +describe('Gyro2 exit test', () => { + let txInput: ExitTxInput; + let poolInput: PoolStateInput; + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput: poolInput, + testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens + exitInput: {} as ExitInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [txInput.poolStateInput.address], + [0], + [parseUnits('1000', 18)], + ); + }); + + describe('proportional exit', () => { + let input: ProportionalExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('0.01'), + decimals: 18, + address: poolInput.address, + }; + input = { + bptIn, + chainId, + rpcUrl, + kind: ExitKind.Proportional, + }; + }); + test('with tokens', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, + }); + + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); + //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network + }); + }); + + describe('unbalanced exit', async () => { + let input: Omit; + let amountsOut: InputAmount[]; + beforeAll(() => { + amountsOut = poolInput.tokens.map((t) => ({ + rawAmount: parseUnits('0.001', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: ExitKind.Unbalanced, + }; + }); + test('must throw error, exit kind not supported', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + }; + await expect(() => + doExit({ ...txInput, exitInput }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); + + describe('single asset exit', () => { + let input: SingleAssetExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; + const tokenOut = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH + input = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; + }); + test('must throw error, exit kind not supported', async () => { + await expect(() => + doExit({ ...txInput, exitInput: input }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYRO2', + tokens: [ + { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC(PoS) + decimals: 6, + index: 0, + }, + { + address: '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', // DAI + decimals: 18, + index: 1, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyro2Join.integration.test.ts b/test/gyro2Join.integration.test.ts new file mode 100644 index 00000000..ca9a5319 --- /dev/null +++ b/test/gyro2Join.integration.test.ts @@ -0,0 +1,204 @@ +//0xdac42eeb17758daa38caf9a3540c808247527ae3000200000000000000000a2b - 2CLP-USDC-DAI +// pnpm test -- weightedJoin.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseUnits, + publicActions, + walletActions, + parseEther, + Address, +} from 'viem'; + +import { + ProportionalJoinInput, + JoinKind, + Slippage, + Hex, + PoolStateInput, + CHAINS, + ChainId, + PoolJoin, + JoinInput, + InputAmount, + getPoolAddress, + UnbalancedJoinInput, + SingleAssetJoinInput, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; +import { JoinTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; + +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +const chainId = ChainId.POLYGON; +const poolId = + '0xdac42eeb17758daa38caf9a3540c808247527ae3000200000000000000000a2b'; // 2CLP-USDC-DAI + +describe('Gyro2 join test', () => { + let txInput: JoinTxInput; + let poolStateInput: PoolStateInput; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolStateInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput, + testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens + joinInput: {} as JoinInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [ + ...txInput.poolStateInput.tokens.map((t) => t.address), + txInput.poolStateInput.address, + ], + [0, 0, 0], + [ + ...txInput.poolStateInput.tokens.map((t) => { + return parseUnits('1', t.decimals); + }), + parseUnits('1', 18), + ], + ); + }); + + describe('proportional join', () => { + let joinInput: ProportionalJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + joinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + }); + test('with tokens', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + + assertProportionalJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); + //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network + }); + + describe('unbalanced join', () => { + let input: Omit; + let amountsIn: InputAmount[]; + beforeAll(() => { + amountsIn = txInput.poolStateInput.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + }); + test('must throw unsupported single asset join error', async () => { + const joinInput = { + ...input, + amountsIn: [...amountsIn.splice(0, 1)], + }; + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); + + describe('single asset join', () => { + let joinInput: SingleAssetJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + joinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + }); + + test('must throw unsupported single asset join error', async () => { + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYRO2', + tokens: [ + { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC(PoS) + decimals: 6, + index: 0, + }, + { + address: '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', // DAI + decimals: 18, + index: 1, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyro3Exit.integration.test.ts b/test/gyro3Exit.integration.test.ts new file mode 100644 index 00000000..8ed4efe9 --- /dev/null +++ b/test/gyro3Exit.integration.test.ts @@ -0,0 +1,191 @@ +// pnpm test -- weightedExit.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseEther, + parseUnits, + publicActions, + walletActions, +} from 'viem'; +import { + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + Slippage, + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, + InputAmount, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; +import { ExitTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; + +const chainId = ChainId.POLYGON; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +const poolId = + '0x17f1ef81707811ea15d9ee7c741179bbe2a63887000100000000000000000799'; // 3CLP-BUSD-USDC-USDT + +describe('Gyro3 exit test', () => { + let txInput: ExitTxInput; + let poolInput: PoolStateInput; + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput: poolInput, + testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens + exitInput: {} as ExitInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [txInput.poolStateInput.address], + [0], + [parseUnits('1000', 18)], + ); + }); + + describe('proportional exit', () => { + let input: ProportionalExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('0.01'), + decimals: 18, + address: poolInput.address, + }; + input = { + bptIn, + chainId, + rpcUrl, + kind: ExitKind.Proportional, + }; + }); + test('with tokens', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, + }); + + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); + //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network + }); + }); + + describe('unbalanced exit', async () => { + let input: Omit; + let amountsOut: InputAmount[]; + beforeAll(() => { + amountsOut = poolInput.tokens.map((t) => ({ + rawAmount: parseUnits('0.001', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: ExitKind.Unbalanced, + }; + }); + test('must throw error, exit kind not supported', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + }; + await expect(() => + doExit({ ...txInput, exitInput }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); + + describe('single asset exit', () => { + let input: SingleAssetExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; + const tokenOut = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH + input = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; + }); + test('must throw error, exit kind not supported', async () => { + await expect(() => + doExit({ ...txInput, exitInput: input }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYRO2', + tokens: [ + { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC(PoS) + decimals: 6, + index: 0, + }, + { + address: '0x9c9e5fd8bbc25984b178fdce6117defa39d2db39', // BUSD + decimals: 18, + index: 1, + }, + { + address: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', // USDT(PoS) + decimals: 6, + index: 2, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyro3Join.integration.test.ts b/test/gyro3Join.integration.test.ts new file mode 100644 index 00000000..0998170a --- /dev/null +++ b/test/gyro3Join.integration.test.ts @@ -0,0 +1,208 @@ +//0x17f1ef81707811ea15d9ee7c741179bbe2a63887000100000000000000000799 - 3CLP-BUSD-USDC-USDT +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseUnits, + publicActions, + walletActions, + parseEther, + Address, +} from 'viem'; + +import { + ProportionalJoinInput, + JoinKind, + Slippage, + Hex, + PoolStateInput, + CHAINS, + ChainId, + PoolJoin, + JoinInput, + InputAmount, + getPoolAddress, + UnbalancedJoinInput, + SingleAssetJoinInput, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; +import { JoinTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; + +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +const chainId = ChainId.POLYGON; +const poolId = + '0x17f1ef81707811ea15d9ee7c741179bbe2a63887000100000000000000000799'; // 3CLP-BUSD-USDC-USDT + +describe('Gyro3 join test', () => { + let txInput: JoinTxInput; + let poolStateInput: PoolStateInput; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolStateInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput, + testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens + joinInput: {} as JoinInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [ + ...txInput.poolStateInput.tokens.map((t) => t.address), + txInput.poolStateInput.address, + ], + [0,51,0,0], + [ + ...txInput.poolStateInput.tokens.map((t) => + parseUnits('10000', t.decimals), + ), + parseUnits('10000', 18), + ], + ); + }); + + describe('proportional join', () => { + let joinInput: ProportionalJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + joinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + }); + test('with tokens', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + + assertProportionalJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); + //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network + }); + + describe('unbalanced join', () => { + let input: Omit; + let amountsIn: InputAmount[]; + beforeAll(() => { + amountsIn = txInput.poolStateInput.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + }); + test('must throw unsupported single asset join error', async () => { + const joinInput = { + ...input, + amountsIn: [...amountsIn.splice(0, 1)], + }; + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); + + describe('single asset join', () => { + let joinInput: SingleAssetJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + joinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + }); + + test('must throw unsupported single asset join error', async () => { + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYRO2', + tokens: [ + { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC(PoS) + decimals: 6, + index: 0, + }, + { + address: '0x9c9e5fd8bbc25984b178fdce6117defa39d2db39', // BUSD + decimals: 18, + index: 1, + }, + { + address: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', // USDT(PoS) + decimals: 6, + index: 2, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyroEExit.integration.test.ts b/test/gyroEExit.integration.test.ts new file mode 100644 index 00000000..0b48a977 --- /dev/null +++ b/test/gyroEExit.integration.test.ts @@ -0,0 +1,186 @@ +// pnpm test -- weightedExit.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseEther, + parseUnits, + publicActions, + walletActions, +} from 'viem'; +import { + SingleAssetExitInput, + ProportionalExitInput, + UnbalancedExitInput, + ExitKind, + Slippage, + PoolStateInput, + PoolExit, + Address, + Hex, + CHAINS, + ChainId, + getPoolAddress, + ExitInput, + InputAmount, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; +import { ExitTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; + +const chainId = ChainId.POLYGON; +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +const poolId = + '0x97469e6236bd467cd147065f77752b00efadce8a0002000000000000000008c0'; // ECLP-TUSD-USDC + +describe('GyroE V1 exit test', () => { + let txInput: ExitTxInput; + let poolInput: PoolStateInput; + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolExit: new PoolExit(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput: poolInput, + testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens + exitInput: {} as ExitInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [txInput.poolStateInput.address], + [0], + [parseUnits('1000', 18)], + ); + }); + + describe('proportional exit', () => { + let input: ProportionalExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; + input = { + bptIn, + chainId, + rpcUrl, + kind: ExitKind.Proportional, + }; + }); + test('with tokens', async () => { + const exitResult = await doExit({ + ...txInput, + exitInput: input, + }); + + assertProportionalExit( + txInput.client.chain?.id as number, + txInput.poolStateInput, + input, + exitResult, + txInput.slippage, + ); + }); + //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network + }); + + describe('unbalanced exit', async () => { + let input: Omit; + let amountsOut: InputAmount[]; + beforeAll(() => { + amountsOut = poolInput.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: ExitKind.Unbalanced, + }; + }); + test('must throw error, exit kind not supported', async () => { + const exitInput = { + ...input, + amountsOut: amountsOut.slice(0, 1), + }; + await expect(() => + doExit({ ...txInput, exitInput }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); + + describe('single asset exit', () => { + let input: SingleAssetExitInput; + beforeAll(() => { + const bptIn: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolInput.address, + }; + const tokenOut = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; // WETH + input = { + chainId, + rpcUrl, + bptIn, + tokenOut, + kind: ExitKind.SingleAsset, + }; + }); + test('must throw error, exit kind not supported', async () => { + await expect(() => + doExit({ ...txInput, exitInput: input }), + ).rejects.toThrowError(gyroExitKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYROE', + tokens: [ + { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC + decimals: 6, + index: 0, + }, + { + address: '0x2e1ad108ff1d8c782fcbbb89aad783ac49586756', // TUSD + decimals: 18, + index: 1, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyroEJoin.integration.test.ts b/test/gyroEJoin.integration.test.ts new file mode 100644 index 00000000..c82f74dd --- /dev/null +++ b/test/gyroEJoin.integration.test.ts @@ -0,0 +1,203 @@ +// pnpm test -- weightedJoin.integration.test.ts +import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; +import dotenv from 'dotenv'; +dotenv.config(); + +import { + createTestClient, + http, + parseUnits, + publicActions, + walletActions, + parseEther, + Address, +} from 'viem'; + +import { + ProportionalJoinInput, + JoinKind, + Slippage, + Hex, + PoolStateInput, + CHAINS, + ChainId, + PoolJoin, + JoinInput, + InputAmount, + getPoolAddress, + UnbalancedJoinInput, + SingleAssetJoinInput, +} from '../src'; +import { forkSetup } from './lib/utils/helper'; +import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; +import { JoinTxInput } from './lib/utils/types'; +import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; +import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; + +const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); +const chainId = ChainId.POLYGON; +const poolId = + '0xa489c057de6c3177380ea264ebdf686b7f564f510002000000000000000008e2'; // ECLP-wstETH-wETH + +describe('gyroE V2 join test', () => { + let txInput: JoinTxInput; + let poolStateInput: PoolStateInput; + + beforeAll(async () => { + // setup mock api + const api = new MockApi(); + + // get pool state from api + poolStateInput = await api.getPool(poolId); + + const client = createTestClient({ + mode: 'anvil', + chain: CHAINS[chainId], + transport: http(rpcUrl), + }) + .extend(publicActions) + .extend(walletActions); + + txInput = { + client, + poolJoin: new PoolJoin(), + slippage: Slippage.fromPercentage('1'), // 1% + poolStateInput, + testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens + joinInput: {} as JoinInput, + }; + }); + + beforeEach(async () => { + await forkSetup( + txInput.client, + txInput.testAddress, + [ + ...txInput.poolStateInput.tokens.map((t) => t.address), + txInput.poolStateInput.address, + ], + undefined, + [ + ...txInput.poolStateInput.tokens.map((t) => + parseUnits('10000', t.decimals), + ), + parseUnits('10000', 18), + ], + ); + }); + + describe('proportional join', () => { + let joinInput: ProportionalJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + joinInput = { + bptOut, + chainId, + rpcUrl, + kind: JoinKind.Proportional, + }; + }); + test('with tokens', async () => { + const joinResult = await doJoin({ + ...txInput, + joinInput, + }); + + assertProportionalJoin( + txInput.client.chain?.id as number, + txInput.poolStateInput, + joinInput, + joinResult, + txInput.slippage, + ); + }); + }); + + describe('unbalanced join', () => { + let input: Omit; + let amountsIn: InputAmount[]; + beforeAll(() => { + amountsIn = txInput.poolStateInput.tokens.map((t) => ({ + rawAmount: parseUnits('1', t.decimals), + decimals: t.decimals, + address: t.address, + })); + input = { + chainId, + rpcUrl, + kind: JoinKind.Unbalanced, + }; + }); + test('with tokens', async () => { + const joinInput = { + ...input, + amountsIn: [...amountsIn.splice(0, 1)], + }; + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network + }); + + describe('single asset join', () => { + let joinInput: SingleAssetJoinInput; + beforeAll(() => { + const bptOut: InputAmount = { + rawAmount: parseEther('1'), + decimals: 18, + address: poolStateInput.address, + }; + const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; + joinInput = { + bptOut, + tokenIn, + chainId, + rpcUrl, + kind: JoinKind.SingleAsset, + }; + }); + + test('must throw unsupported single asset join error', async () => { + await expect(() => + doJoin({ + ...txInput, + joinInput, + }), + ).rejects.toThrowError(gyroJoinKindNotSupported); + }); + }); +}); + +/*********************** Mock To Represent API Requirements **********************/ + +export class MockApi { + public async getPool(id: Hex): Promise { + return { + id, + address: getPoolAddress(id) as Address, + type: 'GYROE', + tokens: [ + { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC + decimals: 6, + index: 0, + }, + { + address: '0x2e1ad108ff1d8c782fcbbb89aad783ac49586756', // TUSD + decimals: 18, + index: 1, + }, + ], + }; + } +} + +/******************************************************************************/ diff --git a/test/gyroEV2Exit.integration.test.ts b/test/gyroEV2Exit.integration.test.ts index 2dc41dd5..9f1f194b 100644 --- a/test/gyroEV2Exit.integration.test.ts +++ b/test/gyroEV2Exit.integration.test.ts @@ -38,7 +38,7 @@ const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const poolId = '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // ECLP-wstETH-wETH -describe('weighted exit test', () => { +describe('GyroE V2 exit test', () => { let txInput: ExitTxInput; let poolInput: PoolStateInput; beforeAll(async () => { diff --git a/test/gyroEV2Join.integration.test.ts b/test/gyroEV2Join.integration.test.ts index f90a8257..2cc03115 100644 --- a/test/gyroEV2Join.integration.test.ts +++ b/test/gyroEV2Join.integration.test.ts @@ -39,7 +39,7 @@ const chainId = ChainId.MAINNET; const poolId = '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // ECLP-wstETH-wETH -describe('gyro join test', () => { +describe('GyroE V2 join test', () => { let txInput: JoinTxInput; let poolStateInput: PoolStateInput; @@ -123,7 +123,6 @@ describe('gyro join test', () => { useNativeAssetAsWrappedAmountIn: true, }, }); - console.log(joinResult.joinBuildOutput.value); assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, From 8663fa1da442fbb1976ca2db76dc8c8bc82fc606 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Fri, 10 Nov 2023 09:56:45 +0000 Subject: [PATCH 170/199] fix: Update exits to use TokenAmounts. --- examples/exitPool.ts | 7 +++++-- .../composable-stable/composableStableExit.ts | 9 +++++++-- src/entities/exit/types.ts | 4 ++-- src/entities/exit/weighted/weightedExit.ts | 9 +++++++-- test/lib/utils/exitHelper.ts | 16 ++++++++++------ 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/examples/exitPool.ts b/examples/exitPool.ts index 9e4744cd..6fb8edd0 100644 --- a/examples/exitPool.ts +++ b/examples/exitPool.ts @@ -75,8 +75,11 @@ const exit = async () => { }); console.log('\nWith slippage applied:'); - console.log(`Max BPT In: ${call.maxBptIn.toString()}`); // TODO these should be InputAmounts or TokenAmounts? - console.log(`Min amounts out: ${call.minAmountsOut}`); // TODO these should be InputAmounts or TokenAmounts? + console.log(`Max BPT In: ${call.maxBptIn.amount}`); + console.log('Min amounts out: '); + call.minAmountsOut.forEach((a) => + console.log(a.token.address, a.amount.toString()), + ); // Make the tx against the local fork and print the result await makeForkTx( diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index d85a15e3..179d928e 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -135,8 +135,13 @@ export class ComposableStableExit implements BaseExit { call, to: BALANCER_VAULT, value: 0n, - maxBptIn: amounts.maxBptAmountIn, - minAmountsOut: amounts.minAmountsOut, + maxBptIn: TokenAmount.fromRawAmount( + input.bptIn.token, + amounts.maxBptAmountIn, + ), + minAmountsOut: input.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(a.token, amounts.minAmountsOut[i]), + ), }; } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 4e7ad17e..add9abdc 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -72,8 +72,8 @@ export type ExitBuildOutput = { call: Address; to: Address; value: bigint; - maxBptIn: bigint; - minAmountsOut: bigint[]; + maxBptIn: TokenAmount; + minAmountsOut: TokenAmount[]; }; export interface BaseExit { diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 46c56e43..cb793162 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -119,8 +119,13 @@ export class WeightedExit implements BaseExit { call, to: BALANCER_VAULT, value: 0n, - maxBptIn: amounts.maxBptAmountIn, - minAmountsOut: amounts.minAmountsOut, + maxBptIn: TokenAmount.fromRawAmount( + input.bptIn.token, + amounts.maxBptAmountIn, + ), + minAmountsOut: input.amountsOut.map((a, i) => + TokenAmount.fromRawAmount(a.token, amounts.minAmountsOut[i]), + ), }; } diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 5dd72cec..fbb2c725 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -50,8 +50,6 @@ export const sdkExit = async ({ }; }; - - function isComposableStableExitQueryResult(result: ExitQueryResult): boolean { return (result as ComposableStableExitQueryResult).bptIndex !== undefined; } @@ -316,14 +314,20 @@ function assertExitBuildOutput( slippage: Slippage, ) { // if exactIn minAmountsOut should use amountsOut with slippage applied, else should use same amountsOut as input + // slippage.removeFrom(a.amount) const minAmountsOut = isExactIn - ? exitQueryResult.amountsOut.map((a) => slippage.removeFrom(a.amount)) - : exitQueryResult.amountsOut.map((a) => a.amount); + ? exitQueryResult.amountsOut.map((a) => + TokenAmount.fromRawAmount(a.token, slippage.removeFrom(a.amount)), + ) + : [...exitQueryResult.amountsOut]; // if exactIn slippage cannot be applied to bptIn, else should use bptIn with slippage applied const maxBptIn = isExactIn - ? exitQueryResult.bptIn.amount - : slippage.applyTo(exitQueryResult.bptIn.amount); + ? ({ ...exitQueryResult.bptIn } as TokenAmount) + : TokenAmount.fromRawAmount( + exitQueryResult.bptIn.token, + slippage.applyTo(exitQueryResult.bptIn.amount), + ); const expectedBuildOutput: Omit = { minAmountsOut, From 2a9d1dfe45a92b67505e090442f880837416acaf Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Fri, 10 Nov 2023 10:05:43 +0000 Subject: [PATCH 171/199] fix: Update joins to use TokenAmounts. --- examples/joinPool.ts | 7 +++++-- .../join/composable-stable/composableStableJoin.ts | 9 +++++++-- src/entities/join/types.ts | 8 ++++---- src/entities/join/weighted/weightedJoin.ts | 9 +++++++-- test/lib/utils/joinHelper.ts | 13 +++++++++---- 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/examples/joinPool.ts b/examples/joinPool.ts index 390d1e6d..211eddd6 100644 --- a/examples/joinPool.ts +++ b/examples/joinPool.ts @@ -73,8 +73,11 @@ const join = async () => { }); console.log('\nWith slippage applied:'); - console.log(`Max tokens in: ${call.maxAmountsIn}`); // TODO these should be InputAmounts or TokenAmounts? - console.log(`Min BPT Out: ${call.minBptOut.toString()}`); // TODO these should be InputAmounts or TokenAmounts? + console.log('Max tokens in:'); + call.maxAmountsIn.forEach((a) => + console.log(a.token.address, a.amount.toString()), + ); + console.log(`Min BPT Out: ${call.minBptOut.amount.toString()}`); // Make the tx against the local fork and print the result const slots = [1, 3, 0]; diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 88e6d731..d686b86d 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -97,8 +97,13 @@ export class ComposableStableJoin implements BaseJoin { call, to: BALANCER_VAULT, value: value === undefined ? 0n : value, - minBptOut: amounts.minimumBpt, - maxAmountsIn: amounts.maxAmountsIn, + minBptOut: TokenAmount.fromRawAmount( + input.bptOut.token, + amounts.minimumBpt, + ), + maxAmountsIn: input.amountsIn.map((a, i) => + TokenAmount.fromRawAmount(a.token, amounts.maxAmountsIn[i]), + ), }; } diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 204db979..54a6772b 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -82,8 +82,8 @@ export interface BaseJoin { call: Hex; to: Address; value: bigint; - minBptOut: bigint; - maxAmountsIn: bigint[]; + minBptOut: TokenAmount; + maxAmountsIn: TokenAmount[]; }; } @@ -91,8 +91,8 @@ export type JoinBuildOutput = { call: Hex; to: Address; value: bigint; - minBptOut: bigint; - maxAmountsIn: bigint[]; + minBptOut: TokenAmount; + maxAmountsIn: TokenAmount[]; }; export type JoinConfig = { diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 5a434932..a4cb36d0 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -89,8 +89,13 @@ export class WeightedJoin implements BaseJoin { call, to: BALANCER_VAULT, value: value === undefined ? 0n : value, - minBptOut: amounts.minimumBpt, - maxAmountsIn: amounts.maxAmountsIn, + minBptOut: TokenAmount.fromRawAmount( + input.bptOut.token, + amounts.minimumBpt, + ), + maxAmountsIn: input.amountsIn.map((a, i) => + TokenAmount.fromRawAmount(a.token, amounts.maxAmountsIn[i]), + ), }; } diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index ea778d69..a8dc32c0 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -362,13 +362,18 @@ function assertJoinBuildOutput( ) { // if exactIn maxAmountsIn should use same amountsIn as input else slippage should be applied const maxAmountsIn = isExactIn - ? joinQueryResult.amountsIn.map((a) => a.amount) - : joinQueryResult.amountsIn.map((a) => slippage.applyTo(a.amount)); + ? [...joinQueryResult.amountsIn] + : joinQueryResult.amountsIn.map((a) => + TokenAmount.fromRawAmount(a.token, slippage.applyTo(a.amount)), + ); // if exactIn slippage should be applied to bptOut else should use same bptOut as input const minBptOut = isExactIn - ? slippage.removeFrom(joinQueryResult.bptOut.amount) - : joinQueryResult.bptOut.amount; + ? TokenAmount.fromRawAmount( + joinQueryResult.bptOut.token, + slippage.removeFrom(joinQueryResult.bptOut.amount), + ) + : ({ ...joinQueryResult.bptOut } as TokenAmount); const expectedBuildOutput: Omit = { maxAmountsIn, From 483220db37e693fd757cf5d50c97992f59940832 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 10:23:09 -0300 Subject: [PATCH 172/199] Refactor AmountsJoin into AddLiquidityAmounts --- .../composable-stable/composableStableJoin.ts | 42 +++++++++++-------- src/entities/join/weighted/weightedJoin.ts | 11 +++-- src/entities/types.ts | 2 +- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 88e6d731..21e862be 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -12,11 +12,14 @@ import { ComposableStableJoinQueryResult, ComposableJoinCall, } from '../types'; -import { AmountsJoin as AmountsJoinBase, PoolState } from '../../types'; +import { + AddLiquidityAmounts as AddLiquidityAmountsBase, + PoolState, +} from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; import { ComposableStableEncoder } from '../../encoders/composableStable'; -type AmountsJoin = AmountsJoinBase & { +type AddLiquidityAmounts = AddLiquidityAmountsBase & { maxAmountsInNoBpt: bigint[]; }; @@ -106,12 +109,12 @@ export class ComposableStableJoin implements BaseJoin { poolTokens: Token[], input: JoinInput, bptIndex: number, - ): AmountsJoin { - let amountsJoin: AmountsJoinBase; + ): AddLiquidityAmounts { + let addLiquidityAmounts: AddLiquidityAmountsBase; switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: { - amountsJoin = { + addLiquidityAmounts = { minimumBpt: 0n, maxAmountsIn: getAmounts( poolTokens, @@ -130,7 +133,7 @@ export class ComposableStableJoin implements BaseJoin { throw Error("Can't find index of SingleAsset"); const maxAmountsIn = Array(poolTokens.length).fill(0n); maxAmountsIn[tokenInIndex] = MAX_UINT256; - amountsJoin = { + addLiquidityAmounts = { minimumBpt: input.bptOut.rawAmount, maxAmountsIn, tokenInIndex, @@ -138,7 +141,7 @@ export class ComposableStableJoin implements BaseJoin { break; } case JoinKind.Proportional: { - amountsJoin = { + addLiquidityAmounts = { minimumBpt: input.bptOut.rawAmount, maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), tokenInIndex: undefined, @@ -150,23 +153,23 @@ export class ComposableStableJoin implements BaseJoin { } return { - ...amountsJoin, + ...addLiquidityAmounts, maxAmountsInNoBpt: [ - ...amountsJoin.maxAmountsIn.slice(0, bptIndex), - ...amountsJoin.maxAmountsIn.slice(bptIndex + 1), + ...addLiquidityAmounts.maxAmountsIn.slice(0, bptIndex), + ...addLiquidityAmounts.maxAmountsIn.slice(bptIndex + 1), ], }; } - private getAmountsCall(input: ComposableJoinCall): AmountsJoin { - let amountsJoin: AmountsJoinBase; + private getAmountsCall(input: ComposableJoinCall): AddLiquidityAmounts { + let addLiquidityAmounts: AddLiquidityAmountsBase; switch (input.joinKind) { case JoinKind.Init: case JoinKind.Unbalanced: { const minimumBpt = input.slippage.removeFrom( input.bptOut.amount, ); - amountsJoin = { + addLiquidityAmounts = { minimumBpt, maxAmountsIn: input.amountsIn.map((a) => a.amount), tokenInIndex: input.tokenInIndex, @@ -175,7 +178,7 @@ export class ComposableStableJoin implements BaseJoin { } case JoinKind.SingleAsset: case JoinKind.Proportional: { - amountsJoin = { + addLiquidityAmounts = { minimumBpt: input.bptOut.amount, maxAmountsIn: input.amountsIn.map((a) => input.slippage.applyTo(a.amount), @@ -188,15 +191,18 @@ export class ComposableStableJoin implements BaseJoin { throw Error('Unsupported Join Type'); } return { - ...amountsJoin, + ...addLiquidityAmounts, maxAmountsInNoBpt: [ - ...amountsJoin.maxAmountsIn.slice(0, input.bptIndex), - ...amountsJoin.maxAmountsIn.slice(input.bptIndex + 1), + ...addLiquidityAmounts.maxAmountsIn.slice(0, input.bptIndex), + ...addLiquidityAmounts.maxAmountsIn.slice(input.bptIndex + 1), ], }; } - private encodeUserData(kind: JoinKind, amounts: AmountsJoin): Address { + private encodeUserData( + kind: JoinKind, + amounts: AddLiquidityAmounts, + ): Address { switch (kind) { case JoinKind.Init: return ComposableStableEncoder.joinInit( diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 5a434932..01ba4001 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -13,7 +13,7 @@ import { WeightedJoinQueryResult, WeightedJoinCall, } from '../types'; -import { AmountsJoin, PoolState } from '../../types'; +import { AddLiquidityAmounts, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; export class WeightedJoin implements BaseJoin { @@ -97,7 +97,7 @@ export class WeightedJoin implements BaseJoin { private getAmountsQuery( poolTokens: Token[], input: JoinInput, - ): AmountsJoin { + ): AddLiquidityAmounts { switch (input.kind) { case JoinKind.Init: case JoinKind.Unbalanced: { @@ -133,7 +133,7 @@ export class WeightedJoin implements BaseJoin { } } - private getAmountsCall(input: WeightedJoinCall): AmountsJoin { + private getAmountsCall(input: WeightedJoinCall): AddLiquidityAmounts { switch (input.joinKind) { case JoinKind.Init: case JoinKind.Unbalanced: { @@ -161,7 +161,10 @@ export class WeightedJoin implements BaseJoin { } } - private encodeUserData(kind: JoinKind, amounts: AmountsJoin): Address { + private encodeUserData( + kind: JoinKind, + amounts: AddLiquidityAmounts, + ): Address { switch (kind) { case JoinKind.Init: return WeightedEncoder.joinInit(amounts.maxAmountsIn); diff --git a/src/entities/types.ts b/src/entities/types.ts index 4be403fe..57dd8a13 100644 --- a/src/entities/types.ts +++ b/src/entities/types.ts @@ -17,7 +17,7 @@ export type PoolStateInput = { tokens: MinimalToken[]; }; -export type AmountsJoin = { +export type AddLiquidityAmounts = { maxAmountsIn: bigint[]; tokenInIndex: number | undefined; minimumBpt: bigint; From b06078c7b0b341d7b20177613fddcdcbe0a5cce9 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 10:40:07 -0300 Subject: [PATCH 173/199] Refactor JoinKind into AddLiquidityKind --- README.md | 2 +- examples/joinPool.ts | 4 +-- src/entities/encoders/composableStable.ts | 1 + .../composable-stable/composableStableJoin.ts | 34 +++++++++---------- src/entities/join/types.ts | 12 +++---- src/entities/join/utils/validateInputs.ts | 10 +++--- src/entities/join/weighted/weightedJoin.ts | 34 +++++++++---------- test/composableStableJoin.integration.test.ts | 8 ++--- test/lib/utils/joinHelper.ts | 6 ++-- test/weightedJoin.integration.test.ts | 8 ++--- 10 files changed, 60 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index d3f781dd..ce806243 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ it can be used for: bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); diff --git a/examples/joinPool.ts b/examples/joinPool.ts index 390d1e6d..45adc522 100644 --- a/examples/joinPool.ts +++ b/examples/joinPool.ts @@ -12,7 +12,7 @@ import { BalancerApi, ChainId, JoinInput, - JoinKind, + AddLiquidityKind, PoolJoin, Slippage, } from '../src'; @@ -50,7 +50,7 @@ const join = async () => { amountsIn, chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; // Simulate the join to get the amount of BPT out diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index 1d4e2ebb..dd9113c2 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -1,6 +1,7 @@ import { encodeAbiParameters } from 'viem'; import { Address } from '../../types'; +// TODO: check if we should update join interface here as well - asking because it's matching v2 SC exactly export enum ComposableStablePoolJoinKind { INIT = 0, EXACT_TOKENS_IN_FOR_BPT_OUT = 1, diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 21e862be..7e9e180b 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -8,7 +8,7 @@ import { BaseJoin, JoinBuildOutput, JoinInput, - JoinKind, + AddLiquidityKind, ComposableStableJoinQueryResult, ComposableJoinCall, } from '../types'; @@ -63,7 +63,7 @@ export class ComposableStableJoin implements BaseJoin { return { poolType: poolState.type, - joinKind: input.kind, + addLiquidityKind: input.kind, poolId: poolState.id, bptOut, amountsIn, @@ -76,7 +76,7 @@ export class ComposableStableJoin implements BaseJoin { public buildCall(input: ComposableJoinCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); - const userData = this.encodeUserData(input.joinKind, amounts); + const userData = this.encodeUserData(input.addLiquidityKind, amounts); const { args } = parseJoinArgs({ ...input, @@ -112,8 +112,8 @@ export class ComposableStableJoin implements BaseJoin { ): AddLiquidityAmounts { let addLiquidityAmounts: AddLiquidityAmountsBase; switch (input.kind) { - case JoinKind.Init: - case JoinKind.Unbalanced: { + case AddLiquidityKind.Init: + case AddLiquidityKind.Unbalanced: { addLiquidityAmounts = { minimumBpt: 0n, maxAmountsIn: getAmounts( @@ -125,7 +125,7 @@ export class ComposableStableJoin implements BaseJoin { }; break; } - case JoinKind.SingleAsset: { + case AddLiquidityKind.SingleAsset: { const tokenInIndex = poolTokens .filter((_, index) => index !== bptIndex) // Need to remove Bpt .findIndex((t) => t.isSameAddress(input.tokenIn)); @@ -140,7 +140,7 @@ export class ComposableStableJoin implements BaseJoin { }; break; } - case JoinKind.Proportional: { + case AddLiquidityKind.Proportional: { addLiquidityAmounts = { minimumBpt: input.bptOut.rawAmount, maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), @@ -163,9 +163,9 @@ export class ComposableStableJoin implements BaseJoin { private getAmountsCall(input: ComposableJoinCall): AddLiquidityAmounts { let addLiquidityAmounts: AddLiquidityAmountsBase; - switch (input.joinKind) { - case JoinKind.Init: - case JoinKind.Unbalanced: { + switch (input.addLiquidityKind) { + case AddLiquidityKind.Init: + case AddLiquidityKind.Unbalanced: { const minimumBpt = input.slippage.removeFrom( input.bptOut.amount, ); @@ -176,8 +176,8 @@ export class ComposableStableJoin implements BaseJoin { }; break; } - case JoinKind.SingleAsset: - case JoinKind.Proportional: { + case AddLiquidityKind.SingleAsset: + case AddLiquidityKind.Proportional: { addLiquidityAmounts = { minimumBpt: input.bptOut.amount, maxAmountsIn: input.amountsIn.map((a) => @@ -200,27 +200,27 @@ export class ComposableStableJoin implements BaseJoin { } private encodeUserData( - kind: JoinKind, + kind: AddLiquidityKind, amounts: AddLiquidityAmounts, ): Address { switch (kind) { - case JoinKind.Init: + case AddLiquidityKind.Init: return ComposableStableEncoder.joinInit( amounts.maxAmountsInNoBpt, ); - case JoinKind.Unbalanced: + case AddLiquidityKind.Unbalanced: return ComposableStableEncoder.joinUnbalanced( amounts.maxAmountsInNoBpt, amounts.minimumBpt, ); - case JoinKind.SingleAsset: { + case AddLiquidityKind.SingleAsset: { if (amounts.tokenInIndex === undefined) throw Error('No Index'); return ComposableStableEncoder.joinSingleAsset( amounts.minimumBpt, amounts.tokenInIndex, // Has to be index without BPT ); } - case JoinKind.Proportional: { + case AddLiquidityKind.Proportional: { return ComposableStableEncoder.joinProportional( amounts.minimumBpt, ); diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 204db979..24a4d4ef 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -3,7 +3,7 @@ import { Slippage } from '../slippage'; import { PoolState } from '../types'; import { Address, Hex, InputAmount } from '../../types'; -export enum JoinKind { +export enum AddLiquidityKind { Init = 'Init', Unbalanced = 'Unbalanced', SingleAsset = 'SingleAsset', @@ -20,23 +20,23 @@ type BaseJoinInput = { export type InitJoinInput = BaseJoinInput & { amountsIn: InputAmount[]; - kind: JoinKind.Init; + kind: AddLiquidityKind.Init; }; export type UnbalancedJoinInput = BaseJoinInput & { amountsIn: InputAmount[]; - kind: JoinKind.Unbalanced; + kind: AddLiquidityKind.Unbalanced; }; export type SingleAssetJoinInput = BaseJoinInput & { bptOut: InputAmount; tokenIn: Address; - kind: JoinKind.SingleAsset; + kind: AddLiquidityKind.SingleAsset; }; export type ProportionalJoinInput = BaseJoinInput & { bptOut: InputAmount; - kind: JoinKind.Proportional; + kind: AddLiquidityKind.Proportional; }; export type JoinInput = @@ -48,7 +48,7 @@ export type JoinInput = type BaseJoinQueryResult = { poolType: string; poolId: Hex; - joinKind: JoinKind; + addLiquidityKind: AddLiquidityKind; bptOut: TokenAmount; amountsIn: TokenAmount[]; fromInternalBalance: boolean; diff --git a/src/entities/join/utils/validateInputs.ts b/src/entities/join/utils/validateInputs.ts index 89d577ab..89c2b657 100644 --- a/src/entities/join/utils/validateInputs.ts +++ b/src/entities/join/utils/validateInputs.ts @@ -1,4 +1,4 @@ -import { JoinInput, JoinKind } from '../types'; +import { JoinInput, AddLiquidityKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; @@ -10,19 +10,19 @@ export function validateInputs(input: JoinInput, poolState: PoolStateInput) { throw new Error('Pool Tokens does not contain BPT'); } switch (input.kind) { - case JoinKind.Init: - case JoinKind.Unbalanced: + case AddLiquidityKind.Init: + case AddLiquidityKind.Unbalanced: areTokensInArray( input.amountsIn.map((a) => a.address), poolState.tokens.map((t) => t.address), ); break; - case JoinKind.SingleAsset: + case AddLiquidityKind.SingleAsset: areTokensInArray( [input.tokenIn], poolState.tokens.map((t) => t.address), ); - case JoinKind.Proportional: + case AddLiquidityKind.Proportional: areTokensInArray([input.bptOut.address], [poolState.address]); default: break; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 01ba4001..cc625eb4 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -9,7 +9,7 @@ import { BaseJoin, JoinBuildOutput, JoinInput, - JoinKind, + AddLiquidityKind, WeightedJoinQueryResult, WeightedJoinCall, } from '../types'; @@ -53,7 +53,7 @@ export class WeightedJoin implements BaseJoin { return { poolType: poolState.type, - joinKind: input.kind, + addLiquidityKind: input.kind, poolId: poolState.id, bptOut, amountsIn, @@ -65,7 +65,7 @@ export class WeightedJoin implements BaseJoin { public buildCall(input: WeightedJoinCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); - const userData = this.encodeUserData(input.joinKind, amounts); + const userData = this.encodeUserData(input.addLiquidityKind, amounts); const { args } = parseJoinArgs({ ...input, @@ -99,15 +99,15 @@ export class WeightedJoin implements BaseJoin { input: JoinInput, ): AddLiquidityAmounts { switch (input.kind) { - case JoinKind.Init: - case JoinKind.Unbalanced: { + case AddLiquidityKind.Init: + case AddLiquidityKind.Unbalanced: { return { minimumBpt: 0n, maxAmountsIn: getAmounts(poolTokens, input.amountsIn), tokenInIndex: undefined, }; } - case JoinKind.SingleAsset: { + case AddLiquidityKind.SingleAsset: { const tokenInIndex = poolTokens.findIndex((t) => t.isSameAddress(input.tokenIn), ); @@ -121,7 +121,7 @@ export class WeightedJoin implements BaseJoin { tokenInIndex, }; } - case JoinKind.Proportional: { + case AddLiquidityKind.Proportional: { return { minimumBpt: input.bptOut.rawAmount, maxAmountsIn: Array(poolTokens.length).fill(MAX_UINT256), @@ -134,9 +134,9 @@ export class WeightedJoin implements BaseJoin { } private getAmountsCall(input: WeightedJoinCall): AddLiquidityAmounts { - switch (input.joinKind) { - case JoinKind.Init: - case JoinKind.Unbalanced: { + switch (input.addLiquidityKind) { + case AddLiquidityKind.Init: + case AddLiquidityKind.Unbalanced: { const minimumBpt = input.slippage.removeFrom( input.bptOut.amount, ); @@ -146,8 +146,8 @@ export class WeightedJoin implements BaseJoin { tokenInIndex: input.tokenInIndex, }; } - case JoinKind.SingleAsset: - case JoinKind.Proportional: { + case AddLiquidityKind.SingleAsset: + case AddLiquidityKind.Proportional: { return { minimumBpt: input.bptOut.amount, maxAmountsIn: input.amountsIn.map((a) => @@ -162,25 +162,25 @@ export class WeightedJoin implements BaseJoin { } private encodeUserData( - kind: JoinKind, + kind: AddLiquidityKind, amounts: AddLiquidityAmounts, ): Address { switch (kind) { - case JoinKind.Init: + case AddLiquidityKind.Init: return WeightedEncoder.joinInit(amounts.maxAmountsIn); - case JoinKind.Unbalanced: + case AddLiquidityKind.Unbalanced: return WeightedEncoder.joinUnbalanced( amounts.maxAmountsIn, amounts.minimumBpt, ); - case JoinKind.SingleAsset: { + case AddLiquidityKind.SingleAsset: { if (amounts.tokenInIndex === undefined) throw Error('No Index'); return WeightedEncoder.joinSingleAsset( amounts.minimumBpt, amounts.tokenInIndex, ); } - case JoinKind.Proportional: { + case AddLiquidityKind.Proportional: { return WeightedEncoder.joinProportional(amounts.minimumBpt); } default: diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 562517f7..173270af 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -16,7 +16,7 @@ import { UnbalancedJoinInput, ProportionalJoinInput, SingleAssetJoinInput, - JoinKind, + AddLiquidityKind, Slippage, Address, Hex, @@ -103,7 +103,7 @@ describe('composable stable join test', () => { input = { chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; }); test('token inputs', async () => { @@ -158,7 +158,7 @@ describe('composable stable join test', () => { tokenIn, chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: AddLiquidityKind.SingleAsset, }; }); test('with token', async () => { @@ -208,7 +208,7 @@ describe('composable stable join test', () => { bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; }); test('with tokens', async () => { diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index ea778d69..9cbeec27 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -167,7 +167,7 @@ export function assertUnbalancedJoin( poolId: poolStateInput.id, poolType: poolStateInput.type, fromInternalBalance: !!joinInput.fromInternalBalance, - joinKind: joinInput.kind, + addLiquidityKind: joinInput.kind, }; const queryCheck = getCheck(joinQueryResult, true); @@ -225,7 +225,7 @@ export function assertSingleTokenJoin( poolId: poolStateInput.id, poolType: poolStateInput.type, fromInternalBalance: !!joinInput.fromInternalBalance, - joinKind: joinInput.kind, + addLiquidityKind: joinInput.kind, }; const queryCheck = getCheck(joinQueryResult, false); @@ -289,7 +289,7 @@ export function assertProportionalJoin( poolId: poolStateInput.id, poolType: poolStateInput.type, fromInternalBalance: !!joinInput.fromInternalBalance, - joinKind: joinInput.kind, + addLiquidityKind: joinInput.kind, }; const queryCheck = getCheck(joinQueryResult, false); diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 97a6f61b..83da6e13 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -16,7 +16,7 @@ import { UnbalancedJoinInput, ProportionalJoinInput, SingleAssetJoinInput, - JoinKind, + AddLiquidityKind, Slippage, Address, Hex, @@ -102,7 +102,7 @@ describe('weighted join test', () => { input = { chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; }); test('with tokens', async () => { @@ -158,7 +158,7 @@ describe('weighted join test', () => { tokenIn, chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: AddLiquidityKind.SingleAsset, }; }); @@ -211,7 +211,7 @@ describe('weighted join test', () => { bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; }); test('with tokens', async () => { From 010ad6dd5ccaea295bfa59baf88b8e3d16814546 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 10:51:44 -0300 Subject: [PATCH 174/199] Refactor JoinInput into AddLiquidityInput --- README.md | 4 +- examples/joinPool.ts | 8 +- .../composable-stable/composableStableJoin.ts | 6 +- src/entities/join/poolJoin.ts | 4 +- src/entities/join/types.ts | 25 +++--- src/entities/join/utils/validateInputs.ts | 7 +- src/entities/join/weighted/weightedJoin.ts | 6 +- test/composableStableJoin.integration.test.ts | 44 +++++------ test/lib/utils/joinHelper.ts | 79 ++++++++++--------- test/lib/utils/types.ts | 4 +- test/weightedJoin.integration.test.ts | 52 ++++++------ 11 files changed, 125 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index ce806243..2ab22c87 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ it can be used for: ```ts import { BalancerApi, PoolJoin } from "@balancer/sdk"; ... - const joinInput: ProportionalJoinInput = { + const addLiquidityInput: AddLiquidityProportionalInput = { bptOut, chainId, rpcUrl, @@ -45,7 +45,7 @@ it can be used for: const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); const poolJoin = new PoolJoin(); - const queryResult = await poolJoin.query(joinInput, poolState); + const queryResult = await poolJoin.query(addLiquidityInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = poolJoin.buildCall({ ...queryResult, diff --git a/examples/joinPool.ts b/examples/joinPool.ts index 45adc522..fb96da6d 100644 --- a/examples/joinPool.ts +++ b/examples/joinPool.ts @@ -11,7 +11,7 @@ config(); import { BalancerApi, ChainId, - JoinInput, + AddLiquidityInput, AddLiquidityKind, PoolJoin, Slippage, @@ -45,8 +45,8 @@ const join = async () => { address: t.address, })); - // Construct the JoinInput, in this case an Unbalanced join - const joinInput: JoinInput = { + // Construct the AddLiquidityInput, in this case an Unbalanced join + const addLiquidityInput: AddLiquidityInput = { amountsIn, chainId, rpcUrl, @@ -55,7 +55,7 @@ const join = async () => { // Simulate the join to get the amount of BPT out const poolJoin = new PoolJoin(); - const queryResult = await poolJoin.query(joinInput, poolStateInput); + const queryResult = await poolJoin.query(addLiquidityInput, poolStateInput); console.log('\nJoin Query Result:'); console.log('Tokens In:'); diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 7e9e180b..8ec2ef63 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -7,7 +7,7 @@ import { vaultAbi } from '../../../abi'; import { BaseJoin, JoinBuildOutput, - JoinInput, + AddLiquidityInput, AddLiquidityKind, ComposableStableJoinQueryResult, ComposableJoinCall, @@ -25,7 +25,7 @@ type AddLiquidityAmounts = AddLiquidityAmountsBase & { export class ComposableStableJoin implements BaseJoin { public async query( - input: JoinInput, + input: AddLiquidityInput, poolState: PoolState, ): Promise { const bptIndex = poolState.tokens.findIndex( @@ -107,7 +107,7 @@ export class ComposableStableJoin implements BaseJoin { private getAmountsQuery( poolTokens: Token[], - input: JoinInput, + input: AddLiquidityInput, bptIndex: number, ): AddLiquidityAmounts { let addLiquidityAmounts: AddLiquidityAmountsBase; diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index d80e6e3a..8f179094 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -2,7 +2,7 @@ import { BaseJoin, JoinBuildOutput, JoinConfig, - JoinInput, + AddLiquidityInput, JoinQueryResult, JoinCall, } from './types'; @@ -35,7 +35,7 @@ export class PoolJoin { } public async query( - input: JoinInput, + input: AddLiquidityInput, poolState: PoolStateInput, ): Promise { validateInputs(input, poolState); diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 24a4d4ef..c7fe3635 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -11,39 +11,39 @@ export enum AddLiquidityKind { } // This will be extended for each pools specific input requirements -type BaseJoinInput = { +type AddLiquidityBaseInput = { chainId: number; rpcUrl: string; useNativeAssetAsWrappedAmountIn?: boolean; fromInternalBalance?: boolean; }; -export type InitJoinInput = BaseJoinInput & { +export type AddLiquidityInitInput = AddLiquidityBaseInput & { amountsIn: InputAmount[]; kind: AddLiquidityKind.Init; }; -export type UnbalancedJoinInput = BaseJoinInput & { +export type AddLiquidityUnbalancedInput = AddLiquidityBaseInput & { amountsIn: InputAmount[]; kind: AddLiquidityKind.Unbalanced; }; -export type SingleAssetJoinInput = BaseJoinInput & { +export type AddLiquiditySingleAssetInput = AddLiquidityBaseInput & { bptOut: InputAmount; tokenIn: Address; kind: AddLiquidityKind.SingleAsset; }; -export type ProportionalJoinInput = BaseJoinInput & { +export type AddLiquidityProportionalInput = AddLiquidityBaseInput & { bptOut: InputAmount; kind: AddLiquidityKind.Proportional; }; -export type JoinInput = - | InitJoinInput - | UnbalancedJoinInput - | SingleAssetJoinInput - | ProportionalJoinInput; +export type AddLiquidityInput = + | AddLiquidityInitInput + | AddLiquidityUnbalancedInput + | AddLiquiditySingleAssetInput + | AddLiquidityProportionalInput; type BaseJoinQueryResult = { poolType: string; @@ -77,7 +77,10 @@ export type WeightedJoinCall = BaseJoinCall & BaseJoinQueryResult; export type JoinCall = WeightedJoinCall | ComposableJoinCall; export interface BaseJoin { - query(input: JoinInput, poolState: PoolState): Promise; + query( + input: AddLiquidityInput, + poolState: PoolState, + ): Promise; buildCall(input: JoinCall): { call: Hex; to: Address; diff --git a/src/entities/join/utils/validateInputs.ts b/src/entities/join/utils/validateInputs.ts index 89c2b657..a2906367 100644 --- a/src/entities/join/utils/validateInputs.ts +++ b/src/entities/join/utils/validateInputs.ts @@ -1,8 +1,11 @@ -import { JoinInput, AddLiquidityKind } from '../types'; +import { AddLiquidityInput, AddLiquidityKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; -export function validateInputs(input: JoinInput, poolState: PoolStateInput) { +export function validateInputs( + input: AddLiquidityInput, + poolState: PoolStateInput, +) { const bptIndex = poolState.tokens.findIndex( (t) => t.address === poolState.address, ); diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index cc625eb4..0f4ce300 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -8,7 +8,7 @@ import { vaultAbi } from '../../../abi'; import { BaseJoin, JoinBuildOutput, - JoinInput, + AddLiquidityInput, AddLiquidityKind, WeightedJoinQueryResult, WeightedJoinCall, @@ -18,7 +18,7 @@ import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; export class WeightedJoin implements BaseJoin { public async query( - input: JoinInput, + input: AddLiquidityInput, poolState: PoolState, ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); @@ -96,7 +96,7 @@ export class WeightedJoin implements BaseJoin { private getAmountsQuery( poolTokens: Token[], - input: JoinInput, + input: AddLiquidityInput, ): AddLiquidityAmounts { switch (input.kind) { case AddLiquidityKind.Init: diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 173270af..f9b88016 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -13,9 +13,9 @@ import { } from 'viem'; import { - UnbalancedJoinInput, - ProportionalJoinInput, - SingleAssetJoinInput, + AddLiquidityUnbalancedInput, + AddLiquidityProportionalInput, + AddLiquiditySingleAssetInput, AddLiquidityKind, Slippage, Address, @@ -25,7 +25,7 @@ import { ChainId, getPoolAddress, PoolJoin, - JoinInput, + AddLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; @@ -68,7 +68,7 @@ describe('composable stable join test', () => { slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolStateInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - joinInput: {} as JoinInput, + addLiquidityInput: {} as AddLiquidityInput, }; }); @@ -87,7 +87,7 @@ describe('composable stable join test', () => { }); describe('unbalanced join', () => { - let input: Omit; + let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { const bptIndex = txInput.poolStateInput.tokens.findIndex( @@ -107,37 +107,37 @@ describe('composable stable join test', () => { }; }); test('token inputs', async () => { - const joinInput = { + const addLiquidityInput = { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertUnbalancedJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); }); test('with native', async () => { - const joinInput = { + const addLiquidityInput = { ...input, amountsIn, useNativeAssetAsWrappedAmountIn: true, }; const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertUnbalancedJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); @@ -145,7 +145,7 @@ describe('composable stable join test', () => { }); describe('single asset join', () => { - let input: SingleAssetJoinInput; + let input: AddLiquiditySingleAssetInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -164,7 +164,7 @@ describe('composable stable join test', () => { test('with token', async () => { const joinResult = await doJoin({ ...txInput, - joinInput: input, + addLiquidityInput: input, }); assertSingleTokenJoin( @@ -177,19 +177,19 @@ describe('composable stable join test', () => { }); test('with native', async () => { - const joinInput = { + const addLiquidityInput = { ...input, useNativeAssetAsWrappedAmountIn: true, }; const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertSingleTokenJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); @@ -197,7 +197,7 @@ describe('composable stable join test', () => { }); describe('proportional join', () => { - let input: ProportionalJoinInput; + let input: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -214,7 +214,7 @@ describe('composable stable join test', () => { test('with tokens', async () => { const joinResult = await doJoin({ ...txInput, - joinInput: input, + addLiquidityInput: input, }); assertProportionalJoin( @@ -226,18 +226,18 @@ describe('composable stable join test', () => { ); }); test('with native', async () => { - const joinInput = { + const addLiquidityInput = { ...input, useNativeAssetAsWrappedAmountIn: true, }; const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 9cbeec27..374f144d 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -1,16 +1,16 @@ import { expect } from 'vitest'; import { PoolJoin, - JoinInput, + AddLiquidityInput, PoolStateInput, Slippage, Address, JoinBuildOutput, JoinQueryResult, - UnbalancedJoinInput, + AddLiquidityUnbalancedInput, BALANCER_VAULT, - SingleAssetJoinInput, - ProportionalJoinInput, + AddLiquiditySingleAssetInput, + AddLiquidityProportionalInput, Token, ChainId, TokenAmount, @@ -30,13 +30,13 @@ type JoinResult = { async function sdkJoin({ poolJoin, - joinInput, + addLiquidityInput, poolStateInput, slippage, testAddress, }: { poolJoin: PoolJoin; - joinInput: JoinInput; + addLiquidityInput: AddLiquidityInput; poolStateInput: PoolStateInput; slippage: Slippage; testAddress: Address; @@ -44,7 +44,10 @@ async function sdkJoin({ joinBuildOutput: JoinBuildOutput; joinQueryResult: JoinQueryResult; }> { - const joinQueryResult = await poolJoin.query(joinInput, poolStateInput); + const joinQueryResult = await poolJoin.query( + addLiquidityInput, + poolStateInput, + ); const joinBuildOutput = poolJoin.buildCall({ ...joinQueryResult, slippage, @@ -94,7 +97,7 @@ function getCheck(result: JoinQueryResult, isExactIn: boolean) { * @param txInput * @param poolJoin: PoolJoin - The pool join class, used to query the join and build the join call * @param poolInput: PoolStateInput - The state of the pool being joined - * @param joinInput: JoinInput - The parameters of the join transaction, example: bptOut, amountsIn, etc. + * @param addLiquidityInput: AddLiquidityInput - The parameters of the join transaction, example: bptOut, amountsIn, etc. * @param testAddress: Address - The address to send the transaction from * @param client: Client & PublicActions & WalletActions - The RPC client * @param slippage: Slippage - The slippage tolerance for the join transaction @@ -103,7 +106,7 @@ export async function doJoin(txInput: JoinTxInput) { const { poolJoin, poolStateInput, - joinInput, + addLiquidityInput, testAddress, client, slippage, @@ -111,7 +114,7 @@ export async function doJoin(txInput: JoinTxInput) { const { joinQueryResult, joinBuildOutput } = await sdkJoin({ poolJoin, - joinInput, + addLiquidityInput, poolStateInput, slippage, testAddress, @@ -139,7 +142,7 @@ export async function doJoin(txInput: JoinTxInput) { export function assertUnbalancedJoin( chainId: ChainId, poolStateInput: PoolStateInput, - joinInput: UnbalancedJoinInput, + addLiquidityInput: AddLiquidityUnbalancedInput, joinResult: JoinResult, slippage: Slippage, ) { @@ -149,12 +152,14 @@ export function assertUnbalancedJoin( const expectedAmountsIn = poolStateInput.tokens.map((t) => { let token; if ( - joinInput.useNativeAssetAsWrappedAmountIn && + addLiquidityInput.useNativeAssetAsWrappedAmountIn && t.address === NATIVE_ASSETS[chainId].wrapped ) token = new Token(chainId, zeroAddress, t.decimals); else token = new Token(chainId, t.address, t.decimals); - const input = joinInput.amountsIn.find((a) => a.address === t.address); + const input = addLiquidityInput.amountsIn.find( + (a) => a.address === t.address, + ); if (input === undefined) return TokenAmount.fromRawAmount(token, 0n); else return TokenAmount.fromRawAmount(token, input.rawAmount); }); @@ -166,8 +171,8 @@ export function assertUnbalancedJoin( // Should match inputs poolId: poolStateInput.id, poolType: poolStateInput.type, - fromInternalBalance: !!joinInput.fromInternalBalance, - addLiquidityKind: joinInput.kind, + fromInternalBalance: !!addLiquidityInput.fromInternalBalance, + addLiquidityKind: addLiquidityInput.kind, }; const queryCheck = getCheck(joinQueryResult, true); @@ -178,7 +183,7 @@ export function assertUnbalancedJoin( expect(joinQueryResult.bptOut.amount > 0n).to.be.true; assertJoinBuildOutput( - joinInput, + addLiquidityInput, joinQueryResult, joinBuildOutput, true, @@ -187,7 +192,7 @@ export function assertUnbalancedJoin( assertTokenDeltas( poolStateInput, - joinInput, + addLiquidityInput, joinQueryResult, joinBuildOutput, txOutput, @@ -197,7 +202,7 @@ export function assertUnbalancedJoin( export function assertSingleTokenJoin( chainId: ChainId, poolStateInput: PoolStateInput, - joinInput: SingleAssetJoinInput, + addLiquidityInput: AddLiquiditySingleAssetInput, joinResult: JoinResult, slippage: Slippage, ) { @@ -216,16 +221,16 @@ export function assertSingleTokenJoin( // Query should use same bpt out as user sets bptOut: TokenAmount.fromRawAmount( bptToken, - joinInput.bptOut.rawAmount, + addLiquidityInput.bptOut.rawAmount, ), tokenInIndex: tokensWithoutBpt.findIndex( - (t) => t.address === joinInput.tokenIn, + (t) => t.address === addLiquidityInput.tokenIn, ), // Should match inputs poolId: poolStateInput.id, poolType: poolStateInput.type, - fromInternalBalance: !!joinInput.fromInternalBalance, - addLiquidityKind: joinInput.kind, + fromInternalBalance: !!addLiquidityInput.fromInternalBalance, + addLiquidityKind: addLiquidityInput.kind, }; const queryCheck = getCheck(joinQueryResult, false); @@ -236,12 +241,12 @@ export function assertSingleTokenJoin( // (Note joinQueryResult also has value for bpt if pre-minted) joinQueryResult.amountsIn.forEach((a) => { if ( - !joinInput.useNativeAssetAsWrappedAmountIn && - a.token.address === joinInput.tokenIn + !addLiquidityInput.useNativeAssetAsWrappedAmountIn && + a.token.address === addLiquidityInput.tokenIn ) expect(a.amount > 0n).to.be.true; else if ( - joinInput.useNativeAssetAsWrappedAmountIn && + addLiquidityInput.useNativeAssetAsWrappedAmountIn && a.token.address === zeroAddress ) expect(a.amount > 0n).to.be.true; @@ -249,7 +254,7 @@ export function assertSingleTokenJoin( }); assertJoinBuildOutput( - joinInput, + addLiquidityInput, joinQueryResult, joinBuildOutput, false, @@ -258,7 +263,7 @@ export function assertSingleTokenJoin( assertTokenDeltas( poolStateInput, - joinInput, + addLiquidityInput, joinQueryResult, joinBuildOutput, txOutput, @@ -268,7 +273,7 @@ export function assertSingleTokenJoin( export function assertProportionalJoin( chainId: ChainId, poolStateInput: PoolStateInput, - joinInput: ProportionalJoinInput, + addLiquidityInput: AddLiquidityProportionalInput, joinResult: JoinResult, slippage: Slippage, ) { @@ -281,15 +286,15 @@ export function assertProportionalJoin( // Query should use same bpt out as user sets bptOut: TokenAmount.fromRawAmount( bptToken, - joinInput.bptOut.rawAmount, + addLiquidityInput.bptOut.rawAmount, ), // Only expect tokenInIndex for SingleAssetJoin tokenInIndex: undefined, // Should match inputs poolId: poolStateInput.id, poolType: poolStateInput.type, - fromInternalBalance: !!joinInput.fromInternalBalance, - addLiquidityKind: joinInput.kind, + fromInternalBalance: !!addLiquidityInput.fromInternalBalance, + addLiquidityKind: addLiquidityInput.kind, }; const queryCheck = getCheck(joinQueryResult, false); @@ -304,7 +309,7 @@ export function assertProportionalJoin( }); assertJoinBuildOutput( - joinInput, + addLiquidityInput, joinQueryResult, joinBuildOutput, false, @@ -313,7 +318,7 @@ export function assertProportionalJoin( assertTokenDeltas( poolStateInput, - joinInput, + addLiquidityInput, joinQueryResult, joinBuildOutput, txOutput, @@ -322,7 +327,7 @@ export function assertProportionalJoin( function assertTokenDeltas( poolStateInput: PoolStateInput, - joinInput: JoinInput, + addLiquidityInput: AddLiquidityInput, joinQueryResult: JoinQueryResult, joinBuildOutput: JoinBuildOutput, txOutput: TxResult, @@ -342,7 +347,7 @@ function assertTokenDeltas( ]; // If input is wrapped native we must replace it with 0 and update native value instead - if (joinInput.useNativeAssetAsWrappedAmountIn) { + if (addLiquidityInput.useNativeAssetAsWrappedAmountIn) { const index = amountsWithoutBpt.findIndex( (a) => a.token.address === zeroAddress, ); @@ -354,7 +359,7 @@ function assertTokenDeltas( } function assertJoinBuildOutput( - joinInput: JoinInput, + addLiquidityInput: AddLiquidityInput, joinQueryResult: JoinQueryResult, joinBuildOutput: JoinBuildOutput, isExactIn: boolean, @@ -375,7 +380,7 @@ function assertJoinBuildOutput( minBptOut, to: BALANCER_VAULT, // Value should equal value of any wrapped asset if using native - value: joinInput.useNativeAssetAsWrappedAmountIn + value: addLiquidityInput.useNativeAssetAsWrappedAmountIn ? (joinQueryResult.amountsIn.find( (a) => a.token.address === zeroAddress, )?.amount as bigint) diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index e9c9803b..4b458a38 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -1,7 +1,7 @@ import { Client, PublicActions, TestActions, WalletActions } from 'viem'; import { Address, - JoinInput, + AddLiquidityInput, ExitInput, PoolExit, PoolJoin, @@ -12,7 +12,7 @@ import { export type JoinTxInput = { client: Client & PublicActions & TestActions & WalletActions; poolJoin: PoolJoin; - joinInput: JoinInput; + addLiquidityInput: AddLiquidityInput; slippage: Slippage; poolStateInput: PoolStateInput; testAddress: Address; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 83da6e13..15967e8b 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -13,9 +13,9 @@ import { } from 'viem'; import { - UnbalancedJoinInput, - ProportionalJoinInput, - SingleAssetJoinInput, + AddLiquidityUnbalancedInput, + AddLiquidityProportionalInput, + AddLiquiditySingleAssetInput, AddLiquidityKind, Slippage, Address, @@ -25,7 +25,7 @@ import { ChainId, getPoolAddress, PoolJoin, - JoinInput, + AddLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; @@ -68,7 +68,7 @@ describe('weighted join test', () => { slippage: Slippage.fromPercentage('1'), // 1% poolStateInput, testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig - joinInput: {} as JoinInput, + addLiquidityInput: {} as AddLiquidityInput, }; }); @@ -91,7 +91,7 @@ describe('weighted join test', () => { }); describe('unbalanced join', () => { - let input: Omit; + let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { amountsIn = txInput.poolStateInput.tokens.map((t) => ({ @@ -106,38 +106,38 @@ describe('weighted join test', () => { }; }); test('with tokens', async () => { - const joinInput = { + const addLiquidityInput = { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertUnbalancedJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); }); test('with native', async () => { - const joinInput = { + const addLiquidityInput = { ...input, amountsIn, useNativeAssetAsWrappedAmountIn: true, }; const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertUnbalancedJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); @@ -145,7 +145,7 @@ describe('weighted join test', () => { }); describe('single asset join', () => { - let joinInput: SingleAssetJoinInput; + let addLiquidityInput: AddLiquiditySingleAssetInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -153,7 +153,7 @@ describe('weighted join test', () => { address: poolStateInput.address, }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - joinInput = { + addLiquidityInput = { bptOut, tokenIn, chainId, @@ -165,13 +165,13 @@ describe('weighted join test', () => { test('with token', async () => { const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertSingleTokenJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); @@ -180,8 +180,8 @@ describe('weighted join test', () => { test('with native', async () => { const joinResult = await doJoin({ ...txInput, - joinInput: { - ...joinInput, + addLiquidityInput: { + ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, }); @@ -190,7 +190,7 @@ describe('weighted join test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, { - ...joinInput, + ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, joinResult, @@ -200,14 +200,14 @@ describe('weighted join test', () => { }); describe('proportional join', () => { - let joinInput: ProportionalJoinInput; + let addLiquidityInput: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), decimals: 18, address: poolStateInput.address, }; - joinInput = { + addLiquidityInput = { bptOut, chainId, rpcUrl, @@ -217,13 +217,13 @@ describe('weighted join test', () => { test('with tokens', async () => { const joinResult = await doJoin({ ...txInput, - joinInput, + addLiquidityInput, }); assertProportionalJoin( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, + addLiquidityInput, joinResult, txInput.slippage, ); @@ -231,8 +231,8 @@ describe('weighted join test', () => { test('with native', async () => { const joinResult = await doJoin({ ...txInput, - joinInput: { - ...joinInput, + addLiquidityInput: { + ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, }); @@ -241,7 +241,7 @@ describe('weighted join test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, { - ...joinInput, + ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, joinResult, From 7c9851d2015bc90e63e499aaa380785360976510 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 10:56:07 -0300 Subject: [PATCH 175/199] Refactor JoinQueryResult into AddLiquidityQueryResult --- .../composable-stable/composableStableJoin.ts | 4 +- src/entities/join/poolJoin.ts | 4 +- src/entities/join/types.ts | 24 +-- src/entities/join/weighted/weightedJoin.ts | 4 +- test/lib/utils/joinHelper.ts | 167 ++++++++++-------- 5 files changed, 110 insertions(+), 93 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 8ec2ef63..2f4a7cfc 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -9,7 +9,7 @@ import { JoinBuildOutput, AddLiquidityInput, AddLiquidityKind, - ComposableStableJoinQueryResult, + AddLiquidityComposableStableQueryResult, ComposableJoinCall, } from '../types'; import { @@ -27,7 +27,7 @@ export class ComposableStableJoin implements BaseJoin { public async query( input: AddLiquidityInput, poolState: PoolState, - ): Promise { + ): Promise { const bptIndex = poolState.tokens.findIndex( (t) => t.address === poolState.address, ); diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 8f179094..0518a610 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -3,7 +3,7 @@ import { JoinBuildOutput, JoinConfig, AddLiquidityInput, - JoinQueryResult, + AddLiquidityQueryResult, JoinCall, } from './types'; import { WeightedJoin } from './weighted/weightedJoin'; @@ -37,7 +37,7 @@ export class PoolJoin { public async query( input: AddLiquidityInput, poolState: PoolStateInput, - ): Promise { + ): Promise { validateInputs(input, poolState); const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index c7fe3635..dd978ac3 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -45,7 +45,7 @@ export type AddLiquidityInput = | AddLiquiditySingleAssetInput | AddLiquidityProportionalInput; -type BaseJoinQueryResult = { +type AddLiquidityBaseQueryResult = { poolType: string; poolId: Hex; addLiquidityKind: AddLiquidityKind; @@ -55,15 +55,16 @@ type BaseJoinQueryResult = { tokenInIndex?: number; }; -export type WeightedJoinQueryResult = BaseJoinQueryResult; +export type AddLiquidityWeightedQueryResult = AddLiquidityBaseQueryResult; -export type ComposableStableJoinQueryResult = BaseJoinQueryResult & { - bptIndex: number; -}; +export type AddLiquidityComposableStableQueryResult = + AddLiquidityBaseQueryResult & { + bptIndex: number; + }; -export type JoinQueryResult = - | WeightedJoinQueryResult - | ComposableStableJoinQueryResult; +export type AddLiquidityQueryResult = + | AddLiquidityWeightedQueryResult + | AddLiquidityComposableStableQueryResult; type BaseJoinCall = { slippage: Slippage; @@ -71,8 +72,9 @@ type BaseJoinCall = { recipient: Address; }; -export type ComposableJoinCall = BaseJoinCall & ComposableStableJoinQueryResult; -export type WeightedJoinCall = BaseJoinCall & BaseJoinQueryResult; +export type ComposableJoinCall = BaseJoinCall & + AddLiquidityComposableStableQueryResult; +export type WeightedJoinCall = BaseJoinCall & AddLiquidityBaseQueryResult; export type JoinCall = WeightedJoinCall | ComposableJoinCall; @@ -80,7 +82,7 @@ export interface BaseJoin { query( input: AddLiquidityInput, poolState: PoolState, - ): Promise; + ): Promise; buildCall(input: JoinCall): { call: Hex; to: Address; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 0f4ce300..5f4369b1 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -10,7 +10,7 @@ import { JoinBuildOutput, AddLiquidityInput, AddLiquidityKind, - WeightedJoinQueryResult, + AddLiquidityWeightedQueryResult, WeightedJoinCall, } from '../types'; import { AddLiquidityAmounts, PoolState } from '../../types'; @@ -20,7 +20,7 @@ export class WeightedJoin implements BaseJoin { public async query( input: AddLiquidityInput, poolState: PoolState, - ): Promise { + ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); const userData = this.encodeUserData(input.kind, amounts); diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 374f144d..6961428f 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -6,7 +6,7 @@ import { Slippage, Address, JoinBuildOutput, - JoinQueryResult, + AddLiquidityQueryResult, AddLiquidityUnbalancedInput, BALANCER_VAULT, AddLiquiditySingleAssetInput, @@ -14,7 +14,7 @@ import { Token, ChainId, TokenAmount, - ComposableStableJoinQueryResult, + AddLiquidityComposableStableQueryResult, NATIVE_ASSETS, } from '../../../src'; import { TxResult, sendTransactionGetBalances } from './helper'; @@ -23,7 +23,7 @@ import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type JoinResult = { - joinQueryResult: JoinQueryResult; + addLiquidityQueryResult: AddLiquidityQueryResult; joinBuildOutput: JoinBuildOutput; txOutput: TxResult; }; @@ -42,14 +42,14 @@ async function sdkJoin({ testAddress: Address; }): Promise<{ joinBuildOutput: JoinBuildOutput; - joinQueryResult: JoinQueryResult; + addLiquidityQueryResult: AddLiquidityQueryResult; }> { - const joinQueryResult = await poolJoin.query( + const addLiquidityQueryResult = await poolJoin.query( addLiquidityInput, poolStateInput, ); const joinBuildOutput = poolJoin.buildCall({ - ...joinQueryResult, + ...addLiquidityQueryResult, slippage, sender: testAddress, recipient: testAddress, @@ -57,26 +57,31 @@ async function sdkJoin({ return { joinBuildOutput, - joinQueryResult, + addLiquidityQueryResult, }; } -function isComposableStableJoinQueryResult(result: JoinQueryResult): boolean { - return (result as ComposableStableJoinQueryResult).bptIndex !== undefined; +function isAddLiquidityComposableStableQueryResult( + result: AddLiquidityQueryResult, +): boolean { + return ( + (result as AddLiquidityComposableStableQueryResult).bptIndex !== + undefined + ); } -function getCheck(result: JoinQueryResult, isExactIn: boolean) { - if (isComposableStableJoinQueryResult(result)) { +function getCheck(result: AddLiquidityQueryResult, isExactIn: boolean) { + if (isAddLiquidityComposableStableQueryResult(result)) { if (isExactIn) { // Using this destructuring to return only the fields of interest // rome-ignore lint/correctness/noUnusedVariables: const { bptOut, bptIndex, ...check } = - result as ComposableStableJoinQueryResult; + result as AddLiquidityComposableStableQueryResult; return check; } else { // rome-ignore lint/correctness/noUnusedVariables: const { amountsIn, bptIndex, ...check } = - result as ComposableStableJoinQueryResult; + result as AddLiquidityComposableStableQueryResult; return check; } } else { @@ -112,7 +117,7 @@ export async function doJoin(txInput: JoinTxInput) { slippage, } = txInput; - const { joinQueryResult, joinBuildOutput } = await sdkJoin({ + const { addLiquidityQueryResult, joinBuildOutput } = await sdkJoin({ poolJoin, addLiquidityInput, poolStateInput, @@ -133,7 +138,7 @@ export async function doJoin(txInput: JoinTxInput) { ); return { - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, txOutput, }; @@ -146,7 +151,7 @@ export function assertUnbalancedJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; + const { txOutput, addLiquidityQueryResult, joinBuildOutput } = joinResult; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) const expectedAmountsIn = poolStateInput.tokens.map((t) => { @@ -164,7 +169,10 @@ export function assertUnbalancedJoin( else return TokenAmount.fromRawAmount(token, input.rawAmount); }); - const expectedQueryResult: Omit = { + const expectedQueryResult: Omit< + AddLiquidityQueryResult, + 'bptOut' | 'bptIndex' + > = { // Query should use same amountsIn as input amountsIn: expectedAmountsIn, tokenInIndex: undefined, @@ -175,16 +183,16 @@ export function assertUnbalancedJoin( addLiquidityKind: addLiquidityInput.kind, }; - const queryCheck = getCheck(joinQueryResult, true); + const queryCheck = getCheck(addLiquidityQueryResult, true); expect(queryCheck).to.deep.eq(expectedQueryResult); // Expect some bpt amount - expect(joinQueryResult.bptOut.amount > 0n).to.be.true; + expect(addLiquidityQueryResult.bptOut.amount > 0n).to.be.true; assertJoinBuildOutput( addLiquidityInput, - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, true, slippage, @@ -193,7 +201,7 @@ export function assertUnbalancedJoin( assertTokenDeltas( poolStateInput, addLiquidityInput, - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, txOutput, ); @@ -206,9 +214,10 @@ export function assertSingleTokenJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; + const { txOutput, addLiquidityQueryResult, joinBuildOutput } = joinResult; - if (joinQueryResult.tokenInIndex === undefined) throw Error('No index'); + if (addLiquidityQueryResult.tokenInIndex === undefined) + throw Error('No index'); const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -216,30 +225,32 @@ export function assertSingleTokenJoin( (t) => t.address !== poolStateInput.address, ); - const expectedQueryResult: Omit = - { - // Query should use same bpt out as user sets - bptOut: TokenAmount.fromRawAmount( - bptToken, - addLiquidityInput.bptOut.rawAmount, - ), - tokenInIndex: tokensWithoutBpt.findIndex( - (t) => t.address === addLiquidityInput.tokenIn, - ), - // Should match inputs - poolId: poolStateInput.id, - poolType: poolStateInput.type, - fromInternalBalance: !!addLiquidityInput.fromInternalBalance, - addLiquidityKind: addLiquidityInput.kind, - }; - - const queryCheck = getCheck(joinQueryResult, false); + const expectedQueryResult: Omit< + AddLiquidityQueryResult, + 'amountsIn' | 'bptIndex' + > = { + // Query should use same bpt out as user sets + bptOut: TokenAmount.fromRawAmount( + bptToken, + addLiquidityInput.bptOut.rawAmount, + ), + tokenInIndex: tokensWithoutBpt.findIndex( + (t) => t.address === addLiquidityInput.tokenIn, + ), + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + fromInternalBalance: !!addLiquidityInput.fromInternalBalance, + addLiquidityKind: addLiquidityInput.kind, + }; + + const queryCheck = getCheck(addLiquidityQueryResult, false); expect(queryCheck).to.deep.eq(expectedQueryResult); // Expect only tokenIn to have amount > 0 - // (Note joinQueryResult also has value for bpt if pre-minted) - joinQueryResult.amountsIn.forEach((a) => { + // (Note addLiquidityQueryResult also has value for bpt if pre-minted) + addLiquidityQueryResult.amountsIn.forEach((a) => { if ( !addLiquidityInput.useNativeAssetAsWrappedAmountIn && a.token.address === addLiquidityInput.tokenIn @@ -255,7 +266,7 @@ export function assertSingleTokenJoin( assertJoinBuildOutput( addLiquidityInput, - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, false, slippage, @@ -264,7 +275,7 @@ export function assertSingleTokenJoin( assertTokenDeltas( poolStateInput, addLiquidityInput, - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, txOutput, ); @@ -277,32 +288,34 @@ export function assertProportionalJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, joinQueryResult, joinBuildOutput } = joinResult; + const { txOutput, addLiquidityQueryResult, joinBuildOutput } = joinResult; const bptToken = new Token(chainId, poolStateInput.address, 18); - const expectedQueryResult: Omit = - { - // Query should use same bpt out as user sets - bptOut: TokenAmount.fromRawAmount( - bptToken, - addLiquidityInput.bptOut.rawAmount, - ), - // Only expect tokenInIndex for SingleAssetJoin - tokenInIndex: undefined, - // Should match inputs - poolId: poolStateInput.id, - poolType: poolStateInput.type, - fromInternalBalance: !!addLiquidityInput.fromInternalBalance, - addLiquidityKind: addLiquidityInput.kind, - }; - - const queryCheck = getCheck(joinQueryResult, false); + const expectedQueryResult: Omit< + AddLiquidityQueryResult, + 'amountsIn' | 'bptIndex' + > = { + // Query should use same bpt out as user sets + bptOut: TokenAmount.fromRawAmount( + bptToken, + addLiquidityInput.bptOut.rawAmount, + ), + // Only expect tokenInIndex for SingleAssetJoin + tokenInIndex: undefined, + // Should match inputs + poolId: poolStateInput.id, + poolType: poolStateInput.type, + fromInternalBalance: !!addLiquidityInput.fromInternalBalance, + addLiquidityKind: addLiquidityInput.kind, + }; + + const queryCheck = getCheck(addLiquidityQueryResult, false); expect(queryCheck).to.deep.eq(expectedQueryResult); // Expect all assets in to have an amount > 0 apart from BPT if it exists - joinQueryResult.amountsIn.forEach((a) => { + addLiquidityQueryResult.amountsIn.forEach((a) => { if (a.token.address === poolStateInput.address) expect(a.amount).toEqual(0n); else expect(a.amount > 0n).to.be.true; @@ -310,7 +323,7 @@ export function assertProportionalJoin( assertJoinBuildOutput( addLiquidityInput, - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, false, slippage, @@ -319,7 +332,7 @@ export function assertProportionalJoin( assertTokenDeltas( poolStateInput, addLiquidityInput, - joinQueryResult, + addLiquidityQueryResult, joinBuildOutput, txOutput, ); @@ -328,21 +341,21 @@ export function assertProportionalJoin( function assertTokenDeltas( poolStateInput: PoolStateInput, addLiquidityInput: AddLiquidityInput, - joinQueryResult: JoinQueryResult, + addLiquidityQueryResult: AddLiquidityQueryResult, joinBuildOutput: JoinBuildOutput, txOutput: TxResult, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); - // joinQueryResult amountsIn will have a value for the BPT token if it is a pre-minted pool - const amountsWithoutBpt = [...joinQueryResult.amountsIn].filter( + // addLiquidityQueryResult amountsIn will have a value for the BPT token if it is a pre-minted pool + const amountsWithoutBpt = [...addLiquidityQueryResult.amountsIn].filter( (t) => t.token.address !== poolStateInput.address, ); // Matching order of getTokens helper: [poolTokens, BPT, native] const expectedDeltas = [ ...amountsWithoutBpt.map((a) => a.amount), - joinQueryResult.bptOut.amount, + addLiquidityQueryResult.bptOut.amount, 0n, ]; @@ -360,20 +373,22 @@ function assertTokenDeltas( function assertJoinBuildOutput( addLiquidityInput: AddLiquidityInput, - joinQueryResult: JoinQueryResult, + addLiquidityQueryResult: AddLiquidityQueryResult, joinBuildOutput: JoinBuildOutput, isExactIn: boolean, slippage: Slippage, ) { // if exactIn maxAmountsIn should use same amountsIn as input else slippage should be applied const maxAmountsIn = isExactIn - ? joinQueryResult.amountsIn.map((a) => a.amount) - : joinQueryResult.amountsIn.map((a) => slippage.applyTo(a.amount)); + ? addLiquidityQueryResult.amountsIn.map((a) => a.amount) + : addLiquidityQueryResult.amountsIn.map((a) => + slippage.applyTo(a.amount), + ); // if exactIn slippage should be applied to bptOut else should use same bptOut as input const minBptOut = isExactIn - ? slippage.removeFrom(joinQueryResult.bptOut.amount) - : joinQueryResult.bptOut.amount; + ? slippage.removeFrom(addLiquidityQueryResult.bptOut.amount) + : addLiquidityQueryResult.bptOut.amount; const expectedBuildOutput: Omit = { maxAmountsIn, @@ -381,7 +396,7 @@ function assertJoinBuildOutput( to: BALANCER_VAULT, // Value should equal value of any wrapped asset if using native value: addLiquidityInput.useNativeAssetAsWrappedAmountIn - ? (joinQueryResult.amountsIn.find( + ? (addLiquidityQueryResult.amountsIn.find( (a) => a.token.address === zeroAddress, )?.amount as bigint) : 0n, From f608e6f823b093f11ddc78e1905b7cbac2dd2d71 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 10:58:23 -0300 Subject: [PATCH 176/199] Refactor JoinCall into AddLiquidityCall --- .../join/composable-stable/composableStableJoin.ts | 8 +++++--- src/entities/join/poolJoin.ts | 4 ++-- src/entities/join/types.ts | 13 ++++++++----- src/entities/join/weighted/weightedJoin.ts | 8 +++++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/composableStableJoin.ts index 2f4a7cfc..252ab7cd 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/composableStableJoin.ts @@ -10,7 +10,7 @@ import { AddLiquidityInput, AddLiquidityKind, AddLiquidityComposableStableQueryResult, - ComposableJoinCall, + AddLiquidityComposableStableCall, } from '../types'; import { AddLiquidityAmounts as AddLiquidityAmountsBase, @@ -73,7 +73,7 @@ export class ComposableStableJoin implements BaseJoin { }; } - public buildCall(input: ComposableJoinCall): JoinBuildOutput { + public buildCall(input: AddLiquidityComposableStableCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.addLiquidityKind, amounts); @@ -161,7 +161,9 @@ export class ComposableStableJoin implements BaseJoin { }; } - private getAmountsCall(input: ComposableJoinCall): AddLiquidityAmounts { + private getAmountsCall( + input: AddLiquidityComposableStableCall, + ): AddLiquidityAmounts { let addLiquidityAmounts: AddLiquidityAmountsBase; switch (input.addLiquidityKind) { case AddLiquidityKind.Init: diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts index 0518a610..9b11437d 100644 --- a/src/entities/join/poolJoin.ts +++ b/src/entities/join/poolJoin.ts @@ -4,7 +4,7 @@ import { JoinConfig, AddLiquidityInput, AddLiquidityQueryResult, - JoinCall, + AddLiquidityCall, } from './types'; import { WeightedJoin } from './weighted/weightedJoin'; import { PoolStateInput } from '../types'; @@ -49,7 +49,7 @@ export class PoolJoin { return this.getJoin(poolState.type).query(input, mappedPoolState); } - public buildCall(input: JoinCall): JoinBuildOutput { + public buildCall(input: AddLiquidityCall): JoinBuildOutput { return this.getJoin(input.poolType).buildCall(input); } } diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index dd978ac3..165fbe65 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -66,24 +66,27 @@ export type AddLiquidityQueryResult = | AddLiquidityWeightedQueryResult | AddLiquidityComposableStableQueryResult; -type BaseJoinCall = { +type AddLiquidityBaseCall = { slippage: Slippage; sender: Address; recipient: Address; }; -export type ComposableJoinCall = BaseJoinCall & +export type AddLiquidityComposableStableCall = AddLiquidityBaseCall & AddLiquidityComposableStableQueryResult; -export type WeightedJoinCall = BaseJoinCall & AddLiquidityBaseQueryResult; +export type AddLiquidityWeightedCall = AddLiquidityBaseCall & + AddLiquidityBaseQueryResult; -export type JoinCall = WeightedJoinCall | ComposableJoinCall; +export type AddLiquidityCall = + | AddLiquidityWeightedCall + | AddLiquidityComposableStableCall; export interface BaseJoin { query( input: AddLiquidityInput, poolState: PoolState, ): Promise; - buildCall(input: JoinCall): { + buildCall(input: AddLiquidityCall): { call: Hex; to: Address; value: bigint; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/weightedJoin.ts index 5f4369b1..c7e485b0 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/weightedJoin.ts @@ -11,7 +11,7 @@ import { AddLiquidityInput, AddLiquidityKind, AddLiquidityWeightedQueryResult, - WeightedJoinCall, + AddLiquidityWeightedCall, } from '../types'; import { AddLiquidityAmounts, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; @@ -62,7 +62,7 @@ export class WeightedJoin implements BaseJoin { }; } - public buildCall(input: WeightedJoinCall): JoinBuildOutput { + public buildCall(input: AddLiquidityWeightedCall): JoinBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.addLiquidityKind, amounts); @@ -133,7 +133,9 @@ export class WeightedJoin implements BaseJoin { } } - private getAmountsCall(input: WeightedJoinCall): AddLiquidityAmounts { + private getAmountsCall( + input: AddLiquidityWeightedCall, + ): AddLiquidityAmounts { switch (input.addLiquidityKind) { case AddLiquidityKind.Init: case AddLiquidityKind.Unbalanced: { From 280950dde4d9c52987771bf4ec79c19dfae92da6 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 12:34:24 -0300 Subject: [PATCH 177/199] Refactor PoolJoin into AddLiquidity --- README.md | 8 +-- examples/joinPool.ts | 11 ++-- src/entities/join/addLiquidity.ts | 58 +++++++++++++++++++ ...oin.ts => addLiquidityComposableStable.ts} | 8 +-- src/entities/join/index.ts | 2 +- src/entities/join/poolJoin.ts | 55 ------------------ src/entities/join/types.ts | 6 +- ...eightedJoin.ts => addLiquidityWeighted.ts} | 10 ++-- test/composableStableJoin.integration.test.ts | 6 +- test/lib/utils/joinHelper.ts | 16 ++--- test/lib/utils/types.ts | 4 +- test/weightedJoin.integration.test.ts | 6 +- 12 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 src/entities/join/addLiquidity.ts rename src/entities/join/composable-stable/{composableStableJoin.ts => addLiquidityComposableStable.ts} (97%) delete mode 100644 src/entities/join/poolJoin.ts rename src/entities/join/weighted/{weightedJoin.ts => addLiquidityWeighted.ts} (96%) diff --git a/README.md b/README.md index 2ab22c87..fa0bc918 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ it can be used for: ### Usage for Joining Pool ```ts - import { BalancerApi, PoolJoin } from "@balancer/sdk"; + import { BalancerApi, AddLiquidity } from "@balancer/sdk"; ... const addLiquidityInput: AddLiquidityProportionalInput = { bptOut, @@ -44,10 +44,10 @@ it can be used for: const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); - const poolJoin = new PoolJoin(); - const queryResult = await poolJoin.query(addLiquidityInput, poolState); + const addLiquidity = new AddLiquidity(); + const queryResult = await addLiquidity.query(addLiquidityInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = - poolJoin.buildCall({ + addLiquidity.buildCall({ ...queryResult, slippage, sender: signerAddress, diff --git a/examples/joinPool.ts b/examples/joinPool.ts index fb96da6d..eaa4f891 100644 --- a/examples/joinPool.ts +++ b/examples/joinPool.ts @@ -13,7 +13,7 @@ import { ChainId, AddLiquidityInput, AddLiquidityKind, - PoolJoin, + AddLiquidity, Slippage, } from '../src'; import { parseUnits } from 'viem'; @@ -54,8 +54,11 @@ const join = async () => { }; // Simulate the join to get the amount of BPT out - const poolJoin = new PoolJoin(); - const queryResult = await poolJoin.query(addLiquidityInput, poolStateInput); + const addLiquidity = new AddLiquidity(); + const queryResult = await addLiquidity.query( + addLiquidityInput, + poolStateInput, + ); console.log('\nJoin Query Result:'); console.log('Tokens In:'); @@ -65,7 +68,7 @@ const join = async () => { console.log(`BPT Out: ${queryResult.bptOut.amount.toString()}`); // Apply slippage to the BPT amount received from the query and construct the call - const call = poolJoin.buildCall({ + const call = addLiquidity.buildCall({ ...queryResult, slippage, sender: userAccount, diff --git a/src/entities/join/addLiquidity.ts b/src/entities/join/addLiquidity.ts new file mode 100644 index 00000000..9e0b653f --- /dev/null +++ b/src/entities/join/addLiquidity.ts @@ -0,0 +1,58 @@ +import { + AddLiquidityBase, + JoinBuildOutput, + AddLiquidityConfig, + AddLiquidityInput, + AddLiquidityQueryResult, + AddLiquidityCall, +} from './types'; +import { AddLiquidityWeighted } from './weighted/addLiquidityWeighted'; +import { PoolStateInput } from '../types'; +import { validateInputs } from './utils/validateInputs'; +import { getSortedTokens } from '../utils/getSortedTokens'; +import { AddLiquidityComposableStable } from './composable-stable/addLiquidityComposableStable'; + +export class AddLiquidity { + private readonly addLiquidityTypes: Record = {}; + + constructor(config?: AddLiquidityConfig) { + const { customAddLiquidityTypes } = config || {}; + this.addLiquidityTypes = { + WEIGHTED: new AddLiquidityWeighted(), + // PHANTOM_STABLE === ComposableStables in API + PHANTOM_STABLE: new AddLiquidityComposableStable(), + // custom pool Joins take precedence over base Joins + ...customAddLiquidityTypes, + }; + } + + public getAddLiquidity(poolType: string): AddLiquidityBase { + if (!this.addLiquidityTypes[poolType]) { + throw new Error('Unsupported pool type'); + } + + return this.addLiquidityTypes[poolType]; + } + + public async query( + input: AddLiquidityInput, + poolState: PoolStateInput, + ): Promise { + validateInputs(input, poolState); + + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); + const mappedPoolState = { + ...poolState, + tokens: sortedTokens, + }; + + return this.getAddLiquidity(poolState.type).query( + input, + mappedPoolState, + ); + } + + public buildCall(input: AddLiquidityCall): JoinBuildOutput { + return this.getAddLiquidity(input.poolType).buildCall(input); + } +} diff --git a/src/entities/join/composable-stable/composableStableJoin.ts b/src/entities/join/composable-stable/addLiquidityComposableStable.ts similarity index 97% rename from src/entities/join/composable-stable/composableStableJoin.ts rename to src/entities/join/composable-stable/addLiquidityComposableStable.ts index 252ab7cd..db3071b7 100644 --- a/src/entities/join/composable-stable/composableStableJoin.ts +++ b/src/entities/join/composable-stable/addLiquidityComposableStable.ts @@ -1,11 +1,11 @@ import { encodeFunctionData } from 'viem'; -import { Token } from '../../../entities/token'; -import { TokenAmount } from '../../../entities/tokenAmount'; +import { Token } from '../../token'; +import { TokenAmount } from '../../tokenAmount'; import { Address } from '../../../types'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; import { - BaseJoin, + AddLiquidityBase, JoinBuildOutput, AddLiquidityInput, AddLiquidityKind, @@ -23,7 +23,7 @@ type AddLiquidityAmounts = AddLiquidityAmountsBase & { maxAmountsInNoBpt: bigint[]; }; -export class ComposableStableJoin implements BaseJoin { +export class AddLiquidityComposableStable implements AddLiquidityBase { public async query( input: AddLiquidityInput, poolState: PoolState, diff --git a/src/entities/join/index.ts b/src/entities/join/index.ts index d20b54f9..6beed28f 100644 --- a/src/entities/join/index.ts +++ b/src/entities/join/index.ts @@ -1,2 +1,2 @@ -export * from './poolJoin'; +export * from './addLiquidity'; export * from './types'; diff --git a/src/entities/join/poolJoin.ts b/src/entities/join/poolJoin.ts deleted file mode 100644 index 9b11437d..00000000 --- a/src/entities/join/poolJoin.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - BaseJoin, - JoinBuildOutput, - JoinConfig, - AddLiquidityInput, - AddLiquidityQueryResult, - AddLiquidityCall, -} from './types'; -import { WeightedJoin } from './weighted/weightedJoin'; -import { PoolStateInput } from '../types'; -import { validateInputs } from './utils/validateInputs'; -import { getSortedTokens } from '../utils/getSortedTokens'; -import { ComposableStableJoin } from './composable-stable/composableStableJoin'; - -export class PoolJoin { - private readonly poolJoins: Record = {}; - - constructor(config?: JoinConfig) { - const { customPoolJoins } = config || {}; - this.poolJoins = { - WEIGHTED: new WeightedJoin(), - // PHANTOM_STABLE === ComposableStables in API - PHANTOM_STABLE: new ComposableStableJoin(), - // custom pool Joins take precedence over base Joins - ...customPoolJoins, - }; - } - - public getJoin(poolType: string): BaseJoin { - if (!this.poolJoins[poolType]) { - throw new Error('Unsupported pool type'); - } - - return this.poolJoins[poolType]; - } - - public async query( - input: AddLiquidityInput, - poolState: PoolStateInput, - ): Promise { - validateInputs(input, poolState); - - const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); - const mappedPoolState = { - ...poolState, - tokens: sortedTokens, - }; - - return this.getJoin(poolState.type).query(input, mappedPoolState); - } - - public buildCall(input: AddLiquidityCall): JoinBuildOutput { - return this.getJoin(input.poolType).buildCall(input); - } -} diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 165fbe65..c6404d4d 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -81,7 +81,7 @@ export type AddLiquidityCall = | AddLiquidityWeightedCall | AddLiquidityComposableStableCall; -export interface BaseJoin { +export interface AddLiquidityBase { query( input: AddLiquidityInput, poolState: PoolState, @@ -103,6 +103,6 @@ export type JoinBuildOutput = { maxAmountsIn: bigint[]; }; -export type JoinConfig = { - customPoolJoins: Record; +export type AddLiquidityConfig = { + customAddLiquidityTypes: Record; }; diff --git a/src/entities/join/weighted/weightedJoin.ts b/src/entities/join/weighted/addLiquidityWeighted.ts similarity index 96% rename from src/entities/join/weighted/weightedJoin.ts rename to src/entities/join/weighted/addLiquidityWeighted.ts index c7e485b0..c0a6f072 100644 --- a/src/entities/join/weighted/weightedJoin.ts +++ b/src/entities/join/weighted/addLiquidityWeighted.ts @@ -1,12 +1,12 @@ import { encodeFunctionData } from 'viem'; -import { Token } from '../../../entities/token'; -import { TokenAmount } from '../../../entities/tokenAmount'; -import { WeightedEncoder } from '../../../entities/encoders/weighted'; +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 { vaultAbi } from '../../../abi'; import { - BaseJoin, + AddLiquidityBase, JoinBuildOutput, AddLiquidityInput, AddLiquidityKind, @@ -16,7 +16,7 @@ import { import { AddLiquidityAmounts, PoolState } from '../../types'; import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; -export class WeightedJoin implements BaseJoin { +export class AddLiquidityWeighted implements AddLiquidityBase { public async query( input: AddLiquidityInput, poolState: PoolState, diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index f9b88016..30320e08 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- composableStableJoin.integration.test.ts +// pnpm test -- addLiquidityComposableStable.integration.test.ts import { describe, test, beforeAll, beforeEach } from 'vitest'; import { config } from 'dotenv'; config(); @@ -24,7 +24,7 @@ import { CHAINS, ChainId, getPoolAddress, - PoolJoin, + AddLiquidity, AddLiquidityInput, InputAmount, } from '../src'; @@ -64,7 +64,7 @@ describe('composable stable join test', () => { txInput = { client, - poolJoin: new PoolJoin(), + addLiquidity: new AddLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolStateInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 6961428f..22920211 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -1,6 +1,6 @@ import { expect } from 'vitest'; import { - PoolJoin, + AddLiquidity, AddLiquidityInput, PoolStateInput, Slippage, @@ -29,13 +29,13 @@ type JoinResult = { }; async function sdkJoin({ - poolJoin, + addLiquidity, addLiquidityInput, poolStateInput, slippage, testAddress, }: { - poolJoin: PoolJoin; + addLiquidity: AddLiquidity; addLiquidityInput: AddLiquidityInput; poolStateInput: PoolStateInput; slippage: Slippage; @@ -44,11 +44,11 @@ async function sdkJoin({ joinBuildOutput: JoinBuildOutput; addLiquidityQueryResult: AddLiquidityQueryResult; }> { - const addLiquidityQueryResult = await poolJoin.query( + const addLiquidityQueryResult = await addLiquidity.query( addLiquidityInput, poolStateInput, ); - const joinBuildOutput = poolJoin.buildCall({ + const joinBuildOutput = addLiquidity.buildCall({ ...addLiquidityQueryResult, slippage, sender: testAddress, @@ -100,7 +100,7 @@ function getCheck(result: AddLiquidityQueryResult, isExactIn: boolean) { /** * Create and submit join transaction. * @param txInput - * @param poolJoin: PoolJoin - The pool join class, used to query the join and build the join call + * @param addLiquidity: AddLiquidity - The pool join class, used to query the join and build the join call * @param poolInput: PoolStateInput - The state of the pool being joined * @param addLiquidityInput: AddLiquidityInput - The parameters of the join transaction, example: bptOut, amountsIn, etc. * @param testAddress: Address - The address to send the transaction from @@ -109,7 +109,7 @@ function getCheck(result: AddLiquidityQueryResult, isExactIn: boolean) { */ export async function doJoin(txInput: JoinTxInput) { const { - poolJoin, + addLiquidity, poolStateInput, addLiquidityInput, testAddress, @@ -118,7 +118,7 @@ export async function doJoin(txInput: JoinTxInput) { } = txInput; const { addLiquidityQueryResult, joinBuildOutput } = await sdkJoin({ - poolJoin, + addLiquidity, addLiquidityInput, poolStateInput, slippage, diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index 4b458a38..a36afd20 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -4,14 +4,14 @@ import { AddLiquidityInput, ExitInput, PoolExit, - PoolJoin, + AddLiquidity, PoolStateInput, Slippage, } from '../../../src'; export type JoinTxInput = { client: Client & PublicActions & TestActions & WalletActions; - poolJoin: PoolJoin; + addLiquidity: AddLiquidity; addLiquidityInput: AddLiquidityInput; slippage: Slippage; poolStateInput: PoolStateInput; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 15967e8b..7bee52fa 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- weightedJoin.integration.test.ts +// pnpm test -- addLiquidityWeighted.integration.test.ts import { describe, test, beforeAll, beforeEach } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -24,7 +24,7 @@ import { CHAINS, ChainId, getPoolAddress, - PoolJoin, + AddLiquidity, AddLiquidityInput, InputAmount, } from '../src'; @@ -64,7 +64,7 @@ describe('weighted join test', () => { txInput = { client, - poolJoin: new PoolJoin(), + addLiquidity: new AddLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput, testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig From 85cfd332ef3746cc036e2efec52619735ec3f8b7 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 12:36:59 -0300 Subject: [PATCH 178/199] Refactor JoinBuildOutput into AddLiquidityOutput --- src/entities/join/addLiquidity.ts | 4 +- .../addLiquidityComposableStable.ts | 6 +- src/entities/join/types.ts | 2 +- .../join/weighted/addLiquidityWeighted.ts | 4 +- test/lib/utils/joinHelper.ts | 60 ++++++++++--------- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/entities/join/addLiquidity.ts b/src/entities/join/addLiquidity.ts index 9e0b653f..5695072d 100644 --- a/src/entities/join/addLiquidity.ts +++ b/src/entities/join/addLiquidity.ts @@ -1,6 +1,6 @@ import { AddLiquidityBase, - JoinBuildOutput, + AddLiquidityBuildOutput, AddLiquidityConfig, AddLiquidityInput, AddLiquidityQueryResult, @@ -52,7 +52,7 @@ export class AddLiquidity { ); } - public buildCall(input: AddLiquidityCall): JoinBuildOutput { + public buildCall(input: AddLiquidityCall): AddLiquidityBuildOutput { return this.getAddLiquidity(input.poolType).buildCall(input); } } diff --git a/src/entities/join/composable-stable/addLiquidityComposableStable.ts b/src/entities/join/composable-stable/addLiquidityComposableStable.ts index db3071b7..67fdf7f6 100644 --- a/src/entities/join/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/join/composable-stable/addLiquidityComposableStable.ts @@ -6,7 +6,7 @@ import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; import { AddLiquidityBase, - JoinBuildOutput, + AddLiquidityBuildOutput, AddLiquidityInput, AddLiquidityKind, AddLiquidityComposableStableQueryResult, @@ -73,7 +73,9 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { }; } - public buildCall(input: AddLiquidityComposableStableCall): JoinBuildOutput { + public buildCall( + input: AddLiquidityComposableStableCall, + ): AddLiquidityBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.addLiquidityKind, amounts); diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index c6404d4d..82c9ec1b 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -95,7 +95,7 @@ export interface AddLiquidityBase { }; } -export type JoinBuildOutput = { +export type AddLiquidityBuildOutput = { call: Hex; to: Address; value: bigint; diff --git a/src/entities/join/weighted/addLiquidityWeighted.ts b/src/entities/join/weighted/addLiquidityWeighted.ts index c0a6f072..4b5cfc95 100644 --- a/src/entities/join/weighted/addLiquidityWeighted.ts +++ b/src/entities/join/weighted/addLiquidityWeighted.ts @@ -7,7 +7,7 @@ import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../utils'; import { vaultAbi } from '../../../abi'; import { AddLiquidityBase, - JoinBuildOutput, + AddLiquidityBuildOutput, AddLiquidityInput, AddLiquidityKind, AddLiquidityWeightedQueryResult, @@ -62,7 +62,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { }; } - public buildCall(input: AddLiquidityWeightedCall): JoinBuildOutput { + public buildCall(input: AddLiquidityWeightedCall): AddLiquidityBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData(input.addLiquidityKind, amounts); diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 22920211..3d751615 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -5,7 +5,7 @@ import { PoolStateInput, Slippage, Address, - JoinBuildOutput, + AddLiquidityBuildOutput, AddLiquidityQueryResult, AddLiquidityUnbalancedInput, BALANCER_VAULT, @@ -24,7 +24,7 @@ import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type JoinResult = { addLiquidityQueryResult: AddLiquidityQueryResult; - joinBuildOutput: JoinBuildOutput; + addLiquidityBuildOutput: AddLiquidityBuildOutput; txOutput: TxResult; }; @@ -41,14 +41,14 @@ async function sdkJoin({ slippage: Slippage; testAddress: Address; }): Promise<{ - joinBuildOutput: JoinBuildOutput; + addLiquidityBuildOutput: AddLiquidityBuildOutput; addLiquidityQueryResult: AddLiquidityQueryResult; }> { const addLiquidityQueryResult = await addLiquidity.query( addLiquidityInput, poolStateInput, ); - const joinBuildOutput = addLiquidity.buildCall({ + const addLiquidityBuildOutput = addLiquidity.buildCall({ ...addLiquidityQueryResult, slippage, sender: testAddress, @@ -56,7 +56,7 @@ async function sdkJoin({ }); return { - joinBuildOutput, + addLiquidityBuildOutput, addLiquidityQueryResult, }; } @@ -117,7 +117,7 @@ export async function doJoin(txInput: JoinTxInput) { slippage, } = txInput; - const { addLiquidityQueryResult, joinBuildOutput } = await sdkJoin({ + const { addLiquidityQueryResult, addLiquidityBuildOutput } = await sdkJoin({ addLiquidity, addLiquidityInput, poolStateInput, @@ -132,14 +132,14 @@ export async function doJoin(txInput: JoinTxInput) { tokens, client, testAddress, - joinBuildOutput.to, - joinBuildOutput.call, - joinBuildOutput.value, + addLiquidityBuildOutput.to, + addLiquidityBuildOutput.call, + addLiquidityBuildOutput.value, ); return { addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, txOutput, }; } @@ -151,7 +151,8 @@ export function assertUnbalancedJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, addLiquidityQueryResult, joinBuildOutput } = joinResult; + const { txOutput, addLiquidityQueryResult, addLiquidityBuildOutput } = + joinResult; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) const expectedAmountsIn = poolStateInput.tokens.map((t) => { @@ -190,10 +191,10 @@ export function assertUnbalancedJoin( // Expect some bpt amount expect(addLiquidityQueryResult.bptOut.amount > 0n).to.be.true; - assertJoinBuildOutput( + assertAddLiquidityBuildOutput( addLiquidityInput, addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, true, slippage, ); @@ -202,7 +203,7 @@ export function assertUnbalancedJoin( poolStateInput, addLiquidityInput, addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, txOutput, ); } @@ -214,7 +215,8 @@ export function assertSingleTokenJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, addLiquidityQueryResult, joinBuildOutput } = joinResult; + const { txOutput, addLiquidityQueryResult, addLiquidityBuildOutput } = + joinResult; if (addLiquidityQueryResult.tokenInIndex === undefined) throw Error('No index'); @@ -264,10 +266,10 @@ export function assertSingleTokenJoin( else expect(a.amount).toEqual(0n); }); - assertJoinBuildOutput( + assertAddLiquidityBuildOutput( addLiquidityInput, addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, false, slippage, ); @@ -276,7 +278,7 @@ export function assertSingleTokenJoin( poolStateInput, addLiquidityInput, addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, txOutput, ); } @@ -288,7 +290,8 @@ export function assertProportionalJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, addLiquidityQueryResult, joinBuildOutput } = joinResult; + const { txOutput, addLiquidityQueryResult, addLiquidityBuildOutput } = + joinResult; const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -321,10 +324,10 @@ export function assertProportionalJoin( else expect(a.amount > 0n).to.be.true; }); - assertJoinBuildOutput( + assertAddLiquidityBuildOutput( addLiquidityInput, addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, false, slippage, ); @@ -333,7 +336,7 @@ export function assertProportionalJoin( poolStateInput, addLiquidityInput, addLiquidityQueryResult, - joinBuildOutput, + addLiquidityBuildOutput, txOutput, ); } @@ -342,7 +345,7 @@ function assertTokenDeltas( poolStateInput: PoolStateInput, addLiquidityInput: AddLiquidityInput, addLiquidityQueryResult: AddLiquidityQueryResult, - joinBuildOutput: JoinBuildOutput, + addLiquidityBuildOutput: AddLiquidityBuildOutput, txOutput: TxResult, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); @@ -365,16 +368,17 @@ function assertTokenDeltas( (a) => a.token.address === zeroAddress, ); expectedDeltas[index] = 0n; - expectedDeltas[expectedDeltas.length - 1] = joinBuildOutput.value; + expectedDeltas[expectedDeltas.length - 1] = + addLiquidityBuildOutput.value; } expect(txOutput.balanceDeltas).to.deep.eq(expectedDeltas); } -function assertJoinBuildOutput( +function assertAddLiquidityBuildOutput( addLiquidityInput: AddLiquidityInput, addLiquidityQueryResult: AddLiquidityQueryResult, - joinBuildOutput: JoinBuildOutput, + addLiquidityBuildOutput: AddLiquidityBuildOutput, isExactIn: boolean, slippage: Slippage, ) { @@ -390,7 +394,7 @@ function assertJoinBuildOutput( ? slippage.removeFrom(addLiquidityQueryResult.bptOut.amount) : addLiquidityQueryResult.bptOut.amount; - const expectedBuildOutput: Omit = { + const expectedBuildOutput: Omit = { maxAmountsIn, minBptOut, to: BALANCER_VAULT, @@ -403,6 +407,6 @@ function assertJoinBuildOutput( }; // rome-ignore lint/correctness/noUnusedVariables: - const { call, ...buildCheck } = joinBuildOutput; + const { call, ...buildCheck } = addLiquidityBuildOutput; expect(buildCheck).to.deep.eq(expectedBuildOutput); } From 0676e28a9641a302636f3ed310d038b4a17834c9 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 12:38:56 -0300 Subject: [PATCH 179/199] Refactor QueryResult into QueryOutput for consistency --- README.md | 8 +- examples/exitPool.ts | 8 +- examples/joinPool.ts | 8 +- .../composable-stable/composableStableExit.ts | 10 +- src/entities/exit/poolExit.ts | 4 +- src/entities/exit/types.ts | 16 +-- src/entities/exit/weighted/weightedExit.ts | 10 +- src/entities/join/addLiquidity.ts | 4 +- .../addLiquidityComposableStable.ts | 10 +- src/entities/join/types.ts | 20 ++-- .../join/weighted/addLiquidityWeighted.ts | 10 +- test/lib/utils/exitHelper.ts | 100 +++++++++-------- test/lib/utils/joinHelper.ts | 104 +++++++++--------- 13 files changed, 155 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index fa0bc918..d32c6ae4 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,10 @@ it can be used for: const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); const addLiquidity = new AddLiquidity(); - const queryResult = await addLiquidity.query(addLiquidityInput, poolState); + const queryOutput = await addLiquidity.query(addLiquidityInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = addLiquidity.buildCall({ - ...queryResult, + ...queryOutput, slippage, sender: signerAddress, recipient: signerAddress, @@ -82,10 +82,10 @@ const exitInput: SingleAssetExitInput = { const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); const poolExit = new PoolExit(); -const queryResult = await poolExit.query(exitInput, poolState); +const queryOutput = await poolExit.query(exitInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = poolExit.buildCall({ - ...queryResult, + ...queryOutput, slippage, sender: signerAddress, recipient: signerAddress, diff --git a/examples/exitPool.ts b/examples/exitPool.ts index 9e4744cd..998c7fd9 100644 --- a/examples/exitPool.ts +++ b/examples/exitPool.ts @@ -58,17 +58,17 @@ const exit = async () => { // Simulate the exit to get the tokens out const poolExit = new PoolExit(); - const queryResult = await poolExit.query(exitInput, poolStateInput); + const queryOutput = await poolExit.query(exitInput, poolStateInput); console.log('\nExit Query Result:'); - console.log(`BPT In: ${queryResult.bptIn.amount.toString()}\nTokens Out:`); - queryResult.amountsOut.map((a) => + console.log(`BPT In: ${queryOutput.bptIn.amount.toString()}\nTokens Out:`); + queryOutput.amountsOut.map((a) => console.log(a.token.address, a.amount.toString()), ); // Apply slippage to the tokens out received from the query and construct the call const call = poolExit.buildCall({ - ...queryResult, + ...queryOutput, slippage, sender: userAccount, recipient: userAccount, diff --git a/examples/joinPool.ts b/examples/joinPool.ts index eaa4f891..a16b73ad 100644 --- a/examples/joinPool.ts +++ b/examples/joinPool.ts @@ -55,21 +55,21 @@ const join = async () => { // Simulate the join to get the amount of BPT out const addLiquidity = new AddLiquidity(); - const queryResult = await addLiquidity.query( + const queryOutput = await addLiquidity.query( addLiquidityInput, poolStateInput, ); console.log('\nJoin Query Result:'); console.log('Tokens In:'); - queryResult.amountsIn.map((a) => + queryOutput.amountsIn.map((a) => console.log(a.token.address, a.amount.toString()), ); - console.log(`BPT Out: ${queryResult.bptOut.amount.toString()}`); + console.log(`BPT Out: ${queryOutput.bptOut.amount.toString()}`); // Apply slippage to the BPT amount received from the query and construct the call const call = addLiquidity.buildCall({ - ...queryResult, + ...queryOutput, slippage, sender: userAccount, recipient: userAccount, diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index d85a15e3..dc22729d 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -15,7 +15,7 @@ import { ExitBuildOutput, ExitInput, ExitKind, - ExitQueryResult, + ExitQueryOutput, } from '../types'; import { AmountsExit, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; @@ -26,7 +26,7 @@ export class ComposableStableExit implements BaseExit { public async query( input: ExitInput, poolState: PoolState, - ): Promise { + ): Promise { const bptIndex = poolState.tokens.findIndex( (t) => t.address === poolState.address, ); @@ -52,15 +52,15 @@ export class ComposableStableExit implements BaseExit { userData, toInternalBalance: !!input.toInternalBalance, }); - const queryResult = await doQueryExit( + const queryOutput = await doQueryExit( input.rpcUrl, input.chainId, args, ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + const bptIn = TokenAmount.fromRawAmount(bpt, queryOutput.bptIn); - const amountsOut = queryResult.amountsOut.map((a, i) => + const amountsOut = queryOutput.amountsOut.map((a, i) => TokenAmount.fromRawAmount(tokensOut[i], a), ); diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 51d46df0..f175cae5 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -4,7 +4,7 @@ import { ExitCall, ExitConfig, ExitInput, - ExitQueryResult, + ExitQueryOutput, } from './types'; import { WeightedExit } from './weighted/weightedExit'; import { PoolStateInput } from '../types'; @@ -37,7 +37,7 @@ export class PoolExit { public async query( input: ExitInput, poolState: PoolStateInput, - ): Promise { + ): Promise { validateInputs(input, poolState); const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 4e7ad17e..f915b8ca 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -38,12 +38,12 @@ export type ExitInput = | SingleAssetExitInput | ProportionalExitInput; -export type ExitQueryResult = - | BaseExitQueryResult - | ComposableStableExitQueryResult; +export type ExitQueryOutput = + | BaseExitQueryOutput + | ComposableStableExitQueryOutput; // Returned from a exit query -export type BaseExitQueryResult = { +export type BaseExitQueryOutput = { poolType: string; poolId: Address; exitKind: ExitKind; @@ -53,7 +53,7 @@ export type BaseExitQueryResult = { toInternalBalance: boolean; }; -export type ComposableStableExitQueryResult = BaseExitQueryResult & { +export type ComposableStableExitQueryOutput = BaseExitQueryOutput & { bptIndex: number; }; @@ -63,8 +63,8 @@ type BaseExitCall = { recipient: Address; }; export type ComposableStableExitCall = BaseExitCall & - ComposableStableExitQueryResult; -export type WeightedExitCall = BaseExitCall & BaseExitQueryResult; + ComposableStableExitQueryOutput; +export type WeightedExitCall = BaseExitCall & BaseExitQueryOutput; export type ExitCall = ComposableStableExitCall | WeightedExitCall; @@ -77,7 +77,7 @@ export type ExitBuildOutput = { }; export interface BaseExit { - query(input: ExitInput, poolState: PoolState): Promise; + query(input: ExitInput, poolState: PoolState): Promise; buildCall(input: ExitCall): ExitBuildOutput; } diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 46c56e43..0d89d79d 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -16,7 +16,7 @@ import { ExitCall, ExitInput, ExitKind, - ExitQueryResult, + ExitQueryOutput, WeightedExitCall, } from '../types'; import { AmountsExit, PoolState } from '../../types'; @@ -27,7 +27,7 @@ export class WeightedExit implements BaseExit { public async query( input: ExitInput, poolState: PoolState, - ): Promise { + ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); const userData = this.encodeUserData(input.kind, amounts); @@ -45,16 +45,16 @@ export class WeightedExit implements BaseExit { toInternalBalance: !!input.toInternalBalance, }); - const queryResult = await doQueryExit( + const queryOutput = await doQueryExit( input.rpcUrl, input.chainId, args, ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptIn = TokenAmount.fromRawAmount(bpt, queryResult.bptIn); + const bptIn = TokenAmount.fromRawAmount(bpt, queryOutput.bptIn); - const amountsOut = queryResult.amountsOut.map((a, i) => + const amountsOut = queryOutput.amountsOut.map((a, i) => TokenAmount.fromRawAmount(tokensOut[i], a), ); diff --git a/src/entities/join/addLiquidity.ts b/src/entities/join/addLiquidity.ts index 5695072d..bca11619 100644 --- a/src/entities/join/addLiquidity.ts +++ b/src/entities/join/addLiquidity.ts @@ -3,7 +3,7 @@ import { AddLiquidityBuildOutput, AddLiquidityConfig, AddLiquidityInput, - AddLiquidityQueryResult, + AddLiquidityQueryOutput, AddLiquidityCall, } from './types'; import { AddLiquidityWeighted } from './weighted/addLiquidityWeighted'; @@ -37,7 +37,7 @@ export class AddLiquidity { public async query( input: AddLiquidityInput, poolState: PoolStateInput, - ): Promise { + ): Promise { validateInputs(input, poolState); const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); diff --git a/src/entities/join/composable-stable/addLiquidityComposableStable.ts b/src/entities/join/composable-stable/addLiquidityComposableStable.ts index 67fdf7f6..d77a144a 100644 --- a/src/entities/join/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/join/composable-stable/addLiquidityComposableStable.ts @@ -9,7 +9,7 @@ import { AddLiquidityBuildOutput, AddLiquidityInput, AddLiquidityKind, - AddLiquidityComposableStableQueryResult, + AddLiquidityComposableStableQueryOutput, AddLiquidityComposableStableCall, } from '../types'; import { @@ -27,7 +27,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { public async query( input: AddLiquidityInput, poolState: PoolState, - ): Promise { + ): Promise { const bptIndex = poolState.tokens.findIndex( (t) => t.address === poolState.address, ); @@ -48,16 +48,16 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { fromInternalBalance: input.fromInternalBalance ?? false, }); - const queryResult = await doQueryJoin( + const queryOutput = await doQueryJoin( input.rpcUrl, input.chainId, args, ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); + const bptOut = TokenAmount.fromRawAmount(bpt, queryOutput.bptOut); - const amountsIn = queryResult.amountsIn.map((a, i) => + const amountsIn = queryOutput.amountsIn.map((a, i) => TokenAmount.fromRawAmount(tokensIn[i], a), ); diff --git a/src/entities/join/types.ts b/src/entities/join/types.ts index 82c9ec1b..ffb2f2ca 100644 --- a/src/entities/join/types.ts +++ b/src/entities/join/types.ts @@ -45,7 +45,7 @@ export type AddLiquidityInput = | AddLiquiditySingleAssetInput | AddLiquidityProportionalInput; -type AddLiquidityBaseQueryResult = { +type AddLiquidityBaseQueryOutput = { poolType: string; poolId: Hex; addLiquidityKind: AddLiquidityKind; @@ -55,16 +55,16 @@ type AddLiquidityBaseQueryResult = { tokenInIndex?: number; }; -export type AddLiquidityWeightedQueryResult = AddLiquidityBaseQueryResult; +export type AddLiquidityWeightedQueryOutput = AddLiquidityBaseQueryOutput; -export type AddLiquidityComposableStableQueryResult = - AddLiquidityBaseQueryResult & { +export type AddLiquidityComposableStableQueryOutput = + AddLiquidityBaseQueryOutput & { bptIndex: number; }; -export type AddLiquidityQueryResult = - | AddLiquidityWeightedQueryResult - | AddLiquidityComposableStableQueryResult; +export type AddLiquidityQueryOutput = + | AddLiquidityWeightedQueryOutput + | AddLiquidityComposableStableQueryOutput; type AddLiquidityBaseCall = { slippage: Slippage; @@ -73,9 +73,9 @@ type AddLiquidityBaseCall = { }; export type AddLiquidityComposableStableCall = AddLiquidityBaseCall & - AddLiquidityComposableStableQueryResult; + AddLiquidityComposableStableQueryOutput; export type AddLiquidityWeightedCall = AddLiquidityBaseCall & - AddLiquidityBaseQueryResult; + AddLiquidityBaseQueryOutput; export type AddLiquidityCall = | AddLiquidityWeightedCall @@ -85,7 +85,7 @@ export interface AddLiquidityBase { query( input: AddLiquidityInput, poolState: PoolState, - ): Promise; + ): Promise; buildCall(input: AddLiquidityCall): { call: Hex; to: Address; diff --git a/src/entities/join/weighted/addLiquidityWeighted.ts b/src/entities/join/weighted/addLiquidityWeighted.ts index 4b5cfc95..c4a4a1b5 100644 --- a/src/entities/join/weighted/addLiquidityWeighted.ts +++ b/src/entities/join/weighted/addLiquidityWeighted.ts @@ -10,7 +10,7 @@ import { AddLiquidityBuildOutput, AddLiquidityInput, AddLiquidityKind, - AddLiquidityWeightedQueryResult, + AddLiquidityWeightedQueryOutput, AddLiquidityWeightedCall, } from '../types'; import { AddLiquidityAmounts, PoolState } from '../../types'; @@ -20,7 +20,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { public async query( input: AddLiquidityInput, poolState: PoolState, - ): Promise { + ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); const userData = this.encodeUserData(input.kind, amounts); @@ -38,16 +38,16 @@ export class AddLiquidityWeighted implements AddLiquidityBase { fromInternalBalance: input.fromInternalBalance ?? false, }); - const queryResult = await doQueryJoin( + const queryOutput = await doQueryJoin( input.rpcUrl, input.chainId, args, ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); + const bptOut = TokenAmount.fromRawAmount(bpt, queryOutput.bptOut); - const amountsIn = queryResult.amountsIn.map((a, i) => + const amountsIn = queryOutput.amountsIn.map((a, i) => TokenAmount.fromRawAmount(tokensIn[i], a), ); diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 5dd72cec..9d6abe9f 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -1,9 +1,9 @@ import { ExitTxInput } from './types'; import { ChainId, - ComposableStableExitQueryResult, + ComposableStableExitQueryOutput, ExitBuildOutput, - ExitQueryResult, + ExitQueryOutput, NATIVE_ASSETS, PoolStateInput, TokenAmount, @@ -21,7 +21,7 @@ import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type ExitResult = { - exitQueryResult: ExitQueryResult; + exitQueryOutput: ExitQueryOutput; exitBuildOutput: ExitBuildOutput; txOutput: TxResult; }; @@ -34,11 +34,11 @@ export const sdkExit = async ({ testAddress, }: Omit): Promise<{ exitBuildOutput: ExitBuildOutput; - exitQueryResult: ExitQueryResult; + exitQueryOutput: ExitQueryOutput; }> => { - const exitQueryResult = await poolExit.query(exitInput, poolStateInput); + const exitQueryOutput = await poolExit.query(exitInput, poolStateInput); const exitBuildOutput = poolExit.buildCall({ - ...exitQueryResult, + ...exitQueryOutput, slippage, sender: testAddress, recipient: testAddress, @@ -46,28 +46,26 @@ export const sdkExit = async ({ return { exitBuildOutput, - exitQueryResult, + exitQueryOutput, }; }; - - -function isComposableStableExitQueryResult(result: ExitQueryResult): boolean { - return (result as ComposableStableExitQueryResult).bptIndex !== undefined; +function isComposableStableExitQueryOutput(result: ExitQueryOutput): boolean { + return (result as ComposableStableExitQueryOutput).bptIndex !== undefined; } -function getCheck(result: ExitQueryResult, isExactIn: boolean) { - if (isComposableStableExitQueryResult(result)) { +function getCheck(result: ExitQueryOutput, isExactIn: boolean) { + if (isComposableStableExitQueryOutput(result)) { if (isExactIn) { // Using this destructuring to return only the fields of interest // rome-ignore lint/correctness/noUnusedVariables: const { amountsOut, bptIndex, ...check } = - result as ComposableStableExitQueryResult; + result as ComposableStableExitQueryOutput; return check; } else { // rome-ignore lint/correctness/noUnusedVariables: const { bptIn, bptIndex, ...check } = - result as ComposableStableExitQueryResult; + result as ComposableStableExitQueryOutput; return check; } } else { @@ -103,7 +101,7 @@ export async function doExit(txInput: ExitTxInput) { slippage, } = txInput; - const { exitQueryResult, exitBuildOutput } = await sdkExit({ + const { exitQueryOutput, exitBuildOutput } = await sdkExit({ poolExit, exitInput, poolStateInput, @@ -125,7 +123,7 @@ export async function doExit(txInput: ExitTxInput) { ); return { - exitQueryResult, + exitQueryOutput, exitBuildOutput, txOutput, }; @@ -138,7 +136,7 @@ export function assertUnbalancedExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + const { txOutput, exitQueryOutput, exitBuildOutput } = exitResult; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) const expectedAmountsOut = poolStateInput.tokens.map((t) => { @@ -154,7 +152,7 @@ export function assertUnbalancedExit( else return TokenAmount.fromRawAmount(token, input.rawAmount); }); - const expectedQueryResult: Omit = { + const expectedQueryOutput: Omit = { // Query should use same amountsOut as input amountsOut: expectedAmountsOut, tokenOutIndex: undefined, @@ -165,16 +163,16 @@ export function assertUnbalancedExit( exitKind: exitInput.kind, }; - const queryCheck = getCheck(exitQueryResult, false); + const queryCheck = getCheck(exitQueryOutput, false); - expect(queryCheck).to.deep.eq(expectedQueryResult); + expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect some bpt amount - expect(exitQueryResult.bptIn.amount > 0n).to.be.true; + expect(exitQueryOutput.bptIn.amount > 0n).to.be.true; - assertExitBuildOutput(exitQueryResult, exitBuildOutput, false, slippage); + assertExitBuildOutput(exitQueryOutput, exitBuildOutput, false, slippage); - assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); + assertTokenDeltas(poolStateInput, exitInput, exitQueryOutput, txOutput); } export function assertSingleTokenExit( @@ -184,9 +182,9 @@ export function assertSingleTokenExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + const { txOutput, exitQueryOutput, exitBuildOutput } = exitResult; - if (exitQueryResult.tokenOutIndex === undefined) throw Error('No index'); + if (exitQueryOutput.tokenOutIndex === undefined) throw Error('No index'); const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -194,8 +192,8 @@ export function assertSingleTokenExit( (t) => t.address !== poolStateInput.address, ); - const expectedQueryResult: Omit< - ExitQueryResult, + const expectedQueryOutput: Omit< + ExitQueryOutput, 'amountsOut' | 'bptIndex' > = { // Query should use same bpt out as user sets @@ -210,13 +208,13 @@ export function assertSingleTokenExit( exitKind: exitInput.kind, }; - const queryCheck = getCheck(exitQueryResult, true); + const queryCheck = getCheck(exitQueryOutput, true); - expect(queryCheck).to.deep.eq(expectedQueryResult); + expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect only tokenOut to have amount > 0 - // (Note exitQueryResult also has value for bpt if pre-minted) - exitQueryResult.amountsOut.forEach((a) => { + // (Note exitQueryOutput also has value for bpt if pre-minted) + exitQueryOutput.amountsOut.forEach((a) => { if ( !exitInput.exitWithNativeAsset && a.token.address === exitInput.tokenOut @@ -230,9 +228,9 @@ export function assertSingleTokenExit( else expect(a.amount).toEqual(0n); }); - assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); + assertExitBuildOutput(exitQueryOutput, exitBuildOutput, true, slippage); - assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); + assertTokenDeltas(poolStateInput, exitInput, exitQueryOutput, txOutput); } export function assertProportionalExit( @@ -242,12 +240,12 @@ export function assertProportionalExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + const { txOutput, exitQueryOutput, exitBuildOutput } = exitResult; const bptToken = new Token(chainId, poolStateInput.address, 18); - const expectedQueryResult: Omit< - ExitQueryResult, + const expectedQueryOutput: Omit< + ExitQueryOutput, 'amountsOut' | 'bptIndex' > = { // Query should use same bpt out as user sets @@ -261,39 +259,39 @@ export function assertProportionalExit( exitKind: exitInput.kind, }; - const queryCheck = getCheck(exitQueryResult, true); + const queryCheck = getCheck(exitQueryOutput, true); - expect(queryCheck).to.deep.eq(expectedQueryResult); + expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect all assets in to have an amount > 0 apart from BPT if it exists - exitQueryResult.amountsOut.forEach((a) => { + exitQueryOutput.amountsOut.forEach((a) => { if (a.token.address === poolStateInput.address) expect(a.amount).toEqual(0n); else expect(a.amount > 0n).to.be.true; }); - assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); + assertExitBuildOutput(exitQueryOutput, exitBuildOutput, true, slippage); - assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); + assertTokenDeltas(poolStateInput, exitInput, exitQueryOutput, txOutput); } function assertTokenDeltas( poolStateInput: PoolStateInput, exitInput: ExitInput, - exitQueryResult: ExitQueryResult, + exitQueryOutput: ExitQueryOutput, txOutput: TxResult, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); - // exitQueryResult amountsOut will have a value for the BPT token if it is a pre-minted pool - const amountsWithoutBpt = [...exitQueryResult.amountsOut].filter( + // exitQueryOutput amountsOut will have a value for the BPT token if it is a pre-minted pool + const amountsWithoutBpt = [...exitQueryOutput.amountsOut].filter( (t) => t.token.address !== poolStateInput.address, ); // Matching order of getTokens helper: [poolTokens, BPT, native] const expectedDeltas = [ ...amountsWithoutBpt.map((a) => a.amount), - exitQueryResult.bptIn.amount, + exitQueryOutput.bptIn.amount, 0n, ]; @@ -310,20 +308,20 @@ function assertTokenDeltas( } function assertExitBuildOutput( - exitQueryResult: ExitQueryResult, + exitQueryOutput: ExitQueryOutput, exitBuildOutput: ExitBuildOutput, isExactIn: boolean, slippage: Slippage, ) { // if exactIn minAmountsOut should use amountsOut with slippage applied, else should use same amountsOut as input const minAmountsOut = isExactIn - ? exitQueryResult.amountsOut.map((a) => slippage.removeFrom(a.amount)) - : exitQueryResult.amountsOut.map((a) => a.amount); + ? exitQueryOutput.amountsOut.map((a) => slippage.removeFrom(a.amount)) + : exitQueryOutput.amountsOut.map((a) => a.amount); // if exactIn slippage cannot be applied to bptIn, else should use bptIn with slippage applied const maxBptIn = isExactIn - ? exitQueryResult.bptIn.amount - : slippage.applyTo(exitQueryResult.bptIn.amount); + ? exitQueryOutput.bptIn.amount + : slippage.applyTo(exitQueryOutput.bptIn.amount); const expectedBuildOutput: Omit = { minAmountsOut, diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index 3d751615..a87a1c8b 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -6,7 +6,7 @@ import { Slippage, Address, AddLiquidityBuildOutput, - AddLiquidityQueryResult, + AddLiquidityQueryOutput, AddLiquidityUnbalancedInput, BALANCER_VAULT, AddLiquiditySingleAssetInput, @@ -14,7 +14,7 @@ import { Token, ChainId, TokenAmount, - AddLiquidityComposableStableQueryResult, + AddLiquidityComposableStableQueryOutput, NATIVE_ASSETS, } from '../../../src'; import { TxResult, sendTransactionGetBalances } from './helper'; @@ -23,7 +23,7 @@ import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type JoinResult = { - addLiquidityQueryResult: AddLiquidityQueryResult; + addLiquidityQueryOutput: AddLiquidityQueryOutput; addLiquidityBuildOutput: AddLiquidityBuildOutput; txOutput: TxResult; }; @@ -42,14 +42,14 @@ async function sdkJoin({ testAddress: Address; }): Promise<{ addLiquidityBuildOutput: AddLiquidityBuildOutput; - addLiquidityQueryResult: AddLiquidityQueryResult; + addLiquidityQueryOutput: AddLiquidityQueryOutput; }> { - const addLiquidityQueryResult = await addLiquidity.query( + const addLiquidityQueryOutput = await addLiquidity.query( addLiquidityInput, poolStateInput, ); const addLiquidityBuildOutput = addLiquidity.buildCall({ - ...addLiquidityQueryResult, + ...addLiquidityQueryOutput, slippage, sender: testAddress, recipient: testAddress, @@ -57,31 +57,31 @@ async function sdkJoin({ return { addLiquidityBuildOutput, - addLiquidityQueryResult, + addLiquidityQueryOutput, }; } -function isAddLiquidityComposableStableQueryResult( - result: AddLiquidityQueryResult, +function isAddLiquidityComposableStableQueryOutput( + result: AddLiquidityQueryOutput, ): boolean { return ( - (result as AddLiquidityComposableStableQueryResult).bptIndex !== + (result as AddLiquidityComposableStableQueryOutput).bptIndex !== undefined ); } -function getCheck(result: AddLiquidityQueryResult, isExactIn: boolean) { - if (isAddLiquidityComposableStableQueryResult(result)) { +function getCheck(result: AddLiquidityQueryOutput, isExactIn: boolean) { + if (isAddLiquidityComposableStableQueryOutput(result)) { if (isExactIn) { // Using this destructuring to return only the fields of interest // rome-ignore lint/correctness/noUnusedVariables: const { bptOut, bptIndex, ...check } = - result as AddLiquidityComposableStableQueryResult; + result as AddLiquidityComposableStableQueryOutput; return check; } else { // rome-ignore lint/correctness/noUnusedVariables: const { amountsIn, bptIndex, ...check } = - result as AddLiquidityComposableStableQueryResult; + result as AddLiquidityComposableStableQueryOutput; return check; } } else { @@ -117,7 +117,7 @@ export async function doJoin(txInput: JoinTxInput) { slippage, } = txInput; - const { addLiquidityQueryResult, addLiquidityBuildOutput } = await sdkJoin({ + const { addLiquidityQueryOutput, addLiquidityBuildOutput } = await sdkJoin({ addLiquidity, addLiquidityInput, poolStateInput, @@ -138,7 +138,7 @@ export async function doJoin(txInput: JoinTxInput) { ); return { - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, txOutput, }; @@ -151,7 +151,7 @@ export function assertUnbalancedJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, addLiquidityQueryResult, addLiquidityBuildOutput } = + const { txOutput, addLiquidityQueryOutput, addLiquidityBuildOutput } = joinResult; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) @@ -170,8 +170,8 @@ export function assertUnbalancedJoin( else return TokenAmount.fromRawAmount(token, input.rawAmount); }); - const expectedQueryResult: Omit< - AddLiquidityQueryResult, + const expectedQueryOutput: Omit< + AddLiquidityQueryOutput, 'bptOut' | 'bptIndex' > = { // Query should use same amountsIn as input @@ -184,16 +184,16 @@ export function assertUnbalancedJoin( addLiquidityKind: addLiquidityInput.kind, }; - const queryCheck = getCheck(addLiquidityQueryResult, true); + const queryCheck = getCheck(addLiquidityQueryOutput, true); - expect(queryCheck).to.deep.eq(expectedQueryResult); + expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect some bpt amount - expect(addLiquidityQueryResult.bptOut.amount > 0n).to.be.true; + expect(addLiquidityQueryOutput.bptOut.amount > 0n).to.be.true; assertAddLiquidityBuildOutput( addLiquidityInput, - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, true, slippage, @@ -202,7 +202,7 @@ export function assertUnbalancedJoin( assertTokenDeltas( poolStateInput, addLiquidityInput, - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, txOutput, ); @@ -215,10 +215,10 @@ export function assertSingleTokenJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, addLiquidityQueryResult, addLiquidityBuildOutput } = + const { txOutput, addLiquidityQueryOutput, addLiquidityBuildOutput } = joinResult; - if (addLiquidityQueryResult.tokenInIndex === undefined) + if (addLiquidityQueryOutput.tokenInIndex === undefined) throw Error('No index'); const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -227,8 +227,8 @@ export function assertSingleTokenJoin( (t) => t.address !== poolStateInput.address, ); - const expectedQueryResult: Omit< - AddLiquidityQueryResult, + const expectedQueryOutput: Omit< + AddLiquidityQueryOutput, 'amountsIn' | 'bptIndex' > = { // Query should use same bpt out as user sets @@ -246,13 +246,13 @@ export function assertSingleTokenJoin( addLiquidityKind: addLiquidityInput.kind, }; - const queryCheck = getCheck(addLiquidityQueryResult, false); + const queryCheck = getCheck(addLiquidityQueryOutput, false); - expect(queryCheck).to.deep.eq(expectedQueryResult); + expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect only tokenIn to have amount > 0 - // (Note addLiquidityQueryResult also has value for bpt if pre-minted) - addLiquidityQueryResult.amountsIn.forEach((a) => { + // (Note addLiquidityQueryOutput also has value for bpt if pre-minted) + addLiquidityQueryOutput.amountsIn.forEach((a) => { if ( !addLiquidityInput.useNativeAssetAsWrappedAmountIn && a.token.address === addLiquidityInput.tokenIn @@ -268,7 +268,7 @@ export function assertSingleTokenJoin( assertAddLiquidityBuildOutput( addLiquidityInput, - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, false, slippage, @@ -277,7 +277,7 @@ export function assertSingleTokenJoin( assertTokenDeltas( poolStateInput, addLiquidityInput, - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, txOutput, ); @@ -290,13 +290,13 @@ export function assertProportionalJoin( joinResult: JoinResult, slippage: Slippage, ) { - const { txOutput, addLiquidityQueryResult, addLiquidityBuildOutput } = + const { txOutput, addLiquidityQueryOutput, addLiquidityBuildOutput } = joinResult; const bptToken = new Token(chainId, poolStateInput.address, 18); - const expectedQueryResult: Omit< - AddLiquidityQueryResult, + const expectedQueryOutput: Omit< + AddLiquidityQueryOutput, 'amountsIn' | 'bptIndex' > = { // Query should use same bpt out as user sets @@ -313,12 +313,12 @@ export function assertProportionalJoin( addLiquidityKind: addLiquidityInput.kind, }; - const queryCheck = getCheck(addLiquidityQueryResult, false); + const queryCheck = getCheck(addLiquidityQueryOutput, false); - expect(queryCheck).to.deep.eq(expectedQueryResult); + expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect all assets in to have an amount > 0 apart from BPT if it exists - addLiquidityQueryResult.amountsIn.forEach((a) => { + addLiquidityQueryOutput.amountsIn.forEach((a) => { if (a.token.address === poolStateInput.address) expect(a.amount).toEqual(0n); else expect(a.amount > 0n).to.be.true; @@ -326,7 +326,7 @@ export function assertProportionalJoin( assertAddLiquidityBuildOutput( addLiquidityInput, - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, false, slippage, @@ -335,7 +335,7 @@ export function assertProportionalJoin( assertTokenDeltas( poolStateInput, addLiquidityInput, - addLiquidityQueryResult, + addLiquidityQueryOutput, addLiquidityBuildOutput, txOutput, ); @@ -344,21 +344,21 @@ export function assertProportionalJoin( function assertTokenDeltas( poolStateInput: PoolStateInput, addLiquidityInput: AddLiquidityInput, - addLiquidityQueryResult: AddLiquidityQueryResult, + addLiquidityQueryOutput: AddLiquidityQueryOutput, addLiquidityBuildOutput: AddLiquidityBuildOutput, txOutput: TxResult, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); - // addLiquidityQueryResult amountsIn will have a value for the BPT token if it is a pre-minted pool - const amountsWithoutBpt = [...addLiquidityQueryResult.amountsIn].filter( + // addLiquidityQueryOutput amountsIn will have a value for the BPT token if it is a pre-minted pool + const amountsWithoutBpt = [...addLiquidityQueryOutput.amountsIn].filter( (t) => t.token.address !== poolStateInput.address, ); // Matching order of getTokens helper: [poolTokens, BPT, native] const expectedDeltas = [ ...amountsWithoutBpt.map((a) => a.amount), - addLiquidityQueryResult.bptOut.amount, + addLiquidityQueryOutput.bptOut.amount, 0n, ]; @@ -377,22 +377,22 @@ function assertTokenDeltas( function assertAddLiquidityBuildOutput( addLiquidityInput: AddLiquidityInput, - addLiquidityQueryResult: AddLiquidityQueryResult, + addLiquidityQueryOutput: AddLiquidityQueryOutput, addLiquidityBuildOutput: AddLiquidityBuildOutput, isExactIn: boolean, slippage: Slippage, ) { // if exactIn maxAmountsIn should use same amountsIn as input else slippage should be applied const maxAmountsIn = isExactIn - ? addLiquidityQueryResult.amountsIn.map((a) => a.amount) - : addLiquidityQueryResult.amountsIn.map((a) => + ? addLiquidityQueryOutput.amountsIn.map((a) => a.amount) + : addLiquidityQueryOutput.amountsIn.map((a) => slippage.applyTo(a.amount), ); // if exactIn slippage should be applied to bptOut else should use same bptOut as input const minBptOut = isExactIn - ? slippage.removeFrom(addLiquidityQueryResult.bptOut.amount) - : addLiquidityQueryResult.bptOut.amount; + ? slippage.removeFrom(addLiquidityQueryOutput.bptOut.amount) + : addLiquidityQueryOutput.bptOut.amount; const expectedBuildOutput: Omit = { maxAmountsIn, @@ -400,7 +400,7 @@ function assertAddLiquidityBuildOutput( to: BALANCER_VAULT, // Value should equal value of any wrapped asset if using native value: addLiquidityInput.useNativeAssetAsWrappedAmountIn - ? (addLiquidityQueryResult.amountsIn.find( + ? (addLiquidityQueryOutput.amountsIn.find( (a) => a.token.address === zeroAddress, )?.amount as bigint) : 0n, From 3a014d74dbf280b034e44fa61da17a9d9517df14 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 14:13:09 -0300 Subject: [PATCH 180/199] Refactor doQueryJoin and parseJoinArgs to doAddLiquidityQuery and parseAddLiquidityArgs --- .../{join => addLiquidity}/addLiquidity.ts | 0 .../addLiquidityComposableStable.ts | 16 ++++++++++------ src/entities/{join => addLiquidity}/index.ts | 0 src/entities/{join => addLiquidity}/types.ts | 0 .../utils/validateInputs.ts | 0 .../weighted/addLiquidityWeighted.ts | 12 ++++++++---- src/entities/index.ts | 2 +- .../{doQueryJoin.ts => doAddLiquidityQuery.ts} | 2 +- src/entities/utils/index.ts | 4 ++-- ...parseJoinArgs.ts => parseAddLiquidityArgs.ts} | 2 +- 10 files changed, 23 insertions(+), 15 deletions(-) rename src/entities/{join => addLiquidity}/addLiquidity.ts (100%) rename src/entities/{join => addLiquidity}/composable-stable/addLiquidityComposableStable.ts (95%) rename src/entities/{join => addLiquidity}/index.ts (100%) rename src/entities/{join => addLiquidity}/types.ts (100%) rename src/entities/{join => addLiquidity}/utils/validateInputs.ts (100%) rename src/entities/{join => addLiquidity}/weighted/addLiquidityWeighted.ts (96%) rename src/entities/utils/{doQueryJoin.ts => doAddLiquidityQuery.ts} (95%) rename src/entities/utils/{parseJoinArgs.ts => parseAddLiquidityArgs.ts} (96%) diff --git a/src/entities/join/addLiquidity.ts b/src/entities/addLiquidity/addLiquidity.ts similarity index 100% rename from src/entities/join/addLiquidity.ts rename to src/entities/addLiquidity/addLiquidity.ts diff --git a/src/entities/join/composable-stable/addLiquidityComposableStable.ts b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts similarity index 95% rename from src/entities/join/composable-stable/addLiquidityComposableStable.ts rename to src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts index d77a144a..0176d462 100644 --- a/src/entities/join/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts @@ -16,7 +16,11 @@ import { AddLiquidityAmounts as AddLiquidityAmountsBase, PoolState, } from '../../types'; -import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; +import { + doAddLiquidityQuery, + getAmounts, + parseAddLiquidityArgs, +} from '../../utils'; import { ComposableStableEncoder } from '../../encoders/composableStable'; type AddLiquidityAmounts = AddLiquidityAmountsBase & { @@ -35,7 +39,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { const userData = this.encodeUserData(input.kind, amounts); - const { args, tokensIn } = parseJoinArgs({ + const { args, tokensIn } = parseAddLiquidityArgs({ useNativeAssetAsWrappedAmountIn: !!input.useNativeAssetAsWrappedAmountIn, chainId: input.chainId, @@ -48,16 +52,16 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { fromInternalBalance: input.fromInternalBalance ?? false, }); - const queryOutput = await doQueryJoin( + const queryResult = await doAddLiquidityQuery( input.rpcUrl, input.chainId, args, ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptOut = TokenAmount.fromRawAmount(bpt, queryOutput.bptOut); + const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); - const amountsIn = queryOutput.amountsIn.map((a, i) => + const amountsIn = queryResult.amountsIn.map((a, i) => TokenAmount.fromRawAmount(tokensIn[i], a), ); @@ -80,7 +84,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { const userData = this.encodeUserData(input.addLiquidityKind, amounts); - const { args } = parseJoinArgs({ + const { args } = parseAddLiquidityArgs({ ...input, sortedTokens: input.amountsIn.map((a) => a.token), maxAmountsIn: amounts.maxAmountsIn, diff --git a/src/entities/join/index.ts b/src/entities/addLiquidity/index.ts similarity index 100% rename from src/entities/join/index.ts rename to src/entities/addLiquidity/index.ts diff --git a/src/entities/join/types.ts b/src/entities/addLiquidity/types.ts similarity index 100% rename from src/entities/join/types.ts rename to src/entities/addLiquidity/types.ts diff --git a/src/entities/join/utils/validateInputs.ts b/src/entities/addLiquidity/utils/validateInputs.ts similarity index 100% rename from src/entities/join/utils/validateInputs.ts rename to src/entities/addLiquidity/utils/validateInputs.ts diff --git a/src/entities/join/weighted/addLiquidityWeighted.ts b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts similarity index 96% rename from src/entities/join/weighted/addLiquidityWeighted.ts rename to src/entities/addLiquidity/weighted/addLiquidityWeighted.ts index c4a4a1b5..9d433648 100644 --- a/src/entities/join/weighted/addLiquidityWeighted.ts +++ b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts @@ -14,7 +14,11 @@ import { AddLiquidityWeightedCall, } from '../types'; import { AddLiquidityAmounts, PoolState } from '../../types'; -import { doQueryJoin, getAmounts, parseJoinArgs } from '../../utils'; +import { + doAddLiquidityQuery, + getAmounts, + parseAddLiquidityArgs, +} from '../../utils'; export class AddLiquidityWeighted implements AddLiquidityBase { public async query( @@ -25,7 +29,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { const userData = this.encodeUserData(input.kind, amounts); - const { args, tokensIn } = parseJoinArgs({ + const { args, tokensIn } = parseAddLiquidityArgs({ useNativeAssetAsWrappedAmountIn: !!input.useNativeAssetAsWrappedAmountIn, chainId: input.chainId, @@ -38,7 +42,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { fromInternalBalance: input.fromInternalBalance ?? false, }); - const queryOutput = await doQueryJoin( + const queryOutput = await doAddLiquidityQuery( input.rpcUrl, input.chainId, args, @@ -67,7 +71,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { const userData = this.encodeUserData(input.addLiquidityKind, amounts); - const { args } = parseJoinArgs({ + const { args } = parseAddLiquidityArgs({ ...input, sortedTokens: input.amountsIn.map((a) => a.token), maxAmountsIn: amounts.maxAmountsIn, diff --git a/src/entities/index.ts b/src/entities/index.ts index c626c3bb..033c4619 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,5 +1,5 @@ export * from './encoders'; -export * from './join'; +export * from './addLiquidity'; export * from './exit'; export * from './path'; export * from './swap'; diff --git a/src/entities/utils/doQueryJoin.ts b/src/entities/utils/doAddLiquidityQuery.ts similarity index 95% rename from src/entities/utils/doQueryJoin.ts rename to src/entities/utils/doAddLiquidityQuery.ts index b2ed6635..d328f7be 100644 --- a/src/entities/utils/doQueryJoin.ts +++ b/src/entities/utils/doAddLiquidityQuery.ts @@ -3,7 +3,7 @@ import { Address } from '../../types'; import { BALANCER_HELPERS, CHAINS } from '../../utils'; import { balancerHelpersAbi } from '../../abi'; -export async function doQueryJoin( +export async function doAddLiquidityQuery( rpcUrl: string, chainId: number, args: readonly [ diff --git a/src/entities/utils/index.ts b/src/entities/utils/index.ts index 17362bd0..ed6f7e2f 100644 --- a/src/entities/utils/index.ts +++ b/src/entities/utils/index.ts @@ -1,5 +1,5 @@ -export * from './doQueryJoin'; +export * from './doAddLiquidityQuery'; export * from './getAmounts'; export * from './getSortedTokens'; -export * from './parseJoinArgs'; +export * from './parseAddLiquidityArgs'; export * from './replaceWrapped'; diff --git a/src/entities/utils/parseJoinArgs.ts b/src/entities/utils/parseAddLiquidityArgs.ts similarity index 96% rename from src/entities/utils/parseJoinArgs.ts rename to src/entities/utils/parseAddLiquidityArgs.ts index 8611a2ff..77051a08 100644 --- a/src/entities/utils/parseJoinArgs.ts +++ b/src/entities/utils/parseAddLiquidityArgs.ts @@ -2,7 +2,7 @@ import { Address, Hex } from '../../types'; import { Token } from '../token'; import { replaceWrapped } from './replaceWrapped'; -export function parseJoinArgs({ +export function parseAddLiquidityArgs({ useNativeAssetAsWrappedAmountIn, chainId, sortedTokens, From 50c69344130954aa9084a0a560bc028cd56160fa Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 14:34:39 -0300 Subject: [PATCH 181/199] Refactor join into addLiquidity within tests --- test/composableStableJoin.integration.test.ts | 48 +++++++++---------- test/lib/utils/joinHelper.ts | 41 ++++++++-------- test/lib/utils/types.ts | 2 +- test/weightedJoin.integration.test.ts | 48 +++++++++---------- 4 files changed, 70 insertions(+), 69 deletions(-) diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 30320e08..fdf0fb5f 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -29,12 +29,12 @@ import { InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { JoinTxInput } from './lib/utils/types'; +import { AddLiquidityTxInput } from './lib/utils/types'; import { - doJoin, - assertUnbalancedJoin, - assertSingleTokenJoin, - assertProportionalJoin, + doAddLiquidity, + assertAddLiquidityUnbalanced, + assertAddLiquiditySingleToken, + assertAddLiquidityProportional, } from './lib/utils/joinHelper'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; @@ -44,7 +44,7 @@ const poolId = '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool describe('composable stable join test', () => { - let txInput: JoinTxInput; + let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; beforeAll(async () => { @@ -111,15 +111,15 @@ describe('composable stable join test', () => { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertUnbalancedJoin( + assertAddLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -130,15 +130,15 @@ describe('composable stable join test', () => { amountsIn, useNativeAssetAsWrappedAmountIn: true, }; - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertUnbalancedJoin( + assertAddLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -162,16 +162,16 @@ describe('composable stable join test', () => { }; }); test('with token', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput: input, }); - assertSingleTokenJoin( + assertAddLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, input, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -181,16 +181,16 @@ describe('composable stable join test', () => { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertSingleTokenJoin( + assertAddLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -212,16 +212,16 @@ describe('composable stable join test', () => { }; }); test('with tokens', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput: input, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -230,15 +230,15 @@ describe('composable stable join test', () => { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/joinHelper.ts index a87a1c8b..6b547e65 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/joinHelper.ts @@ -18,17 +18,17 @@ import { NATIVE_ASSETS, } from '../../../src'; import { TxResult, sendTransactionGetBalances } from './helper'; -import { JoinTxInput } from './types'; +import { AddLiquidityTxInput } from './types'; import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; -type JoinResult = { +type AddLiquidityOutput = { addLiquidityQueryOutput: AddLiquidityQueryOutput; addLiquidityBuildOutput: AddLiquidityBuildOutput; txOutput: TxResult; }; -async function sdkJoin({ +async function sdkAddLiquidity({ addLiquidity, addLiquidityInput, poolStateInput, @@ -107,7 +107,7 @@ function getCheck(result: AddLiquidityQueryOutput, isExactIn: boolean) { * @param client: Client & PublicActions & WalletActions - The RPC client * @param slippage: Slippage - The slippage tolerance for the join transaction */ -export async function doJoin(txInput: JoinTxInput) { +export async function doAddLiquidity(txInput: AddLiquidityTxInput) { const { addLiquidity, poolStateInput, @@ -117,13 +117,14 @@ export async function doJoin(txInput: JoinTxInput) { slippage, } = txInput; - const { addLiquidityQueryOutput, addLiquidityBuildOutput } = await sdkJoin({ - addLiquidity, - addLiquidityInput, - poolStateInput, - slippage, - testAddress, - }); + const { addLiquidityQueryOutput, addLiquidityBuildOutput } = + await sdkAddLiquidity({ + addLiquidity, + addLiquidityInput, + poolStateInput, + slippage, + testAddress, + }); const tokens = getTokensForBalanceCheck(poolStateInput); @@ -144,15 +145,15 @@ export async function doJoin(txInput: JoinTxInput) { }; } -export function assertUnbalancedJoin( +export function assertAddLiquidityUnbalanced( chainId: ChainId, poolStateInput: PoolStateInput, addLiquidityInput: AddLiquidityUnbalancedInput, - joinResult: JoinResult, + addLiquidityOutput: AddLiquidityOutput, slippage: Slippage, ) { const { txOutput, addLiquidityQueryOutput, addLiquidityBuildOutput } = - joinResult; + addLiquidityOutput; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) const expectedAmountsIn = poolStateInput.tokens.map((t) => { @@ -208,15 +209,15 @@ export function assertUnbalancedJoin( ); } -export function assertSingleTokenJoin( +export function assertAddLiquiditySingleToken( chainId: ChainId, poolStateInput: PoolStateInput, addLiquidityInput: AddLiquiditySingleAssetInput, - joinResult: JoinResult, + addLiquidityOutput: AddLiquidityOutput, slippage: Slippage, ) { const { txOutput, addLiquidityQueryOutput, addLiquidityBuildOutput } = - joinResult; + addLiquidityOutput; if (addLiquidityQueryOutput.tokenInIndex === undefined) throw Error('No index'); @@ -283,15 +284,15 @@ export function assertSingleTokenJoin( ); } -export function assertProportionalJoin( +export function assertAddLiquidityProportional( chainId: ChainId, poolStateInput: PoolStateInput, addLiquidityInput: AddLiquidityProportionalInput, - joinResult: JoinResult, + addLiquidityOutput: AddLiquidityOutput, slippage: Slippage, ) { const { txOutput, addLiquidityQueryOutput, addLiquidityBuildOutput } = - joinResult; + addLiquidityOutput; const bptToken = new Token(chainId, poolStateInput.address, 18); diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index a36afd20..24eb95a8 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -9,7 +9,7 @@ import { Slippage, } from '../../../src'; -export type JoinTxInput = { +export type AddLiquidityTxInput = { client: Client & PublicActions & TestActions & WalletActions; addLiquidity: AddLiquidity; addLiquidityInput: AddLiquidityInput; diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 7bee52fa..b2830ab7 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -30,12 +30,12 @@ import { } from '../src'; import { forkSetup } from './lib/utils/helper'; import { - assertProportionalJoin, - assertSingleTokenJoin, - assertUnbalancedJoin, - doJoin, + assertAddLiquidityProportional, + assertAddLiquiditySingleToken, + assertAddLiquidityUnbalanced, + doAddLiquidity, } from './lib/utils/joinHelper'; -import { JoinTxInput } from './lib/utils/types'; +import { AddLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); @@ -44,7 +44,7 @@ const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // 80wjAURA-20WETH describe('weighted join test', () => { - let txInput: JoinTxInput; + let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; beforeAll(async () => { @@ -111,15 +111,15 @@ describe('weighted join test', () => { amountsIn: [...amountsIn.splice(0, 1)], }; - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertUnbalancedJoin( + assertAddLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -130,15 +130,15 @@ describe('weighted join test', () => { amountsIn, useNativeAssetAsWrappedAmountIn: true, }; - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertUnbalancedJoin( + assertAddLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -163,22 +163,22 @@ describe('weighted join test', () => { }); test('with token', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertSingleTokenJoin( + assertAddLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); test('with native', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput: { ...addLiquidityInput, @@ -186,14 +186,14 @@ describe('weighted join test', () => { }, }); - assertSingleTokenJoin( + assertAddLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, { ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); @@ -215,21 +215,21 @@ describe('weighted join test', () => { }; }); test('with tokens', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, addLiquidityInput, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); test('with native', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, addLiquidityInput: { ...addLiquidityInput, @@ -237,14 +237,14 @@ describe('weighted join test', () => { }, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, { ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); From e70254f3e2f4917309c3f8237f38d156cc3bae91 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 14:44:40 -0300 Subject: [PATCH 182/199] Refactor remaining comments throughout the code --- src/entities/addLiquidity/addLiquidity.ts | 2 +- .../addLiquidityComposableStable.ts | 6 +++--- .../addLiquidity/weighted/addLiquidityWeighted.ts | 6 +++--- test/composableStableJoin.integration.test.ts | 10 +++++----- .../utils/{joinHelper.ts => addLiquidityHelper.ts} | 12 ++++++------ test/lib/utils/exitHelper.ts | 2 +- test/lib/utils/helper.ts | 2 +- test/weightedJoin.integration.test.ts | 10 +++++----- 8 files changed, 25 insertions(+), 25 deletions(-) rename test/lib/utils/{joinHelper.ts => addLiquidityHelper.ts} (97%) diff --git a/src/entities/addLiquidity/addLiquidity.ts b/src/entities/addLiquidity/addLiquidity.ts index bca11619..7a9cc93e 100644 --- a/src/entities/addLiquidity/addLiquidity.ts +++ b/src/entities/addLiquidity/addLiquidity.ts @@ -21,7 +21,7 @@ export class AddLiquidity { WEIGHTED: new AddLiquidityWeighted(), // PHANTOM_STABLE === ComposableStables in API PHANTOM_STABLE: new AddLiquidityComposableStable(), - // custom pool Joins take precedence over base Joins + // custom pool add liquidity types take precedence over base types ...customAddLiquidityTypes, }; } diff --git a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts index 0176d462..130dd896 100644 --- a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts @@ -155,7 +155,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { break; } default: - throw Error('Unsupported Join Type'); + throw Error('Unsupported Add Liquidity Kind'); } return { @@ -196,7 +196,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { break; } default: - throw Error('Unsupported Join Type'); + throw Error('Unsupported Add Liquidity Kind'); } return { ...addLiquidityAmounts, @@ -234,7 +234,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { ); } default: - throw Error('Unsupported Join Type'); + throw Error('Unsupported Add Liquidity Kind'); } } } diff --git a/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts index 9d433648..abb7c916 100644 --- a/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts +++ b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts @@ -133,7 +133,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { }; } default: - throw Error('Unsupported Join Type'); + throw Error('Unsupported Add Liquidity Kind'); } } @@ -163,7 +163,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { }; } default: - throw Error('Unsupported Join Type'); + throw Error('Unsupported Add Liquidity Kind'); } } @@ -190,7 +190,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { return WeightedEncoder.joinProportional(amounts.minimumBpt); } default: - throw Error('Unsupported Join Type'); + throw Error('Unsupported Add Liquidity Kind'); } } } diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index fdf0fb5f..9a3aeb2d 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -35,7 +35,7 @@ import { assertAddLiquidityUnbalanced, assertAddLiquiditySingleToken, assertAddLiquidityProportional, -} from './lib/utils/joinHelper'; +} from './lib/utils/addLiquidityHelper'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); @@ -43,7 +43,7 @@ const chainId = ChainId.MAINNET; const poolId = '0x156c02f3f7fef64a3a9d80ccf7085f23cce91d76000000000000000000000570'; // Balancer vETH/WETH StablePool -describe('composable stable join test', () => { +describe('add liquidity composable stable test', () => { let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; @@ -86,7 +86,7 @@ describe('composable stable join test', () => { ); }); - describe('unbalanced join', () => { + describe('add liquidity unbalanced', () => { let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { @@ -144,7 +144,7 @@ describe('composable stable join test', () => { }); }); - describe('single asset join', () => { + describe('add liquidity single asset', () => { let input: AddLiquiditySingleAssetInput; beforeAll(() => { const bptOut: InputAmount = { @@ -196,7 +196,7 @@ describe('composable stable join test', () => { }); }); - describe('proportional join', () => { + describe('add liquidity proportional', () => { let input: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { diff --git a/test/lib/utils/joinHelper.ts b/test/lib/utils/addLiquidityHelper.ts similarity index 97% rename from test/lib/utils/joinHelper.ts rename to test/lib/utils/addLiquidityHelper.ts index 6b547e65..9c2b1e08 100644 --- a/test/lib/utils/joinHelper.ts +++ b/test/lib/utils/addLiquidityHelper.ts @@ -98,14 +98,14 @@ function getCheck(result: AddLiquidityQueryOutput, isExactIn: boolean) { } /** - * Create and submit join transaction. + * Create and submit add liquidity transaction. * @param txInput - * @param addLiquidity: AddLiquidity - The pool join class, used to query the join and build the join call - * @param poolInput: PoolStateInput - The state of the pool being joined - * @param addLiquidityInput: AddLiquidityInput - The parameters of the join transaction, example: bptOut, amountsIn, etc. + * @param addLiquidity: AddLiquidity - The add liquidity class, used to query outputs and build transaction call + * @param poolInput: PoolStateInput - The state of the pool + * @param addLiquidityInput: AddLiquidityInput - The parameters of the transaction, example: bptOut, amountsIn, etc. * @param testAddress: Address - The address to send the transaction from * @param client: Client & PublicActions & WalletActions - The RPC client - * @param slippage: Slippage - The slippage tolerance for the join transaction + * @param slippage: Slippage - The slippage tolerance for the transaction */ export async function doAddLiquidity(txInput: AddLiquidityTxInput) { const { @@ -305,7 +305,7 @@ export function assertAddLiquidityProportional( bptToken, addLiquidityInput.bptOut.rawAmount, ), - // Only expect tokenInIndex for SingleAssetJoin + // Only expect tokenInIndex for AddLiquiditySingleAsset tokenInIndex: undefined, // Should match inputs poolId: poolStateInput.id, diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 9d6abe9f..3543ce59 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -250,7 +250,7 @@ export function assertProportionalExit( > = { // Query should use same bpt out as user sets bptIn: TokenAmount.fromRawAmount(bptToken, exitInput.bptIn.rawAmount), - // Only expect tokenInIndex for SingleAssetJoin + // Only expect tokenInIndex for AddLiquiditySingleAsset tokenOutIndex: undefined, // Should match inputs poolId: poolStateInput.id, diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 32c42b38..9cfddd3d 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -285,7 +285,7 @@ export const forkSetup = async ( } for (let i = 0; i < tokens.length; i++) { - // Set initial account balance for each token that will be used to join pool + // Set initial account balance for each token that will be used to add liquidity to the pool await setTokenBalance( client, accountAddress, diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index b2830ab7..720d1dcb 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -34,7 +34,7 @@ import { assertAddLiquiditySingleToken, assertAddLiquidityUnbalanced, doAddLiquidity, -} from './lib/utils/joinHelper'; +} from './lib/utils/addLiquidityHelper'; import { AddLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; @@ -43,7 +43,7 @@ const chainId = ChainId.MAINNET; const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512'; // 80wjAURA-20WETH -describe('weighted join test', () => { +describe('add liquidity weighted test', () => { let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; @@ -90,7 +90,7 @@ describe('weighted join test', () => { ); }); - describe('unbalanced join', () => { + describe('add liquidity unbalanced', () => { let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { @@ -144,7 +144,7 @@ describe('weighted join test', () => { }); }); - describe('single asset join', () => { + describe('add liquidity single asset', () => { let addLiquidityInput: AddLiquiditySingleAssetInput; beforeAll(() => { const bptOut: InputAmount = { @@ -199,7 +199,7 @@ describe('weighted join test', () => { }); }); - describe('proportional join', () => { + describe('add liquidity proportional', () => { let addLiquidityInput: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { From 6a4dd95294ef729e33a3ac919c08d9d0626ab4ef Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 14:48:29 -0300 Subject: [PATCH 183/199] Refactor encoders to expose addLiquidity functions instead of join --- .../addLiquidityComposableStable.ts | 8 ++++---- .../addLiquidity/weighted/addLiquidityWeighted.ts | 10 ++++++---- src/entities/encoders/composableStable.ts | 15 +++++++-------- src/entities/encoders/weighted.ts | 14 +++++++------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts index 130dd896..439fe8fc 100644 --- a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts @@ -213,23 +213,23 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { ): Address { switch (kind) { case AddLiquidityKind.Init: - return ComposableStableEncoder.joinInit( + return ComposableStableEncoder.addLiquidityInit( amounts.maxAmountsInNoBpt, ); case AddLiquidityKind.Unbalanced: - return ComposableStableEncoder.joinUnbalanced( + return ComposableStableEncoder.addLiquidityUnbalanced( amounts.maxAmountsInNoBpt, amounts.minimumBpt, ); case AddLiquidityKind.SingleAsset: { if (amounts.tokenInIndex === undefined) throw Error('No Index'); - return ComposableStableEncoder.joinSingleAsset( + return ComposableStableEncoder.addLiquiditySingleAsset( amounts.minimumBpt, amounts.tokenInIndex, // Has to be index without BPT ); } case AddLiquidityKind.Proportional: { - return ComposableStableEncoder.joinProportional( + return ComposableStableEncoder.addLiquidityProportional( amounts.minimumBpt, ); } diff --git a/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts index abb7c916..f6f32b9c 100644 --- a/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts +++ b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts @@ -173,21 +173,23 @@ export class AddLiquidityWeighted implements AddLiquidityBase { ): Address { switch (kind) { case AddLiquidityKind.Init: - return WeightedEncoder.joinInit(amounts.maxAmountsIn); + return WeightedEncoder.addLiquidityInit(amounts.maxAmountsIn); case AddLiquidityKind.Unbalanced: - return WeightedEncoder.joinUnbalanced( + return WeightedEncoder.addLiquidityUnbalanced( amounts.maxAmountsIn, amounts.minimumBpt, ); case AddLiquidityKind.SingleAsset: { if (amounts.tokenInIndex === undefined) throw Error('No Index'); - return WeightedEncoder.joinSingleAsset( + return WeightedEncoder.addLiquiditySingleAsset( amounts.minimumBpt, amounts.tokenInIndex, ); } case AddLiquidityKind.Proportional: { - return WeightedEncoder.joinProportional(amounts.minimumBpt); + return WeightedEncoder.addLiquidityProportional( + amounts.minimumBpt, + ); } default: throw Error('Unsupported Add Liquidity Kind'); diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index dd9113c2..f533b6fd 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -1,7 +1,6 @@ import { encodeAbiParameters } from 'viem'; import { Address } from '../../types'; -// TODO: check if we should update join interface here as well - asking because it's matching v2 SC exactly export enum ComposableStablePoolJoinKind { INIT = 0, EXACT_TOKENS_IN_FOR_BPT_OUT = 1, @@ -27,18 +26,18 @@ export class ComposableStableEncoder { * Encodes the userData parameter for providing the initial liquidity to a ComposableStablePool * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances */ - static joinInit = (amountsIn: bigint[]): Address => + static addLiquidityInit = (amountsIn: bigint[]): Address => encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256[]' }], [BigInt(ComposableStablePoolJoinKind.INIT), amountsIn], ); /** - * Encodes the userData parameter for joining a ComposableStablePool with exact token inputs + * Encodes the userData parameter for adding liquidity to a ComposableStablePool with exact token inputs * @param amountsIn - the amounts each of token to deposit in the pool as liquidity * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens */ - static joinUnbalanced = ( + static addLiquidityUnbalanced = ( amountsIn: bigint[], minimumBPT: bigint, ): Address => @@ -54,11 +53,11 @@ export class ComposableStableEncoder { ); /** - * Encodes the userData parameter for joining a ComposableStablePool with a single token to receive an exact amount of BPT + * Encodes the userData parameter for adding liquidity to a ComposableStablePool with a single token to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ - static joinSingleAsset = ( + static addLiquiditySingleAsset = ( bptAmountOut: bigint, enterTokenIndex: number, ): Address => { @@ -74,10 +73,10 @@ export class ComposableStableEncoder { }; /** - * Encodes the userData parameter for joining a ComposableStablePool proportionally to receive an exact amount of BPT + * Encodes the userData parameter for adding liquidity to a ComposableStablePool proportionally to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted */ - static joinProportional = (bptAmountOut: bigint): Address => { + static addLiquidityProportional = (bptAmountOut: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index 5882d43a..5b4b3951 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -27,18 +27,18 @@ export class WeightedEncoder { * Encodes the userData parameter for providing the initial liquidity to a WeightedPool * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances */ - static joinInit = (amountsIn: bigint[]): Address => + static addLiquidityInit = (amountsIn: bigint[]): Address => encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256[]' }], [BigInt(WeightedPoolJoinKind.INIT), amountsIn], ); /** - * Encodes the userData parameter for joining a WeightedPool with exact token inputs + * Encodes the userData parameter for adding liquidity to a WeightedPool with exact token inputs * @param amountsIn - the amounts each of token to deposit in the pool as liquidity * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens */ - static joinUnbalanced = ( + static addLiquidityUnbalanced = ( amountsIn: bigint[], minimumBPT: bigint, ): Address => @@ -52,11 +52,11 @@ export class WeightedEncoder { ); /** - * Encodes the userData parameter for joining a WeightedPool with a single token to receive an exact amount of BPT + * Encodes the userData parameter for adding liquidity to a WeightedPool with a single token to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ - static joinSingleAsset = ( + static addLiquiditySingleAsset = ( bptAmountOut: bigint, enterTokenIndex: number, ): Address => { @@ -72,10 +72,10 @@ export class WeightedEncoder { }; /** - * Encodes the userData parameter for joining a WeightedPool proportionally to receive an exact amount of BPT + * Encodes the userData parameter for adding liquidity to a WeightedPool proportionally to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted */ - static joinProportional = (bptAmountOut: bigint): Address => { + static addLiquidityProportional = (bptAmountOut: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ From cd50289667de1005eec5fee6f36a96fa277cd7dc Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 14:53:32 -0300 Subject: [PATCH 184/199] Refactor join into addLiquidity within examples and readme --- README.md | 6 +++--- examples/{joinPool.ts => addLiquidity.ts} | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) rename examples/{joinPool.ts => addLiquidity.ts} (89%) diff --git a/README.md b/README.md index d32c6ae4..16e3dc7b 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ Testing requires access to an archive node for onchain quote comparisons. This c The Balancer API Provider is a provider that facilitates data fetching from the Balancer API, it can be used for: -- Fetch Pool State for Joins; +- Fetch Pool State for AddLiquidity; - Fetch Pool State for Exits. -### Usage for Joining Pool +### Usage for adding liquidity to a Pool ```ts import { BalancerApi, AddLiquidity } from "@balancer/sdk"; @@ -65,7 +65,7 @@ it can be used for: value, }); ``` -Full working join example: [examples/join/weighted.ts](./examples/join/weighted.ts) +Full working add liquidity example: [examples/addLiquidity.ts](./examples/addLiquidity.ts) ### Usage for Exiting Pool ```ts diff --git a/examples/joinPool.ts b/examples/addLiquidity.ts similarity index 89% rename from examples/joinPool.ts rename to examples/addLiquidity.ts index a16b73ad..b5e85977 100644 --- a/examples/joinPool.ts +++ b/examples/addLiquidity.ts @@ -1,9 +1,9 @@ /** - * Example showing how to join a pool. + * Example showing how to add liquidity to a pool. * (Runs against a local Anvil fork) * * Run with: - * pnpm example ./examples/joinPool.ts + * pnpm example ./examples/addLiquidity.ts */ import { config } from 'dotenv'; config(); @@ -20,7 +20,7 @@ import { parseUnits } from 'viem'; import { ANVIL_NETWORKS, startFork } from '../test/anvil/anvil-global-setup'; import { makeForkTx } from './utils/makeForkTx'; -const join = async () => { +const addLiquidity = async () => { // User defined const chainId = ChainId.MAINNET; const userAccount = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; @@ -45,7 +45,7 @@ const join = async () => { address: t.address, })); - // Construct the AddLiquidityInput, in this case an Unbalanced join + // Construct the AddLiquidityInput, in this case an AddLiquidityUnbalanced const addLiquidityInput: AddLiquidityInput = { amountsIn, chainId, @@ -53,14 +53,14 @@ const join = async () => { kind: AddLiquidityKind.Unbalanced, }; - // Simulate the join to get the amount of BPT out + // Simulate addLiquidity to get the amount of BPT out const addLiquidity = new AddLiquidity(); const queryOutput = await addLiquidity.query( addLiquidityInput, poolStateInput, ); - console.log('\nJoin Query Result:'); + console.log('Add Liquidity Query Output:'); console.log('Tokens In:'); queryOutput.amountsIn.map((a) => console.log(a.token.address, a.amount.toString()), @@ -97,4 +97,4 @@ const join = async () => { ); }; -join().then(() => {}); +addLiquidity().then(() => {}); From fbb33b18e876a17935150682d709862c03faebd6 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:10:15 -0300 Subject: [PATCH 185/199] Refactor ExitKind into RemoveLiquidityKind --- README.md | 2 +- examples/exitPool.ts | 4 +- .../composable-stable/composableStableExit.ts | 42 ++++++++++-------- src/entities/exit/types.ts | 10 ++--- src/entities/exit/utils/validateInputs.ts | 8 ++-- src/entities/exit/weighted/weightedExit.ts | 43 +++++++++++-------- src/entities/types.ts | 2 +- test/composableStableExit.integration.test.ts | 8 ++-- test/lib/utils/exitHelper.ts | 6 +-- test/weightedExit.integration.test.ts | 8 ++-- 10 files changed, 75 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 16e3dc7b..88f364da 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ const exitInput: SingleAssetExitInput = { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleAsset, }; const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); diff --git a/examples/exitPool.ts b/examples/exitPool.ts index 17f5aa53..81f93dd5 100644 --- a/examples/exitPool.ts +++ b/examples/exitPool.ts @@ -10,7 +10,7 @@ dotenv.config(); import { ChainId, - ExitKind, + RemoveLiquidityKind, PoolExit, PoolStateInput, Slippage, @@ -53,7 +53,7 @@ const exit = async () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleAsset, }; // Simulate the exit to get the tokens out diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index d90fee43..cde462df 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -14,10 +14,10 @@ import { ComposableStableExitCall, ExitBuildOutput, ExitInput, - ExitKind, + RemoveLiquidityKind, ExitQueryResult, } from '../types'; -import { AmountsExit, PoolState } from '../../types'; +import { RemoveLiquidityAmounts, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; import { ComposableStableEncoder } from '../../encoders/composableStable'; import { getAmounts } from '../../utils'; @@ -66,7 +66,7 @@ export class ComposableStableExit implements BaseExit { return { poolType: poolState.type, - exitKind: input.kind, + removeLiquidityKind: input.kind, poolId: poolState.id, bptIn, amountsOut, @@ -80,15 +80,15 @@ export class ComposableStableExit implements BaseExit { tokens: Token[], input: ExitInput, bptIndex: number, - ): AmountsExit { + ): RemoveLiquidityAmounts { switch (input.kind) { - case ExitKind.Unbalanced: + case RemoveLiquidityKind.Unbalanced: return { minAmountsOut: getAmounts(tokens, input.amountsOut), tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: tokens @@ -96,7 +96,7 @@ export class ComposableStableExit implements BaseExit { .findIndex((t) => t.isSameAddress(input.tokenOut)), maxBptAmountIn: input.bptIn.rawAmount, }; - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: undefined, @@ -114,7 +114,10 @@ export class ComposableStableExit implements BaseExit { ...amounts.minAmountsOut.slice(input.bptIndex + 1), ], }; - const userData = this.encodeUserData(input.exitKind, amountsWithoutBpt); + const userData = this.encodeUserData( + input.removeLiquidityKind, + amountsWithoutBpt, + ); const { args } = parseExitArgs({ poolId: input.poolId, @@ -145,15 +148,17 @@ export class ComposableStableExit implements BaseExit { }; } - private getAmountsCall(input: ComposableStableExitCall): AmountsExit { - switch (input.exitKind) { - case ExitKind.Unbalanced: + private getAmountsCall( + input: ComposableStableExitCall, + ): RemoveLiquidityAmounts { + switch (input.removeLiquidityKind) { + case RemoveLiquidityKind.Unbalanced: return { minAmountsOut: input.amountsOut.map((a) => a.amount), tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: if (input.tokenOutIndex === undefined) { throw new Error( 'tokenOutIndex must be defined for SingleAsset exit', @@ -166,7 +171,7 @@ export class ComposableStableExit implements BaseExit { tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.bptIn.amount, }; - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: return { minAmountsOut: input.amountsOut.map((a) => input.slippage.removeFrom(a.amount), @@ -179,14 +184,17 @@ export class ComposableStableExit implements BaseExit { } } - private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + private encodeUserData( + kind: RemoveLiquidityKind, + amounts: RemoveLiquidityAmounts, + ): Address { switch (kind) { - case ExitKind.Unbalanced: + case RemoveLiquidityKind.Unbalanced: return ComposableStableEncoder.exitUnbalanced( amounts.minAmountsOut, amounts.maxBptAmountIn, ); - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: if (amounts.tokenOutIndex === undefined) throw Error('No Index'); @@ -194,7 +202,7 @@ export class ComposableStableExit implements BaseExit { amounts.maxBptAmountIn, amounts.tokenOutIndex, ); - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: return ComposableStableEncoder.exitProportional( amounts.maxBptAmountIn, ); diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index add9abdc..801f4d8a 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -3,7 +3,7 @@ import { Slippage } from '../slippage'; import { Address, InputAmount } from '../../types'; import { PoolState } from '../types'; -export enum ExitKind { +export enum RemoveLiquidityKind { Unbalanced = 'Unbalanced', // exitExactOut SingleAsset = 'SingleAsset', // exitExactInSingleAsset Proportional = 'Proportional', // exitExactInProportional @@ -19,18 +19,18 @@ export type BaseExitInput = { export type UnbalancedExitInput = BaseExitInput & { amountsOut: InputAmount[]; - kind: ExitKind.Unbalanced; + kind: RemoveLiquidityKind.Unbalanced; }; export type SingleAssetExitInput = BaseExitInput & { bptIn: InputAmount; tokenOut: Address; - kind: ExitKind.SingleAsset; + kind: RemoveLiquidityKind.SingleAsset; }; export type ProportionalExitInput = BaseExitInput & { bptIn: InputAmount; - kind: ExitKind.Proportional; + kind: RemoveLiquidityKind.Proportional; }; export type ExitInput = @@ -46,7 +46,7 @@ export type ExitQueryResult = export type BaseExitQueryResult = { poolType: string; poolId: Address; - exitKind: ExitKind; + removeLiquidityKind: RemoveLiquidityKind; bptIn: TokenAmount; amountsOut: TokenAmount[]; tokenOutIndex?: number; diff --git a/src/entities/exit/utils/validateInputs.ts b/src/entities/exit/utils/validateInputs.ts index 50f0286c..64ec50af 100644 --- a/src/entities/exit/utils/validateInputs.ts +++ b/src/entities/exit/utils/validateInputs.ts @@ -1,4 +1,4 @@ -import { ExitInput, ExitKind } from '../types'; +import { ExitInput, RemoveLiquidityKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; @@ -10,18 +10,18 @@ export function validateInputs(input: ExitInput, poolState: PoolStateInput) { throw new Error('Pool Tokens does not contain BPT'); } switch (input.kind) { - case ExitKind.Unbalanced: + case RemoveLiquidityKind.Unbalanced: areTokensInArray( input.amountsOut.map((a) => a.address), poolState.tokens.map((t) => t.address), ); break; - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: areTokensInArray( [input.tokenOut], poolState.tokens.map((t) => t.address), ); - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: areTokensInArray([input.bptIn.address], [poolState.address]); default: break; diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 1728ce35..a6fe3084 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -15,11 +15,11 @@ import { ExitBuildOutput, ExitCall, ExitInput, - ExitKind, + RemoveLiquidityKind, ExitQueryResult, WeightedExitCall, } from '../types'; -import { AmountsExit, PoolState } from '../../types'; +import { RemoveLiquidityAmounts, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; import { getAmounts } from '../../utils'; @@ -60,7 +60,7 @@ export class WeightedExit implements BaseExit { return { poolType: poolState.type, - exitKind: input.kind, + removeLiquidityKind: input.kind, poolId: poolState.id, bptIn, amountsOut, @@ -69,15 +69,18 @@ export class WeightedExit implements BaseExit { }; } - private getAmountsQuery(tokens: Token[], input: ExitInput): AmountsExit { + private getAmountsQuery( + tokens: Token[], + input: ExitInput, + ): RemoveLiquidityAmounts { switch (input.kind) { - case ExitKind.Unbalanced: + case RemoveLiquidityKind.Unbalanced: return { minAmountsOut: getAmounts(tokens, input.amountsOut), tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: tokens.findIndex((t) => @@ -85,7 +88,7 @@ export class WeightedExit implements BaseExit { ), maxBptAmountIn: input.bptIn.rawAmount, }; - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: undefined, @@ -97,7 +100,10 @@ export class WeightedExit implements BaseExit { public buildCall(input: WeightedExitCall): ExitBuildOutput { const amounts = this.getAmountsCall(input); - const userData = this.encodeUserData(input.exitKind, amounts); + const userData = this.encodeUserData( + input.removeLiquidityKind, + amounts, + ); const { args } = parseExitArgs({ poolId: input.poolId, @@ -129,15 +135,15 @@ export class WeightedExit implements BaseExit { }; } - private getAmountsCall(input: ExitCall): AmountsExit { - switch (input.exitKind) { - case ExitKind.Unbalanced: + private getAmountsCall(input: ExitCall): RemoveLiquidityAmounts { + switch (input.removeLiquidityKind) { + case RemoveLiquidityKind.Unbalanced: return { minAmountsOut: input.amountsOut.map((a) => a.amount), tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: if (input.tokenOutIndex === undefined) { throw new Error( 'tokenOutIndex must be defined for SingleAsset exit', @@ -150,7 +156,7 @@ export class WeightedExit implements BaseExit { tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.bptIn.amount, }; - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: return { minAmountsOut: input.amountsOut.map((a) => input.slippage.removeFrom(a.amount), @@ -163,14 +169,17 @@ export class WeightedExit implements BaseExit { } } - private encodeUserData(kind: ExitKind, amounts: AmountsExit): Address { + private encodeUserData( + kind: RemoveLiquidityKind, + amounts: RemoveLiquidityAmounts, + ): Address { switch (kind) { - case ExitKind.Unbalanced: + case RemoveLiquidityKind.Unbalanced: return WeightedEncoder.exitUnbalanced( amounts.minAmountsOut, amounts.maxBptAmountIn, ); - case ExitKind.SingleAsset: + case RemoveLiquidityKind.SingleAsset: if (amounts.tokenOutIndex === undefined) throw Error('No Index'); @@ -178,7 +187,7 @@ export class WeightedExit implements BaseExit { amounts.maxBptAmountIn, amounts.tokenOutIndex, ); - case ExitKind.Proportional: + case RemoveLiquidityKind.Proportional: return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); default: throw Error('Unsupported Exit Type'); diff --git a/src/entities/types.ts b/src/entities/types.ts index 57dd8a13..864f99ba 100644 --- a/src/entities/types.ts +++ b/src/entities/types.ts @@ -23,7 +23,7 @@ export type AddLiquidityAmounts = { minimumBpt: bigint; }; -export type AmountsExit = { +export type RemoveLiquidityAmounts = { minAmountsOut: bigint[]; tokenOutIndex: number | undefined; maxBptAmountIn: bigint; diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index c6493f8d..a3707bca 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -15,7 +15,7 @@ import { SingleAssetExitInput, ProportionalExitInput, UnbalancedExitInput, - ExitKind, + RemoveLiquidityKind, Slippage, Token, PoolStateInput, @@ -102,7 +102,7 @@ describe('composable stable exit test', () => { input = { chainId, rpcUrl, - kind: ExitKind.Unbalanced, + kind: RemoveLiquidityKind.Unbalanced, }; }); test('exiting with wrapped', async () => { @@ -153,7 +153,7 @@ describe('composable stable exit test', () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleAsset, }; }); test('exiting with wrapped', async () => { @@ -203,7 +203,7 @@ describe('composable stable exit test', () => { bptIn, chainId, rpcUrl, - kind: ExitKind.Proportional, + kind: RemoveLiquidityKind.Proportional, }; }); test('with tokens', async () => { diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 9956c1c5..0c2829a2 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -160,7 +160,7 @@ export function assertUnbalancedExit( poolId: poolStateInput.id, poolType: poolStateInput.type, toInternalBalance: !!exitInput.toInternalBalance, - exitKind: exitInput.kind, + removeLiquidityKind: exitInput.kind, }; const queryCheck = getCheck(exitQueryResult, false); @@ -205,7 +205,7 @@ export function assertSingleTokenExit( poolId: poolStateInput.id, poolType: poolStateInput.type, toInternalBalance: !!exitInput.toInternalBalance, - exitKind: exitInput.kind, + removeLiquidityKind: exitInput.kind, }; const queryCheck = getCheck(exitQueryResult, true); @@ -256,7 +256,7 @@ export function assertProportionalExit( poolId: poolStateInput.id, poolType: poolStateInput.type, toInternalBalance: !!exitInput.toInternalBalance, - exitKind: exitInput.kind, + removeLiquidityKind: exitInput.kind, }; const queryCheck = getCheck(exitQueryResult, true); diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 4bbb6a4f..4faf79ff 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -15,7 +15,7 @@ import { SingleAssetExitInput, ProportionalExitInput, UnbalancedExitInput, - ExitKind, + RemoveLiquidityKind, Slippage, PoolStateInput, PoolExit, @@ -92,7 +92,7 @@ describe('weighted exit test', () => { input = { chainId, rpcUrl, - kind: ExitKind.Unbalanced, + kind: RemoveLiquidityKind.Unbalanced, }; }); test('exiting with wrapped', async () => { @@ -143,7 +143,7 @@ describe('weighted exit test', () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleAsset, }; }); test('exiting with wrapped', async () => { @@ -193,7 +193,7 @@ describe('weighted exit test', () => { bptIn, chainId, rpcUrl, - kind: ExitKind.Proportional, + kind: RemoveLiquidityKind.Proportional, }; }); test('with tokens', async () => { From c47de578080a3b35a34d36d1219a7b52f53ce33f Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:14:55 -0300 Subject: [PATCH 186/199] Refactor ExitInput into RemoveLiquidityInput --- README.md | 4 +- examples/exitPool.ts | 11 ++- .../composable-stable/composableStableExit.ts | 6 +- src/entities/exit/poolExit.ts | 4 +- src/entities/exit/types.ts | 21 +++-- src/entities/exit/utils/validateInputs.ts | 7 +- src/entities/exit/weighted/weightedExit.ts | 6 +- test/composableStableExit.integration.test.ts | 47 +++++----- test/lib/utils/exitHelper.ts | 88 ++++++++++++------- test/lib/utils/types.ts | 4 +- test/weightedExit.integration.test.ts | 47 +++++----- 11 files changed, 143 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 88f364da..6c000d15 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Full working add liquidity example: [examples/addLiquidity.ts](./examples/addLiq ```ts import { BalancerApi, PoolExit } from "@balancer/sdk"; ... -const exitInput: SingleAssetExitInput = { +const removeLiquidityInput: RemoveLiquiditySingleTokenInput = { chainId, rpcUrl, bptIn, @@ -82,7 +82,7 @@ const exitInput: SingleAssetExitInput = { const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); const poolExit = new PoolExit(); -const queryOutput = await poolExit.query(exitInput, poolState); +const queryOutput = await poolExit.query(removeLiquidityInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = poolExit.buildCall({ ...queryOutput, diff --git a/examples/exitPool.ts b/examples/exitPool.ts index 81f93dd5..03443d48 100644 --- a/examples/exitPool.ts +++ b/examples/exitPool.ts @@ -15,7 +15,7 @@ import { PoolStateInput, Slippage, InputAmount, - ExitInput, + RemoveLiquidityInput, BalancerApi, } from '../src'; import { parseEther } from 'viem'; @@ -42,13 +42,13 @@ const exit = async () => { const poolStateInput: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - // Construct the ExitInput, in this case a SingleAsset exit + // Construct the RemoveLiquidityInput, in this case a SingleAsset exit const bptIn: InputAmount = { rawAmount: parseEther('1'), decimals: 18, address: poolStateInput.address, }; - const exitInput: ExitInput = { + const removeLiquidityInput: RemoveLiquidityInput = { chainId, rpcUrl, bptIn, @@ -58,7 +58,10 @@ const exit = async () => { // Simulate the exit to get the tokens out const poolExit = new PoolExit(); - const queryOutput = await poolExit.query(exitInput, poolStateInput); + const queryOutput = await poolExit.query( + removeLiquidityInput, + poolStateInput, + ); console.log('\nExit Query Result:'); console.log(`BPT In: ${queryOutput.bptIn.amount.toString()}\nTokens Out:`); diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index cde462df..7ed794ee 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -13,7 +13,7 @@ import { BaseExit, ComposableStableExitCall, ExitBuildOutput, - ExitInput, + RemoveLiquidityInput, RemoveLiquidityKind, ExitQueryResult, } from '../types'; @@ -24,7 +24,7 @@ import { getAmounts } from '../../utils'; export class ComposableStableExit implements BaseExit { public async query( - input: ExitInput, + input: RemoveLiquidityInput, poolState: PoolState, ): Promise { const bptIndex = poolState.tokens.findIndex( @@ -78,7 +78,7 @@ export class ComposableStableExit implements BaseExit { private getAmountsQuery( tokens: Token[], - input: ExitInput, + input: RemoveLiquidityInput, bptIndex: number, ): RemoveLiquidityAmounts { switch (input.kind) { diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index 51d46df0..deaff1f8 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -3,7 +3,7 @@ import { ExitBuildOutput, ExitCall, ExitConfig, - ExitInput, + RemoveLiquidityInput, ExitQueryResult, } from './types'; import { WeightedExit } from './weighted/weightedExit'; @@ -35,7 +35,7 @@ export class PoolExit { } public async query( - input: ExitInput, + input: RemoveLiquidityInput, poolState: PoolStateInput, ): Promise { validateInputs(input, poolState); diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 801f4d8a..e3594905 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -10,33 +10,33 @@ export enum RemoveLiquidityKind { } // This will be extended for each pools specific output requirements -export type BaseExitInput = { +export type RemoveLiquidityBaseInput = { chainId: number; rpcUrl: string; exitWithNativeAsset?: boolean; toInternalBalance?: boolean; }; -export type UnbalancedExitInput = BaseExitInput & { +export type RemoveLiquidityUnbalancedInput = RemoveLiquidityBaseInput & { amountsOut: InputAmount[]; kind: RemoveLiquidityKind.Unbalanced; }; -export type SingleAssetExitInput = BaseExitInput & { +export type RemoveLiquiditySingleTokenInput = RemoveLiquidityBaseInput & { bptIn: InputAmount; tokenOut: Address; kind: RemoveLiquidityKind.SingleAsset; }; -export type ProportionalExitInput = BaseExitInput & { +export type RemoveLiquidityProportionalInput = RemoveLiquidityBaseInput & { bptIn: InputAmount; kind: RemoveLiquidityKind.Proportional; }; -export type ExitInput = - | UnbalancedExitInput - | SingleAssetExitInput - | ProportionalExitInput; +export type RemoveLiquidityInput = + | RemoveLiquidityUnbalancedInput + | RemoveLiquiditySingleTokenInput + | RemoveLiquidityProportionalInput; export type ExitQueryResult = | BaseExitQueryResult @@ -77,7 +77,10 @@ export type ExitBuildOutput = { }; export interface BaseExit { - query(input: ExitInput, poolState: PoolState): Promise; + query( + input: RemoveLiquidityInput, + poolState: PoolState, + ): Promise; buildCall(input: ExitCall): ExitBuildOutput; } diff --git a/src/entities/exit/utils/validateInputs.ts b/src/entities/exit/utils/validateInputs.ts index 64ec50af..35a8b575 100644 --- a/src/entities/exit/utils/validateInputs.ts +++ b/src/entities/exit/utils/validateInputs.ts @@ -1,8 +1,11 @@ -import { ExitInput, RemoveLiquidityKind } from '../types'; +import { RemoveLiquidityInput, RemoveLiquidityKind } from '../types'; import { PoolStateInput } from '../../types'; import { areTokensInArray } from '../../utils/areTokensInArray'; -export function validateInputs(input: ExitInput, poolState: PoolStateInput) { +export function validateInputs( + input: RemoveLiquidityInput, + poolState: PoolStateInput, +) { const bptIndex = poolState.tokens.findIndex( (t) => t.address === poolState.address, ); diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index a6fe3084..126bf504 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -14,7 +14,7 @@ import { BaseExit, ExitBuildOutput, ExitCall, - ExitInput, + RemoveLiquidityInput, RemoveLiquidityKind, ExitQueryResult, WeightedExitCall, @@ -25,7 +25,7 @@ import { getAmounts } from '../../utils'; export class WeightedExit implements BaseExit { public async query( - input: ExitInput, + input: RemoveLiquidityInput, poolState: PoolState, ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); @@ -71,7 +71,7 @@ export class WeightedExit implements BaseExit { private getAmountsQuery( tokens: Token[], - input: ExitInput, + input: RemoveLiquidityInput, ): RemoveLiquidityAmounts { switch (input.kind) { case RemoveLiquidityKind.Unbalanced: diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index a3707bca..10eb55d0 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -12,9 +12,9 @@ import { walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, + RemoveLiquiditySingleTokenInput, + RemoveLiquidityProportionalInput, + RemoveLiquidityUnbalancedInput, RemoveLiquidityKind, Slippage, Token, @@ -25,7 +25,7 @@ import { CHAINS, ChainId, getPoolAddress, - ExitInput, + RemoveLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; @@ -68,7 +68,7 @@ describe('composable stable exit test', () => { slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - exitInput: {} as ExitInput, + removeLiquidityInput: {} as RemoveLiquidityInput, }; bptToken = new Token(chainId, poolInput.address, 18, 'BPT'); }); @@ -84,7 +84,7 @@ describe('composable stable exit test', () => { }); describe('unbalanced exit', async () => { - let input: Omit; + let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { const bptIndex = txInput.poolStateInput.tokens.findIndex( @@ -106,33 +106,36 @@ describe('composable stable exit test', () => { }; }); test('exiting with wrapped', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; - const exitResult = await doExit({ ...txInput, exitInput }); + const exitResult = await doExit({ + ...txInput, + removeLiquidityInput, + }); assertUnbalancedExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); }); test('exiting with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), exitWithNativeAsset: true, }; const exitResult = await doExit({ ...txInput, - exitInput, + removeLiquidityInput, }); assertUnbalancedExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); @@ -140,7 +143,7 @@ describe('composable stable exit test', () => { }); describe('single asset exit', () => { - let input: SingleAssetExitInput; + let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -159,7 +162,7 @@ describe('composable stable exit test', () => { test('exiting with wrapped', async () => { const exitResult = await doExit({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); assertSingleTokenExit( @@ -172,19 +175,19 @@ describe('composable stable exit test', () => { }); test('exiting with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, exitWithNativeAsset: true, }; const exitResult = await doExit({ ...txInput, - exitInput, + removeLiquidityInput, }); assertSingleTokenExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); @@ -192,7 +195,7 @@ describe('composable stable exit test', () => { }); describe('proportional exit', () => { - let input: ProportionalExitInput; + let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -209,7 +212,7 @@ describe('composable stable exit test', () => { test('with tokens', async () => { const exitResult = await doExit({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); assertProportionalExit( @@ -221,18 +224,18 @@ describe('composable stable exit test', () => { ); }); test('with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, useNativeAssetAsWrappedAmountIn: true, }; const exitResult = await doExit({ ...txInput, - exitInput, + removeLiquidityInput, }); assertProportionalExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 0c2829a2..5e2c6089 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -9,11 +9,11 @@ import { TokenAmount, Slippage, Token, - UnbalancedExitInput, - SingleAssetExitInput, + RemoveLiquidityUnbalancedInput, + RemoveLiquiditySingleTokenInput, BALANCER_VAULT, - ExitInput, - ProportionalExitInput, + RemoveLiquidityInput, + RemoveLiquidityProportionalInput, } from '../../../src'; import { sendTransactionGetBalances, TxResult } from './helper'; import { expect } from 'vitest'; @@ -28,7 +28,7 @@ type ExitResult = { export const sdkExit = async ({ poolExit, - exitInput, + removeLiquidityInput, poolStateInput, slippage, testAddress, @@ -36,7 +36,10 @@ export const sdkExit = async ({ exitBuildOutput: ExitBuildOutput; exitQueryResult: ExitQueryResult; }> => { - const exitQueryResult = await poolExit.query(exitInput, poolStateInput); + const exitQueryResult = await poolExit.query( + removeLiquidityInput, + poolStateInput, + ); const exitBuildOutput = poolExit.buildCall({ ...exitQueryResult, slippage, @@ -86,7 +89,7 @@ function getCheck(result: ExitQueryResult, isExactIn: boolean) { * @param txInput * @param client: Client & PublicActions & WalletActions - The RPC client * @param poolExit: PoolExit - The pool exit class, used to query the exit and build the exit call - * @param exitInput: ExitInput - The parameters of the exit transaction, example: bptIn, amountsOut, etc. + * @param removeLiquidityInput: RemoveLiquidityInput - The parameters of the exit transaction, example: bptIn, amountsOut, etc. * @param slippage: Slippage - The slippage tolerance for the exit transaction * @param poolStateInput: PoolStateInput - The state of the pool being exited * @param testAddress: Address - The address to send the transaction from @@ -95,7 +98,7 @@ export async function doExit(txInput: ExitTxInput) { const { poolExit, poolStateInput, - exitInput, + removeLiquidityInput, testAddress, client, slippage, @@ -103,7 +106,7 @@ export async function doExit(txInput: ExitTxInput) { const { exitQueryResult, exitBuildOutput } = await sdkExit({ poolExit, - exitInput, + removeLiquidityInput, poolStateInput, slippage, testAddress, @@ -132,7 +135,7 @@ export async function doExit(txInput: ExitTxInput) { export function assertUnbalancedExit( chainId: ChainId, poolStateInput: PoolStateInput, - exitInput: UnbalancedExitInput, + removeLiquidityInput: RemoveLiquidityUnbalancedInput, exitResult: ExitResult, slippage: Slippage, ) { @@ -142,12 +145,14 @@ export function assertUnbalancedExit( const expectedAmountsOut = poolStateInput.tokens.map((t) => { let token; if ( - exitInput.exitWithNativeAsset && + removeLiquidityInput.exitWithNativeAsset && t.address === NATIVE_ASSETS[chainId].wrapped ) token = new Token(chainId, zeroAddress, t.decimals); else token = new Token(chainId, t.address, t.decimals); - const input = exitInput.amountsOut.find((a) => a.address === t.address); + const input = removeLiquidityInput.amountsOut.find( + (a) => a.address === t.address, + ); if (input === undefined) return TokenAmount.fromRawAmount(token, 0n); else return TokenAmount.fromRawAmount(token, input.rawAmount); }); @@ -159,8 +164,8 @@ export function assertUnbalancedExit( // Should match inputs poolId: poolStateInput.id, poolType: poolStateInput.type, - toInternalBalance: !!exitInput.toInternalBalance, - removeLiquidityKind: exitInput.kind, + toInternalBalance: !!removeLiquidityInput.toInternalBalance, + removeLiquidityKind: removeLiquidityInput.kind, }; const queryCheck = getCheck(exitQueryResult, false); @@ -172,13 +177,18 @@ export function assertUnbalancedExit( assertExitBuildOutput(exitQueryResult, exitBuildOutput, false, slippage); - assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); + assertTokenDeltas( + poolStateInput, + removeLiquidityInput, + exitQueryResult, + txOutput, + ); } export function assertSingleTokenExit( chainId: ChainId, poolStateInput: PoolStateInput, - exitInput: SingleAssetExitInput, + removeLiquidityInput: RemoveLiquiditySingleTokenInput, exitResult: ExitResult, slippage: Slippage, ) { @@ -197,15 +207,18 @@ export function assertSingleTokenExit( 'amountsOut' | 'bptIndex' > = { // Query should use same bpt out as user sets - bptIn: TokenAmount.fromRawAmount(bptToken, exitInput.bptIn.rawAmount), + bptIn: TokenAmount.fromRawAmount( + bptToken, + removeLiquidityInput.bptIn.rawAmount, + ), tokenOutIndex: tokensWithoutBpt.findIndex( - (t) => t.address === exitInput.tokenOut, + (t) => t.address === removeLiquidityInput.tokenOut, ), // Should match inputs poolId: poolStateInput.id, poolType: poolStateInput.type, - toInternalBalance: !!exitInput.toInternalBalance, - removeLiquidityKind: exitInput.kind, + toInternalBalance: !!removeLiquidityInput.toInternalBalance, + removeLiquidityKind: removeLiquidityInput.kind, }; const queryCheck = getCheck(exitQueryResult, true); @@ -216,12 +229,12 @@ export function assertSingleTokenExit( // (Note exitQueryResult also has value for bpt if pre-minted) exitQueryResult.amountsOut.forEach((a) => { if ( - !exitInput.exitWithNativeAsset && - a.token.address === exitInput.tokenOut + !removeLiquidityInput.exitWithNativeAsset && + a.token.address === removeLiquidityInput.tokenOut ) expect(a.amount > 0n).to.be.true; else if ( - exitInput.exitWithNativeAsset && + removeLiquidityInput.exitWithNativeAsset && a.token.address === zeroAddress ) expect(a.amount > 0n).to.be.true; @@ -230,13 +243,18 @@ export function assertSingleTokenExit( assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); - assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); + assertTokenDeltas( + poolStateInput, + removeLiquidityInput, + exitQueryResult, + txOutput, + ); } export function assertProportionalExit( chainId: ChainId, poolStateInput: PoolStateInput, - exitInput: ProportionalExitInput, + removeLiquidityInput: RemoveLiquidityProportionalInput, exitResult: ExitResult, slippage: Slippage, ) { @@ -249,14 +267,17 @@ export function assertProportionalExit( 'amountsOut' | 'bptIndex' > = { // Query should use same bpt out as user sets - bptIn: TokenAmount.fromRawAmount(bptToken, exitInput.bptIn.rawAmount), + bptIn: TokenAmount.fromRawAmount( + bptToken, + removeLiquidityInput.bptIn.rawAmount, + ), // Only expect tokenInIndex for AddLiquiditySingleAsset tokenOutIndex: undefined, // Should match inputs poolId: poolStateInput.id, poolType: poolStateInput.type, - toInternalBalance: !!exitInput.toInternalBalance, - removeLiquidityKind: exitInput.kind, + toInternalBalance: !!removeLiquidityInput.toInternalBalance, + removeLiquidityKind: removeLiquidityInput.kind, }; const queryCheck = getCheck(exitQueryResult, true); @@ -272,12 +293,17 @@ export function assertProportionalExit( assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); - assertTokenDeltas(poolStateInput, exitInput, exitQueryResult, txOutput); + assertTokenDeltas( + poolStateInput, + removeLiquidityInput, + exitQueryResult, + txOutput, + ); } function assertTokenDeltas( poolStateInput: PoolStateInput, - exitInput: ExitInput, + removeLiquidityInput: RemoveLiquidityInput, exitQueryResult: ExitQueryResult, txOutput: TxResult, ) { @@ -296,7 +322,7 @@ function assertTokenDeltas( ]; // If input is exit with native we must replace it with 0 and update native value instead - if (exitInput.exitWithNativeAsset) { + if (removeLiquidityInput.exitWithNativeAsset) { const index = amountsWithoutBpt.findIndex( (a) => a.token.address === zeroAddress, ); diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index 24eb95a8..f1f05f1a 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -2,7 +2,7 @@ import { Client, PublicActions, TestActions, WalletActions } from 'viem'; import { Address, AddLiquidityInput, - ExitInput, + RemoveLiquidityInput, PoolExit, AddLiquidity, PoolStateInput, @@ -21,7 +21,7 @@ export type AddLiquidityTxInput = { export type ExitTxInput = { client: Client & PublicActions & TestActions & WalletActions; poolExit: PoolExit; - exitInput: ExitInput; + removeLiquidityInput: RemoveLiquidityInput; slippage: Slippage; poolStateInput: PoolStateInput; testAddress: Address; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 4faf79ff..70872553 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -12,9 +12,9 @@ import { walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, + RemoveLiquiditySingleTokenInput, + RemoveLiquidityProportionalInput, + RemoveLiquidityUnbalancedInput, RemoveLiquidityKind, Slippage, PoolStateInput, @@ -24,7 +24,7 @@ import { CHAINS, ChainId, getPoolAddress, - ExitInput, + RemoveLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; @@ -66,7 +66,7 @@ describe('weighted exit test', () => { slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - exitInput: {} as ExitInput, + removeLiquidityInput: {} as RemoveLiquidityInput, }; }); @@ -81,7 +81,7 @@ describe('weighted exit test', () => { }); describe('unbalanced exit', async () => { - let input: Omit; + let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { amountsOut = poolInput.tokens.map((t) => ({ @@ -96,33 +96,36 @@ describe('weighted exit test', () => { }; }); test('exiting with wrapped', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; - const exitResult = await doExit({ ...txInput, exitInput }); + const exitResult = await doExit({ + ...txInput, + removeLiquidityInput, + }); assertUnbalancedExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); }); test('exiting with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), exitWithNativeAsset: true, }; const exitResult = await doExit({ ...txInput, - exitInput, + removeLiquidityInput, }); assertUnbalancedExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); @@ -130,7 +133,7 @@ describe('weighted exit test', () => { }); describe('single asset exit', () => { - let input: SingleAssetExitInput; + let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -149,7 +152,7 @@ describe('weighted exit test', () => { test('exiting with wrapped', async () => { const exitResult = await doExit({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); assertSingleTokenExit( @@ -162,19 +165,19 @@ describe('weighted exit test', () => { }); test('exiting with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, exitWithNativeAsset: true, }; const exitResult = await doExit({ ...txInput, - exitInput, + removeLiquidityInput, }); assertSingleTokenExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); @@ -182,7 +185,7 @@ describe('weighted exit test', () => { }); describe('proportional exit', () => { - let input: ProportionalExitInput; + let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -199,7 +202,7 @@ describe('weighted exit test', () => { test('with tokens', async () => { const exitResult = await doExit({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); assertProportionalExit( @@ -211,18 +214,18 @@ describe('weighted exit test', () => { ); }); test('with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, useNativeAssetAsWrappedAmountIn: true, }; const exitResult = await doExit({ ...txInput, - exitInput, + removeLiquidityInput, }); assertProportionalExit( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, + removeLiquidityInput, exitResult, txInput.slippage, ); From 933ebafac0f6eb4c0f42a6e48d9c8197ee9389eb Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:18:33 -0300 Subject: [PATCH 187/199] Refactor ExitQueryResult into RemoveLiquidityQueryOutput --- .../composable-stable/composableStableExit.ts | 4 +- src/entities/exit/poolExit.ts | 4 +- src/entities/exit/types.ts | 21 ++-- src/entities/exit/weighted/weightedExit.ts | 4 +- test/lib/utils/exitHelper.ts | 117 +++++++++++------- 5 files changed, 89 insertions(+), 61 deletions(-) diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index 7ed794ee..f5958455 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -15,7 +15,7 @@ import { ExitBuildOutput, RemoveLiquidityInput, RemoveLiquidityKind, - ExitQueryResult, + RemoveLiquidityQueryOutput, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; @@ -26,7 +26,7 @@ export class ComposableStableExit implements BaseExit { public async query( input: RemoveLiquidityInput, poolState: PoolState, - ): Promise { + ): Promise { const bptIndex = poolState.tokens.findIndex( (t) => t.address === poolState.address, ); diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index deaff1f8..d5cc955c 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -4,7 +4,7 @@ import { ExitCall, ExitConfig, RemoveLiquidityInput, - ExitQueryResult, + RemoveLiquidityQueryOutput, } from './types'; import { WeightedExit } from './weighted/weightedExit'; import { PoolStateInput } from '../types'; @@ -37,7 +37,7 @@ export class PoolExit { public async query( input: RemoveLiquidityInput, poolState: PoolStateInput, - ): Promise { + ): Promise { validateInputs(input, poolState); const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index e3594905..5a23f342 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -38,12 +38,12 @@ export type RemoveLiquidityInput = | RemoveLiquiditySingleTokenInput | RemoveLiquidityProportionalInput; -export type ExitQueryResult = - | BaseExitQueryResult - | ComposableStableExitQueryResult; +export type RemoveLiquidityQueryOutput = + | RemoveLiquidityBaseQueryOutput + | RemoveLiquidityComposableStableQueryOutput; // Returned from a exit query -export type BaseExitQueryResult = { +export type RemoveLiquidityBaseQueryOutput = { poolType: string; poolId: Address; removeLiquidityKind: RemoveLiquidityKind; @@ -53,9 +53,10 @@ export type BaseExitQueryResult = { toInternalBalance: boolean; }; -export type ComposableStableExitQueryResult = BaseExitQueryResult & { - bptIndex: number; -}; +export type RemoveLiquidityComposableStableQueryOutput = + RemoveLiquidityBaseQueryOutput & { + bptIndex: number; + }; type BaseExitCall = { slippage: Slippage; @@ -63,8 +64,8 @@ type BaseExitCall = { recipient: Address; }; export type ComposableStableExitCall = BaseExitCall & - ComposableStableExitQueryResult; -export type WeightedExitCall = BaseExitCall & BaseExitQueryResult; + RemoveLiquidityComposableStableQueryOutput; +export type WeightedExitCall = BaseExitCall & RemoveLiquidityBaseQueryOutput; export type ExitCall = ComposableStableExitCall | WeightedExitCall; @@ -80,7 +81,7 @@ export interface BaseExit { query( input: RemoveLiquidityInput, poolState: PoolState, - ): Promise; + ): Promise; buildCall(input: ExitCall): ExitBuildOutput; } diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 126bf504..1a2f6243 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -16,7 +16,7 @@ import { ExitCall, RemoveLiquidityInput, RemoveLiquidityKind, - ExitQueryResult, + RemoveLiquidityQueryOutput, WeightedExitCall, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; @@ -27,7 +27,7 @@ export class WeightedExit implements BaseExit { public async query( input: RemoveLiquidityInput, poolState: PoolState, - ): Promise { + ): Promise { const amounts = this.getAmountsQuery(poolState.tokens, input); const userData = this.encodeUserData(input.kind, amounts); diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 5e2c6089..6074638f 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -1,9 +1,9 @@ import { ExitTxInput } from './types'; import { ChainId, - ComposableStableExitQueryResult, + RemoveLiquidityComposableStableQueryOutput, ExitBuildOutput, - ExitQueryResult, + RemoveLiquidityQueryOutput, NATIVE_ASSETS, PoolStateInput, TokenAmount, @@ -21,7 +21,7 @@ import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type ExitResult = { - exitQueryResult: ExitQueryResult; + removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; exitBuildOutput: ExitBuildOutput; txOutput: TxResult; }; @@ -34,14 +34,14 @@ export const sdkExit = async ({ testAddress, }: Omit): Promise<{ exitBuildOutput: ExitBuildOutput; - exitQueryResult: ExitQueryResult; + removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; }> => { - const exitQueryResult = await poolExit.query( + const removeLiquidityQueryOutput = await poolExit.query( removeLiquidityInput, poolStateInput, ); const exitBuildOutput = poolExit.buildCall({ - ...exitQueryResult, + ...removeLiquidityQueryOutput, slippage, sender: testAddress, recipient: testAddress, @@ -49,26 +49,31 @@ export const sdkExit = async ({ return { exitBuildOutput, - exitQueryResult, + removeLiquidityQueryOutput, }; }; -function isComposableStableExitQueryResult(result: ExitQueryResult): boolean { - return (result as ComposableStableExitQueryResult).bptIndex !== undefined; +function isRemoveLiquidityComposableStableQueryOutput( + result: RemoveLiquidityQueryOutput, +): boolean { + return ( + (result as RemoveLiquidityComposableStableQueryOutput).bptIndex !== + undefined + ); } -function getCheck(result: ExitQueryResult, isExactIn: boolean) { - if (isComposableStableExitQueryResult(result)) { +function getCheck(result: RemoveLiquidityQueryOutput, isExactIn: boolean) { + if (isRemoveLiquidityComposableStableQueryOutput(result)) { if (isExactIn) { // Using this destructuring to return only the fields of interest // rome-ignore lint/correctness/noUnusedVariables: const { amountsOut, bptIndex, ...check } = - result as ComposableStableExitQueryResult; + result as RemoveLiquidityComposableStableQueryOutput; return check; } else { // rome-ignore lint/correctness/noUnusedVariables: const { bptIn, bptIndex, ...check } = - result as ComposableStableExitQueryResult; + result as RemoveLiquidityComposableStableQueryOutput; return check; } } else { @@ -104,7 +109,7 @@ export async function doExit(txInput: ExitTxInput) { slippage, } = txInput; - const { exitQueryResult, exitBuildOutput } = await sdkExit({ + const { removeLiquidityQueryOutput, exitBuildOutput } = await sdkExit({ poolExit, removeLiquidityInput, poolStateInput, @@ -126,7 +131,7 @@ export async function doExit(txInput: ExitTxInput) { ); return { - exitQueryResult, + removeLiquidityQueryOutput, exitBuildOutput, txOutput, }; @@ -139,7 +144,8 @@ export function assertUnbalancedExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + const { txOutput, removeLiquidityQueryOutput, exitBuildOutput } = + exitResult; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) const expectedAmountsOut = poolStateInput.tokens.map((t) => { @@ -157,7 +163,10 @@ export function assertUnbalancedExit( else return TokenAmount.fromRawAmount(token, input.rawAmount); }); - const expectedQueryOutput: Omit = { + const expectedQueryOutput: Omit< + RemoveLiquidityQueryOutput, + 'bptIn' | 'bptIndex' + > = { // Query should use same amountsOut as input amountsOut: expectedAmountsOut, tokenOutIndex: undefined, @@ -168,19 +177,24 @@ export function assertUnbalancedExit( removeLiquidityKind: removeLiquidityInput.kind, }; - const queryCheck = getCheck(exitQueryResult, false); + const queryCheck = getCheck(removeLiquidityQueryOutput, false); expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect some bpt amount - expect(exitQueryResult.bptIn.amount > 0n).to.be.true; + expect(removeLiquidityQueryOutput.bptIn.amount > 0n).to.be.true; - assertExitBuildOutput(exitQueryResult, exitBuildOutput, false, slippage); + assertExitBuildOutput( + removeLiquidityQueryOutput, + exitBuildOutput, + false, + slippage, + ); assertTokenDeltas( poolStateInput, removeLiquidityInput, - exitQueryResult, + removeLiquidityQueryOutput, txOutput, ); } @@ -192,9 +206,11 @@ export function assertSingleTokenExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + const { txOutput, removeLiquidityQueryOutput, exitBuildOutput } = + exitResult; - if (exitQueryResult.tokenOutIndex === undefined) throw Error('No index'); + if (removeLiquidityQueryOutput.tokenOutIndex === undefined) + throw Error('No index'); const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -203,7 +219,7 @@ export function assertSingleTokenExit( ); const expectedQueryOutput: Omit< - ExitQueryResult, + RemoveLiquidityQueryOutput, 'amountsOut' | 'bptIndex' > = { // Query should use same bpt out as user sets @@ -221,13 +237,13 @@ export function assertSingleTokenExit( removeLiquidityKind: removeLiquidityInput.kind, }; - const queryCheck = getCheck(exitQueryResult, true); + const queryCheck = getCheck(removeLiquidityQueryOutput, true); expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect only tokenOut to have amount > 0 - // (Note exitQueryResult also has value for bpt if pre-minted) - exitQueryResult.amountsOut.forEach((a) => { + // (Note removeLiquidityQueryOutput also has value for bpt if pre-minted) + removeLiquidityQueryOutput.amountsOut.forEach((a) => { if ( !removeLiquidityInput.exitWithNativeAsset && a.token.address === removeLiquidityInput.tokenOut @@ -241,12 +257,17 @@ export function assertSingleTokenExit( else expect(a.amount).toEqual(0n); }); - assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); + assertExitBuildOutput( + removeLiquidityQueryOutput, + exitBuildOutput, + true, + slippage, + ); assertTokenDeltas( poolStateInput, removeLiquidityInput, - exitQueryResult, + removeLiquidityQueryOutput, txOutput, ); } @@ -258,12 +279,13 @@ export function assertProportionalExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, exitQueryResult, exitBuildOutput } = exitResult; + const { txOutput, removeLiquidityQueryOutput, exitBuildOutput } = + exitResult; const bptToken = new Token(chainId, poolStateInput.address, 18); const expectedQueryOutput: Omit< - ExitQueryResult, + RemoveLiquidityQueryOutput, 'amountsOut' | 'bptIndex' > = { // Query should use same bpt out as user sets @@ -280,23 +302,28 @@ export function assertProportionalExit( removeLiquidityKind: removeLiquidityInput.kind, }; - const queryCheck = getCheck(exitQueryResult, true); + const queryCheck = getCheck(removeLiquidityQueryOutput, true); expect(queryCheck).to.deep.eq(expectedQueryOutput); // Expect all assets in to have an amount > 0 apart from BPT if it exists - exitQueryResult.amountsOut.forEach((a) => { + removeLiquidityQueryOutput.amountsOut.forEach((a) => { if (a.token.address === poolStateInput.address) expect(a.amount).toEqual(0n); else expect(a.amount > 0n).to.be.true; }); - assertExitBuildOutput(exitQueryResult, exitBuildOutput, true, slippage); + assertExitBuildOutput( + removeLiquidityQueryOutput, + exitBuildOutput, + true, + slippage, + ); assertTokenDeltas( poolStateInput, removeLiquidityInput, - exitQueryResult, + removeLiquidityQueryOutput, txOutput, ); } @@ -304,20 +331,20 @@ export function assertProportionalExit( function assertTokenDeltas( poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquidityInput, - exitQueryResult: ExitQueryResult, + removeLiquidityQueryOutput: RemoveLiquidityQueryOutput, txOutput: TxResult, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); - // exitQueryResult amountsOut will have a value for the BPT token if it is a pre-minted pool - const amountsWithoutBpt = [...exitQueryResult.amountsOut].filter( + // removeLiquidityQueryOutput amountsOut will have a value for the BPT token if it is a pre-minted pool + const amountsWithoutBpt = [...removeLiquidityQueryOutput.amountsOut].filter( (t) => t.token.address !== poolStateInput.address, ); // Matching order of getTokens helper: [poolTokens, BPT, native] const expectedDeltas = [ ...amountsWithoutBpt.map((a) => a.amount), - exitQueryResult.bptIn.amount, + removeLiquidityQueryOutput.bptIn.amount, 0n, ]; @@ -334,7 +361,7 @@ function assertTokenDeltas( } function assertExitBuildOutput( - exitQueryResult: ExitQueryResult, + removeLiquidityQueryOutput: RemoveLiquidityQueryOutput, exitBuildOutput: ExitBuildOutput, isExactIn: boolean, slippage: Slippage, @@ -342,17 +369,17 @@ function assertExitBuildOutput( // if exactIn minAmountsOut should use amountsOut with slippage applied, else should use same amountsOut as input // slippage.removeFrom(a.amount) const minAmountsOut = isExactIn - ? exitQueryResult.amountsOut.map((a) => + ? removeLiquidityQueryOutput.amountsOut.map((a) => TokenAmount.fromRawAmount(a.token, slippage.removeFrom(a.amount)), ) - : [...exitQueryResult.amountsOut]; + : [...removeLiquidityQueryOutput.amountsOut]; // if exactIn slippage cannot be applied to bptIn, else should use bptIn with slippage applied const maxBptIn = isExactIn - ? ({ ...exitQueryResult.bptIn } as TokenAmount) + ? ({ ...removeLiquidityQueryOutput.bptIn } as TokenAmount) : TokenAmount.fromRawAmount( - exitQueryResult.bptIn.token, - slippage.applyTo(exitQueryResult.bptIn.amount), + removeLiquidityQueryOutput.bptIn.token, + slippage.applyTo(removeLiquidityQueryOutput.bptIn.amount), ); const expectedBuildOutput: Omit = { From 2cf4d394a9e629e01a4a25c8a10d022403f5e26a Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:20:16 -0300 Subject: [PATCH 188/199] Refactor ExitCall into RemoveLiquidityCall --- .../exit/composable-stable/composableStableExit.ts | 8 +++++--- src/entities/exit/poolExit.ts | 4 ++-- src/entities/exit/types.ts | 13 ++++++++----- src/entities/exit/weighted/weightedExit.ts | 8 ++++---- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/composableStableExit.ts index f5958455..8109feb6 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/composableStableExit.ts @@ -11,7 +11,7 @@ import { vaultAbi } from '../../../abi'; import { parseExitArgs } from '../../utils/parseExitArgs'; import { BaseExit, - ComposableStableExitCall, + RemoveLiquidityComposableStableCall, ExitBuildOutput, RemoveLiquidityInput, RemoveLiquidityKind, @@ -105,7 +105,9 @@ export class ComposableStableExit implements BaseExit { } } - public buildCall(input: ComposableStableExitCall): ExitBuildOutput { + public buildCall( + input: RemoveLiquidityComposableStableCall, + ): ExitBuildOutput { const amounts = this.getAmountsCall(input); const amountsWithoutBpt = { ...amounts, @@ -149,7 +151,7 @@ export class ComposableStableExit implements BaseExit { } private getAmountsCall( - input: ComposableStableExitCall, + input: RemoveLiquidityComposableStableCall, ): RemoveLiquidityAmounts { switch (input.removeLiquidityKind) { case RemoveLiquidityKind.Unbalanced: diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts index d5cc955c..214af780 100644 --- a/src/entities/exit/poolExit.ts +++ b/src/entities/exit/poolExit.ts @@ -1,7 +1,7 @@ import { BaseExit, ExitBuildOutput, - ExitCall, + RemoveLiquidityCall, ExitConfig, RemoveLiquidityInput, RemoveLiquidityQueryOutput, @@ -49,7 +49,7 @@ export class PoolExit { return this.getExit(poolState.type).query(input, mappedPoolState); } - public buildCall(input: ExitCall): ExitBuildOutput { + public buildCall(input: RemoveLiquidityCall): ExitBuildOutput { return this.getExit(input.poolType).buildCall(input); } } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 5a23f342..885b564f 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -58,16 +58,19 @@ export type RemoveLiquidityComposableStableQueryOutput = bptIndex: number; }; -type BaseExitCall = { +type RemoveLiquidityBaseCall = { slippage: Slippage; sender: Address; recipient: Address; }; -export type ComposableStableExitCall = BaseExitCall & +export type RemoveLiquidityComposableStableCall = RemoveLiquidityBaseCall & RemoveLiquidityComposableStableQueryOutput; -export type WeightedExitCall = BaseExitCall & RemoveLiquidityBaseQueryOutput; +export type RemoveLiquidityWeightedCall = RemoveLiquidityBaseCall & + RemoveLiquidityBaseQueryOutput; -export type ExitCall = ComposableStableExitCall | WeightedExitCall; +export type RemoveLiquidityCall = + | RemoveLiquidityComposableStableCall + | RemoveLiquidityWeightedCall; export type ExitBuildOutput = { call: Address; @@ -82,7 +85,7 @@ export interface BaseExit { input: RemoveLiquidityInput, poolState: PoolState, ): Promise; - buildCall(input: ExitCall): ExitBuildOutput; + buildCall(input: RemoveLiquidityCall): ExitBuildOutput; } export type ExitConfig = { diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/weightedExit.ts index 1a2f6243..f1a75295 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/weightedExit.ts @@ -13,11 +13,11 @@ import { parseExitArgs } from '../../utils/parseExitArgs'; import { BaseExit, ExitBuildOutput, - ExitCall, + RemoveLiquidityCall, RemoveLiquidityInput, RemoveLiquidityKind, RemoveLiquidityQueryOutput, - WeightedExitCall, + RemoveLiquidityWeightedCall, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; @@ -97,7 +97,7 @@ export class WeightedExit implements BaseExit { } } - public buildCall(input: WeightedExitCall): ExitBuildOutput { + public buildCall(input: RemoveLiquidityWeightedCall): ExitBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData( @@ -135,7 +135,7 @@ export class WeightedExit implements BaseExit { }; } - private getAmountsCall(input: ExitCall): RemoveLiquidityAmounts { + private getAmountsCall(input: RemoveLiquidityCall): RemoveLiquidityAmounts { switch (input.removeLiquidityKind) { case RemoveLiquidityKind.Unbalanced: return { From 1332692585bc955dcf9e7805102d9fe9ca1d6295 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:31:21 -0300 Subject: [PATCH 189/199] Refactor PoolExit into RemoveLiquidity --- README.md | 8 +-- examples/exitPool.ts | 8 +-- ....ts => removeLiquidityComposableStable.ts} | 4 +- src/entities/exit/index.ts | 2 +- src/entities/exit/poolExit.ts | 55 ----------------- src/entities/exit/removeLiquidity.ts | 59 +++++++++++++++++++ src/entities/exit/types.ts | 6 +- ...htedExit.ts => removeLiquidityWeighted.ts} | 4 +- test/composableStableExit.integration.test.ts | 6 +- test/lib/utils/exitHelper.ts | 12 ++-- test/lib/utils/types.ts | 4 +- test/weightedExit.integration.test.ts | 6 +- 12 files changed, 89 insertions(+), 85 deletions(-) rename src/entities/exit/composable-stable/{composableStableExit.ts => removeLiquidityComposableStable.ts} (98%) delete mode 100644 src/entities/exit/poolExit.ts create mode 100644 src/entities/exit/removeLiquidity.ts rename src/entities/exit/weighted/{weightedExit.ts => removeLiquidityWeighted.ts} (98%) diff --git a/README.md b/README.md index 6c000d15..6be27c28 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Full working add liquidity example: [examples/addLiquidity.ts](./examples/addLiq ### Usage for Exiting Pool ```ts -import { BalancerApi, PoolExit } from "@balancer/sdk"; +import { BalancerApi, RemoveLiquidity } from "@balancer/sdk"; ... const removeLiquidityInput: RemoveLiquiditySingleTokenInput = { chainId, @@ -81,10 +81,10 @@ const removeLiquidityInput: RemoveLiquiditySingleTokenInput = { const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); const poolState = await balancerApi.pools.fetchPoolState('0x5f1d6874cb1e7156e79a7563d2b61c6cbce03150000200000000000000000586'); -const poolExit = new PoolExit(); -const queryOutput = await poolExit.query(removeLiquidityInput, poolState); +const removeLiquidity = new RemoveLiquidity(); +const queryOutput = await removeLiquidity.query(removeLiquidityInput, poolState); const { call, to, value, maxAmountsIn, minBptOut } = - poolExit.buildCall({ + removeLiquidity.buildCall({ ...queryOutput, slippage, sender: signerAddress, diff --git a/examples/exitPool.ts b/examples/exitPool.ts index 03443d48..b6ccea5f 100644 --- a/examples/exitPool.ts +++ b/examples/exitPool.ts @@ -11,7 +11,7 @@ dotenv.config(); import { ChainId, RemoveLiquidityKind, - PoolExit, + RemoveLiquidity, PoolStateInput, Slippage, InputAmount, @@ -57,8 +57,8 @@ const exit = async () => { }; // Simulate the exit to get the tokens out - const poolExit = new PoolExit(); - const queryOutput = await poolExit.query( + const removeLiquidity = new RemoveLiquidity(); + const queryOutput = await removeLiquidity.query( removeLiquidityInput, poolStateInput, ); @@ -70,7 +70,7 @@ const exit = async () => { ); // Apply slippage to the tokens out received from the query and construct the call - const call = poolExit.buildCall({ + const call = removeLiquidity.buildCall({ ...queryOutput, slippage, sender: userAccount, diff --git a/src/entities/exit/composable-stable/composableStableExit.ts b/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts similarity index 98% rename from src/entities/exit/composable-stable/composableStableExit.ts rename to src/entities/exit/composable-stable/removeLiquidityComposableStable.ts index 8109feb6..1c1f89ca 100644 --- a/src/entities/exit/composable-stable/composableStableExit.ts +++ b/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts @@ -10,7 +10,7 @@ import { import { vaultAbi } from '../../../abi'; import { parseExitArgs } from '../../utils/parseExitArgs'; import { - BaseExit, + RemoveLiquidityBase, RemoveLiquidityComposableStableCall, ExitBuildOutput, RemoveLiquidityInput, @@ -22,7 +22,7 @@ import { doQueryExit } from '../../utils/doQueryExit'; import { ComposableStableEncoder } from '../../encoders/composableStable'; import { getAmounts } from '../../utils'; -export class ComposableStableExit implements BaseExit { +export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { public async query( input: RemoveLiquidityInput, poolState: PoolState, diff --git a/src/entities/exit/index.ts b/src/entities/exit/index.ts index 318c97dc..9c1cf5b4 100644 --- a/src/entities/exit/index.ts +++ b/src/entities/exit/index.ts @@ -1,2 +1,2 @@ -export * from './poolExit'; +export * from './removeLiquidity'; export * from './types'; diff --git a/src/entities/exit/poolExit.ts b/src/entities/exit/poolExit.ts deleted file mode 100644 index 214af780..00000000 --- a/src/entities/exit/poolExit.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - BaseExit, - ExitBuildOutput, - RemoveLiquidityCall, - ExitConfig, - RemoveLiquidityInput, - RemoveLiquidityQueryOutput, -} from './types'; -import { WeightedExit } from './weighted/weightedExit'; -import { PoolStateInput } from '../types'; -import { validateInputs } from './utils/validateInputs'; -import { getSortedTokens } from '../utils/getSortedTokens'; -import { ComposableStableExit } from './composable-stable/composableStableExit'; - -export class PoolExit { - private readonly poolExits: Record = {}; - - constructor(config?: ExitConfig) { - const { customPoolExits } = config || {}; - this.poolExits = { - WEIGHTED: new WeightedExit(), - // PHANTOM_STABLE === ComposableStables in API - PHANTOM_STABLE: new ComposableStableExit(), - // 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: RemoveLiquidityInput, - poolState: PoolStateInput, - ): Promise { - 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: RemoveLiquidityCall): ExitBuildOutput { - return this.getExit(input.poolType).buildCall(input); - } -} diff --git a/src/entities/exit/removeLiquidity.ts b/src/entities/exit/removeLiquidity.ts new file mode 100644 index 00000000..5bf75ca7 --- /dev/null +++ b/src/entities/exit/removeLiquidity.ts @@ -0,0 +1,59 @@ +import { + RemoveLiquidityBase, + ExitBuildOutput, + RemoveLiquidityCall, + RemoveLiquidityConfig, + RemoveLiquidityInput, + RemoveLiquidityQueryOutput, +} from './types'; +import { RemoveLiquidityWeighted } from './weighted/removeLiquidityWeighted'; +import { PoolStateInput } from '../types'; +import { validateInputs } from './utils/validateInputs'; +import { getSortedTokens } from '../utils/getSortedTokens'; +import { RemoveLiquidityComposableStable } from './composable-stable/removeLiquidityComposableStable'; + +export class RemoveLiquidity { + private readonly removeLiquidityTypes: Record = + {}; + + constructor(config?: RemoveLiquidityConfig) { + const { customRemoveLiquidityTypes } = config || {}; + this.removeLiquidityTypes = { + WEIGHTED: new RemoveLiquidityWeighted(), + // PHANTOM_STABLE === ComposableStables in API + PHANTOM_STABLE: new RemoveLiquidityComposableStable(), + // custom pool Exits take precedence over base Exits + ...customRemoveLiquidityTypes, + }; + } + + public getRemoveLiquidity(poolType: string): RemoveLiquidityBase { + if (!this.removeLiquidityTypes[poolType]) { + throw new Error('Unsupported pool type'); + } + + return this.removeLiquidityTypes[poolType]; + } + + public async query( + input: RemoveLiquidityInput, + poolState: PoolStateInput, + ): Promise { + validateInputs(input, poolState); + + const sortedTokens = getSortedTokens(poolState.tokens, input.chainId); + const mappedPoolState = { + ...poolState, + tokens: sortedTokens, + }; + + return this.getRemoveLiquidity(poolState.type).query( + input, + mappedPoolState, + ); + } + + public buildCall(input: RemoveLiquidityCall): ExitBuildOutput { + return this.getRemoveLiquidity(input.poolType).buildCall(input); + } +} diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 885b564f..465c52b6 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -80,7 +80,7 @@ export type ExitBuildOutput = { minAmountsOut: TokenAmount[]; }; -export interface BaseExit { +export interface RemoveLiquidityBase { query( input: RemoveLiquidityInput, poolState: PoolState, @@ -88,8 +88,8 @@ export interface BaseExit { buildCall(input: RemoveLiquidityCall): ExitBuildOutput; } -export type ExitConfig = { - customPoolExits: Record; +export type RemoveLiquidityConfig = { + customRemoveLiquidityTypes: Record; }; export type ExitPoolRequest = { diff --git a/src/entities/exit/weighted/weightedExit.ts b/src/entities/exit/weighted/removeLiquidityWeighted.ts similarity index 98% rename from src/entities/exit/weighted/weightedExit.ts rename to src/entities/exit/weighted/removeLiquidityWeighted.ts index f1a75295..2b442548 100644 --- a/src/entities/exit/weighted/weightedExit.ts +++ b/src/entities/exit/weighted/removeLiquidityWeighted.ts @@ -11,7 +11,7 @@ import { import { vaultAbi } from '../../../abi'; import { parseExitArgs } from '../../utils/parseExitArgs'; import { - BaseExit, + RemoveLiquidityBase, ExitBuildOutput, RemoveLiquidityCall, RemoveLiquidityInput, @@ -23,7 +23,7 @@ import { RemoveLiquidityAmounts, PoolState } from '../../types'; import { doQueryExit } from '../../utils/doQueryExit'; import { getAmounts } from '../../utils'; -export class WeightedExit implements BaseExit { +export class RemoveLiquidityWeighted implements RemoveLiquidityBase { public async query( input: RemoveLiquidityInput, poolState: PoolState, diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index 10eb55d0..f97d5adf 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- weightedExit.integration.test.ts +// pnpm test -- removeLiquidityWeighted.integration.test.ts import { describe, test, beforeAll, beforeEach } from 'vitest'; import { config } from 'dotenv'; config(); @@ -19,7 +19,7 @@ import { Slippage, Token, PoolStateInput, - PoolExit, + RemoveLiquidity, Address, Hex, CHAINS, @@ -64,7 +64,7 @@ describe('composable stable exit test', () => { txInput = { client, - poolExit: new PoolExit(), + removeLiquidity: new RemoveLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 6074638f..5cbf2805 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -27,7 +27,7 @@ type ExitResult = { }; export const sdkExit = async ({ - poolExit, + removeLiquidity, removeLiquidityInput, poolStateInput, slippage, @@ -36,11 +36,11 @@ export const sdkExit = async ({ exitBuildOutput: ExitBuildOutput; removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; }> => { - const removeLiquidityQueryOutput = await poolExit.query( + const removeLiquidityQueryOutput = await removeLiquidity.query( removeLiquidityInput, poolStateInput, ); - const exitBuildOutput = poolExit.buildCall({ + const exitBuildOutput = removeLiquidity.buildCall({ ...removeLiquidityQueryOutput, slippage, sender: testAddress, @@ -93,7 +93,7 @@ function getCheck(result: RemoveLiquidityQueryOutput, isExactIn: boolean) { * Create and submit exit transaction. * @param txInput * @param client: Client & PublicActions & WalletActions - The RPC client - * @param poolExit: PoolExit - The pool exit class, used to query the exit and build the exit call + * @param removeLiquidity: RemoveLiquidity - The pool exit class, used to query the exit and build the exit call * @param removeLiquidityInput: RemoveLiquidityInput - The parameters of the exit transaction, example: bptIn, amountsOut, etc. * @param slippage: Slippage - The slippage tolerance for the exit transaction * @param poolStateInput: PoolStateInput - The state of the pool being exited @@ -101,7 +101,7 @@ function getCheck(result: RemoveLiquidityQueryOutput, isExactIn: boolean) { * */ export async function doExit(txInput: ExitTxInput) { const { - poolExit, + removeLiquidity, poolStateInput, removeLiquidityInput, testAddress, @@ -110,7 +110,7 @@ export async function doExit(txInput: ExitTxInput) { } = txInput; const { removeLiquidityQueryOutput, exitBuildOutput } = await sdkExit({ - poolExit, + removeLiquidity, removeLiquidityInput, poolStateInput, slippage, diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index f1f05f1a..481a20cb 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -3,7 +3,7 @@ import { Address, AddLiquidityInput, RemoveLiquidityInput, - PoolExit, + RemoveLiquidity, AddLiquidity, PoolStateInput, Slippage, @@ -20,7 +20,7 @@ export type AddLiquidityTxInput = { export type ExitTxInput = { client: Client & PublicActions & TestActions & WalletActions; - poolExit: PoolExit; + removeLiquidity: RemoveLiquidity; removeLiquidityInput: RemoveLiquidityInput; slippage: Slippage; poolStateInput: PoolStateInput; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 70872553..dcd41b1e 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- weightedExit.integration.test.ts +// pnpm test -- removeLiquidityWeighted.integration.test.ts import { describe, test, beforeAll, beforeEach } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -18,7 +18,7 @@ import { RemoveLiquidityKind, Slippage, PoolStateInput, - PoolExit, + RemoveLiquidity, Address, Hex, CHAINS, @@ -62,7 +62,7 @@ describe('weighted exit test', () => { txInput = { client, - poolExit: new PoolExit(), + removeLiquidity: new RemoveLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig From b63f1b8b3700a6c1d437e03949b8c051b8677b6b Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:32:13 -0300 Subject: [PATCH 190/199] Refactor ExitBuildOutput into RemoveLiquidityOutput --- .../removeLiquidityComposableStable.ts | 4 +- src/entities/exit/removeLiquidity.ts | 4 +- src/entities/exit/types.ts | 4 +- .../exit/weighted/removeLiquidityWeighted.ts | 6 +- test/lib/utils/exitHelper.ts | 59 ++++++++++--------- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts b/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts index 1c1f89ca..ed1bf911 100644 --- a/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts +++ b/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts @@ -12,7 +12,7 @@ import { parseExitArgs } from '../../utils/parseExitArgs'; import { RemoveLiquidityBase, RemoveLiquidityComposableStableCall, - ExitBuildOutput, + RemoveLiquidityBuildOutput, RemoveLiquidityInput, RemoveLiquidityKind, RemoveLiquidityQueryOutput, @@ -107,7 +107,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { public buildCall( input: RemoveLiquidityComposableStableCall, - ): ExitBuildOutput { + ): RemoveLiquidityBuildOutput { const amounts = this.getAmountsCall(input); const amountsWithoutBpt = { ...amounts, diff --git a/src/entities/exit/removeLiquidity.ts b/src/entities/exit/removeLiquidity.ts index 5bf75ca7..bfbf7a54 100644 --- a/src/entities/exit/removeLiquidity.ts +++ b/src/entities/exit/removeLiquidity.ts @@ -1,6 +1,6 @@ import { RemoveLiquidityBase, - ExitBuildOutput, + RemoveLiquidityBuildOutput, RemoveLiquidityCall, RemoveLiquidityConfig, RemoveLiquidityInput, @@ -53,7 +53,7 @@ export class RemoveLiquidity { ); } - public buildCall(input: RemoveLiquidityCall): ExitBuildOutput { + public buildCall(input: RemoveLiquidityCall): RemoveLiquidityBuildOutput { return this.getRemoveLiquidity(input.poolType).buildCall(input); } } diff --git a/src/entities/exit/types.ts b/src/entities/exit/types.ts index 465c52b6..bf257176 100644 --- a/src/entities/exit/types.ts +++ b/src/entities/exit/types.ts @@ -72,7 +72,7 @@ export type RemoveLiquidityCall = | RemoveLiquidityComposableStableCall | RemoveLiquidityWeightedCall; -export type ExitBuildOutput = { +export type RemoveLiquidityBuildOutput = { call: Address; to: Address; value: bigint; @@ -85,7 +85,7 @@ export interface RemoveLiquidityBase { input: RemoveLiquidityInput, poolState: PoolState, ): Promise; - buildCall(input: RemoveLiquidityCall): ExitBuildOutput; + buildCall(input: RemoveLiquidityCall): RemoveLiquidityBuildOutput; } export type RemoveLiquidityConfig = { diff --git a/src/entities/exit/weighted/removeLiquidityWeighted.ts b/src/entities/exit/weighted/removeLiquidityWeighted.ts index 2b442548..84006c0d 100644 --- a/src/entities/exit/weighted/removeLiquidityWeighted.ts +++ b/src/entities/exit/weighted/removeLiquidityWeighted.ts @@ -12,7 +12,7 @@ import { vaultAbi } from '../../../abi'; import { parseExitArgs } from '../../utils/parseExitArgs'; import { RemoveLiquidityBase, - ExitBuildOutput, + RemoveLiquidityBuildOutput, RemoveLiquidityCall, RemoveLiquidityInput, RemoveLiquidityKind, @@ -97,7 +97,9 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { } } - public buildCall(input: RemoveLiquidityWeightedCall): ExitBuildOutput { + public buildCall( + input: RemoveLiquidityWeightedCall, + ): RemoveLiquidityBuildOutput { const amounts = this.getAmountsCall(input); const userData = this.encodeUserData( diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 5cbf2805..3907fafe 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -2,7 +2,7 @@ import { ExitTxInput } from './types'; import { ChainId, RemoveLiquidityComposableStableQueryOutput, - ExitBuildOutput, + RemoveLiquidityBuildOutput, RemoveLiquidityQueryOutput, NATIVE_ASSETS, PoolStateInput, @@ -22,7 +22,7 @@ import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type ExitResult = { removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; - exitBuildOutput: ExitBuildOutput; + RemoveLiquidityBuildOutput: RemoveLiquidityBuildOutput; txOutput: TxResult; }; @@ -33,14 +33,14 @@ export const sdkExit = async ({ slippage, testAddress, }: Omit): Promise<{ - exitBuildOutput: ExitBuildOutput; + RemoveLiquidityBuildOutput: RemoveLiquidityBuildOutput; removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; }> => { const removeLiquidityQueryOutput = await removeLiquidity.query( removeLiquidityInput, poolStateInput, ); - const exitBuildOutput = removeLiquidity.buildCall({ + const RemoveLiquidityBuildOutput = removeLiquidity.buildCall({ ...removeLiquidityQueryOutput, slippage, sender: testAddress, @@ -48,7 +48,7 @@ export const sdkExit = async ({ }); return { - exitBuildOutput, + RemoveLiquidityBuildOutput, removeLiquidityQueryOutput, }; }; @@ -109,13 +109,14 @@ export async function doExit(txInput: ExitTxInput) { slippage, } = txInput; - const { removeLiquidityQueryOutput, exitBuildOutput } = await sdkExit({ - removeLiquidity, - removeLiquidityInput, - poolStateInput, - slippage, - testAddress, - }); + const { removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = + await sdkExit({ + removeLiquidity, + removeLiquidityInput, + poolStateInput, + slippage, + testAddress, + }); // get tokens for balance change - pool tokens, BPT, native const tokens = getTokensForBalanceCheck(poolStateInput); @@ -125,14 +126,14 @@ export async function doExit(txInput: ExitTxInput) { tokens, client, testAddress, - exitBuildOutput.to, - exitBuildOutput.call, - exitBuildOutput.value, + RemoveLiquidityBuildOutput.to, + RemoveLiquidityBuildOutput.call, + RemoveLiquidityBuildOutput.value, ); return { removeLiquidityQueryOutput, - exitBuildOutput, + RemoveLiquidityBuildOutput, txOutput, }; } @@ -144,7 +145,7 @@ export function assertUnbalancedExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, removeLiquidityQueryOutput, exitBuildOutput } = + const { txOutput, removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = exitResult; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) @@ -184,9 +185,9 @@ export function assertUnbalancedExit( // Expect some bpt amount expect(removeLiquidityQueryOutput.bptIn.amount > 0n).to.be.true; - assertExitBuildOutput( + assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput, - exitBuildOutput, + RemoveLiquidityBuildOutput, false, slippage, ); @@ -206,7 +207,7 @@ export function assertSingleTokenExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, removeLiquidityQueryOutput, exitBuildOutput } = + const { txOutput, removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = exitResult; if (removeLiquidityQueryOutput.tokenOutIndex === undefined) @@ -257,9 +258,9 @@ export function assertSingleTokenExit( else expect(a.amount).toEqual(0n); }); - assertExitBuildOutput( + assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput, - exitBuildOutput, + RemoveLiquidityBuildOutput, true, slippage, ); @@ -279,7 +280,7 @@ export function assertProportionalExit( exitResult: ExitResult, slippage: Slippage, ) { - const { txOutput, removeLiquidityQueryOutput, exitBuildOutput } = + const { txOutput, removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = exitResult; const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -313,9 +314,9 @@ export function assertProportionalExit( else expect(a.amount > 0n).to.be.true; }); - assertExitBuildOutput( + assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput, - exitBuildOutput, + RemoveLiquidityBuildOutput, true, slippage, ); @@ -360,9 +361,9 @@ function assertTokenDeltas( expect(txOutput.balanceDeltas).to.deep.eq(expectedDeltas); } -function assertExitBuildOutput( +function assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput: RemoveLiquidityQueryOutput, - exitBuildOutput: ExitBuildOutput, + RemoveLiquidityBuildOutput: RemoveLiquidityBuildOutput, isExactIn: boolean, slippage: Slippage, ) { @@ -382,7 +383,7 @@ function assertExitBuildOutput( slippage.applyTo(removeLiquidityQueryOutput.bptIn.amount), ); - const expectedBuildOutput: Omit = { + const expectedBuildOutput: Omit = { minAmountsOut, maxBptIn, to: BALANCER_VAULT, @@ -391,6 +392,6 @@ function assertExitBuildOutput( }; // rome-ignore lint/correctness/noUnusedVariables: - const { call, ...buildCheck } = exitBuildOutput; + const { call, ...buildCheck } = RemoveLiquidityBuildOutput; expect(buildCheck).to.deep.eq(expectedBuildOutput); } From 30ea9e36ec429d90a187c5a14054a4e24ef9fa3c Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 15:44:37 -0300 Subject: [PATCH 191/199] Refactor encoders and other files from exit to removeLiquidity --- README.md | 2 +- src/entities/encoders/composableStable.ts | 12 +++---- src/entities/encoders/weighted.ts | 12 +++---- .../removeLiquidityComposableStable.ts | 16 ++++----- .../exit/weighted/removeLiquidityWeighted.ts | 18 +++++----- .../{doQueryExit.ts => doRemoveLiquidity.ts} | 2 +- ...xitArgs.ts => parseRemoveLiquidityArgs.ts} | 2 +- test/composableStableExit.integration.test.ts | 36 +++++++++---------- test/lib/utils/exitHelper.ts | 34 +++++++++--------- test/lib/utils/types.ts | 2 +- test/weightedExit.integration.test.ts | 36 +++++++++---------- 11 files changed, 87 insertions(+), 85 deletions(-) rename src/entities/utils/{doQueryExit.ts => doRemoveLiquidity.ts} (95%) rename src/entities/utils/{parseExitArgs.ts => parseRemoveLiquidityArgs.ts} (96%) diff --git a/README.md b/README.md index 6be27c28..3f8250f0 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ it can be used for: ``` Full working add liquidity example: [examples/addLiquidity.ts](./examples/addLiquidity.ts) -### Usage for Exiting Pool +### Usage for removing liquidity from a Pool ```ts import { BalancerApi, RemoveLiquidity } from "@balancer/sdk"; ... diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index f533b6fd..98ac37c5 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -89,11 +89,11 @@ export class ComposableStableEncoder { }; /** - * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT + * Encodes the userData parameter for removing liquidity from a ComposableStablePool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned * @param enterTokenIndex - the index of the token to removed from the pool */ - static exitSingleAsset = ( + static removeLiquiditySingleToken = ( bptAmountIn: bigint, exitTokenIndex: number, ): Address => { @@ -110,10 +110,10 @@ export class ComposableStableEncoder { }; /** - * Encodes the userData parameter for exiting a ComposableStablePool by removing tokens in return for an exact amount of BPT + * Encodes the userData parameter for removing liquidity from a ComposableStablePool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned */ - static exitProportional = (bptAmountIn: bigint): Address => { + static removeLiquidityProportional = (bptAmountIn: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ @@ -126,11 +126,11 @@ export class ComposableStableEncoder { }; /** - * Encodes the userData parameter for exiting a ComposableStablePool by removing exact amounts of tokens + * Encodes the userData parameter for removing liquidity from a ComposableStablePool by removing exact amounts of tokens * @param amountsOut - the amounts of each token to be withdrawn from the pool * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens */ - static exitUnbalanced = ( + static removeLiquidityUnbalanced = ( amountsOut: bigint[], maxBPTAmountIn: bigint, ): Address => diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index 5b4b3951..a59a7192 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -86,11 +86,11 @@ export class WeightedEncoder { }; /** - * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT + * Encodes the userData parameter for removing liquidity from a WeightedPool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned * @param enterTokenIndex - the index of the token to removed from the pool */ - static exitSingleAsset = ( + static removeLiquiditySingleToken = ( bptAmountIn: bigint, exitTokenIndex: number, ): Address => { @@ -105,10 +105,10 @@ export class WeightedEncoder { }; /** - * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT + * Encodes the userData parameter for removing liquidity from a WeightedPool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned */ - static exitProportional = (bptAmountIn: bigint): Address => { + static removeLiquidityProportional = (bptAmountIn: bigint): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }], [ @@ -119,11 +119,11 @@ export class WeightedEncoder { }; /** - * Encodes the userData parameter for exiting a WeightedPool by removing exact amounts of tokens + * Encodes the userData parameter for removing liquidity from a WeightedPool by removing exact amounts of tokens * @param amountsOut - the amounts of each token to be withdrawn from the pool * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens */ - static exitUnbalanced = ( + static removeLiquidityUnbalanced = ( amountsOut: bigint[], maxBPTAmountIn: bigint, ): Address => diff --git a/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts b/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts index ed1bf911..b34d5ac7 100644 --- a/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts +++ b/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts @@ -8,7 +8,7 @@ import { ZERO_ADDRESS, } from '../../../utils/constants'; import { vaultAbi } from '../../../abi'; -import { parseExitArgs } from '../../utils/parseExitArgs'; +import { parseRemoveLiquidityArgs } from '../../utils/parseRemoveLiquidityArgs'; import { RemoveLiquidityBase, RemoveLiquidityComposableStableCall, @@ -18,7 +18,7 @@ import { RemoveLiquidityQueryOutput, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; -import { doQueryExit } from '../../utils/doQueryExit'; +import { doRemoveLiquidity } from '../../utils/doRemoveLiquidity'; import { ComposableStableEncoder } from '../../encoders/composableStable'; import { getAmounts } from '../../utils'; @@ -41,7 +41,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { const userData = this.encodeUserData(input.kind, amountsWithoutBpt); // tokensOut will have zero address if exit with native asset - const { args, tokensOut } = parseExitArgs({ + const { args, tokensOut } = parseRemoveLiquidityArgs({ chainId: input.chainId, exitWithNativeAsset: !!input.exitWithNativeAsset, poolId: poolState.id, @@ -52,7 +52,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { userData, toInternalBalance: !!input.toInternalBalance, }); - const queryOutput = await doQueryExit( + const queryOutput = await doRemoveLiquidity( input.rpcUrl, input.chainId, args, @@ -121,7 +121,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { amountsWithoutBpt, ); - const { args } = parseExitArgs({ + const { args } = parseRemoveLiquidityArgs({ poolId: input.poolId, sortedTokens: input.amountsOut.map((a) => a.token), sender: input.sender, @@ -192,7 +192,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { ): Address { switch (kind) { case RemoveLiquidityKind.Unbalanced: - return ComposableStableEncoder.exitUnbalanced( + return ComposableStableEncoder.removeLiquidityUnbalanced( amounts.minAmountsOut, amounts.maxBptAmountIn, ); @@ -200,12 +200,12 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { if (amounts.tokenOutIndex === undefined) throw Error('No Index'); - return ComposableStableEncoder.exitSingleAsset( + return ComposableStableEncoder.removeLiquiditySingleToken( amounts.maxBptAmountIn, amounts.tokenOutIndex, ); case RemoveLiquidityKind.Proportional: - return ComposableStableEncoder.exitProportional( + return ComposableStableEncoder.removeLiquidityProportional( amounts.maxBptAmountIn, ); default: diff --git a/src/entities/exit/weighted/removeLiquidityWeighted.ts b/src/entities/exit/weighted/removeLiquidityWeighted.ts index 84006c0d..abbfdf7e 100644 --- a/src/entities/exit/weighted/removeLiquidityWeighted.ts +++ b/src/entities/exit/weighted/removeLiquidityWeighted.ts @@ -9,7 +9,7 @@ import { ZERO_ADDRESS, } from '../../../utils/constants'; import { vaultAbi } from '../../../abi'; -import { parseExitArgs } from '../../utils/parseExitArgs'; +import { parseRemoveLiquidityArgs } from '../../utils/parseRemoveLiquidityArgs'; import { RemoveLiquidityBase, RemoveLiquidityBuildOutput, @@ -20,7 +20,7 @@ import { RemoveLiquidityWeightedCall, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; -import { doQueryExit } from '../../utils/doQueryExit'; +import { doRemoveLiquidity } from '../../utils/doRemoveLiquidity'; import { getAmounts } from '../../utils'; export class RemoveLiquidityWeighted implements RemoveLiquidityBase { @@ -33,7 +33,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { const userData = this.encodeUserData(input.kind, amounts); // tokensOut will have zero address if exit with native asset - const { args, tokensOut } = parseExitArgs({ + const { args, tokensOut } = parseRemoveLiquidityArgs({ chainId: input.chainId, exitWithNativeAsset: !!input.exitWithNativeAsset, poolId: poolState.id, @@ -45,7 +45,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { toInternalBalance: !!input.toInternalBalance, }); - const queryOutput = await doQueryExit( + const queryOutput = await doRemoveLiquidity( input.rpcUrl, input.chainId, args, @@ -107,7 +107,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { amounts, ); - const { args } = parseExitArgs({ + const { args } = parseRemoveLiquidityArgs({ poolId: input.poolId, sortedTokens: input.amountsOut.map((a) => a.token), sender: input.sender, @@ -177,7 +177,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { ): Address { switch (kind) { case RemoveLiquidityKind.Unbalanced: - return WeightedEncoder.exitUnbalanced( + return WeightedEncoder.removeLiquidityUnbalanced( amounts.minAmountsOut, amounts.maxBptAmountIn, ); @@ -185,12 +185,14 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { if (amounts.tokenOutIndex === undefined) throw Error('No Index'); - return WeightedEncoder.exitSingleAsset( + return WeightedEncoder.removeLiquiditySingleToken( amounts.maxBptAmountIn, amounts.tokenOutIndex, ); case RemoveLiquidityKind.Proportional: - return WeightedEncoder.exitProportional(amounts.maxBptAmountIn); + return WeightedEncoder.removeLiquidityProportional( + amounts.maxBptAmountIn, + ); default: throw Error('Unsupported Exit Type'); } diff --git a/src/entities/utils/doQueryExit.ts b/src/entities/utils/doRemoveLiquidity.ts similarity index 95% rename from src/entities/utils/doQueryExit.ts rename to src/entities/utils/doRemoveLiquidity.ts index 7cc8124c..7d6b03b5 100644 --- a/src/entities/utils/doQueryExit.ts +++ b/src/entities/utils/doRemoveLiquidity.ts @@ -4,7 +4,7 @@ import { BALANCER_HELPERS, CHAINS } from '../../utils/constants'; import { balancerHelpersAbi } from '../../abi'; import { ExitPoolRequest } from '../exit/types'; -export async function doQueryExit( +export async function doRemoveLiquidity( rpcUrl: string, chainId: number, args: readonly [Address, Address, Address, ExitPoolRequest], diff --git a/src/entities/utils/parseExitArgs.ts b/src/entities/utils/parseRemoveLiquidityArgs.ts similarity index 96% rename from src/entities/utils/parseExitArgs.ts rename to src/entities/utils/parseRemoveLiquidityArgs.ts index 0ba9de43..ec303ffd 100644 --- a/src/entities/utils/parseExitArgs.ts +++ b/src/entities/utils/parseRemoveLiquidityArgs.ts @@ -3,7 +3,7 @@ import { Token } from '../token'; import { ExitPoolRequest } from '../exit/types'; import { replaceWrapped } from './replaceWrapped'; -export function parseExitArgs({ +export function parseRemoveLiquidityArgs({ chainId, exitWithNativeAsset, sortedTokens, diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index f97d5adf..20ef31f3 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -29,7 +29,7 @@ import { InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { ExitTxInput } from './lib/utils/types'; +import { RemoveLiquidityTxInput } from './lib/utils/types'; import { assertProportionalExit, assertSingleTokenExit, @@ -44,7 +44,7 @@ const poolId = '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool describe('composable stable exit test', () => { - let txInput: ExitTxInput; + let txInput: RemoveLiquidityTxInput; let bptToken: Token; let poolInput: PoolStateInput; beforeAll(async () => { @@ -105,12 +105,12 @@ describe('composable stable exit test', () => { kind: RemoveLiquidityKind.Unbalanced, }; }); - test('exiting with wrapped', async () => { + test('removing liquidity with wrapped', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -118,17 +118,17 @@ describe('composable stable exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); - test('exiting with native', async () => { + test('removing liquidity with native', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), exitWithNativeAsset: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -136,7 +136,7 @@ describe('composable stable exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); @@ -159,8 +159,8 @@ describe('composable stable exit test', () => { kind: RemoveLiquidityKind.SingleAsset, }; }); - test('exiting with wrapped', async () => { - const exitResult = await doExit({ + test('removing liquidity with wrapped', async () => { + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput: input, }); @@ -169,17 +169,17 @@ describe('composable stable exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); - test('exiting with native', async () => { + test('removing liquidity with native', async () => { const removeLiquidityInput = { ...input, exitWithNativeAsset: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -188,7 +188,7 @@ describe('composable stable exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); @@ -210,7 +210,7 @@ describe('composable stable exit test', () => { }; }); test('with tokens', async () => { - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput: input, }); @@ -219,7 +219,7 @@ describe('composable stable exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); @@ -228,7 +228,7 @@ describe('composable stable exit test', () => { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -236,7 +236,7 @@ describe('composable stable exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/exitHelper.ts index 3907fafe..13eefd9f 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/exitHelper.ts @@ -1,4 +1,4 @@ -import { ExitTxInput } from './types'; +import { RemoveLiquidityTxInput } from './types'; import { ChainId, RemoveLiquidityComposableStableQueryOutput, @@ -20,9 +20,9 @@ import { expect } from 'vitest'; import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; -type ExitResult = { +type RemoveLiquidityOutput = { removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; - RemoveLiquidityBuildOutput: RemoveLiquidityBuildOutput; + removeLiquidityBuildOutput: RemoveLiquidityBuildOutput; txOutput: TxResult; }; @@ -32,7 +32,7 @@ export const sdkExit = async ({ poolStateInput, slippage, testAddress, -}: Omit): Promise<{ +}: Omit): Promise<{ RemoveLiquidityBuildOutput: RemoveLiquidityBuildOutput; removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; }> => { @@ -99,7 +99,7 @@ function getCheck(result: RemoveLiquidityQueryOutput, isExactIn: boolean) { * @param poolStateInput: PoolStateInput - The state of the pool being exited * @param testAddress: Address - The address to send the transaction from * */ -export async function doExit(txInput: ExitTxInput) { +export async function doExit(txInput: RemoveLiquidityTxInput) { const { removeLiquidity, poolStateInput, @@ -142,11 +142,11 @@ export function assertUnbalancedExit( chainId: ChainId, poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquidityUnbalancedInput, - exitResult: ExitResult, + removeLiquidityOutput: RemoveLiquidityOutput, slippage: Slippage, ) { - const { txOutput, removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = - exitResult; + const { txOutput, removeLiquidityQueryOutput, removeLiquidityBuildOutput } = + removeLiquidityOutput; // Get an amount for each pool token defaulting to 0 if not provided as input (this will include BPT token if in tokenList) const expectedAmountsOut = poolStateInput.tokens.map((t) => { @@ -187,7 +187,7 @@ export function assertUnbalancedExit( assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput, - RemoveLiquidityBuildOutput, + removeLiquidityBuildOutput, false, slippage, ); @@ -204,11 +204,11 @@ export function assertSingleTokenExit( chainId: ChainId, poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquiditySingleTokenInput, - exitResult: ExitResult, + removeLiquidityOutput: RemoveLiquidityOutput, slippage: Slippage, ) { - const { txOutput, removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = - exitResult; + const { txOutput, removeLiquidityQueryOutput, removeLiquidityBuildOutput } = + removeLiquidityOutput; if (removeLiquidityQueryOutput.tokenOutIndex === undefined) throw Error('No index'); @@ -260,7 +260,7 @@ export function assertSingleTokenExit( assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput, - RemoveLiquidityBuildOutput, + removeLiquidityBuildOutput, true, slippage, ); @@ -277,11 +277,11 @@ export function assertProportionalExit( chainId: ChainId, poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquidityProportionalInput, - exitResult: ExitResult, + removeLiquidityOutput: RemoveLiquidityOutput, slippage: Slippage, ) { - const { txOutput, removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = - exitResult; + const { txOutput, removeLiquidityQueryOutput, removeLiquidityBuildOutput } = + removeLiquidityOutput; const bptToken = new Token(chainId, poolStateInput.address, 18); @@ -316,7 +316,7 @@ export function assertProportionalExit( assertRemoveLiquidityBuildOutput( removeLiquidityQueryOutput, - RemoveLiquidityBuildOutput, + removeLiquidityBuildOutput, true, slippage, ); diff --git a/test/lib/utils/types.ts b/test/lib/utils/types.ts index 481a20cb..da030039 100644 --- a/test/lib/utils/types.ts +++ b/test/lib/utils/types.ts @@ -18,7 +18,7 @@ export type AddLiquidityTxInput = { testAddress: Address; }; -export type ExitTxInput = { +export type RemoveLiquidityTxInput = { client: Client & PublicActions & TestActions & WalletActions; removeLiquidity: RemoveLiquidity; removeLiquidityInput: RemoveLiquidityInput; diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index dcd41b1e..dcff324d 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -34,7 +34,7 @@ import { assertUnbalancedExit, doExit, } from './lib/utils/exitHelper'; -import { ExitTxInput } from './lib/utils/types'; +import { RemoveLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; const chainId = ChainId.MAINNET; @@ -43,7 +43,7 @@ const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH describe('weighted exit test', () => { - let txInput: ExitTxInput; + let txInput: RemoveLiquidityTxInput; let poolInput: PoolStateInput; beforeAll(async () => { // setup mock api @@ -95,12 +95,12 @@ describe('weighted exit test', () => { kind: RemoveLiquidityKind.Unbalanced, }; }); - test('exiting with wrapped', async () => { + test('removing liquidity with wrapped', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -108,17 +108,17 @@ describe('weighted exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); - test('exiting with native', async () => { + test('removing liquidity with native', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), exitWithNativeAsset: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -126,7 +126,7 @@ describe('weighted exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); @@ -149,8 +149,8 @@ describe('weighted exit test', () => { kind: RemoveLiquidityKind.SingleAsset, }; }); - test('exiting with wrapped', async () => { - const exitResult = await doExit({ + test('removing liquidity with wrapped', async () => { + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput: input, }); @@ -159,17 +159,17 @@ describe('weighted exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); - test('exiting with native', async () => { + test('removing liquidity with native', async () => { const removeLiquidityInput = { ...input, exitWithNativeAsset: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -178,7 +178,7 @@ describe('weighted exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); @@ -200,7 +200,7 @@ describe('weighted exit test', () => { }; }); test('with tokens', async () => { - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput: input, }); @@ -209,7 +209,7 @@ describe('weighted exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); @@ -218,7 +218,7 @@ describe('weighted exit test', () => { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doExit({ ...txInput, removeLiquidityInput, }); @@ -226,7 +226,7 @@ describe('weighted exit test', () => { txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); From 26025a83176cd276b9fd53a1102b73831e23e80a Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 16:03:06 -0300 Subject: [PATCH 192/199] Refactor remaining exit to removeLiquidity --- src/entities/index.ts | 2 +- .../removeLiquidityComposableStable.ts | 4 +- .../{exit => removeLiquidity}/index.ts | 0 .../removeLiquidity.ts | 0 .../{exit => removeLiquidity}/types.ts | 0 .../utils/validateInputs.ts | 0 .../weighted/removeLiquidityWeighted.ts | 4 +- src/entities/utils/doRemoveLiquidity.ts | 2 +- .../utils/parseRemoveLiquidityArgs.ts | 2 +- test/composableStableExit.integration.test.ts | 50 +++++++++---------- ...exitHelper.ts => removeLiquidityHelper.ts} | 41 ++++++++------- test/weightedExit.integration.test.ts | 50 +++++++++---------- 12 files changed, 77 insertions(+), 78 deletions(-) rename src/entities/{exit => removeLiquidity}/composable-stable/removeLiquidityComposableStable.ts (98%) rename src/entities/{exit => removeLiquidity}/index.ts (100%) rename src/entities/{exit => removeLiquidity}/removeLiquidity.ts (100%) rename src/entities/{exit => removeLiquidity}/types.ts (100%) rename src/entities/{exit => removeLiquidity}/utils/validateInputs.ts (100%) rename src/entities/{exit => removeLiquidity}/weighted/removeLiquidityWeighted.ts (98%) rename test/lib/utils/{exitHelper.ts => removeLiquidityHelper.ts} (92%) diff --git a/src/entities/index.ts b/src/entities/index.ts index 033c4619..19c78a9d 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -1,6 +1,6 @@ export * from './encoders'; export * from './addLiquidity'; -export * from './exit'; +export * from './removeLiquidity'; export * from './path'; export * from './swap'; export * from './slippage'; diff --git a/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts similarity index 98% rename from src/entities/exit/composable-stable/removeLiquidityComposableStable.ts rename to src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts index b34d5ac7..1e2275f2 100644 --- a/src/entities/exit/composable-stable/removeLiquidityComposableStable.ts +++ b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts @@ -182,7 +182,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { maxBptAmountIn: input.bptIn.amount, }; default: - throw Error('Unsupported Exit Type'); + throw Error('Unsupported Remove Liquidity Kind'); } } @@ -209,7 +209,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { amounts.maxBptAmountIn, ); default: - throw Error('Unsupported Exit Type'); + throw Error('Unsupported Remove Liquidity Kind'); } } } diff --git a/src/entities/exit/index.ts b/src/entities/removeLiquidity/index.ts similarity index 100% rename from src/entities/exit/index.ts rename to src/entities/removeLiquidity/index.ts diff --git a/src/entities/exit/removeLiquidity.ts b/src/entities/removeLiquidity/removeLiquidity.ts similarity index 100% rename from src/entities/exit/removeLiquidity.ts rename to src/entities/removeLiquidity/removeLiquidity.ts diff --git a/src/entities/exit/types.ts b/src/entities/removeLiquidity/types.ts similarity index 100% rename from src/entities/exit/types.ts rename to src/entities/removeLiquidity/types.ts diff --git a/src/entities/exit/utils/validateInputs.ts b/src/entities/removeLiquidity/utils/validateInputs.ts similarity index 100% rename from src/entities/exit/utils/validateInputs.ts rename to src/entities/removeLiquidity/utils/validateInputs.ts diff --git a/src/entities/exit/weighted/removeLiquidityWeighted.ts b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts similarity index 98% rename from src/entities/exit/weighted/removeLiquidityWeighted.ts rename to src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts index abbfdf7e..496e4a14 100644 --- a/src/entities/exit/weighted/removeLiquidityWeighted.ts +++ b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts @@ -167,7 +167,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { maxBptAmountIn: input.bptIn.amount, }; default: - throw Error('Unsupported Exit Type'); + throw Error('Unsupported Remove Liquidity Kind'); } } @@ -194,7 +194,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { amounts.maxBptAmountIn, ); default: - throw Error('Unsupported Exit Type'); + throw Error('Unsupported Remove Liquidity Kind'); } } } diff --git a/src/entities/utils/doRemoveLiquidity.ts b/src/entities/utils/doRemoveLiquidity.ts index 7d6b03b5..8df6233d 100644 --- a/src/entities/utils/doRemoveLiquidity.ts +++ b/src/entities/utils/doRemoveLiquidity.ts @@ -2,7 +2,7 @@ import { createPublicClient, http } from 'viem'; import { Address } from '../../types'; import { BALANCER_HELPERS, CHAINS } from '../../utils/constants'; import { balancerHelpersAbi } from '../../abi'; -import { ExitPoolRequest } from '../exit/types'; +import { ExitPoolRequest } from '../removeLiquidity/types'; export async function doRemoveLiquidity( rpcUrl: string, diff --git a/src/entities/utils/parseRemoveLiquidityArgs.ts b/src/entities/utils/parseRemoveLiquidityArgs.ts index ec303ffd..3c3ad9aa 100644 --- a/src/entities/utils/parseRemoveLiquidityArgs.ts +++ b/src/entities/utils/parseRemoveLiquidityArgs.ts @@ -1,6 +1,6 @@ import { Address } from '../../types'; import { Token } from '../token'; -import { ExitPoolRequest } from '../exit/types'; +import { ExitPoolRequest } from '../removeLiquidity/types'; import { replaceWrapped } from './replaceWrapped'; export function parseRemoveLiquidityArgs({ diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index 20ef31f3..d61692d1 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -31,11 +31,11 @@ import { import { forkSetup } from './lib/utils/helper'; import { RemoveLiquidityTxInput } from './lib/utils/types'; import { - assertProportionalExit, - assertSingleTokenExit, - assertUnbalancedExit, - doExit, -} from './lib/utils/exitHelper'; + assertRemoveLiquidityProportional, + assertRemoveLiquiditySingleToken, + assertRemoveLiquidityUnbalanced, + doRemoveLiquidity, +} from './lib/utils/removeLiquidityHelper'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; const chainId = ChainId.MAINNET; @@ -43,7 +43,7 @@ const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const poolId = '0x1a44e35d5451e0b78621a1b3e7a53dfaa306b1d000000000000000000000051b'; // baoETH-ETH StablePool -describe('composable stable exit test', () => { +describe('composable stable remove liquidity test', () => { let txInput: RemoveLiquidityTxInput; let bptToken: Token; let poolInput: PoolStateInput; @@ -83,7 +83,7 @@ describe('composable stable exit test', () => { ); }); - describe('unbalanced exit', async () => { + describe('remove liquidity unbalanced', async () => { let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { @@ -105,16 +105,16 @@ describe('composable stable exit test', () => { kind: RemoveLiquidityKind.Unbalanced, }; }); - test('removing liquidity with wrapped', async () => { + test('with wrapped', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertUnbalancedExit( + assertRemoveLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, @@ -122,17 +122,17 @@ describe('composable stable exit test', () => { txInput.slippage, ); }); - test('removing liquidity with native', async () => { + test('with native', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), exitWithNativeAsset: true, }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertUnbalancedExit( + assertRemoveLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, @@ -142,7 +142,7 @@ describe('composable stable exit test', () => { }); }); - describe('single asset exit', () => { + describe('remove liquidity single token', () => { let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { @@ -159,13 +159,13 @@ describe('composable stable exit test', () => { kind: RemoveLiquidityKind.SingleAsset, }; }); - test('removing liquidity with wrapped', async () => { - const removeLiquidityOutput = await doExit({ + test('with wrapped', async () => { + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput: input, }); - assertSingleTokenExit( + assertRemoveLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, input, @@ -174,17 +174,17 @@ describe('composable stable exit test', () => { ); }); - test('removing liquidity with native', async () => { + test('with native', async () => { const removeLiquidityInput = { ...input, exitWithNativeAsset: true, }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertSingleTokenExit( + assertRemoveLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, @@ -194,7 +194,7 @@ describe('composable stable exit test', () => { }); }); - describe('proportional exit', () => { + describe('remove liquidity proportional', () => { let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { @@ -210,12 +210,12 @@ describe('composable stable exit test', () => { }; }); test('with tokens', async () => { - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput: input, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, @@ -228,11 +228,11 @@ describe('composable stable exit test', () => { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, diff --git a/test/lib/utils/exitHelper.ts b/test/lib/utils/removeLiquidityHelper.ts similarity index 92% rename from test/lib/utils/exitHelper.ts rename to test/lib/utils/removeLiquidityHelper.ts index 13eefd9f..1d03c9bd 100644 --- a/test/lib/utils/exitHelper.ts +++ b/test/lib/utils/removeLiquidityHelper.ts @@ -26,21 +26,21 @@ type RemoveLiquidityOutput = { txOutput: TxResult; }; -export const sdkExit = async ({ +export const sdkRemoveLiquidity = async ({ removeLiquidity, removeLiquidityInput, poolStateInput, slippage, testAddress, }: Omit): Promise<{ - RemoveLiquidityBuildOutput: RemoveLiquidityBuildOutput; + removeLiquidityBuildOutput: RemoveLiquidityBuildOutput; removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; }> => { const removeLiquidityQueryOutput = await removeLiquidity.query( removeLiquidityInput, poolStateInput, ); - const RemoveLiquidityBuildOutput = removeLiquidity.buildCall({ + const removeLiquidityBuildOutput = removeLiquidity.buildCall({ ...removeLiquidityQueryOutput, slippage, sender: testAddress, @@ -48,7 +48,7 @@ export const sdkExit = async ({ }); return { - RemoveLiquidityBuildOutput, + removeLiquidityBuildOutput, removeLiquidityQueryOutput, }; }; @@ -90,16 +90,16 @@ function getCheck(result: RemoveLiquidityQueryOutput, isExactIn: boolean) { } /** - * Create and submit exit transaction. + * Create and submit remove liquidity transaction. * @param txInput * @param client: Client & PublicActions & WalletActions - The RPC client - * @param removeLiquidity: RemoveLiquidity - The pool exit class, used to query the exit and build the exit call - * @param removeLiquidityInput: RemoveLiquidityInput - The parameters of the exit transaction, example: bptIn, amountsOut, etc. - * @param slippage: Slippage - The slippage tolerance for the exit transaction - * @param poolStateInput: PoolStateInput - The state of the pool being exited + * @param removeLiquidity: RemoveLiquidity - The remove liquidity class, used to query outputs and build transaction call + * @param removeLiquidityInput: RemoveLiquidityInput - The parameters of the transaction, example: bptIn, amountsOut, etc. + * @param slippage: Slippage - The slippage tolerance for the transaction + * @param poolStateInput: PoolStateInput - The state of the pool * @param testAddress: Address - The address to send the transaction from * */ -export async function doExit(txInput: RemoveLiquidityTxInput) { +export async function doRemoveLiquidity(txInput: RemoveLiquidityTxInput) { const { removeLiquidity, poolStateInput, @@ -109,8 +109,8 @@ export async function doExit(txInput: RemoveLiquidityTxInput) { slippage, } = txInput; - const { removeLiquidityQueryOutput, RemoveLiquidityBuildOutput } = - await sdkExit({ + const { removeLiquidityQueryOutput, removeLiquidityBuildOutput } = + await sdkRemoveLiquidity({ removeLiquidity, removeLiquidityInput, poolStateInput, @@ -126,19 +126,19 @@ export async function doExit(txInput: RemoveLiquidityTxInput) { tokens, client, testAddress, - RemoveLiquidityBuildOutput.to, - RemoveLiquidityBuildOutput.call, - RemoveLiquidityBuildOutput.value, + removeLiquidityBuildOutput.to, + removeLiquidityBuildOutput.call, + removeLiquidityBuildOutput.value, ); return { removeLiquidityQueryOutput, - RemoveLiquidityBuildOutput, + removeLiquidityBuildOutput, txOutput, }; } -export function assertUnbalancedExit( +export function assertRemoveLiquidityUnbalanced( chainId: ChainId, poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquidityUnbalancedInput, @@ -200,7 +200,7 @@ export function assertUnbalancedExit( ); } -export function assertSingleTokenExit( +export function assertRemoveLiquiditySingleToken( chainId: ChainId, poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquiditySingleTokenInput, @@ -273,7 +273,7 @@ export function assertSingleTokenExit( ); } -export function assertProportionalExit( +export function assertRemoveLiquidityProportional( chainId: ChainId, poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquidityProportionalInput, @@ -387,8 +387,7 @@ function assertRemoveLiquidityBuildOutput( minAmountsOut, maxBptIn, to: BALANCER_VAULT, - // Value should always be 0 for exits - value: 0n, + value: 0n, // Value should always be 0 when removing liquidity }; // rome-ignore lint/correctness/noUnusedVariables: diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index dcff324d..62fd6c22 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -29,11 +29,11 @@ import { } from '../src'; import { forkSetup } from './lib/utils/helper'; import { - assertProportionalExit, - assertSingleTokenExit, - assertUnbalancedExit, - doExit, -} from './lib/utils/exitHelper'; + assertRemoveLiquidityProportional, + assertRemoveLiquiditySingleToken, + assertRemoveLiquidityUnbalanced, + doRemoveLiquidity, +} from './lib/utils/removeLiquidityHelper'; import { RemoveLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; @@ -42,7 +42,7 @@ const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const poolId = '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; // 80BAL-20WETH -describe('weighted exit test', () => { +describe('weighted remove liquidity test', () => { let txInput: RemoveLiquidityTxInput; let poolInput: PoolStateInput; beforeAll(async () => { @@ -80,7 +80,7 @@ describe('weighted exit test', () => { ); }); - describe('unbalanced exit', async () => { + describe('remove liquidity unbalanced', async () => { let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { @@ -95,16 +95,16 @@ describe('weighted exit test', () => { kind: RemoveLiquidityKind.Unbalanced, }; }); - test('removing liquidity with wrapped', async () => { + test('with wrapped', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertUnbalancedExit( + assertRemoveLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, @@ -112,17 +112,17 @@ describe('weighted exit test', () => { txInput.slippage, ); }); - test('removing liquidity with native', async () => { + test('with native', async () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), exitWithNativeAsset: true, }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertUnbalancedExit( + assertRemoveLiquidityUnbalanced( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, @@ -132,7 +132,7 @@ describe('weighted exit test', () => { }); }); - describe('single asset exit', () => { + describe('remove liquidity single asset', () => { let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { @@ -149,13 +149,13 @@ describe('weighted exit test', () => { kind: RemoveLiquidityKind.SingleAsset, }; }); - test('removing liquidity with wrapped', async () => { - const removeLiquidityOutput = await doExit({ + test('with wrapped', async () => { + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput: input, }); - assertSingleTokenExit( + assertRemoveLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, input, @@ -164,17 +164,17 @@ describe('weighted exit test', () => { ); }); - test('removing liquidity with native', async () => { + test('with native', async () => { const removeLiquidityInput = { ...input, exitWithNativeAsset: true, }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertSingleTokenExit( + assertRemoveLiquiditySingleToken( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, @@ -184,7 +184,7 @@ describe('weighted exit test', () => { }); }); - describe('proportional exit', () => { + describe('remove liquidity proportional', () => { let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { @@ -200,12 +200,12 @@ describe('weighted exit test', () => { }; }); test('with tokens', async () => { - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput: input, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, @@ -218,11 +218,11 @@ describe('weighted exit test', () => { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const removeLiquidityOutput = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, removeLiquidityInput, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, removeLiquidityInput, From 54b917c22728d01e64d6e95c9c498d05eacbd8c0 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 16:04:12 -0300 Subject: [PATCH 193/199] Refactor SingleAsset to SingleToken --- README.md | 2 +- examples/exitPool.ts | 4 ++-- .../composable-stable/addLiquidityComposableStable.ts | 10 +++++----- src/entities/addLiquidity/types.ts | 8 ++++---- src/entities/addLiquidity/utils/validateInputs.ts | 2 +- .../addLiquidity/weighted/addLiquidityWeighted.ts | 10 +++++----- src/entities/encoders/composableStable.ts | 2 +- src/entities/encoders/weighted.ts | 2 +- .../removeLiquidityComposableStable.ts | 8 ++++---- src/entities/removeLiquidity/types.ts | 4 ++-- src/entities/removeLiquidity/utils/validateInputs.ts | 2 +- .../weighted/removeLiquidityWeighted.ts | 8 ++++---- test/composableStableExit.integration.test.ts | 2 +- test/composableStableJoin.integration.test.ts | 6 +++--- test/lib/utils/addLiquidityHelper.ts | 6 +++--- test/lib/utils/removeLiquidityHelper.ts | 2 +- test/weightedExit.integration.test.ts | 2 +- test/weightedJoin.integration.test.ts | 6 +++--- 18 files changed, 43 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 3f8250f0..fc0e0254 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ const removeLiquidityInput: RemoveLiquiditySingleTokenInput = { rpcUrl, bptIn, tokenOut, - kind: RemoveLiquidityKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; const balancerApi = new BalancerApi('https://backend-v3-canary.beets-ftm-node.com/graphql', 1); diff --git a/examples/exitPool.ts b/examples/exitPool.ts index b6ccea5f..61ace2f4 100644 --- a/examples/exitPool.ts +++ b/examples/exitPool.ts @@ -42,7 +42,7 @@ const exit = async () => { const poolStateInput: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - // Construct the RemoveLiquidityInput, in this case a SingleAsset exit + // Construct the RemoveLiquidityInput, in this case a SingleToken exit const bptIn: InputAmount = { rawAmount: parseEther('1'), decimals: 18, @@ -53,7 +53,7 @@ const exit = async () => { rpcUrl, bptIn, tokenOut, - kind: RemoveLiquidityKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; // Simulate the exit to get the tokens out diff --git a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts index 4a90a596..fcd2faaf 100644 --- a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts @@ -136,12 +136,12 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { }; break; } - case AddLiquidityKind.SingleAsset: { + case AddLiquidityKind.SingleToken: { const tokenInIndex = poolTokens .filter((_, index) => index !== bptIndex) // Need to remove Bpt .findIndex((t) => t.isSameAddress(input.tokenIn)); if (tokenInIndex === -1) - throw Error("Can't find index of SingleAsset"); + throw Error("Can't find index of SingleToken"); const maxAmountsIn = Array(poolTokens.length).fill(0n); maxAmountsIn[tokenInIndex] = MAX_UINT256; addLiquidityAmounts = { @@ -189,7 +189,7 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { }; break; } - case AddLiquidityKind.SingleAsset: + case AddLiquidityKind.SingleToken: case AddLiquidityKind.Proportional: { addLiquidityAmounts = { minimumBpt: input.bptOut.amount, @@ -226,9 +226,9 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { amounts.maxAmountsInNoBpt, amounts.minimumBpt, ); - case AddLiquidityKind.SingleAsset: { + case AddLiquidityKind.SingleToken: { if (amounts.tokenInIndex === undefined) throw Error('No Index'); - return ComposableStableEncoder.addLiquiditySingleAsset( + return ComposableStableEncoder.addLiquiditySingleToken( amounts.minimumBpt, amounts.tokenInIndex, // Has to be index without BPT ); diff --git a/src/entities/addLiquidity/types.ts b/src/entities/addLiquidity/types.ts index 434a48f1..3d743f84 100644 --- a/src/entities/addLiquidity/types.ts +++ b/src/entities/addLiquidity/types.ts @@ -6,7 +6,7 @@ import { Address, Hex, InputAmount } from '../../types'; export enum AddLiquidityKind { Init = 'Init', Unbalanced = 'Unbalanced', - SingleAsset = 'SingleAsset', + SingleToken = 'SingleToken', Proportional = 'Proportional', } @@ -28,10 +28,10 @@ export type AddLiquidityUnbalancedInput = AddLiquidityBaseInput & { kind: AddLiquidityKind.Unbalanced; }; -export type AddLiquiditySingleAssetInput = AddLiquidityBaseInput & { +export type AddLiquiditySingleTokenInput = AddLiquidityBaseInput & { bptOut: InputAmount; tokenIn: Address; - kind: AddLiquidityKind.SingleAsset; + kind: AddLiquidityKind.SingleToken; }; export type AddLiquidityProportionalInput = AddLiquidityBaseInput & { @@ -42,7 +42,7 @@ export type AddLiquidityProportionalInput = AddLiquidityBaseInput & { export type AddLiquidityInput = | AddLiquidityInitInput | AddLiquidityUnbalancedInput - | AddLiquiditySingleAssetInput + | AddLiquiditySingleTokenInput | AddLiquidityProportionalInput; type AddLiquidityBaseQueryOutput = { diff --git a/src/entities/addLiquidity/utils/validateInputs.ts b/src/entities/addLiquidity/utils/validateInputs.ts index a2906367..86193cbc 100644 --- a/src/entities/addLiquidity/utils/validateInputs.ts +++ b/src/entities/addLiquidity/utils/validateInputs.ts @@ -20,7 +20,7 @@ export function validateInputs( poolState.tokens.map((t) => t.address), ); break; - case AddLiquidityKind.SingleAsset: + case AddLiquidityKind.SingleToken: areTokensInArray( [input.tokenIn], poolState.tokens.map((t) => t.address), diff --git a/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts index 90f52064..0a6b9021 100644 --- a/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts +++ b/src/entities/addLiquidity/weighted/addLiquidityWeighted.ts @@ -116,12 +116,12 @@ export class AddLiquidityWeighted implements AddLiquidityBase { tokenInIndex: undefined, }; } - case AddLiquidityKind.SingleAsset: { + case AddLiquidityKind.SingleToken: { const tokenInIndex = poolTokens.findIndex((t) => t.isSameAddress(input.tokenIn), ); if (tokenInIndex === -1) - throw Error("Can't find index of SingleAsset"); + throw Error("Can't find index of SingleToken"); const maxAmountsIn = Array(poolTokens.length).fill(0n); maxAmountsIn[tokenInIndex] = MAX_UINT256; return { @@ -157,7 +157,7 @@ export class AddLiquidityWeighted implements AddLiquidityBase { tokenInIndex: input.tokenInIndex, }; } - case AddLiquidityKind.SingleAsset: + case AddLiquidityKind.SingleToken: case AddLiquidityKind.Proportional: { return { minimumBpt: input.bptOut.amount, @@ -184,9 +184,9 @@ export class AddLiquidityWeighted implements AddLiquidityBase { amounts.maxAmountsIn, amounts.minimumBpt, ); - case AddLiquidityKind.SingleAsset: { + case AddLiquidityKind.SingleToken: { if (amounts.tokenInIndex === undefined) throw Error('No Index'); - return WeightedEncoder.addLiquiditySingleAsset( + return WeightedEncoder.addLiquiditySingleToken( amounts.minimumBpt, amounts.tokenInIndex, ); diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index 98ac37c5..0121c812 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -57,7 +57,7 @@ export class ComposableStableEncoder { * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ - static addLiquiditySingleAsset = ( + static addLiquiditySingleToken = ( bptAmountOut: bigint, enterTokenIndex: number, ): Address => { diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index a59a7192..f2f7a656 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -56,7 +56,7 @@ export class WeightedEncoder { * @param bptAmountOut - the amount of BPT to be minted * @param enterTokenIndex - the index of the token to be provided as liquidity */ - static addLiquiditySingleAsset = ( + static addLiquiditySingleToken = ( bptAmountOut: bigint, enterTokenIndex: number, ): Address => { diff --git a/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts index 1e2275f2..5731af49 100644 --- a/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts +++ b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts @@ -88,7 +88,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: tokens @@ -160,10 +160,10 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: if (input.tokenOutIndex === undefined) { throw new Error( - 'tokenOutIndex must be defined for SingleAsset exit', + 'tokenOutIndex must be defined for SingleToken exit', ); } return { @@ -196,7 +196,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { amounts.minAmountsOut, amounts.maxBptAmountIn, ); - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: if (amounts.tokenOutIndex === undefined) throw Error('No Index'); diff --git a/src/entities/removeLiquidity/types.ts b/src/entities/removeLiquidity/types.ts index bf257176..5f96ecc7 100644 --- a/src/entities/removeLiquidity/types.ts +++ b/src/entities/removeLiquidity/types.ts @@ -5,7 +5,7 @@ import { PoolState } from '../types'; export enum RemoveLiquidityKind { Unbalanced = 'Unbalanced', // exitExactOut - SingleAsset = 'SingleAsset', // exitExactInSingleAsset + SingleToken = 'SingleToken', // exitExactInSingleToken Proportional = 'Proportional', // exitExactInProportional } @@ -25,7 +25,7 @@ export type RemoveLiquidityUnbalancedInput = RemoveLiquidityBaseInput & { export type RemoveLiquiditySingleTokenInput = RemoveLiquidityBaseInput & { bptIn: InputAmount; tokenOut: Address; - kind: RemoveLiquidityKind.SingleAsset; + kind: RemoveLiquidityKind.SingleToken; }; export type RemoveLiquidityProportionalInput = RemoveLiquidityBaseInput & { diff --git a/src/entities/removeLiquidity/utils/validateInputs.ts b/src/entities/removeLiquidity/utils/validateInputs.ts index 35a8b575..26db6eb7 100644 --- a/src/entities/removeLiquidity/utils/validateInputs.ts +++ b/src/entities/removeLiquidity/utils/validateInputs.ts @@ -19,7 +19,7 @@ export function validateInputs( poolState.tokens.map((t) => t.address), ); break; - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: areTokensInArray( [input.tokenOut], poolState.tokens.map((t) => t.address), diff --git a/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts index 496e4a14..93386daa 100644 --- a/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts +++ b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts @@ -80,7 +80,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { tokenOutIndex: undefined, maxBptAmountIn: MAX_UINT256, }; - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: return { minAmountsOut: Array(tokens.length).fill(0n), tokenOutIndex: tokens.findIndex((t) => @@ -145,10 +145,10 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { tokenOutIndex: input.tokenOutIndex, maxBptAmountIn: input.slippage.applyTo(input.bptIn.amount), }; - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: if (input.tokenOutIndex === undefined) { throw new Error( - 'tokenOutIndex must be defined for SingleAsset exit', + 'tokenOutIndex must be defined for SingleToken exit', ); } return { @@ -181,7 +181,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { amounts.minAmountsOut, amounts.maxBptAmountIn, ); - case RemoveLiquidityKind.SingleAsset: + case RemoveLiquidityKind.SingleToken: if (amounts.tokenOutIndex === undefined) throw Error('No Index'); diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index d61692d1..7d6c0985 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -156,7 +156,7 @@ describe('composable stable remove liquidity test', () => { rpcUrl, bptIn, tokenOut, - kind: RemoveLiquidityKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; }); test('with wrapped', async () => { diff --git a/test/composableStableJoin.integration.test.ts b/test/composableStableJoin.integration.test.ts index 9a3aeb2d..326cafc7 100644 --- a/test/composableStableJoin.integration.test.ts +++ b/test/composableStableJoin.integration.test.ts @@ -15,7 +15,7 @@ import { import { AddLiquidityUnbalancedInput, AddLiquidityProportionalInput, - AddLiquiditySingleAssetInput, + AddLiquiditySingleTokenInput, AddLiquidityKind, Slippage, Address, @@ -145,7 +145,7 @@ describe('add liquidity composable stable test', () => { }); describe('add liquidity single asset', () => { - let input: AddLiquiditySingleAssetInput; + let input: AddLiquiditySingleTokenInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -158,7 +158,7 @@ describe('add liquidity composable stable test', () => { tokenIn, chainId, rpcUrl, - kind: AddLiquidityKind.SingleAsset, + kind: AddLiquidityKind.SingleToken, }; }); test('with token', async () => { diff --git a/test/lib/utils/addLiquidityHelper.ts b/test/lib/utils/addLiquidityHelper.ts index 5b794b93..942f7f6f 100644 --- a/test/lib/utils/addLiquidityHelper.ts +++ b/test/lib/utils/addLiquidityHelper.ts @@ -9,7 +9,7 @@ import { AddLiquidityQueryOutput, AddLiquidityUnbalancedInput, BALANCER_VAULT, - AddLiquiditySingleAssetInput, + AddLiquiditySingleTokenInput, AddLiquidityProportionalInput, Token, ChainId, @@ -212,7 +212,7 @@ export function assertAddLiquidityUnbalanced( export function assertAddLiquiditySingleToken( chainId: ChainId, poolStateInput: PoolStateInput, - addLiquidityInput: AddLiquiditySingleAssetInput, + addLiquidityInput: AddLiquiditySingleTokenInput, addLiquidityOutput: AddLiquidityOutput, slippage: Slippage, ) { @@ -305,7 +305,7 @@ export function assertAddLiquidityProportional( bptToken, addLiquidityInput.bptOut.rawAmount, ), - // Only expect tokenInIndex for AddLiquiditySingleAsset + // Only expect tokenInIndex for AddLiquiditySingleToken tokenInIndex: undefined, // Should match inputs poolId: poolStateInput.id, diff --git a/test/lib/utils/removeLiquidityHelper.ts b/test/lib/utils/removeLiquidityHelper.ts index 1d03c9bd..81f74f40 100644 --- a/test/lib/utils/removeLiquidityHelper.ts +++ b/test/lib/utils/removeLiquidityHelper.ts @@ -294,7 +294,7 @@ export function assertRemoveLiquidityProportional( bptToken, removeLiquidityInput.bptIn.rawAmount, ), - // Only expect tokenInIndex for AddLiquiditySingleAsset + // Only expect tokenInIndex for AddLiquiditySingleToken tokenOutIndex: undefined, // Should match inputs poolId: poolStateInput.id, diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index 62fd6c22..a85cc6a2 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -146,7 +146,7 @@ describe('weighted remove liquidity test', () => { rpcUrl, bptIn, tokenOut, - kind: RemoveLiquidityKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; }); test('with wrapped', async () => { diff --git a/test/weightedJoin.integration.test.ts b/test/weightedJoin.integration.test.ts index 720d1dcb..2e69ede5 100644 --- a/test/weightedJoin.integration.test.ts +++ b/test/weightedJoin.integration.test.ts @@ -15,7 +15,7 @@ import { import { AddLiquidityUnbalancedInput, AddLiquidityProportionalInput, - AddLiquiditySingleAssetInput, + AddLiquiditySingleTokenInput, AddLiquidityKind, Slippage, Address, @@ -145,7 +145,7 @@ describe('add liquidity weighted test', () => { }); describe('add liquidity single asset', () => { - let addLiquidityInput: AddLiquiditySingleAssetInput; + let addLiquidityInput: AddLiquiditySingleTokenInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -158,7 +158,7 @@ describe('add liquidity weighted test', () => { tokenIn, chainId, rpcUrl, - kind: AddLiquidityKind.SingleAsset, + kind: AddLiquidityKind.SingleToken, }; }); From c82f93579339591a4e7cfc499a36a2e2c28a59de Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 16:17:27 -0300 Subject: [PATCH 194/199] Refactor exitWithNativeAsset parameter to toNativeAsset --- .changeset/large-socks-exist.md | 3 ++- README.md | 4 ++-- examples/{exitPool.ts => removeLiquidity.ts} | 14 +++++++------- src/entities/encoders/composableStable.ts | 13 ++++++------- src/entities/encoders/weighted.ts | 14 +++++++------- .../removeLiquidityComposableStable.ts | 6 +++--- src/entities/removeLiquidity/removeLiquidity.ts | 2 +- src/entities/removeLiquidity/types.ts | 10 +++++----- .../weighted/removeLiquidityWeighted.ts | 6 +++--- src/entities/utils/parseRemoveLiquidityArgs.ts | 6 +++--- test/composableStableExit.integration.test.ts | 4 ++-- test/lib/utils/removeLiquidityHelper.ts | 10 +++++----- test/weightedExit.integration.test.ts | 4 ++-- 13 files changed, 48 insertions(+), 48 deletions(-) rename examples/{exitPool.ts => removeLiquidity.ts} (88%) diff --git a/.changeset/large-socks-exist.md b/.changeset/large-socks-exist.md index 2768f766..79fc865b 100644 --- a/.changeset/large-socks-exist.md +++ b/.changeset/large-socks-exist.md @@ -2,7 +2,8 @@ "@balancer/sdk": minor --- -- Add join/exit pool support (non-nested pools) +- Add add/remove liquidity pool support (non-nested pools) - Weighted pool type +- ComposableStable pool type - Uses balancerHelpers to query amounts in/out rather than relying on specific pool math and associated data - Integration tests run against local viem fork diff --git a/README.md b/README.md index fc0e0254..e16433c2 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The Balancer API Provider is a provider that facilitates data fetching from the Balancer API, it can be used for: - Fetch Pool State for AddLiquidity; -- Fetch Pool State for Exits. +- Fetch Pool State for RemoveLiquidity. ### Usage for adding liquidity to a Pool @@ -102,7 +102,7 @@ await client.sendTransaction({ value, }); ``` -Full working exit example: [examples/exit/weighted.ts](./examples/exit/weighted.ts) +Full working remove liquidity example: [examples/removeLiquidity.ts](./examples/removeLiquidity.ts) ## Anvil client To download and install the anvil client, run the following commands (MacOS): diff --git a/examples/exitPool.ts b/examples/removeLiquidity.ts similarity index 88% rename from examples/exitPool.ts rename to examples/removeLiquidity.ts index 61ace2f4..d286d909 100644 --- a/examples/exitPool.ts +++ b/examples/removeLiquidity.ts @@ -1,9 +1,9 @@ /** - * Example showing how to exit a pool. + * Example showing how to remove liquidity from a pool. * (Runs against a local Anvil fork) * * Run with: - * pnpm example ./examples/exitPool.ts + * pnpm example ./examples/removeLiquidity.ts */ import dotenv from 'dotenv'; dotenv.config(); @@ -22,7 +22,7 @@ import { parseEther } from 'viem'; import { ANVIL_NETWORKS, startFork } from '../test/anvil/anvil-global-setup'; import { makeForkTx } from './utils/makeForkTx'; -const exit = async () => { +const removeLiquidity = async () => { // User defined: const chainId = ChainId.MAINNET; const userAccount = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; @@ -42,7 +42,7 @@ const exit = async () => { const poolStateInput: PoolStateInput = await balancerApi.pools.fetchPoolState(poolId); - // Construct the RemoveLiquidityInput, in this case a SingleToken exit + // Construct the RemoveLiquidityInput, in this case a RemoveLiquiditySingleToken const bptIn: InputAmount = { rawAmount: parseEther('1'), decimals: 18, @@ -56,14 +56,14 @@ const exit = async () => { kind: RemoveLiquidityKind.SingleToken, }; - // Simulate the exit to get the tokens out + // Simulate removing liquidity to get the tokens out const removeLiquidity = new RemoveLiquidity(); const queryOutput = await removeLiquidity.query( removeLiquidityInput, poolStateInput, ); - console.log('\nExit Query Result:'); + console.log('Remove Liquidity Query Output:'); console.log(`BPT In: ${queryOutput.bptIn.amount.toString()}\nTokens Out:`); queryOutput.amountsOut.map((a) => console.log(a.token.address, a.amount.toString()), @@ -103,4 +103,4 @@ const exit = async () => { ); }; -exit(); +removeLiquidity(); diff --git a/src/entities/encoders/composableStable.ts b/src/entities/encoders/composableStable.ts index 0121c812..689d0a51 100644 --- a/src/entities/encoders/composableStable.ts +++ b/src/entities/encoders/composableStable.ts @@ -55,19 +55,18 @@ export class ComposableStableEncoder { /** * Encodes the userData parameter for adding liquidity to a ComposableStablePool with a single token to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted - * @param enterTokenIndex - the index of the token to be provided as liquidity + * @param tokenIndex - the index of the token to be provided as liquidity */ static addLiquiditySingleToken = ( bptAmountOut: bigint, - enterTokenIndex: number, + tokenIndex: number, ): Address => { - // if enterTokenIndex is provided, it's assumed to be an allTokensIn return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], [ BigInt(ComposableStablePoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), bptAmountOut, - BigInt(enterTokenIndex), + BigInt(tokenIndex), ], ); }; @@ -91,11 +90,11 @@ export class ComposableStableEncoder { /** * Encodes the userData parameter for removing liquidity from a ComposableStablePool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned - * @param enterTokenIndex - the index of the token to removed from the pool + * @param tokenIndex - the index of the token to be removed from the pool */ static removeLiquiditySingleToken = ( bptAmountIn: bigint, - exitTokenIndex: number, + tokenIndex: number, ): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], @@ -104,7 +103,7 @@ export class ComposableStableEncoder { ComposableStablePoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, ), bptAmountIn, - BigInt(exitTokenIndex), + BigInt(tokenIndex), ], ); }; diff --git a/src/entities/encoders/weighted.ts b/src/entities/encoders/weighted.ts index f2f7a656..3d8f065f 100644 --- a/src/entities/encoders/weighted.ts +++ b/src/entities/encoders/weighted.ts @@ -54,19 +54,19 @@ export class WeightedEncoder { /** * Encodes the userData parameter for adding liquidity to a WeightedPool with a single token to receive an exact amount of BPT * @param bptAmountOut - the amount of BPT to be minted - * @param enterTokenIndex - the index of the token to be provided as liquidity + * @param tokenIndex - the index of the token to be provided as liquidity */ static addLiquiditySingleToken = ( bptAmountOut: bigint, - enterTokenIndex: number, + tokenIndex: number, ): Address => { - // if enterTokenIndex is provided, it's assumed to be an allTokensIn + // if tokenIndex is provided, it's assumed to be an allTokensIn return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], [ BigInt(WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT), bptAmountOut, - BigInt(enterTokenIndex), + BigInt(tokenIndex), ], ); }; @@ -88,18 +88,18 @@ export class WeightedEncoder { /** * Encodes the userData parameter for removing liquidity from a WeightedPool by removing tokens in return for an exact amount of BPT * @param bptAmountIn - the amount of BPT to be burned - * @param enterTokenIndex - the index of the token to removed from the pool + * @param tokenIndex - the index of the token to removed from the pool */ static removeLiquiditySingleToken = ( bptAmountIn: bigint, - exitTokenIndex: number, + tokenIndex: number, ): Address => { return encodeAbiParameters( [{ type: 'uint256' }, { type: 'uint256' }, { type: 'uint256' }], [ BigInt(WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT), bptAmountIn, - BigInt(exitTokenIndex), + BigInt(tokenIndex), ], ); }; diff --git a/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts index 5731af49..efdc8024 100644 --- a/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts +++ b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts @@ -40,10 +40,10 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { }; const userData = this.encodeUserData(input.kind, amountsWithoutBpt); - // tokensOut will have zero address if exit with native asset + // tokensOut will have zero address if removing liquidity to native asset const { args, tokensOut } = parseRemoveLiquidityArgs({ chainId: input.chainId, - exitWithNativeAsset: !!input.exitWithNativeAsset, + toNativeAsset: !!input.toNativeAsset, poolId: poolState.id, sortedTokens: poolState.tokens, sender: ZERO_ADDRESS, @@ -163,7 +163,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { case RemoveLiquidityKind.SingleToken: if (input.tokenOutIndex === undefined) { throw new Error( - 'tokenOutIndex must be defined for SingleToken exit', + 'tokenOutIndex must be defined for RemoveLiquiditySingleToken', ); } return { diff --git a/src/entities/removeLiquidity/removeLiquidity.ts b/src/entities/removeLiquidity/removeLiquidity.ts index bfbf7a54..bd1676f8 100644 --- a/src/entities/removeLiquidity/removeLiquidity.ts +++ b/src/entities/removeLiquidity/removeLiquidity.ts @@ -22,7 +22,7 @@ export class RemoveLiquidity { WEIGHTED: new RemoveLiquidityWeighted(), // PHANTOM_STABLE === ComposableStables in API PHANTOM_STABLE: new RemoveLiquidityComposableStable(), - // custom pool Exits take precedence over base Exits + // custom remove liquidity types take precedence over base types ...customRemoveLiquidityTypes, }; } diff --git a/src/entities/removeLiquidity/types.ts b/src/entities/removeLiquidity/types.ts index 5f96ecc7..b90d2fcb 100644 --- a/src/entities/removeLiquidity/types.ts +++ b/src/entities/removeLiquidity/types.ts @@ -4,16 +4,16 @@ import { Address, InputAmount } from '../../types'; import { PoolState } from '../types'; export enum RemoveLiquidityKind { - Unbalanced = 'Unbalanced', // exitExactOut - SingleToken = 'SingleToken', // exitExactInSingleToken - Proportional = 'Proportional', // exitExactInProportional + Unbalanced = 'Unbalanced', // exact out + SingleToken = 'SingleToken', // exact in (single token out) + Proportional = 'Proportional', // exact in (all tokens out) } // This will be extended for each pools specific output requirements export type RemoveLiquidityBaseInput = { chainId: number; rpcUrl: string; - exitWithNativeAsset?: boolean; + toNativeAsset?: boolean; toInternalBalance?: boolean; }; @@ -42,7 +42,7 @@ export type RemoveLiquidityQueryOutput = | RemoveLiquidityBaseQueryOutput | RemoveLiquidityComposableStableQueryOutput; -// Returned from a exit query +// Returned from a remove liquidity query export type RemoveLiquidityBaseQueryOutput = { poolType: string; poolId: Address; diff --git a/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts index 93386daa..bd3c9f71 100644 --- a/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts +++ b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts @@ -32,10 +32,10 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { const userData = this.encodeUserData(input.kind, amounts); - // tokensOut will have zero address if exit with native asset + // tokensOut will have zero address if removing liquidity to native asset const { args, tokensOut } = parseRemoveLiquidityArgs({ chainId: input.chainId, - exitWithNativeAsset: !!input.exitWithNativeAsset, + toNativeAsset: !!input.toNativeAsset, poolId: poolState.id, sortedTokens: poolState.tokens, sender: ZERO_ADDRESS, @@ -148,7 +148,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { case RemoveLiquidityKind.SingleToken: if (input.tokenOutIndex === undefined) { throw new Error( - 'tokenOutIndex must be defined for SingleToken exit', + 'tokenOutIndex must be defined for RemoveLiquiditySingleToken', ); } return { diff --git a/src/entities/utils/parseRemoveLiquidityArgs.ts b/src/entities/utils/parseRemoveLiquidityArgs.ts index 3c3ad9aa..6e54b5c9 100644 --- a/src/entities/utils/parseRemoveLiquidityArgs.ts +++ b/src/entities/utils/parseRemoveLiquidityArgs.ts @@ -5,7 +5,7 @@ import { replaceWrapped } from './replaceWrapped'; export function parseRemoveLiquidityArgs({ chainId, - exitWithNativeAsset, + toNativeAsset, sortedTokens, poolId, sender, @@ -15,7 +15,7 @@ export function parseRemoveLiquidityArgs({ toInternalBalance, }: { chainId?: number; - exitWithNativeAsset?: boolean; + toNativeAsset?: boolean; sortedTokens: Token[]; poolId: Address; sender: Address; @@ -26,7 +26,7 @@ export function parseRemoveLiquidityArgs({ }) { // replace wrapped token with native asset if needed const tokensOut = - chainId && exitWithNativeAsset + chainId && toNativeAsset ? replaceWrapped([...sortedTokens], chainId) : [...sortedTokens]; diff --git a/test/composableStableExit.integration.test.ts b/test/composableStableExit.integration.test.ts index 7d6c0985..daa6999a 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/composableStableExit.integration.test.ts @@ -126,7 +126,7 @@ describe('composable stable remove liquidity test', () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), - exitWithNativeAsset: true, + toNativeAsset: true, }; const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, @@ -177,7 +177,7 @@ describe('composable stable remove liquidity test', () => { test('with native', async () => { const removeLiquidityInput = { ...input, - exitWithNativeAsset: true, + toNativeAsset: true, }; const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, diff --git a/test/lib/utils/removeLiquidityHelper.ts b/test/lib/utils/removeLiquidityHelper.ts index 81f74f40..a9915802 100644 --- a/test/lib/utils/removeLiquidityHelper.ts +++ b/test/lib/utils/removeLiquidityHelper.ts @@ -152,7 +152,7 @@ export function assertRemoveLiquidityUnbalanced( const expectedAmountsOut = poolStateInput.tokens.map((t) => { let token; if ( - removeLiquidityInput.exitWithNativeAsset && + removeLiquidityInput.toNativeAsset && t.address === NATIVE_ASSETS[chainId].wrapped ) token = new Token(chainId, zeroAddress, t.decimals); @@ -246,12 +246,12 @@ export function assertRemoveLiquiditySingleToken( // (Note removeLiquidityQueryOutput also has value for bpt if pre-minted) removeLiquidityQueryOutput.amountsOut.forEach((a) => { if ( - !removeLiquidityInput.exitWithNativeAsset && + !removeLiquidityInput.toNativeAsset && a.token.address === removeLiquidityInput.tokenOut ) expect(a.amount > 0n).to.be.true; else if ( - removeLiquidityInput.exitWithNativeAsset && + removeLiquidityInput.toNativeAsset && a.token.address === zeroAddress ) expect(a.amount > 0n).to.be.true; @@ -349,8 +349,8 @@ function assertTokenDeltas( 0n, ]; - // If input is exit with native we must replace it with 0 and update native value instead - if (removeLiquidityInput.exitWithNativeAsset) { + // If removing liquidity to native asset we must replace it with 0 and update native value instead + if (removeLiquidityInput.toNativeAsset) { const index = amountsWithoutBpt.findIndex( (a) => a.token.address === zeroAddress, ); diff --git a/test/weightedExit.integration.test.ts b/test/weightedExit.integration.test.ts index a85cc6a2..12f621e7 100644 --- a/test/weightedExit.integration.test.ts +++ b/test/weightedExit.integration.test.ts @@ -116,7 +116,7 @@ describe('weighted remove liquidity test', () => { const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), - exitWithNativeAsset: true, + toNativeAsset: true, }; const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, @@ -167,7 +167,7 @@ describe('weighted remove liquidity test', () => { test('with native', async () => { const removeLiquidityInput = { ...input, - exitWithNativeAsset: true, + toNativeAsset: true, }; const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, From 65651a26b21a3a37d7592fcf75829c61d39934dd Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 16:19:09 -0300 Subject: [PATCH 195/199] Refactor some result into output for consistency --- .../composable-stable/addLiquidityComposableStable.ts | 6 +++--- test/lib/utils/addLiquidityHelper.ts | 6 +++--- test/lib/utils/helper.ts | 4 ++-- test/lib/utils/removeLiquidityHelper.ts | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts index fcd2faaf..72a1c60d 100644 --- a/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts +++ b/src/entities/addLiquidity/composable-stable/addLiquidityComposableStable.ts @@ -52,16 +52,16 @@ export class AddLiquidityComposableStable implements AddLiquidityBase { fromInternalBalance: input.fromInternalBalance ?? false, }); - const queryResult = await doAddLiquidityQuery( + const queryOutput = await doAddLiquidityQuery( input.rpcUrl, input.chainId, args, ); const bpt = new Token(input.chainId, poolState.address, 18); - const bptOut = TokenAmount.fromRawAmount(bpt, queryResult.bptOut); + const bptOut = TokenAmount.fromRawAmount(bpt, queryOutput.bptOut); - const amountsIn = queryResult.amountsIn.map((a, i) => + const amountsIn = queryOutput.amountsIn.map((a, i) => TokenAmount.fromRawAmount(tokensIn[i], a), ); diff --git a/test/lib/utils/addLiquidityHelper.ts b/test/lib/utils/addLiquidityHelper.ts index 942f7f6f..ab608255 100644 --- a/test/lib/utils/addLiquidityHelper.ts +++ b/test/lib/utils/addLiquidityHelper.ts @@ -17,7 +17,7 @@ import { AddLiquidityComposableStableQueryOutput, NATIVE_ASSETS, } from '../../../src'; -import { TxResult, sendTransactionGetBalances } from './helper'; +import { TxOutput, sendTransactionGetBalances } from './helper'; import { AddLiquidityTxInput } from './types'; import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; @@ -25,7 +25,7 @@ import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type AddLiquidityOutput = { addLiquidityQueryOutput: AddLiquidityQueryOutput; addLiquidityBuildOutput: AddLiquidityBuildOutput; - txOutput: TxResult; + txOutput: TxOutput; }; async function sdkAddLiquidity({ @@ -347,7 +347,7 @@ function assertTokenDeltas( addLiquidityInput: AddLiquidityInput, addLiquidityQueryOutput: AddLiquidityQueryOutput, addLiquidityBuildOutput: AddLiquidityBuildOutput, - txOutput: TxResult, + txOutput: TxOutput, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); diff --git a/test/lib/utils/helper.ts b/test/lib/utils/helper.ts index 9cfddd3d..759b368a 100644 --- a/test/lib/utils/helper.ts +++ b/test/lib/utils/helper.ts @@ -18,7 +18,7 @@ import { import { erc20Abi } from '../../../src/abi'; import { BALANCER_VAULT, MAX_UINT256, ZERO_ADDRESS } from '../../../src/utils'; -export type TxResult = { +export type TxOutput = { transactionReceipt: TransactionReceipt; balanceDeltas: bigint[]; gasUsed: bigint; @@ -94,7 +94,7 @@ export async function sendTransactionGetBalances( to: Address, data: Address, value?: bigint, -): Promise { +): Promise { const balanceBefore = await getBalances( tokensForBalanceCheck, client, diff --git a/test/lib/utils/removeLiquidityHelper.ts b/test/lib/utils/removeLiquidityHelper.ts index a9915802..8ec12546 100644 --- a/test/lib/utils/removeLiquidityHelper.ts +++ b/test/lib/utils/removeLiquidityHelper.ts @@ -15,7 +15,7 @@ import { RemoveLiquidityInput, RemoveLiquidityProportionalInput, } from '../../../src'; -import { sendTransactionGetBalances, TxResult } from './helper'; +import { sendTransactionGetBalances, TxOutput } from './helper'; import { expect } from 'vitest'; import { zeroAddress } from 'viem'; import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; @@ -23,7 +23,7 @@ import { getTokensForBalanceCheck } from './getTokensForBalanceCheck'; type RemoveLiquidityOutput = { removeLiquidityQueryOutput: RemoveLiquidityQueryOutput; removeLiquidityBuildOutput: RemoveLiquidityBuildOutput; - txOutput: TxResult; + txOutput: TxOutput; }; export const sdkRemoveLiquidity = async ({ @@ -333,7 +333,7 @@ function assertTokenDeltas( poolStateInput: PoolStateInput, removeLiquidityInput: RemoveLiquidityInput, removeLiquidityQueryOutput: RemoveLiquidityQueryOutput, - txOutput: TxResult, + txOutput: TxOutput, ) { expect(txOutput.transactionReceipt.status).to.eq('success'); From 2dfca4c24a9db1406bc6856add0fb9e69a2b8444 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 16:28:16 -0300 Subject: [PATCH 196/199] Rename liquidity test files --- ...test.ts => addLiquidityComposableStable.integration.test.ts} | 0 ...gration.test.ts => addLiquidityWeighted.integration.test.ts} | 0 ...t.ts => removeLiquidityComposableStable.integration.test.ts} | 2 +- ...tion.test.ts => removeLiquidityWeighted.integration.test.ts} | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename test/{composableStableJoin.integration.test.ts => addLiquidityComposableStable.integration.test.ts} (100%) rename test/{weightedJoin.integration.test.ts => addLiquidityWeighted.integration.test.ts} (100%) rename test/{composableStableExit.integration.test.ts => removeLiquidityComposableStable.integration.test.ts} (99%) rename test/{weightedExit.integration.test.ts => removeLiquidityWeighted.integration.test.ts} (100%) diff --git a/test/composableStableJoin.integration.test.ts b/test/addLiquidityComposableStable.integration.test.ts similarity index 100% rename from test/composableStableJoin.integration.test.ts rename to test/addLiquidityComposableStable.integration.test.ts diff --git a/test/weightedJoin.integration.test.ts b/test/addLiquidityWeighted.integration.test.ts similarity index 100% rename from test/weightedJoin.integration.test.ts rename to test/addLiquidityWeighted.integration.test.ts diff --git a/test/composableStableExit.integration.test.ts b/test/removeLiquidityComposableStable.integration.test.ts similarity index 99% rename from test/composableStableExit.integration.test.ts rename to test/removeLiquidityComposableStable.integration.test.ts index daa6999a..74680dc8 100644 --- a/test/composableStableExit.integration.test.ts +++ b/test/removeLiquidityComposableStable.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- removeLiquidityWeighted.integration.test.ts +// pnpm test -- removeLiquidityComposableStable.integration.test.ts import { describe, test, beforeAll, beforeEach } from 'vitest'; import { config } from 'dotenv'; config(); diff --git a/test/weightedExit.integration.test.ts b/test/removeLiquidityWeighted.integration.test.ts similarity index 100% rename from test/weightedExit.integration.test.ts rename to test/removeLiquidityWeighted.integration.test.ts From 4e529d40a539e8b2248a691c2feabce52e3f8ef1 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Fri, 10 Nov 2023 16:42:40 -0300 Subject: [PATCH 197/199] Fix doRemoveLiquidityQuery --- .../composable-stable/removeLiquidityComposableStable.ts | 4 ++-- .../removeLiquidity/weighted/removeLiquidityWeighted.ts | 4 ++-- .../utils/{doRemoveLiquidity.ts => doRemoveLiquidityQuery.ts} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/entities/utils/{doRemoveLiquidity.ts => doRemoveLiquidityQuery.ts} (94%) diff --git a/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts index efdc8024..e75e978c 100644 --- a/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts +++ b/src/entities/removeLiquidity/composable-stable/removeLiquidityComposableStable.ts @@ -18,7 +18,7 @@ import { RemoveLiquidityQueryOutput, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; -import { doRemoveLiquidity } from '../../utils/doRemoveLiquidity'; +import { doRemoveLiquidityQuery } from '../../utils/doRemoveLiquidityQuery'; import { ComposableStableEncoder } from '../../encoders/composableStable'; import { getAmounts } from '../../utils'; @@ -52,7 +52,7 @@ export class RemoveLiquidityComposableStable implements RemoveLiquidityBase { userData, toInternalBalance: !!input.toInternalBalance, }); - const queryOutput = await doRemoveLiquidity( + const queryOutput = await doRemoveLiquidityQuery( input.rpcUrl, input.chainId, args, diff --git a/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts index bd3c9f71..2b1aa6fa 100644 --- a/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts +++ b/src/entities/removeLiquidity/weighted/removeLiquidityWeighted.ts @@ -20,7 +20,7 @@ import { RemoveLiquidityWeightedCall, } from '../types'; import { RemoveLiquidityAmounts, PoolState } from '../../types'; -import { doRemoveLiquidity } from '../../utils/doRemoveLiquidity'; +import { doRemoveLiquidityQuery } from '../../utils/doRemoveLiquidityQuery'; import { getAmounts } from '../../utils'; export class RemoveLiquidityWeighted implements RemoveLiquidityBase { @@ -45,7 +45,7 @@ export class RemoveLiquidityWeighted implements RemoveLiquidityBase { toInternalBalance: !!input.toInternalBalance, }); - const queryOutput = await doRemoveLiquidity( + const queryOutput = await doRemoveLiquidityQuery( input.rpcUrl, input.chainId, args, diff --git a/src/entities/utils/doRemoveLiquidity.ts b/src/entities/utils/doRemoveLiquidityQuery.ts similarity index 94% rename from src/entities/utils/doRemoveLiquidity.ts rename to src/entities/utils/doRemoveLiquidityQuery.ts index 8df6233d..3001fa3d 100644 --- a/src/entities/utils/doRemoveLiquidity.ts +++ b/src/entities/utils/doRemoveLiquidityQuery.ts @@ -4,7 +4,7 @@ import { BALANCER_HELPERS, CHAINS } from '../../utils/constants'; import { balancerHelpersAbi } from '../../abi'; import { ExitPoolRequest } from '../removeLiquidity/types'; -export async function doRemoveLiquidity( +export async function doRemoveLiquidityQuery( rpcUrl: string, chainId: number, args: readonly [Address, Address, Address, ExitPoolRequest], From 1f29e68704ac691042345245abcb88e6ac9b0be5 Mon Sep 17 00:00:00 2001 From: Luiz Gustavo Abou Hatem De Liz Date: Fri, 10 Nov 2023 17:40:09 -0300 Subject: [PATCH 198/199] Renaming wrong gyro types; --- test/gyro3Exit.integration.test.ts | 2 +- test/gyro3Join.integration.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/gyro3Exit.integration.test.ts b/test/gyro3Exit.integration.test.ts index 8ed4efe9..e6ef9f1a 100644 --- a/test/gyro3Exit.integration.test.ts +++ b/test/gyro3Exit.integration.test.ts @@ -166,7 +166,7 @@ export class MockApi { return { id, address: getPoolAddress(id) as Address, - type: 'GYRO2', + type: 'GYRO3', tokens: [ { address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC(PoS) diff --git a/test/gyro3Join.integration.test.ts b/test/gyro3Join.integration.test.ts index 0998170a..acf1bb6a 100644 --- a/test/gyro3Join.integration.test.ts +++ b/test/gyro3Join.integration.test.ts @@ -183,7 +183,7 @@ export class MockApi { return { id, address: getPoolAddress(id) as Address, - type: 'GYRO2', + type: 'GYRO3', tokens: [ { address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC(PoS) From b366164965b8eccb7d231f6712c858a99fd56bc7 Mon Sep 17 00:00:00 2001 From: Bruno Eidam Guerios Date: Mon, 13 Nov 2023 16:53:59 -0300 Subject: [PATCH 199/199] Refactor join into addLiquidity and exit into removeLiquidity for Gyro pools --- .../addLiquidity/utils/validateInputs.ts | 16 ++-- .../removeLiquidity/utils/validateInputs.ts | 16 ++-- ... => addLiquidityGyro2.integration.test.ts} | 81 +++++++++-------- ... => addLiquidityGyro3.integration.test.ts} | 81 +++++++++-------- ... => addLiquidityGyroE.integration.test.ts} | 79 ++++++++-------- ...> addLiquidityGyroEV2.integration.test.ts} | 91 ++++++++++--------- ... removeLiquidityGyro2.integration.test.ts} | 69 +++++++------- ... removeLiquidityGyro3.integration.test.ts} | 69 +++++++------- ... removeLiquidityGyroE.integration.test.ts} | 69 +++++++------- ...emoveLiquidityGyroEV2.integration.test.ts} | 81 +++++++++-------- 10 files changed, 338 insertions(+), 314 deletions(-) rename test/{gyro2Join.integration.test.ts => addLiquidityGyro2.integration.test.ts} (70%) rename test/{gyro3Join.integration.test.ts => addLiquidityGyro3.integration.test.ts} (71%) rename test/{gyroEJoin.integration.test.ts => addLiquidityGyroE.integration.test.ts} (71%) rename test/{gyroEV2Join.integration.test.ts => addLiquidityGyroEV2.integration.test.ts} (69%) rename test/{gyro2Exit.integration.test.ts => removeLiquidityGyro2.integration.test.ts} (69%) rename test/{gyro3Exit.integration.test.ts => removeLiquidityGyro3.integration.test.ts} (70%) rename test/{gyroEExit.integration.test.ts => removeLiquidityGyroE.integration.test.ts} (69%) rename test/{gyroEV2Exit.integration.test.ts => removeLiquidityGyroEV2.integration.test.ts} (67%) diff --git a/src/entities/addLiquidity/utils/validateInputs.ts b/src/entities/addLiquidity/utils/validateInputs.ts index d73856da..b6160c78 100644 --- a/src/entities/addLiquidity/utils/validateInputs.ts +++ b/src/entities/addLiquidity/utils/validateInputs.ts @@ -8,12 +8,12 @@ export function validateInputs( input: AddLiquidityInput, poolState: PoolStateInput, ) { - validateComposableStableWithoutBPT( + validateComposableStableWithBPT( poolState.type, poolState.address, poolState.tokens, ); - validateGyroPoolJoinIsNotProportional(input.kind, poolState.type); + validateAddLiquidityGyroIsProportional(input.kind, poolState.type); switch (input.kind) { case AddLiquidityKind.Init: case AddLiquidityKind.Unbalanced: @@ -34,10 +34,10 @@ export function validateInputs( } } -export const gyroJoinKindNotSupported = - 'INPUT_ERROR: Gyro pools do not implement this join kind, only Proportional Joins(3 - ALL_TOKENS_IN_FOR_BPT_OUT) are supported'; +export const addLiquidityKindNotSupportedByGyro = + 'INPUT_ERROR: Gyro pools do not implement this add liquidity kind, only Add Liquidity Proportional (3 - ALL_TOKENS_IN_FOR_BPT_OUT) is supported'; -function validateGyroPoolJoinIsNotProportional( +function validateAddLiquidityGyroIsProportional( kind: AddLiquidityKind, poolType: string, ) { @@ -45,11 +45,11 @@ function validateGyroPoolJoinIsNotProportional( ['GYROE', 'GYRO2', 'GYRO3'].includes(poolType) && kind !== AddLiquidityKind.Proportional ) { - throw new Error(gyroJoinKindNotSupported); + throw new Error(addLiquidityKindNotSupportedByGyro); } } -function validateComposableStableWithoutBPT( +function validateComposableStableWithBPT( poolType: string, poolAddress: Address, poolTokens: MinimalToken[], @@ -57,7 +57,7 @@ function validateComposableStableWithoutBPT( const bptIndex = poolTokens.findIndex((t) => t.address === poolAddress); if (['PHANTOM_STABLE'].includes(poolType) && bptIndex < 0) { throw new Error( - 'INPUT_ERROR: Composable Stable Pool State without BPT token included', + 'INPUT_ERROR: Composable Stable Pool State should have BPT token included', ); } } diff --git a/src/entities/removeLiquidity/utils/validateInputs.ts b/src/entities/removeLiquidity/utils/validateInputs.ts index 7ceced66..beb84b27 100644 --- a/src/entities/removeLiquidity/utils/validateInputs.ts +++ b/src/entities/removeLiquidity/utils/validateInputs.ts @@ -8,12 +8,12 @@ export function validateInputs( input: RemoveLiquidityInput, poolState: PoolStateInput, ) { - validateComposableStableWithoutBPT( + validateComposableStableWithBPT( poolState.type, poolState.address, poolState.tokens, ); - validateGyroPoolJoinIsNotProportional(input.kind, poolState.type); + validateRemoveLiquidityGyroIsProportional(input.kind, poolState.type); switch (input.kind) { case RemoveLiquidityKind.Unbalanced: areTokensInArray( @@ -33,10 +33,10 @@ export function validateInputs( } } -export const gyroExitKindNotSupported = - 'INPUT_ERROR: Gyro pools do not implement this exit kind, only Proportional Exits(1 - EXACT_BPT_IN_FOR_TOKENS_OUT) are supported'; +export const removeLiquidityKindNotSupportedByGyro = + 'INPUT_ERROR: Gyro pools do not implement this remove liquidity kind, only Remove Liquidity Proportional (1 - EXACT_BPT_IN_FOR_TOKENS_OUT) is supported'; -function validateGyroPoolJoinIsNotProportional( +function validateRemoveLiquidityGyroIsProportional( kind: RemoveLiquidityKind, poolType: string, ) { @@ -44,11 +44,11 @@ function validateGyroPoolJoinIsNotProportional( ['GYROE', 'GYRO2', 'GYRO3'].includes(poolType) && kind !== RemoveLiquidityKind.Proportional ) { - throw new Error(gyroExitKindNotSupported); + throw new Error(removeLiquidityKindNotSupportedByGyro); } } -function validateComposableStableWithoutBPT( +function validateComposableStableWithBPT( poolType: string, poolAddress: Address, poolTokens: MinimalToken[], @@ -56,7 +56,7 @@ function validateComposableStableWithoutBPT( const bptIndex = poolTokens.findIndex((t) => t.address === poolAddress); if (['PHANTOM_STABLE'].includes(poolType) && bptIndex < 0) { throw new Error( - 'INPUT_ERROR: Composable Stable Pool State without BPT token included', + 'INPUT_ERROR: Composable Stable Pool State should have BPT token included', ); } } diff --git a/test/gyro2Join.integration.test.ts b/test/addLiquidityGyro2.integration.test.ts similarity index 70% rename from test/gyro2Join.integration.test.ts rename to test/addLiquidityGyro2.integration.test.ts index ca9a5319..0d076711 100644 --- a/test/gyro2Join.integration.test.ts +++ b/test/addLiquidityGyro2.integration.test.ts @@ -1,5 +1,5 @@ //0xdac42eeb17758daa38caf9a3540c808247527ae3000200000000000000000a2b - 2CLP-USDC-DAI -// pnpm test -- weightedJoin.integration.test.ts +// pnpm test -- addLiquidityGyro2.integration.test.ts import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -15,33 +15,36 @@ import { } from 'viem'; import { - ProportionalJoinInput, - JoinKind, + AddLiquidityProportionalInput, + AddLiquidityKind, Slippage, Hex, PoolStateInput, CHAINS, ChainId, - PoolJoin, - JoinInput, + AddLiquidity, + AddLiquidityInput, InputAmount, getPoolAddress, - UnbalancedJoinInput, - SingleAssetJoinInput, + AddLiquidityUnbalancedInput, + AddLiquiditySingleTokenInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; -import { JoinTxInput } from './lib/utils/types'; +import { + assertAddLiquidityProportional, + doAddLiquidity, +} from './lib/utils/addLiquidityHelper'; +import { AddLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; +import { addLiquidityKindNotSupportedByGyro } from '../src/entities/addLiquidity/utils/validateInputs'; const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); const chainId = ChainId.POLYGON; const poolId = '0xdac42eeb17758daa38caf9a3540c808247527ae3000200000000000000000a2b'; // 2CLP-USDC-DAI -describe('Gyro2 join test', () => { - let txInput: JoinTxInput; +describe('Gyro2 add liquidity test', () => { + let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; beforeAll(async () => { @@ -61,11 +64,11 @@ describe('Gyro2 join test', () => { txInput = { client, - poolJoin: new PoolJoin(), + addLiquidity: new AddLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput, testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens - joinInput: {} as JoinInput, + addLiquidityInput: {} as AddLiquidityInput, }; }); @@ -87,40 +90,40 @@ describe('Gyro2 join test', () => { ); }); - describe('proportional join', () => { - let joinInput: ProportionalJoinInput; + describe('proportional', () => { + let addLiquidityInput: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), decimals: 18, address: poolStateInput.address, }; - joinInput = { + addLiquidityInput = { bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, - joinResult, + addLiquidityInput, + addLiquidityOutput, txInput.slippage, ); }); //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network }); - describe('unbalanced join', () => { - let input: Omit; + describe('unbalanced', () => { + let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { amountsIn = txInput.poolStateInput.tokens.map((t) => ({ @@ -131,25 +134,25 @@ describe('Gyro2 join test', () => { input = { chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; }); - test('must throw unsupported single asset join error', async () => { - const joinInput = { + test('must throw add liquidity kind not supported error', async () => { + const addLiquidityInput = { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); - describe('single asset join', () => { - let joinInput: SingleAssetJoinInput; + describe('single token', () => { + let addLiquidityInput: AddLiquiditySingleTokenInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -157,22 +160,22 @@ describe('Gyro2 join test', () => { address: poolStateInput.address, }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - joinInput = { + addLiquidityInput = { bptOut, tokenIn, chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: AddLiquidityKind.SingleToken, }; }); - test('must throw unsupported single asset join error', async () => { + test('must throw add liquidity kind not supported error', async () => { await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyro3Join.integration.test.ts b/test/addLiquidityGyro3.integration.test.ts similarity index 71% rename from test/gyro3Join.integration.test.ts rename to test/addLiquidityGyro3.integration.test.ts index acf1bb6a..e2f7ef52 100644 --- a/test/gyro3Join.integration.test.ts +++ b/test/addLiquidityGyro3.integration.test.ts @@ -14,33 +14,36 @@ import { } from 'viem'; import { - ProportionalJoinInput, - JoinKind, + AddLiquidityProportionalInput, + AddLiquidityKind, Slippage, Hex, PoolStateInput, CHAINS, ChainId, - PoolJoin, - JoinInput, + AddLiquidity, + AddLiquidityInput, InputAmount, getPoolAddress, - UnbalancedJoinInput, - SingleAssetJoinInput, + AddLiquidityUnbalancedInput, + AddLiquiditySingleTokenInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; -import { JoinTxInput } from './lib/utils/types'; +import { + assertAddLiquidityProportional, + doAddLiquidity, +} from './lib/utils/addLiquidityHelper'; +import { AddLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; +import { addLiquidityKindNotSupportedByGyro } from '../src/entities/addLiquidity/utils/validateInputs'; const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); const chainId = ChainId.POLYGON; const poolId = '0x17f1ef81707811ea15d9ee7c741179bbe2a63887000100000000000000000799'; // 3CLP-BUSD-USDC-USDT -describe('Gyro3 join test', () => { - let txInput: JoinTxInput; +describe('Gyro3 add liquidity test', () => { + let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; beforeAll(async () => { @@ -60,11 +63,11 @@ describe('Gyro3 join test', () => { txInput = { client, - poolJoin: new PoolJoin(), + addLiquidity: new AddLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput, testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens - joinInput: {} as JoinInput, + addLiquidityInput: {} as AddLiquidityInput, }; }); @@ -76,7 +79,7 @@ describe('Gyro3 join test', () => { ...txInput.poolStateInput.tokens.map((t) => t.address), txInput.poolStateInput.address, ], - [0,51,0,0], + [0, 51, 0, 0], [ ...txInput.poolStateInput.tokens.map((t) => parseUnits('10000', t.decimals), @@ -86,40 +89,40 @@ describe('Gyro3 join test', () => { ); }); - describe('proportional join', () => { - let joinInput: ProportionalJoinInput; + describe('proportional', () => { + let addLiquidityInput: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), decimals: 18, address: poolStateInput.address, }; - joinInput = { + addLiquidityInput = { bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, - joinResult, + addLiquidityInput, + addLiquidityOutput, txInput.slippage, ); }); //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network }); - describe('unbalanced join', () => { - let input: Omit; + describe('unbalanced', () => { + let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { amountsIn = txInput.poolStateInput.tokens.map((t) => ({ @@ -130,25 +133,25 @@ describe('Gyro3 join test', () => { input = { chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; }); - test('must throw unsupported single asset join error', async () => { - const joinInput = { + test('must throw add liquidity kind not supported error', async () => { + const addLiquidityInput = { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); - describe('single asset join', () => { - let joinInput: SingleAssetJoinInput; + describe('single token', () => { + let addLiquidityInput: AddLiquiditySingleTokenInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -156,22 +159,22 @@ describe('Gyro3 join test', () => { address: poolStateInput.address, }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - joinInput = { + addLiquidityInput = { bptOut, tokenIn, chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: AddLiquidityKind.SingleToken, }; }); - test('must throw unsupported single asset join error', async () => { + test('must throw add liquidity kind not supported error', async () => { await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyroEJoin.integration.test.ts b/test/addLiquidityGyroE.integration.test.ts similarity index 71% rename from test/gyroEJoin.integration.test.ts rename to test/addLiquidityGyroE.integration.test.ts index c82f74dd..0b8bf984 100644 --- a/test/gyroEJoin.integration.test.ts +++ b/test/addLiquidityGyroE.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- weightedJoin.integration.test.ts +// pnpm test -- addLiquidityGyro3.integration.test.ts import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -14,33 +14,36 @@ import { } from 'viem'; import { - ProportionalJoinInput, - JoinKind, + AddLiquidityProportionalInput, + AddLiquidityKind, Slippage, Hex, PoolStateInput, CHAINS, ChainId, - PoolJoin, - JoinInput, + AddLiquidity, + AddLiquidityInput, InputAmount, getPoolAddress, - UnbalancedJoinInput, - SingleAssetJoinInput, + AddLiquidityUnbalancedInput, + AddLiquiditySingleTokenInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; -import { JoinTxInput } from './lib/utils/types'; +import { + assertAddLiquidityProportional, + doAddLiquidity, +} from './lib/utils/addLiquidityHelper'; +import { AddLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; +import { addLiquidityKindNotSupportedByGyro } from '../src/entities/addLiquidity/utils/validateInputs'; const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); const chainId = ChainId.POLYGON; const poolId = '0xa489c057de6c3177380ea264ebdf686b7f564f510002000000000000000008e2'; // ECLP-wstETH-wETH -describe('gyroE V2 join test', () => { - let txInput: JoinTxInput; +describe('gyroE V2 add liquidity test', () => { + let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; beforeAll(async () => { @@ -60,11 +63,11 @@ describe('gyroE V2 join test', () => { txInput = { client, - poolJoin: new PoolJoin(), + addLiquidity: new AddLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput, testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens - joinInput: {} as JoinInput, + addLiquidityInput: {} as AddLiquidityInput, }; }); @@ -86,39 +89,39 @@ describe('gyroE V2 join test', () => { ); }); - describe('proportional join', () => { - let joinInput: ProportionalJoinInput; + describe('proportional', () => { + let addLiquidityInput: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), decimals: 18, address: poolStateInput.address, }; - joinInput = { + addLiquidityInput = { bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, - joinResult, + addLiquidityInput, + addLiquidityOutput, txInput.slippage, ); }); }); - describe('unbalanced join', () => { - let input: Omit; + describe('unbalanced', () => { + let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { amountsIn = txInput.poolStateInput.tokens.map((t) => ({ @@ -129,26 +132,26 @@ describe('gyroE V2 join test', () => { input = { chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; }); test('with tokens', async () => { - const joinInput = { + const addLiquidityInput = { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network }); - describe('single asset join', () => { - let joinInput: SingleAssetJoinInput; + describe('single token', () => { + let addLiquidityInput: AddLiquiditySingleTokenInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -156,22 +159,22 @@ describe('gyroE V2 join test', () => { address: poolStateInput.address, }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - joinInput = { + addLiquidityInput = { bptOut, tokenIn, chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: AddLiquidityKind.SingleToken, }; }); - test('must throw unsupported single asset join error', async () => { + test('must throw add liquidity kind not supported error', async () => { await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyroEV2Join.integration.test.ts b/test/addLiquidityGyroEV2.integration.test.ts similarity index 69% rename from test/gyroEV2Join.integration.test.ts rename to test/addLiquidityGyroEV2.integration.test.ts index 2cc03115..edb1638d 100644 --- a/test/gyroEV2Join.integration.test.ts +++ b/test/addLiquidityGyroEV2.integration.test.ts @@ -1,4 +1,4 @@ -// pnpm test -- weightedJoin.integration.test.ts +// pnpm test -- addLiquidityGyroEV2.integration.test.ts import { describe, test, beforeAll, beforeEach, expect } from 'vitest'; import dotenv from 'dotenv'; dotenv.config(); @@ -14,33 +14,36 @@ import { } from 'viem'; import { - ProportionalJoinInput, - JoinKind, + AddLiquidityProportionalInput, + AddLiquidityKind, Slippage, Hex, PoolStateInput, CHAINS, ChainId, - PoolJoin, - JoinInput, + AddLiquidity, + AddLiquidityInput, InputAmount, getPoolAddress, - UnbalancedJoinInput, - SingleAssetJoinInput, + AddLiquidityUnbalancedInput, + AddLiquiditySingleTokenInput, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalJoin, doJoin } from './lib/utils/joinHelper'; -import { JoinTxInput } from './lib/utils/types'; +import { + assertAddLiquidityProportional, + doAddLiquidity, +} from './lib/utils/addLiquidityHelper'; +import { AddLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroJoinKindNotSupported } from '../src/entities/join/utils/validateInputs'; +import { addLiquidityKindNotSupportedByGyro } from '../src/entities/addLiquidity/utils/validateInputs'; const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const chainId = ChainId.MAINNET; const poolId = '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // ECLP-wstETH-wETH -describe('GyroE V2 join test', () => { - let txInput: JoinTxInput; +describe('GyroE V2 add liquidity test', () => { + let txInput: AddLiquidityTxInput; let poolStateInput: PoolStateInput; beforeAll(async () => { @@ -60,11 +63,11 @@ describe('GyroE V2 join test', () => { txInput = { client, - poolJoin: new PoolJoin(), + addLiquidity: new AddLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput, testAddress: '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f', // Balancer DAO Multisig - joinInput: {} as JoinInput, + addLiquidityInput: {} as AddLiquidityInput, }; }); @@ -86,58 +89,58 @@ describe('GyroE V2 join test', () => { ); }); - describe('proportional join', () => { - let joinInput: ProportionalJoinInput; + describe('proportional', () => { + let addLiquidityInput: AddLiquidityProportionalInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('2'), decimals: 18, address: poolStateInput.address, }; - joinInput = { + addLiquidityInput = { bptOut, chainId, rpcUrl, - kind: JoinKind.Proportional, + kind: AddLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - joinInput, - joinResult, + addLiquidityInput, + addLiquidityOutput, txInput.slippage, ); }); test('with native', async () => { - const joinResult = await doJoin({ + const addLiquidityOutput = await doAddLiquidity({ ...txInput, - joinInput: { - ...joinInput, + addLiquidityInput: { + ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, }); - assertProportionalJoin( + assertAddLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, { - ...joinInput, + ...addLiquidityInput, useNativeAssetAsWrappedAmountIn: true, }, - joinResult, + addLiquidityOutput, txInput.slippage, ); }); }); - describe('unbalanced join', () => { - let input: Omit; + describe('unbalanced', () => { + let input: Omit; let amountsIn: InputAmount[]; beforeAll(() => { amountsIn = txInput.poolStateInput.tokens.map((t) => ({ @@ -148,25 +151,25 @@ describe('GyroE V2 join test', () => { input = { chainId, rpcUrl, - kind: JoinKind.Unbalanced, + kind: AddLiquidityKind.Unbalanced, }; }); test('with tokens', async () => { - const joinInput = { + const addLiquidityInput = { ...input, amountsIn: [...amountsIn.splice(0, 1)], }; await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); - describe('single asset join', () => { - let joinInput: SingleAssetJoinInput; + describe('single token', () => { + let addLiquidityInput: AddLiquiditySingleTokenInput; beforeAll(() => { const bptOut: InputAmount = { rawAmount: parseEther('1'), @@ -174,22 +177,22 @@ describe('GyroE V2 join test', () => { address: poolStateInput.address, }; const tokenIn = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - joinInput = { + addLiquidityInput = { bptOut, tokenIn, chainId, rpcUrl, - kind: JoinKind.SingleAsset, + kind: AddLiquidityKind.SingleToken, }; }); - test('must throw unsupported single asset join error', async () => { + test('must throw add liquidity kind not supported error', async () => { await expect(() => - doJoin({ + doAddLiquidity({ ...txInput, - joinInput, + addLiquidityInput, }), - ).rejects.toThrowError(gyroJoinKindNotSupported); + ).rejects.toThrowError(addLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyro2Exit.integration.test.ts b/test/removeLiquidityGyro2.integration.test.ts similarity index 69% rename from test/gyro2Exit.integration.test.ts rename to test/removeLiquidityGyro2.integration.test.ts index 23b6870d..77d249c9 100644 --- a/test/gyro2Exit.integration.test.ts +++ b/test/removeLiquidityGyro2.integration.test.ts @@ -12,34 +12,37 @@ import { walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, - ExitKind, + RemoveLiquiditySingleTokenInput, + RemoveLiquidityProportionalInput, + RemoveLiquidityUnbalancedInput, + RemoveLiquidityKind, Slippage, PoolStateInput, - PoolExit, + RemoveLiquidity, Address, Hex, CHAINS, ChainId, getPoolAddress, - ExitInput, + RemoveLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; -import { ExitTxInput } from './lib/utils/types'; +import { + assertRemoveLiquidityProportional, + doRemoveLiquidity, +} from './lib/utils/removeLiquidityHelper'; +import { RemoveLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; +import { removeLiquidityKindNotSupportedByGyro } from '../src/entities/removeLiquidity/utils/validateInputs'; const chainId = ChainId.POLYGON; const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); const poolId = '0xdac42eeb17758daa38caf9a3540c808247527ae3000200000000000000000a2b'; // 2CLP-USDC-DAI -describe('Gyro2 exit test', () => { - let txInput: ExitTxInput; +describe('Gyro2 remove liquidity test', () => { + let txInput: RemoveLiquidityTxInput; let poolInput: PoolStateInput; beforeAll(async () => { // setup mock api @@ -58,11 +61,11 @@ describe('Gyro2 exit test', () => { txInput = { client, - poolExit: new PoolExit(), + removeLiquidity: new RemoveLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens - exitInput: {} as ExitInput, + removeLiquidityInput: {} as RemoveLiquidityInput, }; }); @@ -76,8 +79,8 @@ describe('Gyro2 exit test', () => { ); }); - describe('proportional exit', () => { - let input: ProportionalExitInput; + describe('proportional', () => { + let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('0.01'), @@ -88,28 +91,28 @@ describe('Gyro2 exit test', () => { bptIn, chainId, rpcUrl, - kind: ExitKind.Proportional, + kind: RemoveLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const exitResult = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network }); }); - describe('unbalanced exit', async () => { - let input: Omit; + describe('unbalanced', async () => { + let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { amountsOut = poolInput.tokens.map((t) => ({ @@ -120,22 +123,22 @@ describe('Gyro2 exit test', () => { input = { chainId, rpcUrl, - kind: ExitKind.Unbalanced, + kind: RemoveLiquidityKind.Unbalanced, }; }); - test('must throw error, exit kind not supported', async () => { - const exitInput = { + test('must throw remove liquidity kind not supported error', async () => { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; await expect(() => - doExit({ ...txInput, exitInput }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); - describe('single asset exit', () => { - let input: SingleAssetExitInput; + describe('single token', () => { + let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -148,13 +151,13 @@ describe('Gyro2 exit test', () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; }); - test('must throw error, exit kind not supported', async () => { + test('must throw remove liquidity kind not supported error', async () => { await expect(() => - doExit({ ...txInput, exitInput: input }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput: input }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyro3Exit.integration.test.ts b/test/removeLiquidityGyro3.integration.test.ts similarity index 70% rename from test/gyro3Exit.integration.test.ts rename to test/removeLiquidityGyro3.integration.test.ts index e6ef9f1a..e456bfe0 100644 --- a/test/gyro3Exit.integration.test.ts +++ b/test/removeLiquidityGyro3.integration.test.ts @@ -12,34 +12,37 @@ import { walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, - ExitKind, + RemoveLiquiditySingleTokenInput, + RemoveLiquidityProportionalInput, + RemoveLiquidityUnbalancedInput, + RemoveLiquidityKind, Slippage, PoolStateInput, - PoolExit, + RemoveLiquidity, Address, Hex, CHAINS, ChainId, getPoolAddress, - ExitInput, + RemoveLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; -import { ExitTxInput } from './lib/utils/types'; +import { + assertRemoveLiquidityProportional, + doRemoveLiquidity, +} from './lib/utils/removeLiquidityHelper'; +import { RemoveLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; +import { removeLiquidityKindNotSupportedByGyro } from '../src/entities/removeLiquidity/utils/validateInputs'; const chainId = ChainId.POLYGON; const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); const poolId = '0x17f1ef81707811ea15d9ee7c741179bbe2a63887000100000000000000000799'; // 3CLP-BUSD-USDC-USDT -describe('Gyro3 exit test', () => { - let txInput: ExitTxInput; +describe('Gyro3 remove liquidity test', () => { + let txInput: RemoveLiquidityTxInput; let poolInput: PoolStateInput; beforeAll(async () => { // setup mock api @@ -58,11 +61,11 @@ describe('Gyro3 exit test', () => { txInput = { client, - poolExit: new PoolExit(), + removeLiquidity: new RemoveLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens - exitInput: {} as ExitInput, + removeLiquidityInput: {} as RemoveLiquidityInput, }; }); @@ -76,8 +79,8 @@ describe('Gyro3 exit test', () => { ); }); - describe('proportional exit', () => { - let input: ProportionalExitInput; + describe('proportional', () => { + let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('0.01'), @@ -88,28 +91,28 @@ describe('Gyro3 exit test', () => { bptIn, chainId, rpcUrl, - kind: ExitKind.Proportional, + kind: RemoveLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const exitResult = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network }); }); - describe('unbalanced exit', async () => { - let input: Omit; + describe('unbalanced', async () => { + let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { amountsOut = poolInput.tokens.map((t) => ({ @@ -120,22 +123,22 @@ describe('Gyro3 exit test', () => { input = { chainId, rpcUrl, - kind: ExitKind.Unbalanced, + kind: RemoveLiquidityKind.Unbalanced, }; }); - test('must throw error, exit kind not supported', async () => { - const exitInput = { + test('must throw remove liquidity kind not supported error', async () => { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; await expect(() => - doExit({ ...txInput, exitInput }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); - describe('single asset exit', () => { - let input: SingleAssetExitInput; + describe('single token', () => { + let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -148,13 +151,13 @@ describe('Gyro3 exit test', () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; }); - test('must throw error, exit kind not supported', async () => { + test('must throw remove liquidity kind not supported error', async () => { await expect(() => - doExit({ ...txInput, exitInput: input }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput: input }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyroEExit.integration.test.ts b/test/removeLiquidityGyroE.integration.test.ts similarity index 69% rename from test/gyroEExit.integration.test.ts rename to test/removeLiquidityGyroE.integration.test.ts index 0b48a977..dc839492 100644 --- a/test/gyroEExit.integration.test.ts +++ b/test/removeLiquidityGyroE.integration.test.ts @@ -12,34 +12,37 @@ import { walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, - ExitKind, + RemoveLiquiditySingleTokenInput, + RemoveLiquidityProportionalInput, + RemoveLiquidityUnbalancedInput, + RemoveLiquidityKind, Slippage, PoolStateInput, - PoolExit, + RemoveLiquidity, Address, Hex, CHAINS, ChainId, getPoolAddress, - ExitInput, + RemoveLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; -import { ExitTxInput } from './lib/utils/types'; +import { + assertRemoveLiquidityProportional, + doRemoveLiquidity, +} from './lib/utils/removeLiquidityHelper'; +import { RemoveLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; +import { removeLiquidityKindNotSupportedByGyro } from '../src/entities/removeLiquidity/utils/validateInputs'; const chainId = ChainId.POLYGON; const { rpcUrl } = await startFork(ANVIL_NETWORKS.POLYGON); const poolId = '0x97469e6236bd467cd147065f77752b00efadce8a0002000000000000000008c0'; // ECLP-TUSD-USDC -describe('GyroE V1 exit test', () => { - let txInput: ExitTxInput; +describe('GyroE V1 remove liquidity test', () => { + let txInput: RemoveLiquidityTxInput; let poolInput: PoolStateInput; beforeAll(async () => { // setup mock api @@ -58,11 +61,11 @@ describe('GyroE V1 exit test', () => { txInput = { client, - poolExit: new PoolExit(), + removeLiquidity: new RemoveLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0xe84f75fc9caa49876d0ba18d309da4231d44e94d', // MATIC Holder Wallet, must hold amount of matic to approve tokens - exitInput: {} as ExitInput, + removeLiquidityInput: {} as RemoveLiquidityInput, }; }); @@ -76,8 +79,8 @@ describe('GyroE V1 exit test', () => { ); }); - describe('proportional exit', () => { - let input: ProportionalExitInput; + describe('proportional', () => { + let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -88,28 +91,28 @@ describe('GyroE V1 exit test', () => { bptIn, chainId, rpcUrl, - kind: ExitKind.Proportional, + kind: RemoveLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const exitResult = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); //Removed test with native, because there are no GyroE V1 pool with wrapped native asset in any network }); - describe('unbalanced exit', async () => { - let input: Omit; + describe('unbalanced', async () => { + let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { amountsOut = poolInput.tokens.map((t) => ({ @@ -120,22 +123,22 @@ describe('GyroE V1 exit test', () => { input = { chainId, rpcUrl, - kind: ExitKind.Unbalanced, + kind: RemoveLiquidityKind.Unbalanced, }; }); - test('must throw error, exit kind not supported', async () => { - const exitInput = { + test('must throw remove liquidity kind not supported error', async () => { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; await expect(() => - doExit({ ...txInput, exitInput }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); - describe('single asset exit', () => { - let input: SingleAssetExitInput; + describe('single token', () => { + let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -148,13 +151,13 @@ describe('GyroE V1 exit test', () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; }); - test('must throw error, exit kind not supported', async () => { + test('must throw remove liquidity kind not supported error', async () => { await expect(() => - doExit({ ...txInput, exitInput: input }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput: input }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); }); diff --git a/test/gyroEV2Exit.integration.test.ts b/test/removeLiquidityGyroEV2.integration.test.ts similarity index 67% rename from test/gyroEV2Exit.integration.test.ts rename to test/removeLiquidityGyroEV2.integration.test.ts index 9f1f194b..ff7cd8dd 100644 --- a/test/gyroEV2Exit.integration.test.ts +++ b/test/removeLiquidityGyroEV2.integration.test.ts @@ -12,34 +12,37 @@ import { walletActions, } from 'viem'; import { - SingleAssetExitInput, - ProportionalExitInput, - UnbalancedExitInput, - ExitKind, + RemoveLiquiditySingleTokenInput, + RemoveLiquidityProportionalInput, + RemoveLiquidityUnbalancedInput, + RemoveLiquidityKind, Slippage, PoolStateInput, - PoolExit, + RemoveLiquidity, Address, Hex, CHAINS, ChainId, getPoolAddress, - ExitInput, + RemoveLiquidityInput, InputAmount, } from '../src'; import { forkSetup } from './lib/utils/helper'; -import { assertProportionalExit, doExit } from './lib/utils/exitHelper'; -import { ExitTxInput } from './lib/utils/types'; +import { + assertRemoveLiquidityProportional, + doRemoveLiquidity, +} from './lib/utils/removeLiquidityHelper'; +import { RemoveLiquidityTxInput } from './lib/utils/types'; import { ANVIL_NETWORKS, startFork } from './anvil/anvil-global-setup'; -import { gyroExitKindNotSupported } from '../src/entities/exit/utils/validateInputs'; +import { removeLiquidityKindNotSupportedByGyro } from '../src/entities/removeLiquidity/utils/validateInputs'; const chainId = ChainId.MAINNET; const { rpcUrl } = await startFork(ANVIL_NETWORKS.MAINNET); const poolId = '0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de'; // ECLP-wstETH-wETH -describe('GyroE V2 exit test', () => { - let txInput: ExitTxInput; +describe('GyroE V2 remove liquidity test', () => { + let txInput: RemoveLiquidityTxInput; let poolInput: PoolStateInput; beforeAll(async () => { // setup mock api @@ -58,11 +61,11 @@ describe('GyroE V2 exit test', () => { txInput = { client, - poolExit: new PoolExit(), + removeLiquidity: new RemoveLiquidity(), slippage: Slippage.fromPercentage('1'), // 1% poolStateInput: poolInput, testAddress: '0x10a19e7ee7d7f8a52822f6817de8ea18204f2e4f', // Balancer DAO Multisig - exitInput: {} as ExitInput, + removeLiquidityInput: {} as RemoveLiquidityInput, }; }); @@ -76,8 +79,8 @@ describe('GyroE V2 exit test', () => { ); }); - describe('proportional exit', () => { - let input: ProportionalExitInput; + describe('proportional', () => { + let input: RemoveLiquidityProportionalInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('0.01'), @@ -88,44 +91,44 @@ describe('GyroE V2 exit test', () => { bptIn, chainId, rpcUrl, - kind: ExitKind.Proportional, + kind: RemoveLiquidityKind.Proportional, }; }); test('with tokens', async () => { - const exitResult = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, - exitInput: input, + removeLiquidityInput: input, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, input, - exitResult, + removeLiquidityOutput, txInput.slippage, ); }); test('with native', async () => { - const exitInput = { + const removeLiquidityInput = { ...input, useNativeAssetAsWrappedAmountIn: true, }; - const exitResult = await doExit({ + const removeLiquidityOutput = await doRemoveLiquidity({ ...txInput, - exitInput, + removeLiquidityInput, }); - assertProportionalExit( + assertRemoveLiquidityProportional( txInput.client.chain?.id as number, txInput.poolStateInput, - exitInput, - exitResult, + removeLiquidityInput, + removeLiquidityOutput, txInput.slippage, ); }); }); - describe('unbalanced exit', async () => { - let input: Omit; + describe('unbalanced', async () => { + let input: Omit; let amountsOut: InputAmount[]; beforeAll(() => { amountsOut = poolInput.tokens.map((t) => ({ @@ -136,22 +139,22 @@ describe('GyroE V2 exit test', () => { input = { chainId, rpcUrl, - kind: ExitKind.Unbalanced, + kind: RemoveLiquidityKind.Unbalanced, }; }); - test('must throw error, exit kind not supported', async () => { - const exitInput = { + test('must throw remove liquidity kind not supported error', async () => { + const removeLiquidityInput = { ...input, amountsOut: amountsOut.slice(0, 1), }; await expect(() => - doExit({ ...txInput, exitInput }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); - describe('single asset exit', () => { - let input: SingleAssetExitInput; + describe('single token', () => { + let input: RemoveLiquiditySingleTokenInput; beforeAll(() => { const bptIn: InputAmount = { rawAmount: parseEther('1'), @@ -164,13 +167,13 @@ describe('GyroE V2 exit test', () => { rpcUrl, bptIn, tokenOut, - kind: ExitKind.SingleAsset, + kind: RemoveLiquidityKind.SingleToken, }; }); - test('must throw error, exit kind not supported', async () => { + test('must throw remove liquidity kind not supported error', async () => { await expect(() => - doExit({ ...txInput, exitInput: input }), - ).rejects.toThrowError(gyroExitKindNotSupported); + doRemoveLiquidity({ ...txInput, removeLiquidityInput: input }), + ).rejects.toThrowError(removeLiquidityKindNotSupportedByGyro); }); }); });