Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Managed Pool: BPT management fees #1163

Merged
merged 80 commits into from
Apr 6, 2022
Merged
Changes from 1 commit
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
8217ac5
Remove protocol fees from WeightedPools (temporarily)
nventuro Jan 26, 2022
4ca6146
Add new BPT fee math function
nventuro Jan 28, 2022
0525f97
Add protocol fees back to WeightedPool
nventuro Jan 28, 2022
a353515
Fix init, restore some tests
nventuro Jan 31, 2022
3f357a7
Remove extra state
nventuro Jan 31, 2022
ae9a01d
Add protocol fees to oracle pool
nventuro Jan 31, 2022
99ac9cd
Remove extra state
nventuro Jan 31, 2022
d48fc28
Add last invariant update tests
nventuro Feb 2, 2022
c4fcc41
Fix pause detection, add final tests
nventuro Feb 2, 2022
cfc86c2
Remove extra console.log
nventuro Feb 2, 2022
c0e344c
Remove unnecessary test from ManagedPool
nventuro Feb 2, 2022
0d8eaca
Fix before hook
nventuro Feb 2, 2022
2eecfab
Add and fix oracle tests
nventuro Feb 2, 2022
ddfb092
Merge branch 'master' into weighted-protocol-fees
nventuro Feb 2, 2022
58c4c01
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Feb 11, 2022
83b2568
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Feb 11, 2022
f4335b5
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 1, 2022
601dc54
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 5, 2022
a4a76f6
Remove token collected management fees
EndymionJkb Mar 5, 2022
af7267e
Remove obsolete management fees; put in skeleton for protocol fee tests
EndymionJkb Mar 5, 2022
db15a3a
Cache protocol fee swap percentage, so that it is available during swaps
EndymionJkb Mar 5, 2022
f63ce39
First cut protocol + mgmt fee payment in BPT
EndymionJkb Mar 6, 2022
48489f7
Adjust Managed Pool controller to withdraw by transferring BPT out of…
EndymionJkb Mar 6, 2022
eceba96
lint
EndymionJkb Mar 6, 2022
7c4f3bc
Replace processSwapFeeAmount with processTaxableSwapAmounts
EndymionJkb Mar 6, 2022
b9ed7b0
Generalize _payProtocolAndManagementFees to work for regular and join…
EndymionJkb Mar 6, 2022
03de464
Fixup names
EndymionJkb Mar 7, 2022
7ffae35
Remove gas limits so all tests pass
EndymionJkb Mar 7, 2022
2f14310
one more
EndymionJkb Mar 7, 2022
a6131b9
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 10, 2022
cdb4359
Merge branch 'weighted-protocol-fees' into bpt-mgmt-fee
EndymionJkb Mar 10, 2022
53e30f6
Add protocol fee switch, for security pools that cannot pay protocol …
EndymionJkb Mar 11, 2022
af7d6e3
lint
EndymionJkb Mar 13, 2022
72d43be
Add comment
EndymionJkb Mar 13, 2022
a1e5f1b
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 14, 2022
ad6bb72
Merge branch 'weighted-protocol-fees' into bpt-mgmt-fee
EndymionJkb Mar 14, 2022
8e53786
Remove code to charge fees on joinSwaps and exitSwaps (restore original)
EndymionJkb Mar 14, 2022
2c0a50c
Remove unnecessary callbacks
EndymionJkb Mar 14, 2022
649bdd2
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 14, 2022
61ecba0
Merge branch 'weighted-protocol-fees' into bpt-mgmt-fee
EndymionJkb Mar 14, 2022
60eaced
Retrieve baby from bath water
EndymionJkb Mar 15, 2022
d11f7c7
Reorganize parameters (simplify API)
EndymionJkb Mar 15, 2022
c848092
lint
EndymionJkb Mar 15, 2022
7c8b1c5
Fix deployment in benchmarks
EndymionJkb Mar 15, 2022
dcf0e5e
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 16, 2022
d2551cc
Merge branch 'weighted-protocol-fees' into bpt-mgmt-fee
EndymionJkb Mar 16, 2022
cf65c9a
intermittent stable pool test failure
EndymionJkb Mar 16, 2022
bb45c54
Review comments; save bytecode with complement()
EndymionJkb Mar 16, 2022
5788c13
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 16, 2022
8322bad
Merge branch 'weighted-protocol-fees' into bpt-mgmt-fee
EndymionJkb Mar 16, 2022
f061ff7
Apply suggestions from code review
nventuro Mar 17, 2022
47ff6a2
Update pkg/pool-weighted/contracts/WeightedMath.sol
nventuro Mar 17, 2022
4f0f20a
Review comments
EndymionJkb Mar 17, 2022
bac06a5
Merge branch 'master' into weighted-protocol-fees
EndymionJkb Mar 17, 2022
29078df
Fix SafeMath issue
EndymionJkb Mar 17, 2022
233c0ec
Merge branch 'weighted-protocol-fees' into bpt-mgmt-fee
EndymionJkb Mar 17, 2022
ee47f58
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 18, 2022
b0e7101
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 19, 2022
880f7a8
rerun
EndymionJkb Mar 19, 2022
36bbd5d
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 22, 2022
c169fe9
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 22, 2022
d54b098
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 24, 2022
75d21ff
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 26, 2022
38897d9
Generalize protocol swap fee handling
EndymionJkb Mar 26, 2022
7afcc6d
Remove unlimitedContractSize
EndymionJkb Mar 28, 2022
534ff45
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 30, 2022
3cf9b3a
Review updates
EndymionJkb Mar 30, 2022
36e8b64
Slight test improvements
EndymionJkb Mar 30, 2022
af396c5
lint
EndymionJkb Mar 30, 2022
3ab533c
rerun
EndymionJkb Mar 30, 2022
5430213
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Mar 31, 2022
0156afa
Simplify swap logic - avoid overriding onSwap
EndymionJkb Apr 1, 2022
1013dbe
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Apr 4, 2022
01a7f79
Remove BaseWeightedPool duplication
EndymionJkb Apr 4, 2022
c301f85
Remove unnecessary modifiers and a stale comment
EndymionJkb Apr 5, 2022
809f0e9
Account for swap fee in GivenIn calculation
EndymionJkb Apr 5, 2022
9a1e48a
Simplify balance calculations; add tests
EndymionJkb Apr 5, 2022
f4b9217
Add description to test
EndymionJkb Apr 5, 2022
39ac6a1
Merge branch 'master' into bpt-mgmt-fee
EndymionJkb Apr 6, 2022
3775bf5
Remove stale comments
EndymionJkb Apr 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Simplify balance calculations; add tests
  • Loading branch information
