From 6ff29fed560cc83bee9550285deec6a212e53697 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 1 Dec 2022 11:40:14 -0500 Subject: [PATCH 01/20] refactor: remove the deployer that is one big special case --- .../pools/weighted/WeightedPoolDeployer.ts | 318 ------------------ 1 file changed, 318 deletions(-) delete mode 100644 pvt/helpers/src/models/pools/weighted/WeightedPoolDeployer.ts diff --git a/pvt/helpers/src/models/pools/weighted/WeightedPoolDeployer.ts b/pvt/helpers/src/models/pools/weighted/WeightedPoolDeployer.ts deleted file mode 100644 index f475e6481f..0000000000 --- a/pvt/helpers/src/models/pools/weighted/WeightedPoolDeployer.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { Contract } from 'ethers'; - -import * as expectEvent from '../../../test/expectEvent'; -import { deploy, deployedAt } from '../../../contract'; - -import Vault from '../../vault/Vault'; -import WeightedPool from './WeightedPool'; -import VaultDeployer from '../../vault/VaultDeployer'; -import TypesConverter from '../../types/TypesConverter'; -import { - BasePoolRights, - ManagedPoolParams, - ManagedPoolRights, - RawWeightedPoolDeployment, - WeightedPoolDeployment, - WeightedPoolType, -} from './types'; -import { ZERO_ADDRESS } from '@balancer-labs/v2-helpers/src/constants'; -import { DAY } from '@balancer-labs/v2-helpers/src/time'; -import { ProtocolFee } from '../../vault/types'; - -const NAME = 'Balancer Pool Token'; -const SYMBOL = 'BPT'; - -export default { - async deploy(params: RawWeightedPoolDeployment): Promise { - const deployment = TypesConverter.toWeightedPoolDeployment(params); - const vault = params?.vault ?? (await VaultDeployer.deploy(TypesConverter.toRawVaultDeployment(params))); - const pool = await (params.fromFactory ? this._deployFromFactory : this._deployStandalone)(deployment, vault); - const poolId = await pool.getPoolId(); - - const { - tokens, - weights, - rateProviders, - assetManagers, - swapFeePercentage, - poolType, - swapEnabledOnStart, - mustAllowlistLPs, - managementAumFeePercentage, - aumProtocolFeesCollector, - } = deployment; - - return new WeightedPool( - pool, - poolId, - vault, - tokens, - weights, - TypesConverter.toAddresses(rateProviders), - assetManagers, - swapFeePercentage, - poolType, - swapEnabledOnStart, - mustAllowlistLPs, - managementAumFeePercentage, - aumProtocolFeesCollector - ); - }, - - async _deployStandalone(params: WeightedPoolDeployment, vault: Vault): Promise { - const { - tokens, - weights, - rateProviders, - assetManagers, - swapFeePercentage, - pauseWindowDuration, - bufferPeriodDuration, - poolType, - swapEnabledOnStart, - mustAllowlistLPs, - managementAumFeePercentage, - aumProtocolFeesCollector, - owner, - from, - aumFeeId, - mockContractName, - } = params; - - let result: Promise; - - switch (poolType) { - case WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL: { - result = deploy('v2-pool-weighted/LiquidityBootstrappingPool', { - args: [ - vault.address, - NAME, - SYMBOL, - tokens.addresses, - weights, - swapFeePercentage, - pauseWindowDuration, - bufferPeriodDuration, - TypesConverter.toAddress(owner), - swapEnabledOnStart, - ], - from, - }); - break; - } - case WeightedPoolType.MANAGED_POOL: { - const addRemoveTokenLib = await deploy('v2-pool-weighted/ManagedPoolAddRemoveTokenLib'); - const math = await deploy('v2-pool-weighted/ExternalWeightedMath'); - const circuitBreakerLib = await deploy('v2-pool-weighted/CircuitBreakerLib'); - result = deploy('v2-pool-weighted/ManagedPool', { - args: [ - { - name: NAME, - symbol: SYMBOL, - tokens: tokens.addresses, - normalizedWeights: weights, - swapFeePercentage: swapFeePercentage, - assetManagers: assetManagers, - swapEnabledOnStart: swapEnabledOnStart, - mustAllowlistLPs: mustAllowlistLPs, - managementAumFeePercentage: managementAumFeePercentage, - aumProtocolFeesCollector: aumProtocolFeesCollector, - aumFeeId: aumFeeId, - }, - vault.address, - vault.protocolFeesProvider.address, - math.address, - owner, - pauseWindowDuration, - bufferPeriodDuration, - ], - from, - libraries: { - CircuitBreakerLib: circuitBreakerLib.address, - ManagedPoolAddRemoveTokenLib: addRemoveTokenLib.address, - }, - }); - break; - } - case WeightedPoolType.MOCK_MANAGED_POOL: { - if (mockContractName == undefined) { - throw new Error('Mock contract name required to deploy mock base pool'); - } - const addRemoveTokenLib = await deploy('v2-pool-weighted/ManagedPoolAddRemoveTokenLib'); - - const math = await deploy('v2-pool-weighted/ExternalWeightedMath'); - const circuitBreakerLib = await deploy('v2-pool-weighted/CircuitBreakerLib'); - result = deploy(mockContractName, { - args: [ - { - name: NAME, - symbol: SYMBOL, - tokens: tokens.addresses, - normalizedWeights: weights, - swapFeePercentage: swapFeePercentage, - assetManagers: assetManagers, - swapEnabledOnStart: swapEnabledOnStart, - mustAllowlistLPs: mustAllowlistLPs, - managementAumFeePercentage: managementAumFeePercentage, - aumProtocolFeesCollector: aumProtocolFeesCollector, - aumFeeId: aumFeeId, - }, - vault.address, - vault.protocolFeesProvider.address, - math.address, - owner, - pauseWindowDuration, - bufferPeriodDuration, - ], - from, - libraries: { - CircuitBreakerLib: circuitBreakerLib.address, - ManagedPoolAddRemoveTokenLib: addRemoveTokenLib.address, - }, - }); - break; - } - default: { - result = deploy('v2-pool-weighted/WeightedPool', { - args: [ - { - name: NAME, - symbol: SYMBOL, - tokens: tokens.addresses, - normalizedWeights: weights, - rateProviders: rateProviders, - assetManagers: assetManagers, - swapFeePercentage: swapFeePercentage, - }, - vault.address, - vault.protocolFeesProvider.address, - pauseWindowDuration, - bufferPeriodDuration, - owner, - ], - from, - }); - } - } - - return result; - }, - - async _deployFromFactory(params: WeightedPoolDeployment, vault: Vault): Promise { - // Note that we only support asset managers with the standalone deploy method. - const { - tokens, - weights, - rateProviders, - assetManagers, - swapFeePercentage, - swapEnabledOnStart, - mustAllowlistLPs, - managementAumFeePercentage, - poolType, - owner, - from, - aumFeeId, - } = params; - - let result: Promise; - - switch (poolType) { - case WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL: { - const factory = await deploy('v2-pool-weighted/LiquidityBootstrappingPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], - from, - }); - const tx = await factory.create( - NAME, - SYMBOL, - tokens.addresses, - weights, - swapFeePercentage, - owner, - swapEnabledOnStart - ); - const receipt = await tx.wait(); - const event = expectEvent.inReceipt(receipt, 'PoolCreated'); - result = deployedAt('v2-pool-weighted/LiquidityBootstrappingPool', event.args.pool); - break; - } - case WeightedPoolType.MANAGED_POOL: { - const addRemoveTokenLib = await deploy('v2-pool-weighted/ManagedPoolAddRemoveTokenLib'); - const circuitBreakerLib = await deploy('v2-pool-weighted/CircuitBreakerLib'); - const factory = await deploy('v2-pool-weighted/ManagedPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], - from, - libraries: { - CircuitBreakerLib: circuitBreakerLib.address, - ManagedPoolAddRemoveTokenLib: addRemoveTokenLib.address, - }, - }); - - const controlledFactory = await deploy('v2-pool-weighted/ControlledManagedPoolFactory', { - args: [factory.address], - from, - }); - - const newPoolParams: ManagedPoolParams = { - name: NAME, - symbol: SYMBOL, - tokens: tokens.addresses, - normalizedWeights: weights, - assetManagers, - swapFeePercentage: swapFeePercentage, - swapEnabledOnStart: swapEnabledOnStart, - mustAllowlistLPs: mustAllowlistLPs, - managementAumFeePercentage: managementAumFeePercentage, - aumFeeId: aumFeeId ?? ProtocolFee.AUM, - }; - - const basePoolRights: BasePoolRights = { - canTransferOwnership: true, - canChangeSwapFee: true, - canUpdateMetadata: true, - }; - - const managedPoolRights: ManagedPoolRights = { - canChangeWeights: true, - canDisableSwaps: true, - canSetMustAllowlistLPs: true, - canSetCircuitBreakers: true, - canChangeTokens: true, - canChangeMgmtFees: true, - }; - - const tx = await controlledFactory - .connect(from || ZERO_ADDRESS) - .create(newPoolParams, basePoolRights, managedPoolRights, DAY, from?.address || ZERO_ADDRESS); - const receipt = await tx.wait(); - const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); - result = deployedAt('v2-pool-weighted/ManagedPool', event.args.pool); - break; - } - case WeightedPoolType.MOCK_MANAGED_POOL: { - throw new Error('Mock type not supported to deploy from factory'); - } - default: { - const factory = await deploy('v2-pool-weighted/WeightedPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], - from, - }); - const tx = await factory.create( - NAME, - SYMBOL, - tokens.addresses, - weights, - rateProviders, - swapFeePercentage, - owner - ); - const receipt = await tx.wait(); - const event = expectEvent.inReceipt(receipt, 'PoolCreated'); - result = deployedAt('v2-pool-weighted/WeightedPool', event.args.pool); - } - } - - return result; - }, -}; From 76ce1fe234aa2e2f568bc30408c027f52d947526 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 1 Dec 2022 11:41:30 -0500 Subject: [PATCH 02/20] refactor: generalize BasePool and introduce BaseWeighted for shared weighted functions --- pvt/helpers/src/models/pools/base/BasePool.ts | 7 +- .../models/pools/weighted/BaseWeightedPool.ts | 552 ++++++++++++++++++ 2 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts diff --git a/pvt/helpers/src/models/pools/base/BasePool.ts b/pvt/helpers/src/models/pools/base/BasePool.ts index 7dd8fb4959..f77d4d9cae 100644 --- a/pvt/helpers/src/models/pools/base/BasePool.ts +++ b/pvt/helpers/src/models/pools/base/BasePool.ts @@ -14,13 +14,16 @@ import Vault from '../../vault/Vault'; import { RecoveryModeExitParams, ExitResult, JoinExitBasePool, FailureMode } from './types'; +export const NAME = 'Balancer Pool Token'; +export const SYMBOL = 'BPT'; + export default class BasePool { instance: Contract; poolId: string; tokens: TokenList; swapFeePercentage: BigNumberish; vault: Vault; - owner?: SignerWithAddress; + owner?: Account; constructor( instance: Contract, @@ -28,7 +31,7 @@ export default class BasePool { vault: Vault, tokens: TokenList, swapFeePercentage: BigNumberish, - owner?: SignerWithAddress + owner?: Account ) { this.instance = instance; this.poolId = poolId; diff --git a/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts b/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts new file mode 100644 index 0000000000..2552fe15df --- /dev/null +++ b/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts @@ -0,0 +1,552 @@ +import { BigNumber, Contract, ContractFunction, ContractReceipt, ContractTransaction } from 'ethers'; +import { BigNumberish, bn, fp, fpMul } from '../../../numbers'; +import { MAX_UINT256, ZERO_ADDRESS } from '../../../constants'; +import * as expectEvent from '../../../test/expectEvent'; +import Vault from '../../vault/Vault'; +import Token from '../../tokens/Token'; +import TokenList from '../../tokens/TokenList'; +import TypesConverter from '../../types/TypesConverter'; +import { MinimalSwap } from '../../vault/types'; +import { + JoinExitWeightedPool, + InitWeightedPool, + JoinGivenInWeightedPool, + JoinGivenOutWeightedPool, + JoinAllGivenOutWeightedPool, + JoinResult, + ExitResult, + SwapResult, + SingleExitGivenInWeightedPool, + MultiExitGivenInWeightedPool, + ExitGivenOutWeightedPool, + SwapWeightedPool, + ExitQueryResult, + JoinQueryResult, + PoolQueryResult, + GradualWeightUpdateParams, +} from './types'; +import { + calculateInvariant, + calcBptOutGivenExactTokensIn, + calcTokenInGivenExactBptOut, + calcTokenOutGivenExactBptIn, + calcOutGivenIn, + calculateOneTokenSwapFeeAmount, + calcInGivenOut, + calculateMaxOneTokenSwapFeeAmount, + calculateSpotPrice, + calculateBPTPrice, +} from './math'; + +import { SwapKind, WeightedPoolEncoder } from '@balancer-labs/balancer-js'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import BasePool from '../base/BasePool'; +import { Account } from '../../types/types'; + +const MAX_IN_RATIO = fp(0.3); +const MAX_OUT_RATIO = fp(0.3); +const MAX_INVARIANT_RATIO = fp(3); +const MIN_INVARIANT_RATIO = fp(0.7); + +export default class BaseWeightedPool extends BasePool { + weights: BigNumberish[]; + + constructor( + instance: Contract, + poolId: string, + vault: Vault, + tokens: TokenList, + weights: BigNumberish[], + swapFeePercentage: BigNumberish, + owner?: Account + ) { + super(instance, poolId, vault, tokens, swapFeePercentage, owner); + + this.weights = weights; + } + + get maxWeight(): BigNumberish { + return this.weights.reduce((max, weight) => (bn(weight).gt(max) ? weight : max), bn(0)); + } + + get normalizedWeights(): BigNumberish[] { + return this.weights; + } + + get maxWeightIndex(): BigNumberish { + const maxIdx = this.weights.indexOf(this.maxWeight); + return bn(maxIdx); + } + + async getLastPostJoinExitInvariant(): Promise { + return this.instance.getLastPostJoinExitInvariant(); + } + + async getMaxInvariantDecrease(): Promise { + const supply = await this.totalSupply(); + return supply.sub(fpMul(MIN_INVARIANT_RATIO, supply)); + } + + async getMaxInvariantIncrease(): Promise { + const supply = await this.totalSupply(); + return fpMul(MAX_INVARIANT_RATIO, supply).sub(supply); + } + + async getMaxIn(tokenIndex: number, currentBalances?: BigNumber[]): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + return fpMul(currentBalances[tokenIndex], MAX_IN_RATIO); + } + + async getMaxOut(tokenIndex: number, currentBalances?: BigNumber[]): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + return fpMul(currentBalances[tokenIndex], MAX_OUT_RATIO); + } + + async getNormalizedWeights(): Promise { + return this.instance.getNormalizedWeights(); + } + + async estimateSpotPrice(currentBalances?: BigNumberish[]): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + + const scalingFactors = await this.getScalingFactors(); + return calculateSpotPrice( + currentBalances.map((x, i) => fpMul(x, scalingFactors[i])), + this.weights + ); + } + + async estimateBptPrice( + tokenIndex: number, + currentBalance?: BigNumberish, + currentSupply?: BigNumberish + ): Promise { + if (!currentBalance) currentBalance = (await this.getBalances())[tokenIndex]; + if (!currentSupply) currentSupply = await this.totalSupply(); + + const scalingFactors = await this.getScalingFactors(); + + return calculateBPTPrice( + fpMul(currentBalance, scalingFactors[tokenIndex]), + this.weights[tokenIndex], + currentSupply + ); + } + + async estimateInvariant(currentBalances?: BigNumberish[]): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + const scalingFactors = await this.getScalingFactors(); + + return calculateInvariant( + currentBalances.map((x, i) => fpMul(x, scalingFactors[i])), + this.weights + ); + } + + async estimateSwapFeeAmount( + paidToken: number | Token, + protocolFeePercentage: BigNumberish, + currentBalances?: BigNumberish[] + ): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + const lastInvariant = await this.estimateInvariant(); + const paidTokenIndex = this.tokens.indexOf(paidToken); + const feeAmount = calculateOneTokenSwapFeeAmount(currentBalances, this.weights, lastInvariant, paidTokenIndex); + return fpMul(bn(feeAmount), protocolFeePercentage); + } + + async estimateMaxSwapFeeAmount( + paidToken: number | Token, + protocolFeePercentage: BigNumberish, + currentBalances?: BigNumberish[] + ): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + const paidTokenIndex = this.tokens.indexOf(paidToken); + const feeAmount = calculateMaxOneTokenSwapFeeAmount( + currentBalances, + this.weights, + MIN_INVARIANT_RATIO, + paidTokenIndex + ); + return fpMul(bn(feeAmount), protocolFeePercentage); + } + + async estimateGivenIn(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); + + return bn( + calcOutGivenIn( + currentBalances[tokenIn], + this.weights[tokenIn], + currentBalances[tokenOut], + this.weights[tokenOut], + params.amount + ) + ); + } + + async estimateGivenOut(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { + if (!currentBalances) currentBalances = await this.getBalances(); + const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); + + return bn( + calcInGivenOut( + currentBalances[tokenIn], + this.weights[tokenIn], + currentBalances[tokenOut], + this.weights[tokenOut], + params.amount + ) + ); + } + + async estimateBptOut( + amountsIn: BigNumberish[], + currentBalances?: BigNumberish[], + supply?: BigNumberish + ): Promise { + if (!supply) supply = await this.totalSupply(); + if (!currentBalances) currentBalances = await this.getBalances(); + return calcBptOutGivenExactTokensIn(currentBalances, this.weights, amountsIn, supply, this.swapFeePercentage); + } + + async estimateTokenIn( + token: number | Token, + bptOut: BigNumberish, + currentBalances?: BigNumberish[], + supply?: BigNumberish + ): Promise { + if (!supply) supply = await this.totalSupply(); + if (!currentBalances) currentBalances = await this.getBalances(); + const tokenIndex = this.tokens.indexOf(token); + return calcTokenInGivenExactBptOut( + tokenIndex, + currentBalances, + this.weights, + bptOut, + supply, + this.swapFeePercentage + ); + } + + async estimateTokenOut( + token: number | Token, + bptIn: BigNumberish, + currentBalances?: BigNumberish[], + supply?: BigNumberish + ): Promise { + if (!supply) supply = await this.totalSupply(); + if (!currentBalances) currentBalances = await this.getBalances(); + const tokenIndex = this.tokens.indexOf(token); + return calcTokenOutGivenExactBptIn( + tokenIndex, + currentBalances, + this.weights, + bptIn, + supply, + this.swapFeePercentage + ); + } + + async swapGivenIn(params: SwapWeightedPool): Promise { + return this.swap(await this._buildSwapParams(SwapKind.GivenIn, params)); + } + + async swapGivenOut(params: SwapWeightedPool): Promise { + return this.swap(await this._buildSwapParams(SwapKind.GivenOut, params)); + } + + async updateProtocolFeePercentageCache(): Promise { + return this.instance.updateProtocolFeePercentageCache(); + } + + async swap(params: MinimalSwap): Promise { + let receipt: ContractReceipt; + if (this.vault.mocked) { + const tx = await this.vault.minimalSwap(params); + receipt = await tx.wait(); + } else { + if (!params.from) throw new Error('No signer provided'); + const tx = await this.vault.instance.connect(params.from).swap( + { + poolId: params.poolId, + kind: params.kind, + assetIn: params.tokenIn, + assetOut: params.tokenOut, + amount: params.amount, + userData: params.data, + }, + { + sender: TypesConverter.toAddress(params.from), + recipient: TypesConverter.toAddress(params.to) ?? ZERO_ADDRESS, + fromInternalBalance: false, + toInternalBalance: false, + }, + params.kind == 0 ? 0 : MAX_UINT256, + MAX_UINT256 + ); + receipt = await tx.wait(); + } + const { amountIn, amountOut } = expectEvent.inReceipt(receipt, 'Swap').args; + const amount = params.kind == SwapKind.GivenIn ? amountOut : amountIn; + + return { amount, receipt }; + } + + async init(params: InitWeightedPool): Promise { + return this.join(this._buildInitParams(params)); + } + + async joinGivenIn(params: JoinGivenInWeightedPool): Promise { + return this.join(this._buildJoinGivenInParams(params)); + } + + async queryJoinGivenIn(params: JoinGivenInWeightedPool): Promise { + return this.queryJoin(this._buildJoinGivenInParams(params)); + } + + async joinGivenOut(params: JoinGivenOutWeightedPool): Promise { + return this.join(this._buildJoinGivenOutParams(params)); + } + + async queryJoinGivenOut(params: JoinGivenOutWeightedPool): Promise { + return this.queryJoin(this._buildJoinGivenOutParams(params)); + } + + async joinAllGivenOut(params: JoinAllGivenOutWeightedPool): Promise { + return this.join(this._buildJoinAllGivenOutParams(params)); + } + + async queryJoinAllGivenOut(params: JoinAllGivenOutWeightedPool): Promise { + return this.queryJoin(this._buildJoinAllGivenOutParams(params)); + } + + async exitGivenOut(params: ExitGivenOutWeightedPool): Promise { + return this.exit(this._buildExitGivenOutParams(params)); + } + + async queryExitGivenOut(params: ExitGivenOutWeightedPool): Promise { + return this.queryExit(this._buildExitGivenOutParams(params)); + } + + async singleExitGivenIn(params: SingleExitGivenInWeightedPool): Promise { + return this.exit(this._buildSingleExitGivenInParams(params)); + } + + async querySingleExitGivenIn(params: SingleExitGivenInWeightedPool): Promise { + return this.queryExit(this._buildSingleExitGivenInParams(params)); + } + + async multiExitGivenIn(params: MultiExitGivenInWeightedPool): Promise { + return this.exit(this._buildMultiExitGivenInParams(params)); + } + + async queryMultiExitGivenIn(params: MultiExitGivenInWeightedPool): Promise { + return this.queryExit(this._buildMultiExitGivenInParams(params)); + } + + async queryJoin(params: JoinExitWeightedPool): Promise { + const fn = this.instance.queryJoin; + return (await this._executeQuery(params, fn)) as JoinQueryResult; + } + + async join(params: JoinExitWeightedPool): Promise { + const currentBalances = params.currentBalances || (await this.getBalances()); + const to = params.recipient ? TypesConverter.toAddress(params.recipient) : params.from?.address ?? ZERO_ADDRESS; + const { tokens } = await this.getTokens(); + + const tx = await this.vault.joinPool({ + poolAddress: this.address, + poolId: this.poolId, + recipient: to, + currentBalances, + tokens, + lastChangeBlock: params.lastChangeBlock ?? 0, + protocolFeePercentage: params.protocolFeePercentage ?? 0, + data: params.data ?? '0x', + from: params.from, + }); + + const receipt = await tx.wait(); + const { deltas, protocolFeeAmounts } = expectEvent.inReceipt(receipt, 'PoolBalanceChanged').args; + return { amountsIn: deltas, dueProtocolFeeAmounts: protocolFeeAmounts, receipt }; + } + + async queryExit(params: JoinExitWeightedPool): Promise { + const fn = this.instance.queryExit; + return (await this._executeQuery(params, fn)) as ExitQueryResult; + } + + async exit(params: JoinExitWeightedPool): Promise { + const currentBalances = params.currentBalances || (await this.getBalances()); + const to = params.recipient ? TypesConverter.toAddress(params.recipient) : params.from?.address ?? ZERO_ADDRESS; + const { tokens } = await this.getTokens(); + + const tx = await this.vault.exitPool({ + poolAddress: this.address, + poolId: this.poolId, + recipient: to, + currentBalances, + tokens, + lastChangeBlock: params.lastChangeBlock ?? 0, + protocolFeePercentage: params.protocolFeePercentage ?? 0, + data: params.data ?? '0x', + from: params.from, + }); + + const receipt = await tx.wait(); + const { deltas, protocolFeeAmounts } = expectEvent.inReceipt(receipt, 'PoolBalanceChanged').args; + return { amountsOut: deltas.map((x: BigNumber) => x.mul(-1)), dueProtocolFeeAmounts: protocolFeeAmounts, receipt }; + } + + private async _executeQuery(params: JoinExitWeightedPool, fn: ContractFunction): Promise { + const currentBalances = params.currentBalances || (await this.getBalances()); + const to = params.recipient ? TypesConverter.toAddress(params.recipient) : params.from?.address ?? ZERO_ADDRESS; + + return fn( + this.poolId, + params.from?.address || ZERO_ADDRESS, + to, + currentBalances, + params.lastChangeBlock ?? 0, + params.protocolFeePercentage ?? 0, + params.data ?? '0x' + ); + } + + private async _buildSwapParams(kind: number, params: SwapWeightedPool): Promise { + const currentBalances = await this.getBalances(); + const { tokens } = await this.vault.getPoolTokens(this.poolId); + const tokenIn = typeof params.in === 'number' ? tokens[params.in] : params.in.address; + const tokenOut = typeof params.out === 'number' ? tokens[params.out] : params.out.address; + return { + kind, + poolAddress: this.address, + poolId: this.poolId, + from: params.from, + to: params.recipient ?? ZERO_ADDRESS, + tokenIn: tokenIn ?? ZERO_ADDRESS, + tokenOut: tokenOut ?? ZERO_ADDRESS, + balanceTokenIn: currentBalances[tokens.indexOf(tokenIn)] || bn(0), + balanceTokenOut: currentBalances[tokens.indexOf(tokenOut)] || bn(0), + lastChangeBlock: params.lastChangeBlock ?? 0, + data: params.data ?? '0x', + amount: params.amount, + }; + } + + private _buildInitParams(params: InitWeightedPool): JoinExitWeightedPool { + const { initialBalances: balances } = params; + const amountsIn = Array.isArray(balances) ? balances : Array(this.tokens.length).fill(balances); + + return { + from: params.from, + recipient: params.recipient, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.joinInit(amountsIn), + }; + } + + private _buildJoinGivenInParams(params: JoinGivenInWeightedPool): JoinExitWeightedPool { + const { amountsIn: amounts } = params; + const amountsIn = Array.isArray(amounts) ? amounts : Array(this.tokens.length).fill(amounts); + + return { + from: params.from, + recipient: params.recipient, + lastChangeBlock: params.lastChangeBlock, + currentBalances: params.currentBalances, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.joinExactTokensInForBPTOut(amountsIn, params.minimumBptOut ?? 0), + }; + } + + private _buildJoinGivenOutParams(params: JoinGivenOutWeightedPool): JoinExitWeightedPool { + return { + from: params.from, + recipient: params.recipient, + lastChangeBlock: params.lastChangeBlock, + currentBalances: params.currentBalances, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.joinTokenInForExactBPTOut(params.bptOut, this.tokens.indexOf(params.token)), + }; + } + + private _buildJoinAllGivenOutParams(params: JoinAllGivenOutWeightedPool): JoinExitWeightedPool { + return { + from: params.from, + recipient: params.recipient, + lastChangeBlock: params.lastChangeBlock, + currentBalances: params.currentBalances, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.joinAllTokensInForExactBPTOut(params.bptOut), + }; + } + + private _buildExitGivenOutParams(params: ExitGivenOutWeightedPool): JoinExitWeightedPool { + const { amountsOut: amounts } = params; + const amountsOut = Array.isArray(amounts) ? amounts : Array(this.tokens.length).fill(amounts); + return { + from: params.from, + recipient: params.recipient, + lastChangeBlock: params.lastChangeBlock, + currentBalances: params.currentBalances, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.exitBPTInForExactTokensOut(amountsOut, params.maximumBptIn ?? MAX_UINT256), + }; + } + + private _buildSingleExitGivenInParams(params: SingleExitGivenInWeightedPool): JoinExitWeightedPool { + return { + from: params.from, + recipient: params.recipient, + lastChangeBlock: params.lastChangeBlock, + currentBalances: params.currentBalances, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.exitExactBPTInForOneTokenOut(params.bptIn, this.tokens.indexOf(params.token)), + }; + } + + private _buildMultiExitGivenInParams(params: MultiExitGivenInWeightedPool): JoinExitWeightedPool { + return { + from: params.from, + recipient: params.recipient, + lastChangeBlock: params.lastChangeBlock, + currentBalances: params.currentBalances, + protocolFeePercentage: params.protocolFeePercentage, + data: WeightedPoolEncoder.exitExactBPTInForTokensOut(params.bptIn), + }; + } + + async setJoinExitEnabled(from: SignerWithAddress, joinExitEnabled: boolean): Promise { + const pool = this.instance.connect(from); + return pool.setJoinExitEnabled(joinExitEnabled); + } + + async setSwapEnabled(from: SignerWithAddress, swapEnabled: boolean): Promise { + const pool = this.instance.connect(from); + return pool.setSwapEnabled(swapEnabled); + } + + async setSwapFeePercentage(from: SignerWithAddress, swapFeePercentage: BigNumberish): Promise { + const pool = this.instance.connect(from); + return pool.setSwapFeePercentage(swapFeePercentage); + } + + async updateWeightsGradually( + from: SignerWithAddress, + startTime: BigNumberish, + endTime: BigNumberish, + endWeights: BigNumberish[] + ): Promise { + const pool = this.instance.connect(from); + + return await pool.updateWeightsGradually(startTime, endTime, endWeights); + } + + async getGradualWeightUpdateParams(from?: SignerWithAddress): Promise { + const pool = from ? this.instance.connect(from) : this.instance; + return await pool.getGradualWeightUpdateParams(); + } +} From a3a123d3d28c2e7c09f3cd66d0d8ecbb8b69de3c Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 1 Dec 2022 11:42:44 -0500 Subject: [PATCH 03/20] refactor: introduce classes for individual weighted pool types --- .../weighted/LiquidityBootstrappingPool.ts | 93 +++ .../src/models/pools/weighted/ManagedPool.ts | 344 ++++++++ .../src/models/pools/weighted/WeightedPool.ts | 737 ++---------------- .../src/models/pools/weighted/types.ts | 75 +- .../src/models/types/TypesConverter.ts | 89 ++- 5 files changed, 627 insertions(+), 711 deletions(-) create mode 100644 pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts create mode 100644 pvt/helpers/src/models/pools/weighted/ManagedPool.ts diff --git a/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts b/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts new file mode 100644 index 0000000000..2449f353a8 --- /dev/null +++ b/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts @@ -0,0 +1,93 @@ +import { Contract } from 'ethers'; +import TypesConverter from '../../types/TypesConverter'; +import Vault from '../../vault/Vault'; +import VaultDeployer from '../../vault/VaultDeployer'; +import BaseWeightedPool from './BaseWeightedPool'; +import { NAME, SYMBOL } from '../base/BasePool'; +import { deploy, deployedAt } from '../../../contract'; +import { RawLiquidityBootstrappingPoolDeployment, LiquidityBootstrappingPoolDeployment } from './types'; +import TokenList from '../../tokens/TokenList'; +import { BigNumberish } from '../../../numbers'; +import { Account } from '../../types/types'; +import * as expectEvent from '../../../test/expectEvent'; + +export default class LiquidityBootstrappingPool extends BaseWeightedPool { + swapEnabledOnStart: boolean; + + constructor( + instance: Contract, + poolId: string, + vault: Vault, + tokens: TokenList, + weights: BigNumberish[], + swapFeePercentage: BigNumberish, + swapEnabledOnStart: boolean, + owner?: Account + ) { + super(instance, poolId, vault, tokens, weights, swapFeePercentage, owner); + + this.swapEnabledOnStart = swapEnabledOnStart; + } + + static async create(params: RawLiquidityBootstrappingPoolDeployment = {}): Promise { + const vault = params?.vault ?? (await VaultDeployer.deploy(TypesConverter.toRawVaultDeployment(params))); + const deployment = TypesConverter.toLiquidityBootstrappingPoolDeployment(params); + const pool = await (params.fromFactory ? this._deployFromFactory : this._deployStandalone)(deployment, vault); + const poolId = await pool.getPoolId(); + + const { tokens, weights, swapFeePercentage, owner, swapEnabledOnStart } = deployment; + + return new LiquidityBootstrappingPool( + pool, + poolId, + vault, + tokens, + weights, + swapFeePercentage, + swapEnabledOnStart, + owner + ); + } + + static async _deployStandalone(params: LiquidityBootstrappingPoolDeployment, vault: Vault): Promise { + const { from } = params; + + return deploy('v2-pool-weighted/LiquidityBootstrappingPool', { + args: [ + vault.address, + NAME, + SYMBOL, + params.tokens.addresses, + params.weights, + params.swapFeePercentage, + params.pauseWindowDuration, + params.bufferPeriodDuration, + TypesConverter.toAddress(params.owner), + params.swapEnabledOnStart, + ], + from, + }); + } + + static async _deployFromFactory(params: LiquidityBootstrappingPoolDeployment, vault: Vault): Promise { + const { tokens, weights, swapFeePercentage, swapEnabledOnStart, owner, from } = params; + + const factory = await deploy('v2-pool-weighted/LiquidityBootstrappingPoolFactory', { + args: [vault.address, vault.getFeesProvider().address], + from, + }); + + const tx = await factory.create( + NAME, + SYMBOL, + tokens.addresses, + weights, + swapFeePercentage, + owner, + swapEnabledOnStart + ); + const receipt = await tx.wait(); + const event = expectEvent.inReceipt(receipt, 'PoolCreated'); + return deployedAt('v2-pool-weighted/LiquidityBootstrappingPool', event.args.pool); + } +} diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts new file mode 100644 index 0000000000..04362a647e --- /dev/null +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -0,0 +1,344 @@ +import { BigNumber, Contract, ContractTransaction } from 'ethers'; +import TypesConverter from '../../types/TypesConverter'; +import VaultDeployer from '../../vault/VaultDeployer'; +import WeightedPool from './WeightedPool'; +import { NAME, SYMBOL } from '../base/BasePool'; +import { + RawManagedPoolDeployment, + ManagedPoolDeployment, + ManagedPoolParams, + BasePoolRights, + ManagedPoolRights, + GradualSwapFeeUpdateParams, + CircuitBreakerState, +} from './types'; +import Vault from '../../vault/Vault'; +import { deploy, deployedAt } from '../../../contract'; +import * as expectEvent from '../../../test/expectEvent'; +import { Account } from '../../types/types'; +import TokenList from '../../tokens/TokenList'; +import { BigNumberish } from '../../../numbers'; +import { ProtocolFee } from '../../vault/types'; +import { ZERO_ADDRESS } from '../../../constants'; +import { DAY } from '../../../time'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import Token from '../../tokens/Token'; +import { accountToAddress } from '@balancer-labs/balancer-js'; + +export default class ManagedPool extends WeightedPool { + static weightedMathLib: Contract; + static addRemoveTokenLib: Contract; + static circuitBreakerLib: Contract; + + swapEnabledOnStart: boolean; + mustAllowlistLPs: boolean; + managementAumFeePercentage: BigNumberish; + aumFeeId: BigNumberish; + + constructor( + instance: Contract, + poolId: string, + vault: Vault, + tokens: TokenList, + weights: BigNumberish[], + rateProviders: Account[], + assetManagers: string[], + swapFeePercentage: BigNumberish, + swapEnabledOnStart: boolean, + mustAllowlistLPs: boolean, + managementAumFeePercentage: BigNumberish, + aumFeeId?: BigNumberish, + owner?: Account + ) { + super(instance, poolId, vault, tokens, weights, rateProviders, assetManagers, swapFeePercentage, owner); + + this.swapEnabledOnStart = swapEnabledOnStart; + this.mustAllowlistLPs = mustAllowlistLPs; + this.managementAumFeePercentage = managementAumFeePercentage; + this.aumFeeId = aumFeeId ?? ProtocolFee.AUM; + } + + static async create(params: RawManagedPoolDeployment = {}): Promise { + const vault = params?.vault ?? (await VaultDeployer.deploy(TypesConverter.toRawVaultDeployment(params))); + const deployment = TypesConverter.toManagedPoolDeployment(params); + + ManagedPool.weightedMathLib = await deploy('v2-pool-weighted/ExternalWeightedMath'); + ManagedPool.addRemoveTokenLib = await deploy('v2-pool-weighted/ManagedPoolAddRemoveTokenLib'); + ManagedPool.circuitBreakerLib = await deploy('v2-pool-weighted/CircuitBreakerLib'); + + const pool = await (params.fromFactory ? this._deployFromFactory : this._deployStandalone)(deployment, vault); + const poolId = await pool.getPoolId(); + + const { + tokens, + weights, + rateProviders, + assetManagers, + swapFeePercentage, + swapEnabledOnStart, + mustAllowlistLPs, + managementAumFeePercentage, + aumFeeId, + owner, + } = deployment; + + return new ManagedPool( + pool, + poolId, + vault, + tokens, + weights, + rateProviders, + assetManagers, + swapFeePercentage, + swapEnabledOnStart, + mustAllowlistLPs, + managementAumFeePercentage, + aumFeeId, + owner + ); + } + + async updateWeightsGradually( + from: SignerWithAddress, + startTime: BigNumberish, + endTime: BigNumberish, + endWeights: BigNumberish[], + tokens?: string[] + ): Promise { + const pool = this.instance.connect(from); + + if (!tokens) { + const { tokens: registeredTokens } = await this.getTokens(); + // If the first token is BPT then we can assume that the Pool is composable. + if (registeredTokens[0] == this.address) { + tokens = registeredTokens.slice(1); + } else { + tokens = registeredTokens; + } + } + + return await pool.updateWeightsGradually(startTime, endTime, tokens, endWeights); + } + + async updateSwapFeeGradually( + from: SignerWithAddress, + startTime: BigNumberish, + endTime: BigNumberish, + startSwapFeePercentage: BigNumberish, + endSwapFeePercentage: BigNumberish + ): Promise { + const pool = this.instance.connect(from); + return await pool.updateSwapFeeGradually(startTime, endTime, startSwapFeePercentage, endSwapFeePercentage); + } + + static async _deployStandalone(params: ManagedPoolDeployment, vault: Vault): Promise { + const { + tokens, + weights, + swapFeePercentage, + assetManagers, + swapEnabledOnStart, + mustAllowlistLPs, + managementAumFeePercentage, + aumFeeId, + owner, + pauseWindowDuration, + bufferPeriodDuration, + mockContractName, + from, + } = params; + + return deploy(mockContractName ?? 'v2-pool-weighted/ManagedPool', { + args: [ + { + name: NAME, + symbol: SYMBOL, + tokens: tokens.addresses, + normalizedWeights: weights, + assetManagers: assetManagers, + swapFeePercentage: swapFeePercentage, + swapEnabledOnStart: swapEnabledOnStart, + mustAllowlistLPs: mustAllowlistLPs, + managementAumFeePercentage: managementAumFeePercentage, + aumFeeId: aumFeeId, + }, + vault.address, + vault.protocolFeesProvider.address, + ManagedPool.weightedMathLib.address, + owner, + pauseWindowDuration, + bufferPeriodDuration, + ], + from, + libraries: { + CircuitBreakerLib: ManagedPool.circuitBreakerLib.address, + ManagedPoolAddRemoveTokenLib: ManagedPool.addRemoveTokenLib.address, + }, + }); + } + + async getGradualSwapFeeUpdateParams(from?: SignerWithAddress): Promise { + const pool = from ? this.instance.connect(from) : this.instance; + return await pool.getGradualSwapFeeUpdateParams(); + } + + async getCircuitBreakerState(token: Token | string): Promise { + return await this.instance.getCircuitBreakerState(TypesConverter.toAddress(token)); + } + + async addToken( + from: SignerWithAddress, + token: Token | string, + assetManager: Account, + normalizedWeight: BigNumberish, + mintAmount?: BigNumberish, + recipient?: string + ): Promise { + return this.instance + .connect(from) + .addToken( + TypesConverter.toAddress(token), + accountToAddress(assetManager), + normalizedWeight, + mintAmount ?? 0, + recipient ?? from.address + ); + } + + async removeToken( + from: SignerWithAddress, + token: Token | string, + sender?: string, + burnAmount?: BigNumberish + ): Promise { + return this.instance + .connect(from) + .removeToken(TypesConverter.toAddress(token), burnAmount ?? 0, sender ?? from.address); + } + + async setCircuitBreakers( + from: SignerWithAddress, + tokens: Token[] | string[], + bptPrices: BigNumber[], + lowerBounds: BigNumber[], + upperBounds: BigNumber[] + ): Promise { + const tokensArg = tokens.map((t) => TypesConverter.toAddress(t)); + const pool = this.instance.connect(from); + + return await pool.setCircuitBreakers(tokensArg, bptPrices, lowerBounds, upperBounds); + } + + async setManagementAumFeePercentage( + from: SignerWithAddress, + managementFee: BigNumberish + ): Promise { + const pool = this.instance.connect(from); + return pool.setManagementAumFeePercentage(managementFee); + } + + async addAllowedAddress(from: SignerWithAddress, member: Account): Promise { + const pool = this.instance.connect(from); + return pool.addAllowedAddress(TypesConverter.toAddress(member)); + } + + async removeAllowedAddress(from: SignerWithAddress, member: Account): Promise { + const pool = this.instance.connect(from); + return pool.removeAllowedAddress(TypesConverter.toAddress(member)); + } + + async getMustAllowlistLPs(): Promise { + return this.instance.getMustAllowlistLPs(); + } + + async setMustAllowlistLPs(from: SignerWithAddress, mustAllowlistLPs: boolean): Promise { + const pool = this.instance.connect(from); + return pool.setMustAllowlistLPs(mustAllowlistLPs); + } + + async isAllowedAddress(member: string): Promise { + return this.instance.isAllowedAddress(member); + } + + async collectAumManagementFees(from: SignerWithAddress): Promise { + const pool = this.instance.connect(from); + return pool.collectAumManagementFees(); + } + + async getJoinExitEnabled(from: SignerWithAddress): Promise { + return this.instance.connect(from).getJoinExitEnabled(); + } + + async getSwapEnabled(from: SignerWithAddress): Promise { + return this.instance.connect(from).getSwapEnabled(); + } + + async getManagementAumFeeParams(): Promise<[BigNumber, BigNumber]> { + return this.instance.getManagementAumFeeParams(); + } + + static async _deployFromFactory(params: ManagedPoolDeployment, vault: Vault): Promise { + const { + tokens, + weights, + assetManagers, + swapFeePercentage, + swapEnabledOnStart, + mustAllowlistLPs, + managementAumFeePercentage, + aumFeeId, + from, + } = params; + + const factory = await deploy('v2-pool-weighted/ManagedPoolFactory', { + args: [vault.address, vault.getFeesProvider().address], + from, + libraries: { + CircuitBreakerLib: ManagedPool.circuitBreakerLib.address, + ManagedPoolAddRemoveTokenLib: ManagedPool.addRemoveTokenLib.address, + }, + }); + + const controlledFactory = await deploy('v2-pool-weighted/ControlledManagedPoolFactory', { + args: [factory.address], + from, + }); + + const newPoolParams: ManagedPoolParams = { + name: NAME, + symbol: SYMBOL, + tokens: tokens.addresses, + normalizedWeights: weights, + assetManagers: assetManagers, + swapFeePercentage: swapFeePercentage, + swapEnabledOnStart: swapEnabledOnStart, + mustAllowlistLPs: mustAllowlistLPs, + managementAumFeePercentage: managementAumFeePercentage, + aumFeeId: aumFeeId ?? ProtocolFee.AUM, + }; + + const basePoolRights: BasePoolRights = { + canTransferOwnership: true, + canChangeSwapFee: true, + canUpdateMetadata: true, + }; + + const managedPoolRights: ManagedPoolRights = { + canChangeWeights: true, + canDisableSwaps: true, + canSetMustAllowlistLPs: true, + canSetCircuitBreakers: true, + canChangeTokens: true, + canChangeMgmtFees: true, + canDisableJoinExit: true, + }; + + const tx = await controlledFactory + .connect(from || ZERO_ADDRESS) + .create(newPoolParams, basePoolRights, managedPoolRights, DAY, from?.address || ZERO_ADDRESS); + const receipt = await tx.wait(); + const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); + return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool); + } +} diff --git a/pvt/helpers/src/models/pools/weighted/WeightedPool.ts b/pvt/helpers/src/models/pools/weighted/WeightedPool.ts index 7cef7c91e8..7a65f0069c 100644 --- a/pvt/helpers/src/models/pools/weighted/WeightedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/WeightedPool.ts @@ -1,71 +1,19 @@ -import { BigNumber, Contract, ContractFunction, ContractReceipt, ContractTransaction } from 'ethers'; -import { BigNumberish, bn, fp, fpMul } from '../../../numbers'; -import { MAX_UINT256, ZERO_ADDRESS } from '../../../constants'; -import * as expectEvent from '../../../test/expectEvent'; +import { Contract } from 'ethers'; +import TypesConverter from '../../types/TypesConverter'; +import VaultDeployer from '../../vault/VaultDeployer'; +import BaseWeightedPool from './BaseWeightedPool'; +import { NAME, SYMBOL } from '../base/BasePool'; +import { RawWeightedPoolDeployment, WeightedPoolDeployment } from './types'; import Vault from '../../vault/Vault'; -import Token from '../../tokens/Token'; +import { deploy, deployedAt } from '../../../contract'; +import * as expectEvent from '../../../test/expectEvent'; +import { Account } from '../../types/types'; import TokenList from '../../tokens/TokenList'; -import TypesConverter from '../../types/TypesConverter'; -import WeightedPoolDeployer from './WeightedPoolDeployer'; -import { MinimalSwap } from '../../vault/types'; -import { - JoinExitWeightedPool, - InitWeightedPool, - JoinGivenInWeightedPool, - JoinGivenOutWeightedPool, - JoinAllGivenOutWeightedPool, - JoinResult, - RawWeightedPoolDeployment, - ExitResult, - SwapResult, - SingleExitGivenInWeightedPool, - MultiExitGivenInWeightedPool, - ExitGivenOutWeightedPool, - SwapWeightedPool, - ExitQueryResult, - JoinQueryResult, - PoolQueryResult, - GradualWeightUpdateParams, - GradualSwapFeeUpdateParams, - WeightedPoolType, - CircuitBreakerState, -} from './types'; -import { - calculateInvariant, - calcBptOutGivenExactTokensIn, - calcTokenInGivenExactBptOut, - calcTokenOutGivenExactBptIn, - calcOutGivenIn, - calculateOneTokenSwapFeeAmount, - calcInGivenOut, - calculateMaxOneTokenSwapFeeAmount, - calculateSpotPrice, - calculateBPTPrice, -} from './math'; - -import { Account, accountToAddress, SwapKind, WeightedPoolEncoder } from '@balancer-labs/balancer-js'; -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import BasePool from '../base/BasePool'; -import assert from 'assert'; - -const MAX_IN_RATIO = fp(0.3); -const MAX_OUT_RATIO = fp(0.3); -const MAX_INVARIANT_RATIO = fp(3); -const MIN_INVARIANT_RATIO = fp(0.7); +import { BigNumberish } from '../../../numbers'; -export default class WeightedPool extends BasePool { - weights: BigNumberish[]; - rateProviders: string[]; +export default class WeightedPool extends BaseWeightedPool { + rateProviders: Account[]; assetManagers: string[]; - poolType: WeightedPoolType; - swapEnabledOnStart: boolean; - mustAllowlistLPs: boolean; - managementAumFeePercentage: BigNumberish; - aumProtocolFeesCollector: string; - - static async create(params: RawWeightedPoolDeployment = {}): Promise { - return WeightedPoolDeployer.deploy(params); - } constructor( instance: Contract, @@ -73,644 +21,75 @@ export default class WeightedPool extends BasePool { vault: Vault, tokens: TokenList, weights: BigNumberish[], - rateProviders: string[], + rateProviders: Account[], assetManagers: string[], swapFeePercentage: BigNumberish, - poolType: WeightedPoolType, - swapEnabledOnStart: boolean, - mustAllowlistLPs: boolean, - managementAumFeePercentage: BigNumberish, - aumProtocolFeesCollector: string, - owner?: SignerWithAddress + owner?: Account ) { - super(instance, poolId, vault, tokens, swapFeePercentage, owner); + super(instance, poolId, vault, tokens, weights, swapFeePercentage, owner); - this.weights = weights; this.rateProviders = rateProviders; this.assetManagers = assetManagers; - this.poolType = poolType; - this.swapEnabledOnStart = swapEnabledOnStart; - this.mustAllowlistLPs = mustAllowlistLPs; - this.managementAumFeePercentage = managementAumFeePercentage; - this.aumProtocolFeesCollector = aumProtocolFeesCollector; - } - - get maxWeight(): BigNumberish { - return this.weights.reduce((max, weight) => (bn(weight).gt(max) ? weight : max), bn(0)); - } - - get normalizedWeights(): BigNumberish[] { - return this.weights; - } - - get maxWeightIndex(): BigNumberish { - const maxIdx = this.weights.indexOf(this.maxWeight); - return bn(maxIdx); - } - - async getLastPostJoinExitInvariant(): Promise { - return this.instance.getLastPostJoinExitInvariant(); - } - - async getMaxInvariantDecrease(): Promise { - const supply = await this.totalSupply(); - return supply.sub(fpMul(MIN_INVARIANT_RATIO, supply)); - } - - async getMaxInvariantIncrease(): Promise { - const supply = await this.totalSupply(); - return fpMul(MAX_INVARIANT_RATIO, supply).sub(supply); - } - - async getMaxIn(tokenIndex: number, currentBalances?: BigNumber[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - return fpMul(currentBalances[tokenIndex], MAX_IN_RATIO); - } - - async getMaxOut(tokenIndex: number, currentBalances?: BigNumber[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - return fpMul(currentBalances[tokenIndex], MAX_OUT_RATIO); - } - - async getJoinExitEnabled(from: SignerWithAddress): Promise { - return this.instance.connect(from).getJoinExitEnabled(); - } - - async getSwapEnabled(from: SignerWithAddress): Promise { - return this.instance.connect(from).getSwapEnabled(); - } - - async getManagementAumFeeParams(): Promise<[BigNumber, BigNumber]> { - return this.instance.getManagementAumFeeParams(); - } - - async getNormalizedWeights(): Promise { - return this.instance.getNormalizedWeights(); - } - - async estimateSpotPrice(currentBalances?: BigNumberish[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - - const scalingFactors = await this.getScalingFactors(); - return calculateSpotPrice( - currentBalances.map((x, i) => fpMul(x, scalingFactors[i])), - this.weights - ); - } - - async estimateBptPrice( - tokenIndex: number, - currentBalance?: BigNumberish, - currentSupply?: BigNumberish - ): Promise { - if (!currentBalance) currentBalance = (await this.getBalances())[tokenIndex]; - if (!currentSupply) currentSupply = await this.totalSupply(); - - const scalingFactors = await this.getScalingFactors(); - - return calculateBPTPrice( - fpMul(currentBalance, scalingFactors[tokenIndex]), - this.weights[tokenIndex], - currentSupply - ); - } - - async estimateInvariant(currentBalances?: BigNumberish[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const scalingFactors = await this.getScalingFactors(); - - return calculateInvariant( - currentBalances.map((x, i) => fpMul(x, scalingFactors[i])), - this.weights - ); - } - - async estimateSwapFeeAmount( - paidToken: number | Token, - protocolFeePercentage: BigNumberish, - currentBalances?: BigNumberish[] - ): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const lastInvariant = await this.estimateInvariant(); - const paidTokenIndex = this.tokens.indexOf(paidToken); - const feeAmount = calculateOneTokenSwapFeeAmount(currentBalances, this.weights, lastInvariant, paidTokenIndex); - return fpMul(bn(feeAmount), protocolFeePercentage); - } - - async estimateMaxSwapFeeAmount( - paidToken: number | Token, - protocolFeePercentage: BigNumberish, - currentBalances?: BigNumberish[] - ): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const paidTokenIndex = this.tokens.indexOf(paidToken); - const feeAmount = calculateMaxOneTokenSwapFeeAmount( - currentBalances, - this.weights, - MIN_INVARIANT_RATIO, - paidTokenIndex - ); - return fpMul(bn(feeAmount), protocolFeePercentage); - } - - async estimateGivenIn(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); - - return bn( - calcOutGivenIn( - currentBalances[tokenIn], - this.weights[tokenIn], - currentBalances[tokenOut], - this.weights[tokenOut], - params.amount - ) - ); - } - - async estimateGivenOut(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); - - return bn( - calcInGivenOut( - currentBalances[tokenIn], - this.weights[tokenIn], - currentBalances[tokenOut], - this.weights[tokenOut], - params.amount - ) - ); } - async estimateBptOut( - amountsIn: BigNumberish[], - currentBalances?: BigNumberish[], - supply?: BigNumberish - ): Promise { - if (!supply) supply = await this.totalSupply(); - if (!currentBalances) currentBalances = await this.getBalances(); - return calcBptOutGivenExactTokensIn(currentBalances, this.weights, amountsIn, supply, this.swapFeePercentage); - } + static async create(params: RawWeightedPoolDeployment = {}): Promise { + const vault = params?.vault ?? (await VaultDeployer.deploy(TypesConverter.toRawVaultDeployment(params))); + const deployment = TypesConverter.toWeightedPoolDeployment(params); + const pool = await (params.fromFactory ? this._deployFromFactory : this._deployStandalone)(deployment, vault); + const poolId = await pool.getPoolId(); - async estimateTokenIn( - token: number | Token, - bptOut: BigNumberish, - currentBalances?: BigNumberish[], - supply?: BigNumberish - ): Promise { - if (!supply) supply = await this.totalSupply(); - if (!currentBalances) currentBalances = await this.getBalances(); - const tokenIndex = this.tokens.indexOf(token); - return calcTokenInGivenExactBptOut( - tokenIndex, - currentBalances, - this.weights, - bptOut, - supply, - this.swapFeePercentage - ); - } + const { tokens, weights, rateProviders, assetManagers, swapFeePercentage, owner } = deployment; - async estimateTokenOut( - token: number | Token, - bptIn: BigNumberish, - currentBalances?: BigNumberish[], - supply?: BigNumberish - ): Promise { - if (!supply) supply = await this.totalSupply(); - if (!currentBalances) currentBalances = await this.getBalances(); - const tokenIndex = this.tokens.indexOf(token); - return calcTokenOutGivenExactBptIn( - tokenIndex, - currentBalances, - this.weights, - bptIn, - supply, - this.swapFeePercentage + return new WeightedPool( + pool, + poolId, + vault, + tokens, + weights, + rateProviders, + assetManagers, + swapFeePercentage, + owner ); } - async swapGivenIn(params: SwapWeightedPool): Promise { - return this.swap(await this._buildSwapParams(SwapKind.GivenIn, params)); - } + static async _deployStandalone(params: WeightedPoolDeployment, vault: Vault): Promise { + const { from } = params; - async swapGivenOut(params: SwapWeightedPool): Promise { - return this.swap(await this._buildSwapParams(SwapKind.GivenOut, params)); - } - - async updateProtocolFeePercentageCache(): Promise { - return this.instance.updateProtocolFeePercentageCache(); - } - - async swap(params: MinimalSwap): Promise { - let receipt: ContractReceipt; - if (this.vault.mocked) { - const tx = await this.vault.minimalSwap(params); - receipt = await tx.wait(); - } else { - if (!params.from) throw new Error('No signer provided'); - const tx = await this.vault.instance.connect(params.from).swap( - { - poolId: params.poolId, - kind: params.kind, - assetIn: params.tokenIn, - assetOut: params.tokenOut, - amount: params.amount, - userData: params.data, - }, + return deploy('v2-pool-weighted/WeightedPool', { + args: [ { - sender: TypesConverter.toAddress(params.from), - recipient: TypesConverter.toAddress(params.to) ?? ZERO_ADDRESS, - fromInternalBalance: false, - toInternalBalance: false, + name: NAME, + symbol: SYMBOL, + tokens: params.tokens.addresses, + normalizedWeights: params.weights, + rateProviders: TypesConverter.toAddresses(params.rateProviders), + assetManagers: params.assetManagers, + swapFeePercentage: params.swapFeePercentage, }, - params.kind == 0 ? 0 : MAX_UINT256, - MAX_UINT256 - ); - receipt = await tx.wait(); - } - const { amountIn, amountOut } = expectEvent.inReceipt(receipt, 'Swap').args; - const amount = params.kind == SwapKind.GivenIn ? amountOut : amountIn; - - return { amount, receipt }; - } - - async init(params: InitWeightedPool): Promise { - return this.join(this._buildInitParams(params)); - } - - async joinGivenIn(params: JoinGivenInWeightedPool): Promise { - return this.join(this._buildJoinGivenInParams(params)); - } - - async queryJoinGivenIn(params: JoinGivenInWeightedPool): Promise { - return this.queryJoin(this._buildJoinGivenInParams(params)); - } - - async joinGivenOut(params: JoinGivenOutWeightedPool): Promise { - return this.join(this._buildJoinGivenOutParams(params)); - } - - async queryJoinGivenOut(params: JoinGivenOutWeightedPool): Promise { - return this.queryJoin(this._buildJoinGivenOutParams(params)); - } - - async joinAllGivenOut(params: JoinAllGivenOutWeightedPool): Promise { - return this.join(this._buildJoinAllGivenOutParams(params)); - } - - async queryJoinAllGivenOut(params: JoinAllGivenOutWeightedPool): Promise { - return this.queryJoin(this._buildJoinAllGivenOutParams(params)); - } - - async exitGivenOut(params: ExitGivenOutWeightedPool): Promise { - return this.exit(this._buildExitGivenOutParams(params)); - } - - async queryExitGivenOut(params: ExitGivenOutWeightedPool): Promise { - return this.queryExit(this._buildExitGivenOutParams(params)); - } - - async singleExitGivenIn(params: SingleExitGivenInWeightedPool): Promise { - return this.exit(this._buildSingleExitGivenInParams(params)); - } - - async querySingleExitGivenIn(params: SingleExitGivenInWeightedPool): Promise { - return this.queryExit(this._buildSingleExitGivenInParams(params)); - } - - async multiExitGivenIn(params: MultiExitGivenInWeightedPool): Promise { - return this.exit(this._buildMultiExitGivenInParams(params)); - } - - async queryMultiExitGivenIn(params: MultiExitGivenInWeightedPool): Promise { - return this.queryExit(this._buildMultiExitGivenInParams(params)); - } - - async queryJoin(params: JoinExitWeightedPool): Promise { - const fn = this.instance.queryJoin; - return (await this._executeQuery(params, fn)) as JoinQueryResult; - } - - async join(params: JoinExitWeightedPool): Promise { - const currentBalances = params.currentBalances || (await this.getBalances()); - const to = params.recipient ? TypesConverter.toAddress(params.recipient) : params.from?.address ?? ZERO_ADDRESS; - const { tokens } = await this.getTokens(); - - const tx = await this.vault.joinPool({ - poolAddress: this.address, - poolId: this.poolId, - recipient: to, - currentBalances, - tokens, - lastChangeBlock: params.lastChangeBlock ?? 0, - protocolFeePercentage: params.protocolFeePercentage ?? 0, - data: params.data ?? '0x', - from: params.from, + vault.address, + vault.protocolFeesProvider.address, + params.pauseWindowDuration, + params.bufferPeriodDuration, + params.owner, + ], + from, }); - - const receipt = await tx.wait(); - const { deltas, protocolFeeAmounts } = expectEvent.inReceipt(receipt, 'PoolBalanceChanged').args; - return { amountsIn: deltas, dueProtocolFeeAmounts: protocolFeeAmounts, receipt }; } - async queryExit(params: JoinExitWeightedPool): Promise { - const fn = this.instance.queryExit; - return (await this._executeQuery(params, fn)) as ExitQueryResult; - } + static async _deployFromFactory(params: WeightedPoolDeployment, vault: Vault): Promise { + // Note that we only support asset managers with the standalone deploy method. - async exit(params: JoinExitWeightedPool): Promise { - const currentBalances = params.currentBalances || (await this.getBalances()); - const to = params.recipient ? TypesConverter.toAddress(params.recipient) : params.from?.address ?? ZERO_ADDRESS; - const { tokens } = await this.getTokens(); + const { tokens, weights, rateProviders, swapFeePercentage, owner, from } = params; - const tx = await this.vault.exitPool({ - poolAddress: this.address, - poolId: this.poolId, - recipient: to, - currentBalances, - tokens, - lastChangeBlock: params.lastChangeBlock ?? 0, - protocolFeePercentage: params.protocolFeePercentage ?? 0, - data: params.data ?? '0x', - from: params.from, + const factory = await deploy('v2-pool-weighted/WeightedPoolFactory', { + args: [vault.address, vault.getFeesProvider().address], + from, }); + const tx = await factory.create(NAME, SYMBOL, tokens.addresses, weights, rateProviders, swapFeePercentage, owner); const receipt = await tx.wait(); - const { deltas, protocolFeeAmounts } = expectEvent.inReceipt(receipt, 'PoolBalanceChanged').args; - return { amountsOut: deltas.map((x: BigNumber) => x.mul(-1)), dueProtocolFeeAmounts: protocolFeeAmounts, receipt }; - } - - private async _executeQuery(params: JoinExitWeightedPool, fn: ContractFunction): Promise { - const currentBalances = params.currentBalances || (await this.getBalances()); - const to = params.recipient ? TypesConverter.toAddress(params.recipient) : params.from?.address ?? ZERO_ADDRESS; - - return fn( - this.poolId, - params.from?.address || ZERO_ADDRESS, - to, - currentBalances, - params.lastChangeBlock ?? 0, - params.protocolFeePercentage ?? 0, - params.data ?? '0x' - ); - } - - private async _buildSwapParams(kind: number, params: SwapWeightedPool): Promise { - const currentBalances = await this.getBalances(); - const { tokens } = await this.vault.getPoolTokens(this.poolId); - const tokenIn = typeof params.in === 'number' ? tokens[params.in] : params.in.address; - const tokenOut = typeof params.out === 'number' ? tokens[params.out] : params.out.address; - return { - kind, - poolAddress: this.address, - poolId: this.poolId, - from: params.from, - to: params.recipient ?? ZERO_ADDRESS, - tokenIn: tokenIn ?? ZERO_ADDRESS, - tokenOut: tokenOut ?? ZERO_ADDRESS, - balanceTokenIn: currentBalances[tokens.indexOf(tokenIn)] || bn(0), - balanceTokenOut: currentBalances[tokens.indexOf(tokenOut)] || bn(0), - lastChangeBlock: params.lastChangeBlock ?? 0, - data: params.data ?? '0x', - amount: params.amount, - }; - } - - private _buildInitParams(params: InitWeightedPool): JoinExitWeightedPool { - const { initialBalances: balances } = params; - const amountsIn = Array.isArray(balances) ? balances : Array(this.tokens.length).fill(balances); - - return { - from: params.from, - recipient: params.recipient, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.joinInit(amountsIn), - }; - } - - private _buildJoinGivenInParams(params: JoinGivenInWeightedPool): JoinExitWeightedPool { - const { amountsIn: amounts } = params; - const amountsIn = Array.isArray(amounts) ? amounts : Array(this.tokens.length).fill(amounts); - - return { - from: params.from, - recipient: params.recipient, - lastChangeBlock: params.lastChangeBlock, - currentBalances: params.currentBalances, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.joinExactTokensInForBPTOut(amountsIn, params.minimumBptOut ?? 0), - }; - } - - private _buildJoinGivenOutParams(params: JoinGivenOutWeightedPool): JoinExitWeightedPool { - return { - from: params.from, - recipient: params.recipient, - lastChangeBlock: params.lastChangeBlock, - currentBalances: params.currentBalances, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.joinTokenInForExactBPTOut(params.bptOut, this.tokens.indexOf(params.token)), - }; - } - - private _buildJoinAllGivenOutParams(params: JoinAllGivenOutWeightedPool): JoinExitWeightedPool { - return { - from: params.from, - recipient: params.recipient, - lastChangeBlock: params.lastChangeBlock, - currentBalances: params.currentBalances, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.joinAllTokensInForExactBPTOut(params.bptOut), - }; - } - - private _buildExitGivenOutParams(params: ExitGivenOutWeightedPool): JoinExitWeightedPool { - const { amountsOut: amounts } = params; - const amountsOut = Array.isArray(amounts) ? amounts : Array(this.tokens.length).fill(amounts); - return { - from: params.from, - recipient: params.recipient, - lastChangeBlock: params.lastChangeBlock, - currentBalances: params.currentBalances, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.exitBPTInForExactTokensOut(amountsOut, params.maximumBptIn ?? MAX_UINT256), - }; - } - - private _buildSingleExitGivenInParams(params: SingleExitGivenInWeightedPool): JoinExitWeightedPool { - return { - from: params.from, - recipient: params.recipient, - lastChangeBlock: params.lastChangeBlock, - currentBalances: params.currentBalances, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.exitExactBPTInForOneTokenOut(params.bptIn, this.tokens.indexOf(params.token)), - }; - } - - private _buildMultiExitGivenInParams(params: MultiExitGivenInWeightedPool): JoinExitWeightedPool { - return { - from: params.from, - recipient: params.recipient, - lastChangeBlock: params.lastChangeBlock, - currentBalances: params.currentBalances, - protocolFeePercentage: params.protocolFeePercentage, - data: WeightedPoolEncoder.exitExactBPTInForTokensOut(params.bptIn), - }; - } - - private _isManagedPool() { - return this.poolType == WeightedPoolType.MANAGED_POOL || this.poolType == WeightedPoolType.MOCK_MANAGED_POOL; - } - - async setJoinExitEnabled(from: SignerWithAddress, joinExitEnabled: boolean): Promise { - const pool = this.instance.connect(from); - return pool.setJoinExitEnabled(joinExitEnabled); - } - - async setSwapEnabled(from: SignerWithAddress, swapEnabled: boolean): Promise { - const pool = this.instance.connect(from); - return pool.setSwapEnabled(swapEnabled); - } - - async setSwapFeePercentage(from: SignerWithAddress, swapFeePercentage: BigNumberish): Promise { - if (this._isManagedPool()) { - throw new Error('Not available in managed pool'); - } - const pool = this.instance.connect(from); - return pool.setSwapFeePercentage(swapFeePercentage); - } - - async setManagementAumFeePercentage( - from: SignerWithAddress, - managementFee: BigNumberish - ): Promise { - const pool = this.instance.connect(from); - return pool.setManagementAumFeePercentage(managementFee); - } - - async addAllowedAddress(from: SignerWithAddress, member: Account): Promise { - const pool = this.instance.connect(from); - return pool.addAllowedAddress(TypesConverter.toAddress(member)); - } - - async removeAllowedAddress(from: SignerWithAddress, member: Account): Promise { - const pool = this.instance.connect(from); - return pool.removeAllowedAddress(TypesConverter.toAddress(member)); - } - - async getMustAllowlistLPs(): Promise { - return this.instance.getMustAllowlistLPs(); - } - - async setMustAllowlistLPs(from: SignerWithAddress, mustAllowlistLPs: boolean): Promise { - const pool = this.instance.connect(from); - return pool.setMustAllowlistLPs(mustAllowlistLPs); - } - - async isAllowedAddress(member: string): Promise { - return this.instance.isAllowedAddress(member); - } - - async collectAumManagementFees(from: SignerWithAddress): Promise { - const pool = this.instance.connect(from); - return pool.collectAumManagementFees(); - } - - async updateWeightsGradually( - from: SignerWithAddress, - startTime: BigNumberish, - endTime: BigNumberish, - endWeights: BigNumberish[], - tokens?: string[] - ): Promise { - const pool = this.instance.connect(from); - - if (this._isManagedPool()) { - if (!tokens) { - const { tokens: registeredTokens } = await this.getTokens(); - // If the first token is BPT then we can assume that the Pool is composable. - if (registeredTokens[0] == this.address) { - tokens = registeredTokens.slice(1); - } else { - tokens = registeredTokens; - } - } - - return await pool.updateWeightsGradually(startTime, endTime, tokens, endWeights); - } - - return await pool.updateWeightsGradually(startTime, endTime, endWeights); - } - - async updateSwapFeeGradually( - from: SignerWithAddress, - startTime: BigNumberish, - endTime: BigNumberish, - startSwapFeePercentage: BigNumberish, - endSwapFeePercentage: BigNumberish - ): Promise { - assert(this._isManagedPool(), 'Only available in managed pool'); - const pool = this.instance.connect(from); - return await pool.updateSwapFeeGradually(startTime, endTime, startSwapFeePercentage, endSwapFeePercentage); - } - - async setCircuitBreakers( - from: SignerWithAddress, - tokens: Token[] | string[], - bptPrices: BigNumber[], - lowerBounds: BigNumber[], - upperBounds: BigNumber[] - ): Promise { - const tokensArg = tokens.map((t) => TypesConverter.toAddress(t)); - const pool = this.instance.connect(from); - - return await pool.setCircuitBreakers(tokensArg, bptPrices, lowerBounds, upperBounds); - } - - async getGradualWeightUpdateParams(from?: SignerWithAddress): Promise { - const pool = from ? this.instance.connect(from) : this.instance; - return await pool.getGradualWeightUpdateParams(); - } - - async getGradualSwapFeeUpdateParams(from?: SignerWithAddress): Promise { - const pool = from ? this.instance.connect(from) : this.instance; - return await pool.getGradualSwapFeeUpdateParams(); - } - - async getCircuitBreakerState(token: Token | string): Promise { - return await this.instance.getCircuitBreakerState(TypesConverter.toAddress(token)); - } - - async addToken( - from: SignerWithAddress, - token: Token | string, - assetManager: Account, - normalizedWeight: BigNumberish, - mintAmount?: BigNumberish, - recipient?: string - ): Promise { - return this.instance - .connect(from) - .addToken( - TypesConverter.toAddress(token), - accountToAddress(assetManager), - normalizedWeight, - mintAmount ?? 0, - recipient ?? from.address - ); - } - - async removeToken( - from: SignerWithAddress, - token: Token | string, - sender?: string, - burnAmount?: BigNumberish - ): Promise { - return this.instance - .connect(from) - .removeToken(TypesConverter.toAddress(token), burnAmount ?? 0, sender ?? from.address); + const event = expectEvent.inReceipt(receipt, 'PoolCreated'); + return deployedAt('v2-pool-weighted/WeightedPool', event.args.pool); } } diff --git a/pvt/helpers/src/models/pools/weighted/types.ts b/pvt/helpers/src/models/pools/weighted/types.ts index 3d74901355..511e5b8625 100644 --- a/pvt/helpers/src/models/pools/weighted/types.ts +++ b/pvt/helpers/src/models/pools/weighted/types.ts @@ -8,13 +8,6 @@ import TokenList from '../../tokens/TokenList'; import { Account, NAry } from '../../types/types'; import Vault from '../../vault/Vault'; -export enum WeightedPoolType { - WEIGHTED_POOL = 0, - LIQUIDITY_BOOTSTRAPPING_POOL, - MANAGED_POOL, - MOCK_MANAGED_POOL, -} - export type RawWeightedPoolDeployment = { tokens?: TokenList; weights?: BigNumberish[]; @@ -23,35 +16,85 @@ export type RawWeightedPoolDeployment = { swapFeePercentage?: BigNumberish; pauseWindowDuration?: BigNumberish; bufferPeriodDuration?: BigNumberish; + owner?: Account; + admin?: SignerWithAddress; + from?: SignerWithAddress; + vault?: Vault; + fromFactory?: boolean; +}; + +export type WeightedPoolDeployment = { + tokens: TokenList; + weights: BigNumberish[]; + rateProviders: Account[]; + assetManagers: string[]; + swapFeePercentage: BigNumberish; + pauseWindowDuration: BigNumberish; + bufferPeriodDuration: BigNumberish; + owner: Account; + admin?: SignerWithAddress; + from?: SignerWithAddress; +}; + +export type RawLiquidityBootstrappingPoolDeployment = { + tokens?: TokenList; + weights?: BigNumberish[]; + swapFeePercentage?: BigNumberish; + swapEnabledOnStart?: boolean; + pauseWindowDuration?: BigNumberish; + bufferPeriodDuration?: BigNumberish; + owner?: Account; + admin?: SignerWithAddress; + from?: SignerWithAddress; + vault?: Vault; + fromFactory?: boolean; +}; + +export type LiquidityBootstrappingPoolDeployment = { + tokens: TokenList; + weights: BigNumberish[]; + swapFeePercentage: BigNumberish; + swapEnabledOnStart: boolean; + pauseWindowDuration: BigNumberish; + bufferPeriodDuration: BigNumberish; + owner: Account; + admin?: SignerWithAddress; + from?: SignerWithAddress; +}; + +export type RawManagedPoolDeployment = { + tokens?: TokenList; + weights?: BigNumberish[]; + rateProviders?: Account[]; + assetManagers?: string[]; + swapFeePercentage?: BigNumberish; swapEnabledOnStart?: boolean; mustAllowlistLPs?: boolean; managementAumFeePercentage?: BigNumberish; - aumProtocolFeesCollector?: string; + aumFeeId?: BigNumberish; + pauseWindowDuration?: BigNumberish; + bufferPeriodDuration?: BigNumberish; owner?: Account; admin?: SignerWithAddress; from?: SignerWithAddress; vault?: Vault; fromFactory?: boolean; - poolType?: WeightedPoolType; - aumFeeId?: BigNumberish; mockContractName?: string; }; -export type WeightedPoolDeployment = { +export type ManagedPoolDeployment = { tokens: TokenList; weights: BigNumberish[]; rateProviders: Account[]; assetManagers: string[]; swapFeePercentage: BigNumberish; - pauseWindowDuration: BigNumberish; - bufferPeriodDuration: BigNumberish; - poolType: WeightedPoolType; swapEnabledOnStart: boolean; mustAllowlistLPs: boolean; managementAumFeePercentage: BigNumberish; - aumProtocolFeesCollector: string; aumFeeId?: BigNumberish; - owner?: string; + pauseWindowDuration: BigNumberish; + bufferPeriodDuration: BigNumberish; + owner: Account; admin?: SignerWithAddress; from?: SignerWithAddress; mockContractName?: string; diff --git a/pvt/helpers/src/models/types/TypesConverter.ts b/pvt/helpers/src/models/types/TypesConverter.ts index f28faf7d6b..2da16d3f14 100644 --- a/pvt/helpers/src/models/types/TypesConverter.ts +++ b/pvt/helpers/src/models/types/TypesConverter.ts @@ -13,8 +13,11 @@ import { RawStablePoolDeployment, StablePoolDeployment } from '../pools/stable/t import { RawWeightedPoolDeployment, WeightedPoolDeployment, - WeightedPoolType, BasePoolRights, + RawLiquidityBootstrappingPoolDeployment, + LiquidityBootstrappingPoolDeployment, + RawManagedPoolDeployment, + ManagedPoolDeployment, } from '../pools/weighted/types'; import { RawTokenApproval, @@ -62,45 +65,99 @@ export default { swapFeePercentage, pauseWindowDuration, bufferPeriodDuration, + } = params; + if (!params.owner) params.owner = ZERO_ADDRESS; + if (!tokens) tokens = new TokenList(); + if (!weights) weights = Array(tokens.length).fill(fp(1)); + weights = toNormalizedWeights(weights.map(bn)); + if (!swapFeePercentage) swapFeePercentage = bn(1e16); + if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; + if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (!rateProviders) rateProviders = Array(tokens.length).fill(ZERO_ADDRESS); + if (!assetManagers) assetManagers = Array(tokens.length).fill(ZERO_ADDRESS); + + return { + tokens, + weights, + rateProviders, + assetManagers, + swapFeePercentage, + pauseWindowDuration, + bufferPeriodDuration, + owner: params.owner, + from: params.from, + }; + }, + + toLiquidityBootstrappingPoolDeployment( + params: RawLiquidityBootstrappingPoolDeployment + ): LiquidityBootstrappingPoolDeployment { + let { tokens, weights, swapFeePercentage, swapEnabledOnStart, pauseWindowDuration, bufferPeriodDuration } = params; + if (!params.owner) params.owner = ZERO_ADDRESS; + if (!tokens) tokens = new TokenList(); + if (!weights) weights = Array(tokens.length).fill(fp(1)); + weights = toNormalizedWeights(weights.map(bn)); + if (!swapFeePercentage) swapFeePercentage = bn(1e16); + if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; + if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (swapEnabledOnStart == undefined) swapEnabledOnStart = true; + + return { + tokens, + weights, + swapFeePercentage, swapEnabledOnStart, + pauseWindowDuration, + bufferPeriodDuration, + owner: params.owner, + from: params.from, + }; + }, + + toManagedPoolDeployment(params: RawManagedPoolDeployment): ManagedPoolDeployment { + let { + tokens, + weights, + rateProviders, + assetManagers, + swapFeePercentage, + swapEnabledOnStart, + pauseWindowDuration, + bufferPeriodDuration, + aumFeeId, mustAllowlistLPs, managementAumFeePercentage, - aumProtocolFeesCollector, - poolType, - aumFeeId, } = params; if (!params.owner) params.owner = ZERO_ADDRESS; if (!tokens) tokens = new TokenList(); if (!weights) weights = Array(tokens.length).fill(fp(1)); weights = toNormalizedWeights(weights.map(bn)); if (!swapFeePercentage) swapFeePercentage = bn(1e16); - if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; - if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; if (!rateProviders) rateProviders = Array(tokens.length).fill(ZERO_ADDRESS); if (!assetManagers) assetManagers = Array(tokens.length).fill(ZERO_ADDRESS); - if (!poolType) poolType = WeightedPoolType.WEIGHTED_POOL; - if (!aumProtocolFeesCollector) aumProtocolFeesCollector = ZERO_ADDRESS; + if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; + if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (swapEnabledOnStart == undefined) swapEnabledOnStart = true; if (undefined == aumFeeId) aumFeeId = ProtocolFee.AUM; if (undefined == swapEnabledOnStart) swapEnabledOnStart = true; if (undefined == mustAllowlistLPs) mustAllowlistLPs = false; if (undefined == managementAumFeePercentage) managementAumFeePercentage = FP_ZERO; + return { tokens, weights, - rateProviders: this.toAddresses(rateProviders), + rateProviders, assetManagers, swapFeePercentage, - pauseWindowDuration, - bufferPeriodDuration, swapEnabledOnStart, + aumFeeId, mustAllowlistLPs, managementAumFeePercentage, - aumProtocolFeesCollector, - owner: this.toAddress(params.owner), - from: params.from, - poolType, - aumFeeId, + pauseWindowDuration, + bufferPeriodDuration, mockContractName: params.mockContractName, + owner: params.owner, + from: params.from, }; }, From 717be33b61e4a108b4388a8bd26c5ba4be0099e2 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 1 Dec 2022 11:43:23 -0500 Subject: [PATCH 04/20] test: update pool-utils tests using weighted pools --- pkg/pool-utils/test/BasePoolController.test.ts | 3 +-- pkg/pool-utils/test/ManagedPoolController.test.ts | 13 ++++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/pkg/pool-utils/test/BasePoolController.test.ts b/pkg/pool-utils/test/BasePoolController.test.ts index a4f4f67e66..d2274966e3 100644 --- a/pkg/pool-utils/test/BasePoolController.test.ts +++ b/pkg/pool-utils/test/BasePoolController.test.ts @@ -6,7 +6,7 @@ import { fp } from '@balancer-labs/v2-helpers/src/numbers'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import { WeightedPoolType, BasePoolRights } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; +import { BasePoolRights } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { deploy } from '@balancer-labs/v2-helpers/src/contract'; import { MONTH } from '@balancer-labs/v2-helpers/src/time'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; @@ -63,7 +63,6 @@ async function deployControllerAndPool(canTransfer = true, canChangeSwapFee = tr owner: poolController.address, assetManagers, swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, - poolType: WeightedPoolType.WEIGHTED_POOL, }; pool = await WeightedPool.create(params); } diff --git a/pkg/pool-utils/test/ManagedPoolController.test.ts b/pkg/pool-utils/test/ManagedPoolController.test.ts index e79e762a07..3bc8195ccd 100644 --- a/pkg/pool-utils/test/ManagedPoolController.test.ts +++ b/pkg/pool-utils/test/ManagedPoolController.test.ts @@ -4,13 +4,9 @@ import { Contract } from 'ethers'; import { fp } from '@balancer-labs/v2-helpers/src/numbers'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; -import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; +import ManagedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/ManagedPool'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import { - WeightedPoolType, - ManagedPoolRights, - BasePoolRights, -} from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; +import { ManagedPoolRights, BasePoolRights } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { deploy } from '@balancer-labs/v2-helpers/src/contract'; import { currentTimestamp, MONTH, DAY, HOUR } from '@balancer-labs/v2-helpers/src/time'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; @@ -26,7 +22,7 @@ const MIN_WEIGHT_CHANGE_DURATION = DAY; let admin: SignerWithAddress; let manager: SignerWithAddress; let other: SignerWithAddress; -let pool: WeightedPool; +let pool: ManagedPool; let allTokens: TokenList; let vault: Vault; let poolController: Contract; @@ -95,11 +91,10 @@ async function deployControllerAndPool( owner: poolController.address, assetManagers, swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, - poolType: WeightedPoolType.MANAGED_POOL, swapEnabledOnStart: swapEnabledOnStart, protocolSwapFeePercentage: protocolSwapFeePercentage, }; - pool = await WeightedPool.create(params); + pool = await ManagedPool.create(params); } // Some tests repeated; could have a behavesLikeBasePoolController.behavior.ts From 136badbcc0f2d557bdd7f95d8d46400982295cc7 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 1 Dec 2022 11:43:56 -0500 Subject: [PATCH 05/20] test: adjust weighted pool tests to use individual models --- .../test/BaseWeightedPool.behavior.ts | 8 ++---- .../test/LiquidityBootstrappingPool.test.ts | 28 +++++++------------ pkg/pool-weighted/test/ManagedPool.test.ts | 12 ++++---- .../test/ManagedPoolSettings.test.ts | 14 ++++------ pkg/pool-weighted/test/WeightedPool.test.ts | 4 --- .../test/WeightedPoolProtocolFees.behavior.ts | 3 -- .../test/managed/AddRemove.test.ts | 12 ++++---- 7 files changed, 28 insertions(+), 53 deletions(-) diff --git a/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts b/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts index 7eda7b39e6..7a730f499c 100644 --- a/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts +++ b/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts @@ -7,16 +7,13 @@ import { BigNumberish, bn, fp, fpMul, pct } from '@balancer-labs/v2-helpers/src/ import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; -import { RawWeightedPoolDeployment, WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; +import { RawWeightedPoolDeployment } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { ZERO_ADDRESS } from '@balancer-labs/v2-helpers/src/constants'; import { sharedBeforeEach } from '@balancer-labs/v2-common/sharedBeforeEach'; import { expectBalanceChange } from '@balancer-labs/v2-helpers/src/test/tokenBalance'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; -export function itBehavesAsWeightedPool( - numberOfTokens: number, - poolType: WeightedPoolType = WeightedPoolType.WEIGHTED_POOL -): void { +export function itBehavesAsWeightedPool(numberOfTokens: number): void { const POOL_SWAP_FEE_PERCENTAGE = fp(0.01); const WEIGHTS = [fp(30), fp(70), fp(5), fp(5)]; const INITIAL_BALANCES = [fp(0.9), fp(1.8), fp(2.7), fp(3.6)]; @@ -35,7 +32,6 @@ export function itBehavesAsWeightedPool( tokens, weights, swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, - poolType, ...params, }); } diff --git a/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts b/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts index 845b6d0fd3..aa99a1cc0c 100644 --- a/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts +++ b/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts @@ -7,9 +7,8 @@ import { MINUTE, currentTimestamp } from '@balancer-labs/v2-helpers/src/time'; import * as expectEvent from '@balancer-labs/v2-helpers/src/test/expectEvent'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; -import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; +import LiquidityBootstrappingPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/LiquidityBootstrappingPool'; import { range } from 'lodash'; -import { WeightedPoolType } from '../../../pvt/helpers/src/models/pools/weighted/types'; import { itBehavesAsWeightedPool } from './BaseWeightedPool.behavior'; describe('LiquidityBootstrappingPool', function () { @@ -44,7 +43,7 @@ describe('LiquidityBootstrappingPool', function () { }); let sender: SignerWithAddress; - let pool: WeightedPool; + let pool: LiquidityBootstrappingPool; const weights = [fp(0.3), fp(0.55), fp(0.1), fp(0.05)]; const initialBalances = [fp(0.9), fp(1.8), fp(2.7), fp(3.6)]; @@ -55,41 +54,37 @@ describe('LiquidityBootstrappingPool', function () { const params = { tokens: allTokens.subset(1), weights: [fp(0.3)], - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, }; - await expect(WeightedPool.create(params)).to.be.revertedWith('MIN_TOKENS'); + await expect(LiquidityBootstrappingPool.create(params)).to.be.revertedWith('MIN_TOKENS'); }); it('fails with > 4 tokens', async () => { const params = { tokens: allTokens, weights: tooManyWeights, - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, }; - await expect(WeightedPool.create(params)).to.be.revertedWith('MAX_TOKENS'); + await expect(LiquidityBootstrappingPool.create(params)).to.be.revertedWith('MAX_TOKENS'); }); it('fails with mismatched tokens/weights', async () => { const params = { tokens, weights: tooManyWeights, - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, }; - await expect(WeightedPool.create(params)).to.be.revertedWith('INPUT_LENGTH_MISMATCH'); + await expect(LiquidityBootstrappingPool.create(params)).to.be.revertedWith('INPUT_LENGTH_MISMATCH'); }); }); describe('weights and scaling factors', () => { for (const numTokens of range(2, MAX_TOKENS + 1)) { context(`with ${numTokens} tokens`, () => { - let pool: WeightedPool; + let pool: LiquidityBootstrappingPool; let tokens: TokenList; sharedBeforeEach('deploy pool', async () => { tokens = allTokens.subset(numTokens); - pool = await WeightedPool.create({ - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, + pool = await LiquidityBootstrappingPool.create({ tokens, weights: weights.slice(0, numTokens), }); @@ -118,10 +113,9 @@ describe('LiquidityBootstrappingPool', function () { const params = { tokens, weights, - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, fromFactory: true, }; - pool = await WeightedPool.create(params); + pool = await LiquidityBootstrappingPool.create(params); }); it('has no asset managers', async () => { @@ -138,10 +132,9 @@ describe('LiquidityBootstrappingPool', function () { const params = { tokens, weights, - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, swapEnabledOnStart: false, }; - pool = await WeightedPool.create(params); + pool = await LiquidityBootstrappingPool.create(params); }); it('swaps show disabled on start', async () => { @@ -159,10 +152,9 @@ describe('LiquidityBootstrappingPool', function () { tokens, weights, owner: owner.address, - poolType: WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL, swapEnabledOnStart: true, }; - pool = await WeightedPool.create(params); + pool = await LiquidityBootstrappingPool.create(params); }); it('swaps show enabled on start', async () => { diff --git a/pkg/pool-weighted/test/ManagedPool.test.ts b/pkg/pool-weighted/test/ManagedPool.test.ts index de4699082c..3b61e6c439 100644 --- a/pkg/pool-weighted/test/ManagedPool.test.ts +++ b/pkg/pool-weighted/test/ManagedPool.test.ts @@ -14,14 +14,13 @@ import * as expectEvent from '@balancer-labs/v2-helpers/src/test/expectEvent'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; -import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; +import ManagedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/ManagedPool'; import { ExitResult, JoinQueryResult, JoinResult, - RawWeightedPoolDeployment, + RawManagedPoolDeployment, SwapResult, - WeightedPoolType, } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { PoolSpecialization, SwapKind } from '@balancer-labs/balancer-js'; @@ -35,7 +34,7 @@ describe('ManagedPool', function () { let allTokens: TokenList; let poolTokens: TokenList; let admin: SignerWithAddress, owner: SignerWithAddress, other: SignerWithAddress; - let pool: WeightedPool; + let pool: ManagedPool; let vault: Vault; before('setup signers', async () => { @@ -62,18 +61,17 @@ describe('ManagedPool', function () { await allTokens.approve({ from: owner, to: vault }); }); - async function deployPool(overrides: RawWeightedPoolDeployment = {}): Promise { + async function deployPool(overrides: RawManagedPoolDeployment = {}): Promise { const params = { vault, tokens: poolTokens, weights: poolWeights, owner: owner.address, aumFeeId: ProtocolFee.AUM, - poolType: WeightedPoolType.MOCK_MANAGED_POOL, mockContractName: 'MockManagedPool', ...overrides, }; - return WeightedPool.create(params); + return ManagedPool.create(params); } async function getUnscaledBptPrice(tokenIndex: number): Promise { diff --git a/pkg/pool-weighted/test/ManagedPoolSettings.test.ts b/pkg/pool-weighted/test/ManagedPoolSettings.test.ts index e98ffd044b..83c894ed78 100644 --- a/pkg/pool-weighted/test/ManagedPoolSettings.test.ts +++ b/pkg/pool-weighted/test/ManagedPoolSettings.test.ts @@ -26,8 +26,8 @@ import * as expectEvent from '@balancer-labs/v2-helpers/src/test/expectEvent'; import { deploy } from '@balancer-labs/v2-helpers/src/contract'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; -import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; -import { CircuitBreakerState, WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; +import ManagedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/ManagedPool'; +import { CircuitBreakerState } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { expectEqualWithError } from '@balancer-labs/v2-helpers/src/test/relativeError'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { toNormalizedWeights } from '@balancer-labs/balancer-js'; @@ -43,7 +43,7 @@ describe('ManagedPoolSettings', function () { let poolTokens: TokenList; let tooManyWeights: BigNumber[]; let admin: SignerWithAddress, owner: SignerWithAddress, other: SignerWithAddress; - let pool: WeightedPool; + let pool: ManagedPool; let vault: Vault; before('setup signers', async () => { @@ -79,14 +79,13 @@ describe('ManagedPoolSettings', function () { }); // eslint-disable-next-line @typescript-eslint/no-explicit-any - async function createMockPool(params: any): Promise { + async function createMockPool(params: any): Promise { const fullParams = { ...params, swapFeePercentage: INITIAL_SWAP_FEE, - poolType: WeightedPoolType.MOCK_MANAGED_POOL, mockContractName: 'MockManagedPoolSettings', }; - return WeightedPool.create(fullParams); + return ManagedPool.create(fullParams); } describe('constructor', () => { @@ -181,8 +180,7 @@ describe('ManagedPoolSettings', function () { function itStoresProviderFeeIds(aumFeeId: number) { context(`when aum fee ID is ${ProtocolFee[aumFeeId]}`, () => { sharedBeforeEach('deploy pool', async () => { - pool = await WeightedPool.create({ - poolType: WeightedPoolType.MANAGED_POOL, + pool = await ManagedPool.create({ tokens: allTokens.subset(2), vault, aumFeeId, diff --git a/pkg/pool-weighted/test/WeightedPool.test.ts b/pkg/pool-weighted/test/WeightedPool.test.ts index 73025698d4..e87d879771 100644 --- a/pkg/pool-weighted/test/WeightedPool.test.ts +++ b/pkg/pool-weighted/test/WeightedPool.test.ts @@ -7,7 +7,6 @@ import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; import { FundManagement, SwapKind } from '@balancer-labs/balancer-js'; -import { WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { fp, fpDiv, fpMul, FP_100_PCT } from '@balancer-labs/v2-helpers/src/numbers'; import { range } from 'lodash'; import { itPaysProtocolFeesFromInvariantGrowth } from './WeightedPoolProtocolFees.behavior'; @@ -46,7 +45,6 @@ describe('WeightedPool', function () { tokens = allTokens.subset(2); pool = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens, weights: WEIGHTS.slice(0, 2), swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, @@ -87,7 +85,6 @@ describe('WeightedPool', function () { tokens = allTokens.subset(numTokens); pool = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens, weights: WEIGHTS.slice(0, numTokens), swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, @@ -191,7 +188,6 @@ describe('WeightedPool', function () { await vault.setSwapFeePercentage(protocolFeePercentage); pool = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens, weights: WEIGHTS.slice(0, numTokens), swapFeePercentage: swapFeePercentage, diff --git a/pkg/pool-weighted/test/WeightedPoolProtocolFees.behavior.ts b/pkg/pool-weighted/test/WeightedPoolProtocolFees.behavior.ts index dec7bbb425..61a4dddefa 100644 --- a/pkg/pool-weighted/test/WeightedPoolProtocolFees.behavior.ts +++ b/pkg/pool-weighted/test/WeightedPoolProtocolFees.behavior.ts @@ -1,7 +1,6 @@ import { WeightedPoolEncoder } from '@balancer-labs/balancer-js'; import { deploy } from '@balancer-labs/v2-helpers/src/contract'; import { calculateInvariant } from '@balancer-labs/v2-helpers/src/models/pools/weighted/math'; -import { WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; @@ -47,7 +46,6 @@ export function itPaysProtocolFeesFromInvariantGrowth(): void { pool = await WeightedPool.create({ vault, - poolType: WeightedPoolType.WEIGHTED_POOL, tokens, weights: WEIGHTS.slice(0, numTokens), rateProviders, @@ -109,7 +107,6 @@ export function itPaysProtocolFeesFromInvariantGrowth(): void { sharedBeforeEach(async () => { yieldFeeExemptPool = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens, weights: WEIGHTS.slice(0, numTokens), swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, diff --git a/pkg/pool-weighted/test/managed/AddRemove.test.ts b/pkg/pool-weighted/test/managed/AddRemove.test.ts index f6e1bbd531..a3f7a209c1 100644 --- a/pkg/pool-weighted/test/managed/AddRemove.test.ts +++ b/pkg/pool-weighted/test/managed/AddRemove.test.ts @@ -2,8 +2,7 @@ import { toNormalizedWeights } from '@balancer-labs/balancer-js'; import { sharedBeforeEach } from '@balancer-labs/v2-common/sharedBeforeEach'; import { MAX_UINT256, ZERO_ADDRESS } from '@balancer-labs/v2-helpers/src/constants'; import { deploy } from '@balancer-labs/v2-helpers/src/contract'; -import { WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; -import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; +import ManagedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/ManagedPool'; import Token from '@balancer-labs/v2-helpers/src/models/tokens/Token'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; @@ -50,7 +49,7 @@ describe('ManagedPoolSettings - add/remove token', () => { async function createPool( numberOfTokens: number, weights?: Array - ): Promise<{ pool: WeightedPool; poolTokens: TokenList }> { + ): Promise<{ pool: ManagedPool; poolTokens: TokenList }> { const poolTokens = allTokens.subset(numberOfTokens); if (weights == undefined) { // We pick random weights, but ones that are not so far apart as to cause issues due to minimum weights. The @@ -63,11 +62,10 @@ describe('ManagedPoolSettings - add/remove token', () => { weights = range(numberOfTokens).map(() => fp(100 + random(50))); } - const pool = await WeightedPool.create({ + const pool = await ManagedPool.create({ tokens: poolTokens, weights, owner: owner.address, - poolType: WeightedPoolType.MANAGED_POOL, assetManagers: Array(numberOfTokens).fill(assetManager.address), swapEnabledOnStart: true, vault, @@ -117,7 +115,7 @@ describe('ManagedPoolSettings - add/remove token', () => { itAddsATokenAtTokenCount(MAX_TOKENS - 1); function itAddsATokenAtTokenCount(poolTokenCount: number) { - let pool: WeightedPool; + let pool: ManagedPool; let poolTokens: TokenList; context(`when the pool has ${poolTokenCount} tokens`, () => { @@ -456,7 +454,7 @@ describe('ManagedPoolSettings - add/remove token', () => { itRemovesATokenAtTokenCount(MAX_TOKENS); function itRemovesATokenAtTokenCount(poolTokenCount: number) { - let pool: WeightedPool; + let pool: ManagedPool; let poolTokens: TokenList; context(`when the pool has ${poolTokenCount} tokens`, () => { From e6b3b5a3a3e752ad7dca0989bcdf1d6818f3faad Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 1 Dec 2022 11:44:20 -0500 Subject: [PATCH 06/20] test: adjust dependencies in standalone utils --- pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts | 2 +- pkg/standalone-utils/test/VaultActions.test.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts b/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts index be4fe2de8c..17416334d8 100644 --- a/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts +++ b/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts @@ -45,7 +45,7 @@ describe('ProtocolFeeSplitter', function () { }); sharedBeforeEach('create and initialize pools', async () => { - pool = await WeightedPool.create({ vault, tokens, owner }); + pool = await WeightedPool.create({ vault, tokens, owner: owner.address }); poolId = await pool.getPoolId(); const initialBalances = Array(tokens.length).fill(fp(1000)); diff --git a/pkg/standalone-utils/test/VaultActions.test.ts b/pkg/standalone-utils/test/VaultActions.test.ts index fc1937dcff..5ae7ac763c 100644 --- a/pkg/standalone-utils/test/VaultActions.test.ts +++ b/pkg/standalone-utils/test/VaultActions.test.ts @@ -8,7 +8,6 @@ import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; import { BigNumberish, fp } from '@balancer-labs/v2-helpers/src/numbers'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; -import { WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { getPoolAddress, SwapKind, WeightedPoolEncoder } from '@balancer-labs/balancer-js'; import { MAX_INT256, MAX_UINT256, ZERO_ADDRESS } from '@balancer-labs/v2-helpers/src/constants'; import { expectBalanceChange } from '@balancer-labs/v2-helpers/src/test/tokenBalance'; @@ -75,7 +74,6 @@ describe('VaultActions', function () { // Pool A: DAI-MKR tokensA = new TokenList([tokens.DAI, tokens.MKR]).sort(); const poolA = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens: tokensA, vault, }); @@ -86,7 +84,6 @@ describe('VaultActions', function () { // Pool B: MKR-SNX tokensB = new TokenList([tokens.MKR, tokens.SNX]).sort(); const poolB = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens: tokensB, vault, }); @@ -97,7 +94,6 @@ describe('VaultActions', function () { // Pool C: SNX-BAT tokensC = new TokenList([tokens.SNX, tokens.BAT]).sort(); const poolC = await WeightedPool.create({ - poolType: WeightedPoolType.WEIGHTED_POOL, tokens: tokensC, vault, }); From 0c79a4b23f6472124394d9559875cccc365c7315 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Fri, 2 Dec 2022 01:14:27 -0500 Subject: [PATCH 07/20] remove unused functions; move statics together --- .../models/pools/weighted/BaseWeightedPool.ts | 68 ------ .../src/models/pools/weighted/ManagedPool.ts | 194 +++++++++--------- 2 files changed, 97 insertions(+), 165 deletions(-) diff --git a/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts b/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts index 2552fe15df..26c586f56b 100644 --- a/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts @@ -31,11 +31,7 @@ import { calcTokenInGivenExactBptOut, calcTokenOutGivenExactBptIn, calcOutGivenIn, - calculateOneTokenSwapFeeAmount, calcInGivenOut, - calculateMaxOneTokenSwapFeeAmount, - calculateSpotPrice, - calculateBPTPrice, } from './math'; import { SwapKind, WeightedPoolEncoder } from '@balancer-labs/balancer-js'; @@ -65,19 +61,10 @@ export default class BaseWeightedPool extends BasePool { this.weights = weights; } - get maxWeight(): BigNumberish { - return this.weights.reduce((max, weight) => (bn(weight).gt(max) ? weight : max), bn(0)); - } - get normalizedWeights(): BigNumberish[] { return this.weights; } - get maxWeightIndex(): BigNumberish { - const maxIdx = this.weights.indexOf(this.maxWeight); - return bn(maxIdx); - } - async getLastPostJoinExitInvariant(): Promise { return this.instance.getLastPostJoinExitInvariant(); } @@ -106,33 +93,6 @@ export default class BaseWeightedPool extends BasePool { return this.instance.getNormalizedWeights(); } - async estimateSpotPrice(currentBalances?: BigNumberish[]): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - - const scalingFactors = await this.getScalingFactors(); - return calculateSpotPrice( - currentBalances.map((x, i) => fpMul(x, scalingFactors[i])), - this.weights - ); - } - - async estimateBptPrice( - tokenIndex: number, - currentBalance?: BigNumberish, - currentSupply?: BigNumberish - ): Promise { - if (!currentBalance) currentBalance = (await this.getBalances())[tokenIndex]; - if (!currentSupply) currentSupply = await this.totalSupply(); - - const scalingFactors = await this.getScalingFactors(); - - return calculateBPTPrice( - fpMul(currentBalance, scalingFactors[tokenIndex]), - this.weights[tokenIndex], - currentSupply - ); - } - async estimateInvariant(currentBalances?: BigNumberish[]): Promise { if (!currentBalances) currentBalances = await this.getBalances(); const scalingFactors = await this.getScalingFactors(); @@ -143,34 +103,6 @@ export default class BaseWeightedPool extends BasePool { ); } - async estimateSwapFeeAmount( - paidToken: number | Token, - protocolFeePercentage: BigNumberish, - currentBalances?: BigNumberish[] - ): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const lastInvariant = await this.estimateInvariant(); - const paidTokenIndex = this.tokens.indexOf(paidToken); - const feeAmount = calculateOneTokenSwapFeeAmount(currentBalances, this.weights, lastInvariant, paidTokenIndex); - return fpMul(bn(feeAmount), protocolFeePercentage); - } - - async estimateMaxSwapFeeAmount( - paidToken: number | Token, - protocolFeePercentage: BigNumberish, - currentBalances?: BigNumberish[] - ): Promise { - if (!currentBalances) currentBalances = await this.getBalances(); - const paidTokenIndex = this.tokens.indexOf(paidToken); - const feeAmount = calculateMaxOneTokenSwapFeeAmount( - currentBalances, - this.weights, - MIN_INVARIANT_RATIO, - paidTokenIndex - ); - return fpMul(bn(feeAmount), protocolFeePercentage); - } - async estimateGivenIn(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { if (!currentBalances) currentBalances = await this.getBalances(); const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts index 19a507ce90..0c1c414f63 100644 --- a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -104,39 +104,6 @@ export default class ManagedPool extends WeightedPool { ); } - async updateWeightsGradually( - from: SignerWithAddress, - startTime: BigNumberish, - endTime: BigNumberish, - endWeights: BigNumberish[], - tokens?: string[] - ): Promise { - const pool = this.instance.connect(from); - - if (!tokens) { - const { tokens: registeredTokens } = await this.getTokens(); - // If the first token is BPT then we can assume that the Pool is composable. - if (registeredTokens[0] == this.address) { - tokens = registeredTokens.slice(1); - } else { - tokens = registeredTokens; - } - } - - return await pool.updateWeightsGradually(startTime, endTime, tokens, endWeights); - } - - async updateSwapFeeGradually( - from: SignerWithAddress, - startTime: BigNumberish, - endTime: BigNumberish, - startSwapFeePercentage: BigNumberish, - endSwapFeePercentage: BigNumberish - ): Promise { - const pool = this.instance.connect(from); - return await pool.updateSwapFeeGradually(startTime, endTime, startSwapFeePercentage, endSwapFeePercentage); - } - static async _deployStandalone(params: ManagedPoolDeployment, vault: Vault): Promise { const { tokens, @@ -215,6 +182,103 @@ export default class ManagedPool extends WeightedPool { }); } + static async _deployFromFactory(params: ManagedPoolDeployment, vault: Vault): Promise { + const { + tokens, + weights, + assetManagers, + swapFeePercentage, + swapEnabledOnStart, + mustAllowlistLPs, + managementAumFeePercentage, + aumFeeId, + from, + } = params; + + const factory = await deploy('v2-pool-weighted/ManagedPoolFactory', { + args: [vault.address, vault.getFeesProvider().address], + from, + libraries: { + CircuitBreakerLib: ManagedPool.circuitBreakerLib.address, + ManagedPoolAddRemoveTokenLib: ManagedPool.addRemoveTokenLib.address, + }, + }); + + const controlledFactory = await deploy('v2-pool-weighted/ControlledManagedPoolFactory', { + args: [factory.address], + from, + }); + + const newPoolParams: ManagedPoolParams = { + name: NAME, + symbol: SYMBOL, + tokens: tokens.addresses, + normalizedWeights: weights, + assetManagers: assetManagers, + swapFeePercentage: swapFeePercentage, + swapEnabledOnStart: swapEnabledOnStart, + mustAllowlistLPs: mustAllowlistLPs, + managementAumFeePercentage: managementAumFeePercentage, + aumFeeId: aumFeeId ?? ProtocolFee.AUM, + }; + + const basePoolRights: BasePoolRights = { + canTransferOwnership: true, + canChangeSwapFee: true, + canUpdateMetadata: true, + }; + + const managedPoolRights: ManagedPoolRights = { + canChangeWeights: true, + canDisableSwaps: true, + canSetMustAllowlistLPs: true, + canSetCircuitBreakers: true, + canChangeTokens: true, + canChangeMgmtFees: true, + canDisableJoinExit: true, + }; + + const tx = await controlledFactory + .connect(from || ZERO_ADDRESS) + .create(newPoolParams, basePoolRights, managedPoolRights, DAY, from?.address || ZERO_ADDRESS); + const receipt = await tx.wait(); + const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); + return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool); + } + + async updateWeightsGradually( + from: SignerWithAddress, + startTime: BigNumberish, + endTime: BigNumberish, + endWeights: BigNumberish[], + tokens?: string[] + ): Promise { + const pool = this.instance.connect(from); + + if (!tokens) { + const { tokens: registeredTokens } = await this.getTokens(); + // If the first token is BPT then we can assume that the Pool is composable. + if (registeredTokens[0] == this.address) { + tokens = registeredTokens.slice(1); + } else { + tokens = registeredTokens; + } + } + + return await pool.updateWeightsGradually(startTime, endTime, tokens, endWeights); + } + + async updateSwapFeeGradually( + from: SignerWithAddress, + startTime: BigNumberish, + endTime: BigNumberish, + startSwapFeePercentage: BigNumberish, + endSwapFeePercentage: BigNumberish + ): Promise { + const pool = this.instance.connect(from); + return await pool.updateSwapFeeGradually(startTime, endTime, startSwapFeePercentage, endSwapFeePercentage); + } + async version(): Promise { return this.instance.version(); } @@ -318,68 +382,4 @@ export default class ManagedPool extends WeightedPool { async getManagementAumFeeParams(): Promise<[BigNumber, BigNumber]> { return this.instance.getManagementAumFeeParams(); } - - static async _deployFromFactory(params: ManagedPoolDeployment, vault: Vault): Promise { - const { - tokens, - weights, - assetManagers, - swapFeePercentage, - swapEnabledOnStart, - mustAllowlistLPs, - managementAumFeePercentage, - aumFeeId, - from, - } = params; - - const factory = await deploy('v2-pool-weighted/ManagedPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], - from, - libraries: { - CircuitBreakerLib: ManagedPool.circuitBreakerLib.address, - ManagedPoolAddRemoveTokenLib: ManagedPool.addRemoveTokenLib.address, - }, - }); - - const controlledFactory = await deploy('v2-pool-weighted/ControlledManagedPoolFactory', { - args: [factory.address], - from, - }); - - const newPoolParams: ManagedPoolParams = { - name: NAME, - symbol: SYMBOL, - tokens: tokens.addresses, - normalizedWeights: weights, - assetManagers: assetManagers, - swapFeePercentage: swapFeePercentage, - swapEnabledOnStart: swapEnabledOnStart, - mustAllowlistLPs: mustAllowlistLPs, - managementAumFeePercentage: managementAumFeePercentage, - aumFeeId: aumFeeId ?? ProtocolFee.AUM, - }; - - const basePoolRights: BasePoolRights = { - canTransferOwnership: true, - canChangeSwapFee: true, - canUpdateMetadata: true, - }; - - const managedPoolRights: ManagedPoolRights = { - canChangeWeights: true, - canDisableSwaps: true, - canSetMustAllowlistLPs: true, - canSetCircuitBreakers: true, - canChangeTokens: true, - canChangeMgmtFees: true, - canDisableJoinExit: true, - }; - - const tx = await controlledFactory - .connect(from || ZERO_ADDRESS) - .create(newPoolParams, basePoolRights, managedPoolRights, DAY, from?.address || ZERO_ADDRESS); - const receipt = await tx.wait(); - const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); - return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool); - } } From b2c8810ba3f336f2309baf7829ecca25dcb9f227 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Tue, 6 Dec 2022 10:16:29 -0500 Subject: [PATCH 08/20] factor out pause parameter defaults --- .../src/models/types/TypesConverter.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pvt/helpers/src/models/types/TypesConverter.ts b/pvt/helpers/src/models/types/TypesConverter.ts index 7116e85b6d..708d2b3018 100644 --- a/pvt/helpers/src/models/types/TypesConverter.ts +++ b/pvt/helpers/src/models/types/TypesConverter.ts @@ -29,6 +29,9 @@ import { RawTokenDeployment, } from '../tokens/types'; +const DEFAULT_PAUSE_WINDOW_DURATION = 3 * MONTH; +const DEFAULT_BUFFER_PERIOD_DURATION = MONTH; + export function computeDecimalsFromIndex(i: number): number { // Produces repeating series (0..18) return i % 19; @@ -71,8 +74,8 @@ export default { if (!weights) weights = Array(tokens.length).fill(fp(1)); weights = toNormalizedWeights(weights.map(bn)); if (!swapFeePercentage) swapFeePercentage = bn(1e16); - if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; - if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; + if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; if (!rateProviders) rateProviders = Array(tokens.length).fill(ZERO_ADDRESS); if (!assetManagers) assetManagers = Array(tokens.length).fill(ZERO_ADDRESS); @@ -98,8 +101,8 @@ export default { if (!weights) weights = Array(tokens.length).fill(fp(1)); weights = toNormalizedWeights(weights.map(bn)); if (!swapFeePercentage) swapFeePercentage = bn(1e16); - if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; - if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; + if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; if (swapEnabledOnStart == undefined) swapEnabledOnStart = true; return { @@ -137,8 +140,8 @@ export default { if (!swapFeePercentage) swapFeePercentage = bn(1e16); if (!rateProviders) rateProviders = Array(tokens.length).fill(ZERO_ADDRESS); if (!assetManagers) assetManagers = Array(tokens.length).fill(ZERO_ADDRESS); - if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; - if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; + if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; if (swapEnabledOnStart == undefined) swapEnabledOnStart = true; if (undefined == aumFeeId) aumFeeId = ProtocolFee.AUM; if (undefined == swapEnabledOnStart) swapEnabledOnStart = true; @@ -171,8 +174,8 @@ export default { if (!upperTarget) upperTarget = bn(0); if (!swapFeePercentage) swapFeePercentage = bn(1e12); - if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; - if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; + if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; if (!assetManagers) assetManagers = [ZERO_ADDRESS, ZERO_ADDRESS]; return { @@ -205,8 +208,8 @@ export default { if (!tokenRateCacheDurations) tokenRateCacheDurations = Array(tokens.length).fill(DAY); if (!amplificationParameter) amplificationParameter = bn(200); if (!swapFeePercentage) swapFeePercentage = bn(1e12); - if (!pauseWindowDuration) pauseWindowDuration = 3 * MONTH; - if (!bufferPeriodDuration) bufferPeriodDuration = MONTH; + if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; + if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; if (!exemptFromYieldProtocolFeeFlags) exemptFromYieldProtocolFeeFlags = Array(tokens.length).fill(false); if (!version) version = 'test'; From 5a0d8f955ed28d72f6a8fbd011cfc536b442c2f7 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Wed, 7 Dec 2022 14:46:00 -0500 Subject: [PATCH 09/20] factor: adjust factory parameters --- pvt/helpers/src/models/pools/base/BasePool.ts | 4 +++- .../src/models/pools/weighted/LiquidityBootstrappingPool.ts | 4 ++-- pvt/helpers/src/models/pools/weighted/ManagedPool.ts | 4 ++-- pvt/helpers/src/models/pools/weighted/WeightedPool.ts | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pvt/helpers/src/models/pools/base/BasePool.ts b/pvt/helpers/src/models/pools/base/BasePool.ts index f77d4d9cae..ea416d5da1 100644 --- a/pvt/helpers/src/models/pools/base/BasePool.ts +++ b/pvt/helpers/src/models/pools/base/BasePool.ts @@ -11,11 +11,13 @@ import TokenList from '../../tokens/TokenList'; import { actionId } from '../../misc/actions'; import Token from '../../tokens/Token'; import Vault from '../../vault/Vault'; - import { RecoveryModeExitParams, ExitResult, JoinExitBasePool, FailureMode } from './types'; +import { DAY } from '../../../time'; export const NAME = 'Balancer Pool Token'; export const SYMBOL = 'BPT'; +export const PAUSE_WINDOW_DURATION = DAY * 90; +export const BUFFER_PERIOD_DURATION = DAY * 30; export default class BasePool { instance: Contract; diff --git a/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts b/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts index 2449f353a8..4e79893604 100644 --- a/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts +++ b/pvt/helpers/src/models/pools/weighted/LiquidityBootstrappingPool.ts @@ -3,7 +3,7 @@ import TypesConverter from '../../types/TypesConverter'; import Vault from '../../vault/Vault'; import VaultDeployer from '../../vault/VaultDeployer'; import BaseWeightedPool from './BaseWeightedPool'; -import { NAME, SYMBOL } from '../base/BasePool'; +import { BUFFER_PERIOD_DURATION, NAME, PAUSE_WINDOW_DURATION, SYMBOL } from '../base/BasePool'; import { deploy, deployedAt } from '../../../contract'; import { RawLiquidityBootstrappingPoolDeployment, LiquidityBootstrappingPoolDeployment } from './types'; import TokenList from '../../tokens/TokenList'; @@ -73,7 +73,7 @@ export default class LiquidityBootstrappingPool extends BaseWeightedPool { const { tokens, weights, swapFeePercentage, swapEnabledOnStart, owner, from } = params; const factory = await deploy('v2-pool-weighted/LiquidityBootstrappingPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], + args: [vault.address, vault.getFeesProvider().address, PAUSE_WINDOW_DURATION, BUFFER_PERIOD_DURATION], from, }); diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts index 0c1c414f63..09efcc4f6e 100644 --- a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -2,7 +2,7 @@ import { BigNumber, Contract, ContractTransaction } from 'ethers'; import TypesConverter from '../../types/TypesConverter'; import VaultDeployer from '../../vault/VaultDeployer'; import WeightedPool from './WeightedPool'; -import { NAME, SYMBOL } from '../base/BasePool'; +import { BUFFER_PERIOD_DURATION, NAME, PAUSE_WINDOW_DURATION, SYMBOL } from '../base/BasePool'; import { RawManagedPoolDeployment, ManagedPoolDeployment, @@ -196,7 +196,7 @@ export default class ManagedPool extends WeightedPool { } = params; const factory = await deploy('v2-pool-weighted/ManagedPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], + args: [vault.address, vault.getFeesProvider().address, PAUSE_WINDOW_DURATION, BUFFER_PERIOD_DURATION], from, libraries: { CircuitBreakerLib: ManagedPool.circuitBreakerLib.address, diff --git a/pvt/helpers/src/models/pools/weighted/WeightedPool.ts b/pvt/helpers/src/models/pools/weighted/WeightedPool.ts index 7a65f0069c..b01db9eefc 100644 --- a/pvt/helpers/src/models/pools/weighted/WeightedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/WeightedPool.ts @@ -2,7 +2,7 @@ import { Contract } from 'ethers'; import TypesConverter from '../../types/TypesConverter'; import VaultDeployer from '../../vault/VaultDeployer'; import BaseWeightedPool from './BaseWeightedPool'; -import { NAME, SYMBOL } from '../base/BasePool'; +import { BUFFER_PERIOD_DURATION, NAME, PAUSE_WINDOW_DURATION, SYMBOL } from '../base/BasePool'; import { RawWeightedPoolDeployment, WeightedPoolDeployment } from './types'; import Vault from '../../vault/Vault'; import { deploy, deployedAt } from '../../../contract'; @@ -83,7 +83,7 @@ export default class WeightedPool extends BaseWeightedPool { const { tokens, weights, rateProviders, swapFeePercentage, owner, from } = params; const factory = await deploy('v2-pool-weighted/WeightedPoolFactory', { - args: [vault.address, vault.getFeesProvider().address], + args: [vault.address, vault.getFeesProvider().address, PAUSE_WINDOW_DURATION, BUFFER_PERIOD_DURATION], from, }); From af0bffc257f0256e4952bba795dff795e70ed4e5 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Fri, 6 Jan 2023 16:30:16 -0500 Subject: [PATCH 10/20] refactor: use string enum ManagedPoolType instead of string --- pkg/pool-weighted/test/ManagedPool.test.ts | 3 ++- pkg/pool-weighted/test/ManagedPoolSettings.test.ts | 5 +++-- pkg/pool-weighted/test/managed/AddRemove.test.ts | 2 ++ pvt/helpers/src/models/pools/weighted/ManagedPool.ts | 7 ++++--- pvt/helpers/src/models/pools/weighted/types.ts | 10 ++++++++-- pvt/helpers/src/models/types/TypesConverter.ts | 5 ++++- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/pkg/pool-weighted/test/ManagedPool.test.ts b/pkg/pool-weighted/test/ManagedPool.test.ts index acaf7fb445..632707bc66 100644 --- a/pkg/pool-weighted/test/ManagedPool.test.ts +++ b/pkg/pool-weighted/test/ManagedPool.test.ts @@ -21,6 +21,7 @@ import { JoinResult, RawManagedPoolDeployment, SwapResult, + ManagedPoolType, } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { PoolSpecialization, SwapKind } from '@balancer-labs/balancer-js'; @@ -74,7 +75,7 @@ describe('ManagedPool', function () { weights: poolWeights, owner: owner.address, aumFeeId: ProtocolFee.AUM, - mockContractName: 'MockManagedPool', + poolType: ManagedPoolType.MockManagedPool, poolVersion, ...overrides, }; diff --git a/pkg/pool-weighted/test/ManagedPoolSettings.test.ts b/pkg/pool-weighted/test/ManagedPoolSettings.test.ts index 1d90fec89b..3019521a53 100644 --- a/pkg/pool-weighted/test/ManagedPoolSettings.test.ts +++ b/pkg/pool-weighted/test/ManagedPoolSettings.test.ts @@ -27,7 +27,7 @@ import { deploy } from '@balancer-labs/v2-helpers/src/contract'; import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; import ManagedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/ManagedPool'; -import { CircuitBreakerState } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; +import { CircuitBreakerState, ManagedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { expectEqualWithError } from '@balancer-labs/v2-helpers/src/test/relativeError'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address'; import { toNormalizedWeights } from '@balancer-labs/balancer-js'; @@ -83,7 +83,7 @@ describe('ManagedPoolSettings', function () { const fullParams = { ...params, swapFeePercentage: INITIAL_SWAP_FEE, - mockContractName: 'MockManagedPoolSettings', + poolType: ManagedPoolType.MockManagedPoolSettings, }; return ManagedPool.create(fullParams); } @@ -177,6 +177,7 @@ describe('ManagedPoolSettings', function () { tokens: allTokens.subset(2), vault, aumFeeId, + poolType: ManagedPoolType.MockManagedPoolSettings, }); }); diff --git a/pkg/pool-weighted/test/managed/AddRemove.test.ts b/pkg/pool-weighted/test/managed/AddRemove.test.ts index a3f7a209c1..ddff288d81 100644 --- a/pkg/pool-weighted/test/managed/AddRemove.test.ts +++ b/pkg/pool-weighted/test/managed/AddRemove.test.ts @@ -16,6 +16,7 @@ import { random, range } from 'lodash'; import * as expectEvent from '@balancer-labs/v2-helpers/src/test/expectEvent'; import { ProtocolFee } from '@balancer-labs/v2-helpers/src/models/vault/types'; import { expectTransferEvent } from '@balancer-labs/v2-helpers/src/test/expectTransfer'; +import { ManagedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; describe('ManagedPoolSettings - add/remove token', () => { let vault: Vault; @@ -70,6 +71,7 @@ describe('ManagedPoolSettings - add/remove token', () => { swapEnabledOnStart: true, vault, managementAumFeePercentage: fp(0.1), // Non-zero so that some protocol AUM fees are charged + poolType: ManagedPoolType.MockManagedPool, }); return { pool, poolTokens }; diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts index 09efcc4f6e..9f9171fccf 100644 --- a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -11,6 +11,7 @@ import { ManagedPoolRights, GradualSwapFeeUpdateParams, CircuitBreakerState, + ManagedPoolType, } from './types'; import Vault from '../../vault/Vault'; import { deploy, deployedAt } from '../../../contract'; @@ -118,11 +119,11 @@ export default class ManagedPool extends WeightedPool { owner, pauseWindowDuration, bufferPeriodDuration, - mockContractName, + poolType, from, } = params; - if (mockContractName == 'MockManagedPoolSettings') { + if (poolType == ManagedPoolType.MockManagedPoolSettings) { return deploy('v2-pool-weighted/MockManagedPoolSettings', { args: [ { @@ -148,7 +149,7 @@ export default class ManagedPool extends WeightedPool { }); } - return deploy(mockContractName ?? 'v2-pool-weighted/ManagedPool', { + return deploy('v2-pool-weighted/' + poolType, { args: [ { name: NAME, diff --git a/pvt/helpers/src/models/pools/weighted/types.ts b/pvt/helpers/src/models/pools/weighted/types.ts index 469709d188..c67844a31e 100644 --- a/pvt/helpers/src/models/pools/weighted/types.ts +++ b/pvt/helpers/src/models/pools/weighted/types.ts @@ -8,6 +8,12 @@ import TokenList from '../../tokens/TokenList'; import { Account, NAry } from '../../types/types'; import Vault from '../../vault/Vault'; +export enum ManagedPoolType { + ManagedPool = "ManagedPool", + MockManagedPool = "MockManagedPool", + MockManagedPoolSettings = "MockManagedPoolSettings", +} + export type RawWeightedPoolDeployment = { tokens?: TokenList; weights?: BigNumberish[]; @@ -79,7 +85,7 @@ export type RawManagedPoolDeployment = { from?: SignerWithAddress; vault?: Vault; fromFactory?: boolean; - mockContractName?: string; + poolType?: ManagedPoolType; factoryVersion?: string; poolVersion?: string; }; @@ -101,7 +107,7 @@ export type ManagedPoolDeployment = { owner: Account; admin?: SignerWithAddress; from?: SignerWithAddress; - mockContractName?: string; + poolType?: ManagedPoolType; }; export type SwapWeightedPool = { diff --git a/pvt/helpers/src/models/types/TypesConverter.ts b/pvt/helpers/src/models/types/TypesConverter.ts index 708d2b3018..55b55ed3b4 100644 --- a/pvt/helpers/src/models/types/TypesConverter.ts +++ b/pvt/helpers/src/models/types/TypesConverter.ts @@ -18,6 +18,7 @@ import { LiquidityBootstrappingPoolDeployment, RawManagedPoolDeployment, ManagedPoolDeployment, + ManagedPoolType, } from '../pools/weighted/types'; import { RawTokenApproval, @@ -132,6 +133,7 @@ export default { managementAumFeePercentage, factoryVersion, poolVersion, + poolType, } = params; if (!params.owner) params.owner = ZERO_ADDRESS; if (!tokens) tokens = new TokenList(); @@ -142,6 +144,7 @@ export default { if (!assetManagers) assetManagers = Array(tokens.length).fill(ZERO_ADDRESS); if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; + if (!poolType) poolType = ManagedPoolType.ManagedPool; if (swapEnabledOnStart == undefined) swapEnabledOnStart = true; if (undefined == aumFeeId) aumFeeId = ProtocolFee.AUM; if (undefined == swapEnabledOnStart) swapEnabledOnStart = true; @@ -161,7 +164,7 @@ export default { managementAumFeePercentage, pauseWindowDuration, bufferPeriodDuration, - mockContractName: params.mockContractName, + poolType: params.poolType, factoryVersion, poolVersion, owner: params.owner, From 5756a527ef471c1b17f0c2beb4fcdf7088494733 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Fri, 6 Jan 2023 16:42:51 -0500 Subject: [PATCH 11/20] lint: single vs double quotes --- pvt/helpers/src/models/pools/weighted/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvt/helpers/src/models/pools/weighted/types.ts b/pvt/helpers/src/models/pools/weighted/types.ts index c67844a31e..f9b50c0c78 100644 --- a/pvt/helpers/src/models/pools/weighted/types.ts +++ b/pvt/helpers/src/models/pools/weighted/types.ts @@ -9,9 +9,9 @@ import { Account, NAry } from '../../types/types'; import Vault from '../../vault/Vault'; export enum ManagedPoolType { - ManagedPool = "ManagedPool", - MockManagedPool = "MockManagedPool", - MockManagedPoolSettings = "MockManagedPoolSettings", + ManagedPool = 'ManagedPool', + MockManagedPool = 'MockManagedPool', + MockManagedPoolSettings = 'MockManagedPoolSettings', } export type RawWeightedPoolDeployment = { From 929e73c80ea72d8ccaf23b60fb2e5476366d999a Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Fri, 6 Jan 2023 17:30:47 -0500 Subject: [PATCH 12/20] refactor: consistent capitalization --- pkg/pool-weighted/test/ManagedPool.test.ts | 2 +- pkg/pool-weighted/test/managed/AddRemove.test.ts | 2 +- pvt/helpers/src/models/pools/weighted/types.ts | 6 +++--- pvt/helpers/src/models/types/TypesConverter.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/pool-weighted/test/ManagedPool.test.ts b/pkg/pool-weighted/test/ManagedPool.test.ts index 632707bc66..3b4a9a1dd4 100644 --- a/pkg/pool-weighted/test/ManagedPool.test.ts +++ b/pkg/pool-weighted/test/ManagedPool.test.ts @@ -75,7 +75,7 @@ describe('ManagedPool', function () { weights: poolWeights, owner: owner.address, aumFeeId: ProtocolFee.AUM, - poolType: ManagedPoolType.MockManagedPool, + poolType: ManagedPoolType.MOCK_MANAGED_POOL, poolVersion, ...overrides, }; diff --git a/pkg/pool-weighted/test/managed/AddRemove.test.ts b/pkg/pool-weighted/test/managed/AddRemove.test.ts index ddff288d81..c928663dce 100644 --- a/pkg/pool-weighted/test/managed/AddRemove.test.ts +++ b/pkg/pool-weighted/test/managed/AddRemove.test.ts @@ -71,7 +71,7 @@ describe('ManagedPoolSettings - add/remove token', () => { swapEnabledOnStart: true, vault, managementAumFeePercentage: fp(0.1), // Non-zero so that some protocol AUM fees are charged - poolType: ManagedPoolType.MockManagedPool, + poolType: ManagedPoolType.MOCK_MANAGED_POOL, }); return { pool, poolTokens }; diff --git a/pvt/helpers/src/models/pools/weighted/types.ts b/pvt/helpers/src/models/pools/weighted/types.ts index f9b50c0c78..1b3e8c9d2f 100644 --- a/pvt/helpers/src/models/pools/weighted/types.ts +++ b/pvt/helpers/src/models/pools/weighted/types.ts @@ -9,9 +9,9 @@ import { Account, NAry } from '../../types/types'; import Vault from '../../vault/Vault'; export enum ManagedPoolType { - ManagedPool = 'ManagedPool', - MockManagedPool = 'MockManagedPool', - MockManagedPoolSettings = 'MockManagedPoolSettings', + MANAGED_POOL = 'ManagedPool', + MOCK_MANAGED_POOL = 'MockManagedPool', + MOCK_MANAGED_POOL_SETTINGS = 'MockManagedPoolSettings', } export type RawWeightedPoolDeployment = { diff --git a/pvt/helpers/src/models/types/TypesConverter.ts b/pvt/helpers/src/models/types/TypesConverter.ts index 55b55ed3b4..0db0e64e5d 100644 --- a/pvt/helpers/src/models/types/TypesConverter.ts +++ b/pvt/helpers/src/models/types/TypesConverter.ts @@ -144,7 +144,7 @@ export default { if (!assetManagers) assetManagers = Array(tokens.length).fill(ZERO_ADDRESS); if (!pauseWindowDuration) pauseWindowDuration = DEFAULT_PAUSE_WINDOW_DURATION; if (!bufferPeriodDuration) bufferPeriodDuration = DEFAULT_BUFFER_PERIOD_DURATION; - if (!poolType) poolType = ManagedPoolType.ManagedPool; + if (!poolType) poolType = ManagedPoolType.MANAGED_POOL; if (swapEnabledOnStart == undefined) swapEnabledOnStart = true; if (undefined == aumFeeId) aumFeeId = ProtocolFee.AUM; if (undefined == swapEnabledOnStart) swapEnabledOnStart = true; From 22fe0f296fde30e190d83f4e9e3808f8653a9f71 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Fri, 6 Jan 2023 17:33:27 -0500 Subject: [PATCH 13/20] fix: consistent capitalization --- pkg/pool-weighted/test/ManagedPoolSettings.test.ts | 4 ++-- pvt/helpers/src/models/pools/weighted/ManagedPool.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/pool-weighted/test/ManagedPoolSettings.test.ts b/pkg/pool-weighted/test/ManagedPoolSettings.test.ts index 3019521a53..098386a916 100644 --- a/pkg/pool-weighted/test/ManagedPoolSettings.test.ts +++ b/pkg/pool-weighted/test/ManagedPoolSettings.test.ts @@ -83,7 +83,7 @@ describe('ManagedPoolSettings', function () { const fullParams = { ...params, swapFeePercentage: INITIAL_SWAP_FEE, - poolType: ManagedPoolType.MockManagedPoolSettings, + poolType: ManagedPoolType.MOCK_MANAGED_POOL_SETTINGS, }; return ManagedPool.create(fullParams); } @@ -177,7 +177,7 @@ describe('ManagedPoolSettings', function () { tokens: allTokens.subset(2), vault, aumFeeId, - poolType: ManagedPoolType.MockManagedPoolSettings, + poolType: ManagedPoolType.MOCK_MANAGED_POOL_SETTINGS, }); }); diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts index 9f9171fccf..a572ab98f2 100644 --- a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -123,7 +123,7 @@ export default class ManagedPool extends WeightedPool { from, } = params; - if (poolType == ManagedPoolType.MockManagedPoolSettings) { + if (poolType == ManagedPoolType.MOCK_MANAGED_POOL_SETTINGS) { return deploy('v2-pool-weighted/MockManagedPoolSettings', { args: [ { From 15fd5f17d179e7e18b5391f80cb1267dacac4cb0 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Tue, 7 Mar 2023 00:29:16 -0500 Subject: [PATCH 14/20] checkpoint --- .../test/BaseWeightedPool.behavior.ts | 35 +++++++++++++------ .../test/LiquidityBootstrappingPool.test.ts | 1 + .../src/models/pools/weighted/types.ts | 5 +++ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts b/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts index 42f112fbeb..21788a3d8f 100644 --- a/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts +++ b/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts @@ -7,13 +7,15 @@ import { BigNumberish, bn, fp, fpMul, pct } from '@balancer-labs/v2-helpers/src/ import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import WeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/WeightedPool'; -import { RawWeightedPoolDeployment } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; +import { RawWeightedPoolDeployment, WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; import { ZERO_ADDRESS } from '@balancer-labs/v2-helpers/src/constants'; import { sharedBeforeEach } from '@balancer-labs/v2-common/sharedBeforeEach'; import { expectBalanceChange } from '@balancer-labs/v2-helpers/src/test/tokenBalance'; import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault'; +import LiquidityBootstrappingPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/LiquidityBootstrappingPool'; +import BaseWeightedPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/BaseWeightedPool'; -export function itBehavesAsWeightedPool(numberOfTokens: number): void { +export function itBehavesAsWeightedPool(numberOfTokens: number, poolType: WeightedPoolType): void { const POOL_SWAP_FEE_PERCENTAGE = fp(0.01); const WEIGHTS = [fp(30), fp(70), fp(5), fp(5)]; const INITIAL_BALANCES = [fp(0.9), fp(1.8), fp(2.7), fp(3.6)]; @@ -22,21 +24,32 @@ export function itBehavesAsWeightedPool(numberOfTokens: number): void { let recipient: SignerWithAddress, other: SignerWithAddress, lp: SignerWithAddress; let vault: Vault; - let pool: WeightedPool, allTokens: TokenList, tokens: TokenList; + let pool: BaseWeightedPool, allTokens: TokenList, tokens: TokenList; const ZEROS = Array(numberOfTokens).fill(bn(0)); const weights: BigNumberish[] = WEIGHTS.slice(0, numberOfTokens); const initialBalances = INITIAL_BALANCES.slice(0, numberOfTokens); async function deployPool(params: RawWeightedPoolDeployment = {}): Promise { - pool = await WeightedPool.create({ - vault, - tokens, - weights, - swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, - owner: lp, // needed for LBP tests - ...params, - }); + if (poolType == WeightedPoolType.WEIGHTED_POOL) { + pool = await WeightedPool.create({ + vault, + tokens, + weights, + swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, + owner: lp, // needed for LBP tests + ...params, + }); + } else if (poolType == WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL) { + pool = await LiquidityBootstrappingPool.create({ + vault, + tokens, + weights, + swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, + owner: lp, // needed for LBP tests + ...params, + }); + } } before('setup signers', async () => { diff --git a/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts b/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts index becd318283..c1071fc7b1 100644 --- a/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts +++ b/pkg/pool-weighted/test/LiquidityBootstrappingPool.test.ts @@ -10,6 +10,7 @@ import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList'; import LiquidityBootstrappingPool from '@balancer-labs/v2-helpers/src/models/pools/weighted/LiquidityBootstrappingPool'; import { range } from 'lodash'; import { itBehavesAsWeightedPool } from './BaseWeightedPool.behavior'; +import { WeightedPoolType } from '@balancer-labs/v2-helpers/src/models/pools/weighted/types'; describe('LiquidityBootstrappingPool', function () { let owner: SignerWithAddress, other: SignerWithAddress; diff --git a/pvt/helpers/src/models/pools/weighted/types.ts b/pvt/helpers/src/models/pools/weighted/types.ts index 1b3e8c9d2f..b7125d99fd 100644 --- a/pvt/helpers/src/models/pools/weighted/types.ts +++ b/pvt/helpers/src/models/pools/weighted/types.ts @@ -8,6 +8,11 @@ import TokenList from '../../tokens/TokenList'; import { Account, NAry } from '../../types/types'; import Vault from '../../vault/Vault'; +export enum WeightedPoolType { + WEIGHTED_POOL = 0, + LIQUIDITY_BOOTSTRAPPING_POOL, +} + export enum ManagedPoolType { MANAGED_POOL = 'ManagedPool', MOCK_MANAGED_POOL = 'MockManagedPool', From 48cdb022604c219366dbee93d968632c5fb5485d Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Wed, 22 Mar 2023 16:14:39 -0400 Subject: [PATCH 15/20] lint --- pvt/helpers/src/models/pools/weighted/WeightedPool.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pvt/helpers/src/models/pools/weighted/WeightedPool.ts b/pvt/helpers/src/models/pools/weighted/WeightedPool.ts index 80a1ea6114..40b63a6eb1 100644 --- a/pvt/helpers/src/models/pools/weighted/WeightedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/WeightedPool.ts @@ -88,7 +88,16 @@ export default class WeightedPool extends BaseWeightedPool { from, }); - const tx = await factory.create(NAME, SYMBOL, tokens.addresses, weights, rateProviders, swapFeePercentage, owner, randomBytes(32)); + const tx = await factory.create( + NAME, + SYMBOL, + tokens.addresses, + weights, + rateProviders, + swapFeePercentage, + owner, + randomBytes(32) + ); const receipt = await tx.wait(); const event = expectEvent.inReceipt(receipt, 'PoolCreated'); return deployedAt('v2-pool-weighted/WeightedPool', event.args.pool); From c0a82f692e8c5a146f9038d5f923fbbd286ee27d Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Wed, 22 Mar 2023 18:15:44 -0400 Subject: [PATCH 16/20] fix: overload TokenList.indicesOf --- pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts | 4 ++-- pvt/helpers/src/models/tokens/TokenList.ts | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts b/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts index 26c586f56b..aaf4450fb1 100644 --- a/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/BaseWeightedPool.ts @@ -105,7 +105,7 @@ export default class BaseWeightedPool extends BasePool { async estimateGivenIn(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { if (!currentBalances) currentBalances = await this.getBalances(); - const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); + const [tokenIn, tokenOut] = this.tokens.indicesOfTwoTokens(params.in, params.out); return bn( calcOutGivenIn( @@ -120,7 +120,7 @@ export default class BaseWeightedPool extends BasePool { async estimateGivenOut(params: SwapWeightedPool, currentBalances?: BigNumberish[]): Promise { if (!currentBalances) currentBalances = await this.getBalances(); - const [tokenIn, tokenOut] = this.tokens.indicesOf(params.in, params.out); + const [tokenIn, tokenOut] = this.tokens.indicesOfTwoTokens(params.in, params.out); return bn( calcInGivenOut( diff --git a/pvt/helpers/src/models/tokens/TokenList.ts b/pvt/helpers/src/models/tokens/TokenList.ts index 314b69a36f..43aa3e8565 100644 --- a/pvt/helpers/src/models/tokens/TokenList.ts +++ b/pvt/helpers/src/models/tokens/TokenList.ts @@ -82,7 +82,11 @@ export default class TokenList { return typeof token === 'number' ? token : this.tokens.indexOf(token); } - indicesOf(token: number | Token, anotherToken: number | Token): number[] { + indicesOf(tokens: (number | Token)[]): number[] { + return tokens.map((token) => this.indexOf(token)); + } + + indicesOfTwoTokens(token: number | Token, anotherToken: number | Token): number[] { return [this.indexOf(token), this.indexOf(anotherToken)]; } From c7af7b0776026f5aa3727304f580266bc316b112 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Mon, 24 Apr 2023 20:02:52 -0400 Subject: [PATCH 17/20] lint --- .../src/models/pools/weighted/ManagedPool.ts | 36 +++++-------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts index 38e0636fff..35165161b7 100644 --- a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -129,24 +129,6 @@ export default class ManagedPool extends WeightedPool { } = params; if (poolType == ManagedPoolType.MOCK_MANAGED_POOL_SETTINGS) { - const args = [ - { - tokens: tokens.addresses, - normalizedWeights: weights, - swapFeePercentage: swapFeePercentage, - swapEnabledOnStart: swapEnabledOnStart, - mustAllowlistLPs: mustAllowlistLPs, - managementAumFeePercentage: managementAumFeePercentage, - aumFeeId: aumFeeId, - }, - vault.address, - vault.protocolFeesProvider.address, - ManagedPool.weightedMathLib.address, - ManagedPool.recoverModeHelperLib.address, - assetManagers, - owner, - ]; - return deploy('v2-pool-weighted/MockManagedPoolSettings', { args: [ { @@ -233,7 +215,7 @@ export default class ManagedPool extends WeightedPool { factoryVersion, poolVersion, PAUSE_WINDOW_DURATION, - BUFFER_PERIOD_DURATION + BUFFER_PERIOD_DURATION, ], from, libraries: { @@ -294,15 +276,15 @@ export default class ManagedPool extends WeightedPool { const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool);*/ - const salt = randomBytes(32); - - const tx = await factory - .connect(from || ZERO_ADDRESS) - .create(poolParams, settingsParams, from?.address || ZERO_ADDRESS, salt); - const receipt = await tx.wait(); - const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); + const salt = randomBytes(32); + + const tx = await factory + .connect(from || ZERO_ADDRESS) + .create(poolParams, settingsParams, from?.address || ZERO_ADDRESS, salt); + const receipt = await tx.wait(); + const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); - return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool); + return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool); } async updateWeightsGradually( From e53fbf7078609350c4e03e4eb3efcfd36855eb12 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 27 Apr 2023 12:25:45 -0400 Subject: [PATCH 18/20] docs: cleanup/clarify comments --- .../test/BaseWeightedPool.behavior.ts | 3 +- .../src/models/pools/weighted/ManagedPool.ts | 36 ------------------- .../src/models/pools/weighted/types.ts | 1 + 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts b/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts index 0231c914d5..17427c33a1 100644 --- a/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts +++ b/pkg/pool-weighted/test/BaseWeightedPool.behavior.ts @@ -37,7 +37,6 @@ export function itBehavesAsWeightedPool(numberOfTokens: number, poolType: Weight tokens, weights, swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, - owner: lp.address, // needed for LBP tests ...params, }); } else if (poolType == WeightedPoolType.LIQUIDITY_BOOTSTRAPPING_POOL) { @@ -46,7 +45,7 @@ export function itBehavesAsWeightedPool(numberOfTokens: number, poolType: Weight tokens, weights, swapFeePercentage: POOL_SWAP_FEE_PERCENTAGE, - owner: lp.address, // needed for LBP tests + owner: lp.address, // needed for LBP tests (only owner can join) ...params, }); } diff --git a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts index 35165161b7..463cd3a195 100644 --- a/pvt/helpers/src/models/pools/weighted/ManagedPool.ts +++ b/pvt/helpers/src/models/pools/weighted/ManagedPool.ts @@ -240,42 +240,6 @@ export default class ManagedPool extends WeightedPool { aumFeeId: aumFeeId ?? ProtocolFee.AUM, }; - /*const newPoolParams: ManagedPoolParams = { - name: NAME, - symbol: SYMBOL, - tokens: tokens.addresses, - normalizedWeights: weights, - assetManagers: assetManagers, - swapFeePercentage: swapFeePercentage, - swapEnabledOnStart: swapEnabledOnStart, - mustAllowlistLPs: mustAllowlistLPs, - managementAumFeePercentage: managementAumFeePercentage, - aumFeeId: aumFeeId ?? ProtocolFee.AUM, - }; - - const basePoolRights: BasePoolRights = { - canTransferOwnership: true, - canChangeSwapFee: true, - canUpdateMetadata: true, - }; - - const managedPoolRights: ManagedPoolRights = { - canChangeWeights: true, - canDisableSwaps: true, - canSetMustAllowlistLPs: true, - canSetCircuitBreakers: true, - canChangeTokens: true, - canChangeMgmtFees: true, - canDisableJoinExit: true, - }; - - const tx = await controlledFactory - .connect(from || ZERO_ADDRESS) - .create(newPoolParams, basePoolRights, managedPoolRights, DAY, from?.address || ZERO_ADDRESS); - const receipt = await tx.wait(); - const event = expectEvent.inReceipt(receipt, 'ManagedPoolCreated'); - return deployedAt('v2-pool-weighted/ManagedPool', event.args.pool);*/ - const salt = randomBytes(32); const tx = await factory diff --git a/pvt/helpers/src/models/pools/weighted/types.ts b/pvt/helpers/src/models/pools/weighted/types.ts index 2352d9b481..ebeef3a537 100644 --- a/pvt/helpers/src/models/pools/weighted/types.ts +++ b/pvt/helpers/src/models/pools/weighted/types.ts @@ -13,6 +13,7 @@ export enum WeightedPoolType { LIQUIDITY_BOOTSTRAPPING_POOL, } +// These names are used in the helpers to fetch the artifacts export enum ManagedPoolType { MANAGED_POOL = 'ManagedPool', MOCK_MANAGED_POOL = 'MockManagedPool', From 51d4861771b064f8cc7deb97ca9df4c859004799 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 27 Apr 2023 12:26:59 -0400 Subject: [PATCH 19/20] allow WeightedPool to be created with all flavors of Account --- pvt/helpers/src/models/types/TypesConverter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvt/helpers/src/models/types/TypesConverter.ts b/pvt/helpers/src/models/types/TypesConverter.ts index 293258c445..609a9e3a0c 100644 --- a/pvt/helpers/src/models/types/TypesConverter.ts +++ b/pvt/helpers/src/models/types/TypesConverter.ts @@ -88,7 +88,7 @@ export default { swapFeePercentage, pauseWindowDuration, bufferPeriodDuration, - owner: params.owner, + owner: this.toAddress(params.owner), from: params.from, }; }, From 8dc2c67905a3ec8845c51ffedc81116d628231d7 Mon Sep 17 00:00:00 2001 From: Jeffrey Bennett Date: Thu, 27 Apr 2023 12:27:23 -0400 Subject: [PATCH 20/20] create WeightedPool with signer owner --- pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts b/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts index 50d5a242f0..475dfd52b4 100644 --- a/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts +++ b/pkg/standalone-utils/test/ProtocolFeeSplitter.test.ts @@ -47,7 +47,7 @@ describe('ProtocolFeeSplitter', function () { }); sharedBeforeEach('create and initialize pools', async () => { - pool = await WeightedPool.create({ vault, tokens, owner: owner.address }); + pool = await WeightedPool.create({ vault, tokens, owner }); poolId = await pool.getPoolId(); const initialBalances = Array(tokens.length).fill(fp(1000));