From 9280c32f56cb1a591717d76988476ea337c70287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Tue, 6 Sep 2022 13:00:33 -0500 Subject: [PATCH] Add getActualSupply (#1704) * Add getActualSupply * Update pkg/pool-stable/test/ComposableStablePool.test.ts Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> * Update pkg/pool-stable/contracts/ComposableStablePool.sol Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> * Remove virtual supply Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .../contracts/ComposableStablePool.sol | 56 +++++++++---------- .../ComposableStablePoolProtocolFees.sol | 21 +------ .../test/ComposableStablePool.test.ts | 7 +++ 3 files changed, 34 insertions(+), 50 deletions(-) diff --git a/pkg/pool-stable/contracts/ComposableStablePool.sol b/pkg/pool-stable/contracts/ComposableStablePool.sol index 2d5116954b..dbb6a2aee3 100644 --- a/pkg/pool-stable/contracts/ComposableStablePool.sol +++ b/pkg/pool-stable/contracts/ComposableStablePool.sol @@ -997,28 +997,16 @@ contract ComposableStablePool is // accrued but are not yet minted: in calculating these we'll actually end up fetching most of the data we need // for the invariant. - // First we query the Vault for current registered balances (which includes preminted BPT), to then calculate - // the current scaled balances and virtual supply. - (, uint256[] memory registeredBalances, ) = getVault().getPoolTokens(getPoolId()); - _upscaleArray(registeredBalances, _scalingFactors()); - (uint256 virtualSupply, uint256[] memory balances) = _dropBptItemFromBalances(registeredBalances); - - // Now we need to calculate any BPT due in the form of protocol fees. This requires data from the last join or - // exit operation. - (uint256 lastJoinExitAmp, uint256 lastPostJoinExitInvariant) = getLastJoinExitData(); - ( - uint256 expectedProtocolOwnershipPercentage, + uint256[] memory balances, + uint256 virtualSupply, + uint256 protocolFeeAmount, + uint256 lastJoinExitAmp, uint256 currentInvariantWithLastJoinExitAmp - ) = _getProtocolPoolOwnershipPercentage(balances, lastJoinExitAmp, lastPostJoinExitInvariant); - - uint256 protocolFeeAmount = _calculateAdjustedProtocolFeeAmount( - virtualSupply, - expectedProtocolOwnershipPercentage - ); + ) = _getSupplyAndFeesData(); // Due protocol fees will be minted at the next join or exit, so we can simply add them to the current virtual - // supply to make the calculation with the correct amount. + // supply to get the actual supply. uint256 actualTotalSupply = virtualSupply.add(protocolFeeAmount); // All that's missing now is the invariant. We have the balances required to calculate it already, but still @@ -1067,21 +1055,29 @@ contract ComposableStablePool is // not paused. _ensureNotPaused(); - // First we need to get the data required to compute and pay due protocol fees. - (, uint256[] memory registeredBalances, ) = getVault().getPoolTokens(getPoolId()); - _upscaleArray(registeredBalances, _scalingFactors()); - (uint256 lastJoinExitAmp, uint256 lastPostJoinExitInvariant) = getLastJoinExitData(); - - (, uint256[] memory balances, uint256 currentInvariantWithLastJoinExitAmp) = _payProtocolFeesBeforeJoinExit( - registeredBalances, - lastJoinExitAmp, - lastPostJoinExitInvariant - ); + // We need to calculate the amount of unminted BPT that represents protocol fees to then pay those. This yields + // some auxiliary values that turn out to also be useful for the rest of the tasks we want to perform. + ( + uint256[] memory balances, + , + uint256 protocolFeeAmount, + uint256 lastJoinExitAmp, + uint256 currentInvariantWithLastJoinExitAmp + ) = _getSupplyAndFeesData(); - // With the fees paid, we now store the current invariant and the amplification factor used to compute it, - // marking the Pool as free of protocol debt. + if (protocolFeeAmount > 0) { + _payProtocolFees(protocolFeeAmount); + } + // With the fees paid, we now need to calculate the current invariant so we can store it alongside the current + // amplification factor, marking the Pool as free of protocol debt. (uint256 currentAmp, ) = _getAmplificationParameter(); + + // It turns out that the process for due protocol fee calculation involves computing the current invariant, + // except using the amplification factor at the last join or exit. This would typically not be terribly useful, + // but since the amplification factor only changes rarely there is high probability of its current value being + // the same as it was in the last join or exit. If that is the case, then we can skip the costly invariant + // computation altogether. uint256 currentInvariant = (currentAmp == lastJoinExitAmp) ? currentInvariantWithLastJoinExitAmp : StableMath._calculateInvariant(currentAmp, balances); diff --git a/pkg/pool-stable/contracts/ComposableStablePoolProtocolFees.sol b/pkg/pool-stable/contracts/ComposableStablePoolProtocolFees.sol index 4b064fd418..a7bfa2d424 100644 --- a/pkg/pool-stable/contracts/ComposableStablePoolProtocolFees.sol +++ b/pkg/pool-stable/contracts/ComposableStablePoolProtocolFees.sol @@ -277,7 +277,7 @@ abstract contract ComposableStablePoolProtocolFees is ); if (protocolOwnershipPercentage > 0) { - uint256 protocolFeeAmount = _calculateAdjustedProtocolFeeAmount( + uint256 protocolFeeAmount = ProtocolFees.bptForPoolOwnershipPercentage( postJoinExitSupply, protocolOwnershipPercentage ); @@ -306,25 +306,6 @@ abstract contract ComposableStablePoolProtocolFees is _updateOldRates(); } - /** - * @dev Adjust a protocol fee percentage calculated before minting, to the equivalent value after minting. - */ - function _calculateAdjustedProtocolFeeAmount(uint256 supply, uint256 basePercentage) - internal - pure - returns (uint256) - { - // Since this fee amount will be minted as BPT, which increases the total supply, we need to mint - // slightly more so that it reflects this percentage of the total supply after minting. - // - // The percentage of the Pool the protocol will own after minting is given by: - // `protocol percentage = to mint / (current supply + to mint)`. - // Solving for `to mint`, we arrive at: - // `to mint = current supply * protocol percentage / (1 - protocol percentage)`. - // - return supply.mulDown(basePercentage).divDown(basePercentage.complement()); - } - /** * @notice Return the amplification factor and invariant as of the most recent join or exit (including BPT swaps) */ diff --git a/pkg/pool-stable/test/ComposableStablePool.test.ts b/pkg/pool-stable/test/ComposableStablePool.test.ts index d0ad18dece..1db119ffeb 100644 --- a/pkg/pool-stable/test/ComposableStablePool.test.ts +++ b/pkg/pool-stable/test/ComposableStablePool.test.ts @@ -1622,6 +1622,13 @@ describe('ComposableStablePool', () => { unmintedBPT = virtualSupply.mul(protocolOwnership).div(fp(1).sub(protocolOwnership)); }); + it('the actual supply takes into account unminted protocol fees', async () => { + const virtualSupply = await pool.getVirtualSupply(); + const expectedActualSupply = virtualSupply.add(unmintedBPT); + + expect(await pool.getActualSupply()).to.almostEqual(expectedActualSupply, 1e-6); + }); + it('rate takes into account unminted protocol fees', async () => { const scaledBalances = arrayFpMul(await pool.getBalances(), await pool.getScalingFactors()).filter( (_, i) => i != bptIndex