@@ -13,7 +13,6 @@ import { IPool } from './interfaces/pool/IPool.sol';
1313import { IPositionManager } from './interfaces/position/IPositionManager.sol ' ;
1414import { IPositionManagerOwnerActions } from './interfaces/position/IPositionManagerOwnerActions.sol ' ;
1515import { IPositionManagerDerivedState } from './interfaces/position/IPositionManagerDerivedState.sol ' ;
16- import { Position } from './interfaces/position/IPositionManagerState.sol ' ;
1716
1817import { ERC20PoolFactory } from './ERC20PoolFactory.sol ' ;
1918import { ERC721PoolFactory } from './ERC721PoolFactory.sol ' ;
@@ -39,7 +38,6 @@ import { PositionNFTSVG } from './libraries/external/PositionNFTSVG.sol';
3938 * - `burn` positions `NFT`
4039 */
4140contract PositionManager is PermitERC721 , IPositionManager , Multicall , ReentrancyGuard {
42-
4341 using EnumerableSet for EnumerableSet.UintSet;
4442 using SafeERC20 for ERC20 ;
4543
@@ -54,15 +52,8 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
5452 /*** State Variables ***/
5553 /***********************/
5654
57- /// @dev Mapping of `token id => ajna pool address` for which token was minted.
58- mapping (uint256 => address ) public override poolKey;
59-
60- /// @dev Mapping of `token id => ajna pool address` for which token was minted.
61- mapping (uint256 => mapping (uint256 => Position)) internal positions;
62- /// @dev Mapping of `token id => bucket indexes` associated with position.
63- mapping (uint256 => EnumerableSet.UintSet) internal positionIndexes;
64- /// @dev Mapping of `token id => last redeem timestamp`.
65- mapping (uint256 => uint256 ) internal adjustmentTime;
55+ /// @dev Mapping tracking information of position tokens minted.
56+ mapping (uint256 tokenId = > TokenInfo) internal positionTokens;
6657
6758 /// @dev Id of the next token that will be minted. Skips `0`.
6859 uint176 private _nextId = 1 ;
@@ -119,7 +110,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
119110 if (! _isApprovedOrOwner (msg .sender , tokenId_)) revert NoAuth ();
120111
121112 // revert if the token id is not minted for given pool address
122- if (pool_ != poolKey [tokenId_]) revert WrongPool ();
113+ if (pool_ != positionTokens [tokenId_].pool ) revert WrongPool ();
123114
124115 _;
125116 }
@@ -132,7 +123,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
132123 modifier recordAdjustmentTime (uint256 tokenId_ ) {
133124 _;
134125
135- adjustmentTime [tokenId_] = block .timestamp ;
126+ positionTokens [tokenId_].adjustmentTime = uint96 ( block .timestamp ) ;
136127 }
137128
138129 /*******************/
@@ -159,7 +150,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
159150 * @inheritdoc IPositionManagerOwnerActions
160151 * @dev === Write state ===
161152 * @dev `_nonces`: remove `tokenId` nonce
162- * @dev `poolKey `: remove `tokenId => pool ` mapping
153+ * @dev `tokenInfo `: remove `tokenId => TokenInfo ` mapping
163154 * @dev === Revert on ===
164155 * @dev - `mayInteract`:
165156 * @dev token id is not a valid / minted id
@@ -174,12 +165,11 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
174165 uint256 tokenId_
175166 ) external override mayInteract (pool_, tokenId_) {
176167 // revert if trying to burn an positions token that still has liquidity
177- if (positionIndexes [tokenId_].length () != 0 ) revert LiquidityNotRemoved ();
168+ if (positionTokens [tokenId_].positionIndexes .length () != 0 ) revert LiquidityNotRemoved ();
178169
179170 // remove permit nonces and pool mapping for burned token
180171 delete _nonces[tokenId_];
181- delete poolKey[tokenId_];
182- delete adjustmentTime[tokenId_];
172+ delete positionTokens[tokenId_];
183173
184174 _burn (tokenId_);
185175
@@ -192,36 +182,42 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
192182 * @dev - `lenderInfo()`: get lender position in bucket
193183 * @dev - `transferLP()`: transfer `LP` ownership to `PositionManager` contract
194184 * @dev === Write state ===
195- * @dev `positionIndexes`: add bucket index
196- * @dev `positions`: update `tokenId => bucket id` position
185+ * @dev `TokenInfo. positionIndexes`: add bucket index
186+ * @dev `TokenInfo. positions`: update `tokenId => bucket id` position
197187 * @dev === Revert on ===
198188 * @dev - `mayInteract`:
199189 * @dev token id is not a valid / minted id
200190 * @dev sender is not owner `NoAuth()`
201191 * @dev token id not minted for given pool `WrongPool()`
192+ * @dev - owner supplied insufficient allowance for the lp transfer `AllowanceTooLow()`
202193 * @dev === Emit events ===
194+ * @dev - `TransferLP`
203195 * @dev - `MemorializePosition`
204196 */
205197 function memorializePositions (
206198 address pool_ ,
207199 uint256 tokenId_ ,
208200 uint256 [] calldata indexes_
209201 ) external mayInteract (pool_, tokenId_) override {
210- EnumerableSet.UintSet storage positionIndex = positionIndexes[tokenId_];
202+ TokenInfo storage tokenInfo = positionTokens[tokenId_];
203+ EnumerableSet.UintSet storage positionIndexes = tokenInfo.positionIndexes;
211204
212205 IPool pool = IPool (pool_);
213206 address owner = ownerOf (tokenId_);
207+
214208 LendersBucketLocalVars memory vars;
215209
216- uint256 indexesLength = indexes_. length ;
210+ // local vars used in for loop for reduced gas
217211 uint256 index;
212+ uint256 indexesLength = indexes_.length ;
218213
214+ // loop through all bucket indexes and memorialize lp balance and deposit time to the Position.
219215 for (uint256 i = 0 ; i < indexesLength; ) {
220216 index = indexes_[i];
221217
222218 // record bucket index at which a position has added liquidity
223219 // slither-disable-next-line unused-return
224- positionIndex .add (index);
220+ positionIndexes .add (index);
225221
226222 (vars.lpBalance, vars.depositTime) = pool.lenderInfo (index, owner);
227223
@@ -230,7 +226,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
230226
231227 if (vars.allowance < vars.lpBalance) revert AllowanceTooLow ();
232228
233- Position memory position = positions[tokenId_] [index];
229+ Position memory position = tokenInfo. positions[index];
234230
235231 // check for previous deposits
236232 if (position.depositTime != 0 ) {
@@ -247,7 +243,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
247243 position.depositTime = vars.depositTime;
248244
249245 // save position in storage
250- positions[tokenId_] [index] = position;
246+ tokenInfo. positions[index] = position;
251247
252248 unchecked { ++ i; }
253249 }
@@ -261,11 +257,12 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
261257 /**
262258 * @inheritdoc IPositionManagerOwnerActions
263259 * @dev === Write state ===
264- * @dev `poolKey `: update `tokenId => pool ` mapping
260+ * @dev `tokenInfo `: update `tokenId => TokenInfo ` mapping
265261 * @dev === Revert on ===
266262 * @dev provided pool not valid `NotAjnaPool()`
267263 * @dev === Emit events ===
268264 * @dev - `Mint`
265+ * @dev - `Transfer`
269266 */
270267 function mint (
271268 address pool_ ,
@@ -278,7 +275,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
278275 tokenId_ = _nextId++ ;
279276
280277 // record which pool the tokenId was minted in
281- poolKey [tokenId_] = pool_;
278+ positionTokens [tokenId_].pool = pool_;
282279
283280 _mint (recipient_, tokenId_);
284281
@@ -291,17 +288,19 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
291288 * @dev `bucketInfo()`: get from bucket info
292289 * @dev `moveQuoteToken()`: move liquidity between buckets
293290 * @dev === Write state ===
294- * @dev `positionIndexes`: remove from bucket index
295- * @dev `positionIndexes`: add to bucket index
296- * @dev `positions`: update from bucket position
297- * @dev `positions`: update to bucket position
291+ * @dev `TokenInfo. positionIndexes`: remove from bucket index
292+ * @dev `TokenInfo. positionIndexes`: add to bucket index
293+ * @dev `TokenInfo. positions`: update from bucket position
294+ * @dev `TokenInfo. positions`: update to bucket position
298295 * @dev === Revert on ===
299296 * @dev - `mayInteract`:
300297 * @dev token id is not a valid / minted id
301298 * @dev sender is not owner `NoAuth()`
302299 * @dev token id not minted for given pool `WrongPool()`
303- * @dev - positions token to burn has liquidity `LiquidityNotRemoved()`
300+ * @dev - positions token to burn has liquidity `RemovePositionFailed()`
301+ * @dev - tried to move from bankrupt bucket `BucketBankrupt()`
304302 * @dev === Emit events ===
303+ * @dev - `MoveQuoteToken`
305304 * @dev - `MoveLiquidity`
306305 */
307306 function moveLiquidity (
@@ -311,7 +310,8 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
311310 uint256 toIndex_ ,
312311 uint256 expiry_
313312 ) external override nonReentrant mayInteract (pool_, tokenId_) recordAdjustmentTime (tokenId_) {
314- Position storage fromPosition = positions[tokenId_][fromIndex_];
313+ TokenInfo storage tokenInfo = positionTokens[tokenId_];
314+ Position storage fromPosition = tokenInfo.positions[fromIndex_];
315315
316316 MoveLiquidityLocalVars memory vars;
317317 vars.fromDepositTime = fromPosition.depositTime;
@@ -355,19 +355,19 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
355355 expiry_
356356 );
357357
358- EnumerableSet.UintSet storage positionIndex = positionIndexes[tokenId_] ;
358+ EnumerableSet.UintSet storage positionIndexes = tokenInfo. positionIndexes;
359359
360360 // 1. update FROM memorialized position
361- if (! positionIndex .remove (fromIndex_)) revert RemovePositionFailed (); // revert if FROM position is not in memorialized indexes
361+ if (! positionIndexes .remove (fromIndex_)) revert RemovePositionFailed (); // revert if FROM position is not in memorialized indexes
362362 if (vars.fromLP != vars.lpbAmountFrom) revert RemovePositionFailed (); // bucket has collateral and quote therefore LP is not redeemable for full quote token amount
363363
364- delete positions[tokenId_] [fromIndex_]; // remove memorialized FROM position
364+ delete tokenInfo. positions[fromIndex_]; // remove memorialized FROM position
365365
366366 // 2. update TO memorialized position
367367 // slither-disable-next-line unused-return
368- positionIndex .add (toIndex_); // record the TO memorialized position
368+ positionIndexes .add (toIndex_); // record the TO memorialized position
369369
370- Position storage toPosition = positions[tokenId_] [toIndex_];
370+ Position storage toPosition = tokenInfo. positions[toIndex_];
371371 vars.toDepositTime = toPosition.depositTime;
372372
373373 // reset LP in TO memorialized position if bucket went bankrupt after memorialization
@@ -404,41 +404,44 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
404404 * @dev token id is not a valid / minted id
405405 * @dev sender is not owner `NoAuth()`
406406 * @dev token id not minted for given pool `WrongPool()`
407- * @dev - position not tracked `RemoveLiquidityFailed()`
407+ * @dev - position not tracked `RemovePositionFailed()`
408+ * @dev - tried to redeem bankrupt bucket `BucketBankrupt()`
408409 * @dev === Emit events ===
410+ * @dev - `TransferLP`
409411 * @dev - `RedeemPosition`
410412 */
411413 function redeemPositions (
412414 address pool_ ,
413415 uint256 tokenId_ ,
414416 uint256 [] calldata indexes_
415417 ) external override mayInteract (pool_, tokenId_) recordAdjustmentTime (tokenId_) {
416- EnumerableSet.UintSet storage positionIndex = positionIndexes [tokenId_];
418+ TokenInfo storage tokenInfo = positionTokens [tokenId_];
417419
418420 IPool pool = IPool (pool_);
419421
422+ // local vars used in for loop for reduced gas
423+ uint256 index;
420424 uint256 indexesLength = indexes_.length ;
421425 uint256 [] memory lpAmounts = new uint256 [](indexesLength);
422426
423- uint256 index;
424-
427+ // retrieve LP amounts from each bucket index associated with token id
425428 for (uint256 i = 0 ; i < indexesLength; ) {
426429 index = indexes_[i];
427430
428- Position memory position = positions[tokenId_] [index];
431+ Position memory position = tokenInfo. positions[index];
429432
430433 if (position.lps == 0 || position.depositTime == 0 ) revert RemovePositionFailed ();
431434
432435 // check that bucket didn't go bankrupt after memorialization
433436 if (_bucketBankruptAfterDeposit (pool, index, position.depositTime)) revert BucketBankrupt ();
434437
435438 // remove bucket index at which a position has added liquidity
436- if (! positionIndex .remove (index)) revert RemovePositionFailed ();
439+ if (! tokenInfo.positionIndexes .remove (index)) revert RemovePositionFailed ();
437440
438441 lpAmounts[i] = position.lps;
439442
440443 // remove LP tracked by position manager at bucket index
441- delete positions[tokenId_] [index];
444+ delete tokenInfo. positions[index];
442445
443446 unchecked { ++ i; }
444447 }
@@ -470,10 +473,11 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
470473 ) internal override {
471474 // burning is not constrained by any redeem action
472475 if (to_ != address (0 )) {
476+ TokenInfo storage token = positionTokens[tokenId_];
473477 // revert transfer in case token positions were redeem in the last transfer lock period
474- if (block .timestamp - adjustmentTime[tokenId_] <= TRANSFER_LOCK_PERIOD) revert TransferLocked ();
478+ if (block .timestamp - token. adjustmentTime <= TRANSFER_LOCK_PERIOD) revert TransferLocked ();
475479
476- delete adjustmentTime[tokenId_] ;
480+ token. adjustmentTime = 0 ;
477481 }
478482 }
479483
@@ -530,30 +534,32 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
530534 uint256 tokenId_ ,
531535 uint256 index_
532536 ) external override view returns (uint256 ) {
533- Position memory position = positions[tokenId_][index_];
534- return _bucketBankruptAfterDeposit (IPool (poolKey[tokenId_]), index_, position.depositTime) ? 0 : position.lps;
537+ TokenInfo storage tokenInfo = positionTokens[tokenId_];
538+ Position memory position = tokenInfo.positions[index_];
539+ return _bucketBankruptAfterDeposit (IPool (tokenInfo.pool), index_, position.depositTime) ? 0 : position.lps;
535540 }
536541
537542 /// @inheritdoc IPositionManagerDerivedState
538543 function getPositionIndexes (
539544 uint256 tokenId_
540545 ) external view override returns (uint256 [] memory ) {
541- return positionIndexes [tokenId_].values ();
546+ return positionTokens [tokenId_].positionIndexes .values ();
542547 }
543548
544549 /// @inheritdoc IPositionManagerDerivedState
545550 function getPositionIndexesFiltered (
546551 uint256 tokenId_
547552 ) external view override returns (uint256 [] memory filteredIndexes_ ) {
548- uint256 [] memory indexes = positionIndexes[tokenId_].values ();
553+ TokenInfo storage tokenInfo = positionTokens[tokenId_];
554+ uint256 [] memory indexes = tokenInfo.positionIndexes.values ();
549555 uint256 indexesLength = indexes.length ;
550556
551557 // filter out bankrupt buckets
552558 filteredIndexes_ = new uint256 [](indexesLength);
553559 uint256 filteredIndexesLength = 0 ;
554- IPool pool = IPool (poolKey[tokenId_] );
560+ IPool pool = IPool (tokenInfo.pool );
555561 for (uint256 i = 0 ; i < indexesLength; ) {
556- if (! _bucketBankruptAfterDeposit (pool, indexes[i], positions[tokenId_] [indexes[i]].depositTime)) {
562+ if (! _bucketBankruptAfterDeposit (pool, indexes[i], tokenInfo. positions[indexes[i]].depositTime)) {
557563 filteredIndexes_[filteredIndexesLength++ ] = indexes[i];
558564 }
559565 unchecked { ++ i; }
@@ -568,12 +574,18 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
568574 uint256 tokenId_ ,
569575 uint256 index_
570576 ) external view override returns (uint256 , uint256 ) {
577+ Position memory position = positionTokens[tokenId_].positions[index_];
571578 return (
572- positions[tokenId_][index_] .lps,
573- positions[tokenId_][index_] .depositTime
579+ position .lps,
580+ position .depositTime
574581 );
575582 }
576583
584+ /// @inheritdoc IPositionManagerDerivedState
585+ function poolKey (uint256 tokenId_ ) external view override returns (address ) {
586+ return positionTokens[tokenId_].pool;
587+ }
588+
577589 /// @inheritdoc IPositionManagerDerivedState
578590 function isAjnaPool (
579591 address pool_ ,
@@ -587,15 +599,16 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
587599 uint256 tokenId_ ,
588600 uint256 index_
589601 ) external view override returns (bool ) {
590- return _bucketBankruptAfterDeposit (IPool (poolKey[tokenId_]), index_, positions[tokenId_][index_].depositTime);
602+ TokenInfo storage tokenInfo = positionTokens[tokenId_];
603+ return _bucketBankruptAfterDeposit (IPool (tokenInfo.pool), index_, tokenInfo.positions[index_].depositTime);
591604 }
592605
593606 /// @inheritdoc IPositionManagerDerivedState
594607 function isIndexInPosition (
595608 uint256 tokenId_ ,
596609 uint256 index_
597610 ) external override view returns (bool ) {
598- return positionIndexes [tokenId_].contains (index_);
611+ return positionTokens [tokenId_].positionIndexes .contains (index_);
599612 }
600613
601614 /**
@@ -606,7 +619,8 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
606619 ) public view override returns (string memory ) {
607620 if (! _exists (tokenId_)) revert NoToken ();
608621
609- address pool = poolKey[tokenId_];
622+ TokenInfo storage tokenInfo = positionTokens[tokenId_];
623+ address pool = tokenInfo.pool;
610624
611625 address collateralTokenAddress = IPool (pool).collateralAddress ();
612626 address quoteTokenAddress = IPool (pool).quoteTokenAddress ();
@@ -617,7 +631,7 @@ contract PositionManager is PermitERC721, IPositionManager, Multicall, Reentranc
617631 tokenId: tokenId_,
618632 pool: pool,
619633 owner: ownerOf (tokenId_),
620- indexes: positionIndexes[tokenId_] .values ()
634+ indexes: tokenInfo. positionIndexes.values ()
621635 });
622636
623637 return PositionNFTSVG.constructTokenURI (params);
0 commit comments