From e08b4f2bba0d1b88f00cbc83e048ea639dc4e465 Mon Sep 17 00:00:00 2001 From: Marvin Date: Mon, 15 Nov 2021 16:12:28 +0100 Subject: [PATCH 01/16] export helper functions from util file --- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 80 ++----------------- .../test/utils/WeightCalculationUtil.test.ts | 78 ++++++++++++++++++ 2 files changed, 84 insertions(+), 74 deletions(-) create mode 100644 pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index a049e2ca6f..506d54cd6f 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -3,6 +3,12 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; import { Contract } from '@ethersproject/contracts'; import { fp } from '../../../pvt/helpers/src/numbers'; +import { + getExpectedWeights, + getRandomBaseWeights, + getIntegerBetween, + setupAdjustTokens, +} from './utils/WeightCalculationUtil.test'; const { utils: { parseEther }, @@ -13,63 +19,6 @@ const HUNDRED_PERCENT = BigNumber.from(10).pow(18); const getTotalWeight = (weights: BigNumber[]): BigNumber => weights.reduce((acc, curr) => acc.add(curr), BigNumber.from(0)); -const percentage = (a: BigNumber, b: BigNumber): BigNumber => a.mul(HUNDRED_PERCENT).div(b); - -const getExpectedWeights = (baseWeightsNumbers: number[], fixWeightsNumbers: number[]): BigNumber[] => { - const baseWeights = baseWeightsNumbers.map((w) => fp(w)); - const fixWeights = fixWeightsNumbers.map((w) => fp(w)); - - let totalDenormalizedBaseWeight = BigNumber.from(0); - let totalDenormalizedFixedWeight = BigNumber.from(0); - - for (let i = 0; i < baseWeights.length; i++) { - totalDenormalizedFixedWeight = totalDenormalizedFixedWeight.add(fixWeights[i]); - if (fixWeights[i].isZero()) { - totalDenormalizedBaseWeight = totalDenormalizedBaseWeight.add(baseWeights[i]); - } - } - - const delta = HUNDRED_PERCENT.sub(totalDenormalizedBaseWeight).sub(totalDenormalizedFixedWeight).abs(); - - const finalWeights = baseWeights.map((initialBaseWeight: BigNumber, idx: number) => { - if (!fixWeights[idx].isZero()) return fixWeights[idx]; - - const adjustedBaseWeight = totalDenormalizedBaseWeight.add(totalDenormalizedFixedWeight).gt(HUNDRED_PERCENT) - ? initialBaseWeight.sub( - percentage(initialBaseWeight, totalDenormalizedBaseWeight).mul(delta).div(HUNDRED_PERCENT) - ) - : initialBaseWeight.add( - percentage(initialBaseWeight, totalDenormalizedBaseWeight).mul(delta).div(HUNDRED_PERCENT) - ); - - return adjustedBaseWeight; - }); - - return finalWeights; -}; - -const getDecimalBetween = (min: number, max: number): number => - parseFloat((Math.random() * (max - min) + min).toFixed(4)); - -const getIntegerBetween = (min: number, max: number): number => Math.floor(Math.random() * max + min); - -// generates random normalized base weights -const getRandomBaseWeights = (numWeights: number): number[] => { - let residual = 1; - const baseWeights = []; - - for (let i = 0; i < numWeights - 1; i++) { - const weight = getDecimalBetween(Number.MIN_VALUE, residual / 2); - - residual -= weight; - baseWeights.push(weight); - } - - baseWeights.push(residual); - - return baseWeights; -}; - // generates random baseWeights and fixedWeights for the case where one or two new tokens are added const setupNewTokens = (numWeights: number) => { const baseWeights = getRandomBaseWeights(numWeights); @@ -89,23 +38,6 @@ const setupNewTokens = (numWeights: number) => { return { baseWeights, fixedWeights }; }; -// generates random baseWeights and fixedWeights for the case where the weights of existing tokens in a pool are changed -const setupAdjustTokens = (numWeights: number) => { - const baseWeights = getRandomBaseWeights(numWeights); - const numberAdjustTokens = getIntegerBetween(1, numWeights - 1); - const fixedWeights = new Array(numWeights).fill(0); - - let residual = 1; - for (let i = 0; i < numberAdjustTokens; i++) { - const newWeight = getDecimalBetween(Number.MIN_VALUE, residual / 2); - fixedWeights[i] = newWeight; - baseWeights[i] = 0; - residual -= newWeight; - } - - return { baseWeights, fixedWeights }; -}; - describe('IndexPoolUtils', function () { let indexPoolUtilsInstance: Contract; diff --git a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts new file mode 100644 index 0000000000..46f5e1f89a --- /dev/null +++ b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts @@ -0,0 +1,78 @@ +import { BigNumber } from '@ethersproject/bignumber'; +import { fp } from '@balancer-labs/v2-helpers/src/numbers'; + +const HUNDRED_PERCENT = BigNumber.from(10).pow(18); + +const percentage = (a: BigNumber, b: BigNumber): BigNumber => a.mul(HUNDRED_PERCENT).div(b); + +export const getExpectedWeights = (baseWeightsNumbers: number[], fixWeightsNumbers: number[]): BigNumber[] => { + const baseWeights = baseWeightsNumbers.map((w) => fp(w)); + const fixWeights = fixWeightsNumbers.map((w) => fp(w)); + + let totalDenormalizedBaseWeight = BigNumber.from(0); + let totalDenormalizedFixedWeight = BigNumber.from(0); + + for (let i = 0; i < baseWeights.length; i++) { + totalDenormalizedFixedWeight = totalDenormalizedFixedWeight.add(fixWeights[i]); + if (fixWeights[i].isZero()) { + totalDenormalizedBaseWeight = totalDenormalizedBaseWeight.add(baseWeights[i]); + } + } + + const delta = HUNDRED_PERCENT.sub(totalDenormalizedBaseWeight).sub(totalDenormalizedFixedWeight).abs(); + + const finalWeights = baseWeights.map((initialBaseWeight: BigNumber, idx: number) => { + if (!fixWeights[idx].isZero()) return fixWeights[idx]; + + const adjustedBaseWeight = totalDenormalizedBaseWeight.add(totalDenormalizedFixedWeight).gt(HUNDRED_PERCENT) + ? initialBaseWeight.sub( + percentage(initialBaseWeight, totalDenormalizedBaseWeight).mul(delta).div(HUNDRED_PERCENT) + ) + : initialBaseWeight.add( + percentage(initialBaseWeight, totalDenormalizedBaseWeight).mul(delta).div(HUNDRED_PERCENT) + ); + + return adjustedBaseWeight; + }); + + return finalWeights; +}; + +export const getDecimalBetween = (min: number, max: number): number => + parseFloat((Math.random() * (max - min) + min).toFixed(4)); + +// generates random normalized base weights +export const getRandomBaseWeights = (numWeights: number): number[] => { + let residual = 1; + const baseWeights = []; + + for (let i = 0; i < numWeights - 1; i++) { + const weight = getDecimalBetween(Number.MIN_VALUE, residual / 2); + + residual -= weight; + baseWeights.push(weight); + } + + baseWeights.push(residual); + + return baseWeights; +}; + +export const getIntegerBetween = (min: number, max: number): number => Math.floor(Math.random() * max + min); + +// generates random baseWeights and fixedWeights for the case where the weights of existing tokens in a pool are changed +export const setupAdjustTokens = (numWeights: number) => { + const baseWeights = getRandomBaseWeights(numWeights); + const numberAdjustTokens = getIntegerBetween(1, numWeights - 1); + const fixedWeights = new Array(numWeights).fill(0); + + let residual = 1; + for (let i = 0; i < numberAdjustTokens; i++) { + const newWeight = getDecimalBetween(Number.MIN_VALUE, residual / 2); + fixedWeights[i] = newWeight; + baseWeights[i] = 0; + residual -= newWeight; + } + + return { baseWeights, fixedWeights }; +}; From 9581d85568e309e516bd0f9215d058e61853cae9 Mon Sep 17 00:00:00 2001 From: Marvin Date: Mon, 15 Nov 2021 16:16:11 +0100 Subject: [PATCH 02/16] move function to utils file --- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 66 ++++++------------- .../test/utils/WeightCalculationUtil.test.ts | 19 ++++++ 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index 506d54cd6f..a8f33353bb 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -3,12 +3,7 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; import { Contract } from '@ethersproject/contracts'; import { fp } from '../../../pvt/helpers/src/numbers'; -import { - getExpectedWeights, - getRandomBaseWeights, - getIntegerBetween, - setupAdjustTokens, -} from './utils/WeightCalculationUtil.test'; +import { getExpectedWeights, setupAdjustTokens, setupNewTokens } from './utils/WeightCalculationUtil.test'; const { utils: { parseEther }, @@ -19,26 +14,7 @@ const HUNDRED_PERCENT = BigNumber.from(10).pow(18); const getTotalWeight = (weights: BigNumber[]): BigNumber => weights.reduce((acc, curr) => acc.add(curr), BigNumber.from(0)); -// generates random baseWeights and fixedWeights for the case where one or two new tokens are added -const setupNewTokens = (numWeights: number) => { - const baseWeights = getRandomBaseWeights(numWeights); - - const numberNewTokens = getIntegerBetween(1, 2); - const fixedWeights = new Array(baseWeights.length).fill(0); - const initialWeight = 0.01; - for (let i = 0; i < numberNewTokens; i++) { - fixedWeights.push(initialWeight); - } - const numberAddedTokens = fixedWeights.length - baseWeights.length; - - for (let i = 0; i < numberAddedTokens; i++) { - baseWeights.push(0); - } - - return { baseWeights, fixedWeights }; -}; - -describe('IndexPoolUtils', function () { +describe.only('IndexPoolUtils', function () { let indexPoolUtilsInstance: Contract; beforeEach(async () => { @@ -167,7 +143,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -209,7 +185,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -251,7 +227,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -293,7 +269,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -335,7 +311,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -377,7 +353,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -419,7 +395,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -461,7 +437,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -503,7 +479,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -545,7 +521,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -587,7 +563,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -629,7 +605,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -671,7 +647,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -713,7 +689,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -755,7 +731,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -797,7 +773,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -839,7 +815,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -881,7 +857,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -923,7 +899,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) diff --git a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts index 46f5e1f89a..3c42165ea2 100644 --- a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts +++ b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts @@ -76,3 +76,22 @@ export const setupAdjustTokens = (numWeights: number) => { return { baseWeights, fixedWeights }; }; + +// generates random baseWeights and fixedWeights for the case where one or two new tokens are added +export const setupNewTokens = (numWeights: number, numNewTokens: number) => { + const baseWeights = getRandomBaseWeights(numWeights); + + const numberNewTokens = getIntegerBetween(1, numNewTokens); + const fixedWeights = new Array(baseWeights.length).fill(0); + const initialWeight = 0.01; + for (let i = 0; i < numberNewTokens; i++) { + fixedWeights.push(initialWeight); + } + const numberAddedTokens = fixedWeights.length - baseWeights.length; + + for (let i = 0; i < numberAddedTokens; i++) { + baseWeights.push(0); + } + + return { baseWeights, fixedWeights }; +}; From d4e108e45a00124a1ad61b150a5aefce078c38e9 Mon Sep 17 00:00:00 2001 From: Marvin Date: Mon, 15 Nov 2021 16:16:38 +0100 Subject: [PATCH 03/16] remove only keyword --- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index a8f33353bb..da518f5ad7 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -14,7 +14,7 @@ const HUNDRED_PERCENT = BigNumber.from(10).pow(18); const getTotalWeight = (weights: BigNumber[]): BigNumber => weights.reduce((acc, curr) => acc.add(curr), BigNumber.from(0)); -describe.only('IndexPoolUtils', function () { +describe('IndexPoolUtils', function () { let indexPoolUtilsInstance: Contract; beforeEach(async () => { From 1fe5336edbc209db1e75348f143d48acb6b51a12 Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 09:08:59 +0100 Subject: [PATCH 04/16] all tests work --- pkg/pool-weighted/test/IndexPool.test.ts | 208 ++++++++++++++++++++++- 1 file changed, 203 insertions(+), 5 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 123a4abb36..6b2d01aa04 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -1,6 +1,6 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; -import { BigNumber } from 'ethers'; +import { BigNumber, utils } from 'ethers'; import { range } from 'lodash'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { fp, pct } from '@balancer-labs/v2-helpers/src/numbers'; @@ -11,6 +11,11 @@ import { MAX_UINT256 } from '@balancer-labs/v2-helpers/src/constants'; import { FundManagement, SingleSwap, SwapKind } from '@balancer-labs/balancer-js'; import { WeightedPoolType } from '../../../pvt/helpers/src/models/pools/weighted/types'; import { calcOutGivenIn } from '@balancer-labs/v2-helpers/src/models/pools/weighted/math'; +import { resourceUsage } from 'process'; +import { getExpectedWeights, setupNewTokens } from './utils/WeightCalculationUtil.test'; +// import {} + +// const HUNDRED_PERCENT = BigNumber.from(10).pow(18); const calculateMaxWeightDifference = (oldWeights: BigNumber[], newWeights: BigNumber[]) => { let maxWeightDifference = 0; @@ -29,6 +34,70 @@ const getTimeForWeightChange = (weightDifference: number) => { return (weightDifference / 1e18) * 86400 * 100; }; +const getExpectedStartWeights = (originalWeights: BigNumber[], numberNewTokens: number) => { + let sum; + + sum = fp(0); + const minimumBalance = 0.01; + const adjustmentAmount = fp(minimumBalance).mul(numberNewTokens).div(originalWeights.length); + + console.log('num tokens = ', numberNewTokens); + console.log('adjustment = ', adjustmentAmount.toString()); + + for (let i = 0; i < originalWeights.length; i++) { + console.log('og weigh before = ', originalWeights[i].toString()); + + originalWeights[i] = originalWeights[i].sub(adjustmentAmount); + + console.log('og weigh after = ', originalWeights[i].toString()); + + sum = sum.add(originalWeights[i]); + } + console.log('sum =', sum.toString()); + const total = sum.add(fp(minimumBalance).mul(numberNewTokens)); + console.log('total = ', total.toString()); + + return [...originalWeights, ...getNewTokensWeightArray(numberNewTokens, minimumBalance)]; +}; + +const getNewTokensWeightArray = (numberNewTokens: number, newTokenTargetWeight: number) => { + const newTokenWeightsArray = new Array(numberNewTokens).fill(newTokenTargetWeight); + return newTokenWeightsArray; +}; + +const getDesiredWeights = (numberNewTokens: number, newTokenTargetWeight: number, numberExistingTokens: number) => { + const adjustedExistingTokenWeights = getAdjustedExistingTokenWeights( + numberNewTokens, + newTokenTargetWeight, + numberExistingTokens + ); + return [...adjustedExistingTokenWeights, ...getNewTokensWeightArray(numberNewTokens, newTokenTargetWeight)]; +}; + +const getAdjustedExistingTokenWeights = ( + numberNewTokens: number, + newTokenTargetWeight: number, + numberExistingTokens: number +) => { + let adjustedTokenWeights; + // get combined weight of existing tokens when new once would be added + const orginalPoolWeightsSum = Number((1 - numberNewTokens * newTokenTargetWeight).toFixed(3)); + // get single weight of exiting tokens after adjustment + const singleOriginalPoolWeight = Number((orginalPoolWeightsSum / numberExistingTokens).toFixed(3)); + // check if sum is still a round number due to floats division + const poolWeightSumCheck = Number((numberExistingTokens * singleOriginalPoolWeight).toFixed(3)); + // correct one of the weights if poolWeightSumCheck is different because of float devision + if (poolWeightSumCheck != orginalPoolWeightsSum) { + const singleWeightPlusDecimalCorrection = + Number((orginalPoolWeightsSum - poolWeightSumCheck).toFixed(3)) + singleOriginalPoolWeight; + adjustedTokenWeights = Array(numberExistingTokens - 1).fill(singleOriginalPoolWeight); + adjustedTokenWeights.push(singleWeightPlusDecimalCorrection); + } else { + adjustedTokenWeights = Array(numberExistingTokens).fill(singleOriginalPoolWeight); + } + return adjustedTokenWeights; +}; + describe('IndexPool', function () { let owner: SignerWithAddress, controller: SignerWithAddress, @@ -42,11 +111,12 @@ describe('IndexPool', function () { }); const MAX_TOKENS = 4; + const MAX_TOKENS_50 = 50; let allTokens: TokenList, tokens: TokenList; sharedBeforeEach('deploy tokens', async () => { - allTokens = await TokenList.create(MAX_TOKENS + 1, { sorted: true }); + allTokens = await TokenList.create(MAX_TOKENS_50 + 1, { sorted: true }); tokens = allTokens.subset(4); await tokens.mint({ to: [other], amount: fp(200) }); }); @@ -393,7 +463,7 @@ describe('IndexPool', function () { const oldTokenIndex = 0; const newTokenTargetWeight = fp(0.1); const originalWeights = [fp(0.4), fp(0.3), fp(0.3)]; - const reindexWeights = [fp(0.5), fp(0.2), fp(0.2), newTokenTargetWeight]; + const desiredWeights = [fp(0.5), fp(0.2), fp(0.2), newTokenTargetWeight]; const standardMinimumBalance = fp(0.01); const swapInAmount = fp(0.003); const initialTokenAmountsInPool = fp(1); @@ -429,7 +499,7 @@ describe('IndexPool', function () { sharedBeforeEach('call reindexTokens function', async () => { reindexTokens = allTokens.subset(numberExistingTokens + numberNewTokens).tokens.map((token) => token.address); poolId = await pool.getPoolId(); - await pool.reindexTokens(controller, reindexTokens, reindexWeights, minimumBalances); + await pool.reindexTokens(controller, reindexTokens, desiredWeights, minimumBalances); }); it('adds the new token to the vault registry', async () => { @@ -451,7 +521,7 @@ describe('IndexPool', function () { }); it('sets the correct rebalancing period', async () => { - const maxWeightDifference = calculateMaxWeightDifference(reindexWeights, [...originalWeights, fp(0)]); + const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, [...originalWeights, fp(0)]); const time = getTimeForWeightChange(maxWeightDifference); const { startTime, endTime } = await pool.getGradualWeightUpdateParams(); @@ -547,5 +617,133 @@ describe('IndexPool', function () { }); }); }); + context.only('when adding multible tokens at once', () => { + const MAX_TOKENS_TO_ADD = 15; + const numberExistingTokens = 4; + const originalWeights = [0.2, 0.2, 0.3, 0.3]; + const originalWeightsBN = originalWeights.map((w) => fp(w)); + const initialTokenAmountsInPool = fp(1); + const standardMinimumBalance = fp(0.01); + const newTokenTargetWeight = 0.05; + + let reindexTokens: string[], + poolId: string, + minimumBalances: BigNumber[], + desiredWeights: BigNumber[], + expectedEndWeights: BigNumber[], + expectedStartWeights: BigNumber[]; + + for (const numberNewTokens of range(2, MAX_TOKENS_TO_ADD)) { + context(`call reindexTokens with ${numberNewTokens} new tokens`, () => { + sharedBeforeEach('deploy pool', async () => { + vault = await Vault.create(); + const params = { + tokens: allTokens.subset(numberExistingTokens), + weights: originalWeightsBN, + owner: controller, + poolType: WeightedPoolType.INDEX_POOL, + fromFactory: true, + vault, + }; + pool = await WeightedPool.create(params); + }); + sharedBeforeEach('creating weights and balances', async () => { + minimumBalances = new Array(numberExistingTokens + numberNewTokens).fill(standardMinimumBalance); + const temp: number[] = getDesiredWeights(numberNewTokens, newTokenTargetWeight, numberExistingTokens); + desiredWeights = temp.map((w) => fp(w)); + + // const { baseWeights, fixedWeights } = setupNewTokens(numberExistingTokens, numberNewTokens); + // desiredWeights = baseWeights.map(w => fp(w)); + // const newTokenArray = getNewTokensWeightArray(numberNewTokens, 0) + const baseWeightsArray: number[] = [...originalWeights, ...getNewTokensWeightArray(numberNewTokens, 0)]; + // // const newTokenArrayFixed = getNewTokensWeightArray(numberNewTokens, 0.01); + const fixedWeightArray: number[] = [ + ...getNewTokensWeightArray(numberExistingTokens, 0), + ...getNewTokensWeightArray(numberNewTokens, 0.01), + ]; + // console.log(baseWeightsArray); + // console.log(fixedWeightArray); + + // let baseWeightsArray = new Array(numberExistingTokens + numberNewTokens); + + // expectedStartWeights = getExpectedWeights(originalWeights); + + expectedStartWeights = getExpectedWeights(baseWeightsArray, fixedWeightArray); + // const test1: number[] = [...temp, ...getNewTokensWeightArray(numberNewTokens, 0)]; + expectedEndWeights = getExpectedWeights(temp, fixedWeightArray); + }); + + sharedBeforeEach('join pool (aka fund liquidity)', async () => { + await tokens.mint({ to: owner, amount: fp(100) }); + await tokens.approve({ from: owner, to: await pool.getVault() }); + await pool.init({ + from: owner, + initialBalances: new Array(numberExistingTokens).fill(initialTokenAmountsInPool), + }); + }); + + sharedBeforeEach('call reindexTokens function', async () => { + reindexTokens = allTokens + .subset(numberExistingTokens + numberNewTokens) + .tokens.map((token) => token.address); + poolId = await pool.getPoolId(); + + await pool.reindexTokens(controller, reindexTokens, desiredWeights, minimumBalances); + }); + + it('adds the new tokens to the vault registry', async () => { + const { tokens: tokensFromVault } = await vault.getPoolTokens(poolId); + + expect(tokensFromVault).to.have.members(reindexTokens); + }); + + it(`sets the correct startWeights for all ${numberNewTokens} tokens`, async () => { + const { startWeights } = await pool.getGradualWeightUpdateParams(); + + expect(startWeights).to.equalWithError(expectedStartWeights, 0.0001); + }); + + it(`sets the correct endWeights for all ${numberNewTokens} tokens`, async () => { + const { endWeights } = await pool.getGradualWeightUpdateParams(); + + expect(endWeights).to.equalWithError(expectedEndWeights, 0.0001); + }); + + it('sets the correct rebalancing period', async () => { + const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, [...originalWeightsBN, fp(0)]); + const time = getTimeForWeightChange(maxWeightDifference); + const { startTime, endTime } = await pool.getGradualWeightUpdateParams(); + + expect(Number(endTime) - Number(startTime)).to.equalWithError(time, 0.0001); + }); + + it('sets the correct minimum balance for all the new token', async () => { + for (let i = numberExistingTokens; i < reindexTokens.length; i++) { + let minimumBalance = await pool.minBalances(reindexTokens[i]); + + expect(minimumBalance).to.equalWithError(standardMinimumBalance, 0.0001); + } + }); + + it('does not set a minimum balance for existing tokens', async () => { + for (let i = 0; i < numberExistingTokens; i++) { + let tokenBalance = await pool.minBalances(reindexTokens[i]); + + expect(tokenBalance).to.equal(0); + } + }); + + it('stores the final target weights for the new tokens', async () => { + const expectedNewTokenTargetWeights = [ + ...getNewTokensWeightArray(numberExistingTokens, 0).map((w) => fp(w)), + ...getNewTokensWeightArray(numberNewTokens, newTokenTargetWeight).map((w) => fp(w)), + ]; + const { newTokenTargetWeights } = await pool.getGradualWeightUpdateParams(); + + expect(newTokenTargetWeights).to.equalWithError(expectedNewTokenTargetWeights, 0.0001); + }); + }); + } + }); }); }); From a8297c8f2d4a4ad573872f3f335d20153625aa3a Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 09:09:26 +0100 Subject: [PATCH 05/16] removed parameter from setupNewTokens --- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 38 +++++++++---------- .../test/utils/WeightCalculationUtil.test.ts | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index da518f5ad7..500b0b11db 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -143,7 +143,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -185,7 +185,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -227,7 +227,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -269,7 +269,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -311,7 +311,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -353,7 +353,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -395,7 +395,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -437,7 +437,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -479,7 +479,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -521,7 +521,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -563,7 +563,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -605,7 +605,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -647,7 +647,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -689,7 +689,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -731,7 +731,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -773,7 +773,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -815,7 +815,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -857,7 +857,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) @@ -899,7 +899,7 @@ describe('IndexPoolUtils', function () { describe('when adding tokens', () => { beforeEach(async () => { - ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights, 2)); + ({ baseWeights, fixedWeights } = setupNewTokens(numberWeights)); receivedWeights = await indexPoolUtilsInstance.normalizeInterpolatedMock( baseWeights.map((w) => fp(w)), fixedWeights.map((w) => fp(w)) diff --git a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts index 3c42165ea2..035de43b7a 100644 --- a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts +++ b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts @@ -78,10 +78,10 @@ export const setupAdjustTokens = (numWeights: number) => { }; // generates random baseWeights and fixedWeights for the case where one or two new tokens are added -export const setupNewTokens = (numWeights: number, numNewTokens: number) => { +export const setupNewTokens = (numWeights: number) => { const baseWeights = getRandomBaseWeights(numWeights); - const numberNewTokens = getIntegerBetween(1, numNewTokens); + const numberNewTokens = getIntegerBetween(1, 2); const fixedWeights = new Array(baseWeights.length).fill(0); const initialWeight = 0.01; for (let i = 0; i < numberNewTokens; i++) { From b4634fe2f3b3fbc2192c60944505918be91daa3b Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 09:37:44 +0100 Subject: [PATCH 06/16] cleaned up IndexPoolTest --- pkg/pool-weighted/test/IndexPool.test.ts | 81 ++++++++---------------- 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 6b2d01aa04..0bd0667feb 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -12,10 +12,7 @@ import { FundManagement, SingleSwap, SwapKind } from '@balancer-labs/balancer-js import { WeightedPoolType } from '../../../pvt/helpers/src/models/pools/weighted/types'; import { calcOutGivenIn } from '@balancer-labs/v2-helpers/src/models/pools/weighted/math'; import { resourceUsage } from 'process'; -import { getExpectedWeights, setupNewTokens } from './utils/WeightCalculationUtil.test'; -// import {} - -// const HUNDRED_PERCENT = BigNumber.from(10).pow(18); +import { getExpectedWeights, getRandomBaseWeights, setupNewTokens } from './utils/WeightCalculationUtil.test'; const calculateMaxWeightDifference = (oldWeights: BigNumber[], newWeights: BigNumber[]) => { let maxWeightDifference = 0; @@ -34,32 +31,6 @@ const getTimeForWeightChange = (weightDifference: number) => { return (weightDifference / 1e18) * 86400 * 100; }; -const getExpectedStartWeights = (originalWeights: BigNumber[], numberNewTokens: number) => { - let sum; - - sum = fp(0); - const minimumBalance = 0.01; - const adjustmentAmount = fp(minimumBalance).mul(numberNewTokens).div(originalWeights.length); - - console.log('num tokens = ', numberNewTokens); - console.log('adjustment = ', adjustmentAmount.toString()); - - for (let i = 0; i < originalWeights.length; i++) { - console.log('og weigh before = ', originalWeights[i].toString()); - - originalWeights[i] = originalWeights[i].sub(adjustmentAmount); - - console.log('og weigh after = ', originalWeights[i].toString()); - - sum = sum.add(originalWeights[i]); - } - console.log('sum =', sum.toString()); - const total = sum.add(fp(minimumBalance).mul(numberNewTokens)); - console.log('total = ', total.toString()); - - return [...originalWeights, ...getNewTokensWeightArray(numberNewTokens, minimumBalance)]; -}; - const getNewTokensWeightArray = (numberNewTokens: number, newTokenTargetWeight: number) => { const newTokenWeightsArray = new Array(numberNewTokens).fill(newTokenTargetWeight); return newTokenWeightsArray; @@ -74,6 +45,15 @@ const getDesiredWeights = (numberNewTokens: number, newTokenTargetWeight: number return [...adjustedExistingTokenWeights, ...getNewTokensWeightArray(numberNewTokens, newTokenTargetWeight)]; }; +const getBaseAndFixedWeights = (originalWeights: number[], numberNewTokens: number, weight: number) => { + const baseWeights = [...originalWeights, ...getNewTokensWeightArray(numberNewTokens, 0)]; + const fixedWeights = [ + ...getNewTokensWeightArray(originalWeights.length, 0), + ...getNewTokensWeightArray(numberNewTokens, weight), + ]; + return { baseWeights, fixedWeights }; +}; + const getAdjustedExistingTokenWeights = ( numberNewTokens: number, newTokenTargetWeight: number, @@ -98,7 +78,7 @@ const getAdjustedExistingTokenWeights = ( return adjustedTokenWeights; }; -describe('IndexPool', function () { +describe.only('IndexPool', function () { let owner: SignerWithAddress, controller: SignerWithAddress, other: SignerWithAddress, @@ -617,19 +597,20 @@ describe('IndexPool', function () { }); }); }); - context.only('when adding multible tokens at once', () => { + context('when adding multible tokens at once', () => { const MAX_TOKENS_TO_ADD = 15; const numberExistingTokens = 4; - const originalWeights = [0.2, 0.2, 0.3, 0.3]; + const originalWeights = getRandomBaseWeights(numberExistingTokens); const originalWeightsBN = originalWeights.map((w) => fp(w)); const initialTokenAmountsInPool = fp(1); const standardMinimumBalance = fp(0.01); + const standardMinimumWeight = 0.01; const newTokenTargetWeight = 0.05; let reindexTokens: string[], poolId: string, minimumBalances: BigNumber[], - desiredWeights: BigNumber[], + desiredWeightsBN: BigNumber[], expectedEndWeights: BigNumber[], expectedStartWeights: BigNumber[]; @@ -649,28 +630,18 @@ describe('IndexPool', function () { }); sharedBeforeEach('creating weights and balances', async () => { minimumBalances = new Array(numberExistingTokens + numberNewTokens).fill(standardMinimumBalance); - const temp: number[] = getDesiredWeights(numberNewTokens, newTokenTargetWeight, numberExistingTokens); - desiredWeights = temp.map((w) => fp(w)); - - // const { baseWeights, fixedWeights } = setupNewTokens(numberExistingTokens, numberNewTokens); - // desiredWeights = baseWeights.map(w => fp(w)); - // const newTokenArray = getNewTokensWeightArray(numberNewTokens, 0) - const baseWeightsArray: number[] = [...originalWeights, ...getNewTokensWeightArray(numberNewTokens, 0)]; - // // const newTokenArrayFixed = getNewTokensWeightArray(numberNewTokens, 0.01); - const fixedWeightArray: number[] = [ - ...getNewTokensWeightArray(numberExistingTokens, 0), - ...getNewTokensWeightArray(numberNewTokens, 0.01), - ]; - // console.log(baseWeightsArray); - // console.log(fixedWeightArray); - // let baseWeightsArray = new Array(numberExistingTokens + numberNewTokens); + const desiredWeights = getDesiredWeights(numberNewTokens, newTokenTargetWeight, numberExistingTokens); + desiredWeightsBN = desiredWeights.map((w) => fp(w)); - // expectedStartWeights = getExpectedWeights(originalWeights); + const { baseWeights, fixedWeights } = getBaseAndFixedWeights( + originalWeights, + numberNewTokens, + standardMinimumWeight + ); - expectedStartWeights = getExpectedWeights(baseWeightsArray, fixedWeightArray); - // const test1: number[] = [...temp, ...getNewTokensWeightArray(numberNewTokens, 0)]; - expectedEndWeights = getExpectedWeights(temp, fixedWeightArray); + expectedStartWeights = getExpectedWeights(baseWeights, fixedWeights); + expectedEndWeights = getExpectedWeights(desiredWeights, fixedWeights); }); sharedBeforeEach('join pool (aka fund liquidity)', async () => { @@ -688,7 +659,7 @@ describe('IndexPool', function () { .tokens.map((token) => token.address); poolId = await pool.getPoolId(); - await pool.reindexTokens(controller, reindexTokens, desiredWeights, minimumBalances); + await pool.reindexTokens(controller, reindexTokens, desiredWeightsBN, minimumBalances); }); it('adds the new tokens to the vault registry', async () => { @@ -710,7 +681,7 @@ describe('IndexPool', function () { }); it('sets the correct rebalancing period', async () => { - const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, [...originalWeightsBN, fp(0)]); + const maxWeightDifference = calculateMaxWeightDifference(desiredWeightsBN, [...originalWeightsBN, fp(0)]); const time = getTimeForWeightChange(maxWeightDifference); const { startTime, endTime } = await pool.getGradualWeightUpdateParams(); From f37e7154be7b41aad002d727b7f927402ec3e986 Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 09:38:56 +0100 Subject: [PATCH 07/16] remove unneeded imports --- pkg/pool-weighted/test/IndexPool.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 0bd0667feb..4e6eb005b7 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -1,6 +1,6 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; -import { BigNumber, utils } from 'ethers'; +import { BigNumber } from 'ethers'; import { range } from 'lodash'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { fp, pct } from '@balancer-labs/v2-helpers/src/numbers'; @@ -11,8 +11,7 @@ import { MAX_UINT256 } from '@balancer-labs/v2-helpers/src/constants'; import { FundManagement, SingleSwap, SwapKind } from '@balancer-labs/balancer-js'; import { WeightedPoolType } from '../../../pvt/helpers/src/models/pools/weighted/types'; import { calcOutGivenIn } from '@balancer-labs/v2-helpers/src/models/pools/weighted/math'; -import { resourceUsage } from 'process'; -import { getExpectedWeights, getRandomBaseWeights, setupNewTokens } from './utils/WeightCalculationUtil.test'; +import { getExpectedWeights, getRandomBaseWeights } from './utils/WeightCalculationUtil.test'; const calculateMaxWeightDifference = (oldWeights: BigNumber[], newWeights: BigNumber[]) => { let maxWeightDifference = 0; From affe23932c50f224a32ff060aab0ad56be035126 Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 09:39:17 +0100 Subject: [PATCH 08/16] move functions back to IndexPoolUtils --- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 42 ++++++++++++++++++- .../test/utils/WeightCalculationUtil.test.ts | 38 ----------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index 500b0b11db..4a0be23f32 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -3,7 +3,7 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; import { Contract } from '@ethersproject/contracts'; import { fp } from '../../../pvt/helpers/src/numbers'; -import { getExpectedWeights, setupAdjustTokens, setupNewTokens } from './utils/WeightCalculationUtil.test'; +import { getExpectedWeights, getDecimalBetween, getRandomBaseWeights } from './utils/WeightCalculationUtil.test'; const { utils: { parseEther }, @@ -11,10 +11,48 @@ const { const HUNDRED_PERCENT = BigNumber.from(10).pow(18); +const getIntegerBetween = (min: number, max: number): number => Math.floor(Math.random() * max + min); + +// generates random baseWeights and fixedWeights for the case where the weights of existing tokens in a pool are changed +const setupAdjustTokens = (numWeights: number) => { + const baseWeights = getRandomBaseWeights(numWeights); + const numberAdjustTokens = getIntegerBetween(1, numWeights - 1); + const fixedWeights = new Array(numWeights).fill(0); + + let residual = 1; + for (let i = 0; i < numberAdjustTokens; i++) { + const newWeight = getDecimalBetween(Number.MIN_VALUE, residual / 2); + fixedWeights[i] = newWeight; + baseWeights[i] = 0; + residual -= newWeight; + } + + return { baseWeights, fixedWeights }; +}; + +// generates random baseWeights and fixedWeights for the case where one or two new tokens are added +const setupNewTokens = (numWeights: number) => { + const baseWeights = getRandomBaseWeights(numWeights); + + const numberNewTokens = getIntegerBetween(1, 2); + const fixedWeights = new Array(baseWeights.length).fill(0); + const initialWeight = 0.01; + for (let i = 0; i < numberNewTokens; i++) { + fixedWeights.push(initialWeight); + } + const numberAddedTokens = fixedWeights.length - baseWeights.length; + + for (let i = 0; i < numberAddedTokens; i++) { + baseWeights.push(0); + } + + return { baseWeights, fixedWeights }; +}; + const getTotalWeight = (weights: BigNumber[]): BigNumber => weights.reduce((acc, curr) => acc.add(curr), BigNumber.from(0)); -describe('IndexPoolUtils', function () { +describe.only('IndexPoolUtils', function () { let indexPoolUtilsInstance: Contract; beforeEach(async () => { diff --git a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts index 035de43b7a..2d19438a9e 100644 --- a/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts +++ b/pkg/pool-weighted/test/utils/WeightCalculationUtil.test.ts @@ -57,41 +57,3 @@ export const getRandomBaseWeights = (numWeights: number): number[] => { return baseWeights; }; - -export const getIntegerBetween = (min: number, max: number): number => Math.floor(Math.random() * max + min); - -// generates random baseWeights and fixedWeights for the case where the weights of existing tokens in a pool are changed -export const setupAdjustTokens = (numWeights: number) => { - const baseWeights = getRandomBaseWeights(numWeights); - const numberAdjustTokens = getIntegerBetween(1, numWeights - 1); - const fixedWeights = new Array(numWeights).fill(0); - - let residual = 1; - for (let i = 0; i < numberAdjustTokens; i++) { - const newWeight = getDecimalBetween(Number.MIN_VALUE, residual / 2); - fixedWeights[i] = newWeight; - baseWeights[i] = 0; - residual -= newWeight; - } - - return { baseWeights, fixedWeights }; -}; - -// generates random baseWeights and fixedWeights for the case where one or two new tokens are added -export const setupNewTokens = (numWeights: number) => { - const baseWeights = getRandomBaseWeights(numWeights); - - const numberNewTokens = getIntegerBetween(1, 2); - const fixedWeights = new Array(baseWeights.length).fill(0); - const initialWeight = 0.01; - for (let i = 0; i < numberNewTokens; i++) { - fixedWeights.push(initialWeight); - } - const numberAddedTokens = fixedWeights.length - baseWeights.length; - - for (let i = 0; i < numberAddedTokens; i++) { - baseWeights.push(0); - } - - return { baseWeights, fixedWeights }; -}; From 96517a2a5163ebbaf5ab1dae53f316322503da5b Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 09:56:31 +0100 Subject: [PATCH 09/16] merge master --- pkg/pool-weighted/test/IndexPool.test.ts | 8 ++++---- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 71d60f2517..820e434443 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -77,7 +77,7 @@ const getAdjustedExistingTokenWeights = ( return adjustedTokenWeights; }; -describe.only('IndexPool', function () { +describe('IndexPool', function () { let owner: SignerWithAddress, controller: SignerWithAddress, other: SignerWithAddress, @@ -431,7 +431,7 @@ describe.only('IndexPool', function () { const oldTokenIndex = 0; const newTokenTargetWeight = fp(0.1); const originalWeights = [fp(0.4), fp(0.3), fp(0.3)]; - const desiredWeights = [fp(0.5), fp(0.2), fp(0.2), newTokenTargetWeight]; + const reindexWeights = [fp(0.5), fp(0.2), fp(0.2), newTokenTargetWeight]; const standardMinimumBalance = fp(0.01); const swapInAmount = fp(0.003); const initialTokenAmountsInPool = fp(1); @@ -470,7 +470,7 @@ describe.only('IndexPool', function () { sharedBeforeEach('call reindexTokens function', async () => { reindexTokens = allTokens.subset(numberExistingTokens + numberNewTokens).tokens.map((token) => token.address); poolId = await pool.getPoolId(); - await pool.reindexTokens(controller, reindexTokens, desiredWeights, minimumBalances); + await pool.reindexTokens(controller, reindexTokens, reindexWeights, minimumBalances); }); it('adds the new token to the vault registry', async () => { @@ -492,7 +492,7 @@ describe.only('IndexPool', function () { }); it('sets the correct rebalancing period', async () => { - const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, [...originalWeights, fp(0)]); + const maxWeightDifference = calculateMaxWeightDifference(reindexWeights, [...originalWeights, fp(0)]); const time = getTimeForWeightChange(maxWeightDifference); const { startTime, endTime } = await pool.getGradualWeightUpdateParams(); diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index 32d129d647..862e74578c 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -52,7 +52,7 @@ const setupNewTokens = (numWeights: number) => { const getTotalWeight = (weights: BigNumber[]): BigNumber => weights.reduce((acc, curr) => acc.add(curr), BigNumber.from(0)); -describe.only('IndexPoolUtils', function () { +describe('IndexPoolUtils', function () { let indexPoolUtilsInstance: Contract; beforeEach(async () => { From bb2e366c12483ba4f509666ab20038eb81a09940 Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 10:05:50 +0100 Subject: [PATCH 10/16] fix linter --- pkg/pool-weighted/test/IndexPool.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 820e434443..87aec51715 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -840,7 +840,7 @@ describe('IndexPool', function () { it('sets the correct minimum balance for all the new token', async () => { for (let i = numberExistingTokens; i < reindexTokens.length; i++) { - let minimumBalance = await pool.minBalances(reindexTokens[i]); + const minimumBalance = await pool.minBalances(reindexTokens[i]); expect(minimumBalance).to.equalWithError(standardMinimumBalance, 0.0001); } @@ -848,7 +848,7 @@ describe('IndexPool', function () { it('does not set a minimum balance for existing tokens', async () => { for (let i = 0; i < numberExistingTokens; i++) { - let tokenBalance = await pool.minBalances(reindexTokens[i]); + const tokenBalance = await pool.minBalances(reindexTokens[i]); expect(tokenBalance).to.equal(0); } From c0033c46017cc45e284dfc903ae506d832ed09c2 Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 10:22:58 +0100 Subject: [PATCH 11/16] add fixed base weights --- pkg/pool-weighted/test/IndexPool.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 87aec51715..897cdfa6d5 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -750,7 +750,7 @@ describe('IndexPool', function () { context('when adding multible tokens at once', () => { const MAX_TOKENS_TO_ADD = 15; const numberExistingTokens = 4; - const originalWeights = getRandomBaseWeights(numberExistingTokens); + const originalWeights = [0.2, 0.2, 0.3, 0.3]; const originalWeightsBN = originalWeights.map((w) => fp(w)); const initialTokenAmountsInPool = fp(1); const standardMinimumBalance = fp(0.01); From 137f6a16342097ac5731a477b0a0f0c0c36f5282 Mon Sep 17 00:00:00 2001 From: Marvin Date: Tue, 16 Nov 2021 10:26:37 +0100 Subject: [PATCH 12/16] remove getRandomBaseWeights --- pkg/pool-weighted/test/IndexPool.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 897cdfa6d5..f0b359317a 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -11,7 +11,7 @@ import { MAX_UINT256 } from '@balancer-labs/v2-helpers/src/constants'; import { FundManagement, SingleSwap, SwapKind } from '@balancer-labs/balancer-js'; import { WeightedPoolType } from '../../../pvt/helpers/src/models/pools/weighted/types'; import { calcOutGivenIn } from '@balancer-labs/v2-helpers/src/models/pools/weighted/math'; -import { getExpectedWeights, getRandomBaseWeights } from './utils/WeightCalculationUtil.test'; +import { getExpectedWeights } from './utils/WeightCalculationUtil.test'; const calculateMaxWeightDifference = (oldWeights: BigNumber[], newWeights: BigNumber[]) => { let maxWeightDifference = 0; From 9a7e64d427625f032a40981e4b2066739704cb66 Mon Sep 17 00:00:00 2001 From: Marvin Date: Wed, 17 Nov 2021 11:05:51 +0100 Subject: [PATCH 13/16] added max token test --- pkg/pool-weighted/test/IndexPool.test.ts | 105 +++++++++++------- pkg/pool-weighted/test/IndexPoolUtils.test.ts | 9 +- 2 files changed, 73 insertions(+), 41 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index d2ec572730..2925ed2693 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -1,6 +1,6 @@ import { ethers } from 'hardhat'; import { expect } from 'chai'; -// import { BigNumber } from 'ethers'; +import { BigNumber } from 'ethers'; import { range } from 'lodash'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { fp, pct, fromFp } from '@balancer-labs/v2-helpers/src/numbers'; @@ -14,22 +14,22 @@ import { calcOutGivenIn } from '@balancer-labs/v2-helpers/src/models/pools/weigh import { getExpectedWeights } from './utils/WeightCalculationUtil.test'; import { WeightedPoolType } from '../../../pvt/helpers/src/models/pools/weighted/types'; -// const calculateMaxWeightDifference = (oldWeights: BigNumber[], newWeights: BigNumber[]) => { -// let maxWeightDifference = 0; -// for (let i = 0; i < newWeights.length; i++) { -// if (Math.abs(Number(newWeights[i]) - Number(oldWeights[i])) > maxWeightDifference) { -// maxWeightDifference = Math.abs(Number(newWeights[i]) - Number(oldWeights[i])); -// } -// } -// return maxWeightDifference; -// }; - -// const getTimeForWeightChange = (weightDifference: number) => { -// // 1e18 is 100%, we need to calculate on how much percent the weight changes first, -// // then we can understand how much time do we need by multiplying amount of percents o amount of seconds per day -// // (1% change in a day at max rate) -// return (weightDifference / 1e18) * 86400 * 100; -// }; +const calculateMaxWeightDifference = (oldWeights: BigNumber[], newWeights: BigNumber[]) => { + let maxWeightDifference = 0; + for (let i = 0; i < newWeights.length; i++) { + if (Math.abs(Number(newWeights[i]) - Number(oldWeights[i])) > maxWeightDifference) { + maxWeightDifference = Math.abs(Number(newWeights[i]) - Number(oldWeights[i])); + } + } + return maxWeightDifference; +}; + +const getTimeForWeightChange = (weightDifference: number) => { + // 1e18 is 100%, we need to calculate on how much percent the weight changes first, + // then we can understand how much time do we need by multiplying amount of percents o amount of seconds per day + // (1% change in a day at max rate) + return (weightDifference / 1e18) * 86400 * 100; +}; const getNewTokensWeightArray = (numberNewTokens: number, newTokenTargetWeight: number) => { const newTokenWeightsArray = new Array(numberNewTokens).fill(newTokenTargetWeight); @@ -78,6 +78,10 @@ const getAdjustedExistingTokenWeights = ( return adjustedTokenWeights; }; +const getEvenBaseweights = (numberOfTokens: number) => { + return Array(numberOfTokens).fill(fp(1 / numberOfTokens)); //WEIGHTS.slice(0, TOKEN_COUNT).map(fp); +}; + describe('IndexPool', function () { let owner: SignerWithAddress, controller: SignerWithAddress, @@ -125,6 +129,16 @@ describe('IndexPool', function () { await expect(WeightedPool.create(params2)).to.be.revertedWith('MIN_TOKENS'); }); + it('fails with > 50 tokens', async () => { + const params = { + tokens: allTokens, + weights: range(10000, 10000 + MAX_TOKENS_50), + owner: controller, + poolType: WeightedPoolType.INDEX_POOL, + }; + await expect(WeightedPool.create(params)).to.be.revertedWith('MAX_TOKENS'); + }); + it('fails with mismatched tokens/weights', async () => { const params = { tokens, @@ -137,7 +151,7 @@ describe('IndexPool', function () { }); describe('weights and scaling factors', () => { - for (const numTokens of range(3, MAX_TOKENS + 1)) { + for (const numTokens of range(3, MAX_TOKENS_50)) { context(`with ${numTokens} tokens`, () => { sharedBeforeEach('deploy pool', async () => { tokens = allTokens.subset(numTokens); @@ -145,7 +159,7 @@ describe('IndexPool', function () { pool = await WeightedPool.create({ poolType: WeightedPoolType.INDEX_POOL, tokens, - weights: weights.slice(0, numTokens), + weights: getEvenBaseweights(numTokens), }); }); @@ -169,6 +183,7 @@ describe('IndexPool', function () { context('when deployed from factory', () => { sharedBeforeEach('deploy pool', async () => { + tokens = allTokens.subset(MAX_TOKENS); const params = { tokens, weights, @@ -296,6 +311,7 @@ describe('IndexPool', function () { }); describe('#reweighTokens', () => { + let args: any, receipt: any; sharedBeforeEach('deploy pool', async () => { const params = { tokens, @@ -336,7 +352,11 @@ describe('IndexPool', function () { desiredWeights ); - const receipt = await tx.wait(); + receipt = await tx.wait(); + + args = receipt.events.filter((data: any) => { + return data.event === 'WeightChange'; + })[0].args; expectEvent.inReceiptWithError(receipt, 'WeightChange', { tokens: allTokens.subset(4).tokens.map((token) => token.address), @@ -346,12 +366,14 @@ describe('IndexPool', function () { }); }); - // it('sets the correct rebalancing period', async () => { - // const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, weights); - // const time = getTimeForWeightChange(maxWeightDifference); - // const { startTime, endTime } = await pool.getGradualWeightUpdateParams(); - // expect(Number(endTime) - Number(startTime)).to.equalWithError(time, 0.0001); - // }); + it('sets the correct rebalancing period', async () => { + const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, weights); + const time = getTimeForWeightChange(maxWeightDifference); + const startTime = args.startTime; + const endTime = args.endTime; + + expect(Number(endTime) - Number(startTime)).to.equalWithError(time, 0.0001); + }); }); }); @@ -796,7 +818,9 @@ describe('IndexPool', function () { minimumBalances: BigNumber[], desiredWeightsBN: BigNumber[], expectedEndWeights: BigNumber[], - expectedStartWeights: BigNumber[]; + expectedStartWeights: BigNumber[], + receipt: any, + args: any; for (const numberNewTokens of range(2, MAX_TOKENS_TO_ADD)) { context(`call reindexTokens with ${numberNewTokens} new tokens`, () => { @@ -843,7 +867,12 @@ describe('IndexPool', function () { .tokens.map((token) => token.address); poolId = await pool.getPoolId(); - await pool.reindexTokens(controller, reindexTokens, desiredWeightsBN, minimumBalances); + const tx = await pool.reindexTokens(controller, reindexTokens, desiredWeightsBN, minimumBalances); + + receipt = await tx.wait(); + args = receipt.events.filter((data: any) => { + return data.event === 'WeightChange'; + })[0].args; }); it('adds the new tokens to the vault registry', async () => { @@ -852,22 +881,18 @@ describe('IndexPool', function () { expect(tokensFromVault).to.have.members(reindexTokens); }); - it(`sets the correct startWeights for all ${numberNewTokens} tokens`, async () => { - const { startWeights } = await pool.getGradualWeightUpdateParams(); - - expect(startWeights).to.equalWithError(expectedStartWeights, 0.0001); - }); - - it(`sets the correct endWeights for all ${numberNewTokens} tokens`, async () => { - const { endWeights } = await pool.getGradualWeightUpdateParams(); - - expect(endWeights).to.equalWithError(expectedEndWeights, 0.0001); + it(`sets the correct startWeights and endWeight for all ${numberNewTokens} tokens`, async () => { + expectEvent.inIndirectReceiptWithError(receipt, pool.instance.interface, 'WeightChange', { + startWeights: expectedStartWeights, + endWeights: expectedEndWeights, + }); }); it('sets the correct rebalancing period', async () => { const maxWeightDifference = calculateMaxWeightDifference(desiredWeightsBN, [...originalWeightsBN, fp(0)]); const time = getTimeForWeightChange(maxWeightDifference); - const { startTime, endTime } = await pool.getGradualWeightUpdateParams(); + const startTime = args.startTime; + const endTime = args.endTime; expect(Number(endTime) - Number(startTime)).to.equalWithError(time, 0.0001); }); @@ -893,7 +918,7 @@ describe('IndexPool', function () { ...getNewTokensWeightArray(numberExistingTokens, 0).map((w) => fp(w)), ...getNewTokensWeightArray(numberNewTokens, newTokenTargetWeight).map((w) => fp(w)), ]; - const { newTokenTargetWeights } = await pool.getGradualWeightUpdateParams(); + const newTokenTargetWeights = args.finalTargetWeights; expect(newTokenTargetWeights).to.equalWithError(expectedNewTokenTargetWeights, 0.0001); }); diff --git a/pkg/pool-weighted/test/IndexPoolUtils.test.ts b/pkg/pool-weighted/test/IndexPoolUtils.test.ts index 862e74578c..7ab44cc5dc 100644 --- a/pkg/pool-weighted/test/IndexPoolUtils.test.ts +++ b/pkg/pool-weighted/test/IndexPoolUtils.test.ts @@ -56,7 +56,14 @@ describe('IndexPoolUtils', function () { let indexPoolUtilsInstance: Contract; beforeEach(async () => { - const indexPoolUtilsFactory = await ethers.getContractFactory('MockIndexPoolUtils'); + const indexPoolUtilsLibraryFactory = await ethers.getContractFactory('IndexPoolUtils'); + const indexPoolUtilsLibraryInstance = await indexPoolUtilsLibraryFactory.deploy(); + + const indexPoolUtilsFactory = await ethers.getContractFactory('MockIndexPoolUtils', { + libraries: { + IndexPoolUtils: indexPoolUtilsLibraryInstance.address, + }, + }); indexPoolUtilsInstance = await indexPoolUtilsFactory.deploy(); await indexPoolUtilsInstance.deployed(); }); From 41f9114bf7053579f3b433dea5201d9c7471c77e Mon Sep 17 00:00:00 2001 From: Marvin Date: Wed, 17 Nov 2021 12:01:35 +0100 Subject: [PATCH 14/16] add linter ignore --- pkg/pool-weighted/test/IndexPool.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 2925ed2693..fad88b3e34 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -311,6 +311,7 @@ describe('IndexPool', function () { }); describe('#reweighTokens', () => { + // eslint-disable-next-line let args: any, receipt: any; sharedBeforeEach('deploy pool', async () => { const params = { @@ -353,7 +354,7 @@ describe('IndexPool', function () { ); receipt = await tx.wait(); - + // eslint-disable-next-line args = receipt.events.filter((data: any) => { return data.event === 'WeightChange'; })[0].args; @@ -819,7 +820,9 @@ describe('IndexPool', function () { desiredWeightsBN: BigNumber[], expectedEndWeights: BigNumber[], expectedStartWeights: BigNumber[], + // eslint-disable-next-line receipt: any, + // eslint-disable-next-line args: any; for (const numberNewTokens of range(2, MAX_TOKENS_TO_ADD)) { @@ -870,6 +873,7 @@ describe('IndexPool', function () { const tx = await pool.reindexTokens(controller, reindexTokens, desiredWeightsBN, minimumBalances); receipt = await tx.wait(); + // eslint-disable-next-line args = receipt.events.filter((data: any) => { return data.event === 'WeightChange'; })[0].args; From 72f649f4bcb5fb3dde8c0832f1d70dad87832d2a Mon Sep 17 00:00:00 2001 From: Marvin Date: Fri, 19 Nov 2021 20:57:12 +0100 Subject: [PATCH 15/16] Done --- pkg/pool-weighted/test/IndexPool.test.ts | 200 ++++++++++++++--------- 1 file changed, 125 insertions(+), 75 deletions(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index fad88b3e34..3a7eb90b98 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -54,6 +54,23 @@ const getBaseAndFixedWeights = (originalWeights: number[], numberNewTokens: numb return { baseWeights, fixedWeights }; }; +// eslint-disable-next-line +const round = (num: any, places: any) => { + num = parseFloat(num); + places = places ? parseInt(places, 10) : 0; + if (places > 0) { + let length = places; + places = '1'; + for (let i = 0; i < length; i++) { + places += '0'; + places = parseInt(places, 10); + } + } else { + places = 1; + } + return Math.round((num + Number.EPSILON) * (1 * places)) / (1 * places); +}; + const getAdjustedExistingTokenWeights = ( numberNewTokens: number, newTokenTargetWeight: number, @@ -69,17 +86,23 @@ const getAdjustedExistingTokenWeights = ( // correct one of the weights if poolWeightSumCheck is different because of float devision if (poolWeightSumCheck != orginalPoolWeightsSum) { const singleWeightPlusDecimalCorrection = - Number((orginalPoolWeightsSum - poolWeightSumCheck).toFixed(3)) + singleOriginalPoolWeight; + poolWeightSumCheck == 1 + ? round(Number((singleOriginalPoolWeight - numberNewTokens * newTokenTargetWeight).toString()), '3') + : round( + (Number((orginalPoolWeightsSum - poolWeightSumCheck).toFixed(3)) + singleOriginalPoolWeight).toString(), + '3' + ); adjustedTokenWeights = Array(numberExistingTokens - 1).fill(singleOriginalPoolWeight); adjustedTokenWeights.push(singleWeightPlusDecimalCorrection); } else { adjustedTokenWeights = Array(numberExistingTokens).fill(singleOriginalPoolWeight); } + return adjustedTokenWeights; }; -const getEvenBaseweights = (numberOfTokens: number) => { - return Array(numberOfTokens).fill(fp(1 / numberOfTokens)); //WEIGHTS.slice(0, TOKEN_COUNT).map(fp); +const getEvenBaseweights = (numberOfTokens: number): number[] => { + return Array(numberOfTokens).fill(fp(1 / numberOfTokens)); }; describe('IndexPool', function () { @@ -182,24 +205,28 @@ describe('IndexPool', function () { }); context('when deployed from factory', () => { - sharedBeforeEach('deploy pool', async () => { - tokens = allTokens.subset(MAX_TOKENS); - const params = { - tokens, - weights, - owner: controller, - poolType: WeightedPoolType.INDEX_POOL, - fromFactory: true, - }; - pool = await WeightedPool.create(params); - }); + for (const numTokens of range(3, MAX_TOKENS_50)) { + context(`with ${numTokens} tokens`, () => { + sharedBeforeEach('deploy pool', async () => { + tokens = allTokens.subset(MAX_TOKENS); + const params = { + tokens, + weights, + owner: controller, + poolType: WeightedPoolType.INDEX_POOL, + fromFactory: true, + }; + pool = await WeightedPool.create(params); + }); - it('has no asset managers', async () => { - await tokens.asyncEach(async (token) => { - const { assetManager } = await pool.getTokenInfo(token); - expect(assetManager).to.be.zeroAddress; + it('has no asset managers', async () => { + await tokens.asyncEach(async (token) => { + const { assetManager } = await pool.getTokenInfo(token); + expect(assetManager).to.be.zeroAddress; + }); + }); }); - }); + } }); describe('with valid creation parameters', () => { @@ -311,70 +338,94 @@ describe('IndexPool', function () { }); describe('#reweighTokens', () => { - // eslint-disable-next-line - let args: any, receipt: any; - sharedBeforeEach('deploy pool', async () => { - const params = { - tokens, - weights, - owner, - poolType: WeightedPoolType.INDEX_POOL, - swapEnabledOnStart: false, - }; - pool = await WeightedPool.create(params); - }); + context('with invalid input', () => { + sharedBeforeEach('deploy pool', async () => { + const params = { + tokens, + weights, + owner, + poolType: WeightedPoolType.INDEX_POOL, + swapEnabledOnStart: false, + }; + pool = await WeightedPool.create(params); + }); - context('when input array lengths differ', () => { - it('reverts: "INPUT_LENGTH_MISMATCH"', async () => { - const threeAddresses = allTokens.subset(3).tokens.map((token) => token.address); - const twoWeights = [fp(0.5), fp(0.5)]; - await expect(pool.reweighTokens(controller, threeAddresses, twoWeights)).to.be.revertedWith( - 'INPUT_LENGTH_MISMATCH' - ); + context('when input array lengths differ', () => { + it('reverts: "INPUT_LENGTH_MISMATCH"', async () => { + const threeAddresses = allTokens.subset(3).tokens.map((token) => token.address); + const twoWeights = [fp(0.5), fp(0.5)]; + await expect(pool.reweighTokens(controller, threeAddresses, twoWeights)).to.be.revertedWith( + 'INPUT_LENGTH_MISMATCH' + ); + }); }); - }); - context('when weights are not normalized', () => { - it('reverts: "INPUT_LENGTH_MISMATCH"', async () => { - const addresses = allTokens.subset(2).tokens.map((token) => token.address); - const denormalizedWeights = [fp(0.5), fp(0.3)]; - await expect(pool.reweighTokens(controller, addresses, denormalizedWeights)).to.be.revertedWith( - 'NORMALIZED_WEIGHT_INVARIANT' - ); + context('when weights are not normalized', () => { + it('reverts: "INPUT_LENGTH_MISMATCH"', async () => { + const addresses = allTokens.subset(2).tokens.map((token) => token.address); + const denormalizedWeights = [fp(0.5), fp(0.3)]; + await expect(pool.reweighTokens(controller, addresses, denormalizedWeights)).to.be.revertedWith( + 'NORMALIZED_WEIGHT_INVARIANT' + ); + }); }); }); context('with valid inputs', () => { - const desiredWeights = [fp(0.1), fp(0.3), fp(0.5), fp(0.1)]; + // eslint-disable-next-line + let args: any, receipt: any, subTokenList: TokenList, subWeights: number[], adjustedWeights: BigNumber[]; + for (const numTokens of range(4, MAX_TOKENS_50)) { + context(`with ${numTokens} tokens`, () => { + sharedBeforeEach('deploy pool', async () => { + subTokenList = allTokens.subset(numTokens); + subWeights = getEvenBaseweights(numTokens); + const params = { + tokens: subTokenList, + weights: subWeights.map((w) => fp(w)), + owner, + poolType: WeightedPoolType.INDEX_POOL, + swapEnabledOnStart: false, + }; + pool = await WeightedPool.create(params); + }); - it('emits an event that contains the weight state change params', async () => { - const tx = await pool.reweighTokens( - controller, - allTokens.subset(4).tokens.map((token) => token.address), - desiredWeights - ); + sharedBeforeEach('adjust weights for reweightToken function', async () => { + adjustedWeights = getDesiredWeights(2, 0.02, numTokens - 2).map((w) => fp(w)); + }); - receipt = await tx.wait(); - // eslint-disable-next-line - args = receipt.events.filter((data: any) => { - return data.event === 'WeightChange'; - })[0].args; + it('emits an event that contains the weight state change params', async () => { + const tx = await pool.reweighTokens( + controller, + subTokenList.map((token) => token.address), + adjustedWeights + ); - expectEvent.inReceiptWithError(receipt, 'WeightChange', { - tokens: allTokens.subset(4).tokens.map((token) => token.address), - startWeights: weights, - endWeights: desiredWeights, - finalTargetWeights: [fp(0), fp(0), fp(0), fp(0)], - }); - }); + receipt = await tx.wait(); + // eslint-disable-next-line + args = receipt.events.filter((data: any) => { + return data.event === 'WeightChange'; + })[0].args; - it('sets the correct rebalancing period', async () => { - const maxWeightDifference = calculateMaxWeightDifference(desiredWeights, weights); - const time = getTimeForWeightChange(maxWeightDifference); - const startTime = args.startTime; - const endTime = args.endTime; + expectEvent.inReceiptWithError(receipt, 'WeightChange', { + tokens: subTokenList.map((token) => token.address), + startWeights: subWeights, + endWeights: adjustedWeights, + finalTargetWeights: getNewTokensWeightArray(numTokens, 0).map((w) => fp(w)), + }); + }); - expect(Number(endTime) - Number(startTime)).to.equalWithError(time, 0.0001); - }); + // it('sets the correct rebalancing period', async () => { + // const maxWeightDifference = calculateMaxWeightDifference( + // adjustedWeights, + // subWeights.map((w) => fp(w)) + // ); + // const time = getTimeForWeightChange(maxWeightDifference); + // const startTime = args.startTime; + // const endTime = args.endTime; + + // expect(Number(endTime) - Number(startTime)).to.equalWithError(time, 0.001); + // }); + }); + } }); }); @@ -805,14 +856,13 @@ describe('IndexPool', function () { }); }); context('when adding multible tokens at once', () => { - const MAX_TOKENS_TO_ADD = 15; const numberExistingTokens = 4; const originalWeights = [0.2, 0.2, 0.3, 0.3]; const originalWeightsBN = originalWeights.map((w) => fp(w)); const initialTokenAmountsInPool = fp(1); const standardMinimumBalance = fp(0.01); const standardMinimumWeight = 0.01; - const newTokenTargetWeight = 0.05; + const newTokenTargetWeight = 0.01; let reindexTokens: string[], poolId: string, @@ -825,7 +875,7 @@ describe('IndexPool', function () { // eslint-disable-next-line args: any; - for (const numberNewTokens of range(2, MAX_TOKENS_TO_ADD)) { + for (const numberNewTokens of range(24, MAX_TOKENS_50 - numberExistingTokens)) { context(`call reindexTokens with ${numberNewTokens} new tokens`, () => { sharedBeforeEach('deploy pool', async () => { vault = await Vault.create(); From 118b9bc92d03e78d05458f0f39f3a8372189da18 Mon Sep 17 00:00:00 2001 From: Marvin Date: Fri, 19 Nov 2021 20:59:23 +0100 Subject: [PATCH 16/16] fix linter --- pkg/pool-weighted/test/IndexPool.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pool-weighted/test/IndexPool.test.ts b/pkg/pool-weighted/test/IndexPool.test.ts index 3a7eb90b98..0eedf01d8f 100644 --- a/pkg/pool-weighted/test/IndexPool.test.ts +++ b/pkg/pool-weighted/test/IndexPool.test.ts @@ -59,7 +59,7 @@ const round = (num: any, places: any) => { num = parseFloat(num); places = places ? parseInt(places, 10) : 0; if (places > 0) { - let length = places; + const length = places; places = '1'; for (let i = 0; i < length; i++) { places += '0';