Part of Quantstamp’s retainer series
Quantstamp reviewed Nayms 2024 (Retainer) on 3 May 2024: 33 findings, 0 critical; 18 marked fixed, 11 acknowledged, 4 open.
Report hosted on certificate.quantstamp.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 4 |
| Medium | 2 | 0 | 2 |
| Low | 2 | 11 | 12 |
The system assumes that the total supply of supported rebase tokens will not decrease. This can have two main consequences: 1. Since the internal accounting is expected to mimic the external price, we can have at a particular moment some price gaps between the external market price of a token and its internal representation, in both directions (over priced, or under priced). Since price alignment can only happen once a user calls one of the two update functions (`distributeAccruedInterest()` or `rebaseERC20()`), we may have situations when there is a delay in triggering a price update function. This could leave the system exposed to arbitrage or incorrect internal prices. Also, some rebasing tokens can be upgraded or hacked, so it cannot be 100% sure that the supply of a given rebasing token cannot decrease with time. 2. In `LibTokenizedVault`, an underflow is possible in the function `_accruedInterest()`, if `total < depositTotal`. In this case, reflecting the rebasing of the supported token will not be possible until the condition returns `true` again.
In `LibACL`, a counter variable `sysAdminCount` makes sure that we cannot have less than one address with the role `sysAdmin`. However, this can happen in two situations: * if Bob is the only `sysAdmin` (`sysAdminCount = 1`), he grants the role to himself (`sysAdminCount = 2`), he revokes himself (`sysAdminCount = 1`) => no more valid address with the role `sysAdmin`; * if Bob is the only `sysAdmin` (`sysAdminCount = 1`), he grants to a random address owned by no one (`sysAdminCount = 2`), he revokes himself (`sysAdminCount = 1`) => no more valid address with the role `sysAdmin`; Even if the event is unlikely since only an address with the role `sysAdmin` can perform these operations, the impact would be high. **Note:** this issue is close to the issue #6 of the previous audit (link [here](https://certificate.quantstamp.com/full/nayms-2/79da5daf-4326-4f12-a71c-828b72fbea09/index.html)).
When starting a token sale, the sold token amount may be less than the corresponding entities token `objectMinimumSell` amount, which was set during `EntityFacet.enableEntityTokenization()`, resulting in the created offer to be automatically in the `LibConstants.OFFER_STATE_FULFILLED` state. Similarly, the `buyAmount` is not verified to be greater than `objectMinimumSell` of the corresponding `entity.assetId`. Also, offers can be created for not-yet-tokenized entity p-tokens. Leading to their automatic fulfillment on creation.
It is important to validate inputs, even if they only come from trusted addresses, to avoid human error: 1. In the function `LibFeeRouter._addFeeSchedule()`: </br>a. qs-badge-fixed the fact that `_entityId` matches an existing entity is not checked; </br>b. qs-badge-fixed there is no min and max check for the length of the `_receivers`, which could lead to a denial of service; 2. `LibAdmin._approveSelfOnboarding()`: </br>a. qs-badge-fixed `_entityId` can be `address(0)`, the approval will be recorded, but the user will not be able to `onboard()`; </br>b. qs-badge-fixed it is possible to repeat the steps 1) `approveSelfOnboarding()` and 2) `onboard()` for the same user but multiple entities; 3. In `LibMarket._setMinimumSell()`, no check makes sure that `_objectId` represents an existing object. 4. In `LibEntity._validateSimplePolicyCreation()`, no check makes sure that: </br>a. qs-badge-fixed the maturation date is too far in the future; </br>b. qs-badge-fixed the number of policy-level commission receivers is not too high, which could lead to out-of-gas errors when trying to pay the commissions; 5. qs-badge-fixed In the function `LibTokenizedVaultStaking._initStaking()`, no check makes sure that `_entityId` represents an existing entity; 6. In the function `LibTokenizedVaultStaking._payReward()`, p-tokens can be transferred as rewards, instead of only limiting to supported external tokens. 7. qs-badge-fixed In the function `LibTokenizedVaultStaking._validateStakingParams()`, the value of `initDate` can be in a very long time from the current time. 8. qs-badge-fixed In the function `LibTokenizedVaultStaking._validateStakingParams()`, there is no min or max check for the value of `interval`. 9. qs-badge-fixed In the function `LibTokenizedVaultStaking._stake()`, the `_amount` can be `0`, `_stakerId` can be `0` if `msg.sender` has no parent, and `_entityId` can be a non-existent entity. 10. qs-badge-fixed In the function `LibTokenizedVaultStaking._validateStakingParams()`, the following check is insufficient to ensure that the boost multiplier asymptotically converges to 2, which is only true when `_config.a + _config.r == _config.divider`: ``` if (_config.a + _config.r > _config.divider) revert APlusRCannotBeGreaterThanDivider(); ``` 11. In the function `LibTokenizedVaultStaking._validateStakingParams()`, the value of `tokenId` is not checked. In particular, the staking token ID may be: * qs-badge-fixed the zero/empty object ID; * the p-token of another entity; * an external token ID; * the ID of a wrapped internal token; * any other arbitrary non-object ID;
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (245)
236 projects reviewed · 332 reports · latest Oct 2026
See Quantstamp’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