From 5e7f5cfc12a5ffee152d41e6121b161bc046936d Mon Sep 17 00:00:00 2001 From: Ian Harvey Date: Tue, 4 Jul 2023 05:15:02 -0400 Subject: [PATCH] Rewards Invariants: Significant improvements (#897) * added multiple staked positions * alphabetized functions, made position with indexes accurate, updateExchangeRates now updates buckets that have position in them * reverted alphabetization change so PR is easier to read * cleaned up and removed excess tracking * cleanup log typo * cleaned up how already claimed rewards are tracked * Fix kickWithDeposit handler call in rewards manager regression test * updateExchangeRate now creates positions * removed unused tracker * fixed unit tests * Add rewardsManager handler with ERC721Pool (#899) Co-authored-by: Ian Harvey * fixed regression_gen * Rewards invariants erc721pool (#908) * Add rewardsManager handler with ERC721Pool * made number of max epochs customizable and removed excess logging --------- Co-authored-by: prateek105 Co-authored-by: Ian Harvey * renamed vars made epoch periods longer * added dynamic updating * cleaned up map checking * tests now pass without failing when no position is created via add quote token * refactored positions cleanly * refactored rewards and positions * clean up * cleanup * renamed test files, hardcoded pool hash, added MAX_AJNA_AMOUNT to readme --------- Co-authored-by: Ian Harvey Co-authored-by: prateek105 Co-authored-by: Prateek Gupta --- Makefile | 3 +- tests/README.md | 2 + .../ERC20PoolRewardsInvariants.t.sol | 69 +++ .../ERC721PoolPositionsInvariants.t.sol | 1 - .../ERC721PoolRewardsInvariants.t.sol | 70 +++ .../RewardsInvariants.t.sol | 40 +- .../handlers/BaseERC20PoolPositionHandler.sol | 198 -------- .../handlers/ERC20PoolPositionHandler.sol | 11 +- .../handlers/ERC20PoolRewardsHandler.sol | 73 +++ .../handlers/ERC721PoolPositionHandler.sol | 11 +- .../handlers/ERC721PoolRewardsHandler.sol | 71 +++ ...ionHandler.sol => PositionPoolHandler.sol} | 33 +- .../handlers/RewardsHandler.sol | 188 ------- .../handlers/RewardsPoolHandler.sol | 189 +++++++ .../UnboundedBasePositionHandler.sol | 47 +- .../UnboundedERC721PoolPositionsHandler.sol | 313 ------------ ...r.sol => UnboundedPositionPoolHandler.sol} | 155 +++--- ...er.sol => UnboundedRewardsPoolHandler.sol} | 99 +++- .../base/handlers/unbounded/BaseHandler.sol | 2 +- .../IPositionsAndRewardsHandler.sol | 15 +- ...ressionTestERC20PoolPositionManager.t.sol} | 12 +- ...egressionTestERC20PoolRewardsManager.t.sol | 480 ++++++++++++++++++ ...ressionTestERC721PoolPositionManager.t.sol | 18 + ...gressionTestERC721PoolRewardsManager.t.sol | 31 ++ .../RegressionTestRewardsManager.t.sol | 371 -------------- 25 files changed, 1237 insertions(+), 1265 deletions(-) create mode 100644 tests/forge/invariants/PositionsAndRewards/ERC20PoolRewardsInvariants.t.sol create mode 100644 tests/forge/invariants/PositionsAndRewards/ERC721PoolRewardsInvariants.t.sol delete mode 100644 tests/forge/invariants/PositionsAndRewards/handlers/BaseERC20PoolPositionHandler.sol create mode 100644 tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolRewardsHandler.sol create mode 100644 tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolRewardsHandler.sol rename tests/forge/invariants/PositionsAndRewards/handlers/{BaseERC721PoolPositionHandler.sol => PositionPoolHandler.sol} (89%) delete mode 100644 tests/forge/invariants/PositionsAndRewards/handlers/RewardsHandler.sol create mode 100644 tests/forge/invariants/PositionsAndRewards/handlers/RewardsPoolHandler.sol delete mode 100644 tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC721PoolPositionsHandler.sol rename tests/forge/invariants/PositionsAndRewards/handlers/unbounded/{UnboundedERC20PoolPositionsHandler.sol => UnboundedPositionPoolHandler.sol} (78%) rename tests/forge/invariants/PositionsAndRewards/handlers/unbounded/{UnboundedRewardsHandler.sol => UnboundedRewardsPoolHandler.sol} (78%) rename tests/forge/regression/PositionAndRewards/{RegressionTestPositionManager.t.sol => RegressionTestERC20PoolPositionManager.t.sol} (92%) create mode 100644 tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolRewardsManager.t.sol create mode 100644 tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolPositionManager.t.sol create mode 100644 tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolRewardsManager.t.sol delete mode 100644 tests/forge/regression/PositionAndRewards/RegressionTestRewardsManager.t.sol diff --git a/Makefile b/Makefile index add7a8466..8efb142aa 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,8 @@ test-invariant-erc20 :; forge t --mt invariant --nmc ${CONTR test-invariant-erc721 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC721 test-invariant-position-erc20 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC20PoolPosition test-invariant-position-erc721 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC721PoolPosition -test-invariant-rewards :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc Rewards +test-invariant-rewards-erc20 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC20PoolRewards +test-invariant-rewards-erc721 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC721PoolRewards test-invariant :; forge t --mt ${MT} --nmc RegressionTest test-invariant-erc20-precision :; ./tests/forge/invariants/test-invariant-erc20-precision.sh test-invariant-erc721-precision :; ./tests/forge/invariants/test-invariant-erc721-precision.sh diff --git a/tests/README.md b/tests/README.md index 4ee047dc4..b46490a88 100644 --- a/tests/README.md +++ b/tests/README.md @@ -75,6 +75,8 @@ Invariant test scenarios can be externally configured by customizing following e | MAX_POOL_DEBT | ERC20 ERC721 | 1e45 | The max amount of debt that can be taken from the pool. If debt goes above this amount, borrower debt will be repaid | | SKIP_TIME | ERC20 ERC721 | 24 hours | The upper limit of time that can be skipped after a pool action (fuzzed) | | SKIP_TIME_TO_KICK | ERC20 ERC721 | 200 days | The time to be skipped and drive a new loan undercollateralized. Use a big value to ensure a successful kick | +| MAX_EPOCH_ADVANCE | ERC20 ERC721 | 5 | The maximum number of epochs that will be created before an unstake or claimRewards call | +| MAX_AJNA_AMOUNT | ERC20 ERC721 | 100_000_000 | The maximum amount of ajna provided to the rewards contract | | FOUNDRY_INVARIANT_RUNS | ERC20 ERC721 | 10 | The number of runs for each scenario | | FOUNDRY_INVARIANT_DEPTH | ERC20 ERC721 | 200 | The number of actions performed in each scenario | | LOGS_VERBOSITY | ERC20 ERC721 | 0 |

Details to log

0 = No Logs

1 = pool State

2 = pool State, Auctions details

3 = pool State, Auctions details , Buckets details

4 = pool State, Auctions details , Buckets details, Lender details

5 = pool State, Auctions details , Buckets details, Lender details, Borrower details

Note - Log File with name `logFile.txt` will be generated in project directory| diff --git a/tests/forge/invariants/PositionsAndRewards/ERC20PoolRewardsInvariants.t.sol b/tests/forge/invariants/PositionsAndRewards/ERC20PoolRewardsInvariants.t.sol new file mode 100644 index 000000000..571d464b4 --- /dev/null +++ b/tests/forge/invariants/PositionsAndRewards/ERC20PoolRewardsInvariants.t.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import "@std/console.sol"; +import { Maths } from 'src/libraries/internal/Maths.sol'; +import { Pool } from 'src/base/Pool.sol'; +import { ERC20Pool } from 'src/ERC20Pool.sol'; +import { ERC721Pool } from 'src/ERC721Pool.sol'; +import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol'; +import { ERC721PoolFactory } from 'src/ERC721PoolFactory.sol'; +import { PositionManager } from 'src/PositionManager.sol'; +import { RewardsManager } from 'src/RewardsManager.sol'; + +import { TokenWithNDecimals } from '../../utils/Tokens.sol'; + +import { ERC20PoolRewardsHandler } from './handlers/ERC20PoolRewardsHandler.sol'; +import { RewardsInvariants } from './RewardsInvariants.t.sol'; + +contract ERC20PoolRewardsInvariants is RewardsInvariants { + + TokenWithNDecimals internal _collateral; + ERC20Pool internal _erc20pool; + ERC20PoolRewardsHandler internal _erc20poolrewardsHandler; + + function setUp() public override virtual { + + super.setUp(); + + _collateral = new TokenWithNDecimals("Collateral", "C", uint8(vm.envOr("COLLATERAL_PRECISION", uint256(18)))); + _erc20poolFactory = new ERC20PoolFactory(address(_ajna)); + _erc20impl = _erc20poolFactory.implementation(); + _erc721poolFactory = new ERC721PoolFactory(address(_ajna)); + _erc721impl = _erc721poolFactory.implementation(); + _erc20pool = ERC20Pool(_erc20poolFactory.deployPool(address(_collateral), address(_quote), 0.05 * 10**18)); + _pool = Pool(address(_erc20pool)); + _positionManager = new PositionManager(_erc20poolFactory, _erc721poolFactory); + _rewardsManager = new RewardsManager(address(_ajna), _positionManager); + + // fund the rewards manager with 100M ajna + _ajna.mint(address(_rewardsManager), 100_000_000 * 1e18); + + excludeContract(address(_ajna)); + excludeContract(address(_collateral)); + excludeContract(address(_quote)); + excludeContract(address(_erc20poolFactory)); + excludeContract(address(_erc721poolFactory)); + excludeContract(address(_erc20pool)); + excludeContract(address(_poolInfo)); + excludeContract(address(_erc20impl)); + excludeContract(address(_erc721impl)); + excludeContract(address(_positionManager)); + excludeContract(address(_rewardsManager)); + + _erc20poolrewardsHandler = new ERC20PoolRewardsHandler( + address(_rewardsManager), + address(_positionManager), + address(_erc20pool), + address(_ajna), + address(_quote), + address(_collateral), + address(_poolInfo), + NUM_ACTORS, + address(this) + ); + + _handler = address(_erc20poolrewardsHandler); + } +} diff --git a/tests/forge/invariants/PositionsAndRewards/ERC721PoolPositionsInvariants.t.sol b/tests/forge/invariants/PositionsAndRewards/ERC721PoolPositionsInvariants.t.sol index 66a8eed06..b7689f116 100644 --- a/tests/forge/invariants/PositionsAndRewards/ERC721PoolPositionsInvariants.t.sol +++ b/tests/forge/invariants/PositionsAndRewards/ERC721PoolPositionsInvariants.t.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.18; import "@std/console.sol"; - import { Pool } from 'src/base/Pool.sol'; import { ERC20Pool } from 'src/ERC20Pool.sol'; import { ERC721Pool } from 'src/ERC721Pool.sol'; diff --git a/tests/forge/invariants/PositionsAndRewards/ERC721PoolRewardsInvariants.t.sol b/tests/forge/invariants/PositionsAndRewards/ERC721PoolRewardsInvariants.t.sol new file mode 100644 index 000000000..a45e599ea --- /dev/null +++ b/tests/forge/invariants/PositionsAndRewards/ERC721PoolRewardsInvariants.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import "@std/console.sol"; +import { Maths } from 'src/libraries/internal/Maths.sol'; +import { Pool } from 'src/base/Pool.sol'; +import { ERC20Pool } from 'src/ERC20Pool.sol'; +import { ERC721Pool } from 'src/ERC721Pool.sol'; +import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol'; +import { ERC721PoolFactory } from 'src/ERC721PoolFactory.sol'; +import { PositionManager } from 'src/PositionManager.sol'; +import { RewardsManager } from 'src/RewardsManager.sol'; + +import { NFTCollateralToken } from '../../utils/Tokens.sol'; + +import { ERC721PoolRewardsHandler } from './handlers/ERC721PoolRewardsHandler.sol'; +import { RewardsInvariants } from './RewardsInvariants.t.sol'; + +contract ERC721PoolRewardsInvariants is RewardsInvariants { + + NFTCollateralToken internal _collateral; + ERC721Pool internal _erc721pool; + ERC721PoolRewardsHandler internal _erc721poolrewardsHandler; + + function setUp() public override virtual { + + super.setUp(); + + uint256[] memory tokenIds; + _collateral = new NFTCollateralToken(); + _erc20poolFactory = new ERC20PoolFactory(address(_ajna)); + _erc20impl = _erc20poolFactory.implementation(); + _erc721poolFactory = new ERC721PoolFactory(address(_ajna)); + _erc721pool = ERC721Pool(_erc721poolFactory.deployPool(address(_collateral), address(_quote), tokenIds, 0.05 * 10**18)); + _erc721impl = _erc721poolFactory.implementation(); + _pool = Pool(address(_erc721pool)); + _positionManager = new PositionManager(_erc20poolFactory, _erc721poolFactory); + _rewardsManager = new RewardsManager(address(_ajna), _positionManager); + + // fund the rewards manager with 100M ajna + _ajna.mint(address(_rewardsManager), 100_000_000 * 1e18); + + excludeContract(address(_ajna)); + excludeContract(address(_collateral)); + excludeContract(address(_quote)); + excludeContract(address(_erc20poolFactory)); + excludeContract(address(_erc721poolFactory)); + excludeContract(address(_erc721pool)); + excludeContract(address(_poolInfo)); + excludeContract(address(_erc20impl)); + excludeContract(address(_erc721impl)); + excludeContract(address(_positionManager)); + excludeContract(address(_rewardsManager)); + + _erc721poolrewardsHandler = new ERC721PoolRewardsHandler( + address(_rewardsManager), + address(_positionManager), + address(_erc721pool), + address(_ajna), + address(_quote), + address(_collateral), + address(_poolInfo), + NUM_ACTORS, + address(this) + ); + + _handler = address(_erc721poolrewardsHandler); + } +} diff --git a/tests/forge/invariants/PositionsAndRewards/RewardsInvariants.t.sol b/tests/forge/invariants/PositionsAndRewards/RewardsInvariants.t.sol index e9078afa8..53c1b8a42 100644 --- a/tests/forge/invariants/PositionsAndRewards/RewardsInvariants.t.sol +++ b/tests/forge/invariants/PositionsAndRewards/RewardsInvariants.t.sol @@ -3,46 +3,16 @@ pragma solidity 0.8.18; import "@std/console.sol"; -import { Maths } from 'src/libraries/internal/Maths.sol'; -import { RewardsManager } from 'src/RewardsManager.sol'; -import { _getEpochInfo } from 'src/RewardsManager.sol'; +import { Maths } from 'src/libraries/internal/Maths.sol'; +import { RewardsManager, _getEpochInfo } from 'src/RewardsManager.sol'; import { IBaseHandler } from '../interfaces/IBaseHandler.sol'; import { IPositionsAndRewardsHandler } from '../interfaces/IPositionsAndRewardsHandler.sol'; -import { RewardsHandler } from './handlers/RewardsHandler.sol'; -import { ERC20PoolPositionsInvariants } from './ERC20PoolPositionsInvariants.t.sol'; +import { PositionsInvariants } from './PositionsInvariants.sol'; -contract RewardsInvariants is ERC20PoolPositionsInvariants { +abstract contract RewardsInvariants is PositionsInvariants { RewardsManager internal _rewardsManager; - RewardsHandler internal _rewardsHandler; - - function setUp() public override virtual { - - super.setUp(); - - _rewardsManager = new RewardsManager(address(_ajna), _positionManager); - - // fund the rewards manager with 100M ajna - _ajna.mint(address(_rewardsManager), 100_000_000 * 1e18); - - excludeContract(address(_erc20positionHandler)); - excludeContract(address(_rewardsManager)); - - _rewardsHandler = new RewardsHandler( - address(_rewardsManager), - address(_positionManager), - address(_erc20pool), - address(_ajna), - address(_quote), - address(_collateral), - address(_poolInfo), - NUM_ACTORS, - address(this) - ); - - _handler = address(_rewardsHandler); - } function invariant_rewards_RW1_RW2() public useCurrentTimestamp { @@ -76,7 +46,7 @@ contract RewardsInvariants is ERC20PoolPositionsInvariants { function invariant_call_summary() public virtual override useCurrentTimestamp { console.log("\nCall Summary\n"); - console.log("--Positions--------"); + console.log("--Rewards--------"); console.log("UBRewardsHandler.unstake ", IBaseHandler(_handler).numberOfCalls("UBRewardsHandler.unstake")); console.log("BRewardsHandler.unstake ", IBaseHandler(_handler).numberOfCalls("BRewardsHandler.unstake")); console.log("UBRewardsHandler.emergencyUnstake ", IBaseHandler(_handler).numberOfCalls("UBRewardsHandler.emergencyUnstake")); diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/BaseERC20PoolPositionHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/BaseERC20PoolPositionHandler.sol deleted file mode 100644 index c07c02d8c..000000000 --- a/tests/forge/invariants/PositionsAndRewards/handlers/BaseERC20PoolPositionHandler.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity 0.8.18; - -import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; -import '@std/console.sol'; - -import { Maths } from 'src/libraries/internal/Maths.sol'; - -import { _priceAt } from 'src/libraries/helpers/PoolHelper.sol'; - -import { IPositionManagerOwnerActions } from 'src/interfaces/position/IPositionManagerOwnerActions.sol'; -import { PositionManager } from 'src/PositionManager.sol'; -import { ERC20Pool } from 'src/ERC20Pool.sol'; - -import { UnboundedERC20PoolPositionsHandler } from './unbounded/UnboundedERC20PoolPositionsHandler.sol'; - -abstract contract BaseERC20PoolPositionHandler is UnboundedERC20PoolPositionsHandler { - - using EnumerableSet for EnumerableSet.UintSet; - - /********************************/ - /*** Positions Test Functions ***/ - /********************************/ - - function memorializePositions( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 amountToAdd_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BPositionHandler.memorialize']++; - // Pre action // - (uint256 tokenId, uint256[] memory indexes) = _preMemorializePositions(_lenderBucketIndex, amountToAdd_); - - // Action phase // - _memorializePositions(tokenId, indexes); - } - - function redeemPositions( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 amountToAdd_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BPositionHandler.redeem']++; - // Pre action // - (uint256 tokenId, uint256[] memory indexes) = _preRedeemPositions(_lenderBucketIndex, amountToAdd_); - - // Action phase // - _redeemPositions(tokenId, indexes); - } - - function mint( - uint256 actorIndex_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BPositionHandler.mint']++; - - // Action phase // - _mint(); - } - - function burn( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 skippedTime_, - uint256 amountToAdd_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BPositionHandler.burn']++; - // Pre action // - (uint256 tokenId_) = _preBurn(_lenderBucketIndex, amountToAdd_); - - // Action phase // - _burn(tokenId_); - } - - function moveLiquidity( - uint256 actorIndex_, - uint256 skippedTime_, - uint256 amountToMove_, - uint256 fromIndex_, - uint256 toIndex_ - ) external useRandomActor(actorIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BPositionHandler.moveLiquidity']++; - // Pre action // - ( - uint256 tokenId, - uint256 fromIndex, - uint256 toIndex - ) = _preMoveLiquidity(amountToMove_, fromIndex_, toIndex_); - - // retrieve info of bucket from pool - ( - , - uint256 bucketCollateral, - , - , - ) = _pool.bucketInfo(fromIndex); - - // to avoid LP mismatch revert return if bucket has collateral or exchangeRate < 1e18 - if (bucketCollateral != 0) return; - if (_pool.bucketExchangeRate(fromIndex) < 1e18) return; - - // Action phase // - _moveLiquidity(tokenId, fromIndex, toIndex); - } - - function _preMemorializePositions( - uint256 bucketIndex_, - uint256 amountToAdd_ - ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { - - // ensure actor has a position - (uint256 lpBalanceBefore,) = _pool.lenderInfo(bucketIndex_, _actor); - - // add quote token if they don't have a position - if (lpBalanceBefore == 0) { - // Prepare test phase - uint256 boundedAmount = constrictToRange(amountToAdd_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT); - _ensureQuoteAmount(_actor, boundedAmount); - try _pool.addQuoteToken(boundedAmount, bucketIndex_, block.timestamp + 1 minutes, false) { - } catch (bytes memory err) { - _ensurePoolError(err); - } - } - - indexes_ = new uint256[](1); - indexes_[0] = bucketIndex_; - - uint256[] memory lpBalances = new uint256[](1); - - // mint position NFT - tokenId_ = _mint(); - - (lpBalances[0], ) = _pool.lenderInfo(bucketIndex_, _actor); - _pool.increaseLPAllowance(address(_positionManager), indexes_, lpBalances); - } - - function _preRedeemPositions( - uint256 bucketIndex_, - uint256 amountToAdd_ - ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { - - (tokenId_, indexes_) = _getNFTPosition(bucketIndex_, amountToAdd_); - - // approve positionManager to transfer LP tokens - address[] memory transferors = new address[](1); - transferors[0] = address(_positionManager); - - _pool.approveLPTransferors(transferors); - } - - function _preBurn( - uint256 bucketIndex_, - uint256 amountToAdd_ - ) internal returns (uint256 tokenId_) { - uint256[] memory indexes; - - // check and create the position - (tokenId_, indexes) = _preRedeemPositions(bucketIndex_, amountToAdd_); - - _redeemPositions(tokenId_, indexes); - } - - function _preMoveLiquidity( - uint256 amountToMove_, - uint256 fromIndex_, - uint256 toIndex_ - ) internal returns (uint256 tokenId_, uint256 boundedFromIndex_, uint256 boundedToIndex_) { - boundedFromIndex_ = constrictToRange(fromIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX); - boundedToIndex_ = constrictToRange(toIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX); - - uint256[] memory indexes; - (tokenId_, indexes) = _getNFTPosition(boundedFromIndex_, amountToMove_); - boundedFromIndex_ = indexes[0]; - - } - - function _getNFTPosition( - uint256 bucketIndex_, - uint256 amountToAdd_ - ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { - - // Check for exisiting nft positions in PositionManager - uint256[] memory tokenIds = getTokenIdsByActor(address(_actor)); - - if (tokenIds.length != 0 ) { - // use existing position NFT - tokenId_ = tokenIds[0]; - indexes_ = getBucketIndexesByTokenId(tokenId_); - } else { - // create a position for the actor - (tokenId_, indexes_) = _preMemorializePositions(bucketIndex_, amountToAdd_); - _memorializePositions(tokenId_, indexes_); - } - } -} diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolPositionHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolPositionHandler.sol index e856860fd..27e2378a5 100644 --- a/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolPositionHandler.sol +++ b/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolPositionHandler.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.18; -import { PositionManager } from 'src/PositionManager.sol'; +import { PositionManager } from 'src/PositionManager.sol'; -import { BaseERC20PoolPositionHandler } from './BaseERC20PoolPositionHandler.sol'; -import { BaseERC20PoolHandler } from '../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol'; +import { PositionPoolHandler } from './PositionPoolHandler.sol'; +import { BaseERC20PoolHandler } from '../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol'; -contract ERC20PoolPositionHandler is BaseERC20PoolPositionHandler { +contract ERC20PoolPositionHandler is PositionPoolHandler, BaseERC20PoolHandler { constructor( address positions_, @@ -22,5 +22,8 @@ contract ERC20PoolPositionHandler is BaseERC20PoolPositionHandler { // Position manager _positionManager = PositionManager(positions_); + + // pool hash for mint() call + _poolHash = bytes32(keccak256("ERC20_NON_SUBSET_HASH")); } } diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolRewardsHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolRewardsHandler.sol new file mode 100644 index 000000000..f97570036 --- /dev/null +++ b/tests/forge/invariants/PositionsAndRewards/handlers/ERC20PoolRewardsHandler.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import { RewardsManager } from 'src/RewardsManager.sol'; +import { PositionManager } from 'src/PositionManager.sol'; + +import { RewardsPoolHandler } from './RewardsPoolHandler.sol'; +import { ReserveERC20PoolHandler } from '../../ERC20Pool/handlers/ReserveERC20PoolHandler.sol'; + +contract ERC20PoolRewardsHandler is RewardsPoolHandler, ReserveERC20PoolHandler { + + constructor( + address rewards_, + address positions_, + address pool_, + address ajna_, + address quote_, + address collateral_, + address poolInfo_, + uint256 numOfActors_, + address testContract_ + ) ReserveERC20PoolHandler(pool_, ajna_, quote_, collateral_, poolInfo_, numOfActors_, testContract_) { + + // Position manager + _positionManager = PositionManager(positions_); + + // Rewards manager + _rewardsManager = RewardsManager(rewards_); + + // pool hash for mint() call + _poolHash = bytes32(keccak256("ERC20_NON_SUBSET_HASH")); + } + + function _advanceEpochRewardStakers( + uint256 amountToAdd_, + uint256[] memory indexes_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) internal override { + + numberOfEpochs_ = constrictToRange(numberOfEpochs_, 1, vm.envOr("MAX_EPOCH_ADVANCE", uint256(5))); + + for (uint256 epoch = 0; epoch <= numberOfEpochs_; epoch ++) { + // draw some debt and then repay after some times to increase pool earning / reserves + (, uint256 claimableReserves, , ) = _pool.reservesInfo(); + if (claimableReserves == 0) { + uint256 amountToBorrow = _preDrawDebt(amountToAdd_); + _drawDebt(amountToBorrow); + + + _repayDebt(type(uint256).max); + } + + skip(20 days); // epochs are spaced a minimum of 14 days apart + + (, claimableReserves, , ) = _pool.reservesInfo(); + + _kickReserveAuction(); + + // skip time for price to decrease, large price decrease reduces chances of rewards exceeding rewards contract balance + skip(60 hours); + + uint256 boundedTakeAmount = constrictToRange(amountToAdd_, claimableReserves / 2, claimableReserves); + _takeReserves(boundedTakeAmount); + + // exchange rates must be updated so that rewards can be claimed + indexes_ = _randomizeExchangeRateIndexes(indexes_, bucketSubsetToUpdate_); + if (indexes_.length != 0) { _updateExchangeRate(indexes_); } + } + } + +} diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolPositionHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolPositionHandler.sol index 856f87f4f..443bd7519 100644 --- a/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolPositionHandler.sol +++ b/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolPositionHandler.sol @@ -2,12 +2,12 @@ pragma solidity 0.8.18; -import { PositionManager } from 'src/PositionManager.sol'; +import { PositionManager } from 'src/PositionManager.sol'; -import { BaseERC721PoolPositionHandler } from './BaseERC721PoolPositionHandler.sol'; -import { BaseERC721PoolHandler } from '../../ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol'; +import { PositionPoolHandler } from './PositionPoolHandler.sol'; +import { BaseERC721PoolHandler } from '../../ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol'; -contract ERC721PoolPositionHandler is BaseERC721PoolPositionHandler { +contract ERC721PoolPositionHandler is PositionPoolHandler, BaseERC721PoolHandler { constructor( address positions_, @@ -22,5 +22,8 @@ contract ERC721PoolPositionHandler is BaseERC721PoolPositionHandler { // Position manager _positionManager = PositionManager(positions_); + + // pool hash for mint() call + _poolHash = bytes32(keccak256("ERC721_NON_SUBSET_HASH")); } } diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolRewardsHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolRewardsHandler.sol new file mode 100644 index 000000000..9b72b89de --- /dev/null +++ b/tests/forge/invariants/PositionsAndRewards/handlers/ERC721PoolRewardsHandler.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import { RewardsManager } from 'src/RewardsManager.sol'; +import { PositionManager } from 'src/PositionManager.sol'; + +import { RewardsPoolHandler } from './RewardsPoolHandler.sol'; +import { ReserveERC721PoolHandler } from '../../ERC721Pool/handlers/ReserveERC721PoolHandler.sol'; + +contract ERC721PoolRewardsHandler is RewardsPoolHandler, ReserveERC721PoolHandler { + + constructor( + address rewards_, + address positions_, + address pool_, + address ajna_, + address quote_, + address collateral_, + address poolInfo_, + uint256 numOfActors_, + address testContract_ + ) ReserveERC721PoolHandler(pool_, ajna_, quote_, collateral_, poolInfo_, numOfActors_, testContract_) { + + // Position manager + _positionManager = PositionManager(positions_); + + // Rewards manager + _rewardsManager = RewardsManager(rewards_); + + // pool hash for mint() call + _poolHash = bytes32(keccak256("ERC721_NON_SUBSET_HASH")); + } + + function _advanceEpochRewardStakers( + uint256 amountToAdd_, + uint256[] memory indexes_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) internal override { + + numberOfEpochs_ = constrictToRange(numberOfEpochs_, 1, vm.envOr("MAX_EPOCH_ADVANCE", uint256(2))); + + for (uint256 epoch = 0; epoch <= numberOfEpochs_; epoch ++) { + // draw some debt and then repay after some times to increase pool earning / reserves + (, uint256 claimableReserves, , ) = _pool.reservesInfo(); + if (claimableReserves == 0) { + uint256 amountToBorrow = _preDrawDebt(amountToAdd_); + _drawDebt(amountToBorrow); + + _repayDebt(type(uint256).max); + } + + skip(20 days); // epochs are spaced a minimum of 14 days apart + + (, claimableReserves, , ) = _pool.reservesInfo(); + + _kickReserveAuction(); + + // skip time for price to decrease, large price decrease reduces chances of rewards exceeding rewards contract balance + skip(60 hours); + + uint256 boundedTakeAmount = constrictToRange(amountToAdd_, claimableReserves / 2, claimableReserves); + _takeReserves(boundedTakeAmount); + + // exchange rates must be updated so that rewards can be claimed + indexes_ = _randomizeExchangeRateIndexes(indexes_, bucketSubsetToUpdate_); + if (indexes_.length != 0) { _updateExchangeRate(indexes_); } + } + } +} diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/BaseERC721PoolPositionHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/PositionPoolHandler.sol similarity index 89% rename from tests/forge/invariants/PositionsAndRewards/handlers/BaseERC721PoolPositionHandler.sol rename to tests/forge/invariants/PositionsAndRewards/handlers/PositionPoolHandler.sol index eeaedbb08..f6dc30cd5 100644 --- a/tests/forge/invariants/PositionsAndRewards/handlers/BaseERC721PoolPositionHandler.sol +++ b/tests/forge/invariants/PositionsAndRewards/handlers/PositionPoolHandler.sol @@ -2,21 +2,11 @@ pragma solidity 0.8.18; -import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; -import '@std/console.sol'; - import { Maths } from 'src/libraries/internal/Maths.sol'; -import { _priceAt } from 'src/libraries/helpers/PoolHelper.sol'; - -import { IPositionManagerOwnerActions } from 'src/interfaces/position/IPositionManagerOwnerActions.sol'; -import { PositionManager } from 'src/PositionManager.sol'; - -import { UnboundedERC721PoolPositionsHandler } from './unbounded/UnboundedERC721PoolPositionsHandler.sol'; +import { UnboundedPositionPoolHandler } from './unbounded/UnboundedPositionPoolHandler.sol'; -abstract contract BaseERC721PoolPositionHandler is UnboundedERC721PoolPositionsHandler { - - using EnumerableSet for EnumerableSet.UintSet; +abstract contract PositionPoolHandler is UnboundedPositionPoolHandler { /********************************/ /*** Positions Test Functions ***/ @@ -45,7 +35,10 @@ abstract contract BaseERC721PoolPositionHandler is UnboundedERC721PoolPositionsH numberOfCalls['BPositionHandler.redeem']++; // Pre action // (uint256 tokenId, uint256[] memory indexes) = _preRedeemPositions(_lenderBucketIndex, amountToAdd_); - + + // NFT doesn't have a position associated with it, return + if (indexes.length == 0) return; + // Action phase // _redeemPositions(tokenId, indexes); } @@ -97,6 +90,9 @@ abstract contract BaseERC721PoolPositionHandler is UnboundedERC721PoolPositionsH , ) = _pool.bucketInfo(fromIndex); + // NFT doesn't have a position associated with it, return + if (fromIndex == 0) return; + // to avoid LP mismatch revert return if bucket has collateral or exchangeRate < 1e18 if (bucketCollateral != 0) return; if (_pool.bucketExchangeRate(fromIndex) < 1e18) return; @@ -115,8 +111,8 @@ abstract contract BaseERC721PoolPositionHandler is UnboundedERC721PoolPositionsH // add quote token if they don't have a position if (lpBalanceBefore == 0) { - // Prepare test phase - uint256 boundedAmount = constrictToRange(amountToAdd_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT); + // bound amount + uint256 boundedAmount = constrictToRange(amountToAdd_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT); _ensureQuoteAmount(_actor, boundedAmount); try _pool.addQuoteToken(boundedAmount, bucketIndex_, block.timestamp + 1 minutes, false) { } catch (bytes memory err) { @@ -172,8 +168,7 @@ abstract contract BaseERC721PoolPositionHandler is UnboundedERC721PoolPositionsH uint256[] memory indexes; (tokenId_, indexes) = _getNFTPosition(boundedFromIndex_, amountToMove_); - boundedFromIndex_ = indexes[0]; - + boundedFromIndex_ = indexes.length != 0 ? indexes[0]: 0; } function _getNFTPosition( @@ -193,5 +188,5 @@ abstract contract BaseERC721PoolPositionHandler is UnboundedERC721PoolPositionsH (tokenId_, indexes_) = _preMemorializePositions(bucketIndex_, amountToAdd_); _memorializePositions(tokenId_, indexes_); } - } -} + } +} \ No newline at end of file diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/RewardsHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/RewardsHandler.sol deleted file mode 100644 index b824f6be9..000000000 --- a/tests/forge/invariants/PositionsAndRewards/handlers/RewardsHandler.sol +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity 0.8.18; - -import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; - -import { PositionManager } from 'src/PositionManager.sol'; -import { RewardsManager } from 'src/RewardsManager.sol'; - -import { UnboundedRewardsHandler } from './unbounded/UnboundedRewardsHandler.sol'; - -import { ReserveERC20PoolHandler } from '../../ERC20Pool/handlers/ReserveERC20PoolHandler.sol'; -import { BaseERC20PoolPositionHandler } from './BaseERC20PoolPositionHandler.sol'; - -contract RewardsHandler is UnboundedRewardsHandler, BaseERC20PoolPositionHandler, ReserveERC20PoolHandler { - - constructor( - address rewards_, - address positions_, - address pool_, - address ajna_, - address quote_, - address collateral_, - address poolInfo_, - uint256 numOfActors_, - address testContract_ - ) ReserveERC20PoolHandler(pool_, ajna_, quote_, collateral_, poolInfo_, numOfActors_, testContract_) { - - // Position manager - _positionManager = PositionManager(positions_); - - // Rewards manager - _rewardsManager = RewardsManager(rewards_); - } - - /*******************************/ - /*** Rewards Test Functions ***/ - /*******************************/ - - function stake( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 amountToAdd_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BRewardsHandler.stake']++; - // Pre action - (uint256 tokenId,) = _preStake(_lenderBucketIndex, amountToAdd_); - - // Action phase - _stake(tokenId); - } - - function unstake( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 amountToAdd_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BRewardsHandler.unstake']++; - // Pre action - uint256 tokenId = _preUnstake(_lenderBucketIndex, amountToAdd_); - - // if rewards exceed contract balance tx will revert, return - uint256 reward = _rewardsManager.calculateRewards(tokenId, _pool.currentBurnEpoch()); - if (reward > _ajna.balanceOf(address(_rewardsManager))) return; - - // Action phase - _unstake(tokenId); - } - - function emergencyUnstake( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 amountToAdd_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BRewardsHandler.emergencyUnstake']++; - - // Pre action - uint256 tokenId = _preUnstake(_lenderBucketIndex, amountToAdd_); - - // Action phase - _emergencyUnstake(tokenId); - } - - function updateExchangeRate( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BRewardsHandler.updateRate']++; - - // Pre action // - uint256[] memory indexes = _preUpdateExchangeRate(_lenderBucketIndex); - - // Action phase - _updateExchangeRate(indexes); - } - - function claimRewards( - uint256 actorIndex_, - uint256 bucketIndex_, - uint256 amountToAdd_, - uint256 skippedTime_ - ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { - numberOfCalls['BRewardsHandler.claimRewards']++; - - // Pre action // - uint256 tokenId = _preUnstake(_lenderBucketIndex, amountToAdd_); - - uint256 currentEpoch = _pool.currentBurnEpoch(); - - // Action phase - _claimRewards(tokenId, currentEpoch); - } - - /*******************************/ - /*** Rewards Tests Functions ***/ - /*******************************/ - - function _preStake( - uint256 bucketIndex_, - uint256 amountToAdd_ - ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { - - (tokenId_, indexes_) = _preMemorializePositions(bucketIndex_, amountToAdd_); - - _memorializePositions(tokenId_, indexes_); - - // Approve rewards contract to transfer token - _positionManager.approve(address(_rewardsManager), tokenId_); - - } - - function _preUnstake( - uint256 bucketIndex_, - uint256 amountToAdd_ - ) internal returns (uint256 tokenId_) { - - // TODO: Check if the actor has a NFT position or a staked position is tracking events - - // Create a staked position - uint256[] memory indexes; - (tokenId_, indexes)= _preStake(bucketIndex_, amountToAdd_); - _stake(tokenId_); - - _advanceEpochRewardStakers(amountToAdd_, indexes); - } - - function _advanceEpochRewardStakers( - uint256 amountToAdd_, - uint256[] memory indexes_ - ) internal { - - // draw some debt and then repay after some times to increase pool earning / reserves - (, uint256 claimableReserves, , ) = _pool.reservesInfo(); - if (claimableReserves == 0) { - uint256 amountToBorrow = _preDrawDebt(amountToAdd_); - _drawDebt(amountToBorrow); - - skip(20 days); // epochs are spaced a minimum of 14 days apart - - _repayDebt(type(uint256).max); - } - - (, claimableReserves, , ) = _pool.reservesInfo(); - - _kickReserveAuction(); - - // skip time for price to decrease, large price decrease reduces chances of rewards exceeding rewards contract balance - skip(60 hours); - - uint256 boundedTakeAmount = constrictToRange(amountToAdd_, claimableReserves / 2, claimableReserves); - _takeReserves(boundedTakeAmount); - - // exchange rates must be updated so that rewards can be checked - _rewardsManager.updateBucketExchangeRatesAndClaim(address(_pool), keccak256("ERC20_NON_SUBSET_HASH"), indexes_); - - } - - function _preUpdateExchangeRate( - uint256 bucketIndex_ - ) internal pure returns (uint256[] memory indexes_) { - indexes_ = new uint256[](1); - indexes_[0] = bucketIndex_; - } -} diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/RewardsPoolHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/RewardsPoolHandler.sol new file mode 100644 index 000000000..4124c4efb --- /dev/null +++ b/tests/forge/invariants/PositionsAndRewards/handlers/RewardsPoolHandler.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import { PositionPoolHandler } from './PositionPoolHandler.sol'; +import { UnboundedRewardsPoolHandler } from './unbounded/UnboundedRewardsPoolHandler.sol'; + +abstract contract RewardsPoolHandler is UnboundedRewardsPoolHandler, PositionPoolHandler { + + /*******************************/ + /*** Rewards Test Functions ***/ + /*******************************/ + + function stake( + uint256 actorIndex_, + uint256 bucketIndex_, + uint256 amountToAdd_, + uint256 skippedTime_ + ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { + numberOfCalls['BRewardsHandler.stake']++; + // Pre action + (uint256 tokenId, uint256[] memory indexes) = _preStake(_lenderBucketIndex, amountToAdd_); + + // NFT doesn't have a position associated with it, return + if (indexes.length == 0) return; + + // Action phase + _stake(tokenId); + } + + function unstake( + uint256 actorIndex_, + uint256 bucketIndex_, + uint256 amountToAdd_, + uint256 skippedTime_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { + numberOfCalls['BRewardsHandler.unstake']++; + // Pre action + (uint256 tokenId, uint256[] memory indexes) = _preUnstake( + _lenderBucketIndex, + amountToAdd_, + numberOfEpochs_, + bucketSubsetToUpdate_ + ); + + // NFT doesn't have a position associated with it, return + if (indexes.length == 0) return; + + // if rewards exceed contract balance tx will revert, return + uint256 reward = _rewardsManager.calculateRewards(tokenId, _pool.currentBurnEpoch()); + if (reward > _ajna.balanceOf(address(_rewardsManager))) return; + + // Action phase + _unstake(tokenId); + } + + function emergencyUnstake( + uint256 actorIndex_, + uint256 bucketIndex_, + uint256 amountToAdd_, + uint256 skippedTime_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { + numberOfCalls['BRewardsHandler.emergencyUnstake']++; + + // Pre action + (uint256 tokenId, uint256[] memory indexes) = _preUnstake( + _lenderBucketIndex, + amountToAdd_, + numberOfEpochs_, + bucketSubsetToUpdate_ + ); + + // NFT doesn't have a position associated with it, return + if (indexes.length == 0) return; + + // Action phase + _emergencyUnstake(tokenId); + } + + function updateExchangeRate( + uint256 actorIndex_, + uint256 bucketIndex_, + uint256 amountToAdd_, + uint256 skippedTime_ + ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { + numberOfCalls['BRewardsHandler.updateRate']++; + + // Pre action // + uint256[] memory indexes = getBucketIndexesWithPosition(); + + // if there are no existing positions, create a position at a a random index + if (indexes.length == 0) { + (, indexes) = _getStakedPosition(_lenderBucketIndex, amountToAdd_); + + // NFT doesn't have a position associated with it, return + if (indexes.length == 0) return; + } + + // Action phase + _updateExchangeRate(indexes); + } + + function claimRewards( + uint256 actorIndex_, + uint256 bucketIndex_, + uint256 amountToAdd_, + uint256 skippedTime_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) external useRandomActor(actorIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) { + numberOfCalls['BRewardsHandler.claimRewards']++; + + // Pre action // + (uint256 tokenId, uint256[] memory indexes) = _preUnstake( + _lenderBucketIndex, + amountToAdd_, + numberOfEpochs_, + bucketSubsetToUpdate_ + ); + + // NFT doesn't have a position associated with it, return + if (indexes.length == 0) return; + + // Action phase + _claimRewards(tokenId, _pool.currentBurnEpoch()); + } + + /*******************************/ + /*** Prepare Tests Functions ***/ + /*******************************/ + + function _preStake( + uint256 bucketIndex_, + uint256 amountToAdd_ + ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { + + // retreive or create a NFT position + (tokenId_, indexes_)= _getNFTPosition(bucketIndex_, amountToAdd_); + + // Approve rewards contract to transfer token + _positionManager.approve(address(_rewardsManager), tokenId_); + } + + function _preUnstake( + uint256 bucketIndex_, + uint256 amountToAdd_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { + (tokenId_, indexes_)= _getStakedPosition(bucketIndex_, amountToAdd_); + + if (indexes_.length != 0) { + _advanceEpochRewardStakers( + amountToAdd_, + indexes_, + numberOfEpochs_, + bucketSubsetToUpdate_ + ); + } + } + + function _getStakedPosition( + uint256 bucketIndex_, + uint256 amountToAdd_ + ) internal returns (uint256 tokenId_, uint256[] memory indexes_) { + + // Check for exisiting staked positions in RewardsManager + uint256[] memory tokenIds = getStakedTokenIdsByActor(address(_actor)); + + if (tokenIds.length != 0 ) { + // use existing position NFT + tokenId_ = tokenIds[0]; + indexes_ = getBucketIndexesByTokenId(tokenId_); + } else { + // retreive or create a NFT position + (tokenId_, indexes_)= _getNFTPosition(bucketIndex_, amountToAdd_); + + // approve rewards contract to transfer token + _positionManager.approve(address(_rewardsManager), tokenId_); + + // stake the position + _stake(tokenId_); + } + } +} diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedBasePositionHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedBasePositionHandler.sol index 093b902e1..3f55f3b95 100644 --- a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedBasePositionHandler.sol +++ b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedBasePositionHandler.sol @@ -1,18 +1,12 @@ - // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.18; import '@std/Test.sol'; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; -import { Strings } from '@openzeppelin/contracts/utils/Strings.sol'; - -import { IPositionManagerOwnerActions } from 'src/interfaces/position/IPositionManagerOwnerActions.sol'; -import { _depositFeeRate } from 'src/libraries/helpers/PoolHelper.sol'; -import { Maths } from "src/libraries/internal/Maths.sol"; -import { PositionManager } from 'src/PositionManager.sol'; -import { RewardsManager } from 'src/RewardsManager.sol'; +import { PositionManager } from 'src/PositionManager.sol'; +import { RewardsManager } from 'src/RewardsManager.sol'; /** * @dev this contract manages multiple lenders @@ -24,33 +18,27 @@ abstract contract UnboundedBasePositionHandler is Test { PositionManager internal _positionManager; RewardsManager internal _rewardsManager; - uint256 MAX_AJNA_AMOUNT = vm.envOr("MAX_AJNA_AMOUNT_ERC20", uint256(100_000_000 * 1e18)); + bytes32 internal _poolHash; - // Position invariant test state // + uint256 MAX_AJNA_AMOUNT = vm.envOr("MAX_AJNA_AMOUNT_ERC20", uint256(100_000_000 * 1e18)); - // used for PM1_PM2_PM3 tracking - mapping(uint256 => EnumerableSet.UintSet) internal tokenIdsByBucketIndex; + // positionManager & rewardsManager EnumerableSet.UintSet internal bucketIndexesWithPosition; - - // used for removing all CT and QT to reset bucket exchange rate - mapping(uint256 => address) internal actorByTokenId; - mapping(address => EnumerableSet.UintSet) internal tokenIdsByActor; mapping(uint256 => EnumerableSet.UintSet) internal bucketIndexesByTokenId; - // used to track LP changes in `_redeemPositions()` and `_memorializePositions()` - mapping(uint256 => uint256) internal bucketIndexToActorPositionManLps; - mapping(uint256 => uint256) internal bucketIndexToPositionManPoolLps; - mapping(uint256 => uint256) internal bucketIndexToActorPoolLps; + // positionManager + mapping(uint256 => EnumerableSet.UintSet) internal tokenIdsByBucketIndex; + mapping(address => EnumerableSet.UintSet) internal tokenIdsByActor; + // used to track changes in `_redeemPositions()` and `_memorializePositions()` + mapping(uint256 => uint256) internal actorLpsBefore; + mapping(uint256 => uint256) internal posManLpsBefore; mapping(uint256 => uint256) internal bucketIndexToDepositTime; - // Rewards invariant test state // + // rewardsManager + mapping(address => EnumerableSet.UintSet) internal stakedTokenIdsByActor; mapping(uint256 => uint256) public rewardsClaimedPerEpoch; // staking rewards per epoch mapping(uint256 => uint256) public updateRewardsClaimedPerEpoch; // updating rewards per epoch - mapping(uint256 => uint256) public rewardsAlreadyClaimed; // tracks rewards already claimed - uint256 public totalStakerRewPerEpoch; // amount of reserve decrease - uint256 public totalUpdaterRewPerEpoch; // amount of reserve increase - using EnumerableSet for EnumerableSet.UintSet; function getBucketIndexesWithPosition() public view returns(uint256[] memory) { @@ -69,6 +57,10 @@ abstract contract UnboundedBasePositionHandler is Test { return tokenIdsByActor[actor_].values(); } + function getStakedTokenIdsByActor(address actor_) public view returns(uint256[] memory) { + return stakedTokenIdsByActor[actor_].values(); + } + function _ensurePositionsManagerError(bytes memory err_) internal pure { bytes32 err = keccak256(err_); @@ -82,11 +74,14 @@ abstract contract UnboundedBasePositionHandler is Test { err == keccak256(abi.encodeWithSignature("NotAjnaPool()")) || err == keccak256(abi.encodeWithSignature("RemovePositionFailed()")) || err == keccak256(abi.encodeWithSignature("WrongPool()")) || + err == keccak256(abi.encodeWithSignature("NoAllowance()")) || err == keccak256(abi.encodeWithSignature("MoveToSameIndex()")) || err == keccak256(abi.encodeWithSignature("RemoveDepositLockedByAuctionDebt()")) || err == keccak256(abi.encodeWithSignature("DustAmountNotExceeded()")) || err == keccak256(abi.encodeWithSignature("InvalidIndex()")) || - err == keccak256(abi.encodeWithSignature("LUPBelowHTP()")), + err == keccak256(abi.encodeWithSignature("LUPBelowHTP()")) || + err == keccak256(abi.encodeWithSignature("InsufficientLP()")) || + err == keccak256(abi.encodeWithSignature("AuctionNotCleared()")), "Unexpected revert error" ); } diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC721PoolPositionsHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC721PoolPositionsHandler.sol deleted file mode 100644 index e1e6acbf7..000000000 --- a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC721PoolPositionsHandler.sol +++ /dev/null @@ -1,313 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity 0.8.18; - -import '../../../../utils/DSTestPlus.sol'; -import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; - -import { IPositionManagerOwnerActions } from 'src/interfaces/position/IPositionManagerOwnerActions.sol'; -import { - _depositFeeRate, - _lpToQuoteToken, - _priceAt - } from 'src/libraries/helpers/PoolHelper.sol'; -import { Maths } from "src/libraries/internal/Maths.sol"; - -import { BaseERC721PoolHandler } from '../../../ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol'; -import { UnboundedBasePositionHandler } from './UnboundedBasePositionHandler.sol'; - -/** - * @dev this contract manages multiple lenders - * @dev methods in this contract are called in random order - * @dev randomly selects a lender contract to make a txn - */ -abstract contract UnboundedERC721PoolPositionsHandler is UnboundedBasePositionHandler, BaseERC721PoolHandler { - - using EnumerableSet for EnumerableSet.UintSet; - - function _memorializePositions( - uint256 tokenId_, - uint256[] memory indexes_ - ) internal { - numberOfCalls['UBPositionHandler.memorialize']++; - - for(uint256 i=0; i < indexes_.length; i++) { - - // store vals pre action to check after memorializing: - (uint256 actorLps, uint256 actorDepositTime) = _pool.lenderInfo(indexes_[i], address(_actor)); - (uint256 posManLps, uint256 posManDepositTime) = _pool.lenderInfo(indexes_[i], address(_positionManager)); - - bucketIndexToActorPoolLps[indexes_[i]] = actorLps; - bucketIndexToPositionManPoolLps[indexes_[i]] = posManLps; - - // positionManager is assigned the most recent depositTime - bucketIndexToDepositTime[indexes_[i]] = (actorDepositTime >= posManDepositTime) ? actorDepositTime : posManDepositTime; - - // assert that the underlying LP balance in PositionManager is 0 - (uint256 posPreActionLps,) = _positionManager.getPositionInfo(tokenId_, indexes_[i]); - require(posPreActionLps == 0, "tokenID already has lps associated on memorialize"); - - } - - try _positionManager.memorializePositions(address(_pool), tokenId_, indexes_) { - - // track created positions - for ( uint256 i = 0; i < indexes_.length; i++) { - // PM1_PM2_PM3 tracking - bucketIndexesWithPosition.add(indexes_[i]); - tokenIdsByBucketIndex[indexes_[i]].add(tokenId_); - - // info used to tearDown buckets - bucketIndexesByTokenId[tokenId_].add(indexes_[i]); - } - - // info used track actors positions - actorByTokenId[tokenId_] = address(_actor); - tokenIdsByActor[address(_actor)].add(tokenId_); - - // Post action Checks // - for(uint256 i=0; i < indexes_.length; i++) { - uint256 bucketIndex = indexes_[i]; - - // assert that the LP that now exists in the pool contract matches the amount added by the actor - (uint256 poolLps, uint256 poolDepositTime) = _pool.lenderInfo(bucketIndex, address(_positionManager)); - require(poolLps == bucketIndexToActorPoolLps[bucketIndex] + bucketIndexToPositionManPoolLps[bucketIndex], - "PM7: pool contract lps do not match amount added by actor"); - - require(poolDepositTime == bucketIndexToDepositTime[bucketIndex], - "PM7: positionManager depositTime does not match most recent depositTime"); - - // assert that the positionManager LP balance of the actor has increased - (uint256 posLps,) = _positionManager.getPositionInfo(tokenId_, bucketIndex); - require(posLps == bucketIndexToActorPoolLps[bucketIndex], - "PM7: positionManager lps do not match amount added by actor"); - - delete bucketIndexToActorPoolLps[bucketIndex]; - delete bucketIndexToPositionManPoolLps[bucketIndex]; - delete bucketIndexToDepositTime[bucketIndex]; - } - - } catch (bytes memory err) { - _ensurePositionsManagerError(err); - } - } - - function _mint() internal returns (uint256 tokenIdResult) { - numberOfCalls['UBPositionHandler.mint']++; - try _positionManager.mint(address(_pool), _actor, keccak256("ERC721_NON_SUBSET_HASH")) returns (uint256 tokenId) { - - tokenIdResult = tokenId; - - // Post Action Checks // - // assert that the minter is the owner - require(_positionManager.ownerOf(tokenId) == _actor, "PM4: minter is not owner"); - - // assert that poolKey is returns correct pool address - address poolAddress = _positionManager.poolKey(tokenId); - require(poolAddress == address(_pool), "PM4: poolKey does not match pool address"); - - // assert that no positions are associated with this tokenId - uint256[] memory posIndexes = _positionManager.getPositionIndexes(tokenId); - require(posIndexes.length == 0, "PM4: positions are associated with tokenId"); - - } catch (bytes memory err) { - _ensurePositionsManagerError(err); - } - } - - function _redeemPositions( - uint256 tokenId_, - uint256[] memory indexes_ - ) internal { - numberOfCalls['UBPositionHandler.redeem']++; - - address preActionOwner = _positionManager.ownerOf(tokenId_); - - for (uint256 i=0; i < indexes_.length; i++) { - - // store vals in mappings to check lps - (uint256 poolPreActionActorLps,) = _pool.lenderInfo(indexes_[i], preActionOwner); - (uint256 poolPreActionPosManLps,) = _pool.lenderInfo(indexes_[i], address(_positionManager)); - - bucketIndexToActorPoolLps[indexes_[i]] = poolPreActionActorLps; - bucketIndexToPositionManPoolLps[indexes_[i]] = poolPreActionPosManLps; - - // assert that the underlying LP balance in PositionManager is greater than 0 - (uint256 posPreActionLps,) = _positionManager.getPositionInfo(tokenId_, indexes_[i]); - require(posPreActionLps > 0, "tokenID does not have lps associated on redemption"); - } - - try _positionManager.redeemPositions(address(_pool), tokenId_, indexes_) { - // remove tracked positions - for ( uint256 i = 0; i < indexes_.length; i++) { - bucketIndexesWithPosition.remove(indexes_[i]); - tokenIdsByBucketIndex[indexes_[i]].remove(tokenId_); - } - - // info for tear down - delete actorByTokenId[tokenId_]; - delete bucketIndexesByTokenId[tokenId_]; - tokenIdsByActor[address(_actor)].remove(tokenId_); - - // Post action Checks // - // assert that the minter is still the owner - require(_positionManager.ownerOf(tokenId_) == preActionOwner, - 'PM8: previous owner is no longer owner on redemption'); - - // assert that poolKey is still same - address poolAddress = _positionManager.poolKey(tokenId_); - require(poolAddress == address(_pool), 'PM8: poolKey has changed on redemption'); - - // assert that no positions are associated with this tokenId - uint256[] memory posIndexes = _positionManager.getPositionIndexes(tokenId_); - require(posIndexes.length == 0, 'PM8: positions still exist after redemption'); - - for(uint256 i=0; i < indexes_.length; i++) { - uint256 bucketIndex = indexes_[i]; - - uint256 actorPoolLps = bucketIndexToActorPoolLps[bucketIndex]; - uint256 positionManPoolLps = bucketIndexToPositionManPoolLps[bucketIndex]; - - (uint256 poolActorLps,) = _pool.lenderInfo(bucketIndex, preActionOwner); - (uint256 poolPosLps,) = _pool.lenderInfo(bucketIndex, address(_positionManager)); - - // assert PositionsMan LP in pool matches the amount redeemed by actor - // positionMan has now == positionMan pre - actor's LP change - require(poolPosLps == positionManPoolLps - (poolActorLps - actorPoolLps), - "PM8: positionManager's pool contract lps do not match amount redeemed by actor"); - - // assert actor LP in pool matches amount removed from the posMan's position - // assert actor LP in pool = what actor LP had pre + what LP positionManager redeemed to actor - require(poolActorLps == actorPoolLps + (positionManPoolLps - poolPosLps), - "PM8: actor's pool contract lps do not match amount redeemed by actor"); - - // assert that the underlying LP balance in PositionManager is zero - (uint256 posLps, uint256 posDepositTime) = _positionManager.getPositionInfo(tokenId_, bucketIndex); - require(posLps == 0, "PM8: tokenId has lps after redemption"); - require(posDepositTime == 0, "PM8: tokenId has depositTime after redemption"); - - // delete mappings for reuse - delete bucketIndexToActorPoolLps[bucketIndex]; - delete bucketIndexToPositionManPoolLps[bucketIndex]; - } - - } catch (bytes memory err) { - _ensurePositionsManagerError(err); - } - - } - - function _getQuoteAtIndex( - uint256 lp, - uint256 index - ) internal view returns (uint256 quoteAtIndex_) { - // retrieve info of bucket from pool - ( - uint256 bucketLP, - uint256 bucketCollateral, - , - uint256 bucketDeposit, - ) = _pool.bucketInfo(index); - - // calculate the max amount of quote tokens that can be moved, given the tracked LP - quoteAtIndex_ = _lpToQuoteToken( - bucketLP, - bucketCollateral, - bucketDeposit, - lp, - bucketDeposit, - _priceAt(index) - ); - } - - function _moveLiquidity( - uint256 tokenId_, - uint256 fromIndex_, - uint256 toIndex_ - ) internal { - numberOfCalls['UBPositionHandler.moveLiquidity']++; - - // update interest so pre and post token amounts are equal - _pool.updateInterest(); - - // fromIndex values - (uint256 preActionFromLps,) = _positionManager.getPositionInfo(tokenId_, fromIndex_); - uint256 preActionFromIndexQuote = _getQuoteAtIndex(preActionFromLps, fromIndex_); - - // toIndex values - (uint256 preActionToLps,) = _positionManager.getPositionInfo(tokenId_, toIndex_); - uint256 preActionToIndexQuote = _getQuoteAtIndex(preActionToLps, toIndex_); - - /** - * @notice Struct holding parameters for moving the liquidity of a position. - */ - - try _positionManager.moveLiquidity(address(_pool), tokenId_, fromIndex_, toIndex_, block.timestamp + 30, false) { - - bucketIndexesByTokenId[tokenId_].add(toIndex_); - bucketIndexesByTokenId[tokenId_].remove(fromIndex_); - - // Post Action Checks // - // remove tracked positios - bucketIndexesWithPosition.remove(fromIndex_); - tokenIdsByBucketIndex[fromIndex_].remove(tokenId_); - - // track created positions - bucketIndexesWithPosition.add(toIndex_); - tokenIdsByBucketIndex[toIndex_].add(tokenId_); - - // assert that fromIndex LP and deposit time are both zero - (uint256 fromLps, uint256 fromDepositTime) = _positionManager.getPositionInfo(tokenId_, fromIndex_); - require(fromLps == 0, "PM6: from bucket still has LPs after move"); - require(fromDepositTime == 0, "PM6: from bucket still has deposit time after move"); - - // assert that toIndex LP is increased and deposit time matches positionManagers depositTime pre action - (uint256 toLps, uint256 toDepositTime) = _positionManager.getPositionInfo(tokenId_, toIndex_); - (,uint256 postActionDepositTime)= _pool.lenderInfo(toIndex_, address(_positionManager)); - require(toLps >= preActionToLps, "PM6: to bucket lps have not increased"); // difficult to estimate LPS, assert that it is greater than - require(toDepositTime == postActionDepositTime, "PM6: to bucket deposit time does not match positionManager"); - - // get post action QT represented in positionManager for tokenID - uint256 postActionFromIndexQuote = _getQuoteAtIndex(fromLps, fromIndex_); - uint256 postActionToIndexQuote = _getQuoteAtIndex(toLps, toIndex_); - - // positionManager's total QT postAction is less than or equal to preAction - // can be less than or equal due to fee on movements above -> below LUP - - greaterThanWithinDiff( - preActionFromIndexQuote + preActionToIndexQuote, - postActionFromIndexQuote + postActionToIndexQuote, - 1, - "PM6: positiionManager QT balance has increased by `1` margin" - ); - - - } catch (bytes memory err) { - _ensurePositionsManagerError(err); - } - } - - - - function _burn( - uint256 tokenId_ - ) internal { - numberOfCalls['UBPositionHandler.burn']++; - try _positionManager.burn(address(_pool), tokenId_) { - // Post Action Checks // - // should revert if token id is burned - vm.expectRevert("ERC721: invalid token ID"); - require(_positionManager.ownerOf(tokenId_) == address(0), "PM5: ownership is not zero address"); - - // assert that poolKey is returns zero address - address poolAddress = _positionManager.poolKey(tokenId_); - require(poolAddress == address(0), "PM5: poolKey has not been reset on burn"); - - // assert that no positions are associated with this tokenId - uint256[] memory posIndexes = _positionManager.getPositionIndexes(tokenId_); - require(posIndexes.length == 0, "PM5: positions still exist after burn"); - } catch (bytes memory err) { - _ensurePositionsManagerError(err); - } - } -} diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC20PoolPositionsHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedPositionPoolHandler.sol similarity index 78% rename from tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC20PoolPositionsHandler.sol rename to tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedPositionPoolHandler.sol index e9204dcbf..154fc10f4 100644 --- a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedERC20PoolPositionsHandler.sol +++ b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedPositionPoolHandler.sol @@ -16,15 +16,21 @@ import { Maths } from "src/libraries/internal/Maths.sol"; import { BaseERC20PoolHandler } from '../../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol'; import { UnboundedBasePositionHandler } from './UnboundedBasePositionHandler.sol'; +import { BaseHandler } from '../../../base/handlers/unbounded/BaseHandler.sol'; + /** * @dev this contract manages multiple lenders * @dev methods in this contract are called in random order * @dev randomly selects a lender contract to make a txn */ -abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHandler, BaseERC20PoolHandler { +abstract contract UnboundedPositionPoolHandler is UnboundedBasePositionHandler, BaseHandler { using EnumerableSet for EnumerableSet.UintSet; + /*********************************/ + /*** Position Helper Functions ***/ + /*********************************/ + function _memorializePositions( uint256 tokenId_, uint256[] memory indexes_ @@ -34,67 +40,68 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan for(uint256 i=0; i < indexes_.length; i++) { // store vals pre action to check after memorializing: - (uint256 actorLps, uint256 actorDepositTime) = _pool.lenderInfo(indexes_[i], address(_actor)); - (uint256 posManLps, uint256 posManDepositTime) = _pool.lenderInfo(indexes_[i], address(_positionManager)); + (uint256 poolPreActionActorLps, uint256 actorDepositTime) = _pool.lenderInfo(indexes_[i], address(_actor)); + (uint256 poolPreActionPosManLps, uint256 posManDepositTime) = _pool.lenderInfo(indexes_[i], address(_positionManager)); - bucketIndexToActorPoolLps[indexes_[i]] = actorLps; - bucketIndexToPositionManPoolLps[indexes_[i]] = posManLps; + actorLpsBefore[indexes_[i]] = poolPreActionActorLps; + posManLpsBefore[indexes_[i]] = poolPreActionPosManLps; // positionManager is assigned the most recent depositTime bucketIndexToDepositTime[indexes_[i]] = (actorDepositTime >= posManDepositTime) ? actorDepositTime : posManDepositTime; - - // assert that the underlying LP balance in PositionManager is 0 - (uint256 posPreActionLps,) = _positionManager.getPositionInfo(tokenId_, indexes_[i]); - require(posPreActionLps == 0, "tokenID already has lps associated on memorialize"); - } try _positionManager.memorializePositions(address(_pool), tokenId_, indexes_) { // track created positions for ( uint256 i = 0; i < indexes_.length; i++) { - // PM1_PM2_PM3 tracking - bucketIndexesWithPosition.add(indexes_[i]); - tokenIdsByBucketIndex[indexes_[i]].add(tokenId_); - - // info used to tearDown buckets - bucketIndexesByTokenId[tokenId_].add(indexes_[i]); - } + uint256 bucketIndex = indexes_[i]; - // info used track actors positions - actorByTokenId[tokenId_] = address(_actor); - tokenIdsByActor[address(_actor)].add(tokenId_); + bucketIndexesWithPosition.add(bucketIndex); + tokenIdsByBucketIndex[bucketIndex].add(tokenId_); - // Post action Checks // - for(uint256 i=0; i < indexes_.length; i++) { - uint256 bucketIndex = indexes_[i]; + // info used to tearDown buckets + bucketIndexesByTokenId[tokenId_].add(bucketIndex); - // assert that the LP that now exists in the pool contract matches the amount added by the actor (uint256 poolLps, uint256 poolDepositTime) = _pool.lenderInfo(bucketIndex, address(_positionManager)); - require(poolLps == bucketIndexToActorPoolLps[bucketIndex] + bucketIndexToPositionManPoolLps[bucketIndex], - "PM7: pool contract lps do not match amount added by actor"); require(poolDepositTime == bucketIndexToDepositTime[bucketIndex], "PM7: positionManager depositTime does not match most recent depositTime"); + // assert that the LP that now exists in the pool contract matches the amount added by the actor + require(poolLps == actorLpsBefore[bucketIndex] + posManLpsBefore[bucketIndex], + "PM7: pool contract lps do not match amount added by actor"); + // assert that the positionManager LP balance of the actor has increased (uint256 posLps,) = _positionManager.getPositionInfo(tokenId_, bucketIndex); - require(posLps == bucketIndexToActorPoolLps[bucketIndex], + require(posLps == actorLpsBefore[bucketIndex], "PM7: positionManager lps do not match amount added by actor"); - delete bucketIndexToActorPoolLps[bucketIndex]; - delete bucketIndexToPositionManPoolLps[bucketIndex]; + delete actorLpsBefore[bucketIndex]; + delete posManLpsBefore[bucketIndex]; delete bucketIndexToDepositTime[bucketIndex]; } + // info used track actors positions + tokenIdsByActor[address(_actor)].add(tokenId_); + } catch (bytes memory err) { + + // cleanup buckets so they don't interfere with future calls + for ( uint256 i = 0; i < indexes_.length; i++) { + uint256 bucketIndex = indexes_[i]; + + delete actorLpsBefore[bucketIndex]; + delete posManLpsBefore[bucketIndex]; + delete bucketIndexToDepositTime[bucketIndex]; + } _ensurePositionsManagerError(err); } } function _mint() internal returns (uint256 tokenIdResult) { numberOfCalls['UBPositionHandler.mint']++; - try _positionManager.mint(address(_pool), _actor, keccak256("ERC20_NON_SUBSET_HASH")) returns (uint256 tokenId) { + + try _positionManager.mint(address(_pool), _actor, _poolHash) returns (uint256 tokenId) { tokenIdResult = tokenId; @@ -125,60 +132,39 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan for (uint256 i=0; i < indexes_.length; i++) { - // store vals in mappings to check lps (uint256 poolPreActionActorLps,) = _pool.lenderInfo(indexes_[i], preActionOwner); (uint256 poolPreActionPosManLps,) = _pool.lenderInfo(indexes_[i], address(_positionManager)); - bucketIndexToActorPoolLps[indexes_[i]] = poolPreActionActorLps; - bucketIndexToPositionManPoolLps[indexes_[i]] = poolPreActionPosManLps; - - // assert that the underlying LP balance in PositionManager is greater than 0 - (uint256 posPreActionLps,) = _positionManager.getPositionInfo(tokenId_, indexes_[i]); - require(posPreActionLps > 0, "tokenID does not have lps associated on redemption"); + // store vals in mappings to check lps + actorLpsBefore[indexes_[i]] = poolPreActionActorLps; + posManLpsBefore[indexes_[i]] = poolPreActionPosManLps; } try _positionManager.redeemPositions(address(_pool), tokenId_, indexes_) { + // remove tracked positions for ( uint256 i = 0; i < indexes_.length; i++) { - bucketIndexesWithPosition.remove(indexes_[i]); - tokenIdsByBucketIndex[indexes_[i]].remove(tokenId_); - } - - // info for tear down - delete actorByTokenId[tokenId_]; - delete bucketIndexesByTokenId[tokenId_]; - tokenIdsByActor[address(_actor)].remove(tokenId_); - - // Post action Checks // - // assert that the minter is still the owner - require(_positionManager.ownerOf(tokenId_) == preActionOwner, - 'PM8: previous owner is no longer owner on redemption'); - - // assert that poolKey is still same - address poolAddress = _positionManager.poolKey(tokenId_); - require(poolAddress == address(_pool), 'PM8: poolKey has changed on redemption'); + uint256 bucketIndex = indexes_[i]; - // assert that no positions are associated with this tokenId - uint256[] memory posIndexes = _positionManager.getPositionIndexes(tokenId_); - require(posIndexes.length == 0, 'PM8: positions still exist after redemption'); + tokenIdsByBucketIndex[bucketIndex].remove(tokenId_); - for(uint256 i=0; i < indexes_.length; i++) { - uint256 bucketIndex = indexes_[i]; + // if no other positions exist for this bucketIndex, remove from bucketIndexesWithPosition + if (getTokenIdsByBucketIndex(bucketIndex).length == 0) { - uint256 actorPoolLps = bucketIndexToActorPoolLps[bucketIndex]; - uint256 positionManPoolLps = bucketIndexToPositionManPoolLps[bucketIndex]; + bucketIndexesWithPosition.remove(bucketIndex); + } (uint256 poolActorLps,) = _pool.lenderInfo(bucketIndex, preActionOwner); (uint256 poolPosLps,) = _pool.lenderInfo(bucketIndex, address(_positionManager)); // assert PositionsMan LP in pool matches the amount redeemed by actor // positionMan has now == positionMan pre - actor's LP change - require(poolPosLps == positionManPoolLps - (poolActorLps - actorPoolLps), + require(poolPosLps == posManLpsBefore[bucketIndex] - (poolActorLps - actorLpsBefore[bucketIndex]), "PM8: positionManager's pool contract lps do not match amount redeemed by actor"); // assert actor LP in pool matches amount removed from the posMan's position // assert actor LP in pool = what actor LP had pre + what LP positionManager redeemed to actor - require(poolActorLps == actorPoolLps + (positionManPoolLps - poolPosLps), + require(poolActorLps == actorLpsBefore[bucketIndex] + (posManLpsBefore[bucketIndex] - poolPosLps), "PM8: actor's pool contract lps do not match amount redeemed by actor"); // assert that the underlying LP balance in PositionManager is zero @@ -187,14 +173,37 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan require(posDepositTime == 0, "PM8: tokenId has depositTime after redemption"); // delete mappings for reuse - delete bucketIndexToActorPoolLps[bucketIndex]; - delete bucketIndexToPositionManPoolLps[bucketIndex]; + delete actorLpsBefore[bucketIndex]; + delete posManLpsBefore[bucketIndex]; } + // info for tear down + delete bucketIndexesByTokenId[tokenId_]; + tokenIdsByActor[address(_actor)].remove(tokenId_); + + // assert that the minter is still the owner + require(_positionManager.ownerOf(tokenId_) == preActionOwner, + 'PM8: previous owner is no longer owner on redemption'); + + // assert that poolKey address matches pool address + require(_positionManager.poolKey(tokenId_) == address(_pool), + 'PM8: poolKey has changed on redemption'); + + // assert that no positions are associated with this tokenId + uint256[] memory posIndexes = _positionManager.getPositionIndexes(tokenId_); + require(posIndexes.length == 0, 'PM8: positions still exist after redemption'); + } catch (bytes memory err) { + + for ( uint256 i = 0; i < indexes_.length; i++) { + uint256 bucketIndex = indexes_[i]; + // delete mappings for reuse + delete actorLpsBefore[bucketIndex]; + delete posManLpsBefore[bucketIndex]; + } + _ensurePositionsManagerError(err); } - } function _getQuoteAtIndex( @@ -248,10 +257,14 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan bucketIndexesByTokenId[tokenId_].remove(fromIndex_); // Post Action Checks // - // remove tracked positios - bucketIndexesWithPosition.remove(fromIndex_); + // remove tracked positions tokenIdsByBucketIndex[fromIndex_].remove(tokenId_); + // if no other positions exist for this bucketIndex, remove from bucketIndexesWithPosition + if (getTokenIdsByBucketIndex(fromIndex_).length == 0) { + bucketIndexesWithPosition.remove(fromIndex_); + } + // track created positions bucketIndexesWithPosition.add(toIndex_); tokenIdsByBucketIndex[toIndex_].add(tokenId_); @@ -273,7 +286,6 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan // positionManager's total QT postAction is less than or equal to preAction // can be less than or equal due to fee on movements above -> below LUP - greaterThanWithinDiff( preActionFromIndexQuote + preActionToIndexQuote, postActionFromIndexQuote + postActionToIndexQuote, @@ -281,7 +293,6 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan "PM6: positiionManager QT balance has increased by `1` margin" ); - } catch (bytes memory err) { _ensurePositionsManagerError(err); } @@ -304,9 +315,9 @@ abstract contract UnboundedERC20PoolPositionsHandler is UnboundedBasePositionHan // assert that no positions are associated with this tokenId uint256[] memory posIndexes = _positionManager.getPositionIndexes(tokenId_); require(posIndexes.length == 0, "PM5: positions still exist after burn"); + } catch (bytes memory err) { _ensurePositionsManagerError(err); } } -} - +} \ No newline at end of file diff --git a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedRewardsHandler.sol b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedRewardsPoolHandler.sol similarity index 78% rename from tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedRewardsHandler.sol rename to tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedRewardsPoolHandler.sol index 6bd8eb746..313cd545e 100644 --- a/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedRewardsHandler.sol +++ b/tests/forge/invariants/PositionsAndRewards/handlers/unbounded/UnboundedRewardsPoolHandler.sol @@ -2,25 +2,37 @@ pragma solidity 0.8.18; +import '../../../../utils/DSTestPlus.sol'; import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol'; import { IPositionManagerOwnerActions } from 'src/interfaces/position/IPositionManagerOwnerActions.sol'; -import { _depositFeeRate } from 'src/libraries/helpers/PoolHelper.sol'; +import { + _depositFeeRate, + _lpToQuoteToken, + _priceAt + } from 'src/libraries/helpers/PoolHelper.sol'; import { Maths } from "src/libraries/internal/Maths.sol"; -import { UnboundedERC20PoolPositionsHandler } from './UnboundedERC20PoolPositionsHandler.sol'; +import { BaseERC20PoolHandler } from '../../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol'; +import { UnboundedBasePositionHandler } from './UnboundedBasePositionHandler.sol'; -import { _depositFeeRate } from 'src/libraries/helpers/PoolHelper.sol'; +import { BaseHandler } from '../../../base/handlers/unbounded/BaseHandler.sol'; + +import { UnboundedPositionPoolHandler } from './UnboundedPositionPoolHandler.sol'; /** * @dev this contract manages multiple lenders * @dev methods in this contract are called in random order * @dev randomly selects a lender contract to make a txn */ -abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler { +abstract contract UnboundedRewardsPoolHandler is UnboundedPositionPoolHandler { using EnumerableSet for EnumerableSet.UintSet; + /********************************/ + /*** Rewards Helper Functions ***/ + /********************************/ + function _stake( uint256 tokenId_ ) internal updateLocalStateAndPoolInterest { @@ -33,6 +45,9 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler tokenIdsByActor[address(_rewardsManager)].add(tokenId_); tokenIdsByActor[address(_actor)].remove(tokenId_); + // staked position is tracked + stakedTokenIdsByActor[address(_actor)].add(tokenId_); + require(_positionManager.ownerOf(tokenId_) == address(_rewardsManager), "RW5: owner should be rewardsManager"); } catch (bytes memory err) { @@ -53,23 +68,29 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler // loop over all epochs that are going to be uint256 totalRewardsEarnedPreAction; + + uint256[] memory rewardsEarnedInEpochPreAction = new uint256[](_pool.currentBurnEpoch() + 1); + for (uint256 epoch = preActionLastClaimedEpoch; epoch <= _pool.currentBurnEpoch(); epoch++) { - + // for epochs already claimed by the staker, `rewardsClaimed()` should go unchanged if (_rewardsManager.isEpochClaimed(tokenId_, epoch)) { - rewardsAlreadyClaimed[epoch] = _rewardsManager.rewardsClaimed(epoch); + rewardsEarnedInEpochPreAction[epoch] = _rewardsManager.rewardsClaimed(epoch); } // total the rewards earned pre action totalRewardsEarnedPreAction += _rewardsManager.rewardsClaimed(epoch) + _rewardsManager.updateRewardsClaimed(epoch); - } - + } + try _rewardsManager.unstake(tokenId_) { // actor should receive tokenId, positionManager loses ownership tokenIdsByActor[address(_actor)].add(tokenId_); tokenIdsByActor[address(_rewardsManager)].remove(tokenId_); + // staked position is no longer tracked + stakedTokenIdsByActor[address(_actor)].remove(tokenId_); + // balance changes uint256 actorAjnaGain = _ajna.balanceOf(_actor) - actorAjnaBalanceBeforeClaim; @@ -83,8 +104,8 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler "RW6: epoch after claim rewards is not claimed"); } - if (rewardsAlreadyClaimed[epoch] != 0) { - require(rewardsAlreadyClaimed[epoch] == _rewardsManager.rewardsClaimed(epoch), + if (rewardsEarnedInEpochPreAction[epoch] > 0) { + require(rewardsEarnedInEpochPreAction[epoch] == _rewardsManager.rewardsClaimed(epoch), "RW10: staker has claimed rewards from the same epoch twice"); } @@ -132,6 +153,13 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler try _rewardsManager.emergencyUnstake(tokenId_) { + // actor should receive tokenId, positionManager loses ownership + tokenIdsByActor[address(_actor)].add(tokenId_); + tokenIdsByActor[address(_rewardsManager)].remove(tokenId_); + + // staked position is no longer tracked + stakedTokenIdsByActor[address(_actor)].remove(tokenId_); + // loop over all epochs that have occured uint256 totalRewardsEarnedPostAction; for (uint256 epoch = 0; epoch <= _pool.currentBurnEpoch(); epoch++) { @@ -140,10 +168,6 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler totalRewardsEarnedPostAction += _rewardsManager.rewardsClaimed(epoch) + _rewardsManager.updateRewardsClaimed(epoch); } - // actor should receive tokenId, positionManager loses ownership - tokenIdsByActor[address(_actor)].add(tokenId_); - tokenIdsByActor[address(_rewardsManager)].remove(tokenId_); - require(totalRewardsEarnedPreAction == totalRewardsEarnedPostAction, "rewards were earned on emergency unstake"); @@ -203,15 +227,16 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler // loop over all epochs that are going to be uint256 totalRewardsEarnedPreAction; + + uint256[] memory rewardsEarnedInEpochPreAction = new uint256[](_pool.currentBurnEpoch() + 1); for (uint256 epoch = preActionLastClaimedEpoch; epoch <= _pool.currentBurnEpoch(); epoch++) { // track epochs that have already been claimed if (_rewardsManager.isEpochClaimed(tokenId_, epoch)) { - rewardsAlreadyClaimed[epoch] = _rewardsManager.rewardsClaimed(epoch); + rewardsEarnedInEpochPreAction[epoch] = _rewardsManager.rewardsClaimed(epoch); } - - // total staking rewards earned across all actors in epoch pre action - totalRewardsEarnedPreAction += _rewardsManager.rewardsClaimed(epoch); + // total the rewards earned pre action + totalRewardsEarnedPreAction += _rewardsManager.rewardsClaimed(epoch) + _rewardsManager.updateRewardsClaimed(epoch); } try _rewardsManager.claimRewards(tokenId_, epoch_, 0) { @@ -229,16 +254,17 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler "RW6: epoch after claim rewards is not claimed"); } - if (rewardsAlreadyClaimed[epoch] != 0) { - require(rewardsAlreadyClaimed[epoch] == _rewardsManager.rewardsClaimed(epoch), + if (rewardsEarnedInEpochPreAction[epoch] > 0) { + require(rewardsEarnedInEpochPreAction[epoch] == _rewardsManager.rewardsClaimed(epoch), "RW10: staker has claimed rewards from the same epoch twice"); } - // total staking rewards earned across all actors in epoch post action - totalRewardsEarnedPostAction += _rewardsManager.rewardsClaimed(epoch); + // total rewards earned across all actors in epoch post action + totalRewardsEarnedPostAction += _rewardsManager.rewardsClaimed(epoch) + _rewardsManager.updateRewardsClaimed(epoch); - // reset staking rewards earned in epoch - rewardsClaimedPerEpoch[epoch] = _rewardsManager.rewardsClaimed(epoch); + // reset staking and updating rewards earned in epoch + rewardsClaimedPerEpoch[epoch] = _rewardsManager.rewardsClaimed(epoch); + updateRewardsClaimedPerEpoch[epoch] = _rewardsManager.updateRewardsClaimed(epoch); } (, , uint256 lastClaimedEpoch) = _rewardsManager.getStakeInfo(tokenId_); @@ -256,6 +282,29 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler } } + function _advanceEpochRewardStakers( + uint256 amountToAdd_, + uint256[] memory indexes_, + uint256 numberOfEpochs_, + uint256 bucketSubsetToUpdate_ + ) internal virtual; + + + function _randomizeExchangeRateIndexes( + uint256[] memory indexes_, + uint256 bucketSubsetToUpdate_ + ) internal pure returns (uint256[] memory boundBuckets_) { + + uint256 boundIndexes = constrictToRange(bucketSubsetToUpdate_, 0, indexes_.length); + boundBuckets_ = new uint256[](boundIndexes); + + if (boundBuckets_.length !=0) { + for (uint256 i = 0; i < boundIndexes; i++) { + boundBuckets_[i] = indexes_[i]; + } + } + } + function _ensureRewardsManagerError(bytes memory err_) internal pure { bytes32 err = keccak256(err_); @@ -270,4 +319,4 @@ abstract contract UnboundedRewardsHandler is UnboundedERC20PoolPositionsHandler "Unexpected revert error" ); } -} +} \ No newline at end of file diff --git a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol index 7748994be..3cc9ee2f4 100644 --- a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol @@ -61,7 +61,7 @@ abstract contract BaseHandler is Test { mapping(address => mapping(uint256 => uint256)) public lenderDepositTime; // mapping of lender address to bucket index to deposit time address[] public actors; - mapping(bytes => uint256) public numberOfCalls; // Logging + mapping(bytes => uint256) public numberOfCalls; // Logging mapping(bytes => uint256) public numberOfActions; // Logging mapping(address => uint256[]) public touchedBuckets; // Bucket tracking diff --git a/tests/forge/invariants/interfaces/IPositionsAndRewardsHandler.sol b/tests/forge/invariants/interfaces/IPositionsAndRewardsHandler.sol index a9ea056e8..e89d324e4 100644 --- a/tests/forge/invariants/interfaces/IPositionsAndRewardsHandler.sol +++ b/tests/forge/invariants/interfaces/IPositionsAndRewardsHandler.sol @@ -3,13 +3,16 @@ pragma solidity 0.8.18; interface IPositionsAndRewardsHandler { - - function rewardsClaimedPerEpoch(uint256) external view returns(uint256); - function updateRewardsClaimedPerEpoch(uint256) external view returns(uint256); - function epochToRewardsSnapshot(uint256) external view returns(uint256); - + // positionManager & rewardsManager function getBucketIndexesWithPosition() external view returns(uint256[] memory); - function getTokenIdsByBucketIndex(uint256) external view returns(uint256[] memory); function getBucketIndexesByTokenId(uint256) external view returns(uint256[] memory); + + // positionManager function getTokenIdsByActor() external view returns(uint256[] memory); + function getTokenIdsByBucketIndex(uint256) external view returns(uint256[] memory); + + // rewardsManager + function rewardsClaimedPerEpoch(uint256) external view returns(uint256); + function updateRewardsClaimedPerEpoch(uint256) external view returns(uint256); + function getStakedTokenIdsByActor() external view returns(uint256[] memory); } \ No newline at end of file diff --git a/tests/forge/regression/PositionAndRewards/RegressionTestPositionManager.t.sol b/tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolPositionManager.t.sol similarity index 92% rename from tests/forge/regression/PositionAndRewards/RegressionTestPositionManager.t.sol rename to tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolPositionManager.t.sol index 0e8cb9d97..dea6c5e7a 100644 --- a/tests/forge/regression/PositionAndRewards/RegressionTestPositionManager.t.sol +++ b/tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolPositionManager.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.18; import { ERC20PoolPositionsInvariants } from "../../invariants/PositionsAndRewards/ERC20PoolPositionsInvariants.t.sol"; -contract RegressionTestPositionManager is ERC20PoolPositionsInvariants { +contract RegressionTestERC20PoolPositionManager is ERC20PoolPositionsInvariants { function setUp() public override { super.setUp(); @@ -119,6 +119,16 @@ contract RegressionTestPositionManager is ERC20PoolPositionsInvariants { function test_regression_position_manager() external { _erc20positionHandler.redeemPositions(79335468733065507138817566659594782917024872257218805, 1889027018179489664211573893, 43578107449528230070726540147644518395094194018887636259089111851, 0); + } + // This EVM revert fires a `NoAllowance()` error. certain pool errors fire in positionManager calls + // fix: import specific pool errors into positionManager catch + // Add quote token was failing due to a rounding issue in amount supplied that resulted in an attempt to add zero QT to the pool + // fix: altered the addQuoteToken call in preMemorializePosition method + function test_regression_failure_evm_revert_5() external { + _erc20positionHandler.moveLiquidity(35855801402931413691347, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2, 3); + _erc20positionHandler.mint(1, 6981663636108743728471595901895228956316688268); + _erc20positionHandler.failed(); + _erc20positionHandler.redeemPositions(9941, 12031, 22960, 1397); } } diff --git a/tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolRewardsManager.t.sol b/tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolRewardsManager.t.sol new file mode 100644 index 000000000..33b9ffe90 --- /dev/null +++ b/tests/forge/regression/PositionAndRewards/RegressionTestERC20PoolRewardsManager.t.sol @@ -0,0 +1,480 @@ + +pragma solidity 0.8.18; + +import { ERC20PoolRewardsInvariants } from "../../invariants/PositionsAndRewards/ERC20PoolRewardsInvariants.t.sol"; + +contract RegressionTestERC20PoolRewardsManager is ERC20PoolRewardsInvariants { + + function setUp() public override { + super.setUp(); + } + + // Test was failing due to incorrect removal of local tracked positions(tokenIdsByBucketIndex, bucketIndexesWithPosition) in handlers + // Fixed by not removing local tracked positions + function test_regression_rewards_PM1_1() public { + _erc20poolrewardsHandler.unstake(156983341, 3, 1057, 627477641256361, 0, 0); + _erc20poolrewardsHandler.settleAuction(2108881198342615861856429474, 922394580216134598, 4169158839, 1000000019773478651); + invariant_positions_PM1_PM2_PM3(); + } + + // Test was failing due to incorrect removal of local tracked positions(tokenIdsByBucketIndex, bucketIndexesWithPosition) in handlers + // Fixed by not removing local tracked positions + function test_regression_rewards_PM1_2() public { + _erc20poolrewardsHandler.addCollateral(378299828523348996450409252968204856717337200844620995950755116109442848, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 52986329559447389847739820276326448003115507778858588690614563138365, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.memorializePositions(2386297678015684371711534521507, 1, 2015255596877246640, 0); + _erc20poolrewardsHandler.moveLiquidity(999999999999999999999999999999999999999542348, 2634, 6160, 4579, 74058); + invariant_positions_PM1_PM2_PM3(); + } + + // Test was failing due to incorrect removal of local tracked positions(tokenIdsByBucketIndex, bucketIndexesWithPosition) in handlers + // Fixed by not removing local tracked positions + function test_regression_rewards_PM1_3() public { + _erc20poolrewardsHandler.memorializePositions(1072697513541617411598352761547948569235246260453338, 49598781763341098132796575116941537, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 59786055813720421827623480119157950185156928336); + _erc20poolrewardsHandler.drawDebt(71602122977707056985766204553433920464603022469065, 0, 3); + _erc20poolrewardsHandler.settleAuction(1533, 6028992255037431023, 999999999999998827363045226813101730497689206, 3712); + _erc20poolrewardsHandler.bucketTake(115792089237316195423570985008687907853269984665640564039457584007913129639935, 14721144691130718757631011689447950991492275176685060291564256, false, 136782600565674582447300799997512602488616407787063657498, 12104321153503350510632448265168933687786653851546540372949180052575211); + _erc20poolrewardsHandler.unstake(5219408520630054730985988951364206956803005171136246340104521696738150, 2, 0, 7051491938468651247212916289972038814809873, 3, 0); + _erc20poolrewardsHandler.settleAuction(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 120615857050623137463512130550262626813346106); + invariant_positions_PM1_PM2_PM3(); + } + + function test_regression_rewards_PM1_4() public { + _erc20poolrewardsHandler.moveLiquidity(832921267658491751933537549, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 62241022956197145532, 1165012150, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639932, 108613063553696015935192567274231711586207468226993603118670370534031542, 2, 1); + _erc20poolrewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 2, 3); + _erc20poolrewardsHandler.settleAuction(1694548149298356876485941302354, 9052, 1444291546717740702970, 1303240033616582679504132393648); + _erc20poolrewardsHandler.burn(0, 707668523430171576399252973860135329463494151705, 13231138491987546580, 3); + invariant_positions_PM1_PM2_PM3(); + } + + // Invariant was failing when rewards cap is equal to zero + // Fixed by updating invariants to run only when rewards cap is non zero + function test_regression_rewards_RW1() public { + invariant_rewards_RW1_RW2(); + } + + // Test was failing due to unbounded debt drawn in `_preUnstake` + // Fixed by bounding amount to borrow + function test_regression_evm_revert_1() public { + _erc20poolrewardsHandler.kickAuction(4927, 15287, 1672621391, 7794); + _erc20poolrewardsHandler.removeQuoteToken(0, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 2, 3); + _erc20poolrewardsHandler.takeAuction(2575, 5650, 2711, 12004413); + _erc20poolrewardsHandler.mint(1515215594322469882937526919173, 2864); + _erc20poolrewardsHandler.removeQuoteToken(11445, 2303142144561970723486793685729, 3879, 1008905021187010892); + _erc20poolrewardsHandler.redeemPositions(23630504830242022841459200705989645184404322170375013590678501625107, 1, 282473030835977356124316597209309127812, 0); + _erc20poolrewardsHandler.redeemPositions(4829, 7399, 20165, 19797); + _erc20poolrewardsHandler.addQuoteToken(8330901901683684346410, 1944730599598704240629, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.mint(52483, 375); + _erc20poolrewardsHandler.removeQuoteToken(242161003333451991910682, 833804465517702, 0, 153306087017); + _erc20poolrewardsHandler.claimRewards(5460042422485935527540305190804180316252530934172557782973004, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2317020199583405169185090105199, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 3, 0); + } + + // Test was failing due to insufficient user token balance for `addQuoteToken` in `_preMemorializePositions` + // Fixed with adding minting required tokens before `addQuoteToken`. + function test_regression_evm_revert_2() public { + _erc20poolrewardsHandler.redeemPositions(535, 10526, 16402, 90638196); + _erc20poolrewardsHandler.moveQuoteToken(3, 3, 3665933105380066469, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 35609320936341689682324970775); + _erc20poolrewardsHandler.lenderKickAuction(65195123838887638071598468995195715179071041842210505440218069543269527898574, 1428, 1550); + _erc20poolrewardsHandler.updateExchangeRate(3324, 3433, 0, 385); + _erc20poolrewardsHandler.removeQuoteToken(487993211956248337274085963929265840000354071708865988088685578811819, 8714694397591072960002001972219030782403253520, 0, 0); + _erc20poolrewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639934, 3, 3, 0); + _erc20poolrewardsHandler.addQuoteToken(8049702985159192133654841011926250176578891096284667148191654768576101, 420390974052856985135062265979816823871512, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 6168047604119363323178237637165700555180739052007127817776433423995137133826); + _erc20poolrewardsHandler.pledgeCollateral(38623724134600076305519407, 1, 42313782903); + _erc20poolrewardsHandler.takeAuction(2520288506, 56779, 10626, 2578); + _erc20poolrewardsHandler.updateExchangeRate(2374, 3180, 0, 11271); + _erc20poolrewardsHandler.moveQuoteToken(3, 84452381279, 65209096465360247728023547148755401892588275436, 1, 97710781974409185143365462469280072552935020234615584635942788); + _erc20poolrewardsHandler.claimRewards(4219, 7299, 3792253, 3829, 5, 0); + } + + // unstake is being called with a minAmount that exceeds the rewards available, causing revert + // change was made in regression to handle this case + function test_regression_evm_revert_burnedInEpochZero() external { + _erc20poolrewardsHandler.takeAuction(7657762660104020786102326341030666744203129169035726688092178, 1, 3, 63603943629412590405183648739466756021204); + _erc20poolrewardsHandler.moveLiquidity(853498184631967766239539459019, 860800972267934599, 2712933514310088838415608172991, 672432889047616138980078995830, 1940131010529342263123392466824); + _erc20poolrewardsHandler.repayDebt(115792089237316195423570985008687907853269984665640564039457584007913129639933, 427572220473655037333866875012561018809807470070214697627941860984, 44890261877119855592686274106685080718432502924958626579185298373762938186596); + // stake ( update ex rates -> stake ) -> kick res -> take res -> unstake( update ex rates -> unstake) + // epoch: 1 + // burned 27895 + _erc20poolrewardsHandler.unstake(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 1, 2, 0); + _erc20poolrewardsHandler.pledgeCollateral(1, 0, 2); + _erc20poolrewardsHandler.pledgeCollateral(46380480507328, 10, 1); + // stake ( update ex rates -> stake ) -> kick res -> take res -> unstake( update ex rates -> unstake) + // epoch: 2 + // burned 27895 + _erc20poolrewardsHandler.claimRewards(1852732090424016924140170274064383911484, 183940675308906, 0, 53861119148520095956903865568282398357460507464813555898544376318790433189, 3, 0); + _erc20poolrewardsHandler.takeReserves(115792089237316195423570985008687907853269984665640564039457584007913129639932, 395769107397432386894162390920154234120, 10606604808645482457593038172768629927057694502686); + _erc20poolrewardsHandler.removeQuoteToken(2, 1, 2192625645230692453890585257984624461888, 6660232197673667038115249964); + // stake ( update ex rates -> stake ) -> kick res -> take res -> unstake( update ex rates -> unstake) + // test was failing in the stake action that occured in _preUnstake() + // * totalBurnedInEpoch was returning 0 since no burn happened between unstake in claimRewards ^^ and the stake in _preUnstake + // * caused underflow since rewardsCap = 0 in this edge case + // * fixed by adding a check in updateBucketExchangeRates() to not evaluate rewardsCap unless totalBurnedInEpoch > 0 + _erc20poolrewardsHandler.unstake(10754921060610721338628656060623251463708357833056948746687720475, 2630, 3678, 47729066275298389217682475444047844926190, 2, 0); + } + + // During this last moveLiquidity call the user gets more quote tokens worth of LP tokens than they had before + function test_regression_PM_failure() external { + _erc20poolrewardsHandler.repayDebt(85714, 1049291847999068770, 999999999999999999999999628872336833145697942); + _erc20poolrewardsHandler.settleAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 36806208, 15184194898560474755071902858637273513435561597233554208311133688, 467793045980282819019245873531034252276885664851); + _erc20poolrewardsHandler.takeReserves(430754706367378, 137895823818768170443343531843552347803975, 136256767494531323); + _erc20poolrewardsHandler.removeQuoteToken(151907177410358060568159872791300321117419489937830, 7129107044982420534725125240530941606156790404561718416111313794090, 9379839670333585391370, 64411724624691339174378); + _erc20poolrewardsHandler.repayDebt(115792089237316195423570985008687907853269984665640564039457584007913129639933, 4761347487120837320733494601307653768982862843053132338897249261174, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.moveLiquidity(1387083372699602, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1, 19432349521828210006920603112382926535859550351439231094, 1); + _erc20poolrewardsHandler.takeReserves(115792089237316195423570985008687907853269984665640564039457584007913129639935, 28562572353266841739143693967402627296578365988173585532380692, 0); + _erc20poolrewardsHandler.removeCollateral(880053353375737921406212405707, 1753558590, 6280826978696699921318109415672827430264350217031853972826832132306719032380, 787979188955935138704416864067); + _erc20poolrewardsHandler.moveLiquidity(11088, 1034959661872260168, 999999999999999212021821301557602448736097220, 25426918372734382433143072945767633116982163690088039971661147586959577591865, 999999999999999999999999998999999856219412005); + } + + // moveLiquidity() call moves deposit from above -> below the LUP causing a fee to be applied to the user, therefore a loss in QT + function test_regression_moveliquidity_below_lup() external { + _erc20poolrewardsHandler.unstake(4735, 7947, 99648028073174186569406251043082614463523861559444314198794141049070931765266, 165, 1, 0); + _erc20poolrewardsHandler.memorializePositions(1017586779835017595, 2000093450358386131913319801132, 999999999999999994705800289221, 5936); + _erc20poolrewardsHandler.lenderKickAuction(0, 552702177486359210209998874773373639789414577510403177176780671, 1); + _erc20poolrewardsHandler.lenderKickAuction(5408465446957, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 1244705446892222810789723108370662428040158); + _erc20poolrewardsHandler.pledgeCollateral(17006067685850655253277243263894458277559455, 365821919836536381007791134, 3); + _erc20poolrewardsHandler.transferLps(1020398235799939978, 8615, 10094997325303278, 6365, 16905); + _erc20poolrewardsHandler.moveQuoteToken(31568984050285372419235475362633334556373463, 2459831956710974374263868230506844670431779539018807045, 5569725293573705060280053370462598629680698918, 3, 0); + _erc20poolrewardsHandler.drawDebt(2189769129255122063229251712703191878940949, 1, 30); + _erc20poolrewardsHandler.redeemPositions(4988, 1000000019294578543, 113393, 20000); + _erc20poolrewardsHandler.moveLiquidity(11546346822809939448153205354420218227887882771387, 17456495056515572411115147660, 182412808598764326152439106919570567805594493064808060386470, 55874229446601275, 34611500787879233737900); + } + + function test_regression_PM1_PM2_PM3_failure() external { + _erc20poolrewardsHandler.addQuoteToken(1000476160430240196, 31831819542854202372682438294297749483895311991281138887779537875208920731861, 1690250244645061953490579723838, 8303344632134790875350129671); + _erc20poolrewardsHandler.redeemPositions(24517164785660125111092467892090015256239780879372312856314705897654233071616, 789619793367986175384776327373, 17366, 27337330393966417869011597343142520438331591211099340735032445540394415961142); + _erc20poolrewardsHandler.mint(4918260799182, 7979); + _erc20poolrewardsHandler.addCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 3); + _erc20poolrewardsHandler.unstake(115792089237316195423570985008687907853269984665640564039457584007913129639932, 541386860615883, 3, 1427594577268427, 3, 0); + _erc20poolrewardsHandler.repayDebt(3, 73, 14148592833); + _erc20poolrewardsHandler.withdrawBonds(408448193972491682247856759691, 6725156476034981825430803209361659548467896941475, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.burn(207659258550486295439876272535780992392904995291122705229127151, 747338929, 1252191612369811194685436, 1); + _erc20poolrewardsHandler.settleAuction(40898023934445005959403090083409155881516500501072076223, 14829255767842040071, 22556694249976650341045163634875596221258685026085348004092232963852919995373, 0); + _erc20poolrewardsHandler.lenderKickAuction(3, 1763503097380079097391449321238134748267573906097584829633224009446989852620, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.pledgeCollateral(992967362603883335031186827777494890596884348, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.moveLiquidity(1439924512892792038061585821476, 12312838412972807476774254, 1386973529615993967509458441, 3153442172782088538684911, 25874047955237976217666127598767369999822558723350386077928985570803529547776); + + invariant_positions_PM1_PM2_PM3(); + } + + // exchange rate is below one and a moveLiquidity() call occurs + function test_regression_exchangerate_lt_one_failure() external { + _erc20poolrewardsHandler.settleAuction(1, 38076326081732675084782953958723025268483402, 32122014834284740581605218790860586945, 675323572528116699998584163938054267674059083708770338684825); + _erc20poolrewardsHandler.takeAuction(3187233434979664450766064117497382244786499427506246277958134435335, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 68185348, 0); + _erc20poolrewardsHandler.removeQuoteToken(9799774902, 0, 171307717744339938462212153344256080, 22090621942183459004431027189984935997454202251794379); + _erc20poolrewardsHandler.settleAuction(10469641420936113, 158810559950203569266889779145, 2729976800298283367181, 629830085692443228137978633631); + _erc20poolrewardsHandler.kickAuction(1999638989041095890000000, 2621683063801908884388370586075, 5202919328337784754771241, 1704327030); + _erc20poolrewardsHandler.transferLps(115792089237316195423570985008687907853269984665640564039457584007913129639934, 0, 16473, 1298950632239640199, 92988305527741837015515230); + _erc20poolrewardsHandler.addQuoteToken(4513829468825775442619016612, 119081395592772229137, 2956368200448621153724264764841, 43337887745458188956665754735863930); + _erc20poolrewardsHandler.redeemPositions(1280000, 107418549682317941, 373995538053150407541675996799144040378996115919481128822550428, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.moveLiquidity(67209037983603756736, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 107047380550, 251040383784310909950712987871787320169957089, 136465421231555); + _erc20poolrewardsHandler.burn(0, 215634488088281622713592282980500574552450094015166108001671516324248273978, 2872470631302225600444008164197436445, 3825369962689014919985865); + _erc20poolrewardsHandler.redeemPositions(9321565985916881685418690197371166789551668163901391336422536021610052010235, 1000001049598692774037881, 1000916926448247166, 1294903748407840); + _erc20poolrewardsHandler.memorializePositions(11357398784982391024848846139138331345877617925164801651509999164448020739, 129054800695, 2, 0); + _erc20poolrewardsHandler.unstake(1008040767152967082, 2705590298374864519261, 2711436202524373179865882211354132, 1058992097359326876866506180, 0, 0); + _erc20poolrewardsHandler.drawDebt(0, 2, 2); + _erc20poolrewardsHandler.settleAuction(3, 109119248607504264825921197422518323470603, 2736316384792465597, 12368015967168137); + _erc20poolrewardsHandler.lenderKickAuction(1, 34593728349238363, 2); + _erc20poolrewardsHandler.pledgeCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 2); + _erc20poolrewardsHandler.takeReserves(37288205583577963230409441522973702491285105267336919446, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 2); + _erc20poolrewardsHandler.redeemPositions(46789, 6151865526048672236676594, 9043006728606892937350259542, 93268112651994959075836677); + _erc20poolrewardsHandler.moveLiquidity(115792089237316195423570985008687907853269984665640564039457584007913129639934, 3563135139286698066907701283845339, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1711, 0); + } + + // exchange rate was less than one in fromBucket during `moveLiquidity()` call + function test_regression_exchangerate_lt_one_unstake_failure() external { + _erc20poolrewardsHandler.addQuoteToken(6142020986804765534920167884740010286243484147097745265674427, 112422117, 1, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.removeCollateral(10151503789076003424768351930, 50838311790159949733482050440261, 787978286748398677564101888697, 743157368739340183819239223268107466431333883452773104647798952518671555); + _erc20poolrewardsHandler.updateExchangeRate(21755610456008175749216891385815221667397636280908693792396899755901148039675, 76822710358333592680973548681291198, 0, 183811652651622670286097901303322315169696013956957316331731965); + _erc20poolrewardsHandler.moveQuoteToken(3843466665413066001504591, 1000009389351870783, 1000172353696212579, 2796008107630611079450058960364, 10168864164675898312163); + _erc20poolrewardsHandler.moveLiquidity(673087759601966739507343763016554, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2339943610295551416526796192911912414311026002620, 1767503704449485978933058571939541599529908587415055225570810956, 9446221296393433187709657992720367407411357294298157052447175); + _erc20poolrewardsHandler.stake(999999999999999505134320049118757567405586786, 1025431341927257246, 1090, 13569953566136947230136843); + _erc20poolrewardsHandler.moveQuoteToken(1051099440087016359, 1357438875313834074678021636760282066916630639717893146590321, 63313628, 84622650405653151060672, 28876); + _erc20poolrewardsHandler.bucketTake(7462, 1121098501725271973, false, 999999999999999999999989741489665556227804536, 442072679406687075418827994186); + _erc20poolrewardsHandler.moveLiquidity(0, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 515992583720, 11542363425921008807915173674517106); + } + + // auction was clearable when `moveLiquidity()` was called, which fired a revert + function test_regression_auction_clearable_remove_collateral() external { + _erc20poolrewardsHandler.takeReserves(1033259874529284986, 115792089237316195423570985008687907853269984665640564039457584007913129537237, 999999999999999999999999999611297087410149302); + _erc20poolrewardsHandler.bucketTake(115792089237316195423570985008687907853269984665640564039457584007913129639932, 0, true, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 1); + _erc20poolrewardsHandler.settleAuction(1019166912441147606, 1174, 24356906371720, 1427315247291615855384771361467057592874190974); + _erc20poolrewardsHandler.drawDebt(31570870468988913, 2490457201062127395317721901417, 14518); + _erc20poolrewardsHandler.removeQuoteToken(234409660495649, 601338041799139892223226281710979, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 51209466403350773952498018); + _erc20poolrewardsHandler.removeCollateral(1615097416247525221325833769791620, 999999999999998491682012949797990382689794890, 72294939771647531696639626124859859519954417706042013154, 1123754474529168009989296); + _erc20poolrewardsHandler.addQuoteToken(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1418766863689180288802735178388574160614681714182842545424322601317331241, 0); + _erc20poolrewardsHandler.burn(10779801302631984284074768919, 1022785462636254549, 4247197939956962367856295710228836, 714087046845131802724051059732250799440226582946566742486292756992468224); + _erc20poolrewardsHandler.settleAuction(44188076061165790147414944966563643572374763434799334, 1, 6140, 982305872882119302926935288339691246129501298); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.redeemPositions(115792089237316195423570985008687907853269984665640564039457584007913129639934, 1, 0, 1373087264969562284412462993767160637254468276739905307938530638280854638702); + _erc20poolrewardsHandler.pledgeCollateral(10321692057145851776062997, 45715379632908488147290369180827577791327034825339732187105428425706, 73584310749102695099025849803685991935361634); + _erc20poolrewardsHandler.moveQuoteToken(267424702347976937182244333, 53012, 1000122761636267185545584328894, 81844228617571507568624913, 2091433423541324315018709); + _erc20poolrewardsHandler.memorializePositions(4341569243600918031477893648, 1037146955303444803059178, 541740178036862, 1079117209305474618); + _erc20poolrewardsHandler.moveLiquidity(7990221475142856060836580, 5177379241789726416494109766258664604084660827937056770440668685154449638506, 270858793106233, 255697520996289263807310886, 4554); + } + + // underflow occurs on kick with Deposit due to round up occuring on the amount being removed from bucket + function test_regression_kick_deposit_underflow() external { + _erc20poolrewardsHandler.claimRewards(115792089237316195423570985008687907853269984665640564039457584007913129639933, 486888075223510502299880936499251496488108390102993365331518154575959314103, 1489390063300625330233647743808860618285793249553177794776030333650229253556, 29413051449830420745080834496160737679746193111333313068326, 3, 0); + _erc20poolrewardsHandler.takeAuction(2000097453768943289819883643139, 1616, 9008, 2957522668165515327594480); + _erc20poolrewardsHandler.kickAuction(1023868299540571449491438, 2726, 3501, 1009546288143049196); + _erc20poolrewardsHandler.claimRewards(1850558667714835415003, 1128770330214, 48160424827244602174656651208212101506580, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 5, 0); + _erc20poolrewardsHandler.memorializePositions(2706256741681, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 0, 1224263559891519537412449982749693535319617589850332219938434821323); + _erc20poolrewardsHandler.kickAuction(2470057927126901389325412029621991572541444590040210706345694, 33212171733138561367109746153438995283000410403806989, 1143982811769352536641977506, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.addQuoteToken(11577, 2847, 701077953563936355129549681402475369359939627904709959917807724349081600, 6164); + _erc20poolrewardsHandler.mint(7455, 2274); + _erc20poolrewardsHandler.transferLps(3, 461323124985628625982, 3, 2265109234892451242814665907719473205880324711447657612395270, 36536442103629333036112242276175423646850388752964235954199714605113762301); + _erc20poolrewardsHandler.addCollateral(4807, 1257702269788440403102767606588, 10232361733685599944417, 8154); + _erc20poolrewardsHandler.moveLiquidity(1147643, 1101373970, 1, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934); + _erc20poolrewardsHandler.redeemPositions(715216745373273013565709474193709288265853036742499324291033262974521344, 925844451104042264560, 21216664920724276219251928893592593152072674630296951273414530379050570789349, 64994818096056336519112112386345118349558865048523329927782158963716200486113); + _erc20poolrewardsHandler.addCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639934, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639934); + _erc20poolrewardsHandler.drawDebt(7803620494871325091384326, 1000000000005890305, 808187899175442127626759093647); + _erc20poolrewardsHandler.stake(1881897373701350, 1, 384457493842895898324057, 2); + _erc20poolrewardsHandler.kickAuction(1801888015, 36313972, 14589, 68230236911552087964619588008895983939113692817643498711581573912769382961420); + _erc20poolrewardsHandler.lenderKickAuction(3409291658389088656420401948375478879628336006312790484, 256489454668391, 264957533719095533849934255388); + } + + + // called takeReserves() when claimable reserves we're 0 + // fixed by switching from poolInfo.claimableReserves to _pool.reservesInfo() + function test_regression_takereserves_no_claimable() external { + _erc20poolrewardsHandler.settleAuction(2, 3, 132571599922733151315276632984852300695962190184833433609822587845, 7127747531336); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.pledgeCollateral(66498430692251244700, 1672526787, 999118417682487042682458556356); + _erc20poolrewardsHandler.settleAuction(102521875, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 2); + _erc20poolrewardsHandler.redeemPositions(0, 11874544000691612616189791308069964024776658688403726762, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.moveLiquidity(6932062779809046417357379434, 37304063095178465963, 289377204519251903, 24040659239847326449, 688903335135867866827970099664435153097141537805976741866417852208381952); + _erc20poolrewardsHandler.settleAuction(86639131860678742534809729831583343741269560864832321, 261140637, 18623697431831025536282119954975103467560305081672865, 18753717689854910664818243334489713190658697158135381); + _erc20poolrewardsHandler.moveLiquidity(688023305723199887936675774367107725948935104557465010923465143476322304, 426, 1361140653919103091484439143, 2492532610214694144077601771204, 1690059365); + _erc20poolrewardsHandler.removeCollateral(3, 75640118, 25456, 2); + _erc20poolrewardsHandler.stake(1201518266017002700145955555, 422051400149996, 191695523226259206952824982, 2575958376257460112331288247217); + _erc20poolrewardsHandler.removeCollateral(1724406295, 1153800818710038190908366, 198135760955974969122979112, 4072468695723038050466180656348448601624931627598867728374067772641581); + _erc20poolrewardsHandler.moveQuoteToken(1, 2, 7848862956757903893727, 108398233646184752124495509729309782170036195843104530456166511127401848014, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.kickAuction(1033780344472464085003, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 4939129467716821333159227066); + _erc20poolrewardsHandler.memorializePositions(115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 5339231111427732, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 49134859377105172171787763664088172754470175); + _erc20poolrewardsHandler.takeReserves(1000017062814174578, 695225158368402414621475732431414969707809712405441717937557041383099862, 1000099679120030632); + _erc20poolrewardsHandler.updateExchangeRate(1798084723794266922073360424201, 1000261123000933782, 0, 1010104555201180320207069905273); + _erc20poolrewardsHandler.kickReserveAuction(14193, 4151); + _erc20poolrewardsHandler.claimRewards(84674527179871518692009907151225958831784072125472174554, 1123074827467033894904599425374, 0, 2, 1, 0); + } + + // the rewards manager took ownership over the position NFT on stake + // fixed in invariants tests by transfering positon NFT ownership to and from rewards on stake and unstake + function test_regression_rewardsmanager_transfer_position_ownership() external { + _erc20poolrewardsHandler.redeemPositions(1513, 5414, 496, 2041); + _erc20poolrewardsHandler.moveLiquidity(1634580778705039759, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 3513140137853878345040965, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 6059353902491193986166404361793496); + _erc20poolrewardsHandler.takeAuction(20887, 7435, 2230322682, 5173); + _erc20poolrewardsHandler.removeQuoteToken(1, 37847095259185386235427787, 7586404791082, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.kickReserveAuction(746270214, 3227); + _erc20poolrewardsHandler.claimRewards(3, 17838572802108205165768007139310483904447158906777650273909618150730155082, 179308467167974215120170861599730499666095743876089926251458944458077, 3, 2, 0); + _erc20poolrewardsHandler.kickAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 3, 410137998186978556584901507876419312185968499332529, 0); + _erc20poolrewardsHandler.repayDebt(17165, 29, 4926); + _erc20poolrewardsHandler.redeemPositions(10181896186129835628862076, 4191, 2070, 4316); + _erc20poolrewardsHandler.unstake(2, 721416428842444814, 1, 1, 2, 0); + _erc20poolrewardsHandler.bucketTake(1701628611252955073601757907075824586952502043588380, 9931050451872161232934786702827793159570303822, true, 2925965874111818002623246439633594772, 3); + _erc20poolrewardsHandler.bucketTake(2, 15470539950385543111949808932971047871463497008525518386, false, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 1); + _erc20poolrewardsHandler.redeemPositions(115792089237316195423570985008687907853269984665640564039457584007913129639933, 2652132885321220255, 2, 1557926034); + } + + function test_regression_rewards_revert() external { + _erc20poolrewardsHandler.takeReserves(1, 176264227116073539466710292640534, 0); + _erc20poolrewardsHandler.drawDebt(1, 57859908193408492548049732123715354988106416421644089, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.addQuoteToken(1807664453705980418, 3, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.drawDebt(14810, 12501, 11607381230457826577033271); + _erc20poolrewardsHandler.kickAuction(126, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 296936791599072888382604547047579799590572591); + _erc20poolrewardsHandler.bucketTake(2084, 265280, false, 621014983179582, 6560); + _erc20poolrewardsHandler.redeemPositions(7465, 451605496, 3970, 1512); + _erc20poolrewardsHandler.removeQuoteToken(115792089237316195423570985008687907853269984665640564039457584007913129639933, 1751716589805844630991620468, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.kickReserveAuction(11967, 1005568066651507152); + _erc20poolrewardsHandler.burn(115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 2724518779079179784049138199493777186815675, 1874); + _erc20poolrewardsHandler.addQuoteToken(3981, 1690101581, 97503618599900271359071534105156178950663445728441824941278920981153136631167, 999999999999999999999989741489665556227804536); + _erc20poolrewardsHandler.bucketTake(247, 1291084637306870208638, false, 14752, 753); + _erc20poolrewardsHandler.updateExchangeRate(3, 2, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.pullCollateral(1266635973860390773360, 1, 3); + _erc20poolrewardsHandler.mint(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.takeAuction(2214845414466110799205272778680836428372471699, 49004997820594553259859787080944156522906733, 3, 271825241); + _erc20poolrewardsHandler.repayDebt(221806598861099254438373725802873431978648517331723635037739192841369, 11212235606391953616223043585188034400902381408644500974587575, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.unstake(230323675018036494562757, 21630132699099546696940190603184460107953215649, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 2, 0); + } + + // small rounding error occured in poolUtils where it looks like the lender has one extra QT then they actually have in the pool. + // fixed by adding a greater than with diff check + function test_regression_rounding_on_moveliquidity() external { + _erc20poolrewardsHandler.bucketTake(29456557203126366201854827466482433206831494327361303, 19307664601998129837361, false, 3492651658979151995106448, 0); + _erc20poolrewardsHandler.lenderKickAuction(4429580015302257459201655018526, 2770867242698718418626, 9388); + _erc20poolrewardsHandler.repayDebt(1000000034925771973, 6930625368245303852701363167, 695149882294170920069268290133705109872933519679164510383901578196897792); + _erc20poolrewardsHandler.moveLiquidity(41132919728951221583605488, 1102564553356549573347, 3410238307441803358653636, 52534, 4545656474572434187813557497); + } + + /** + Test was failing because positions manager was asserting popol errors and not position manager specific errors. + Fixed by adding _ensurePositionManagerError and _ensureRewardsManagerError and use them for manager function calls. + */ + function test_regression_failure_rewards_error_expect() external { + _erc20poolrewardsHandler.redeemPositions(696309158766729979589534440166220067073887038152281856742599135926157312, 1870474563221356095022900189266, 18339, 1788135699833095102184812046279); + _erc20poolrewardsHandler.kickReserveAuction(2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.memorializePositions(3671371551743995865, 99428, 1769100398, 7346319752739159); + _erc20poolrewardsHandler.removeQuoteToken(2192844669750518881733689799257684129590332632218546259991943830203, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 419); + _erc20poolrewardsHandler.bucketTake(1606233188525778759956925750889, 999999999999999529711266919982837439648513939, false, 9949287379244091378213227, 3763880249088816172848545904993); + _erc20poolrewardsHandler.settleAuction(55047125510583885662282424543762268712076435621770843340435135, 110421716658405583291991613217254392572785222796303390816855215908218229630, 1, 292423500273437202726891613067834132052054453755096719350366291073639790); + _erc20poolrewardsHandler.takeAuction(6950266099624730892351521874528127288800419, 193622918230501420262501464441837, 3, 20111879376924408726124755543747277966848148390627132304350); + _erc20poolrewardsHandler.bucketTake(2, 747496798808534888002766542, true, 221618799467305555283, 661410089069342); + _erc20poolrewardsHandler.settleAuction(39300471897997857700471, 363003327028528645286555715801688230394596448247050, 10248673406547114848805417911883714616830860910846335722973488061592062643, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.updateExchangeRate(3, 4485331781540521052468660062240, 0, 3); + _erc20poolrewardsHandler.burn(20137826853855347137082717445, 194771708143610511040376640, 3706358530371129512271612, 14017756049926664649805910646); + } + // Minting uint256.MAX amounts of ajna to reserve auction takers, which causes overflow in rewards staking actions. + // fixed by minting a smaller value to ajna reserve auction takers + function test_regression_rewards_minting_max_ajna() external { + _erc20poolrewardsHandler.transferLps(13692, 15203, 2000001367681895243673966806260, 13770006083256457112227, 1672449141); + _erc20poolrewardsHandler.takeReserves(4947313423932616986372909633726, 3107772, 18585868099953215630514); + _erc20poolrewardsHandler.bucketTake(3, 3, true, 5727003712726959800, 115792089237316195423570985008687907853269984665640564039457584007913129639934); + _erc20poolrewardsHandler.moveLiquidity(3157314460140, 999999999964761098816798416093, 100749181527550271579655357, 73559801971876655830284650454672056023206176402691276845053940498149797, 2453039451665641392603351405279); + _erc20poolrewardsHandler.pullCollateral(2227999026154255580938434049821, 1929, 21209080205145509799924600234705734221480599); + _erc20poolrewardsHandler.unstake(77763028316274741760991770148, 889544954655483052642168760960, 1000073821603105106, 14781730360850539958536528862089, 1, 0); + _erc20poolrewardsHandler.memorializePositions(3, 2, 357097079890085816811291167102020270214, 1); + _erc20poolrewardsHandler.takeAuction(9839460449437189937506383151267733565283, 75862151750582034326928310158878873987779724, 442512581894915658301040013323770372275116408434438200991, 4465076566725287152347371577222429); + _erc20poolrewardsHandler.takeReserves(18196, 115792089237316195423570985008687907853269984665640564039457459248091501068020, 7726328457662922431145272); + _erc20poolrewardsHandler.updateExchangeRate(934079800963406838548241273326154438, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639934); + } + + // Staker earned rewards for staked position, RW6 broke because invariant was incorrectly accruing `updateRewardsClaimed` vs `rewardsClaimed` + // fixed unbound claimRewards invariants + function test_regression_rewards_incorrect_accum_update() external { + _erc20poolrewardsHandler.bucketTake(4645898402004950106563597036, 1, true, 3, 2); + _erc20poolrewardsHandler.kickReserveAuction(3, 144); + _erc20poolrewardsHandler.claimRewards(44705418907931161819765043998797583827, 151688058596538037321153972615358552, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 5828173, 5, 0); + _erc20poolrewardsHandler.lenderKickAuction(1, 0, 58846077910505664190879804); + _erc20poolrewardsHandler.kickAuction(115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1); + _erc20poolrewardsHandler.burn(115792089237316195423570985008687907853269984665640564039457584007913129639934, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.moveLiquidity(7886, 1223, 249959471556478, 8036, 957); + _erc20poolrewardsHandler.drawDebt(3724423259505818275008000000000, 5967, 5194); + _erc20poolrewardsHandler.claimRewards(1, 0, 0, 330972817125, 3, 0); + } + + // RW1 and RW2 were written incorrectly. a rewards cap of 0.1 was placed against the total rewards amount which was incorrect + // fixed by creating distinct mappings to house rewards: updateRewardsClaimed for updating and rewardsClaimed for staking + function test_regression_rewards_RW1_RW2_combined_accum() external { + _erc20poolrewardsHandler.stake(115792089237316195423570985008687907853269984665640564039457584007913129639935, 87088279133912395360162508437887139725077665220, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 3); + _erc20poolrewardsHandler.kickAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 564626589538129677125005778268696423, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.takeReserves(11684, 194771708143610511040376640, 2726053605342047396811); + _erc20poolrewardsHandler.repayDebt(3262, 43081368297850871705277077223467592454899175043611071, 10274); + _erc20poolrewardsHandler.redeemPositions(127647, 176264227116073539466710292640534, 3642, 6424); + _erc20poolrewardsHandler.unstake(15694742330810166545782643648487796809086809379390, 607818605291333492717424880577475155800497188006507929455743410345480887752, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 0, 0); + + invariant_rewards_RW1_RW2(); + } + + // staker already claimed for existing epoch error because the mapping of already claimed rewards was being altered in another test. + // fixed by removing the mapping once the check was performed, to reduce interference in other tests + function test_regression_failure_staker_multiple_claims() external { + _erc20poolrewardsHandler.removeQuoteToken(24285633761882022498, 1087578245407996, 6663358518051573788202107245824545840942246519100783976433621551356294723, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.moveQuoteToken(11189536869195702264148707856867561997664222332845405361, 3328728726287, 4818513330572085618, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.drawDebt(147725477328679114917217631961569282438136190, 3, 780472003275470496134709650710074636727788); + _erc20poolrewardsHandler.lenderKickAuction(919064247875031136583359099106204830246792228137453488870, 1142457080388403685288317708444374, 4671787121933361676687432014984357526); + _erc20poolrewardsHandler.kickAuction(203912244895332759271129822, 4693281693470198349449305071, 1643664758545948144732945329396, 1000004561414141810); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.transferLps(1000038693989240313671379334981, 2064846640417648425220112605606, 4776864955275099928806866, 6232424780589094815714662395270, 1000068114008626106); + _erc20poolrewardsHandler.claimRewards(1000036940761117130, 15003867908887127997315, 3476353371620033073265076493, 2724312429953097381534815815, 5, 0); + _erc20poolrewardsHandler.burn(688021852742675773311740551529319527808439204801695657427445806654554112, 30320319961996759187294, 7002942661907890346192131385494, 4372348210670480168); + _erc20poolrewardsHandler.unstake(1356091896433556140, 20806050335329426573872365432402, 5853329116210293074909437460260, 202213678846911363250835224523544374961057849761087685126829207099, 0, 0); + _erc20poolrewardsHandler.removeQuoteToken(11944716614558445837844163345350475870007169059677226591038156, 9999999999900006, 2613391316561261247247566287419, 4364050756734297123000000000000); + _erc20poolrewardsHandler.memorializePositions(267426811547115, 20523234942372868849155805716884, 660538126174426773503894024918, 1020371850849496789); + _erc20poolrewardsHandler.stake(1009570841568842278, 8707191874572311188045452143575, 1710745869, 4085931245162069848956376); + _erc20poolrewardsHandler.transferLps(5683740793368242368410, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 263051426251); + _erc20poolrewardsHandler.pullCollateral(0, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.unstake(4318031226528307865, 2427656545187034005147981107428, 1999999999999245325259195647033, 3241340219561149539730512456599, 2, 0); + _erc20poolrewardsHandler.stake(10032989871238071275035296997, 2017261180517741466821728, 2211627002267302216727501639610812218, 548909584864737399169449); + _erc20poolrewardsHandler.drawDebt(27796260741388465380354085173951821931976153684318037320868618, 0, 1672262194402336308934191620772703213726098465095081105360392328585273); + _erc20poolrewardsHandler.bucketTake(1000043044918731278, 989512172161706009835325237367, false, 1000000005789793860, 718039675495668253370172541641380612269183998166480256112884969954061103); + _erc20poolrewardsHandler.claimRewards(1674960979, 1000564244419437928, 8087677654804602510627745801, 4446820520587650728200, 2, 0); + _erc20poolrewardsHandler.claimRewards(226258926917592116664863093705619544, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 18029537521280273078473, 64387518121614299931212379232668997590869195864587409918593738, 3, 0); + _erc20poolrewardsHandler.removeQuoteToken(3165456173686488430468312904346908679008474143924097052287399491, 15313716, 1, 15552668823196538810190871817386173988612672596735983713269529192153445604); + _erc20poolrewardsHandler.repayDebt(115792089237316195423570985008687907853269984665640564039457584007913129639933, 3, 2); + _erc20poolrewardsHandler.addCollateral(10945914945614266, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 239408596493925146571027857511336345824311692975188481875, 16168911541889819611465250007078558808000491263581406139030); + _erc20poolrewardsHandler.pledgeCollateral(43390517402447252786570881, 1000007721712886379, 312183671006043540); + _erc20poolrewardsHandler.burn(1001000021343878223399528504368, 726228322825731430757899965868487151749079834114138594667272479585189079, 7830602496179727327668297777390, 9148487490087675901121950); + _erc20poolrewardsHandler.addCollateral(2, 125397281674915775936395560595460719614532516542110165807175343, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639934); + _erc20poolrewardsHandler.claimRewards(18775690215791444885292093480688, 208511828163843, 4213106861723786168510998809808, 4611795088561515898132575771125, 0, 0); + _erc20poolrewardsHandler.redeemPositions(2479835579104654494942210452716501545070460863704620515865, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 3, 1); + _erc20poolrewardsHandler.mint(2647711055765730795283609134451, 1709697543); + _erc20poolrewardsHandler.removeQuoteToken(1000031404174607617, 5000000000000000000099999999998628859607877633, 999999999999999999999999999999912337547565207, 3682474236458075670320739768); + _erc20poolrewardsHandler.moveQuoteToken(1692705698, 34709306640687381, 7111844725028768215861322, 22408667914897973235640361397760, 4814072800199917128); + _erc20poolrewardsHandler.bucketTake(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639932, false, 454902037913297664707697112174539827120540854483325325297181451700, 115792089237316195423570985008687907853269984665640564039457584007913129639932); + _erc20poolrewardsHandler.emergencyUnstake(1050599261156147113, 1074376691263000000000000, 7002651109185164, 4921048347176326540998962106120, 3, 0); + _erc20poolrewardsHandler.redeemPositions(1605595040568071691761529272186, 6374589932811962143806364569626, 504869791, 703741879815086064785063845794263063501986986299046618128871452969759630); + _erc20poolrewardsHandler.stake(9240016643022332539182984, 695264275347035435556335000, 999952379124307148259328110510, 3501569749230777430235792877533); + _erc20poolrewardsHandler.claimRewards(290604600286948252496463313490999079578398159220189531705245153689, 1229050267483794368236552210802835, 37012905065727386300525031553852696, 2, 3, 0); + _erc20poolrewardsHandler.redeemPositions(710948940482212513086987224336033101275209864506038053434815275674467459, 8721353501337744379589622994550, 1714490698, 9015569170803075570277970519); + _erc20poolrewardsHandler.burn(1011903686256170066, 989528036602738431304462409919, 1672529967, 778713523685173616557020855939); + _erc20poolrewardsHandler.updateExchangeRate(1596809363560413962396693305, 989525800646794783000000000000, 0, 1674388995); + _erc20poolrewardsHandler.unstake(10883531334787, 40877128, 480770847296080066987299717351378237725553372592098, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 0, 0); + } + + // Didn't have InsufficentLP error in positionManager revert catch + function test_regression_failure_insufficientlp_error() external { + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.settleAuction(577241166000000000000, 1674403539, 1706636259141011266423401777, 116931836105284395); + _erc20poolrewardsHandler.withdrawBonds(9867747248697291458, 2276637134958475186531688328, 957911724802440729806437351); + _erc20poolrewardsHandler.kickReserveAuction(3477102777457, 445872926603948273480371969570494); + _erc20poolrewardsHandler.repayDebt(1, 30620519668269882565012117691493200000602, 6550083307387093662714640700181574056143163767381275378557906085492018639); + _erc20poolrewardsHandler.unstake(1, 1, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1006408, 1, 0); + _erc20poolrewardsHandler.kickReserveAuction(60878538920082033291777355178031898195946345, 151301); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.updateExchangeRate(3723, 14193362190521867625362174086119, 2984847556915278249, 1431948565575439403); + _erc20poolrewardsHandler.unstake(620475720165697999262823248208783049842251523083896535779413924216, 115545045573751618433375484275813400274137417951043663590237066664176780, 600099763819723634604346206, 26269036480442907031637331641942418161332430845896343, 9, 0); + _erc20poolrewardsHandler.removeCollateral(38252916900474233, 249205319962072693799202680012, 2639762320817925557558, 342293190484942334564171294066); + _erc20poolrewardsHandler.transferLps(115792089237316195423570985008687907853269984665640564039457584007913129639933, 2, 1, 2, 1); + _erc20poolrewardsHandler.burn(27593814869135792628606584912009895150018454871563189731202218872765, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 220258602523965891502051337036531641825438574592631248310796994083256, 940112205671188795347696630567324480120976919568865373617259559426); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.removeQuoteToken(0, 22430497766563836710873950591723, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 0); + _erc20poolrewardsHandler.unstake(12201, 3685, 695142502617607473790470735635663635795256298354491111178867333663833632, 1015432342463301638549343, 4, 0); + _erc20poolrewardsHandler.transferLps(3536960038764358210048752718460, 134319387439720762165052351935, 723, 869949153720793397042, 21774); + _erc20poolrewardsHandler.moveLiquidity(24860115440580498809182, 67836324731985777005501908631161678093421222411927620708449, 0, 2, 1229559121211985463778197658805966); + } + + // index was out of bounds when randomizing exchange rate indexes to update + // fixed by instantiating the array inside of the method before checking an index + function test_regression_failure_randomize_exchange() external { + _erc20poolrewardsHandler.unstake(1, 112388744220239594704909030506775277176, 172139421382441356491809044903526131290304748473444116242693216651191924227, 28057247583020499196416174332822000099943039439312840, 19193968625018413852484821283626022164578951788319907594245211069, 2); + _erc20poolrewardsHandler.addCollateral(3089, 6608, 7726, 1175); + _erc20poolrewardsHandler.pledgeCollateral(0, 1665266719324436960156414842270616355276732647419460846735970026, 261171399028389597729832); + _erc20poolrewardsHandler.memorializePositions(532834, 1191186008960962857163939357544250199378526932, 0, 3964251013291355791); + _erc20poolrewardsHandler.pledgeCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639935, 2, 20543339646991900614943637491556); + _erc20poolrewardsHandler.updateExchangeRate(532615289845430253976060604679719389, 185170833230037194672314349443140570721508655843569337357873544151, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2); + _erc20poolrewardsHandler.updateExchangeRate(0, 32523919344517383004763068842174082041281550880745738003778749916186211, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.unstake(3, 1, 40471187361674907757381069960028812369118729117842876515089342, 187437312729308548672478773757885237512598946528820032027388592, 15292170957369665004671092050, 38429779365541845038); + _erc20poolrewardsHandler.redeemPositions(10322, 12257, 102312660311860849886587252792220939221079621309292995924587083492940358543873, 8449); + _erc20poolrewardsHandler.removeQuoteToken(7307, 82098259114364813849184226979458795540998210065007809242601931750567529450336, 24962334191555090884200901697709326249765046066909050188293351411730598993972, 660); + _erc20poolrewardsHandler.memorializePositions(2, 124382490368454372603243034072, 0, 78029022644745228072748180572258279953915991346870); + _erc20poolrewardsHandler.kickReserveAuction(114723343247932036926669170356582718294728929122856003892178842867945751327943, 3590); + _erc20poolrewardsHandler.pullCollateral(2, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639935); + _erc20poolrewardsHandler.claimRewards(3, 2, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 571403097017002970217793625212942424774848098644003); + } + + // add quote token was failing silently when the NFT position was being created in _preMemorializePositions + // FIX: altered positionManager and rewardsManager tests to return when interacting with NFTs that don't have positions + function test_regression_failure_fresh() external { + _erc20poolrewardsHandler.lenderKickAuction(1336817465042025253525338004928960182841058778, 193634307182211386319599413, 28); + _erc20poolrewardsHandler.bucketTake(7140164611582533744197731518, 1225291445688823278840, false, 140447174498926838356202241, 1217313358793411649232935169); + _erc20poolrewardsHandler.addQuoteToken(1291248035585293274352616, 1220647486761758905436054955871, 695236879708740352902415512815644621354794330678696591333991698318844416, 999999999999999999999999920707795539823417845); + _erc20poolrewardsHandler.emergencyUnstake(11243347270444025656631825589360306986122538308103875103758903630802, 104436231145294692791434209686384137220761282736602704, 113991909435395201759228253179123476630107324300769445, 1, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 695); + _erc20poolrewardsHandler.repayDebt(115792089237316195423570985008687907853269984665640564039457584007913129639932, 34424191262358238003767015512550467320952787658814634693105516384280765757, 1); + _erc20poolrewardsHandler.stake(1393087899760396821662448, 0, 2, 0); + _erc20poolrewardsHandler.claimRewards(115792089237316195423570985008687907853269984665640564039457584007913129639935, 5116902771599276991193476659475872307811128145867445833445550672, 39432975197416494829678821326411300701120551523826226104277956561932827108228, 1400506682660594660722797942017241426449513741223482862482308212423, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639933); + _erc20poolrewardsHandler.removeQuoteToken(8151756038911522518596857433114495226239328159, 47718147729935650247434891866219939127577592936506349, 31281067, 58833763171183168469062794048547362119753425028657390195355011723); + _erc20poolrewardsHandler.stampLoan(42688, 2472758713701048910); + _erc20poolrewardsHandler.failed(); + _erc20poolrewardsHandler.addCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 1); + _erc20poolrewardsHandler.transferLps(388259788415458780615966376818706719808364924038993605557593712571442, 1, 11183636888477495872920172827726679899364321814783075254674028639735567434261, 2970326540500071539162, 25810338262282535177150005108491212819545); + _erc20poolrewardsHandler.emergencyUnstake(100948, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 12892899796738577129141986151703117590492, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 60249307989625514041093966022923819939033804); + _erc20poolrewardsHandler.kickReserveAuction(150771469208542706010213463867, 43354246406661751231666714345663189); + _erc20poolrewardsHandler.takeAuction(3534600738871654532093694, 710900971556109763949049343751907780671963860281451036129946241395772330, 32791, 92899073076001134670575647915681268872801635774755826272901748142573588803419); + _erc20poolrewardsHandler.removeQuoteToken(1999999571196206119106253024650, 999999999999999359435426300823017114351704074, 847975398478536617553572420, 997892034638190564617566391041); + _erc20poolrewardsHandler.unstake(1, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 15705207428248257128809996708067820574434789987097242585735992570899338201, 3, 120654062796568739935506875947855859740423212347571712122719, 46804941067673252); + _erc20poolrewardsHandler.lenderKickAuction(114769084034992770447742921711671102586, 1495429900, 15541); + _erc20poolrewardsHandler.moveLiquidity(948940574329580087951488104576612006868387916754938, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 3); + } + +} diff --git a/tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolPositionManager.t.sol b/tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolPositionManager.t.sol new file mode 100644 index 000000000..47ec1d428 --- /dev/null +++ b/tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolPositionManager.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import { ERC721PoolPositionsInvariants } from "../../invariants/PositionsAndRewards/ERC721PoolPositionsInvariants.t.sol"; + +contract RegressionTestERC721PoolPositionsManager is ERC721PoolPositionsInvariants { + + function setUp() public override { + super.setUp(); + } + + // `NoAllowance()` revert was firing but wasn't tracked by positionManager handler class + function test_regression_failure_no_allowance_err() external { + _erc721positionHandler.moveLiquidity(2, 2726918499846956781196026606977128745, 4671573035498269269631531108867257349254074281251805650007376127, 65229405930547405981803897204924144159227983393929014243, 136472940433983213576424627235038299016985732062067347200674016); + _erc721positionHandler.memorializePositions(10365, 771, 8152, 3186); + } +} \ No newline at end of file diff --git a/tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolRewardsManager.t.sol b/tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolRewardsManager.t.sol new file mode 100644 index 000000000..16c600a22 --- /dev/null +++ b/tests/forge/regression/PositionAndRewards/RegressionTestERC721PoolRewardsManager.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.18; + +import { ERC721PoolRewardsInvariants } from "../../invariants/PositionsAndRewards/ERC721PoolRewardsInvariants.t.sol"; + +contract RegressionTestERC721PoolRewardsManager is ERC721PoolRewardsInvariants { + + function setUp() public override { + super.setUp(); + } + + // issue in invariants totalling amount of rewards that caller of claimRewards was receiving. + // fix: claimrewards and update rewards now go to caller of claimRewards + function test_regression_failure_rewards_exceeded_claim() external { + _erc721poolrewardsHandler.settleAuction(17873, 2208, 326, 1944); + _erc721poolrewardsHandler.stake(115792089237316195423570985008687907853269984665640564039457584007913129639932, 53134310333307170138, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2340533661754158540520179666008670241532871916995373825004189326661505987844); + _erc721poolrewardsHandler.pledgeCollateral(8132, 11716, 1057); + _erc721poolrewardsHandler.kickReserveAuction(2985325127, 23214); + _erc721poolrewardsHandler.removeCollateral(3461, 514, 17285, 838); + _erc721poolrewardsHandler.kickAuction(18170, 652, 11342, 1168); + _erc721poolrewardsHandler.pullCollateral(20130, 1209987167530552461153974115173428229758989546163, 150941); + _erc721poolrewardsHandler.moveLiquidity(63198806135952229891699111929727509482991997027848329114178785250303971081388, 77371051183995213971267347974759461809434770063921461351617080426027329266071, 4805, 103015036129420501082640292999971233764824083444698625545321391489266878218522, 2289); + _erc721poolrewardsHandler.moveQuoteToken(276169773153138481519606288636310061814657663456104947149, 1, 0, 108537837119796081908394324659000725292282331478997011952318493996290, 155532253556112179854090944828383440910501711771906801208685755840667262568); + _erc721poolrewardsHandler.mergeCollateral(173, 22406963037383631220938302497939718111833223267188040374368716127276); + _erc721poolrewardsHandler.burn(1328065707762407283002828802143541176473931677425004844, 1, 1, 37613208758526068006052551033711685); + _erc721poolrewardsHandler.takeAuction(0, 9352381759360299323960711216326149317387010227218710, 2546377053981808421495007542941590246694727231217, 3663797758192519198918); + _erc721poolrewardsHandler.takeAuction(148878580729371224992950595085688885987, 52018, 3863548495672151022795311051855, 1224829895266858456828928840866630331525272263026827096173292323394330361); + _erc721poolrewardsHandler.claimRewards(339802229099465406190265268924204103831957337149846935, 1, 2641, 1084164255431, 3, 0); + } +} diff --git a/tests/forge/regression/PositionAndRewards/RegressionTestRewardsManager.t.sol b/tests/forge/regression/PositionAndRewards/RegressionTestRewardsManager.t.sol deleted file mode 100644 index d77ecafd4..000000000 --- a/tests/forge/regression/PositionAndRewards/RegressionTestRewardsManager.t.sol +++ /dev/null @@ -1,371 +0,0 @@ - -pragma solidity 0.8.18; - -import { RewardsInvariants } from "../../invariants/PositionsAndRewards/RewardsInvariants.t.sol"; -import { PoolInfoUtils } from 'src/PoolInfoUtils.sol'; - -import '@std/console.sol'; - -contract RegressionTestRewardsManager is RewardsInvariants { - - function setUp() public override { - super.setUp(); - } - - // Test was failing due to incorrect removal of local tracked positions(tokenIdsByBucketIndex, bucketIndexesWithPosition) in handlers - // Fixed by not removing local tracked positions - function test_regression_rewards_PM1_1() public { - _rewardsHandler.unstake(156983341, 3, 1057, 627477641256361); - _rewardsHandler.settleAuction(2108881198342615861856429474, 922394580216134598, 4169158839, 1000000019773478651); - invariant_positions_PM1_PM2_PM3(); - } - - // Test was failing due to incorrect removal of local tracked positions(tokenIdsByBucketIndex, bucketIndexesWithPosition) in handlers - // Fixed by not removing local tracked positions - function test_regression_rewards_PM1_2() public { - _rewardsHandler.addCollateral(378299828523348996450409252968204856717337200844620995950755116109442848, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 52986329559447389847739820276326448003115507778858588690614563138365, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - _rewardsHandler.memorializePositions(2386297678015684371711534521507, 1, 2015255596877246640, 0); - _rewardsHandler.moveLiquidity(999999999999999999999999999999999999999542348, 2634, 6160, 4579, 74058); - invariant_positions_PM1_PM2_PM3(); - } - - // Test was failing due to incorrect removal of local tracked positions(tokenIdsByBucketIndex, bucketIndexesWithPosition) in handlers - // Fixed by not removing local tracked positions - function test_regression_rewards_PM1_3() public { - _rewardsHandler.memorializePositions(1072697513541617411598352761547948569235246260453338, 49598781763341098132796575116941537, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 59786055813720421827623480119157950185156928336); - _rewardsHandler.drawDebt(71602122977707056985766204553433920464603022469065, 0, 3); - _rewardsHandler.settleAuction(1533, 6028992255037431023, 999999999999998827363045226813101730497689206, 3712); - _rewardsHandler.bucketTake(115792089237316195423570985008687907853269984665640564039457584007913129639935, 14721144691130718757631011689447950991492275176685060291564256, false, 136782600565674582447300799997512602488616407787063657498, 12104321153503350510632448265168933687786653851546540372949180052575211); - _rewardsHandler.unstake(5219408520630054730985988951364206956803005171136246340104521696738150, 2, 0, 7051491938468651247212916289972038814809873); - _rewardsHandler.settleAuction(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 120615857050623137463512130550262626813346106); - invariant_positions_PM1_PM2_PM3(); - } - - function test_regression_rewards_PM1_4() public { - _rewardsHandler.moveLiquidity(832921267658491751933537549, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 62241022956197145532, 1165012150, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639932, 108613063553696015935192567274231711586207468226993603118670370534031542, 2, 1); - _rewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 2, 3); - _rewardsHandler.settleAuction(1694548149298356876485941302354, 9052, 1444291546717740702970, 1303240033616582679504132393648); - _rewardsHandler.burn(0, 707668523430171576399252973860135329463494151705, 13231138491987546580, 3); - invariant_positions_PM1_PM2_PM3(); - } - - // Invariant was failing when rewards cap is equal to zero - // Fixed by updating invariants to run only when rewards cap is non zero - function test_regression_rewards_RW1() public { - invariant_rewards_RW1_RW2(); - } - - // Test was failing due to unbounded debt drawn in `_preUnstake` - // Fixed by bounding amount to borrow - function test_regression_evm_revert_1() public { - _rewardsHandler.kickAuction(4927, 15287, 1672621391, 7794); - _rewardsHandler.removeQuoteToken(0, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 2, 3); - _rewardsHandler.takeAuction(2575, 5650, 2711, 12004413); - _rewardsHandler.mint(1515215594322469882937526919173, 2864); - _rewardsHandler.removeQuoteToken(11445, 2303142144561970723486793685729, 3879, 1008905021187010892); - _rewardsHandler.redeemPositions(23630504830242022841459200705989645184404322170375013590678501625107, 1, 282473030835977356124316597209309127812, 0); - _rewardsHandler.redeemPositions(4829, 7399, 20165, 19797); - _rewardsHandler.addQuoteToken(8330901901683684346410, 1944730599598704240629, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.mint(52483, 375); - _rewardsHandler.removeQuoteToken(242161003333451991910682, 833804465517702, 0, 153306087017); - _rewardsHandler.claimRewards(5460042422485935527540305190804180316252530934172557782973004, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2317020199583405169185090105199, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - } - - // Test was failing due to insufficient user token balance for `addQuoteToken` in `_preMemorializePositions` - // Fixed with adding minting required tokens before `addQuoteToken`. - function test_regression_evm_revert_2() public { - _rewardsHandler.redeemPositions(535, 10526, 16402, 90638196); - _rewardsHandler.moveQuoteToken(3, 3, 3665933105380066469, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 35609320936341689682324970775); - _rewardsHandler.lenderKickAuction(65195123838887638071598468995195715179071041842210505440218069543269527898574, 1428, 1550); - _rewardsHandler.updateExchangeRate(3324, 3433, 385); - _rewardsHandler.removeQuoteToken(487993211956248337274085963929265840000354071708865988088685578811819, 8714694397591072960002001972219030782403253520, 0, 0); - _rewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639934, 3, 3, 0); - _rewardsHandler.addQuoteToken(8049702985159192133654841011926250176578891096284667148191654768576101, 420390974052856985135062265979816823871512, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 6168047604119363323178237637165700555180739052007127817776433423995137133826); - _rewardsHandler.pledgeCollateral(38623724134600076305519407, 1, 42313782903); - _rewardsHandler.takeAuction(2520288506, 56779, 10626, 2578); - _rewardsHandler.updateExchangeRate(2374, 3180, 11271); - _rewardsHandler.moveQuoteToken(3, 84452381279, 65209096465360247728023547148755401892588275436, 1, 97710781974409185143365462469280072552935020234615584635942788); - _rewardsHandler.claimRewards(4219, 7299, 3792253, 3829); - } - - // unstake is being called with a minAmount that exceeds the rewards available, causing revert - // change was made in regression to handle this case - function test_regression_evm_revert_burnedInEpochZero() external { - _rewardsHandler.takeAuction(7657762660104020786102326341030666744203129169035726688092178, 1, 3, 63603943629412590405183648739466756021204); - _rewardsHandler.moveLiquidity(853498184631967766239539459019, 860800972267934599, 2712933514310088838415608172991, 672432889047616138980078995830, 1940131010529342263123392466824); - _rewardsHandler.repayDebt(115792089237316195423570985008687907853269984665640564039457584007913129639933, 427572220473655037333866875012561018809807470070214697627941860984, 44890261877119855592686274106685080718432502924958626579185298373762938186596); - // stake ( update ex rates -> stake ) -> kick res -> take res -> unstake( update ex rates -> unstake) - // epoch: 1 - // burned 27895 - _rewardsHandler.unstake(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 1); - _rewardsHandler.pledgeCollateral(1, 0, 2); - _rewardsHandler.pledgeCollateral(46380480507328, 10, 1); - // stake ( update ex rates -> stake ) -> kick res -> take res -> unstake( update ex rates -> unstake) - // epoch: 2 - // burned 27895 - _rewardsHandler.claimRewards(1852732090424016924140170274064383911484, 183940675308906, 0, 53861119148520095956903865568282398357460507464813555898544376318790433189); - _rewardsHandler.takeReserves(115792089237316195423570985008687907853269984665640564039457584007913129639932, 395769107397432386894162390920154234120, 10606604808645482457593038172768629927057694502686); - _rewardsHandler.removeQuoteToken(2, 1, 2192625645230692453890585257984624461888, 6660232197673667038115249964); - // stake ( update ex rates -> stake ) -> kick res -> take res -> unstake( update ex rates -> unstake) - // test was failing in the stake action that occured in _preUnstake() - // * totalBurnedInEpoch was returning 0 since no burn happened between unstake in claimRewards ^^ and the stake in _preUnstake - // * caused underflow since rewardsCap = 0 in this edge case - // * fixed by adding a check in updateBucketExchangeRates() to not evaluate rewardsCap unless totalBurnedInEpoch > 0 - _rewardsHandler.unstake(10754921060610721338628656060623251463708357833056948746687720475, 2630, 3678, 47729066275298389217682475444047844926190); - } - - // During this last moveLiquidity call the user gets more quote tokens worth of LP tokens than they had before - function test_regression_PM_failure() external { - _rewardsHandler.repayDebt(85714, 1049291847999068770, 999999999999999999999999628872336833145697942); - _rewardsHandler.settleAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 36806208, 15184194898560474755071902858637273513435561597233554208311133688, 467793045980282819019245873531034252276885664851); - _rewardsHandler.takeReserves(430754706367378, 137895823818768170443343531843552347803975, 136256767494531323); - _rewardsHandler.removeQuoteToken(151907177410358060568159872791300321117419489937830, 7129107044982420534725125240530941606156790404561718416111313794090, 9379839670333585391370, 64411724624691339174378); - _rewardsHandler.repayDebt(115792089237316195423570985008687907853269984665640564039457584007913129639933, 4761347487120837320733494601307653768982862843053132338897249261174, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.moveLiquidity(1387083372699602, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1, 19432349521828210006920603112382926535859550351439231094, 1); - _rewardsHandler.takeReserves(115792089237316195423570985008687907853269984665640564039457584007913129639935, 28562572353266841739143693967402627296578365988173585532380692, 0); - _rewardsHandler.removeCollateral(880053353375737921406212405707, 1753558590, 6280826978696699921318109415672827430264350217031853972826832132306719032380, 787979188955935138704416864067); - _rewardsHandler.moveLiquidity(11088, 1034959661872260168, 999999999999999212021821301557602448736097220, 25426918372734382433143072945767633116982163690088039971661147586959577591865, 999999999999999999999999998999999856219412005); - } - - // moveLiquidity() call moves deposit from above -> below the LUP causing a fee to be applied to the user, therefore a loss in QT - function test_regression_moveliquidity_below_lup() external { - _rewardsHandler.unstake(4735, 7947, 99648028073174186569406251043082614463523861559444314198794141049070931765266, 165); - _rewardsHandler.memorializePositions(1017586779835017595, 2000093450358386131913319801132, 999999999999999994705800289221, 5936); - _rewardsHandler.lenderKickAuction(0, 552702177486359210209998874773373639789414577510403177176780671, 1); - _rewardsHandler.lenderKickAuction(5408465446957, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 1244705446892222810789723108370662428040158); - _rewardsHandler.pledgeCollateral(17006067685850655253277243263894458277559455, 365821919836536381007791134, 3); - _rewardsHandler.transferLps(1020398235799939978, 8615, 10094997325303278, 6365, 16905); - _rewardsHandler.moveQuoteToken(31568984050285372419235475362633334556373463, 2459831956710974374263868230506844670431779539018807045, 5569725293573705060280053370462598629680698918, 3, 0); - _rewardsHandler.drawDebt(2189769129255122063229251712703191878940949, 1, 30); - _rewardsHandler.redeemPositions(4988, 1000000019294578543, 113393, 20000); - _rewardsHandler.moveLiquidity(11546346822809939448153205354420218227887882771387, 17456495056515572411115147660, 182412808598764326152439106919570567805594493064808060386470, 55874229446601275, 34611500787879233737900); - } - - function test_regression_PM1_PM2_PM3_failure() external { - _rewardsHandler.addQuoteToken(1000476160430240196, 31831819542854202372682438294297749483895311991281138887779537875208920731861, 1690250244645061953490579723838, 8303344632134790875350129671); - _rewardsHandler.redeemPositions(24517164785660125111092467892090015256239780879372312856314705897654233071616, 789619793367986175384776327373, 17366, 27337330393966417869011597343142520438331591211099340735032445540394415961142); - _rewardsHandler.mint(4918260799182, 7979); - _rewardsHandler.addCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 3); - _rewardsHandler.unstake(115792089237316195423570985008687907853269984665640564039457584007913129639932, 541386860615883, 3, 1427594577268427); - _rewardsHandler.repayDebt(3, 73, 14148592833); - _rewardsHandler.withdrawBonds(408448193972491682247856759691, 6725156476034981825430803209361659548467896941475, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.burn(207659258550486295439876272535780992392904995291122705229127151, 747338929, 1252191612369811194685436, 1); - _rewardsHandler.settleAuction(40898023934445005959403090083409155881516500501072076223, 14829255767842040071, 22556694249976650341045163634875596221258685026085348004092232963852919995373, 0); - _rewardsHandler.lenderKickAuction(3, 1763503097380079097391449321238134748267573906097584829633224009446989852620, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.failed(); - _rewardsHandler.pledgeCollateral(992967362603883335031186827777494890596884348, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - _rewardsHandler.moveLiquidity(1439924512892792038061585821476, 12312838412972807476774254, 1386973529615993967509458441, 3153442172782088538684911, 25874047955237976217666127598767369999822558723350386077928985570803529547776); - - invariant_positions_PM1_PM2_PM3(); - } - - // exchange rate is below one and a moveLiquidity() call occurs - function test_regression_exchangerate_lt_one_failure() external { - _rewardsHandler.settleAuction(1, 38076326081732675084782953958723025268483402, 32122014834284740581605218790860586945, 675323572528116699998584163938054267674059083708770338684825); - _rewardsHandler.takeAuction(3187233434979664450766064117497382244786499427506246277958134435335, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 68185348, 0); - _rewardsHandler.removeQuoteToken(9799774902, 0, 171307717744339938462212153344256080, 22090621942183459004431027189984935997454202251794379); - _rewardsHandler.settleAuction(10469641420936113, 158810559950203569266889779145, 2729976800298283367181, 629830085692443228137978633631); - _rewardsHandler.kickAuction(1999638989041095890000000, 2621683063801908884388370586075, 5202919328337784754771241, 1704327030); - _rewardsHandler.transferLps(115792089237316195423570985008687907853269984665640564039457584007913129639934, 0, 16473, 1298950632239640199, 92988305527741837015515230); - _rewardsHandler.addQuoteToken(4513829468825775442619016612, 119081395592772229137, 2956368200448621153724264764841, 43337887745458188956665754735863930); - _rewardsHandler.redeemPositions(1280000, 107418549682317941, 373995538053150407541675996799144040378996115919481128822550428, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.moveLiquidity(67209037983603756736, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 107047380550, 251040383784310909950712987871787320169957089, 136465421231555); - _rewardsHandler.burn(0, 215634488088281622713592282980500574552450094015166108001671516324248273978, 2872470631302225600444008164197436445, 3825369962689014919985865); - _rewardsHandler.redeemPositions(9321565985916881685418690197371166789551668163901391336422536021610052010235, 1000001049598692774037881, 1000916926448247166, 1294903748407840); - _rewardsHandler.memorializePositions(11357398784982391024848846139138331345877617925164801651509999164448020739, 129054800695, 2, 0); - _rewardsHandler.unstake(1008040767152967082, 2705590298374864519261, 2711436202524373179865882211354132, 1058992097359326876866506180); - _rewardsHandler.drawDebt(0, 2, 2); - _rewardsHandler.settleAuction(3, 109119248607504264825921197422518323470603, 2736316384792465597, 12368015967168137); - _rewardsHandler.lenderKickAuction(1, 34593728349238363, 2); - _rewardsHandler.pledgeCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 2); - _rewardsHandler.takeReserves(37288205583577963230409441522973702491285105267336919446, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 2); - _rewardsHandler.redeemPositions(46789, 6151865526048672236676594, 9043006728606892937350259542, 93268112651994959075836677); - _rewardsHandler.moveLiquidity(115792089237316195423570985008687907853269984665640564039457584007913129639934, 3563135139286698066907701283845339, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1711, 0); - } - - // exchange rate was less than one in fromBucket during `moveLiquidity()` call - function test_regression_exchangerate_lt_one_unstake_failure() external { - _rewardsHandler.addQuoteToken(6142020986804765534920167884740010286243484147097745265674427, 112422117, 1, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.removeCollateral(10151503789076003424768351930, 50838311790159949733482050440261, 787978286748398677564101888697, 743157368739340183819239223268107466431333883452773104647798952518671555); - _rewardsHandler.updateExchangeRate(21755610456008175749216891385815221667397636280908693792396899755901148039675, 76822710358333592680973548681291198, 183811652651622670286097901303322315169696013956957316331731965); - _rewardsHandler.moveQuoteToken(3843466665413066001504591, 1000009389351870783, 1000172353696212579, 2796008107630611079450058960364, 10168864164675898312163); - _rewardsHandler.moveLiquidity(673087759601966739507343763016554, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 2339943610295551416526796192911912414311026002620, 1767503704449485978933058571939541599529908587415055225570810956, 9446221296393433187709657992720367407411357294298157052447175); - _rewardsHandler.stake(999999999999999505134320049118757567405586786, 1025431341927257246, 1090, 13569953566136947230136843); - _rewardsHandler.moveQuoteToken(1051099440087016359, 1357438875313834074678021636760282066916630639717893146590321, 63313628, 84622650405653151060672, 28876); - _rewardsHandler.bucketTake(7462, 1121098501725271973, false, 999999999999999999999989741489665556227804536, 442072679406687075418827994186); - _rewardsHandler.moveLiquidity(0, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 515992583720, 11542363425921008807915173674517106); - } - - // auction was clearable when `moveLiquidity()` was called, which fired a revert - function test_regression_auction_clearable_remove_collateral() external { - _rewardsHandler.takeReserves(1033259874529284986, 115792089237316195423570985008687907853269984665640564039457584007913129537237, 999999999999999999999999999611297087410149302); - _rewardsHandler.bucketTake(115792089237316195423570985008687907853269984665640564039457584007913129639932, 0, true, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 1); - _rewardsHandler.settleAuction(1019166912441147606, 1174, 24356906371720, 1427315247291615855384771361467057592874190974); - _rewardsHandler.drawDebt(31570870468988913, 2490457201062127395317721901417, 14518); - _rewardsHandler.removeQuoteToken(234409660495649, 601338041799139892223226281710979, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 51209466403350773952498018); - _rewardsHandler.removeCollateral(1615097416247525221325833769791620, 999999999999998491682012949797990382689794890, 72294939771647531696639626124859859519954417706042013154, 1123754474529168009989296); - _rewardsHandler.addQuoteToken(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1418766863689180288802735178388574160614681714182842545424322601317331241, 0); - _rewardsHandler.burn(10779801302631984284074768919, 1022785462636254549, 4247197939956962367856295710228836, 714087046845131802724051059732250799440226582946566742486292756992468224); - _rewardsHandler.settleAuction(44188076061165790147414944966563643572374763434799334, 1, 6140, 982305872882119302926935288339691246129501298); - _rewardsHandler.failed(); - _rewardsHandler.redeemPositions(115792089237316195423570985008687907853269984665640564039457584007913129639934, 1, 0, 1373087264969562284412462993767160637254468276739905307938530638280854638702); - _rewardsHandler.pledgeCollateral(10321692057145851776062997, 45715379632908488147290369180827577791327034825339732187105428425706, 73584310749102695099025849803685991935361634); - _rewardsHandler.moveQuoteToken(267424702347976937182244333, 53012, 1000122761636267185545584328894, 81844228617571507568624913, 2091433423541324315018709); - _rewardsHandler.memorializePositions(4341569243600918031477893648, 1037146955303444803059178, 541740178036862, 1079117209305474618); - _rewardsHandler.moveLiquidity(7990221475142856060836580, 5177379241789726416494109766258664604084660827937056770440668685154449638506, 270858793106233, 255697520996289263807310886, 4554); - } - - // underflow occurs on kick with Deposit due to round up occuring on the amount being removed from bucket - function test_regression_kick_deposit_underflow() external { - _rewardsHandler.claimRewards(115792089237316195423570985008687907853269984665640564039457584007913129639933, 486888075223510502299880936499251496488108390102993365331518154575959314103, 1489390063300625330233647743808860618285793249553177794776030333650229253556, 29413051449830420745080834496160737679746193111333313068326); - _rewardsHandler.takeAuction(2000097453768943289819883643139, 1616, 9008, 2957522668165515327594480); - _rewardsHandler.kickAuction(1023868299540571449491438, 2726, 3501, 1009546288143049196); - _rewardsHandler.claimRewards(1850558667714835415003, 1128770330214, 48160424827244602174656651208212101506580, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.memorializePositions(2706256741681, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 0, 1224263559891519537412449982749693535319617589850332219938434821323); - _rewardsHandler.kickAuction(2470057927126901389325412029621991572541444590040210706345694, 33212171733138561367109746153438995283000410403806989, 1143982811769352536641977506, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - _rewardsHandler.addQuoteToken(11577, 2847, 701077953563936355129549681402475369359939627904709959917807724349081600, 6164); - _rewardsHandler.mint(7455, 2274); - _rewardsHandler.transferLps(3, 461323124985628625982, 3, 2265109234892451242814665907719473205880324711447657612395270, 36536442103629333036112242276175423646850388752964235954199714605113762301); - _rewardsHandler.addCollateral(4807, 1257702269788440403102767606588, 10232361733685599944417, 8154); - _rewardsHandler.moveLiquidity(1147643, 1101373970, 1, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934); - _rewardsHandler.redeemPositions(715216745373273013565709474193709288265853036742499324291033262974521344, 925844451104042264560, 21216664920724276219251928893592593152072674630296951273414530379050570789349, 64994818096056336519112112386345118349558865048523329927782158963716200486113); - _rewardsHandler.addCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639934, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639934); - _rewardsHandler.drawDebt(7803620494871325091384326, 1000000000005890305, 808187899175442127626759093647); - _rewardsHandler.stake(1881897373701350, 1, 384457493842895898324057, 2); - _rewardsHandler.kickAuction(1801888015, 36313972, 14589, 68230236911552087964619588008895983939113692817643498711581573912769382961420); - _rewardsHandler.lenderKickAuction(3409291658389088656420401948375478879628336006312790484, 256489454668391, 264957533719095533849934255388); - } - - - // called takeReserves() when claimable reserves we're 0 - // fixed by switching from poolInfo.claimableReserves to _pool.reservesInfo() - function test_regression_takereserves_no_claimable() external { - _rewardsHandler.settleAuction(2, 3, 132571599922733151315276632984852300695962190184833433609822587845, 7127747531336); - _rewardsHandler.failed(); - _rewardsHandler.pledgeCollateral(66498430692251244700, 1672526787, 999118417682487042682458556356); - _rewardsHandler.settleAuction(102521875, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 2); - _rewardsHandler.redeemPositions(0, 11874544000691612616189791308069964024776658688403726762, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.moveLiquidity(6932062779809046417357379434, 37304063095178465963, 289377204519251903, 24040659239847326449, 688903335135867866827970099664435153097141537805976741866417852208381952); - _rewardsHandler.settleAuction(86639131860678742534809729831583343741269560864832321, 261140637, 18623697431831025536282119954975103467560305081672865, 18753717689854910664818243334489713190658697158135381); - _rewardsHandler.moveLiquidity(688023305723199887936675774367107725948935104557465010923465143476322304, 426, 1361140653919103091484439143, 2492532610214694144077601771204, 1690059365); - _rewardsHandler.removeCollateral(3, 75640118, 25456, 2); - _rewardsHandler.stake(1201518266017002700145955555, 422051400149996, 191695523226259206952824982, 2575958376257460112331288247217); - _rewardsHandler.removeCollateral(1724406295, 1153800818710038190908366, 198135760955974969122979112, 4072468695723038050466180656348448601624931627598867728374067772641581); - _rewardsHandler.moveQuoteToken(1, 2, 7848862956757903893727, 108398233646184752124495509729309782170036195843104530456166511127401848014, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - _rewardsHandler.kickAuction(1033780344472464085003, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 4939129467716821333159227066); - _rewardsHandler.memorializePositions(115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 5339231111427732, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - _rewardsHandler.takeAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 49134859377105172171787763664088172754470175); - _rewardsHandler.takeReserves(1000017062814174578, 695225158368402414621475732431414969707809712405441717937557041383099862, 1000099679120030632); - _rewardsHandler.updateExchangeRate(1798084723794266922073360424201, 1000261123000933782, 1010104555201180320207069905273); - _rewardsHandler.kickReserveAuction(14193, 4151); - _rewardsHandler.claimRewards(84674527179871518692009907151225958831784072125472174554, 1123074827467033894904599425374, 0, 2); - } - - // the rewards manager took ownership over the position NFT on stake - // fixed in invariants tests by transfering positon NFT ownership to and from rewards on stake and unstake - function test_regression_rewardsmanager_transfer_position_ownership() external { - _rewardsHandler.redeemPositions(1513, 5414, 496, 2041); - _rewardsHandler.moveLiquidity(1634580778705039759, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 3513140137853878345040965, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 6059353902491193986166404361793496); - _rewardsHandler.takeAuction(20887, 7435, 2230322682, 5173); - _rewardsHandler.removeQuoteToken(1, 37847095259185386235427787, 7586404791082, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.kickReserveAuction(746270214, 3227); - _rewardsHandler.claimRewards(3, 17838572802108205165768007139310483904447158906777650273909618150730155082, 179308467167974215120170861599730499666095743876089926251458944458077, 3); - _rewardsHandler.kickAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 3, 410137998186978556584901507876419312185968499332529, 0); - _rewardsHandler.repayDebt(17165, 29, 4926); - _rewardsHandler.redeemPositions(10181896186129835628862076, 4191, 2070, 4316); - _rewardsHandler.unstake(2, 721416428842444814, 1, 1); - _rewardsHandler.bucketTake(1701628611252955073601757907075824586952502043588380, 9931050451872161232934786702827793159570303822, true, 2925965874111818002623246439633594772, 3); - _rewardsHandler.bucketTake(2, 15470539950385543111949808932971047871463497008525518386, false, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 1); - _rewardsHandler.redeemPositions(115792089237316195423570985008687907853269984665640564039457584007913129639933, 2652132885321220255, 2, 1557926034); - } - - function test_regression_rewards_revert() external { - _rewardsHandler.takeReserves(1, 176264227116073539466710292640534, 0); - _rewardsHandler.drawDebt(1, 57859908193408492548049732123715354988106416421644089, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.addQuoteToken(1807664453705980418, 3, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.drawDebt(14810, 12501, 11607381230457826577033271); - _rewardsHandler.kickAuction(126, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 296936791599072888382604547047579799590572591); - _rewardsHandler.bucketTake(2084, 265280, false, 621014983179582, 6560); - _rewardsHandler.redeemPositions(7465, 451605496, 3970, 1512); - _rewardsHandler.removeQuoteToken(115792089237316195423570985008687907853269984665640564039457584007913129639933, 1751716589805844630991620468, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.kickReserveAuction(11967, 1005568066651507152); - _rewardsHandler.burn(115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 2724518779079179784049138199493777186815675, 1874); - _rewardsHandler.addQuoteToken(3981, 1690101581, 97503618599900271359071534105156178950663445728441824941278920981153136631167, 999999999999999999999989741489665556227804536); - _rewardsHandler.bucketTake(247, 1291084637306870208638, false, 14752, 753); - _rewardsHandler.updateExchangeRate(3, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.pullCollateral(1266635973860390773360, 1, 3); - _rewardsHandler.mint(0, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.takeAuction(2214845414466110799205272778680836428372471699, 49004997820594553259859787080944156522906733, 3, 271825241); - _rewardsHandler.repayDebt(221806598861099254438373725802873431978648517331723635037739192841369, 11212235606391953616223043585188034400902381408644500974587575, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - _rewardsHandler.unstake(230323675018036494562757, 21630132699099546696940190603184460107953215649, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639932); - } - - // small rounding error occured in poolUtils where it looks like the lender has one extra QT then they actually have in the pool. - // fixed by adding a greater than with diff check - function test_regression_rounding_on_moveliquidity() external { - _rewardsHandler.bucketTake(29456557203126366201854827466482433206831494327361303, 19307664601998129837361, false, 3492651658979151995106448, 0); - _rewardsHandler.lenderKickAuction(4429580015302257459201655018526, 2770867242698718418626, 9388); - _rewardsHandler.repayDebt(1000000034925771973, 6930625368245303852701363167, 695149882294170920069268290133705109872933519679164510383901578196897792); - _rewardsHandler.moveLiquidity(41132919728951221583605488, 1102564553356549573347, 3410238307441803358653636, 52534, 4545656474572434187813557497); - } - - /** - Test was failing because positions manager was asserting popol errors and not position manager specific errors. - Fixed by adding _ensurePositionManagerError and _ensureRewardsManagerError and use them for manager function calls. - */ - function test_regression_failure_rewards_error_expect() external { - _rewardsHandler.redeemPositions(696309158766729979589534440166220067073887038152281856742599135926157312, 1870474563221356095022900189266, 18339, 1788135699833095102184812046279); - _rewardsHandler.kickReserveAuction(2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.memorializePositions(3671371551743995865, 99428, 1769100398, 7346319752739159); - _rewardsHandler.removeQuoteToken(2192844669750518881733689799257684129590332632218546259991943830203, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 419); - _rewardsHandler.bucketTake(1606233188525778759956925750889, 999999999999999529711266919982837439648513939, false, 9949287379244091378213227, 3763880249088816172848545904993); - _rewardsHandler.settleAuction(55047125510583885662282424543762268712076435621770843340435135, 110421716658405583291991613217254392572785222796303390816855215908218229630, 1, 292423500273437202726891613067834132052054453755096719350366291073639790); - _rewardsHandler.takeAuction(6950266099624730892351521874528127288800419, 193622918230501420262501464441837, 3, 20111879376924408726124755543747277966848148390627132304350); - _rewardsHandler.bucketTake(2, 747496798808534888002766542, true, 221618799467305555283, 661410089069342); - _rewardsHandler.settleAuction(39300471897997857700471, 363003327028528645286555715801688230394596448247050, 10248673406547114848805417911883714616830860910846335722973488061592062643, 115792089237316195423570985008687907853269984665640564039457584007913129639933); - _rewardsHandler.updateExchangeRate(3, 4485331781540521052468660062240, 3); - _rewardsHandler.burn(20137826853855347137082717445, 194771708143610511040376640, 3706358530371129512271612, 14017756049926664649805910646); - } - // Minting uint256.MAX amounts of ajna to reserve auction takers, which causes overflow in rewards staking actions. - // fixed by minting a smaller value to ajna reserve auction takers - function test_regression_rewards_minting_max_ajna() external { - _rewardsHandler.transferLps(13692, 15203, 2000001367681895243673966806260, 13770006083256457112227, 1672449141); - _rewardsHandler.takeReserves(4947313423932616986372909633726, 3107772, 18585868099953215630514); - _rewardsHandler.bucketTake(3, 3, true, 5727003712726959800, 115792089237316195423570985008687907853269984665640564039457584007913129639934); - _rewardsHandler.moveLiquidity(3157314460140, 999999999964761098816798416093, 100749181527550271579655357, 73559801971876655830284650454672056023206176402691276845053940498149797, 2453039451665641392603351405279); - _rewardsHandler.pullCollateral(2227999026154255580938434049821, 1929, 21209080205145509799924600234705734221480599); - _rewardsHandler.unstake(77763028316274741760991770148, 889544954655483052642168760960, 1000073821603105106, 14781730360850539958536528862089); - _rewardsHandler.memorializePositions(3, 2, 357097079890085816811291167102020270214, 1); - _rewardsHandler.takeAuction(9839460449437189937506383151267733565283, 75862151750582034326928310158878873987779724, 442512581894915658301040013323770372275116408434438200991, 4465076566725287152347371577222429); - _rewardsHandler.takeReserves(18196, 115792089237316195423570985008687907853269984665640564039457459248091501068020, 7726328457662922431145272); - _rewardsHandler.updateExchangeRate(934079800963406838548241273326154438, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639934); - } - - // Staker earned rewards for staked position, RW6 broke because invariant was incorrectly accruing `updateRewardsClaimed` vs `rewardsClaimed` - // fixed unbound claimRewards invariants - function test_regression_rewards_incorrect_accum_update() external { - _rewardsHandler.bucketTake(4645898402004950106563597036, 1, true, 3, 2); - _rewardsHandler.kickReserveAuction(3, 144); - _rewardsHandler.claimRewards(44705418907931161819765043998797583827, 151688058596538037321153972615358552, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 5828173); - _rewardsHandler.lenderKickAuction(1, 0, 58846077910505664190879804); - _rewardsHandler.kickAuction(115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 1); - _rewardsHandler.burn(115792089237316195423570985008687907853269984665640564039457584007913129639934, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639932, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.moveLiquidity(7886, 1223, 249959471556478, 8036, 957); - _rewardsHandler.drawDebt(3724423259505818275008000000000, 5967, 5194); - _rewardsHandler.claimRewards(1, 0, 0, 330972817125); - } - - // RW1 and RW2 were written incorrectly. a rewards cap of 0.1 was placed against the total rewards amount which was incorrect - // fixed by creating distinct mappings to house rewards: updateRewardsClaimed for updating and rewardsClaimed for staking - function test_regression_rewards_RW1_RW2_combined_accum() external { - _rewardsHandler.stake(115792089237316195423570985008687907853269984665640564039457584007913129639935, 87088279133912395360162508437887139725077665220, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 3); - _rewardsHandler.kickAuction(115792089237316195423570985008687907853269984665640564039457584007913129639933, 115792089237316195423570985008687907853269984665640564039457584007913129639933, 564626589538129677125005778268696423, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - _rewardsHandler.takeReserves(11684, 194771708143610511040376640, 2726053605342047396811); - _rewardsHandler.repayDebt(3262, 43081368297850871705277077223467592454899175043611071, 10274); - _rewardsHandler.redeemPositions(127647, 176264227116073539466710292640534, 3642, 6424); - _rewardsHandler.unstake(15694742330810166545782643648487796809086809379390, 607818605291333492717424880577475155800497188006507929455743410345480887752, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639935); - - invariant_rewards_RW1_RW2(); - } -}