Skip to content

Commit

Permalink
Quote to lps inc on move (#877)
Browse files Browse the repository at this point in the history
* cleaned up requires, formally added more PositionManager invar

* cleaned up requires, formally added more PositionManager invar

* added logs

* works with diff

* cleanup

---------

Co-authored-by: Ian Harvey <[email protected]>
  • Loading branch information
ith-harvey and Ian Harvey authored Jun 7, 2023
1 parent 2d93b7d commit c7c84e2
Show file tree
Hide file tree
Showing 16 changed files with 115 additions and 99 deletions.
7 changes: 7 additions & 0 deletions tests/INVARIANTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@
- **PM1**: LP balance of PositionManager in a Pool for a Bucket should be the sum of the positions[...][index].lps for all tokens/users
- **PM2** Sum of the LP balance of the PositionManager in a Pool across all buckets should be the sum of the positions[...].[...].lps across all indexes and tokens/users
- **PM3**: Position deposit time (`depositTime`) tracked by tokenId in PositionManager (`positions[tokenId][index]`) should always be of equal or lesser value than the PositionManager's LP at that index in the pool contract.
- **PM4**: mint should populate `poolKey[tokenId]`, `positionIndexes` should be empty and the owner of the NFT should be the caller.
- **PM5**: burn should reset `poolKey[tokenId]`, `positionIndexes` should be empty and the owner of the NFT should be the zero address.
- **PM6**: moveLiquidity should remove all LP from the from index and set deposit time to zero. To index should receive increased LP and deposit time should match positionManager's deposit time. PositionManager should have less then or equal to the same quoteToken after move.
- **PM7**: memorializePosition should transfer all pool contract LP in the provided index to the positionManager. PositionManager should take on the greater of the lender of positionManager's deposit time. PositionManager's LP in the provided index should match what the lender had in the pool contract before memorializePosition was called.
- **PM8**: reedemPositions should maintain the preivous owner of the NFT position and pool associated with position. Remove the passed in indexes from the positionIndexes associated token. LP of redeemer should increase with same amount that LP of PositionManager decrease (by checking the pool). tokenId associated with the indexes redeemed should be zero.


Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import { ERC721PoolFactory } from 'src/ERC721PoolFactory.sol';
import { PositionManager } from 'src/PositionManager.sol';
import { Maths } from 'src/libraries/internal/Maths.sol';

import { IBaseHandler } from '../interfaces/IBaseHandler.sol';
import { BaseInvariants } from '../base/BaseInvariants.sol';
import { ReserveERC20PoolInvariants } from '../ERC20Pool/ReserveERC20PoolInvariants.t.sol';
import { ReserveERC20PoolHandler } from '../ERC20Pool/handlers/ReserveERC20PoolHandler.sol';
import { TokenWithNDecimals } from '../../utils/Tokens.sol';
import { IBaseHandler } from '../interfaces/IBaseHandler.sol';
import { IPositionsAndRewardsHandler } from '../interfaces/IPositionsAndRewardsHandler.sol';
import { BaseInvariants } from '../base/BaseInvariants.sol';
import { ReserveERC20PoolInvariants } from '../ERC20Pool/ReserveERC20PoolInvariants.t.sol';
import { ReserveERC20PoolHandler } from '../ERC20Pool/handlers/ReserveERC20PoolHandler.sol';
import { TokenWithNDecimals } from '../../utils/Tokens.sol';

import { PositionHandler } from './handlers/PositionHandler.sol';

Expand All @@ -43,7 +44,7 @@ contract PositionsInvariants is BaseInvariants {
_erc721impl = _erc721poolFactory.implementation();
_erc20pool = ERC20Pool(_erc20poolFactory.deployPool(address(_collateral), address(_quote), 0.05 * 10**18));
_pool = Pool(address(_erc20pool));
_position = new PositionManager(_erc20poolFactory, _erc721poolFactory);
_position = new PositionManager(_erc20poolFactory, _erc721poolFactory);

excludeContract(address(_ajna));
excludeContract(address(_collateral));
Expand Down Expand Up @@ -71,7 +72,7 @@ contract PositionsInvariants is BaseInvariants {
}

function invariant_positions_PM1_PM2_PM3() public useCurrentTimestamp {
uint256[] memory bucketIndexes = IBaseHandler(_handler).getBucketIndexesWithPosition();
uint256[] memory bucketIndexes = IPositionsAndRewardsHandler(_handler).getBucketIndexesWithPosition();

// loop over bucket indexes with positions
for (uint256 i = 0; i < bucketIndexes.length; i++) {
Expand All @@ -84,7 +85,7 @@ contract PositionsInvariants is BaseInvariants {
poolLpAccum += poolLp;

// loop over tokenIds in bucket indexes
uint256[] memory tokenIds = IBaseHandler(_handler).getTokenIdsByBucketIndex(bucketIndex);
uint256[] memory tokenIds = IPositionsAndRewardsHandler(_handler).getTokenIdsByBucketIndex(bucketIndex);
for (uint256 k = 0; k < tokenIds.length; k++) {
uint256 tokenId = tokenIds[k];

Expand Down
29 changes: 8 additions & 21 deletions tests/forge/invariants/PositionsAndRewards/RewardsInvariants.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { Maths } from 'src/libraries/internal/Maths.sol';
import { RewardsManager } from 'src/RewardsManager.sol';
import { _getEpochInfo } from 'src/RewardsManager.sol';

import { IBaseHandler } from '../interfaces/IBaseHandler.sol';
import { RewardsHandler } from './handlers/RewardsHandler.sol';
import { PositionsInvariants } from './PositionsInvariants.t.sol';
import { IBaseHandler } from '../interfaces/IBaseHandler.sol';
import { IPositionsAndRewardsHandler } from '../interfaces/IPositionsAndRewardsHandler.sol';
import { RewardsHandler } from './handlers/RewardsHandler.sol';
import { PositionsInvariants } from './PositionsInvariants.t.sol';

contract RewardsInvariants is PositionsInvariants {

Expand Down Expand Up @@ -42,39 +43,25 @@ contract RewardsInvariants is PositionsInvariants {

_handler = address(_rewardsHandler);
}
function invariant_rewards_RW1() public useCurrentTimestamp {

function invariant_rewards_RW1_RW2() public useCurrentTimestamp {

// get current epoch (is incremented every kickReserve() call)
uint256 curEpoch = _pool.currentBurnEpoch();

// get rewards that have been claimed
uint256 claimedRewards = IBaseHandler(_handler).totalRewardPerEpoch(curEpoch);
uint256 claimedRewards = IPositionsAndRewardsHandler(_handler).totalRewardPerEpoch(curEpoch);

// total ajna burned by the pool over the epoch
(, uint256 totalBurnedInPeriod,) = _getEpochInfo(address(_pool), curEpoch);

// stake rewards cap is 80% of total burned
uint256 stakeRewardsCap = Maths.wmul(totalBurnedInPeriod, 0.8 * 1e18);

// check claimed rewards < rewards cap
if (stakeRewardsCap != 0) require(claimedRewards < stakeRewardsCap, "Rewards invariant RW1");
}

function invariant_rewards_RW2() public useCurrentTimestamp {

// get current epoch (is incremented every kickReserve() call)
uint256 curEpoch = _pool.currentBurnEpoch();

// get rewards that have been claimed
uint256 claimedRewards = IBaseHandler(_handler).totalRewardPerEpoch(curEpoch);

// total ajna burned by the pool over the epoch
(, uint256 totalBurnedInPeriod,) = _getEpochInfo(address(_pool), curEpoch);

// update rewards cap is 10% of total burned
uint256 updateRewardsCap = Maths.wmul(totalBurnedInPeriod, 0.1 * 1e18);

// check claimed rewards < rewards cap
if (updateRewardsCap != 0) require(claimedRewards < updateRewardsCap, "Rewards invariant RW2");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { PositionManager } from 'src/PositionManager.sol';
import { ERC20Pool } from 'src/ERC20Pool.sol';

import { UnboundedPositionsHandler } from './unbounded/UnboundedPositionsHandler.sol';
import { BaseERC20PoolHandler } from '../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol';
import { BaseERC20PoolHandler } from '../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol';

abstract contract PositionHandlerAbstract is UnboundedPositionsHandler {

Expand Down Expand Up @@ -169,12 +169,12 @@ abstract contract PositionHandlerAbstract is UnboundedPositionsHandler {
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);
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];
boundedFromIndex_ = indexes[0];

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,6 @@ contract RewardsHandler is UnboundedRewardsHandler, PositionHandlerAbstract, Res

// Action phase
_unstake(tokenId);

// Post action
// check token was transferred from rewards contract to actor
assertEq(_position.ownerOf(tokenId), _actor);
}

function updateExchangeRate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IPositionManagerOwnerActions } from 'src/interfaces/position/IPositionM
import { _depositFeeRate } from 'src/libraries/helpers/PoolHelper.sol';
import { Maths } from "src/libraries/internal/Maths.sol";

import { BaseERC20PoolHandler } from '../../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol';
import { BaseERC20PoolHandler } from '../../../ERC20Pool/handlers/unbounded/BaseERC20PoolHandler.sol';

import { PositionManager } from 'src/PositionManager.sol';
import { RewardsManager } from 'src/RewardsManager.sol';
Expand All @@ -34,19 +34,18 @@ abstract contract BasePositionsHandler is BaseERC20PoolHandler {
mapping(uint256 => EnumerableSet.UintSet) internal tokenIdsByBucketIndex;
EnumerableSet.UintSet internal bucketIndexesWithPosition;

// used for removing all CT and QT to reset exchange rate
mapping(address => EnumerableSet.UintSet) internal tokenIdsByActor;
// 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 ``
// used to track LP changes in `_redeemPositions()` and `_memorializePositions()`
mapping(uint256 => uint256) internal bucketIndexToActorPositionManLps;
mapping(uint256 => uint256) internal bucketIndexToPositionManPoolLps;
mapping(uint256 => uint256) internal bucketIndexToActorPoolLps;
mapping(uint256 => uint256) internal bucketIndexToDepositTime;

// Rewards invariant test state //
EnumerableSet.UintSet internal stakedTokenIds;
mapping(uint256 => uint256) public totalRewardPerEpoch; // total rewards per epoch
uint256 public totalStakerRewPerEpoch; // amount of reserve decrease
uint256 public totalUpdaterRewPerEpoch; // amount of reserve increase
Expand Down
Loading

0 comments on commit c7c84e2

Please sign in to comment.