Guardian's review of Contract Updates for Aria, published October 2025. The report records 7 findings across 2 review rounds, including 1 high and 3 medium.
- Published
- Review window
- September 17 to October 1, 2025
- Rounds
- Main Review, Remediation Review
- Language
- Solidity
- Chains
- Story, BNB Chain
- Sector
- Real-world assets
- 0 Critical
- 1 High
- 3 Medium
- 1 Low
- 2 Informational
Scope
Findings 7
Main Review
4 findings · September 17 to 18, 2025-
H-01 High Rewards Included In “Total Staked” Rewards Resolved
Description
The contract currently calculates total staked based on the contract’s IPRWA balance. When royalties are deposited, they inflate the staking balance, effectively treating rewards as staked funds. This causes overestimation of staking size and dilution of later rewards.
Recommendation
Maintain a dedicated
totalStakedstate variable updated only during stake/unstake actions. -
M-01 Medium Distribution Order In Unstake Rewards Resolved
Description
In the current design, the
distributefunction is called after the unstake action. This means users receive IPRWA based on the previous snapshot, potentially under-allocating yield since they were contributing until the point of unstake. While this was intended to avoid gaming the system, it still leaves gaps: for example, a user can repeatedly stake as little as 1 wei to trigger distribution first and then immediately unstake, gaining advantage from timing.Recommendation
Update distributions before calculating shares-to-assets conversion in the unstake function. This ensures users are attributed yield for the full duration of their stake and avoids unnecessary edge cases where minimal stakes can influence the distribution order.
-
M-02 Medium Randomized Distribution Via Probability Rewards Resolved
Description
The protocol introduces randomness (interval + delta) to determine whether distributions occur. While the intent is to mitigate MEV at boundaries, this randomness can be bypassed by wrapper contracts that deterministically call stake only when distribution would be skipped.
Randomness could also result in one set of users not getting attributed to their yield, while one set is.
It also requires consideration of
forceDistributeby admin.Hence, the probabilistic design increases complexity without clear security gains.
Recommendation
Consider streaming royalties proportionally based on elapsed time since the last distribution. For example, distribute
(elapsed / year) * yearlyDistribution. This approach:- Eliminates exploitable randomness,
- Reduces unnecessary complexity,
- Ensures smooth, continuous distribution,
- Aligns directly with stake/unstake actions already in place.
-
L-01 Low Missing Parent Initializers Resolved
Description
The
initialize()function ofRoyaltiesHookonly calls__ReentrancyGuard_init(), but does not invoke__AccessControl_init()or__UUPSUpgradeable_init().Currently, these functions are empty in the OpenZeppelin 0.8.x implementation, so the contract will work as expected. However, skipping them breaks the standard upgradeable-contract initialization pattern and may cause problems if OpenZeppelin adds initialization logic in future versions or if multiple inheritance chains rely on proper linearization.
Recommendation
Call all inherited initializer functions inside
initialize()to preserve upgrade-safe patterns and maintain forward compatibility:__AccessControl_init(); __ReentrancyGuard_init(); __UUPSUpgradeable_init();
Remediation Review
3 findings · October 1, 2025-
M-01 Medium Incorrect Consideration of
avgAPRLogical Error ResolvedDescription
The current comment suggests that
avgAPRis used to calculate the total amount of royalties to be distributed.avgAPR is used to calculate the total amount of royalties to be distributed, whereasThis is incorrect.
APRrepresents the intended annual reward rate (e.g., 10%).avgAPRrepresents how much has already been rewarded (but not yet claimed), relative to the staked balance.
For example:
- Initial stake = 1000 tokens, APR = 10%.
- Expected yearly rewards = 100 tokens.
- After one month: 100 ÷ 12 = 8.33 tokens accrued.
avgAPRat this point = (8.33 ÷ 1000) × 100% ≈ 0.833%.
If a user claims rewards,
avgAPRmust decrease accordingly. Therefore,avgAPRmust always be adjusted as:avgAPR= (rewards accrued − claimed)/stake ×100%Recommendation
Calculate
avgAPRconsidering above and ensure thatavgAPRis consistently adjusted after claims, so it accurately reflects outstanding (unclaimed) rewards.Also consider correcting comment in contract to reflect actual nature of
avgAPR. -
I-01 Informational Direct Donations to Staking Contract Warning Resolved
Description
Direct donations increase
stakedAndRewardsbut notapproxRewards. While normally unattractive (since donors effectively give value to others), this creates a potential exploit during the vault’s initial phase.Attack Scenario
- A malicious actor deposits 1 wei as the first stake.
- They wait for time to pass.
- Instead of staking again (which updates
lastDistributionTime), they donate directly. - The system incorrectly treats this donation as if it had been staked since the beginning, releasing more rewards than intended.
If the first few depositors collude, they could inflate rewards early on at minimal cost.
Recommendation
On redeployment or when vault doesn't have much deposits, consider making the first substantial deposit to prevent exploitation by early depositors.
-
I-02 Informational Missing Admin Functions in IRoyaltiesHookAdmin Best Practices Resolved
Description
The
IRoyaltiesHookAdmininterface currently lacks functions for updating key parameters:APR(the target annual reward rate)avgAPR(the effective reward rate already accrued but not yet claimed)
Recommendation
Consider extending
IRoyaltiesHookAdminto include:setAPR(uint256 newAPR)setAvgAPR(uint256 newAvgAPR)
No findings match.
More from Aria
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
