Omniscia Sova Network Audit

GatedMintEscrow Code Style Findings

GatedMintEscrow Code Style Findings

Description:

The GatedMintRWA contract implementation will emit the deposit ID for which a share mint has occurred when the GatedMintRWA::batchMintShares function is invoked, however, the GatedMintRWA::mintShares function does not emit it thereby preventing off-chain software from properly correlating all mint operations with their respective deposit IDs.

Example:

src/strategy/GatedMintEscrow.sol
140/**
141 * @notice Accept a pending deposit
142 * @param depositId The deposit ID to accept
143 */
144function acceptDeposit(bytes32 depositId) external {
145 // Only strategy can call this function
146 if (msg.sender != strategy) revert Unauthorized();
147
148 PendingDeposit storage deposit = pendingDeposits[depositId];
149 if (deposit.depositor == address(0)) revert DepositNotFound();
150 if (deposit.state != DepositState.PENDING) revert DepositNotPending();
151
152 // Mark as accepted
153 deposit.state = DepositState.ACCEPTED;
154
155 // Update accounting
156 totalPendingAssets -= deposit.assetAmount;
157 userPendingAssets[deposit.depositor] -= deposit.assetAmount;
158
159 // Increment the round number, even for a single deposit
160 currentRound++;
161
162 // Transfer assets to the strategy
163 SafeTransferLib.safeTransfer(asset, strategy, deposit.assetAmount);
164
165 // Tell the GatedMintRWA token to mint shares
166 GatedMintRWA(token).mintShares(deposit.recipient, deposit.assetAmount);
167
168 emit DepositAccepted(depositId, deposit.recipient, deposit.assetAmount);
169}
170
171/**
172 * @notice Accept multiple pending deposits as a batch with equal share accounting
173 * @param depositIds Array of deposit IDs to accept
174 */
175function batchAcceptDeposits(bytes32[] calldata depositIds) external {
176 // Only strategy can call this function
177 if (msg.sender != strategy) revert Unauthorized();
178
179 // Skip empty arrays
180 if (depositIds.length == 0) return;
181
182 uint256 totalBatchAssets = 0;
183
184 address[] memory recipients = new address[](depositIds.length);
185 uint256[] memory assetAmounts = new uint256[](depositIds.length);
186
187 // First pass: validate all deposits and collect information
188 for (uint256 i = 0; i < depositIds.length;) {
189 bytes32 depositId = depositIds[i];
190 PendingDeposit storage deposit = pendingDeposits[depositId];
191
192 // Validate deposit
193 if (deposit.depositor == address(0)) revert DepositNotFound();
194 if (deposit.state != DepositState.PENDING) revert DepositNotPending();
195
196 // Mark as accepted
197 deposit.state = DepositState.ACCEPTED;
198
199 // Accumulate total assets and store recipient and asset amount
200 totalBatchAssets += deposit.assetAmount;
201 userPendingAssets[deposit.depositor] -= deposit.assetAmount;
202
203 recipients[i] = deposit.recipient;
204 assetAmounts[i] = deposit.assetAmount;
205
206 unchecked {
207 ++i;
208 }
209 }
210
211 totalPendingAssets -= totalBatchAssets;
212
213 // Increment the round number
214 currentRound++;
215
216 // Transfer all assets to the strategy in one transaction
217 SafeTransferLib.safeTransfer(asset, strategy, totalBatchAssets);
218
219 // Tell the GatedMintRWA token to mint shares for all deposits with equal treatment
220 GatedMintRWA(token).batchMintShares(depositIds, recipients, assetAmounts, totalBatchAssets);
221
222 emit BatchDepositsAccepted(depositIds, totalBatchAssets);
223}

Recommendation:

We advise the GatedMintRWA::mintShares function to also accept a deposit ID argument, ensuring consistency in the data emitted by the GatedMintRWA contract.

Alleviation (e4d6885477438291b0d3ca477fab7e088522967b):

The deposit IDs were removed from the batch mint operation instead, ensuring that the two integration points are consistent and thus addressing this exhibit.

GME-02C: Non-Standard Library Usage

TypeSeverityLocation
Code StyleGatedMintEscrow.sol:
I-1: L163
I-2: L217
I-3: L245
I-4: L278
I-5: L316

Description:

In contrast to the rest of the codebase, the GatedMintEscrow contract will invoke the relevant SafeTransferLib functions directly from the library rather than exposing them through the address data type.

Example:

src/strategy/GatedMintEscrow.sol
163SafeTransferLib.safeTransfer(asset, strategy, deposit.assetAmount);

Recommendation:

We advise the library to be used in a using statement properly and the code to invoke the functions as exposed from the native types, optimizing the code's legibility.

Alleviation (e4d6885477438291b0d3ca477fab7e088522967b):

All instances of SafeTransferLib library usage have been replaced with proper syntax as exposed by the address type directly, addressing this exhibit.