Omniscia Sova Network Audit
GatedMintRWA Code Style Findings
GatedMintRWA Code Style Findings
GMR-01C: Inefficient Array Resize Operation
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | GatedMintRWA.sol:L246-L254 |
Description:
The referenced code block will copy all identified deposits of the user to a new array so as to resize it.
Example:
224function getUserPendingDeposits(address user) external view returns (bytes32[] memory) {225 bytes32[] memory userDeposits = new bytes32[](userDepositIds[user].length);226 uint256 count = 0;227
228 for (uint256 i = 0; i < userDepositIds[user].length;) {229 bytes32 depositId = userDepositIds[user][i];230
231 // Query the escrow for deposit status232 (,,,, uint8 state) = getDepositDetails(depositId);233
234 // Only include if state is PENDING (0)235 if (state == 0) {236 // 0 = PENDING in the DepositState enum237 userDeposits[count] = depositId;238 count++;239 }240
241 unchecked {242 ++i;243 }244 }245
246 // Resize array to fit only pending deposits247 bytes32[] memory result = new bytes32[](count);248 for (uint256 i = 0; i < count;) {249 result[i] = userDeposits[i];250
251 unchecked {252 ++i;253 }254 }255
256 return result;257}Recommendation:
We advise a low-level assembly mutation to be performed on the userDeposits array directly, assigning its length to the identified deposit count and optimizing the code's gas cost significantly.
Alleviation (e4d6885477438291b0d3ca477fab7e088522967b):
The referenced loop has been replaced by an in-place memory array resize operation as advised, significantly reducing the code's gas cost.
GMR-02C: Inefficient mapping Lookups
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | GatedMintRWA.sol:L228, L229 |
Description:
The linked statements perform key-based lookup operations on mapping declarations from storage multiple times for the same key redundantly.
Example:
228for (uint256 i = 0; i < userDepositIds[user].length;) {229 bytes32 depositId = userDepositIds[user][i];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 (e4d6885477438291b0d3ca477fab7e088522967b):
The code was updated to load all deposit IDs of a user in memory, optimizing the code's gas cost significantly.
GMR-03C: Redundant Casting of Enum
| Type | Severity | Location |
|---|---|---|
| Code Style | ![]() | GatedMintRWA.sol:L232, L235, L274 |
Description:
The GatedMintRWA::getUserPendingDeposits will utilize a hardcoded 0 value to validate an enum's state which significantly hurts the code's legibility.
Example:
224function getUserPendingDeposits(address user) external view returns (bytes32[] memory) {225 bytes32[] memory userDeposits = new bytes32[](userDepositIds[user].length);226 uint256 count = 0;227
228 for (uint256 i = 0; i < userDepositIds[user].length;) {229 bytes32 depositId = userDepositIds[user][i];230
231 // Query the escrow for deposit status232 (,,,, uint8 state) = getDepositDetails(depositId);233
234 // Only include if state is PENDING (0)235 if (state == 0) {236 // 0 = PENDING in the DepositState enum237 userDeposits[count] = depositId;238 count++;239 }240
241 unchecked {242 ++i;243 }244 }245
246 // Resize array to fit only pending deposits247 bytes32[] memory result = new bytes32[](count);248 for (uint256 i = 0; i < count;) {249 result[i] = userDeposits[i];250
251 unchecked {252 ++i;253 }254 }255
256 return result;257}258
259/**260 * @notice Get details for a specific deposit (from Escrow)261 * @param depositId The unique identifier of the deposit262 * @return depositor The address that initiated the deposit263 * @return recipient The address that will receive shares if approved264 * @return assetAmount The amount of assets deposited265 * @return expirationTime The timestamp after which deposit can be reclaimed266 * @return state The current state of the deposit (0=PENDING, 1=ACCEPTED, 2=REFUNDED)267 */268function getDepositDetails(bytes32 depositId)269 public270 view271 returns (address depositor, address recipient, uint256 assetAmount, uint256 expirationTime, uint8 state)272{273 GatedMintEscrow.PendingDeposit memory deposit = GatedMintEscrow(escrow).getPendingDeposit(depositId);274 return (deposit.depositor, deposit.recipient, deposit.assetAmount, deposit.expirationTime, uint8(deposit.state));275}Recommendation:
We advise the code to either cast the desired enum state to the uint8 data type in the comparison within the GatedMintRWA::getUserPendingDeposits function, or to remove the casting operation from the GatedMintRWA::getDepositDetails function.
Alleviation (e4d6885477438291b0d3ca477fab7e088522967b):
The code was revised to yield and utilize an enum value directly, optimizing the code's legibility.
