Omniscia Tangible Audit

Basket Code Style Findings

Basket Code Style Findings

BTE-01C: Inefficient mapping Lookups

Description:

The linked statements perform key-based lookup operations on mapping declarations from storage multiple times for the same key redundantly.

Example:

src/Basket.sol
284/**
285 * @notice This method is executed upon the callback of vrf for entropy. This method will use the randomWord to select
286 * a ranom index from the `depositedTnfts` array to be next redeemable NFT.
287 * @param randomWord Random uint seed received from vrf.
288 */
289function fulfillRandomSeed(uint256 randomWord) external onlyBasketVrfConsumer {
290
291 // choose a nft to be next redeemable
292
293 uint256 index;
294 index = randomWord % depositedTnfts.length;
295
296 address tnft = depositedTnfts[index].tnft;
297 uint256 tokenId = depositedTnfts[index].tokenId;
298
299 nextToRedeem = RedeemData(tnft, tokenId);
300 emit RedeemableChosen(tnft, tokenId);
301
302 seedRequestInFlight = false;
303 pendingSeedRequestId = 0;
304}

Recommendation:

As the lookups internally perform an expensive keccak256 operation, we advise the lookups to be cached wherever possible to a single local declaration that either holds the value of the mapping in case of primitive types or holds a storage pointer to the struct contained.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The Tangible team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase

BTE-02C: Inexistent Error Messages

TypeSeverityLocation
Code StyleBasket.sol:L170, L488, L491

Description:

The linked require checks have no error messages explicitly defined.

Example:

src/Basket.sol
170require(msg.sender == _getBasketVrfConsumer());

Recommendation:

We advise each to be set so to increase the legibility of the codebase and aid in validating the require checks' conditions.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

A proper textual description of the condition validated has been introduced to all referenced require checks thus addressing this exhibit.

BTE-03C: Multiple Expensive Balance Measurements

Description:

The Basket::_transferRent function will inefficiently query the balance of the contract itself multiple times.

Example:

src/Basket.sol
874// start iterating through the master claimable rent array claiming rent for each token.
875uint256 index;
876while (_withdrawAmount > primaryRentToken.balanceOf(address(this)) && index < counter) {
877
878 IRentManager rentManager = _getRentManager(claimableRent[index].tnft);
879 uint256 tokenId = claimableRent[index].tokenId;
880
881 if (rentManager.claimableRentForToken(tokenId) > 0) {
882
883 uint256 preBal = primaryRentToken.balanceOf(address(this));
884 uint256 claimedRent = rentManager.claimRentForToken(tokenId);
885
886 require(primaryRentToken.balanceOf(address(this)) == (preBal + claimedRent), "claiming error");
887 }
888
889 unchecked {
890 ++index;
891 }
892}

Recommendation:

We advise the balance to be tracked locally, significantly reducing the overall gas cost required by the function.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The code was updated to track the balance of the contract before the while loop and to validate that the pre-loop balance plus the cumulative claimedRent value equals the post-loop balance of the contract, significantly optimizing the gas cost of the function whilst retaining the same security guarantees.

BTE-04C: Potentially Inefficient Array Shifts

Description:

The Basket::_redeemTNFT function will perform multiple array shift operations as it removes an entry from at minimum two arrays and at maximum three arrays. These operations are inefficiently performed under certain conditions.

Example:

src/Basket.sol
792uint256 index;
793(index,) = _isDepositedTnft(_tangibleNFT, _tokenId);
794depositedTnfts[index] = depositedTnfts[depositedTnfts.length - 1];
795depositedTnfts.pop();
796
797(index,) = _isTokenIdLibrary(_tangibleNFT, _tokenId);
798tokenIdLibrary[_tangibleNFT][index] = tokenIdLibrary[_tangibleNFT][tokenIdLibrary[_tangibleNFT].length - 1];
799tokenIdLibrary[_tangibleNFT].pop();
800
801if (tokenIdLibrary[_tangibleNFT].length == 0) {
802 (index,) = _isSupportedTnft(_tangibleNFT);
803 tnftsSupported[index] = tnftsSupported[tnftsSupported.length - 1];
804 tnftsSupported.pop();
805}

Recommendation:

We advise the referenced assignments to be performed solely when the index that is being removed is not the last element in the array as otherwise they will execute a costly self-assignment redundantly.

Alleviation (106fc61dcd):

The code was updated per our recommendation solely for the two out of three referenced instances, rendering it partially alleviated.

Alleviation (d7d2436ede):

The last referenced array shift instance was optimized as well rendering the exhibit fully applied.

BTE-05C: Redundant Assignment & Addition Operations

Description:

The referenced assignment-and-addition operations (+=) are redundant as they are performed on a variable that has a value of 0.

Example:

src/Basket.sol
598unchecked {
599 // get total value of nfts in basket.
600 totalValue += totalNftValue;
601
602 // get value of rent accrued by this contract.
603 totalValue += totalRentValue * decimalsDiff();
604}

Recommendation:

We advise direct assignments to be performed and the statements to be relocated outside their unchecked code blocks as no form of arithmetic is needed to be performed for them.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

Both referenced addition-and-assignments statements have been replaced by direct assignments, optimizing the code's gas cost.

BTE-06C: Redundant Evaluation of Claimable Rent

Description:

