Omniscia Avant Protocol Audit
AvUSDMinting Code Style Findings
AvUSDMinting Code Style Findings
AUM-01C: Inefficient Event Emissions
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L534-L536, L541-L543 |
Description:
The referenced event emissions are inefficient as they will temporarily store a value to memory and will additionally read the new value via storage.
Example:
532/// @notice Sets the max mintPerBlock limit533function _setMaxMintPerBlock(uint256 _maxMintPerBlock) internal {534 uint256 oldMaxMintPerBlock = maxMintPerBlock;535 maxMintPerBlock = _maxMintPerBlock;536 emit MaxMintPerBlockChanged(oldMaxMintPerBlock, maxMintPerBlock);537}538
539/// @notice Sets the max redeemPerBlock limit540function _setMaxRedeemPerBlock(uint256 _maxRedeemPerBlock) internal {541 uint256 oldMaxRedeemPerBlock = maxRedeemPerBlock;542 maxRedeemPerBlock = _maxRedeemPerBlock;543 emit MaxRedeemPerBlockChanged(oldMaxRedeemPerBlock, maxRedeemPerBlock);544}
Recommendation:
We advise the events to be emitted prior to each storage assignment, permitting the storage value to be used directly in the event's emission and the input argument to be used as the second value in an optimal way.
Alleviation:
The event emissions have been optimized per our recommendation thus minimizing the number of storage reads they perform.
AUM-02C: Inefficient Remainder Transfer
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L487-L498, L515-L529 |
Description:
The AvUSDMinting::_transferCollateral
and AvUSDMinting::_transferEthCollateral
functions will inefficiently perform two transfers to allocate any remainder to the last address
entry of the addresses
list.
Example:
475/// @notice transfer supported asset to array of custody addresses per defined ratio476function _transferCollateral(477 uint256 amount,478 address asset,479 address benefactor,480 address[] calldata addresses,481 uint256[] calldata ratios482) internal {483 // cannot mint using unsupported asset or native ETH even if it is supported for redemptions484 if (!_supportedAssets.contains(asset) || asset == NATIVE_TOKEN) revert UnsupportedAsset();485 IERC20 token = IERC20(asset);486 uint256 totalTransferred = 0;487 for (uint256 i = 0; i < addresses.length;) {488 uint256 amountToTransfer = (amount * ratios[i]) / ROUTE_REQUIRED_RATIO;489 token.safeTransferFrom(benefactor, addresses[i], amountToTransfer);490 totalTransferred += amountToTransfer;491 unchecked {492 ++i;493 }494 }495 uint256 remainingBalance = amount - totalTransferred;496 if (remainingBalance > 0) {497 token.safeTransferFrom(benefactor, addresses[addresses.length - 1], remainingBalance);498 }499}
Recommendation:
We advise each for
loop to iterate until addresses.length - 1
, and the remainder to be directly transferred to the last address
directly.
Alleviation:
The last member disbursement flow has been optimized per our recommendation ensuring that a single transfer is performed.
AUM-03C: Inefficient Split of Logic
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L443-L451, L456-L459 |
Description:
The AvUSDMinting::_deduplicateOrder
function will inefficiently invoke the AvUSDMinting::verifyNonce
function and use its return arguments to affect the _orderBitmaps
data entry.
Example:
442/// @notice verify validity of nonce by checking its presence443function verifyNonce(address sender, uint256 nonce) public view override returns (uint256, uint256, uint256) {444 if (nonce == 0) revert InvalidNonce();445 uint256 invalidatorSlot = uint64(nonce) >> 8;446 uint256 invalidatorBit = 1 << uint8(nonce);447 uint256 invalidator = _orderBitmaps[sender][invalidatorSlot];448 if (invalidator & invalidatorBit != 0) revert InvalidNonce();449
450 return (invalidatorSlot, invalidator, invalidatorBit);451}452
453/* --------------- PRIVATE --------------- */454
455/// @notice deduplication of taker order456function _deduplicateOrder(address sender, uint256 nonce) private {457 (uint256 invalidatorSlot, uint256 invalidator, uint256 invalidatorBit) = verifyNonce(sender, nonce);458 _orderBitmaps[sender][invalidatorSlot] = invalidator | invalidatorBit;459}
Recommendation:
We advise the AvUSDMinting::_deduplicateOrder
function to directly verify and mutate the _orderBitmaps
data entry, permitting the _orderBitmaps
mapping to be looked up once efficiently.
Alleviation:
The Avant Protocol team evaluated the gas optimization that results from this exhibit and opted to acknowledge it in favour of code readability.
AUM-04C: Inefficient mapping
Lookups
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L291, L294 |
Description:
The linked statements perform key-based lookup operations on mapping
declarations from storage multiple times for the same key redundantly.
Example:
290function confirmDelegatedSigner(address _delegatedBy) external {291 if (delegatedSigner[msg.sender][_delegatedBy] != DelegatedSignerStatus.PENDING) {292 revert DelegationNotInitiated();293 }294 delegatedSigner[msg.sender][_delegatedBy] = DelegatedSignerStatus.ACCEPTED;295 emit DelegatedSignerAdded(msg.sender, _delegatedBy);296}
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:
All referenced inefficient mapping
lookups have been optimized to the greatest extent possible, significantly reducing the gas cost of the functions the statements were located in.
AUM-05C: Non-Standard Modifier Structure
Type | Severity | Location |
---|---|---|
Code Style | AvUSDMinting.sol:L89, L91, L106, L113, L191, L222, L254 |
Description:
The AvUSDMinting::belowMaxMintPerBlock
and AvUSDMinting::belowMaxRedeemPerBlock
modifiers are non-standard in the sense that their control variables are incremented outside their code block.
Example:
238/**239 * @notice Redeem stablecoins for assets240 * @param order struct containing order details and confirmation from server241 * @param signature signature of the taker242 */243function redeem(Order calldata order, Signature calldata signature)244 external245 override246 nonReentrant247 onlyRole(REDEEMER_ROLE)248 belowMaxRedeemPerBlock(order.avusd_amount)249{250 if (order.order_type != OrderType.REDEEM) revert InvalidOrder();251 verifyOrder(order, signature);252 _deduplicateOrder(order.benefactor, order.nonce);253 // Add to the redeemed amount in this block254 redeemedPerBlock[block.number] += order.avusd_amount;255 avusd.burnFrom(order.benefactor, order.avusd_amount);256 _transferToBeneficiary(order.beneficiary, order.collateral_asset, order.collateral_amount);257 emit Redeem(258 msg.sender,259 order.benefactor,260 order.beneficiary,261 order.collateral_asset,262 order.collateral_amount,263 order.avusd_amount264 );265}
Recommendation:
We advise the mintedPerBlock
and redeemedPerBlock
increment statements to be relocated to each modifier's body, ensuring that the variables are incremented right after being evaluated.
Alleviation:
The modifier
declarations have been adjusted as advised ensuring that they adhere to the CEI pattern and are self-contained.
AUM-06C: Non-Standard Negation of Conditional
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L411 |
Description:
The referenced statement will negate a complex conditional clause involving two equalities and an OR (||
) clause.
Example:
411if (!(signer == order.benefactor || delegatedSigner[signer][order.benefactor] == DelegatedSignerStatus.ACCEPTED)) {
Recommendation:
We advise the negation to be incorporated to each conditional directly, optimizing the statement's gas cost as well as legibility.
Alleviation:
The condition's negation has been properly incorporated within its inner comparators addressing this exhibit.
AUM-07C: Potentially Improper Role Assignment
Type | Severity | Location |
---|---|---|
Standard Conformity | AvUSDMinting.sol:L135, L156 |
Description:
The AvUSDMinting::constructor
will assign the DEFAULT_ADMIN_ROLE
to both the msg.sender
and the _admin
if the _admin
does not match the msg.sender
which will result in an inefficient transfer of administrator-ship based on the SingleAdminAccessControl::_grantRole
function.
Example:
135_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);136
137for (uint256 i = 0; i < _assets.length;) {138 addSupportedAsset(_assets[i]);139 unchecked {140 ++i;141 }142}143
144for (uint256 j = 0; j < _custodians.length;) {145 addCustodianAddress(_custodians[j]);146 unchecked {147 ++j;148 }149}150
151// Set the max mint/redeem limits per block152_setMaxMintPerBlock(_maxMintPerBlock);153_setMaxRedeemPerBlock(_maxRedeemPerBlock);154
155if (msg.sender != _admin) {156 _grantRole(DEFAULT_ADMIN_ROLE, _admin);157}
Recommendation:
We advise the role assignment to be performed once towards the correct role, optimizing the events emitted by the contract.
Alleviation:
The Avant Protocol team evaluated this exhibit and acknowledged that the initialization process could be made more efficient, however, they wish to retain the current implementation in place as the cost involved is incurred once.
AUM-08C: Redundant Condition
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L509 |
Description:
The AvUSDMinting::_transferEthCollateral
function will inefficiently evaluate whether the asset
is equal to the NATIVE_TOKEN
and not equal to the WAVAX
token, the latter implying that the code should fail if the asset
is any value other than the WAVAX
token.
Example:
509if (!_supportedAssets.contains(asset) || asset == NATIVE_TOKEN || asset != address(WAVAX)) revert UnsupportedAsset();
Recommendation:
We advise the former conditional to be omitted, optimizing the code's gas cost.
Alleviation:
The redundant part of the conditional has been omitted, optimizing its gas cost.
AUM-09C: Redundant Duplication of Code
Type | Severity | Location |
---|---|---|
Gas Optimization | AvUSDMinting.sol:L186-L203, L217-L235 |
Description:
The AvUSDMinting::mint
and AvUSDMinting::mintWAVAX
functions differentiate between them via a single statement.
Example:
174/**175 * @notice Mint stablecoins from assets176 * @param order struct containing order details and confirmation from server177 * @param signature signature of the taker178 */179function mint(Order calldata order, Route calldata route, Signature calldata signature)180 external181 override182 nonReentrant183 onlyRole(MINTER_ROLE)184 belowMaxMintPerBlock(order.avusd_amount)185{186 if (order.order_type != OrderType.MINT) revert InvalidOrder();187 verifyOrder(order, signature);188 if (!verifyRoute(route)) revert InvalidRoute();189 _deduplicateOrder(order.benefactor, order.nonce);190 // Add to the minted amount in this block191 mintedPerBlock[block.number] += order.avusd_amount;192 _transferCollateral(193 order.collateral_amount, order.collateral_asset, order.benefactor, route.addresses, route.ratios194 );195 avusd.mint(order.beneficiary, order.avusd_amount);196 emit Mint(197 msg.sender,198 order.benefactor,199 order.beneficiary,200 order.collateral_asset,201 order.collateral_amount,202 order.avusd_amount203 );204}205
206/**207 * @notice Mint stablecoins from assets208 * @param order struct containing order details and confirmation from server209 * @param signature signature of the taker210 */211function mintWAVAX(Order calldata order, Route calldata route, Signature calldata signature)212 external213 nonReentrant214 onlyRole(MINTER_ROLE)215 belowMaxMintPerBlock(order.avusd_amount)216{217 if (order.order_type != OrderType.MINT) revert InvalidOrder();218 verifyOrder(order, signature);219 if (!verifyRoute(route)) revert InvalidRoute();220 _deduplicateOrder(order.benefactor, order.nonce);221 // Add to the minted amount in this block222 mintedPerBlock[block.number] += order.avusd_amount;223 // Checks that the collateral asset is WAVAX also224 _transferEthCollateral(225 order.collateral_amount, order.collateral_asset, order.benefactor, route.addresses, route.ratios226 );227 avusd.mint(order.beneficiary, order.avusd_amount);228 emit Mint(229 msg.sender,230 order.benefactor,231 order.beneficiary,232 order.collateral_asset,233 order.collateral_amount,234 order.avusd_amount235 );236}
Recommendation:
We advise the function statements to be relocated to a modifier that executes statements before and after the function's body, permitting the same modifier to be used across both functions.
Alternatively, we advise the same approach to be adopted albeit via internal functions that are invoked before and after.
Alleviation:
The functions have been merged into a single AvUSDMinting::mint
function that differentiates between the previous implementations by evaluating whether the order.collateral_asset
is WAVAX
or not.
AUM-10C: Redundant Parenthesis Statement
Type | Severity | Location |
---|---|---|
Code Style | AvUSDMinting.sol:L439 |
Description:
The referenced statement is redundantly wrapped in parenthesis (()
).
Example:
439return (totalRatio == ROUTE_REQUIRED_RATIO);
Recommendation:
We advise them to be safely omitted, increasing the legibility of the codebase.
Alleviation:
The redundant parenthesis in the referenced statement have been safely omitted.