Omniscia Nexera Audit
MinimalisticERC20FractionDataManager Code Style Findings
MinimalisticERC20FractionDataManager Code Style Findings
MER-01C: Inefficient mapping
Lookups
Type | Severity | Location |
---|---|---|
Gas Optimization | ![]() | MinimalisticERC20FractionDataManager.sol:L216, L222 |
Description:
The linked statements perform key-based lookup operations on mapping
declarations from storage multiple times for the same key redundantly.
Example:
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
Type | Severity | Location |
---|---|---|
Gas Optimization | ![]() | MinimalisticERC20FractionDataManager.sol:L229-L233 |
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:
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.