Omniscia Nexera Audit

MinimalisticERC20FractionDataManager Code Style Findings

MinimalisticERC20FractionDataManager Code Style Findings

MER-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:

contracts/datamanagers/MinimalisticERC20FractionDataManager.sol
216uint256 currentAllowance = _allowances[owner_][spender];
217if (currentAllowance != type(uint256).max) {
218 if (currentAllowance < amount) {
219 revert ERC20InsufficientAllowance(spender, currentAllowance, amount);
220 }
221 unchecked {
222 _allowances[owner_][spender] = currentAllowance - amount;
223 }
224}

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.

As the compiler's optimizations may take care of these caching operations automatically at-times, we advise the optimization to be selectively applied, tested, and then fully adopted to ensure that the proposed caching model indeed leads to a reduction in gas costs.

Alleviation:

The optimization was applied via usage of a custom AllowanceAmount structure which is slightly inefficient.

We advise our original optimization to be applied, caching the _allowances[owner_] lookup to a local mapping(address => uint256) storage variable that can consequently be re-used.

The Nexera team evaluated our original recommendation and via gas unit tests identified that the specialized AllowanceAmount optimization that they have applied is the most optimal one.

As the code of the project has the lowest gas cost with the solution implemented by the Nexera team, we consider this exhibit adequately addressed.

MER-02C: Inexecutable Code

Description:

The MinimalisticERC20FractionDataManager::_writeTransfer function contains code to handle transfers from or towards the zero address, however, the contract itself never allows the function to be invoked with such arguments.

Example:

contracts/datamanagers/MinimalisticERC20FractionDataManager.sol
227function _writeTransfer(address from, address to, uint256 amount) internal {
228 if (address(dataIndex) == address(0)) revert ContractNotInitialized();
229 if (from == address(0)) {
230 dataIndex.write(address(fungibleFractionsDO), _datapoint, IFungibleFractionsOperations.mint.selector, abi.encode(to, erc1155ID, amount));
231 } else if (to == address(0)) {
232 dataIndex.write(address(fungibleFractionsDO), _datapoint, IFungibleFractionsOperations.burn.selector, abi.encode(from, erc1155ID, amount));
233 } else {
234 dataIndex.write(
235 address(fungibleFractionsDO),
236 _datapoint,
237 IFungibleFractionsOperations.transferFrom.selector,
238 abi.encode(from, to, erc1155ID, amount)
239 );
240 }
241 IFractionTransferEventEmitter(erc1155dm).fractionTransferredNotify(from, to, amount);
242}

Recommendation:

We advise the code to omit the first two special cases and to directly issue an IDataIndex::write operation with the IFungibleFractionsOperations::transferFrom selector, optimizing the code's gas cost.

Alleviation:

The code was optimized as advised, omitting the inexecutable code from the MinimalisticERC20FractionDataManager::_writeTransfer implementation.