Audit · firm’s term: audit report
Resonance Security reviewed USPD on 4 Aug 2025: 22 findings, 1 critical; 17 marked fixed, 5 open.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 1 |
| High | 0 | 0 | 0 |
| Medium | 0 | 0 | 3 |
| Low | 5 | 0 | 13 |
The yieldFactor is being used for minting shares, establishing prices and liquidate positions. This factor is obtained using the formulauint256 currentBalance = IERC20(stETH).balanceOf(address(this));which can be manipulated by users by sending stETH to the contract. Doing this increases the yield factor and may affect different parts of the protocol. In the mentioned PoC, a user that should not be able to liquidate, liquidates a position and obtain profit of it.
The attestationService() function verifies the signer of the price and then checks the staleness and deviation. If new priceQueries are generated in the same time frame, the older prices are not blocked and may be still used by users to obtain the best conversion rate instead of the actual latest price.
During price validation, only priceStalenessPeriod is being used to check if a price is stale or not. If this value is high enough, prices obtained from other oracles may be stale and the protocol will still accept them.
The StabilizerEscrow contract is implementing a receive() function. This indicates that this contract can successfully receive a native balance transfer. However, the fact that this receive() implementation is actually empty makes such transfers be out-of-flow. Such transfers can lead to vulnerabilities related to the accounting in the extreme cases. However, they also can cause an inherent loss for any legitimate user who was tricked or made a genuine mistake. Such transfers will not be accounted for accordingly by the contracts so users will lose their native tokens without being able to use the protocol.
Chainlink's latestRoundData function returns the latest price information from a specific oracle feed. However, not validating these prices could be dangerous, particularly if they are used in further arithmetic operations. If the price is 0 or negative, and it is involved in calculations with unsigned integers (uint), it can cause underflows. Underflows with uint can cause the value to wrap around to a very large number (due to uint's inability to represent negative numbers), resulting in unexpected and potentially catastrophic outcomes in the contract logic.
The project uses floating pragmas ^0.8.20.This may result in the contracts being deployed using the wrong pragma version, which is different from the one they were tested with. For example, they might be deployed using an outdated pragma version which may include bugs that affect the system negatively.
The function attestationService() does not validate the input variable priceQuery.assetPair against the expected asset pair for correct oracle functioning, ETH/USDC. Under specific circumstances where the signature may be forged, without proper validations of the assetPair variable, the function attestationService() may run successfully while all implemented validations are bypassed, possibly resulting in incorrect oracle price matching.
The functions transfer() and transferFrom() do not validate the input parameter uspdAmount against the Zero Value, allowing for users to set this parameter as 0 and waste gas on unnecessary transactions.
There is no upper or lower limits when setting maxPriceDeviation or priceStalenessPeriod. This may cause a bad user experience and protocol usage in price changes if they are not set between reasonable values.
The function attestationService() makes use of code that is already implemented within OpenZeppelin's library PausableUpgradeable, therefore resulting in the use of redundant code that may increase gas costs, as well as deteriorate code readability and composability,
The function mint() indirectly performs an arbitrary external call through ERC721's _safeMint(), and does not follow the Checks-Effects-Interactions pattern nor does it implement verification mechanisms against reentrancy, such as OpenZeppelin's ReentrancyGuard.While it does not present an immediate security threat as it is, when further functionality is introduced, possible reentrancy scenarios may occur that may ultimately lead to financial loss on the protocol.
During a slashing or catastrophic validator failure from Lido on Ethereum, the total pooled ETH may decrease due to penalties and everyone's stETH balance decreases proportionally. While each staker's share count remains the same, the value backing each share is lower.The function updateL2YieldFactor() is used to update bridged tokens yield factor. This function however, does not allow the newYieldFactor to be less than the current yield factor, which means that it does not account for slashing occurrences.While these occurrences are extremely unlikely, they are possible, and may ultimately result in loss of funds to the protocol or its users.
In the Solidity programming language, all variables are automatically initialized to a default value corresponding to their type when they are declared. For example, integer types are initialized to 0, boolean types to false, and address types to 0x0000000000000000000000000000000000000000. Explicitly initializing variables to these default values when they are declared is therefore redundant, and since each operation in a contract costs gas, it results in unnecessary gas costs. This could potentially impact the contract's efficiency and the cost of executing its functions.Several instances of this issue are found across the code base.
The following functions were found to be unused within the system:addCollateral()Unused functions increase the complexity and readability of the smart contract's code and their inclusion should be discouraged whenever possible.
The following variables were found to be unused within the system:maxDeviationPercentageUnused variables increase the complexity and readability of the smart contract's code and their inclusion should be discouraged whenever possible.
The smart contract makes use of hardcoded addresses. This development practice should be avoided. Hardcoded addresses should be declared as immutable instead, and assigned via constructor arguments. This allows the code to remain the same across deployments on different networks. This flexibility is also especially important when dealing with contracts that need to interact with multiple external contracts or when the address of an external contract needs to change.
The smart contracts make use of the outdated Ether transfer() function.In Solidity, send, transfer, and call are methods for transferring Ether, each with distinct characteristics and use cases. While send and transfer were commonly used in the past, call has become the preferred method due to its versatility, dynamic gas handling, and adaptability to Ethereum's evolving network conditions. While call is vulnerable to reentrancy attacks, it can be easily mitigated by employing the "Checks-Effects-Interactions" development pattern and making use of reentrancy guard modifiers.
The report names a repository or files. Open the report to see its scope.
18 projects reviewed · 25 reports · latest Aug 2025
See Resonance Security’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us