EndymionJkb committed Apr 5, 2022
commit 9a1e48aa6ecabd06f5d9f1939c03638f2f5dd77a
4 changes: 1 addition & 3 deletions pkg/pool-weighted/contracts/smart/ManagedPool.sol
Original file line number Diff line number Diff line change
@@ -487,11 +487,9 @@ contract ManagedPool is BaseWeightedPool, ReentrancyGuard {

// balances (and swapRequest.amount) are already upscaled by BaseMinimalSwapInfoPool.onSwap
uint256 amountIn = super._onSwapGivenOut(swapRequest, currentBalanceTokenIn, currentBalanceTokenOut);
uint256 scalingFactorTokenIn = _scalingFactor(swapRequest.tokenIn);
uint256 unscaledAmountIn = _addSwapFeeAmount(_downscaleUp(amountIn, scalingFactorTokenIn));

uint256[] memory postSwapBalances = ArrayHelpers.arrayFill(
currentBalanceTokenIn.add(_upscale(unscaledAmountIn, scalingFactorTokenIn)),
currentBalanceTokenIn.add(_addSwapFeeAmount(amountIn)),
currentBalanceTokenOut.sub(swapRequest.amount)
);

158 changes: 91 additions & 67 deletions pkg/pool-weighted/test/ManagedPool.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { BigNumber, Contract } from 'ethers';
import { fp, pct } from '@balancer-labs/v2-helpers/src/numbers';
import { bn, fp, fromFp, pct } from '@balancer-labs/v2-helpers/src/numbers';
import { MINUTE, DAY, advanceTime, currentTimestamp, WEEK } from '@balancer-labs/v2-helpers/src/time';
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 { MAX_UINT256, ZERO_ADDRESS } from '@balancer-labs/v2-helpers/src/constants';
import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault';
@@ -694,20 +694,30 @@ describe('ManagedPool', function () {
const swapFeePercentage = fp(0.02);
const protocolFeePercentage = fp(0.5); // 50 %
const managementSwapFeePercentage = fp(0); // Set to zero to isolate BPT fees
const tokenAmount = 100;
const poolWeights = [fp(0.8), fp(0.2)];
let bptFeeBalance: BigNumber;
// let mockMath: Contract;
let mockMath: Contract;

const localBalances = Array(2).fill(fp(1000));
let twoTokens: TokenList;
let localBalances: Array<BigNumber>;
let swapAmount: BigNumber;

sharedBeforeEach('deploy pool', async () => {
vault = await Vault.create({ admin });
await vault.setSwapFeePercentage(protocolFeePercentage, { from: admin });
protocolFeesCollector = await vault.getFeesCollector();

twoTokens = poolTokens.subset(2);
localBalances = [bn(tokenAmount * 10 ** twoTokens.first.decimals), bn(100 * 10 ** twoTokens.second.decimals)];

// 10% of the initial balance
swapAmount = localBalances[0].div(10);

// Make a 2-token pool for this purpose
const params = {
tokens: poolTokens.subset(2),
weights: [fp(0.8), fp(0.2)],
tokens: twoTokens,
weights: poolWeights,
owner: owner.address,
poolType: WeightedPoolType.MANAGED_POOL,
swapEnabledOnStart: true,
@@ -716,7 +726,7 @@ describe('ManagedPool', function () {
managementSwapFeePercentage,
};
pool = await WeightedPool.create(params);
// mockMath = await deploy('MockWeightedMath');
mockMath = await deploy('MockWeightedMath');
});

sharedBeforeEach('initialize pool', async () => {
@@ -731,7 +741,17 @@ describe('ManagedPool', function () {
expect(bptFeeBalance).to.equal(0);
});

describe('pays protocol fees on swap', () => {
describe('pays protocol fees on swaps', () => {
let upscaledBalances: Array<BigNumber>;
let upscaledSwapAmount: BigNumber;

sharedBeforeEach('', async () => {
const scaleFactor0 = 10 ** (18 - twoTokens.first.decimals);
const scaleFactor1 = 10 ** (18 - twoTokens.second.decimals);
upscaledBalances = [localBalances[0].mul(scaleFactor0), localBalances[1].mul(scaleFactor1)];
upscaledSwapAmount = swapAmount.mul(scaleFactor0);
});

it('charges the expected protocol fee', async () => {
const actualProtocolFee = await protocolFeesCollector.getSwapFeePercentage();
expect(actualProtocolFee).to.equal(protocolFeePercentage);
@@ -744,7 +764,7 @@ describe('ManagedPool', function () {
kind: SwapKind.GivenIn,
assetIn: poolTokens.first.address,
assetOut: poolTokens.second.address,
amount: fp(100),
amount: swapAmount,
userData: '0x',
};
const funds = {
@@ -756,11 +776,33 @@ describe('ManagedPool', function () {
const limit = 0; // Minimum amount out
const deadline = MAX_UINT256;

const prevInvariant = await mockMath.invariant(poolWeights, upscaledBalances);

const adjustedAmountIn = upscaledSwapAmount.mul(fp(1).sub(swapFeePercentage)).div(fp(1));
const amountOut = await mockMath.outGivenIn(
upscaledBalances[0],
poolWeights[0],
upscaledBalances[1],
poolWeights[1],
adjustedAmountIn
);

const postBalances = [upscaledBalances[0].add(upscaledSwapAmount), upscaledBalances[1].sub(amountOut)];
const postInvariant = await mockMath.invariant(poolWeights, postBalances);
const totalSupply = await pool.totalSupply();

const expectedProtocolFees = await mockMath.calculateDueProtocolSwapFeeBPTAmount(
totalSupply,
prevInvariant,
postInvariant,
protocolFeePercentage
);

await vault.instance.connect(owner).swap(singleSwap, funds, limit, deadline);

bptFeeBalance = await pool.balanceOf(protocolFeesCollector.address);

expect(bptFeeBalance).to.gt(0);
expect(bptFeeBalance).to.equalWithError(expectedProtocolFees, 0.000001);
});
});

@@ -771,7 +813,7 @@ describe('ManagedPool', function () {
kind: SwapKind.GivenOut,
assetIn: poolTokens.second.address,
assetOut: poolTokens.first.address,
amount: fp(100),
amount: swapAmount,
userData: '0x',
};
const funds = {
@@ -783,22 +825,52 @@ describe('ManagedPool', function () {
const limit = MAX_UINT256; // Maximum amount in
const deadline = MAX_UINT256;

const prevInvariant = await mockMath.invariant(poolWeights, upscaledBalances);

const amountIn = await mockMath.inGivenOut(
upscaledBalances[1],
poolWeights[1],
upscaledBalances[0],
poolWeights[0],
upscaledSwapAmount
);

// Has to be a better way to do this...
const proportion = fp(1).sub(swapFeePercentage);
const adjustedAmountIn = fp(fromFp(amountIn).toNumber() / fromFp(proportion).toNumber());

const postBalances = [
upscaledBalances[1].sub(upscaledSwapAmount),
upscaledBalances[0].add(adjustedAmountIn),
];
const postInvariant = await mockMath.invariant(poolWeights, postBalances);
const totalSupply = await pool.totalSupply();

const expectedProtocolFees = await mockMath.calculateDueProtocolSwapFeeBPTAmount(
totalSupply,
prevInvariant,
postInvariant,
protocolFeePercentage
);

await vault.instance.connect(owner).swap(singleSwap, funds, limit, deadline);

bptFeeBalance = await pool.balanceOf(protocolFeesCollector.address);

expect(bptFeeBalance).to.gt(0);
expect(bptFeeBalance).to.equalWithError(expectedProtocolFees, 0.000001);
});
});
});

describe('does not pay on join/exit', () => {
context('with balance changes', () => {
let currentBalances: BigNumber[];
let bptIn: BigNumber;

sharedBeforeEach('simulate doubled initial balances', async () => {
sharedBeforeEach('simulate increased initial balances', async () => {
// 4/3 of the initial balances
currentBalances = initialBalances.map((balance) => balance.mul(4).div(3));
bptIn = (await pool.balanceOf(owner)).div(10);
});

it('no protocol fees on join exact tokens in for BPT out', async () => {
@@ -811,7 +883,7 @@ describe('ManagedPool', function () {
it('no protocol fees on exit exact BPT in for one token out', async () => {
await pool.singleExitGivenIn({
from: owner,
bptIn: fp(0.5),
bptIn: bptIn,
token: 0,
currentBalances,
protocolFeePercentage,
@@ -825,7 +897,7 @@ describe('ManagedPool', function () {
it('no protocol fees on exit exact BPT in for all tokens out', async () => {
await pool.multiExitGivenIn({
from: owner,
bptIn: fp(1),
bptIn: bptIn,
currentBalances,
protocolFeePercentage,
});
@@ -836,10 +908,12 @@ describe('ManagedPool', function () {
});

it('no protocol fees on exit BPT In for exact tokens out', async () => {
const { balances } = await pool.getTokens();

await pool.exitGivenOut({
from: owner,
amountsOut: fp(1),
currentBalances,
amountsOut: [balances[0].div(5), balances[1].div(5)],
maximumBptIn: MAX_UINT256,
protocolFeePercentage,
});

@@ -911,56 +985,6 @@ describe('ManagedPool', function () {
});
});
});

describe.skip('fee collection', () => {
describe('swaps', () => {
it('collects management fees on swaps given in', async () => {
const singleSwap = {
poolId: await pool.getPoolId(),
kind: SwapKind.GivenIn,
assetIn: poolTokens.first.address,
assetOut: poolTokens.second.address,
amount: fp(0.01),
userData: '0x',
};
const funds = {
sender: owner.address,
fromInternalBalance: false,
recipient: other.address,
toInternalBalance: false,
};
const limit = 0; // Minimum amount out
const deadline = MAX_UINT256;

await vault.instance.connect(owner).swap(singleSwap, funds, limit, deadline);

expect(true).to.be.true;
});

it('collects management fees on swaps given out', async () => {
const singleSwap = {
poolId: await pool.getPoolId(),
kind: SwapKind.GivenOut,
assetIn: poolTokens.second.address,
assetOut: poolTokens.first.address,
amount: fp(0.01),
userData: '0x',
};
const funds = {
sender: owner.address,
fromInternalBalance: false,
recipient: other.address,
toInternalBalance: false,
};
const limit = MAX_UINT256; // Maximum amount in
const deadline = MAX_UINT256;

await vault.instance.connect(owner).swap(singleSwap, funds, limit, deadline);

expect(true).to.be.true;
});
});
});
});
});
});