diff --git a/src/base/Pool.sol b/src/base/Pool.sol index 60956e0e2..5b17a397e 100644 --- a/src/base/Pool.sol +++ b/src/base/Pool.sol @@ -780,6 +780,11 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool { return PoolCommons.utilization(emaState); } + /// @inheritdoc IPoolDerivedState + function depositScale(uint256 index_) external view override returns (uint256) { + return deposits.scaling[index_]; + } + /// @inheritdoc IPoolState function emasInfo() external view override returns (uint256, uint256, uint256, uint256) { return ( diff --git a/src/interfaces/pool/commons/IPoolDerivedState.sol b/src/interfaces/pool/commons/IPoolDerivedState.sol index 94294f83d..ba47a4092 100644 --- a/src/interfaces/pool/commons/IPoolDerivedState.sol +++ b/src/interfaces/pool/commons/IPoolDerivedState.sol @@ -46,4 +46,13 @@ interface IPoolDerivedState { */ function depositUtilization() external view returns (uint256); + /** + * @notice Returns the scaling value of deposit at given index. + * @param index_ Deposit index. + * @return Deposit scaling. + */ + function depositScale( + uint256 index_ + ) external view returns (uint256); + } diff --git a/src/libraries/internal/Deposits.sol b/src/libraries/internal/Deposits.sol index 2b2e64266..57f721e82 100644 --- a/src/libraries/internal/Deposits.sol +++ b/src/libraries/internal/Deposits.sol @@ -321,10 +321,8 @@ library Deposits { function treeSum( DepositsState storage deposits_ ) internal view returns (uint256) { - // In a scaled Fenwick tree, sum is at the root node, but needs to be scaled - uint256 scaling = deposits_.scaling[SIZE]; - // scaling == 0 means scale factor is actually 1 - return (scaling != 0) ? Maths.wmul(scaling, deposits_.values[SIZE]) : deposits_.values[SIZE]; + // In a scaled Fenwick tree, sum is at the root node and never scaled + return deposits_.values[SIZE]; } /** diff --git a/tests/INVARIANTS.md b/tests/INVARIANTS.md index 24866d478..7d95d18ee 100644 --- a/tests/INVARIANTS.md +++ b/tests/INVARIANTS.md @@ -49,6 +49,7 @@ - **F2**: For any index `i`, the prefix sum up to and including `i` is the sum of values stored in indices `j<=i` - **F3**: For any index `i < MAX_FENWICK_INDEX`, `findIndexOfSum(prefixSum(i)) > i` - **F4**: For any index i, there is zero deposit above i and below findIndexOfSum(prefixSum(i) + 1): `depositAtIndex(j) == 0 for i < j < findIndexOfSum(prefixSum(i)+1)` +- **F5**: Global scalar is never updated (`DepositsState.scaling[8192]` is always 0) ## Exchange rate invariants ## - **R1**: Exchange rates are unchanged by pledging collateral diff --git a/tests/forge/invariants/base/BasicInvariants.t.sol b/tests/forge/invariants/base/BasicInvariants.t.sol index 064416b10..e5dcf137b 100644 --- a/tests/forge/invariants/base/BasicInvariants.t.sol +++ b/tests/forge/invariants/base/BasicInvariants.t.sol @@ -43,6 +43,7 @@ abstract contract BasicInvariants is BaseInvariants { * F2: For any index i, the prefix sum up to and including i is the sum of values stored in indices j<=i * F3: For any index i < MAX_FENWICK_INDEX, findIndexOfSum(prefixSum(i)) > i * F4: For any index i, there is zero deposit above i and below findIndexOfSum(prefixSum(i) + 1): findIndexOfSum(prefixSum(i)) == findIndexOfSum(prefixSum(j) - deposits.valueAt(j)), where j is the next index from i with deposits != 0 + * F5: Global scalar is never updated (`DepositsState.values[8192]` is always 0) ****************************************************************************************************************************************/ // checks pool lps are equal to sum of all lender lps in a bucket @@ -352,6 +353,11 @@ abstract contract BasicInvariants is BaseInvariants { } } + // **F5**: Global scalar is never updated (`DepositsState.scaling[8192]` is always 0) + function invariant_fenwick_globalscalar_F5() public useCurrentTimestamp { + require(_pool.depositScale(8192) == 0, "F5: Global scalar was updated"); + } + function invariant_call_summary() public virtual useCurrentTimestamp { console.log("\nCall Summary\n"); console.log("--Lender----------");