The way the claimableRent array is constructed guarantees that all entries within it have a non-zero rent balance to be claimed.

Example:

src/Basket.sol
848// iterate through all TNFT contracts supported by this basket.
849for (uint256 i; i < tnftsSupported.length;) {
850 address tnft = tnftsSupported[i];
851
852 // for each TNFT supported, make a batch call to the rent manager for all rent claimable for the array of tokenIds.
853 uint256[] memory claimables = _getRentManager(tnft).claimableRentForTokenBatch(tokenIdLibrary[tnft]);
854
855 // iterate through the array of claimable rent for each tokenId for each TNFT and push it to the master claimableRent array.
856 for (uint256 j; j < claimables.length;) {
857 uint256 amountClaimable = claimables[j];
858
859 if (amountClaimable > 0) {
860 claimableRent[counter] = RentData(tnft, tokenIdLibrary[tnft][j], amountClaimable);
861 unchecked {
862 ++counter;
863 }
864 }
865 unchecked {
866 ++j;
867 }
868 }
869 unchecked {
870 ++i;
871 }
872}
873
874// start iterating through the master claimable rent array claiming rent for each token.
875uint256 index;
876while (_withdrawAmount > primaryRentToken.balanceOf(address(this)) && index < counter) {
877
878 IRentManager rentManager = _getRentManager(claimableRent[index].tnft);
879 uint256 tokenId = claimableRent[index].tokenId;
880
881 if (rentManager.claimableRentForToken(tokenId) > 0) {
882
883 uint256 preBal = primaryRentToken.balanceOf(address(this));
884 uint256 claimedRent = rentManager.claimRentForToken(tokenId);
885
886 require(primaryRentToken.balanceOf(address(this)) == (preBal + claimedRent), "claiming error");
887 }
888
889 unchecked {
890 ++index;
891 }
892}

Recommendation:

We advise the referenced if conditional to be omitted, greatly optimizing the function's gas cost.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The redundant evaluation of claimable rent has been omitted, optimizing the loop's gas cost significantly.

BTE-07C: Redundant Invocations of Decimals

Description:

The Basket::decimals function is guaranteed to yield a value of 1e18 on all invocations as its value is never overridden.

Example:

src/Basket.sol
590sharePrice = (getTotalValueOfBasket() * 10 ** decimals()) / totalSupply();

Recommendation:

To ensure consistency with the rebase system that uses 1e18 as a base, we advise the refenced instances to utilize the 1e18 representation and potentially the contract-level constant of it we have proposed in a separate exhibit.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The Tangible team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase

BTE-08C: Redundant Parenthesis Statements

TypeSeverityLocation
Code StyleBasket.sol:L751

Description:

The referenced statements are redundantly wrapped in parenthesis' (()).

Example:

src/Basket.sol
751if ((nextToRedeem.tnft == address(0)) && (!seedRequestInFlight)) {

Recommendation:

We advise them to be safely omitted, increasing the legibility of the codebase.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The redundant parenthesis have been omitted as advised, increasing the legibility of the codebase.

BTE-09C: Redundant Yield of Boolean

Description:

The Basket::_isDepositedTnft and Basket::_isTokenIdLibrary functions are invoked in locations where they are guaranteed to yield true.

Example:

src/Basket.sol
1035/**
1036 * @notice This helper method returns whether a provided TNFT token exists in the depositedTnfts array and if so, where in the array.
1037 * @param _tnft contract address.
1038 * @param _tokenId TokenId of token being fetched.
1039 * @return index -> Where in the `depositedTnfts` array the specified token resides.
1040 * @return exists -> If token exists in `depositedTnfts`, will be true. Otherwise false.
1041 */
1042function _isDepositedTnft(address _tnft, uint256 _tokenId) internal view returns (uint256 index, bool exists) {
1043 for(uint256 i; i < depositedTnfts.length;) {
1044 if (depositedTnfts[i].tokenId == _tokenId && depositedTnfts[i].tnft == _tnft) return (i, true);
1045 unchecked {
1046 ++i;
1047 }
1048 }
1049 return (0, false);
1050}

Recommendation:

We advise the bool value yielded by each function to be omitted, optimizing their overall gas cost.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The referenced functions are no longer present in the codebase, rendering the exhibit no longer applicable.

BTE-10C: Repetitive Value Literals

TypeSeverityLocation
Code StyleBasket.sol:L229, L567, L587

Description:

The linked value literals are repeated across the codebase multiple times.

Example:

src/Basket.sol
229_setRebaseIndex(1 ether);

Recommendation:

We advise each to be set to its dedicated constant variable instead optimizing the legibility of the codebase.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The Tangible team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase

BTE-11C: Suboptimal Struct Declaration Styles

TypeSeverityLocation
Code StyleBasket.sol:L299, L564, L690, L718, L754, L860

Description:

The linked declaration styles of the referenced structs are using index-based argument initialization.

Example:

src/Basket.sol
299nextToRedeem = RedeemData(tnft, tokenId);

Recommendation:

We advise the key-value declaration format to be utilized instead in each instance, greatly increasing the legibility of the codebase.

Alleviation (106fc61dcd38fe29a81d1984ccde9171f5f231af):

The Tangible team evaluated this exhibit but opted to acknowledge it in the current iteration of the codebase