Skip to content

Commit 989b5f2

Browse files
MikeHathawayMikegrandizzy
authored
Code Arena: Style and Gas (#870)
* gas optimize with preincrement and predecrement * remove unneeded + * use local variable in getPositionInfo * remove unnecessary local variable * simplify typecasting * expand natspec * additional gas optimizations * additional gas optimizations * remove todo * Code Arena: Consolidate mappings into TokenInfo struct (#872) * add TokenInfo struct * cleanups * fix nit * Group all mappings in TokenInfo struct (#873) * Group all mappings in TokenInfo struct - minor gas savings, much better code readability * expand natspec * alphebatize interface --------- Co-authored-by: Mike <[email protected]> --------- Co-authored-by: Mike <[email protected]> Co-authored-by: grandizzy <[email protected]> --------- Co-authored-by: Mike <[email protected]> Co-authored-by: grandizzy <[email protected]>
1 parent ab6ac72 commit 989b5f2

File tree

4 files changed

+129
-86
lines changed

4 files changed

+129
-86
lines changed

src/PositionManager.sol

Lines changed: 72 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { IPool } from './interfaces/pool/IPool.sol';
1313
import { IPositionManager } from './interfaces/position/IPositionManager.sol';
1414
import { IPositionManagerOwnerActions } from './interfaces/position/IPositionManagerOwnerActions.sol';
1515
import { IPositionManagerDerivedState } from './interfaces/position/IPositionManagerDerivedState.sol';
16-
import { Position } from './interfaces/position/IPositionManagerState.sol';
1716

1817
import { ERC20PoolFactory } from './ERC20PoolFactory.sol';
1918
import { ERC721PoolFactory } from './ERC721PoolFactory.sol';
@@ -39,7 +38,6 @@ import { PositionNFTSVG } from './libraries/external/PositionNFTSVG.sol';
3938
* - `burn` positions `NFT`
4039
*/
4140
contract 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

Comments
 (0)