This is the top-ranked automated findings report, from Tera bot. All findings in this report will be considered known issues for the purposes of your C4 audit.
Issue | Instances | |
---|---|---|
[Mβ01] | The owner is a single point of failure and a centralization risk |
3 |
Total: 3 instances over 1 issues
Issue | Instances | |
---|---|---|
[Lβ01] | Code does not follow the best practice of check-effects-interaction | 2 |
[Lβ02] | Missing checks for address(0) in initializer |
3 |
[Lβ03] | constructor /initialize function lacks parameter validation |
3 |
[Lβ04] | External calls in an un-bounded for -loop may result in a DOS |
12 |
[Lβ05] | Initialization can be front-run | 2 |
[Lβ06] | Large transfers may not work with some ERC20 tokens |
3 |
[Lβ07] | Multiplication on the result of a division | 3 |
[Lβ08] | Loss of precision | 45 |
[Lβ09] | Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions |
1 |
[Lβ10] | State variables not capped at reasonable values | 2 |
[Lβ11] | Consider implementing two-step procedure for updating protocol addresses | 1 |
Total: 77 instances over 11 issues
Issue | Instances | |
---|---|---|
[Nβ01] | Array indices should be referenced via enum s rather than via numeric literals |
1 |
[Nβ02] | Array is push() ed but not pop() ed |
1 |
[Nβ03] | Consider adding a block/deny-list | 2 |
[Nβ04] | State variables should include comments | 1 |
[Nβ05] | Constants in comparisons should appear on the left side | 26 |
[Nβ06] | Contracts containing only utility functions should be made into libraries | 1 |
[Nβ07] | Custom error has no error details | 17 |
[Nβ08] | High cyclomatic complexity | 2 |
[Nβ09] | Consider using delete rather than assigning zero/false to clear values |
12 |
[Nβ10] | Constant redefined elsewhere | 1 |
[Nβ11] | Events should use parameters to convey information | 2 |
[Nβ12] | Enable IR-based code generation | 1 |
[Nβ13] | Events that mark critical parameter changes should contain both the old and the new value | 2 |
[Nβ14] | Events are missing sender information | 13 |
[Nβ15] | Events may be emitted out of order due to reentrancy | 3 |
[Nβ16] | Use of floating pragma | 1 |
[Nβ17] | Consider adding formal verification proofs | 1 |
[Nβ18] | Contracts should have full test coverage | 1 |
[Nβ19] | Contract should expose an interface |
26 |
[Nβ20] | Large or complicated code bases should implement invariant tests | 1 |
[Nβ21] | Variables need not be initialized to zero | 10 |
[Nβ22] | Invalid NatSpec comment style | 75 |
[Nβ23] | Large multiples of ten should use scientific notation (e.g. 1e6) rather than decimal literals (e.g. 1000000 ), for readability |
3 |
[Nβ24] | Large numeric literals should use underscores for readability. | 2 |
[Nβ25] | Use the latest solidity (prior to 0.8.20 if on L2s) for deployment | 7 |
[Nβ26] | Contract does not follow the Solidity style guide's suggested layout ordering | 1 |
[Nβ27] | File is missing NatSpec | 1 |
[Nβ28] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , for readability |
2 |
[Nβ29] | Not using the named return variables anywhere in the function is confusing | 7 |
[Nβ30] | NatSpec: Function declarations should have descriptions | 3 |
[Nβ31] | NatSpec: Contract declarations should have @author annotations |
7 |
[Nβ32] | NatSpec: Contract declarations should have descriptions | 7 |
[Nβ33] | NatSpec: State variable declarations should have descriptions | 1 |
[Nβ34] | NatSpec: Contract declarations should have @title annotations |
7 |
[Nβ35] | else -block not required |
3 |
[Nβ36] | Numeric values having to do with time should use time units for readability | 1 |
[Nβ37] | Polymorphic functions make security audits more time-consuming and error-prone | 1 |
[Nβ38] | Setters should prevent re-setting of the same value | 6 |
[Nβ39] | Redundant return statement in a function with named return variables | 1 |
[Nβ40] | Missing checks for state variable assignments | 5 |
[Nβ41] | Long functions should be refactored into multiple, smaller, functions | 2 |
[Nβ42] | Lines are too long | 11 |
[Nβ43] | Top-level declarations should be separated by at least two lines | 1 |
[Nβ44] | Typos | 7 |
[Nβ45] | Consider bounding input array length | 6 |
[Nβ46] | Non-external /public function names should begin with an underscore |
9 |
[Nβ47] | Non-external /public variable names should begin with an underscore |
4 |
[Nβ48] | Event is not properly indexed |
1 |
[Nβ49] | Unused event definition |
3 |
[Nβ50] | constant s should be defined rather than using magic numbers |
91 |
[Nβ51] | Consider using named mappings | 11 |
[Nβ52] | Vulnerable versions of packages are being used | 3 |
Total: 414 instances over 52 issues
Gas totals use lower bounds of ranges and count two iterations of each for
-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[Gβ01] | Use assembly to check for address(0) |
10 | 60 |
[Gβ02] | Use assembly to emit events, in order to save gas | 15 | 570 |
[Gβ03] | Use assembly to write address/contract type storage values | 9 | 450 |
[Gβ04] | Use assembly to check zero | 25 | 150 |
[Gβ05] | Avoid updating storage when the value hasn't changed | 1 | 800 |
[Gβ06] | State variables should be cached in stack variables rather than re-reading them from storage | 29 | 2813 |
[Gβ07] | Use calldata instead of memory for immutable arguments |
1 | 300 |
[Gβ08] | Avoid contract existence checks by using low level calls | 36 | 3600 |
[Gβ09] | Events should be emitted outside of loops | 1 | 375 |
[Gβ10] | The result of function calls should be cached rather than re-calling the function | 7 | 700 |
[Gβ11] | Operator >= /<= costs less gas than operator > /< |
43 | 129 |
[Gβ12] | internal /private functions only called once can be inlined to save gas |
6 | 180 |
[Gβ13] | <array>.length should not be looked up in every loop of a for -loop |
10 | 30 |
[Gβ14] | Using bool for storage incurs overhead |
1 | 17100 |
[Gβ15] | Multiple accesses of the same mapping/array key/index should be cached | 39 | 1638 |
[Gβ16] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , where appropriate |
10 | - |
[Gβ17] | Nesting if -statements is cheaper than using && |
7 | 42 |
[Gβ18] | Reduce gas usage by moving to Solidity 0.8.19 or later | 7 | - |
[Gβ19] | Constructors can be marked payable |
2 | 42 |
[Gβ20] | <x> += <y> costs more gas than <x> = <x> + <y> for state variables |
2 | 226 |
[Gβ21] | Using private rather than public for constants, saves gas |
4 | 13600 |
[Gβ22] | Use a more recent version of solidity | 1 | - |
[Gβ23] | Simple checks for zero can be done using assembly to save gas | 20 | 120 |
[Gβ24] | Redundant state variable getters | 1 | - |
[Gβ25] | unchecked {} can be used on the division of two uint s in order to save gas |
20 | 400 |
[Gβ26] | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if -statement |
23 | 1955 |
[Gβ27] | Usage of int s/uint s smaller than 32 bytes incurs overhead |
12 | 660 |
[Gβ28] | Remove or replace unused state variables | 30 | - |
[Gβ29] | internal functions not called by the contract should be removed to save deployment gas |
8 | - |
[Gβ30] | Consider activating via-ir for deploying | 7 | - |
[Gβ31] | Use != 0 or == 0 for unsigned integer comparison |
11 | 44 |
Total: 398 instances over 31 issues with 45984 gas saved
Having a single EOA as the only owner of contracts is a large centralization risk and a single point of failure. A single private key may be taken in a hack, or the sole holder of the key may become unable to retrieve the key when necessary. Consider changing to a multi-signature setup, or having a role-based authorization model.
There are 3 instances of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
118 function initializeTokens(address[] calldata tokens_) external onlyOwner {
177 function setPrimeToken(address prime_) external onlyOwner {
216 function sweepToken(IERC20Upgradeable token_, address to_, uint256 amount_) external onlyOwner {
Code should follow the best-practice of check-effects-interaction, where state variables are updated before any external calls are made. Doing so prevents a large class of reentrancy bugs.
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit _primeLiquidityProvider.accrueTokens() is called before these state variables are updated
580 unreleasedPSRIncome[underlying] = totalIncomeUnreleased;
581 unreleasedPLPIncome[underlying] = totalAccruedInPLP;
...
588 markets[vToken].rewardIndex = markets[vToken].rewardIndex + delta;
// @audit IProtocolShareReserve().releaseFunds() is called before this state variable is updated
// @audit IPrimeLiquidityProvider().releaseFunds()) is called before this state variable is updated
688 unreleasedPLPIncome[underlying] = 0;
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit _accessControlManager not checked
130 function initialize(
131 address _xvsVault,
132 address _xvsVaultRewardToken,
133 uint256 _xvsVaultPoolId,
134 uint128 _alphaNumerator,
135 uint128 _alphaDenominator,
136 address _accessControlManager,
137 address _protocolShareReserve,
138 address _primeLiquidityProvider,
139 address _comptroller,
140 address _oracle,
141 uint256 _loopsLimit
142 ) external virtual initializer {
GitHub: [136]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit accessControlManager_ not checked
// @audit tokens_ not checked
90 function initialize(
91 address accessControlManager_,
92 address[] calldata tokens_,
93 uint256[] calldata distributionSpeeds_
94 ) external initializer {
// @audit accessControlManager_ not checked
// @audit tokens_ not checked
90 function initialize(
91 address accessControlManager_,
92 address[] calldata tokens_,
93 uint256[] calldata distributionSpeeds_
94 ) external initializer {
Constructors and initialization functions play a critical role in contracts by setting important initial states when the contract is first deployed before the system starts. The parameters passed to the constructor and initialization functions directly affect the behavior of the contract / protocol. If incorrect parameters are provided, the system may fail to run, behave abnormally, be unstable, or lack security. Therefore, it's crucial to carefully check each parameter in the constructor and initialization functions. If an exception is found, the transaction should be rolled back.
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit _xvsVaultPoolId not checked
// @audit _alphaNumerator not checked
// @audit _loopsLimit not checked
130 function initialize(
131 address _xvsVault,
132 address _xvsVaultRewardToken,
133 uint256 _xvsVaultPoolId,
134 uint128 _alphaNumerator,
135 uint128 _alphaDenominator,
136 address _accessControlManager,
137 address _protocolShareReserve,
138 address _primeLiquidityProvider,
139 address _comptroller,
140 address _oracle,
141 uint256 _loopsLimit
142 ) external virtual initializer {
// @audit _xvsVaultPoolId not checked
// @audit _alphaNumerator not checked
// @audit _loopsLimit not checked
130 function initialize(
131 address _xvsVault,
132 address _xvsVaultRewardToken,
133 uint256 _xvsVaultPoolId,
134 uint128 _alphaNumerator,
135 uint128 _alphaDenominator,
136 address _accessControlManager,
137 address _protocolShareReserve,
138 address _primeLiquidityProvider,
139 address _comptroller,
140 address _oracle,
141 uint256 _loopsLimit
142 ) external virtual initializer {
// @audit _xvsVaultPoolId not checked
// @audit _alphaNumerator not checked
// @audit _loopsLimit not checked
130 function initialize(
131 address _xvsVault,
132 address _xvsVaultRewardToken,
133 uint256 _xvsVaultPoolId,
134 uint128 _alphaNumerator,
135 uint128 _alphaDenominator,
136 address _accessControlManager,
137 address _protocolShareReserve,
138 address _primeLiquidityProvider,
139 address _comptroller,
140 address _oracle,
141 uint256 _loopsLimit
142 ) external virtual initializer {
Consider limiting the number of iterations in for
-loops that make external calls.
There are 12 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
// @audit external call => IXVSVault(xvsVault).getUserInfo(xvsVaultRewardToken, xvsVaultPoolId, user)
// @audit external call => vToken.borrowBalanceStored(user)
// @audit external call => vToken.exchangeRateStored()
// @audit external call => vToken.balanceOf(user)
// @audit external call => IXVSVault(xvsVault).xvsAddress()
// @audit external call => oracle.updateAssetPrice(xvsToken)
// @audit external call => oracle.updatePrice(market)
// @audit external call => oracle.getPrice(xvsToken)
// @audit external call => oracle.getUnderlyingPrice(market)
// @audit external call => vToken.decimals()
204 for (uint256 i = 0; i < users.length; ) {
205 address user = users[i];
206
207 if (!tokens[user].exists) revert UserHasNoPrimeToken();
208 if (isScoreUpdated[nextScoreUpdateRoundId][user]) continue;
209
210 address[] storage _allMarkets = allMarkets;
211 for (uint256 j = 0; j < _allMarkets.length; ) {
212 address market = _allMarkets[j];
213 _executeBoost(user, market);
214 _updateScore(user, market);
215
216 unchecked {
217 j++;
218 }
219 }
220
221 pendingScoreUpdates--;
222 isScoreUpdated[nextScoreUpdateRoundId][user] = true;
223
224 unchecked {
225 i++;
226 }
227
228 emit UserScoreUpdated(user);
229 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
// @audit external call => IXVSVault(xvsVault).getUserInfo(xvsVaultRewardToken, xvsVaultPoolId, user)
// @audit external call => vToken.borrowBalanceStored(user)
// @audit external call => vToken.exchangeRateStored()
// @audit external call => vToken.balanceOf(user)
// @audit external call => IXVSVault(xvsVault).xvsAddress()
// @audit external call => oracle.updateAssetPrice(xvsToken)
// @audit external call => oracle.updatePrice(market)
// @audit external call => oracle.getPrice(xvsToken)
// @audit external call => oracle.getUnderlyingPrice(market)
// @audit external call => vToken.decimals()
211 for (uint256 j = 0; j < _allMarkets.length; ) {
212 address market = _allMarkets[j];
213 _executeBoost(user, market);
214 _updateScore(user, market);
215
216 unchecked {
217 j++;
218 }
219 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
246 for (uint256 i = 0; i < allMarkets.length; ) {
247 accrueInterest(allMarkets[i]);
248
249 unchecked {
250 i++;
251 }
252 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
// @audit external call => IXVSVault(xvsVault).getUserInfo(xvsVaultRewardToken, xvsVaultPoolId, user)
// @audit external call => vToken.borrowBalanceStored(user)
// @audit external call => vToken.exchangeRateStored()
// @audit external call => vToken.balanceOf(user)
// @audit external call => IXVSVault(xvsVault).xvsAddress()
// @audit external call => oracle.updateAssetPrice(xvsToken)
// @audit external call => oracle.updatePrice(market)
// @audit external call => oracle.getPrice(xvsToken)
// @audit external call => oracle.getUnderlyingPrice(market)
// @audit external call => vToken.decimals()
335 for (uint256 i = 0; i < users.length; ) {
336 Token storage userToken = tokens[users[i]];
337 if (userToken.exists && !userToken.isIrrevocable) {
338 _upgrade(users[i]);
339 } else {
340 _mint(true, users[i]);
341 _initializeMarkets(users[i]);
342 }
343
344 unchecked {
345 i++;
346 }
347 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
// @audit external call => IXVSVault(xvsVault).getUserInfo(xvsVaultRewardToken, xvsVaultPoolId, user)
// @audit external call => vToken.borrowBalanceStored(user)
// @audit external call => vToken.exchangeRateStored()
// @audit external call => vToken.balanceOf(user)
// @audit external call => IXVSVault(xvsVault).xvsAddress()
// @audit external call => oracle.updateAssetPrice(xvsToken)
// @audit external call => oracle.updatePrice(market)
// @audit external call => oracle.getPrice(xvsToken)
// @audit external call => oracle.getUnderlyingPrice(market)
// @audit external call => vToken.decimals()
349 for (uint256 i = 0; i < users.length; ) {
350 _mint(false, users[i]);
351 _initializeMarkets(users[i]);
352 delete stakedAt[users[i]];
353
354 unchecked {
355 i++;
356 }
357 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
// @audit external call => IXVSVault(xvsVault).getUserInfo(xvsVaultRewardToken, xvsVaultPoolId, user)
// @audit external call => vToken.borrowBalanceStored(user)
// @audit external call => vToken.exchangeRateStored()
// @audit external call => vToken.balanceOf(user)
// @audit external call => IXVSVault(xvsVault).xvsAddress()
// @audit external call => oracle.updateAssetPrice(xvsToken)
// @audit external call => oracle.updatePrice(market)
// @audit external call => oracle.getPrice(xvsToken)
// @audit external call => oracle.getUnderlyingPrice(market)
// @audit external call => vToken.decimals()
609 for (uint256 i = 0; i < _allMarkets.length; ) {
610 _executeBoost(user, _allMarkets[i]);
611 _updateScore(user, _allMarkets[i]);
612
613 unchecked {
614 i++;
615 }
616 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
// @audit external call => IXVSVault(xvsVault).getUserInfo(xvsVaultRewardToken, xvsVaultPoolId, user)
// @audit external call => vToken.borrowBalanceStored(user)
// @audit external call => vToken.exchangeRateStored()
// @audit external call => vToken.balanceOf(user)
// @audit external call => IXVSVault(xvsVault).xvsAddress()
// @audit external call => oracle.updateAssetPrice(xvsToken)
// @audit external call => oracle.updatePrice(market)
// @audit external call => oracle.getPrice(xvsToken)
// @audit external call => oracle.getUnderlyingPrice(market)
// @audit external call => vToken.decimals()
625 for (uint256 i = 0; i < _allMarkets.length; ) {
626 address market = _allMarkets[i];
627 accrueInterest(market);
628
629 interests[market][account].rewardIndex = markets[market].rewardIndex;
630
631 uint256 score = _calculateScore(market, account);
632 interests[market][account].score = score;
633 markets[market].sumOfMembersScore = markets[market].sumOfMembersScore + score;
634
635 unchecked {
636 i++;
637 }
638 }
// @audit external call => IVToken(vToken).underlying()
// @audit external call => IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(comptroller, IProtocolShareReserve.Schema.SPREAD_PRIME_CORE, address(this), underlying)
// @audit external call => _primeLiquidityProvider.accrueTokens(underlying)
// @audit external call => _primeLiquidityProvider.tokenAmountAccrued(underlying)
730 for (uint256 i = 0; i < _allMarkets.length; ) {
731 _executeBoost(user, _allMarkets[i]);
732
733 markets[_allMarkets[i]].sumOfMembersScore =
734 markets[_allMarkets[i]].sumOfMembersScore -
735 interests[_allMarkets[i]][user].score;
736 interests[_allMarkets[i]][user].score = 0;
737 interests[_allMarkets[i]][user].rewardIndex = 0;
738
739 unchecked {
740 i++;
741 }
742 }
GitHub: [204, 204, 211, 211, 246, 335, 349, 609, 609, 625, 625, 730]
The initialize()
functions below are not called by another contract atomically after the contract is deployed, so it's possible for a malicious user to call initialize()
which, if it's noticed in time, would require the project to re-deploy the contract in order to properly initialize. Consider creating a factory contract, which will new
and initialize()
each contract atomically.
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
130 function initialize(
131 address _xvsVault,
132 address _xvsVaultRewardToken,
133 uint256 _xvsVaultPoolId,
134 uint128 _alphaNumerator,
135 uint128 _alphaDenominator,
136 address _accessControlManager,
137 address _protocolShareReserve,
138 address _primeLiquidityProvider,
139 address _comptroller,
140 address _oracle,
141 uint256 _loopsLimit
142 ) external virtual initializer {
GitHub: [130]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
90 function initialize(
91 address accessControlManager_,
92 address[] calldata tokens_,
93 uint256[] calldata distributionSpeeds_
94 ) external initializer {
GitHub: [90]
Some IERC20
implementations (e.g UNI
, COMP
) may fail if the valued transferred
is larger than uint96
. Source.
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
692 asset.safeTransfer(user, amount);
GitHub: [692]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
204 IERC20Upgradeable(token_).safeTransfer(prime, accruedAmount);
224 token_.safeTransfer(to_, amount_);
Dividing an integer by another integer will often result in loss of precision. When the result is multiplied by another number, the loss of precision is magnified, often to material magnitudes. (X / Z) * Y
should be re-written as (X * Y) / Z
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
881 uint256 borrowCapUSD = (xvsPrice * ((xvs * markets[market].borrowMultiplier) / EXP_SCALE)) / EXP_SCALE;
882 uint256 supplyCapUSD = (xvsPrice * ((xvs * markets[market].supplyMultiplier) / EXP_SCALE)) / EXP_SCALE;
949 return ((((market.totalBorrows() * market.borrowRatePerBlock()) / EXP_SCALE) * market.reserveFactorMantissa()) /
Division by large numbers may result in the result being zero, due to solidity not supporting fractions. Consider requiring a minimum amount for the numerator to ensure that it is always larger than the denominator
There are 45 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
501 uint256 supply = (exchangeRate * balanceOfAccount) / EXP_SCALE;
585 delta = ((distributionIncome * EXP_SCALE) / markets[vToken].sumOfMembersScore);
654 uint256 supply = (exchangeRate * balanceOfAccount) / EXP_SCALE;
881 uint256 borrowCapUSD = (xvsPrice * ((xvs * markets[market].borrowMultiplier) / EXP_SCALE)) / EXP_SCALE;
882 uint256 supplyCapUSD = (xvsPrice * ((xvs * markets[market].supplyMultiplier) / EXP_SCALE)) / EXP_SCALE;
885 uint256 supplyUSD = (tokenPrice * supply) / EXP_SCALE;
886 uint256 borrowUSD = (tokenPrice * borrow) / EXP_SCALE;
889 supply = supplyUSD > 0 ? (supply * supplyCapUSD) / supplyUSD : 0;
893 borrow = borrowUSD > 0 ? (borrow * borrowCapUSD) / borrowUSD : 0;
922 return (index * score) / EXP_SCALE;
949 return ((((market.totalBorrows() * market.borrowRatePerBlock()) / EXP_SCALE) * market.reserveFactorMantissa()) /
950 EXP_SCALE);
1004 uint256 userYearlyIncome = (userScore * _incomeDistributionYearly(vToken)) / totalScore;
1009 uint256 userSupplyIncomeYearly = (userYearlyIncome * totalCappedSupply) / totalCappedValue;
1010 uint256 userBorrowIncomeYearly = (userYearlyIncome * totalCappedBorrow) / totalCappedValue;
1012 supplyAPR = totalSupply == 0 ? 0 : ((userSupplyIncomeYearly * MAXIMUM_BPS) / totalSupply);
1013 borrowAPR = totalBorrow == 0 ? 0 : ((userBorrowIncomeYearly * MAXIMUM_BPS) / totalBorrow);
GitHub: [501, 585, 654, 881, 882, 885, 886, 889, 893, 922, 949, 949, 1004, 1009, 1010, 1012, 1013]
File: contracts/Tokens/Prime/libs/FixedMath.sol
25 return (n.toInt256() * FixedMath0x.FIXED_1) / int256(d.toInt256());
37 return uint256((u.toInt256() * FixedMath0x.FIXED_1) / f);
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
75 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000000001c8464f76164760000000); // / e ^ -32
80 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000); // / e ^ -16
85 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000afe10820813d78000000000000000); // / e ^ -8
90 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000); // / e ^ -4
95 x = (x * FIXED_1) / int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000); // / e ^ -2
100 x = (x * FIXED_1) / int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000); // / e ^ -1
105 x = (x * FIXED_1) / int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000); // / e ^ -0.5
110 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000); // / e ^ -0.25
115 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d); // / e ^ -0.125
122 r += (z * (0x100000000000000000000000000000000 - y)) / 0x100000000000000000000000000000000;
126 r += (z * (0x099999999999999999999999999999999 - y)) / 0x300000000000000000000000000000000;
128 r += (z * (0x092492492492492492492492492492492 - y)) / 0x400000000000000000000000000000000;
130 r += (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) / 0x500000000000000000000000000000000;
132 r += (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) / 0x600000000000000000000000000000000;
134 r += (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) / 0x700000000000000000000000000000000;
136 r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16
201 r = r / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
208 (r * int256(0x00000000000000000000000000000000000000f1aaddd7742e56d32fb9f99744)) /
209 int256(0x0000000000000000000000000043cbaf42a000812488fc5c220ad7b97bf6e99e); // * e ^ -32
214 (r * int256(0x00000000000000000000000000000000000afe10820813d65dfe6a33c07f738f)) /
215 int256(0x000000000000000000000000000005d27a9f51c31b7c2f8038212a0574779991); // * e ^ -16
220 (r * int256(0x0000000000000000000000000000000002582ab704279e8efd15e0265855c47a)) /
221 int256(0x0000000000000000000000000000001b4c902e273a58678d6d3bfdb93db96d02); // * e ^ -8
226 (r * int256(0x000000000000000000000000000000001152aaa3bf81cb9fdb76eae12d029571)) /
227 int256(0x00000000000000000000000000000003b1cc971a9bb5b9867477440d6d157750); // * e ^ -4
232 (r * int256(0x000000000000000000000000000000002f16ac6c59de6f8d5d6f63c1482a7c86)) /
233 int256(0x000000000000000000000000000000015bf0a8b1457695355fb8ac404e7a79e3); // * e ^ -2
238 (r * int256(0x000000000000000000000000000000004da2cbf1be5827f9eb3ad1aa9866ebb3)) /
239 int256(0x00000000000000000000000000000000d3094c70f034de4b96ff7d5b6f99fcd8); // * e ^ -1
244 (r * int256(0x0000000000000000000000000000000063afbe7ab2082ba1a0ae5e4eb1b479dc)) /
245 int256(0x00000000000000000000000000000000a45af1e1f40c333b3de1db4dd55f29a7); // * e ^ -0.5
250 (r * int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) /
251 int256(0x00000000000000000000000000000000910b022db7ae67ce76b441c27035c6a1); // * e ^ -0.25
256 (r * int256(0x00000000000000000000000000000000783eafef1c0a8f3978c7f81824d62ebf)) /
257 int256(0x0000000000000000000000000000000088415abbe9a76bead8d00cf112e4d4a8); // * e ^ -0.125
GitHub: [75, 80, 85, 90, 95, 100, 105, 110, 115, 122, 126, 128, 130, 132, 134, 136, 201, 208, 214, 220, 226, 232, 238, 244, 250, 256]
[Lβ09] Upgradeable contract is missing a __gap[50]
storage variable to allow for new storage variables in later versions
See this link for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
8 contract PrimeLiquidityProvider is AccessControlledV8, PausableUpgradeable {
GitHub: [8]
Consider adding minimum/maximum value checks to ensure that the state variables below can never be used to excessively harm users, including via griefing
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
154 xvsVaultPoolId = _xvsVaultPoolId;
821 pendingScoreUpdates = totalScoreUpdatesRequired;
A copy-paste error or a typo may end up bricking protocol functionality, or sending tokens to an address with no known private key. Consider implementing a two-step procedure for updating protocol addresses, where the recipient is set as pending, and must 'accept' the assignment by making an affirmative call. A straight forward way of doing this would be to have the target contracts implement EIP-165, and to have the 'set' functions ensure that the recipient is of the right interface type.
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
177 function setPrimeToken(address prime_) external onlyOwner {
178 _ensureZeroAddress(prime_);
179
180 emit PrimeTokenUpdated(prime, prime_);
181 prime = prime_;
GitHub: [177]
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit assets[0]
684 assets[0] = address(asset);
GitHub: [684]
Array entries are added but are never removed. Consider whether this should be the case, or whether there should be a maximum, or whether old entries should be removed. Cases where there are specific potential problems will be flagged separately under a different issue.
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
303 allMarkets.push(vToken);
GitHub: [303]
Doing so will significantly increase centralization, but will help to prevent hackers from using stolen tokens
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
35 contract Prime is IIncomeDestination, AccessControlledV8, PausableUpgradeable, MaxLoopsLimitHelper, PrimeStorageV1 {
GitHub: [35]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
8 contract PrimeLiquidityProvider is AccessControlledV8, PausableUpgradeable {
GitHub: [8]
Consider adding some comments on critical state variables to explain what they are supposed to do: this will help for future code reviews.
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
31 uint256 internal constant EXP_SCALE = 1e18;
GitHub: [31]
Doing so will prevent typo bugs
There are 26 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
106 if (_blocksPerYear == 0) revert InvalidBlocksPerYear();
201 if (pendingScoreUpdates == 0) revert NoScoreUpdatesRequired();
202 if (nextScoreUpdateRoundId == 0) revert NoScoreUpdatesRequired();
377 } else if (stakedAt[user] == 0 && isAccountEligible && !tokens[user].exists) {
398 if (stakedAt[msg.sender] == 0) revert IneligibleToClaim();
479 if (stakedAt[user] == 0) return STAKING_PERIOD;
576 if (distributionIncome == 0) {
810 if (_alphaDenominator == 0 || _alphaNumerator > _alphaDenominator) {
1002 if (totalScore == 0) return (0, 0);
1007 if (totalCappedValue == 0) return (0, 0);
1012 supplyAPR = totalSupply == 0 ? 0 : ((userSupplyIncomeYearly * MAXIMUM_BPS) / totalSupply);
1013 borrowAPR = totalBorrow == 0 ? 0 : ((userBorrowIncomeYearly * MAXIMUM_BPS) / totalBorrow);
GitHub: [106, 201, 202, 377, 398, 479, 576, 810, 1002, 1007, 1012, 1013]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
335 if (lastBlockAccrued == 0) {
GitHub: [335]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
55 if (x <= 0) {
145 if (x == 0) {
206 if ((x & int256(0x0000000000000000000000000000001000000000000000000000000000000000)) != 0) {
212 if ((x & int256(0x0000000000000000000000000000000800000000000000000000000000000000)) != 0) {
218 if ((x & int256(0x0000000000000000000000000000000400000000000000000000000000000000)) != 0) {
224 if ((x & int256(0x0000000000000000000000000000000200000000000000000000000000000000)) != 0) {
230 if ((x & int256(0x0000000000000000000000000000000100000000000000000000000000000000)) != 0) {
236 if ((x & int256(0x0000000000000000000000000000000080000000000000000000000000000000)) != 0) {
242 if ((x & int256(0x0000000000000000000000000000000040000000000000000000000000000000)) != 0) {
248 if ((x & int256(0x0000000000000000000000000000000020000000000000000000000000000000)) != 0) {
254 if ((x & int256(0x0000000000000000000000000000000010000000000000000000000000000000)) != 0) {
GitHub: [55, 145, 206, 212, 218, 224, 230, 236, 242, 248, 254]
File: contracts/Tokens/Prime/libs/Scores.sol
43 // If any side is 0, exit early
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
6 contract PrimeStorageV1 {
GitHub: [6]
Consider adding parameters to the error to indicate which user or values caused the failure
There are 17 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
20 error MarketNotSupported();
21 error InvalidLimit();
22 error IneligibleToClaim();
23 error WaitMoreTime();
24 error UserHasNoPrimeToken();
25 error InvalidCaller();
26 error InvalidComptroller();
27 error NoScoreUpdatesRequired();
28 error MarketAlreadyExists();
29 error InvalidAddress();
30 error InvalidBlocksPerYear();
31 error InvalidAlphaArguments();
32 error InvalidVToken();
GitHub: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
57 error InvalidArguments();
63 error InvalidCaller();
72 error FundsTransferIsPaused();
File: contracts/Tokens/Prime/libs/FixedMath.sol
11 error InvalidFixedPoint();
GitHub: [11]
Consider breaking down these blocks into more manageable units, by splitting things into utility functions, by reducing nesting, and by using early returns
There are 2 instances of this issue:
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
// @audit number of blocks: 14
51 function ln(int256 x) internal pure returns (int256 r) {
// @audit number of blocks: 13
140 function exp(int256 x) internal pure returns (int256 r) {
The delete
keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic
There are 12 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
295 markets[vToken].rewardIndex = 0;
298 markets[vToken].sumOfMembersScore = 0;
376 stakedAt[user] = 0;
401 stakedAt[msg.sender] = 0;
460 unreleasedPSRIncome[_getUnderlying(address(market))] = 0;
677 interests[vToken][user].accrued = 0;
688 unreleasedPLPIncome[underlying] = 0;
736 interests[_allMarkets[i]][user].score = 0;
737 interests[_allMarkets[i]][user].rewardIndex = 0;
750 tokens[user].exists = false;
751 tokens[user].isIrrevocable = false;
GitHub: [295, 298, 376, 401, 460, 677, 688, 736, 737, 750, 751]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
200 tokenAmountAccrued[token_] = 0;
GitHub: [200]
Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant
in a library
. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
// @audit already seen at /contracts/Tokens/Prime/PrimeStorage.sol, /contracts/Tokens/Prime/PrimeLiquidityProvider.sol
31 uint256 internal constant EXP_SCALE = 1e18;
GitHub: [31]
For example, rather than using event Paused()
and event Unpaused()
, use event PauseState(address indexed whoChangedIt, bool wasPaused, bool isNowPaused)
There are 2 instances of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
51 event FundsTransferPaused();
54 event FundsTransferResumed();
The IR-based code generator was introduced with an aim to not only allow code generation to be more transparent and auditable but also to enable more powerful optimization passes that span across functions.
You can enable it on the command line using --via-ir
or with the option {"viaIR": true}
.
This will take longer to compile, but you can just simple test it before deploying and if you got a better benchmark then you can add --via-ir to your deploy command
More on: https://docs.soliditylang.org/en/v0.8.17/ir-breaking-changes.html
There is one instance of this issue:
Global finding
This should especially be done if the new value is not required to be different from the old value
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit updateAssetsState
462 emit UpdatedAssetsState(comptroller, asset);
GitHub: [462]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit _setTokenDistributionSpeed
324 emit TokenDistributionSpeedUpdated(token_, distributionSpeed_);
GitHub: [324]
When an action is triggered based on a user's action, not being able to filter based on who triggered the action makes event processing a lot more cumbersome. Including the msg.sender
the events of these types of action will make events much more useful to end users, especially when msg.sender
is not tx.origin
.
There are 13 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
241 emit AlphaUpdated(alphaNumerator, alphaDenominator, _alphaNumerator, _alphaDenominator);
269 emit MultiplierUpdated(
270 market,
271 markets[market].supplyMultiplier,
272 markets[market].borrowMultiplier,
273 supplyMultiplier,
274 borrowMultiplier
275 );
308 emit MarketAdded(vToken, supplyMultiplier, borrowMultiplier);
320 emit MintLimitsUpdated(irrevocableLimit, revocableLimit, _irrevocableLimit, _revocableLimit);
462 emit UpdatedAssetsState(comptroller, asset);
694 emit InterestClaimed(user, vToken, amount);
718 emit Mint(user, isIrrevocable);
GitHub: [241, 269, 308, 320, 462, 694, 718]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
180 emit PrimeTokenUpdated(prime, prime_);
202 emit TokenTransferredToPrime(token_, accruedAmount);
222 emit SweepToken(address(token_), to_, amount_);
267 emit TokensAccrued(token_, tokenAccrued);
300 emit TokenDistributionInitialized(token_);
324 emit TokenDistributionSpeedUpdated(token_, distributionSpeed_);
GitHub: [180, 202, 222, 267, 300, 324]
Ensure that events follow the best practice of check-effects-interaction, and are emitted before external calls
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit InterfaceComptroller(comptroller).markets(vToken) called before emit MarketAdded(vToken, supplyMultiplier, borrowMultiplier);
292 bool isMarketExist = InterfaceComptroller(comptroller).markets(vToken);
293 if (!isMarketExist) revert InvalidVToken();
294
295 markets[vToken].rewardIndex = 0;
296 markets[vToken].supplyMultiplier = supplyMultiplier;
297 markets[vToken].borrowMultiplier = borrowMultiplier;
298 markets[vToken].sumOfMembersScore = 0;
299 markets[vToken].exists = true;
300
301 vTokenForAsset[_getUnderlying(vToken)] = vToken;
302
303 allMarkets.push(vToken);
304 _startScoreUpdateRound();
305
306 _ensureMaxLoops(allMarkets.length);
307
308 emit MarketAdded(vToken, supplyMultiplier, borrowMultiplier);
// @audit asset.safeTransfer(user, amount) called before emit InterestClaimed(user, vToken, amount);
692 asset.safeTransfer(user, amount);
693
694 emit InterestClaimed(user, vToken, amount);
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit IERC20Upgradeable(token_).balanceOf(address(this)) called before emit TokensAccrued(token_, tokenAccrued);
259 uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this));
260
261 uint256 balanceDiff = balance - tokenAmountAccrued[token_];
262 if (distributionSpeed > 0 && balanceDiff > 0) {
263 uint256 accruedSinceUpdate = deltaBlocks * distributionSpeed;
264 uint256 tokenAccrued = (balanceDiff <= accruedSinceUpdate ? balanceDiff : accruedSinceUpdate);
265
266 tokenAmountAccrued[token_] += tokenAccrued;
267 emit TokensAccrued(token_, tokenAccrued);
GitHub: [259]
Locking the pragma helps avoid accidental deploys with an outdated compiler version that may introduce bugs and unexpected vulnerabilities.
Floating pragma is meant to be used for libraries and contracts that have external users and need backward compatibility.
There is one instance of this issue:
File: contracts/Tokens/Prime/IPrime.sol
2 pragma solidity ^0.5.16;
GitHub: [2]
Consider using formal verification to mathematically prove that your code does what is intended, and does not have any edge cases with unexpected behavior. The solidity compiler itself has this functionality built in
There is one instance of this issue:
Global finding
While 100% code coverage does not guarantee that there are no bugs, it often will catch easy-to-find bugs, and will ensure that there are fewer regressions when the code invariably has to be modified. Furthermore, in order to get full coverage, code authors will often have to re-organize their code so that it is more modular, so that each component can be tested separately, which reduces interdependencies between modules and layers, and makes for code that is easier to reason about and audit.
There is one instance of this issue:
Global finding
All external
/public
functions should extend an interface
. This is useful to make sure that the whole API is extracted.
There are 26 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
174 function getPendingInterests(address user) external returns (PendingInterest[] memory pendingInterests) {
200 function updateScores(address[] memory users) external {
237 function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external {
263 function updateMultipliers(address market, uint256 supplyMultiplier, uint256 borrowMultiplier) external {
288 function addMarket(address vToken, uint256 supplyMultiplier, uint256 borrowMultiplier) external {
316 function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external {
331 function issue(bool isIrrevocable, address[] calldata users) external {
397 function claim() external {
411 function burn(address user) external {
419 function togglePause() external {
433 function claimInterest(address vToken) external whenNotPaused returns (uint256) {
443 function claimInterest(address vToken, address user) external whenNotPaused returns (uint256) {
452 function updateAssetsState(address _comptroller, address asset) external {
469 function getAllMarkets() external view returns (address[] memory) {
478 function claimTimeRemaining(address user) external view returns (uint256) {
496 function calculateAPR(address market, address user) external view returns (uint256 supplyAPR, uint256 borrowAPR) {
527 function estimateAPR(
528 address market,
529 address user,
530 uint256 borrow,
531 uint256 supply,
532 uint256 xvsStaked
533 ) external view returns (uint256 supplyAPR, uint256 borrowAPR) {
597 function getInterestAccrued(address vToken, address user) public returns (uint256) {
GitHub: [174, 200, 237, 263, 288, 316, 331, 397, 411, 419, 433, 443, 452, 469, 478, 496, 527, 597]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
90 function initialize(
91 address accessControlManager_,
92 address[] calldata tokens_,
93 uint256[] calldata distributionSpeeds_
94 ) external initializer {
118 function initializeTokens(address[] calldata tokens_) external onlyOwner {
132 function pauseFundsTransfer() external {
141 function resumeFundsTransfer() external {
153 function setTokensDistributionSpeed(address[] calldata tokens_, uint256[] calldata distributionSpeeds_) external {
177 function setPrimeToken(address prime_) external onlyOwner {
216 function sweepToken(IERC20Upgradeable token_, address to_, uint256 amount_) external onlyOwner {
276 function getBlockNumber() public view virtual returns (uint256) {
Large code bases, or code with lots of inline-assembly, complicated math, or complicated interactions between multiple contracts, should implement invariant fuzzing tests. Invariant fuzzers such as Echidna require the test writer to come up with invariants which should not be violated under any circumstances, and the fuzzer tests various inputs and function calls to ensure that the invariants always hold. Even code with 100% code coverage can still have bugs due to the order of the operations a user performs, and invariant fuzzers, with properly and extensively-written invariants, can close this testing gap significantly.
There is one instance of this issue:
Global finding
The default value for variables is zero, so initializing them to zero is superfluous.
There are 10 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
178 for (uint256 i = 0; i < _allMarkets.length; ) {
204 for (uint256 i = 0; i < users.length; ) {
211 for (uint256 j = 0; j < _allMarkets.length; ) {
246 for (uint256 i = 0; i < allMarkets.length; ) {
335 for (uint256 i = 0; i < users.length; ) {
349 for (uint256 i = 0; i < users.length; ) {
609 for (uint256 i = 0; i < _allMarkets.length; ) {
625 for (uint256 i = 0; i < _allMarkets.length; ) {
730 for (uint256 i = 0; i < _allMarkets.length; ) {
GitHub: [178, 204, 211, 246, 335, 349, 609, 625, 730]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
46 int256 private constant EXP_MAX_VAL = 0;
GitHub: [46]
NatSpec must begin with ///
, or use the /* ... */
syntax.
There are 75 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
111 // Note that the contract is upgradeable. Use initialize() or reinitializers
112 // to set the state variables.
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
316 // Distribution speed updated so let's update distribution state to ensure that
317 // 1. Token accrued properly for the old speed, and
318 // 2. Token accrued at the new speed starts after this block.
321 // Update speed
File: contracts/Tokens/Prime/libs/FixedMath.sol
2 // solhint-disable var-name-mixedcase
36 // multiply `u` by FIXED_1 to cancel out the built-in FIXED_1 in f
48 // divide the product by FIXED_1 to cancel out the built-in FIXED_1 in f
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
2 // solhint-disable max-line-length
6 // Below is code from 0x's LibFixedMath.sol. Changes:
7 // - addition of 0.8-style errors
8 // - removal of unused functions
9 // - added comments for clarity
10 // https://github.com/0xProject/exchange-v3/blob/aae46bef841bfd1cc31028f41793db4fe7197084/contracts/staking/contracts/src/libs/LibFixedMath.sol
39 // Base for the fixed point numbers (this is our 1)
41 // Maximum ln argument (1)
43 // Minimum ln argument. Notice this is related to EXP_MIN_VAL (e ^ -63.875)
45 // Maximum exp argument (0)
47 // Minimum exp argument. Notice this is related to LN_MIN_VAL (-63.875)
69 // Rewrite the input as a quotient of negative natural exponents and a single residual q, such that 1 < q < 2
70 // For example: log(0.3) = log(e^-1 * e^-0.25 * 1.0471028872385522)
71 // = 1 - 0.25 - log(1 + 0.0471028872385522)
72 // e ^ -32
77 // e ^ -16
82 // e ^ -8
87 // e ^ -4
92 // e ^ -2
97 // e ^ -1
102 // e ^ -0.5
107 // e ^ -0.25
112 // e ^ -0.125
117 // `x` is now our residual in the range of 1 <= x <= 2 (or close enough).
119 // Add the taylor series for log(1 + z), where z = x - 1
142 // Saturate to zero below EXP_MIN_VAL.
152 // Rewrite the input as a product of natural exponents and a
153 // single residual q, where q is a number of small magnitude.
154 // For example: e^-34.419 = e^(-32 - 2 - 0.25 - 0.125 - 0.044)
155 // = e^-32 * e^-2 * e^-0.25 * e^-0.125 * e^-0.044
156 // -> q = -0.044
158 // Multiply with the taylor series for e^q
161 // q = x % 0.125 (the residual)
203 // Multiply with the non-residual terms.
205 // e ^ -32
211 // e ^ -16
217 // e ^ -8
223 // e ^ -4
229 // e ^ -2
235 // e ^ -1
241 // e ^ -0.5
247 // e ^ -0.25
253 // e ^ -0.125
GitHub: [2, 6, 7, 8, 9, 10, 39, 41, 43, 45, 47, 69, 70, 71, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 119, 142, 152, 153, 154, 155, 156, 158, 161, 203, 205, 211, 217, 223, 229, 235, 241, 247, 253]
File: contracts/Tokens/Prime/libs/Scores.sol
28 // Score function is:
29 // xvs^π° * capital^(1-π°)
30 // = capital * capital^(-π°) * xvs^π°
31 // = capital * (xvs / capital)^π°
32 // = capital * (e ^ (ln(xvs / capital))) ^ π°
33 // = capital * e ^ (π° * ln(xvs / capital)) (1)
35 // = capital / ( 1 / e ^ (π° * ln(xvs / capital)))
36 // = capital / (e ^ (π° * ln(xvs / capital)) ^ -1)
37 // = capital / e ^ (π° * -1 * ln(xvs / capital))
38 // = capital / e ^ (π° * ln(capital / xvs)) (2)
40 // To avoid overflows, use (1) when xvs < capital and
41 // use (2) when capital < xvs
44 if (xvs == 0 || capital == 0) return 0;
46 // If both sides are equal, we have:
47 // xvs^π° * capital^(1-π°)
48 // = xvs^π° * xvs^(1-π°)
52 bool lessxvsThanCapital = xvs < capital;
55 int256 ratio = lessxvsThanCapital ? FixedMath.toFixed(xvs, capital) : FixedMath.toFixed(capital, xvs);
62 if (lessxvsThanCapital) {
64 return FixedMath.uintMul(capital, exponentiation);
GitHub: [28, 29, 30, 31, 32, 33, 33, 35, 36, 37, 38, 38, 40, 40, 41, 44, 46, 47, 48, 52, 55, 62, 64]
[Nβ23] Large multiples of ten should use scientific notation (e.g. 1e6) rather than decimal literals (e.g. 1000000
), for readability
There are 3 instances of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
34 uint256 public constant MINIMUM_STAKED_XVS = 1000 * EXP_SCALE;
37 uint256 public constant MAXIMUM_XVS_CAP = 100000 * EXP_SCALE;
43 uint256 internal constant MAXIMUM_BPS = 10000;
There are 2 instances of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
37 uint256 public constant MAXIMUM_XVS_CAP = 100000 * EXP_SCALE;
43 uint256 internal constant MAXIMUM_BPS = 10000;
When deploying contracts, you should use the latest released version of Solidity. Apart from exceptional cases, only the latest version receives security fixes.
https://docs.soliditylang.org/en/v0.8.21/
There are 7 instances of this issue:
File: contracts/Tokens/Prime/IPrime.sol
2 pragma solidity ^0.5.16;
GitHub: [2]
File: contracts/Tokens/Prime/Prime.sol
2 pragma solidity 0.8.13;
GitHub: [2]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
2 pragma solidity 0.8.13;
GitHub: [2]
File: contracts/Tokens/Prime/PrimeStorage.sol
2 pragma solidity 0.8.13;
GitHub: [2]
File: contracts/Tokens/Prime/libs/FixedMath.sol
4 pragma solidity 0.8.13;
GitHub: [4]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
4 pragma solidity 0.8.13;
GitHub: [4]
File: contracts/Tokens/Prime/libs/Scores.sol
3 pragma solidity 0.8.13;
GitHub: [3]
This is a best practice that should be followed.
Inside each contract, library or interface, use the following order:
- Type declarations
- State variables
- Events
- Errors
- Modifiers
- Functions
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit Correct order: InvalidArguments -> FundsTransferResumed
8 contract PrimeLiquidityProvider is AccessControlledV8, PausableUpgradeable {
GitHub: [8]
There is one instance of this issue:
File: contracts/Tokens/Prime/IPrime.sol
GitHub: various
[Nβ28] Multiple address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
, for readability
Well-organized data structures make code reviews easier, which may lead to fewer bugs. Consider combining related mappings into mappings to structs, so it's clear what data is related
There are 2 instances of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
21 mapping(address => uint256) public tokenDistributionSpeeds;
22
23 /// @notice The rate at which token is distributed to the Prime contract
24 mapping(address => uint256) public lastAccruedBlock;
25
26 /// @notice The token accrued but not yet transferred to prime contract
27 mapping(address => uint256) public tokenAmountAccrued;
GitHub: [21]
File: contracts/Tokens/Prime/PrimeStorage.sol
46 mapping(address => Token) public tokens;
47
48 /// @notice Tracks total irrevocable tokens minted
49 uint256 public totalIrrevocable;
50
51 /// @notice Tracks total revocable tokens minted
52 uint256 public totalRevocable;
53
54 /// @notice Indicates maximum revocable tokens that can be minted
55 uint256 public revocableLimit;
56
57 /// @notice Indicates maximum irrevocable tokens that can be minted
58 uint256 public irrevocableLimit;
59
60 /// @notice Tracks when prime token eligible users started staking for claiming prime token
61 mapping(address => uint256) public stakedAt;
62
63 /// @notice vToken to market configuration
64 mapping(address => Market) public markets;
65
66 /// @notice vToken to user to user index
67 mapping(address => mapping(address => Interest)) public interests;
68
69 /// @notice A list of boosted markets
70 address[] internal allMarkets;
71
72 /// @notice numberator of alpha. Ex: if alpha is 0.5 then this will be 1
73 uint128 public alphaNumerator;
74
75 /// @notice denominator of alpha. Ex: if alpha is 0.5 then this will be 2
76 uint128 public alphaDenominator;
77
78 /// @notice address of XVS vault
79 address internal xvsVault;
80
81 /// @notice address of XVS vault reward token
82 address internal xvsVaultRewardToken;
83
84 /// @notice address of XVS vault pool id
85 uint256 internal xvsVaultPoolId;
86
87 /// @notice mapping to check if a account's score was updated in the round
88 mapping(uint256 => mapping(address => bool)) public isScoreUpdated;
89
90 /// @notice unique id for next round
91 uint256 public nextScoreUpdateRoundId;
92
93 /// @notice total number of accounts whose score needs to be updated
94 uint256 public totalScoreUpdatesRequired;
95
96 /// @notice total number of accounts whose score is yet to be updated
97 uint256 public pendingScoreUpdates;
98
99 /// @notice mapping used to find if an asset is part of prime markets
100 mapping(address => address) public vTokenForAsset;
101
102 /// @notice address of protocol share reserve contract
103 address public protocolShareReserve;
104
105 /// @notice address of core pool comptroller contract
106 address public comptroller;
107
108 /// @notice unreleased income from PSR that's already distributed to prime holders
109 /// @dev mapping of asset adress => amount
110 mapping(address => uint256) public unreleasedPSRIncome;
111
112 /// @notice unreleased income from PLP that's already distributed to prime holders
113 /// @dev mapping of asset adress => amount
114 mapping(address => uint256) public unreleasedPLPIncome;
GitHub: [46]
Consider changing the variable to be an unnamed one, since the variable is never assigned, nor is it returned by name. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.
There are 7 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
// @audit pendingInterests
174 function getPendingInterests(address user) external returns (PendingInterest[] memory pendingInterests) {
// @audit supplyAPR
// @audit borrowAPR
496 function calculateAPR(address market, address user) external view returns (uint256 supplyAPR, uint256 borrowAPR) {
// @audit supplyAPR
// @audit borrowAPR
527 function estimateAPR(
528 address market,
529 address user,
530 uint256 borrow,
531 uint256 supply,
532 uint256 xvsStaked
533 ) external view returns (uint256 supplyAPR, uint256 borrowAPR) {
GitHub: [174, 496, 496, 527, 527]
File: contracts/Tokens/Prime/libs/FixedMath.sol
// @audit r
53 function ln(int256 x) internal pure returns (int256 r) {
// @audit r
58 function exp(int256 x) internal pure returns (int256 r) {
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
103 constructor(address _wbnb, address _vbnb, uint256 _blocksPerYear) {
GitHub: [103]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
78 constructor() {
GitHub: [78]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
51 function ln(int256 x) internal pure returns (int256 r) {
GitHub: [51]
There are 7 instances of this issue:
see instances
File: contracts/Tokens/Prime/IPrime.sol
4 interface IPrime {
GitHub: [4]
File: contracts/Tokens/Prime/Prime.sol
35 contract Prime is IIncomeDestination, AccessControlledV8, PausableUpgradeable, MaxLoopsLimitHelper, PrimeStorageV1 {
GitHub: [35]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
8 contract PrimeLiquidityProvider is AccessControlledV8, PausableUpgradeable {
GitHub: [8]
File: contracts/Tokens/Prime/PrimeStorage.sol
6 contract PrimeStorageV1 {
GitHub: [6]
File: contracts/Tokens/Prime/libs/FixedMath.sol
13 library FixedMath {
GitHub: [13]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
38 library FixedMath0x {
GitHub: [38]
File: contracts/Tokens/Prime/libs/Scores.sol
10 library Scores {
GitHub: [10]
e.g. @dev
or @notice
, and it must appear above the contract definition braces in order to be identified by the compiler as NatSpec
There are 7 instances of this issue:
see instances
File: contracts/Tokens/Prime/IPrime.sol
4 interface IPrime {
GitHub: [4]
File: contracts/Tokens/Prime/Prime.sol
35 contract Prime is IIncomeDestination, AccessControlledV8, PausableUpgradeable, MaxLoopsLimitHelper, PrimeStorageV1 {
GitHub: [35]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
8 contract PrimeLiquidityProvider is AccessControlledV8, PausableUpgradeable {
GitHub: [8]
File: contracts/Tokens/Prime/PrimeStorage.sol
6 contract PrimeStorageV1 {
GitHub: [6]
File: contracts/Tokens/Prime/libs/FixedMath.sol
13 library FixedMath {
GitHub: [13]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
38 library FixedMath0x {
GitHub: [38]
File: contracts/Tokens/Prime/libs/Scores.sol
10 library Scores {
GitHub: [10]
e.g. @notice
for public state variables, and @dev
for non-public ones
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
31 uint256 internal constant EXP_SCALE = 1e18;
GitHub: [31]
There are 7 instances of this issue:
File: contracts/Tokens/Prime/IPrime.sol
4 interface IPrime {
GitHub: [4]
File: contracts/Tokens/Prime/Prime.sol
35 contract Prime is IIncomeDestination, AccessControlledV8, PausableUpgradeable, MaxLoopsLimitHelper, PrimeStorageV1 {
GitHub: [35]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
8 contract PrimeLiquidityProvider is AccessControlledV8, PausableUpgradeable {
GitHub: [8]
File: contracts/Tokens/Prime/PrimeStorage.sol
6 contract PrimeStorageV1 {
GitHub: [6]
File: contracts/Tokens/Prime/libs/FixedMath.sol
13 library FixedMath {
GitHub: [13]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
38 library FixedMath0x {
GitHub: [38]
File: contracts/Tokens/Prime/libs/Scores.sol
10 library Scores {
GitHub: [10]
One level of nesting can be removed by not having an else
block when the if
-block returns, and if (foo) { return 1; } else { return 2; }
becomes if (foo) { return 1; } return 2;
There are 3 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
482 if (totalTimeStaked < STAKING_PERIOD) {
483 return STAKING_PERIOD - totalTimeStaked;
484 } else {
485 return 0;
486 }
855 if (xvs > MAXIMUM_XVS_CAP) {
856 return MAXIMUM_XVS_CAP;
857 } else {
858 return xvs;
859 }
931 if (vToken == VBNB) {
932 return WBNB;
933 } else {
934 return IVToken(vToken).underlying();
935 }
There are units for seconds, minutes, hours, days, and weeks, and since they're defined, they should be used
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
// "@audit 60"
40 uint256 public constant STAKING_PERIOD = 90 * 24 * 60 * 60;
GitHub: [40]
The instances below point to one of two functions with the same name. Consider naming each function differently, in order to make code navigation and analysis easier.
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit found also on line 433
443 function claimInterest(address vToken, address user) external whenNotPaused returns (uint256) {
GitHub: [443]
This especially problematic when the setter also emits the same value, which may be confusing to offline parsers
There are 6 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
// @audit check alphaNumerator = _alphaNumerator
// @audit check alphaDenominator = _alphaDenominator
237 function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external {
238 _checkAccessAllowed("updateAlpha(uint128,uint128)");
239 _checkAlphaArguments(_alphaNumerator, _alphaDenominator);
240
241 emit AlphaUpdated(alphaNumerator, alphaDenominator, _alphaNumerator, _alphaDenominator);
242
243 alphaNumerator = _alphaNumerator;
244 alphaDenominator = _alphaDenominator;
245
246 for (uint256 i = 0; i < allMarkets.length; ) {
247 accrueInterest(allMarkets[i]);
248
249 unchecked {
250 i++;
251 }
252 }
253
254 _startScoreUpdateRound();
255 }
// @audit check vTokenForAsset[_getUnderlying(vToken)] = vToken
288 function addMarket(address vToken, uint256 supplyMultiplier, uint256 borrowMultiplier) external {
289 _checkAccessAllowed("addMarket(address,uint256,uint256)");
290 if (markets[vToken].exists) revert MarketAlreadyExists();
291
292 bool isMarketExist = InterfaceComptroller(comptroller).markets(vToken);
293 if (!isMarketExist) revert InvalidVToken();
294
295 markets[vToken].rewardIndex = 0;
296 markets[vToken].supplyMultiplier = supplyMultiplier;
297 markets[vToken].borrowMultiplier = borrowMultiplier;
298 markets[vToken].sumOfMembersScore = 0;
299 markets[vToken].exists = true;
300
301 vTokenForAsset[_getUnderlying(vToken)] = vToken;
302
303 allMarkets.push(vToken);
304 _startScoreUpdateRound();
305
306 _ensureMaxLoops(allMarkets.length);
307
308 emit MarketAdded(vToken, supplyMultiplier, borrowMultiplier);
309 }
// @audit check revocableLimit = _revocableLimit
// @audit check irrevocableLimit = _irrevocableLimit
316 function setLimit(uint256 _irrevocableLimit, uint256 _revocableLimit) external {
317 _checkAccessAllowed("setLimit(uint256,uint256)");
318 if (_irrevocableLimit < totalIrrevocable || _revocableLimit < totalRevocable) revert InvalidLimit();
319
320 emit MintLimitsUpdated(irrevocableLimit, revocableLimit, _irrevocableLimit, _revocableLimit);
321
322 revocableLimit = _revocableLimit;
323 irrevocableLimit = _irrevocableLimit;
324 }
GitHub: [237, 237, 288, 316, 316]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit check prime = prime_
177 function setPrimeToken(address prime_) external onlyOwner {
178 _ensureZeroAddress(prime_);
179
180 emit PrimeTokenUpdated(prime, prime_);
181 prime = prime_;
182 }
GitHub: [177]
Because the return variable (or its default value) has been assigned, explicit return at the end of the function is unnecessary, as it is returned automatically.
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
174 function getPendingInterests(address user) external returns (PendingInterest[] memory pendingInterests) {
GitHub: [174]
Consider whether reasonable bounds checks for variables would be useful
There are 5 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
151 alphaNumerator = _alphaNumerator;
152 alphaDenominator = _alphaDenominator;
154 xvsVaultPoolId = _xvsVaultPoolId;
243 alphaNumerator = _alphaNumerator;
244 alphaDenominator = _alphaDenominator;
GitHub: [151, 152, 154, 243, 244]
There are 2 instances of this issue:
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
// @audit Lines in function body: 85
51 function ln(int256 x) internal pure returns (int256 r) {
// @audit Lines in function body: 118
140 function exp(int256 x) internal pure returns (int256 r) {
Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. The solidity style guide recommends a maximum line length of 120 characters, so the lines below should be split when they reach that length.
There are 11 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
4 import { SafeERC20Upgradeable, IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
775 * @notice Accrue rewards for the user. Must be called by Comptroller before changing account's borrow or supply balance.
902 * @return isEligible true if the staked XVS amount is enough to consider the associated user eligible for a Prime token, false otherwise
928 * @return underlying The address of the underlying token associated with the VToken, or the address of the WBNB token if the market is VBNB
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
4 import { SafeERC20Upgradeable, IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
213 * @custom:error Throw InsufficientBalance if amount_ is greater than the available balance of the token in the contract
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
10 // https://github.com/0xProject/exchange-v3/blob/aae46bef841bfd1cc31028f41793db4fe7197084/contracts/staking/contracts/src/libs/LibFixedMath.sol
105 x = (x * FIXED_1) / int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000); // / e ^ -0.5
110 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000); // / e ^ -0.25
115 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d); // / e ^ -0.125
136 r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16
There is one instance of this issue:
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
140 function exp(int256 x) internal pure returns (int256 r) {
GitHub: [140]
There are 7 instances of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit updation
47 /// @notice Emitted on updation of initial balance for token
// @audit intrest
74 /// @notice Error thrown when intrest accrue is called for not initialized token
File: contracts/Tokens/Prime/PrimeStorage.sol
// @audit maxmimum
42 /// @notice maxmimum BPS = 100%
// @audit metadata
45 /// @notice Mapping to get prime token's metadata
// @audit numberator
72 /// @notice numberator of alpha. Ex: if alpha is 0.5 then this will be 1
// @audit adress
108 /// @notice unreleased income from PSR that's already distributed to prime holders
109 /// @dev mapping of asset adress => amount
// @audit adress
112 /// @notice unreleased income from PLP that's already distributed to prime holders
113 /// @dev mapping of asset adress => amount
GitHub: [42, 45, 72, 108, 112]
The functions below take in an unbounded array, and make function calls for entries in the array. While the function will revert if it eventually runs out of gas, it may be a nicer user experience to require()
that the length of the array is below some reasonable maximum, so that the user doesn't have to use up a full transaction's gas only to see that the transaction reverts.
There are 6 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
204 for (uint256 i = 0; i < users.length; ) {
205 address user = users[i];
206
207 if (!tokens[user].exists) revert UserHasNoPrimeToken();
208 if (isScoreUpdated[nextScoreUpdateRoundId][user]) continue;
209
210 address[] storage _allMarkets = allMarkets;
211 for (uint256 j = 0; j < _allMarkets.length; ) {
212 address market = _allMarkets[j];
213 _executeBoost(user, market);
214 _updateScore(user, market);
215
216 unchecked {
217 j++;
218 }
219 }
220
221 pendingScoreUpdates--;
222 isScoreUpdated[nextScoreUpdateRoundId][user] = true;
223
224 unchecked {
225 i++;
226 }
227
228 emit UserScoreUpdated(user);
229 }
335 for (uint256 i = 0; i < users.length; ) {
336 Token storage userToken = tokens[users[i]];
337 if (userToken.exists && !userToken.isIrrevocable) {
338 _upgrade(users[i]);
339 } else {
340 _mint(true, users[i]);
341 _initializeMarkets(users[i]);
342 }
343
344 unchecked {
345 i++;
346 }
347 }
349 for (uint256 i = 0; i < users.length; ) {
350 _mint(false, users[i]);
351 _initializeMarkets(users[i]);
352 delete stakedAt[users[i]];
353
354 unchecked {
355 i++;
356 }
357 }
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
103 for (uint256 i; i < numTokens; ) {
104 _initializeToken(tokens_[i]);
105 _setTokenDistributionSpeed(tokens_[i], distributionSpeeds_[i]);
106
107 unchecked {
108 ++i;
109 }
110 }
119 for (uint256 i; i < tokens_.length; ) {
120 _initializeToken(tokens_[i]);
121
122 unchecked {
123 ++i;
124 }
125 }
161 for (uint256 i; i < numTokens; ) {
162 _ensureTokenInitialized(tokens_[i]);
163 _setTokenDistributionSpeed(tokens_[i], distributionSpeeds_[i]);
164
165 unchecked {
166 ++i;
167 }
168 }
According to the Solidity Style Guide, Non-external
/public
function names should begin with an underscore
There are 9 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
904 function isEligible(uint256 amount) internal view returns (bool) {
GitHub: [904]
File: contracts/Tokens/Prime/libs/FixedMath.sol
22 function toFixed(uint256 n, uint256 d) internal pure returns (int256) {
34 function uintDiv(uint256 u, int256 f) internal pure returns (uint256) {
46 function uintMul(uint256 u, int256 f) internal pure returns (uint256) {
53 function ln(int256 x) internal pure returns (int256 r) {
58 function exp(int256 x) internal pure returns (int256 r) {
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
51 function ln(int256 x) internal pure returns (int256 r) {
140 function exp(int256 x) internal pure returns (int256 r) {
File: contracts/Tokens/Prime/libs/Scores.sol
22 function calculateScore(
23 uint256 xvs,
24 uint256 capital,
25 uint256 alphaNumerator,
26 uint256 alphaDenominator
27 ) internal pure returns (uint256) {
GitHub: [22]
According to the Solidity Style Guide, Non-external
/public
variable names should begin with an underscore
There are 4 instances of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
70 address[] internal allMarkets;
79 address internal xvsVault;
82 address internal xvsVaultRewardToken;
85 uint256 internal xvsVaultPoolId;
Index event fields make the field more quickly accessible to off-chain tools that parse events. This is especially useful when it comes to filtering based on an address. However, note that each index field costs extra gas during emission, so it's not necessarily best to index the maximum allowed per event (three fields). Where applicable, each event
should use three indexed
fields if there are three or more fields, and gas usage is not particularly of concern for the events in question. If there are fewer than three applicable fields, all of the applicable fields should be indexed.
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
36 event PrimeTokenUpdated(address oldPrimeToken, address newPrimeToken);
GitHub: [36]
Note that there may be cases where an event superficially appears to be used, but this is only because there are multiple definitions of the event in different files. In such cases, the event definition should be moved into a separate file. The instances below are the unused definitions.
There are 3 instances of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
48 event TokenInitialBalanceUpdated(address indexed token, uint256 balance);
51 event FundsTransferPaused();
54 event FundsTransferResumed();
Even assembly can benefit from using readable constants instead of hex/numeric literals
There are 91 instances of this issue:
see instances
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
// @audit 0x00000000000000000000000000000000000000000001c8464f76164760000000
73 if (x <= int256(0x00000000000000000000000000000000000000000001c8464f76164760000000)) {
// @audit 0x0000000000000000000000000000001000000000000000000000000000000000
74 r -= int256(0x0000000000000000000000000000001000000000000000000000000000000000); // - 32
// @audit 0x00000000000000000000000000000000000000000001c8464f76164760000000
75 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000000001c8464f76164760000000); // / e ^ -32
// @audit 0x00000000000000000000000000000000000000f1aaddd7742e90000000000000
78 if (x <= int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000)) {
// @audit 0x0000000000000000000000000000000800000000000000000000000000000000
79 r -= int256(0x0000000000000000000000000000000800000000000000000000000000000000); // - 16
// @audit 0x00000000000000000000000000000000000000f1aaddd7742e90000000000000
80 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000); // / e ^ -16
// @audit 0x00000000000000000000000000000000000afe10820813d78000000000000000
83 if (x <= int256(0x00000000000000000000000000000000000afe10820813d78000000000000000)) {
// @audit 0x0000000000000000000000000000000400000000000000000000000000000000
84 r -= int256(0x0000000000000000000000000000000400000000000000000000000000000000); // - 8
// @audit 0x00000000000000000000000000000000000afe10820813d78000000000000000
85 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000afe10820813d78000000000000000); // / e ^ -8
// @audit 0x0000000000000000000000000000000002582ab704279ec00000000000000000
88 if (x <= int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000)) {
// @audit 0x0000000000000000000000000000000200000000000000000000000000000000
89 r -= int256(0x0000000000000000000000000000000200000000000000000000000000000000); // - 4
// @audit 0x0000000000000000000000000000000002582ab704279ec00000000000000000
90 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000); // / e ^ -4
// @audit 0x000000000000000000000000000000001152aaa3bf81cc000000000000000000
93 if (x <= int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000)) {
// @audit 0x0000000000000000000000000000000100000000000000000000000000000000
94 r -= int256(0x0000000000000000000000000000000100000000000000000000000000000000); // - 2
// @audit 0x000000000000000000000000000000001152aaa3bf81cc000000000000000000
95 x = (x * FIXED_1) / int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000); // / e ^ -2
// @audit 0x000000000000000000000000000000002f16ac6c59de70000000000000000000
98 if (x <= int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000)) {
// @audit 0x0000000000000000000000000000000080000000000000000000000000000000
99 r -= int256(0x0000000000000000000000000000000080000000000000000000000000000000); // - 1
// @audit 0x000000000000000000000000000000002f16ac6c59de70000000000000000000
100 x = (x * FIXED_1) / int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000); // / e ^ -1
// @audit 0x000000000000000000000000000000004da2cbf1be5828000000000000000000
103 if (x <= int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000)) {
// @audit 0x0000000000000000000000000000000040000000000000000000000000000000
104 r -= int256(0x0000000000000000000000000000000040000000000000000000000000000000); // - 0.5
// @audit 0x000000000000000000000000000000004da2cbf1be5828000000000000000000
105 x = (x * FIXED_1) / int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000); // / e ^ -0.5
// @audit 0x0000000000000000000000000000000063afbe7ab2082c000000000000000000
108 if (x <= int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000)) {
// @audit 0x0000000000000000000000000000000020000000000000000000000000000000
109 r -= int256(0x0000000000000000000000000000000020000000000000000000000000000000); // - 0.25
// @audit 0x0000000000000000000000000000000063afbe7ab2082c000000000000000000
110 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000); // / e ^ -0.25
// @audit 0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d
113 if (x <= int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) {
// @audit 0x0000000000000000000000000000000010000000000000000000000000000000
114 r -= int256(0x0000000000000000000000000000000010000000000000000000000000000000); // - 0.125
// @audit 0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d
115 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d); // / e ^ -0.125
// @audit 0x100000000000000000000000000000000
122 r += (z * (0x100000000000000000000000000000000 - y)) / 0x100000000000000000000000000000000;
// @audit 0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
// @audit 0x200000000000000000000000000000000
124 r += (z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y)) / 0x200000000000000000000000000000000;
// @audit 0x099999999999999999999999999999999
// @audit 0x300000000000000000000000000000000
126 r += (z * (0x099999999999999999999999999999999 - y)) / 0x300000000000000000000000000000000;
// @audit 0x092492492492492492492492492492492
// @audit 0x400000000000000000000000000000000
128 r += (z * (0x092492492492492492492492492492492 - y)) / 0x400000000000000000000000000000000;
// @audit 0x08e38e38e38e38e38e38e38e38e38e38e
// @audit 0x500000000000000000000000000000000
130 r += (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) / 0x500000000000000000000000000000000;
// @audit 0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b
// @audit 0x600000000000000000000000000000000
132 r += (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) / 0x600000000000000000000000000000000;
// @audit 0x089d89d89d89d89d89d89d89d89d89d89
// @audit 0x700000000000000000000000000000000
134 r += (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) / 0x700000000000000000000000000000000;
// @audit 0x088888888888888888888888888888888
// @audit 0x800000000000000000000000000000000
136 r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16
// @audit 0x0000000000000000000000000000000010000000000000000000000000000000
162 z = y = x % 0x0000000000000000000000000000000010000000000000000000000000000000;
// @audit 0x10e1b3be415a0000
164 r += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)
// @audit 0x05a0913f6b1e0000
166 r += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)
// @audit 0x0168244fdac78000
168 r += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)
// @audit 0x004807432bc18000
170 r += z * 0x004807432bc18000; // add y^05 * (20! / 05!)
// @audit 0x000c0135dca04000
172 r += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)
// @audit 0x0001b707b1cdc000
174 r += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)
// @audit 0x000036e0f639b800
176 r += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)
// @audit 0x00000618fee9f800
178 r += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)
// @audit 0x0000009c197dcc00
180 r += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)
// @audit 0x0000000e30dce400
182 r += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)
// @audit 0x000000012ebd1300
184 r += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)
// @audit 0x0000000017499f00
186 r += z * 0x0000000017499f00; // add y^13 * (20! / 13!)
// @audit 0x0000000001a9d480
188 r += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)
// @audit 0x00000000001c6380
190 r += z * 0x00000000001c6380; // add y^15 * (20! / 15!)
// @audit 0x000000000001c638
192 r += z * 0x000000000001c638; // add y^16 * (20! / 16!)
// @audit 0x0000000000001ab8
194 r += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)
// @audit 0x000000000000017c
196 r += z * 0x000000000000017c; // add y^18 * (20! / 18!)
// @audit 0x0000000000000014
198 r += z * 0x0000000000000014; // add y^19 * (20! / 19!)
// @audit 0x0000000000000001
200 r += z * 0x0000000000000001; // add y^20 * (20! / 20!)
// @audit 0x21c3677c82b40000
201 r = r / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
// @audit 0x0000000000000000000000000000001000000000000000000000000000000000
206 if ((x & int256(0x0000000000000000000000000000001000000000000000000000000000000000)) != 0) {
// @audit 0x00000000000000000000000000000000000000f1aaddd7742e56d32fb9f99744
208 (r * int256(0x00000000000000000000000000000000000000f1aaddd7742e56d32fb9f99744)) /
// @audit 0x0000000000000000000000000043cbaf42a000812488fc5c220ad7b97bf6e99e
209 int256(0x0000000000000000000000000043cbaf42a000812488fc5c220ad7b97bf6e99e); // * e ^ -32
// @audit 0x0000000000000000000000000000000800000000000000000000000000000000
212 if ((x & int256(0x0000000000000000000000000000000800000000000000000000000000000000)) != 0) {
// @audit 0x00000000000000000000000000000000000afe10820813d65dfe6a33c07f738f
214 (r * int256(0x00000000000000000000000000000000000afe10820813d65dfe6a33c07f738f)) /
// @audit 0x000000000000000000000000000005d27a9f51c31b7c2f8038212a0574779991
215 int256(0x000000000000000000000000000005d27a9f51c31b7c2f8038212a0574779991); // * e ^ -16
// @audit 0x0000000000000000000000000000000400000000000000000000000000000000
218 if ((x & int256(0x0000000000000000000000000000000400000000000000000000000000000000)) != 0) {
// @audit 0x0000000000000000000000000000000002582ab704279e8efd15e0265855c47a
220 (r * int256(0x0000000000000000000000000000000002582ab704279e8efd15e0265855c47a)) /
// @audit 0x0000000000000000000000000000001b4c902e273a58678d6d3bfdb93db96d02
221 int256(0x0000000000000000000000000000001b4c902e273a58678d6d3bfdb93db96d02); // * e ^ -8
// @audit 0x0000000000000000000000000000000200000000000000000000000000000000
224 if ((x & int256(0x0000000000000000000000000000000200000000000000000000000000000000)) != 0) {
// @audit 0x000000000000000000000000000000001152aaa3bf81cb9fdb76eae12d029571
226 (r * int256(0x000000000000000000000000000000001152aaa3bf81cb9fdb76eae12d029571)) /
// @audit 0x00000000000000000000000000000003b1cc971a9bb5b9867477440d6d157750
227 int256(0x00000000000000000000000000000003b1cc971a9bb5b9867477440d6d157750); // * e ^ -4
// @audit 0x0000000000000000000000000000000100000000000000000000000000000000
230 if ((x & int256(0x0000000000000000000000000000000100000000000000000000000000000000)) != 0) {
// @audit 0x000000000000000000000000000000002f16ac6c59de6f8d5d6f63c1482a7c86
232 (r * int256(0x000000000000000000000000000000002f16ac6c59de6f8d5d6f63c1482a7c86)) /
// @audit 0x000000000000000000000000000000015bf0a8b1457695355fb8ac404e7a79e3
233 int256(0x000000000000000000000000000000015bf0a8b1457695355fb8ac404e7a79e3); // * e ^ -2
// @audit 0x0000000000000000000000000000000080000000000000000000000000000000
236 if ((x & int256(0x0000000000000000000000000000000080000000000000000000000000000000)) != 0) {
// @audit 0x000000000000000000000000000000004da2cbf1be5827f9eb3ad1aa9866ebb3
238 (r * int256(0x000000000000000000000000000000004da2cbf1be5827f9eb3ad1aa9866ebb3)) /
// @audit 0x00000000000000000000000000000000d3094c70f034de4b96ff7d5b6f99fcd8
239 int256(0x00000000000000000000000000000000d3094c70f034de4b96ff7d5b6f99fcd8); // * e ^ -1
// @audit 0x0000000000000000000000000000000040000000000000000000000000000000
242 if ((x & int256(0x0000000000000000000000000000000040000000000000000000000000000000)) != 0) {
// @audit 0x0000000000000000000000000000000063afbe7ab2082ba1a0ae5e4eb1b479dc
244 (r * int256(0x0000000000000000000000000000000063afbe7ab2082ba1a0ae5e4eb1b479dc)) /
// @audit 0x00000000000000000000000000000000a45af1e1f40c333b3de1db4dd55f29a7
245 int256(0x00000000000000000000000000000000a45af1e1f40c333b3de1db4dd55f29a7); // * e ^ -0.5
// @audit 0x0000000000000000000000000000000020000000000000000000000000000000
248 if ((x & int256(0x0000000000000000000000000000000020000000000000000000000000000000)) != 0) {
// @audit 0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d
250 (r * int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) /
// @audit 0x00000000000000000000000000000000910b022db7ae67ce76b441c27035c6a1
251 int256(0x00000000000000000000000000000000910b022db7ae67ce76b441c27035c6a1); // * e ^ -0.25
// @audit 0x0000000000000000000000000000000010000000000000000000000000000000
254 if ((x & int256(0x0000000000000000000000000000000010000000000000000000000000000000)) != 0) {
// @audit 0x00000000000000000000000000000000783eafef1c0a8f3978c7f81824d62ebf
256 (r * int256(0x00000000000000000000000000000000783eafef1c0a8f3978c7f81824d62ebf)) /
// @audit 0x0000000000000000000000000000000088415abbe9a76bead8d00cf112e4d4a8
257 int256(0x0000000000000000000000000000000088415abbe9a76bead8d00cf112e4d4a8); // * e ^ -0.125
GitHub: [73, 74, 75, 78, 79, 80, 83, 84, 85, 88, 89, 90, 93, 94, 95, 98, 99, 100, 103, 104, 105, 108, 109, 110, 113, 114, 115, 122, 122, 124, 124, 126, 126, 128, 128, 130, 130, 132, 132, 134, 134, 136, 136, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 201, 206, 208, 209, 212, 214, 215, 218, 220, 221, 224, 226, 227, 230, 232, 233, 236, 238, 239, 242, 244, 245, 248, 250, 251, 254, 256, 257]
Consider moving to solidity version 0.8.18 or later, and using named mappings to make it easier to understand the purpose of each mapping
There are 11 instances of this issue:
see instances
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
21 mapping(address => uint256) public tokenDistributionSpeeds;
24 mapping(address => uint256) public lastAccruedBlock;
27 mapping(address => uint256) public tokenAmountAccrued;
File: contracts/Tokens/Prime/PrimeStorage.sol
46 mapping(address => Token) public tokens;
61 mapping(address => uint256) public stakedAt;
64 mapping(address => Market) public markets;
67 mapping(address => mapping(address => Interest)) public interests;
88 mapping(uint256 => mapping(address => bool)) public isScoreUpdated;
100 mapping(address => address) public vTokenForAsset;
110 mapping(address => uint256) public unreleasedPSRIncome;
114 mapping(address => uint256) public unreleasedPLPIncome;
This project is using specific package versions which are vulnerable to the specific CVEs listed below. Consider switching to more recent versions of these packages that don't have these vulnerabilities.
There are 3 instances of this issue:
CVE-2023-40014 - MEDIUM - (@openzeppelin/contracts >=4.0.0 <4.9.3
): OpenZeppelin Contracts is a library for secure smart contract development. Starting in version 4.0.0 and prior to version 4.9.3, contracts using ERC2771Context
along with a custom trusted forwarder may see _msgSender
return address(0)
in calls that originate from the forwarder with calldata shorter than 20 bytes. This combination of circumstances does not appear to be common, in particular it is not the case for MinimalForwarder
from OpenZeppelin Contracts, or any deployed forwarder the team is aware of, given that the signer address is appended to all calls that originate from these forwarders. The problem has been patched in v4.9.3.
CVE-2023-34459 - MEDIUM - (@openzeppelin/contracts >=4.7.0 <4.9.2
): OpenZeppelin Contracts is a library for smart contract development. Starting in version 4.7.0 and prior to version 4.9.2, when the verifyMultiProof
, verifyMultiProofCalldata
, procesprocessMultiProof
, or processMultiProofCalldat
functions are in use, it is possible to construct merkle trees that allow forging a valid multiproof for an arbitrary set of leaves. A contract may be vulnerable if it uses multiproofs for verification and the merkle tree that is processed includes a node with value 0 at depth 1 (just under the root). This could happen inadvertedly for balanced trees with 3 leaves or less, if the leaves are not hashed. This could happen deliberately if a malicious tree builder includes such a node in the tree. A contract is not vulnerable if it uses single-leaf proving (verify
, verifyCalldata
, processProof
, or processProofCalldata
), or if it uses multiproofs with a known tree that has hashed leaves. Standard merkle trees produced or validated with the @openzeppelin/merkle-tree library are safe. The problem has been patched in version 4.9.2. Some workarounds are available. For those using multiproofs: When constructing merkle trees hash the leaves and do not insert empty nodes in your trees. Using the @openzeppelin/merkle-tree package eliminates this issue. Do not accept user-provided merkle roots without reconstructing at least the first level of the tree. Verify the merkle tree structure by reconstructing it from the leaves.
CVE-2023-34234 - MEDIUM - (@openzeppelin/contracts >=4.3.0 <4.9.1
): OpenZeppelin Contracts is a library for smart contract development. By frontrunning the creation of a proposal, an attacker can become the proposer and gain the ability to cancel it. The attacker can do this repeatedly to try to prevent a proposal from being proposed at all. This impacts the Governor
contract in v4.9.0 only, and the GovernorCompatibilityBravo
contract since v4.3.0. This problem has been patched in 4.9.1 by introducing opt-in frontrunning protection. Users are advised to upgrade. Users unable to upgrade may submit the proposal creation transaction to an endpoint with frontrunning protection as a workaround.
Gas totals use lower bounds of ranges and count two iterations of each for
-loop. All values above are runtime, not deployment, values; deployment values are listed in the individual issue descriptions. The table above as well as its gas numbers do not include any of the excluded findings.
A simple zero address check can be written in assembly to save some gas.
There are 10 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
104 if (_wbnb == address(0)) revert InvalidAddress();
105 if (_vbnb == address(0)) revert InvalidAddress();
143 if (_xvsVault == address(0)) revert InvalidAddress();
144 if (_xvsVaultRewardToken == address(0)) revert InvalidAddress();
145 if (_protocolShareReserve == address(0)) revert InvalidAddress();
146 if (_comptroller == address(0)) revert InvalidAddress();
147 if (_oracle == address(0)) revert InvalidAddress();
148 if (_primeLiquidityProvider == address(0)) revert InvalidAddress();
457 if (vToken == address(0)) revert MarketNotSupported();
GitHub: [104, 105, 143, 144, 145, 146, 147, 148, 457]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
345 if (address_ == address(0)) {
GitHub: [345]
To efficiently emit events, it's possible to utilize assembly by making use of scratch space and the free memory pointer. This approach has the advantage of potentially avoiding the costs associated with memory expansion.
However, it's important to note that in order to safely optimize this process, it is preferable to cache and restore the free memory pointer.
A good example of such practice can be seen in Solady's codebase.
There are 15 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
228 emit UserScoreUpdated(user);
241 emit AlphaUpdated(alphaNumerator, alphaDenominator, _alphaNumerator, _alphaDenominator);
308 emit MarketAdded(vToken, supplyMultiplier, borrowMultiplier);
320 emit MintLimitsUpdated(irrevocableLimit, revocableLimit, _irrevocableLimit, _revocableLimit);
462 emit UpdatedAssetsState(comptroller, asset);
694 emit InterestClaimed(user, vToken, amount);
718 emit Mint(user, isIrrevocable);
755 emit Burn(user);
771 emit TokenUpgraded(user);
GitHub: [228, 241, 308, 320, 462, 694, 718, 755, 771]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
180 emit PrimeTokenUpdated(prime, prime_);
202 emit TokenTransferredToPrime(token_, accruedAmount);
222 emit SweepToken(address(token_), to_, amount_);
267 emit TokensAccrued(token_, tokenAccrued);
300 emit TokenDistributionInitialized(token_);
324 emit TokenDistributionSpeedUpdated(token_, distributionSpeed_);
GitHub: [180, 202, 222, 267, 300, 324]
Using assembly { sstore(state.slot, addr)
instead of state = addr
can save gas.
There are 9 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
107 WBNB = _wbnb;
108 VBNB = _vbnb;
153 xvsVaultRewardToken = _xvsVaultRewardToken;
155 xvsVault = _xvsVault;
157 protocolShareReserve = _protocolShareReserve;
158 primeLiquidityProvider = _primeLiquidityProvider;
159 comptroller = _comptroller;
160 oracle = ResilientOracleInterface(_oracle);
GitHub: [107, 108, 153, 155, 157, 158, 159, 160]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
181 prime = prime_;
GitHub: [181]
Using assembly to check for zero can save gas by allowing more direct access to the evm and reducing some of the overhead associated with high-level operations in solidity.
There are 25 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
106 if (_blocksPerYear == 0) revert InvalidBlocksPerYear();
201 if (pendingScoreUpdates == 0) revert NoScoreUpdatesRequired();
202 if (nextScoreUpdateRoundId == 0) revert NoScoreUpdatesRequired();
377 } else if (stakedAt[user] == 0 && isAccountEligible && !tokens[user].exists) {
398 if (stakedAt[msg.sender] == 0) revert IneligibleToClaim();
479 if (stakedAt[user] == 0) return STAKING_PERIOD;
576 if (distributionIncome == 0) {
810 if (_alphaDenominator == 0 || _alphaNumerator > _alphaDenominator) {
1002 if (totalScore == 0) return (0, 0);
1007 if (totalCappedValue == 0) return (0, 0);
1012 supplyAPR = totalSupply == 0 ? 0 : ((userSupplyIncomeYearly * MAXIMUM_BPS) / totalSupply);
1013 borrowAPR = totalBorrow == 0 ? 0 : ((userBorrowIncomeYearly * MAXIMUM_BPS) / totalBorrow);
GitHub: [106, 201, 202, 377, 398, 479, 576, 810, 1002, 1007, 1012, 1013]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
335 if (lastBlockAccrued == 0) {
GitHub: [335]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
145 if (x == 0) {
206 if ((x & int256(0x0000000000000000000000000000001000000000000000000000000000000000)) != 0) {
212 if ((x & int256(0x0000000000000000000000000000000800000000000000000000000000000000)) != 0) {
218 if ((x & int256(0x0000000000000000000000000000000400000000000000000000000000000000)) != 0) {
224 if ((x & int256(0x0000000000000000000000000000000200000000000000000000000000000000)) != 0) {
230 if ((x & int256(0x0000000000000000000000000000000100000000000000000000000000000000)) != 0) {
236 if ((x & int256(0x0000000000000000000000000000000080000000000000000000000000000000)) != 0) {
242 if ((x & int256(0x0000000000000000000000000000000040000000000000000000000000000000)) != 0) {
248 if ((x & int256(0x0000000000000000000000000000000020000000000000000000000000000000)) != 0) {
254 if ((x & int256(0x0000000000000000000000000000000010000000000000000000000000000000)) != 0) {
GitHub: [145, 206, 212, 218, 224, 230, 236, 242, 248, 254]
File: contracts/Tokens/Prime/libs/Scores.sol
43 // If any side is 0, exit early
If the old value is equal to the new value, not re-storing the value will avoid a Gsreset (2900 gas), potentially at the expense of a Gcoldsload (2100 gas) or a Gwarmaccess (100 gas)
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
181 prime = prime_;
GitHub: [181]
[Gβ06] State variables should be cached in stack variables rather than re-reading them from storage
The instances below point to the second+ access of a state variable within a function. Caching of a state variable replaces each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.
There are 29 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit `pendingScoreUpdates` accessed 2 times at lines: 201, 221
221 pendingScoreUpdates--;
// @audit `markets` accessed 3 times at lines: 265, 271, 272
272 markets[market].borrowMultiplier,
// @audit `stakedAt` accessed 2 times at lines: 375, 377
377 } else if (stakedAt[user] == 0 && isAccountEligible && !tokens[user].exists) {
// @audit `tokens` accessed 5 times at lines: 369, 370, 375, 377, 379
379 } else if (tokens[user].exists && isAccountEligible) {
// @audit `stakedAt` accessed 2 times at lines: 398, 399
399 if (block.timestamp - stakedAt[msg.sender] < STAKING_PERIOD) revert WaitMoreTime();
// @audit `comptroller` accessed 2 times at lines: 454, 462
462 emit UpdatedAssetsState(comptroller, asset);
// @audit `stakedAt` accessed 2 times at lines: 479, 481
481 uint256 totalTimeStaked = block.timestamp - stakedAt[user];
// @audit `STAKING_PERIOD` accessed 3 times at lines: 479, 482, 483
483 return STAKING_PERIOD - totalTimeStaked;
// @audit `markets` accessed 4 times at lines: 555, 584, 585, 588
588 markets[vToken].rewardIndex = markets[vToken].rewardIndex + delta;
// @audit `markets` accessed 2 times at lines: 629, 633
633 markets[market].sumOfMembersScore = markets[market].sumOfMembersScore + score;
// @audit `totalIrrevocable` accessed 2 times at lines: 711, 716
// @audit `totalRevocable` accessed 2 times at lines: 713, 716
716 if (totalIrrevocable > irrevocableLimit || totalRevocable > revocableLimit) revert InvalidLimit();
// @audit `tokens` accessed 2 times at lines: 726, 744
744 if (tokens[user].isIrrevocable) {
// @audit `totalIrrevocable` accessed 2 times at lines: 766, 769
769 if (totalIrrevocable > irrevocableLimit) revert InvalidLimit();
// @audit `markets` accessed 2 times at lines: 780, 786
786 interests[vToken][user].rewardIndex = markets[vToken].rewardIndex;
// @audit `markets` accessed 2 times at lines: 795, 800
800 markets[market].sumOfMembersScore = markets[market].sumOfMembersScore - interests[market][user].score + score;
// @audit `totalScoreUpdatesRequired` accessed 2 times at lines: 828, 828
828 if (totalScoreUpdatesRequired > 0) totalScoreUpdatesRequired--;
// @audit `pendingScoreUpdates` accessed 2 times at lines: 830, 831
831 pendingScoreUpdates--;
// @audit `MAXIMUM_XVS_CAP` accessed 2 times at lines: 855, 856
856 return MAXIMUM_XVS_CAP;
// @audit `markets` accessed 2 times at lines: 881, 882
882 uint256 supplyCapUSD = (xvsPrice * ((xvs * markets[market].supplyMultiplier) / EXP_SCALE)) / EXP_SCALE;
// @audit `EXP_SCALE` accessed 6 times at lines: 881, 881, 882, 882, 885, 886
886 uint256 borrowUSD = (tokenPrice * borrow) / EXP_SCALE;
// @audit `interests` accessed 2 times at lines: 919, 920
920 uint256 score = interests[vToken][user].score;
// @audit `EXP_SCALE` accessed 2 times at lines: 949, 950
950 EXP_SCALE);
// @audit `BLOCKS_PER_YEAR` accessed 2 times at lines: 974, 978
978 amount += BLOCKS_PER_YEAR * totalIncomePerBlockFromPLP;
// @audit `MAXIMUM_BPS` accessed 2 times at lines: 1012, 1013
1013 borrowAPR = totalBorrow == 0 ? 0 : ((userBorrowIncomeYearly * MAXIMUM_BPS) / totalBorrow);
GitHub: [221, 272, 377, 379, 399, 462, 481, 483, 588, 633, 716, 716, 744, 769, 786, 800, 828, 831, 856, 882, 886, 920, 950, 978, 1013]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit `prime` accessed 2 times at lines: 193, 204
204 IERC20Upgradeable(token_).safeTransfer(prime, accruedAmount);
// @audit `MAX_DISTRIBUTION_SPEED` accessed 2 times at lines: 311, 312
312 revert InvalidDistributionSpeed(distributionSpeed_, MAX_DISTRIBUTION_SPEED);
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
// @audit `FIXED_1` accessed 19 times at lines: 58, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 121, 123, 125, 127, 129, 131, 133, 135
135 z = (z * w) / FIXED_1; // add y^13 / 13 - y^14 / 14
// @audit `FIXED_1` accessed 21 times at lines: 146, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201
201 r = r / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
Mark data types as calldata
instead of memory
where possible. This makes it so that the data is not automatically loaded into memory. If the data passed into the function does not need to be changed (like updating values in an array), it can be passed in as calldata
. The one exception to this is if the argument must later be passed into another function that takes an argument that specifies memory
storage.
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit `users` can be calldata
200 function updateScores(address[] memory users) external {
GitHub: [200]
Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (100 gas), to check for contract existence for external function calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low level calls never check for contract existence.
There are 36 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
184 market: IVToken(market).underlying(),
292 bool isMarketExist = InterfaceComptroller(comptroller).markets(vToken);
498 uint256 borrow = vToken.borrowBalanceStored(user);
499 uint256 exchangeRate = vToken.exchangeRateStored();
500 uint256 balanceOfAccount = vToken.balanceOf(user);
561 uint256 totalIncomeUnreleased = IProtocolShareReserve(protocolShareReserve).getUnreleasedFunds(
562 comptroller,
563 IProtocolShareReserve.Schema.SPREAD_PRIME_CORE,
564 address(this),
565 underlying
566 );
570 _primeLiquidityProvider.accrueTokens(underlying);
571 uint256 totalAccruedInPLP = _primeLiquidityProvider.tokenAmountAccrued(underlying);
651 uint256 borrow = vToken.borrowBalanceStored(user);
652 uint256 exchangeRate = vToken.exchangeRateStored();
653 uint256 balanceOfAccount = vToken.balanceOf(user);
656 address xvsToken = IXVSVault(xvsVault).xvsAddress();
657 oracle.updateAssetPrice(xvsToken);
658 oracle.updatePrice(market);
661 capital = capital * (10 ** (18 - vToken.decimals()));
682 if (amount > asset.balanceOf(address(this))) {
685 IProtocolShareReserve(protocolShareReserve).releaseFunds(comptroller, assets);
686 if (amount > asset.balanceOf(address(this))) {
687 IPrimeLiquidityProvider(primeLiquidityProvider).releaseFunds(address(asset));
692 asset.safeTransfer(user, amount);
841 (uint256 xvs, , uint256 pendingWithdrawals) = IXVSVault(xvsVault).getUserInfo(
842 xvsVaultRewardToken,
843 xvsVaultPoolId,
844 user
845 );
878 address xvsToken = IXVSVault(xvsVault).xvsAddress();
880 uint256 xvsPrice = oracle.getPrice(xvsToken);
884 uint256 tokenPrice = oracle.getUnderlyingPrice(market);
934 return IVToken(vToken).underlying();
949 return ((((market.totalBorrows() * market.borrowRatePerBlock()) / EXP_SCALE) * market.reserveFactorMantissa()) /
959 IProtocolShareReserve(protocolShareReserve).getPercentageDistribution(
960 address(this),
961 IProtocolShareReserve.Schema.SPREAD_PRIME_CORE
962 );
973 IProtocolShareReserve(protocolShareReserve).MAX_PERCENT();
976 uint256 totalIncomePerBlockFromPLP = IPrimeLiquidityProvider(primeLiquidityProvider)
977 .getEffectiveDistributionSpeed(_getUnderlying(vToken));
GitHub: [184, 292, 498, 499, 500, 561, 570, 571, 651, 652, 653, 656, 657, 658, 661, 682, 685, 686, 687, 692, 841, 878, 880, 884, 934, 949, 949, 949, 959, 973, 976]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
204 IERC20Upgradeable(token_).safeTransfer(prime, accruedAmount);
217 uint256 balance = token_.balanceOf(address(this));
224 token_.safeTransfer(to_, amount_);
234 uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this));
259 uint256 balance = IERC20Upgradeable(token_).balanceOf(address(this));
Emitting an event has an overhead of 375 gas, which will be incurred on every iteration of the loop. It is cheaper to emit
only once after the loop has finished.
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
228 emit UserScoreUpdated(user);
GitHub: [228]
The function calls in solidity are expensive. If the same result of the same function calls are to be used several times, the result should be cached to reduce the gas consumption of repeated calls.
There are 7 instances of this issue:
see instances
File: contracts/Tokens/Prime/Prime.sol
// @audit `InvalidAddress()` is called 2 times at lines: 104, 105
103 constructor(address _wbnb, address _vbnb, uint256 _blocksPerYear) {
// @audit `InvalidAddress()` is called 6 times at lines: 143, 144, 145, 146, 147, 148
130 function initialize(
131 address _xvsVault,
132 address _xvsVaultRewardToken,
133 uint256 _xvsVaultPoolId,
134 uint128 _alphaNumerator,
135 uint128 _alphaDenominator,
136 address _accessControlManager,
137 address _protocolShareReserve,
138 address _primeLiquidityProvider,
139 address _comptroller,
140 address _oracle,
141 uint256 _loopsLimit
142 ) external virtual initializer {
// @audit `NoScoreUpdatesRequired()` is called 2 times at lines: 201, 202
200 function updateScores(address[] memory users) external {
// @audit `_initializeMarkets(users[i])` is called 2 times at lines: 341, 351
331 function issue(bool isIrrevocable, address[] calldata users) external {
// @audit `_accrueInterestAndUpdateScore(user)` is called 2 times at lines: 371, 380
365 function xvsUpdated(address user) external {
// @audit `asset.balanceOf(address(this))` is called 2 times at lines: 682, 686
672 function _claimInterest(address vToken, address user) internal returns (uint256) {
GitHub: [105, 148, 202, 351, 380, 686]
File: contracts/Tokens/Prime/libs/FixedMath.sol
// @audit `n.toInt256()` is called 4 times at lines: 23, 25, 23, 25
22 function toFixed(uint256 n, uint256 d) internal pure returns (int256) {
GitHub: [25]
The compiler uses opcodes GT
and ISZERO
for solidity code that uses >
, but only requires LT
for >=
, which saves 3 gas. If <
is being used, the condition can be inverted.
There are 43 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
178 for (uint256 i = 0; i < _allMarkets.length; ) {
204 for (uint256 i = 0; i < users.length; ) {
211 for (uint256 j = 0; j < _allMarkets.length; ) {
246 for (uint256 i = 0; i < allMarkets.length; ) {
318 if (_irrevocableLimit < totalIrrevocable || _revocableLimit < totalRevocable) revert InvalidLimit();
335 for (uint256 i = 0; i < users.length; ) {
349 for (uint256 i = 0; i < users.length; ) {
375 } else if (!isAccountEligible && !tokens[user].exists && stakedAt[user] > 0) {
399 if (block.timestamp - stakedAt[msg.sender] < STAKING_PERIOD) revert WaitMoreTime();
482 if (totalTimeStaked < STAKING_PERIOD) {
584 if (markets[vToken].sumOfMembersScore > 0) {
609 for (uint256 i = 0; i < _allMarkets.length; ) {
625 for (uint256 i = 0; i < _allMarkets.length; ) {
682 if (amount > asset.balanceOf(address(this))) {
686 if (amount > asset.balanceOf(address(this))) {
716 if (totalIrrevocable > irrevocableLimit || totalRevocable > revocableLimit) revert InvalidLimit();
730 for (uint256 i = 0; i < _allMarkets.length; ) {
769 if (totalIrrevocable > irrevocableLimit) revert InvalidLimit();
810 if (_alphaDenominator == 0 || _alphaNumerator > _alphaDenominator) {
828 if (totalScoreUpdatesRequired > 0) totalScoreUpdatesRequired--;
830 if (pendingScoreUpdates > 0 && !isScoreUpdated[nextScoreUpdateRoundId][user]) {
855 if (xvs > MAXIMUM_XVS_CAP) {
889 supply = supplyUSD > 0 ? (supply * supplyCapUSD) / supplyUSD : 0;
893 borrow = borrowUSD > 0 ? (borrow * borrowCapUSD) / borrowUSD : 0;
GitHub: [178, 204, 211, 246, 318, 318, 335, 349, 375, 399, 482, 584, 609, 625, 682, 686, 716, 716, 730, 769, 810, 828, 830, 855, 889, 893]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
103 for (uint256 i; i < numTokens; ) {
119 for (uint256 i; i < tokens_.length; ) {
161 for (uint256 i; i < numTokens; ) {
218 if (amount_ > balance) {
237 if (balance - accrued > 0) {
257 if (deltaBlocks > 0) {
262 if (distributionSpeed > 0 && balanceDiff > 0) {
291 if (initializedBlock > 0) {
311 if (distributionSpeed_ > MAX_DISTRIBUTION_SPEED) {
GitHub: [103, 119, 161, 218, 237, 257, 262, 262, 291, 311]
File: contracts/Tokens/Prime/libs/FixedMath.sol
23 if (d.toInt256() < n.toInt256()) revert InvalidFraction(n, d);
35 if (f < 0) revert InvalidFixedPoint();
47 if (f < 0) revert InvalidFixedPoint();
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
52 if (x > LN_MAX_VAL) {
141 if (x < EXP_MIN_VAL) {
148 if (x > EXP_MAX_VAL) {
File: contracts/Tokens/Prime/libs/Scores.sol
50 if (xvs == capital) return xvs;
51
52 bool lessxvsThanCapital = xvs < capital;
GitHub: [50]
Not inlining costs 20 to 40 gas because of two extra JUMP
instructions and additional stack operations needed for function calls.
There are 6 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
762 function _upgrade(address user) internal {
827 function _updateRoundAfterTokenBurned(address user) internal {
904 function isEligible(uint256 amount) internal view returns (bool) {
947 function _incomePerBlock(address vToken) internal view returns (uint256) {
957 function _distributionPercentage() internal view returns (uint256) {
970 function _incomeDistributionYearly(address vToken) internal view returns (uint256 amount) {
GitHub: [762, 827, 904, 947, 957, 970]
The overheads outlined below are PER LOOP, excluding the first loop
- storage arrays incur a Gwarmaccess (100 gas)
- memory arrays use
MLOAD
(3 gas) - calldata arrays use
CALLDATALOAD
(3 gas)
There are 10 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
178 for (uint256 i = 0; i < _allMarkets.length; ) {
204 for (uint256 i = 0; i < users.length; ) {
211 for (uint256 j = 0; j < _allMarkets.length; ) {
246 for (uint256 i = 0; i < allMarkets.length; ) {
335 for (uint256 i = 0; i < users.length; ) {
349 for (uint256 i = 0; i < users.length; ) {
609 for (uint256 i = 0; i < _allMarkets.length; ) {
625 for (uint256 i = 0; i < _allMarkets.length; ) {
730 for (uint256 i = 0; i < _allMarkets.length; ) {
GitHub: [178, 204, 211, 246, 335, 349, 609, 625, 730]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
119 for (uint256 i; i < tokens_.length; ) {
GitHub: [119]
Use uint256(1)
and uint256(2)
for true
/false
to avoid a Gwarmaccess (100 gas), and to avoid Gsset (20000 gas) when changing from false
to true
, after having been true
in the past. See source.
There is one instance of this issue:
File: contracts/Tokens/Prime/PrimeStorage.sol
88 mapping(uint256 => mapping(address => bool)) public isScoreUpdated;
GitHub: [88]
The instances below point to the second+ access of a value inside a mapping/array, within a function. Caching a mapping's value in a local storage
or calldata
variable when the value is accessed multiple times, saves ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations. Caching an array's struct avoids recalculating the array offsets into memory/calldata
There are 39 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit markets[market] also accessed at 265
271 markets[market].supplyMultiplier,
// @audit markets[market] also accessed at 265, 271
272 markets[market].borrowMultiplier,
// @audit markets[market] also accessed at 265, 271, 272
276 markets[market].supplyMultiplier = supplyMultiplier;
// @audit markets[market] also accessed at 265, 271, 272, 276
277 markets[market].borrowMultiplier = borrowMultiplier;
// @audit markets[vToken] also accessed at 290
295 markets[vToken].rewardIndex = 0;
// @audit markets[vToken] also accessed at 290, 295
296 markets[vToken].supplyMultiplier = supplyMultiplier;
// @audit markets[vToken] also accessed at 290, 295, 296
297 markets[vToken].borrowMultiplier = borrowMultiplier;
// @audit markets[vToken] also accessed at 290, 295, 296, 297
298 markets[vToken].sumOfMembersScore = 0;
// @audit markets[vToken] also accessed at 290, 295, 296, 297, 298
299 markets[vToken].exists = true;
// @audit tokens[user] also accessed at 369
370 if (tokens[user].isIrrevocable) {
// @audit tokens[user] also accessed at 369, 370
375 } else if (!isAccountEligible && !tokens[user].exists && stakedAt[user] > 0) {
// @audit stakedAt[user] also accessed at 375
376 stakedAt[user] = 0;
// @audit stakedAt[user] also accessed at 375, 376
// @audit tokens[user] also accessed at 369, 370, 375
377 } else if (stakedAt[user] == 0 && isAccountEligible && !tokens[user].exists) {
// @audit stakedAt[user] also accessed at 375, 376, 377
378 stakedAt[user] = block.timestamp;
// @audit tokens[user] also accessed at 369, 370, 375, 377
379 } else if (tokens[user].exists && isAccountEligible) {
// @audit stakedAt[msg.sender] also accessed at 398
399 if (block.timestamp - stakedAt[msg.sender] < STAKING_PERIOD) revert WaitMoreTime();
// @audit stakedAt[msg.sender] also accessed at 398, 399
401 stakedAt[msg.sender] = 0;
// @audit stakedAt[user] also accessed at 479
481 uint256 totalTimeStaked = block.timestamp - stakedAt[user];
// @audit unreleasedPSRIncome[underlying] also accessed at 568
580 unreleasedPSRIncome[underlying] = totalIncomeUnreleased;
// @audit unreleasedPLPIncome[underlying] also accessed at 572
581 unreleasedPLPIncome[underlying] = totalAccruedInPLP;
// @audit markets[vToken] also accessed at 555
584 if (markets[vToken].sumOfMembersScore > 0) {
// @audit markets[vToken] also accessed at 555, 584
585 delta = ((distributionIncome * EXP_SCALE) / markets[vToken].sumOfMembersScore);
// @audit markets[vToken] also accessed at 555, 584, 585
// @audit markets[vToken] also accessed at 555, 584, 585, 588
588 markets[vToken].rewardIndex = markets[vToken].rewardIndex + delta;
// @audit _allMarkets[i] also accessed at 610
611 _updateScore(user, _allMarkets[i]);
// @audit tokens[user] also accessed at 705
707 tokens[user].exists = true;
// @audit tokens[user] also accessed at 705, 707
708 tokens[user].isIrrevocable = isIrrevocable;
// @audit _allMarkets[i] also accessed at 731
733 markets[_allMarkets[i]].sumOfMembersScore =
// @audit markets[_allMarkets[i]] also accessed at 733
// @audit _allMarkets[i] also accessed at 731, 733
734 markets[_allMarkets[i]].sumOfMembersScore -
// @audit markets[market] also accessed at 795
// @audit markets[market] also accessed at 795, 800
800 markets[market].sumOfMembersScore = markets[market].sumOfMembersScore - interests[market][user].score + score;
// @audit markets[market] also accessed at 881
882 uint256 supplyCapUSD = (xvsPrice * ((xvs * markets[market].supplyMultiplier) / EXP_SCALE)) / EXP_SCALE;
GitHub: [271, 272, 276, 277, 295, 296, 297, 298, 299, 370, 375, 376, 377, 377, 378, 379, 399, 401, 481, 580, 581, 584, 585, 588, 588, 611, 707, 708, 733, 734, 734, 800, 800, 882]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit tokenAmountAccrued[token_] also accessed at 199
200 tokenAmountAccrued[token_] = 0;
// @audit tokenAmountAccrued[token_] also accessed at 261
266 tokenAmountAccrued[token_] += tokenAccrued;
// @audit lastAccruedBlock[token_] also accessed at 255
270 lastAccruedBlock[token_] = blockNumber;
// @audit lastAccruedBlock[token_] also accessed at 289
298 lastAccruedBlock[token_] = blockNumber;
// @audit tokenDistributionSpeeds[token_] also accessed at 315
322 tokenDistributionSpeeds[token_] = distributionSpeed_;
GitHub: [200, 266, 270, 298, 322]
[Gβ16] Multiple address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
, where appropriate
Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key's keccak256 hash (Gkeccak256 - 30 gas) and that calculation's associated stack operations.
There are 10 instances of this issue:
see instances
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
21 mapping(address => uint256) public tokenDistributionSpeeds;
24 mapping(address => uint256) public lastAccruedBlock;
27 mapping(address => uint256) public tokenAmountAccrued;
File: contracts/Tokens/Prime/PrimeStorage.sol
46 mapping(address => Token) public tokens;
61 mapping(address => uint256) public stakedAt;
64 mapping(address => Market) public markets;
67 mapping(address => mapping(address => Interest)) public interests;
100 mapping(address => address) public vTokenForAsset;
110 mapping(address => uint256) public unreleasedPSRIncome;
114 mapping(address => uint256) public unreleasedPLPIncome;
Nesting if
-statements avoids the stack operations of setting up and using an extra jumpdest
, and saves 6 gas
There are 7 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
337 if (userToken.exists && !userToken.isIrrevocable) {
369 if (tokens[user].exists && !isAccountEligible) {
375 } else if (!isAccountEligible && !tokens[user].exists && stakedAt[user] > 0) {
377 } else if (stakedAt[user] == 0 && isAccountEligible && !tokens[user].exists) {
379 } else if (tokens[user].exists && isAccountEligible) {
830 if (pendingScoreUpdates > 0 && !isScoreUpdated[nextScoreUpdateRoundId][user]) {
GitHub: [337, 369, 375, 377, 379, 830]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
262 if (distributionSpeed > 0 && balanceDiff > 0) {
GitHub: [262]
See this link for the full details
There are 7 instances of this issue:
see instances
File: contracts/Tokens/Prime/IPrime.sol
2 pragma solidity ^0.5.16;
GitHub: [2]
File: contracts/Tokens/Prime/Prime.sol
2 pragma solidity 0.8.13;
GitHub: [2]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
2 pragma solidity 0.8.13;
GitHub: [2]
File: contracts/Tokens/Prime/PrimeStorage.sol
2 pragma solidity 0.8.13;
GitHub: [2]
File: contracts/Tokens/Prime/libs/FixedMath.sol
4 pragma solidity 0.8.13;
GitHub: [4]
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
4 pragma solidity 0.8.13;
GitHub: [4]
File: contracts/Tokens/Prime/libs/Scores.sol
3 pragma solidity 0.8.13;
GitHub: [3]
Payable functions cost less gas to execute, since the compiler does not have to add extra checks to ensure that a payment wasn't provided. A constructor can safely be marked as payable, since only the deployer would be able to pass funds, and the project itself would not pass any funds.
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
103 constructor(address _wbnb, address _vbnb, uint256 _blocksPerYear) {
GitHub: [103]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
78 constructor() {
GitHub: [78]
Using the addition operator instead of plus-equals saves 113 gas
There are 2 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
785 interests[vToken][user].accrued += _interestAccrued(vToken, user);
GitHub: [785]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
266 tokenAmountAccrued[token_] += tokenAccrued;
GitHub: [266]
If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table
There are 4 instances of this issue:
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
12 uint256 public constant MAX_DISTRIBUTION_SPEED = 1e18;
GitHub: [12]
File: contracts/Tokens/Prime/PrimeStorage.sol
34 uint256 public constant MINIMUM_STAKED_XVS = 1000 * EXP_SCALE;
37 uint256 public constant MAXIMUM_XVS_CAP = 100000 * EXP_SCALE;
40 uint256 public constant STAKING_PERIOD = 90 * 24 * 60 * 60;
Use a solidity version of at least 0.8.2 to get simple compiler automatic inlining
Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads
Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require()
strings
Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value
There is one instance of this issue:
File: contracts/Tokens/Prime/IPrime.sol
2 pragma solidity ^0.5.16;
GitHub: [2]
There are 20 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
104 if (_wbnb == address(0)) revert InvalidAddress();
105 if (_vbnb == address(0)) revert InvalidAddress();
106 if (_blocksPerYear == 0) revert InvalidBlocksPerYear();
143 if (_xvsVault == address(0)) revert InvalidAddress();
144 if (_xvsVaultRewardToken == address(0)) revert InvalidAddress();
145 if (_protocolShareReserve == address(0)) revert InvalidAddress();
146 if (_comptroller == address(0)) revert InvalidAddress();
147 if (_oracle == address(0)) revert InvalidAddress();
148 if (_primeLiquidityProvider == address(0)) revert InvalidAddress();
201 if (pendingScoreUpdates == 0) revert NoScoreUpdatesRequired();
202 if (nextScoreUpdateRoundId == 0) revert NoScoreUpdatesRequired();
398 if (stakedAt[msg.sender] == 0) revert IneligibleToClaim();
457 if (vToken == address(0)) revert MarketNotSupported();
479 if (stakedAt[user] == 0) return STAKING_PERIOD;
576 if (distributionIncome == 0) {
1002 if (totalScore == 0) return (0, 0);
1007 if (totalCappedValue == 0) return (0, 0);
GitHub: [104, 105, 106, 143, 144, 145, 146, 147, 148, 201, 202, 398, 457, 479, 576, 1002, 1007]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
335 if (lastBlockAccrued == 0) {
345 if (address_ == address(0)) {
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
145 if (x == 0) {
GitHub: [145]
Getters for public state variables are automatically generated so there is no need to code them manually and waste gas.
There is one instance of this issue:
File: contracts/Tokens/Prime/Prime.sol
469 function getAllMarkets() external view returns (address[] memory) {
470 return allMarkets;
471 }
GitHub: [469]
The division cannot overflow, since both the numerator and the denominator are non-negative
There are 20 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
501 uint256 supply = (exchangeRate * balanceOfAccount) / EXP_SCALE;
585 delta = ((distributionIncome * EXP_SCALE) / markets[vToken].sumOfMembersScore);
654 uint256 supply = (exchangeRate * balanceOfAccount) / EXP_SCALE;
881 uint256 borrowCapUSD = (xvsPrice * ((xvs * markets[market].borrowMultiplier) / EXP_SCALE)) / EXP_SCALE;
882 uint256 supplyCapUSD = (xvsPrice * ((xvs * markets[market].supplyMultiplier) / EXP_SCALE)) / EXP_SCALE;
885 uint256 supplyUSD = (tokenPrice * supply) / EXP_SCALE;
886 uint256 borrowUSD = (tokenPrice * borrow) / EXP_SCALE;
889 supply = supplyUSD > 0 ? (supply * supplyCapUSD) / supplyUSD : 0;
893 borrow = borrowUSD > 0 ? (borrow * borrowCapUSD) / borrowUSD : 0;
922 return (index * score) / EXP_SCALE;
949 return ((((market.totalBorrows() * market.borrowRatePerBlock()) / EXP_SCALE) * market.reserveFactorMantissa()) /
950 EXP_SCALE);
972 uint256 incomePerBlockForDistributionFromMarket = (totalIncomePerBlockFromMarket * _distributionPercentage()) /
973 IProtocolShareReserve(protocolShareReserve).MAX_PERCENT();
1004 uint256 userYearlyIncome = (userScore * _incomeDistributionYearly(vToken)) / totalScore;
1009 uint256 userSupplyIncomeYearly = (userYearlyIncome * totalCappedSupply) / totalCappedValue;
1010 uint256 userBorrowIncomeYearly = (userYearlyIncome * totalCappedBorrow) / totalCappedValue;
1012 supplyAPR = totalSupply == 0 ? 0 : ((userSupplyIncomeYearly * MAXIMUM_BPS) / totalSupply);
1013 borrowAPR = totalBorrow == 0 ? 0 : ((userBorrowIncomeYearly * MAXIMUM_BPS) / totalBorrow);
GitHub: [501, 585, 654, 881, 881, 882, 882, 885, 886, 889, 893, 922, 949, 949, 972, 1004, 1009, 1010, 1012, 1013]
[Gβ26] Add unchecked {}
for subtractions where the operands cannot underflow because of a previous require()
or if
-statement
require(a <= b); x = b - a
=> require(a <= b); unchecked { x = b - a }
There are 23 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
399 if (block.timestamp - stakedAt[msg.sender] < STAKING_PERIOD) revert WaitMoreTime();
481 uint256 totalTimeStaked = block.timestamp - stakedAt[user];
483 return STAKING_PERIOD - totalTimeStaked;
534 uint256 totalScore = markets[market].sumOfMembersScore - interests[market][user].score;
568 uint256 distributionIncome = totalIncomeUnreleased - unreleasedPSRIncome[underlying];
572 uint256 unreleasedPLPAccruedInterest = totalAccruedInPLP - unreleasedPLPIncome[underlying];
661 capital = capital * (10 ** (18 - vToken.decimals()));
734 markets[_allMarkets[i]].sumOfMembersScore -
735 interests[_allMarkets[i]][user].score;
800 markets[market].sumOfMembersScore = markets[market].sumOfMembersScore - interests[market][user].score + score;
846 return (xvs - pendingWithdrawals);
919 uint256 index = markets[vToken].rewardIndex - interests[vToken][user].rewardIndex;
GitHub: [399, 481, 483, 534, 568, 572, 661, 734, 800, 846, 919]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
237 if (balance - accrued > 0) {
255 uint256 deltaBlocks = blockNumber - lastAccruedBlock[token_];
261 uint256 balanceDiff = balance - tokenAmountAccrued[token_];
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
120 z = y = x - FIXED_1;
122 r += (z * (0x100000000000000000000000000000000 - y)) / 0x100000000000000000000000000000000;
124 r += (z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y)) / 0x200000000000000000000000000000000;
126 r += (z * (0x099999999999999999999999999999999 - y)) / 0x300000000000000000000000000000000;
128 r += (z * (0x092492492492492492492492492492492 - y)) / 0x400000000000000000000000000000000;
130 r += (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) / 0x500000000000000000000000000000000;
132 r += (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) / 0x600000000000000000000000000000000;
134 r += (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) / 0x700000000000000000000000000000000;
136 r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16
GitHub: [120, 122, 124, 126, 128, 130, 132, 134, 136]
Using ints
/uints
smaller than 32 bytes may cost more gas. This is because the EVM operates on 32 bytes at a time, so if an element is smaller than 32 bytes, the EVM must perform more operations to reduce the size of the element from 32 bytes to the desired size.
There are 12 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit uint128
75 uint128 indexed oldNumerator,
// @audit uint128
76 uint128 indexed oldDenominator,
// @audit uint128
77 uint128 indexed newNumerator,
// @audit uint128
78 uint128 newDenominator
// @audit uint128
134 uint128 _alphaNumerator,
// @audit uint128
135 uint128 _alphaDenominator,
// @audit uint128
237 function updateAlpha(uint128 _alphaNumerator, uint128 _alphaDenominator) external {
// @audit uint128
809 function _checkAlphaArguments(uint128 _alphaNumerator, uint128 _alphaDenominator) internal {
GitHub: [75, 76, 77, 78, 134, 135, 237, 237, 809, 809]
File: contracts/Tokens/Prime/PrimeStorage.sol
// @audit uint128
73 uint128 public alphaNumerator;
// @audit uint128
76 uint128 public alphaDenominator;
Saves a storage slot. If the variable is assigned a non-zero value, saves Gsset (20000 gas). If it's assigned a zero value, saves Gsreset (2900 gas). If the variable remains unassigned, there is no gas savings unless the variable is public
, in which case the compiler-generated non-payable getter deployment cost is saved. If the state variable is overriding an interface's public function, mark the variable as constant
or immutable
so that it does not use a storage slot
There are 30 instances of this issue:
see instances
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
15 uint256 internal constant EXP_SCALE = 1e18;
GitHub: [15]
File: contracts/Tokens/Prime/PrimeStorage.sol
34 uint256 public constant MINIMUM_STAKED_XVS = 1000 * EXP_SCALE;
37 uint256 public constant MAXIMUM_XVS_CAP = 100000 * EXP_SCALE;
40 uint256 public constant STAKING_PERIOD = 90 * 24 * 60 * 60;
43 uint256 internal constant MAXIMUM_BPS = 10000;
46 mapping(address => Token) public tokens;
49 uint256 public totalIrrevocable;
52 uint256 public totalRevocable;
55 uint256 public revocableLimit;
58 uint256 public irrevocableLimit;
61 mapping(address => uint256) public stakedAt;
64 mapping(address => Market) public markets;
67 mapping(address => mapping(address => Interest)) public interests;
70 address[] internal allMarkets;
73 uint128 public alphaNumerator;
76 uint128 public alphaDenominator;
79 address internal xvsVault;
82 address internal xvsVaultRewardToken;
85 uint256 internal xvsVaultPoolId;
88 mapping(uint256 => mapping(address => bool)) public isScoreUpdated;
91 uint256 public nextScoreUpdateRoundId;
94 uint256 public totalScoreUpdatesRequired;
97 uint256 public pendingScoreUpdates;
100 mapping(address => address) public vTokenForAsset;
103 address public protocolShareReserve;
106 address public comptroller;
110 mapping(address => uint256) public unreleasedPSRIncome;
114 mapping(address => uint256) public unreleasedPLPIncome;
117 address public primeLiquidityProvider;
120 ResilientOracleInterface public oracle;
GitHub: [34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 110, 114, 117, 120]
If the functions are required by an interface, the contract should inherit from that interface and use the override
keyword
There are 8 instances of this issue:
File: contracts/Tokens/Prime/libs/FixedMath.sol
22 function toFixed(uint256 n, uint256 d) internal pure returns (int256) {
23 if (d.toInt256() < n.toInt256()) revert InvalidFraction(n, d);
24
25 return (n.toInt256() * FixedMath0x.FIXED_1) / int256(d.toInt256());
26 }
34 function uintDiv(uint256 u, int256 f) internal pure returns (uint256) {
35 if (f < 0) revert InvalidFixedPoint();
36 // multiply `u` by FIXED_1 to cancel out the built-in FIXED_1 in f
37 return uint256((u.toInt256() * FixedMath0x.FIXED_1) / f);
38 }
46 function uintMul(uint256 u, int256 f) internal pure returns (uint256) {
47 if (f < 0) revert InvalidFixedPoint();
48 // divide the product by FIXED_1 to cancel out the built-in FIXED_1 in f
49 return uint256((u.toInt256() * f) / FixedMath0x.FIXED_1);
50 }
53 function ln(int256 x) internal pure returns (int256 r) {
54 return FixedMath0x.ln(x);
55 }
58 function exp(int256 x) internal pure returns (int256 r) {
59 return FixedMath0x.exp(x);
60 }
File: contracts/Tokens/Prime/libs/FixedMath0x.sol
51 function ln(int256 x) internal pure returns (int256 r) {
52 if (x > LN_MAX_VAL) {
53 revert LnTooLarge(x);
54 }
55 if (x <= 0) {
56 revert LnNonRealResult(x);
57 }
58 if (x == FIXED_1) {
59 return 0;
60 }
61 if (x <= LN_MIN_VAL) {
62 return EXP_MIN_VAL;
63 }
64
65 int256 y;
66 int256 z;
67 int256 w;
68
69 // Rewrite the input as a quotient of negative natural exponents and a single residual q, such that 1 < q < 2
70 // For example: log(0.3) = log(e^-1 * e^-0.25 * 1.0471028872385522)
71 // = 1 - 0.25 - log(1 + 0.0471028872385522)
72 // e ^ -32
73 if (x <= int256(0x00000000000000000000000000000000000000000001c8464f76164760000000)) {
74 r -= int256(0x0000000000000000000000000000001000000000000000000000000000000000); // - 32
75 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000000001c8464f76164760000000); // / e ^ -32
76 }
77 // e ^ -16
78 if (x <= int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000)) {
79 r -= int256(0x0000000000000000000000000000000800000000000000000000000000000000); // - 16
80 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000); // / e ^ -16
81 }
82 // e ^ -8
83 if (x <= int256(0x00000000000000000000000000000000000afe10820813d78000000000000000)) {
84 r -= int256(0x0000000000000000000000000000000400000000000000000000000000000000); // - 8
85 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000afe10820813d78000000000000000); // / e ^ -8
86 }
87 // e ^ -4
88 if (x <= int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000)) {
89 r -= int256(0x0000000000000000000000000000000200000000000000000000000000000000); // - 4
90 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000); // / e ^ -4
91 }
92 // e ^ -2
93 if (x <= int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000)) {
94 r -= int256(0x0000000000000000000000000000000100000000000000000000000000000000); // - 2
95 x = (x * FIXED_1) / int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000); // / e ^ -2
96 }
97 // e ^ -1
98 if (x <= int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000)) {
99 r -= int256(0x0000000000000000000000000000000080000000000000000000000000000000); // - 1
100 x = (x * FIXED_1) / int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000); // / e ^ -1
101 }
102 // e ^ -0.5
103 if (x <= int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000)) {
104 r -= int256(0x0000000000000000000000000000000040000000000000000000000000000000); // - 0.5
105 x = (x * FIXED_1) / int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000); // / e ^ -0.5
106 }
107 // e ^ -0.25
108 if (x <= int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000)) {
109 r -= int256(0x0000000000000000000000000000000020000000000000000000000000000000); // - 0.25
110 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000); // / e ^ -0.25
111 }
112 // e ^ -0.125
113 if (x <= int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) {
114 r -= int256(0x0000000000000000000000000000000010000000000000000000000000000000); // - 0.125
115 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d); // / e ^ -0.125
116 }
117 // `x` is now our residual in the range of 1 <= x <= 2 (or close enough).
118
119 // Add the taylor series for log(1 + z), where z = x - 1
120 z = y = x - FIXED_1;
121 w = (y * y) / FIXED_1;
122 r += (z * (0x100000000000000000000000000000000 - y)) / 0x100000000000000000000000000000000;
123 z = (z * w) / FIXED_1; // add y^01 / 01 - y^02 / 02
124 r += (z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y)) / 0x200000000000000000000000000000000;
125 z = (z * w) / FIXED_1; // add y^03 / 03 - y^04 / 04
126 r += (z * (0x099999999999999999999999999999999 - y)) / 0x300000000000000000000000000000000;
127 z = (z * w) / FIXED_1; // add y^05 / 05 - y^06 / 06
128 r += (z * (0x092492492492492492492492492492492 - y)) / 0x400000000000000000000000000000000;
129 z = (z * w) / FIXED_1; // add y^07 / 07 - y^08 / 08
130 r += (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) / 0x500000000000000000000000000000000;
131 z = (z * w) / FIXED_1; // add y^09 / 09 - y^10 / 10
132 r += (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) / 0x600000000000000000000000000000000;
133 z = (z * w) / FIXED_1; // add y^11 / 11 - y^12 / 12
134 r += (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) / 0x700000000000000000000000000000000;
135 z = (z * w) / FIXED_1; // add y^13 / 13 - y^14 / 14
136 r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16
137 }
140 function exp(int256 x) internal pure returns (int256 r) {
141 if (x < EXP_MIN_VAL) {
142 // Saturate to zero below EXP_MIN_VAL.
143 return 0;
144 }
145 if (x == 0) {
146 return FIXED_1;
147 }
148 if (x > EXP_MAX_VAL) {
149 revert ExpTooLarge(x);
150 }
151
152 // Rewrite the input as a product of natural exponents and a
153 // single residual q, where q is a number of small magnitude.
154 // For example: e^-34.419 = e^(-32 - 2 - 0.25 - 0.125 - 0.044)
155 // = e^-32 * e^-2 * e^-0.25 * e^-0.125 * e^-0.044
156 // -> q = -0.044
157
158 // Multiply with the taylor series for e^q
159 int256 y;
160 int256 z;
161 // q = x % 0.125 (the residual)
162 z = y = x % 0x0000000000000000000000000000000010000000000000000000000000000000;
163 z = (z * y) / FIXED_1;
164 r += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)
165 z = (z * y) / FIXED_1;
166 r += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)
167 z = (z * y) / FIXED_1;
168 r += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)
169 z = (z * y) / FIXED_1;
170 r += z * 0x004807432bc18000; // add y^05 * (20! / 05!)
171 z = (z * y) / FIXED_1;
172 r += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)
173 z = (z * y) / FIXED_1;
174 r += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)
175 z = (z * y) / FIXED_1;
176 r += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)
177 z = (z * y) / FIXED_1;
178 r += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)
179 z = (z * y) / FIXED_1;
180 r += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)
181 z = (z * y) / FIXED_1;
182 r += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)
183 z = (z * y) / FIXED_1;
184 r += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)
185 z = (z * y) / FIXED_1;
186 r += z * 0x0000000017499f00; // add y^13 * (20! / 13!)
187 z = (z * y) / FIXED_1;
188 r += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)
189 z = (z * y) / FIXED_1;
190 r += z * 0x00000000001c6380; // add y^15 * (20! / 15!)
191 z = (z * y) / FIXED_1;
192 r += z * 0x000000000001c638; // add y^16 * (20! / 16!)
193 z = (z * y) / FIXED_1;
194 r += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)
195 z = (z * y) / FIXED_1;
196 r += z * 0x000000000000017c; // add y^18 * (20! / 18!)
197 z = (z * y) / FIXED_1;
198 r += z * 0x0000000000000014; // add y^19 * (20! / 19!)
199 z = (z * y) / FIXED_1;
200 r += z * 0x0000000000000001; // add y^20 * (20! / 20!)
201 r = r / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
202
203 // Multiply with the non-residual terms.
204 x = -x;
205 // e ^ -32
206 if ((x & int256(0x0000000000000000000000000000001000000000000000000000000000000000)) != 0) {
207 r =
208 (r * int256(0x00000000000000000000000000000000000000f1aaddd7742e56d32fb9f99744)) /
209 int256(0x0000000000000000000000000043cbaf42a000812488fc5c220ad7b97bf6e99e); // * e ^ -32
210 }
211 // e ^ -16
212 if ((x & int256(0x0000000000000000000000000000000800000000000000000000000000000000)) != 0) {
213 r =
214 (r * int256(0x00000000000000000000000000000000000afe10820813d65dfe6a33c07f738f)) /
215 int256(0x000000000000000000000000000005d27a9f51c31b7c2f8038212a0574779991); // * e ^ -16
216 }
217 // e ^ -8
218 if ((x & int256(0x0000000000000000000000000000000400000000000000000000000000000000)) != 0) {
219 r =
220 (r * int256(0x0000000000000000000000000000000002582ab704279e8efd15e0265855c47a)) /
221 int256(0x0000000000000000000000000000001b4c902e273a58678d6d3bfdb93db96d02); // * e ^ -8
222 }
223 // e ^ -4
224 if ((x & int256(0x0000000000000000000000000000000200000000000000000000000000000000)) != 0) {
225 r =
226 (r * int256(0x000000000000000000000000000000001152aaa3bf81cb9fdb76eae12d029571)) /
227 int256(0x00000000000000000000000000000003b1cc971a9bb5b9867477440d6d157750); // * e ^ -4
228 }
229 // e ^ -2
230 if ((x & int256(0x0000000000000000000000000000000100000000000000000000000000000000)) != 0) {
231 r =
232 (r * int256(0x000000000000000000000000000000002f16ac6c59de6f8d5d6f63c1482a7c86)) /
233 int256(0x000000000000000000000000000000015bf0a8b1457695355fb8ac404e7a79e3); // * e ^ -2
234 }
235 // e ^ -1
236 if ((x & int256(0x0000000000000000000000000000000080000000000000000000000000000000)) != 0) {
237 r =
238 (r * int256(0x000000000000000000000000000000004da2cbf1be5827f9eb3ad1aa9866ebb3)) /
239 int256(0x00000000000000000000000000000000d3094c70f034de4b96ff7d5b6f99fcd8); // * e ^ -1
240 }
241 // e ^ -0.5
242 if ((x & int256(0x0000000000000000000000000000000040000000000000000000000000000000)) != 0) {
243 r =
244 (r * int256(0x0000000000000000000000000000000063afbe7ab2082ba1a0ae5e4eb1b479dc)) /
245 int256(0x00000000000000000000000000000000a45af1e1f40c333b3de1db4dd55f29a7); // * e ^ -0.5
246 }
247 // e ^ -0.25
248 if ((x & int256(0x0000000000000000000000000000000020000000000000000000000000000000)) != 0) {
249 r =
250 (r * int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) /
251 int256(0x00000000000000000000000000000000910b022db7ae67ce76b441c27035c6a1); // * e ^ -0.25
252 }
253 // e ^ -0.125
254 if ((x & int256(0x0000000000000000000000000000000010000000000000000000000000000000)) != 0) {
255 r =
256 (r * int256(0x00000000000000000000000000000000783eafef1c0a8f3978c7f81824d62ebf)) /
257 int256(0x0000000000000000000000000000000088415abbe9a76bead8d00cf112e4d4a8); // * e ^ -0.125
258 }
259 }
File: contracts/Tokens/Prime/libs/Scores.sol
22 function calculateScore(
23 uint256 xvs,
24 uint256 capital,
25 uint256 alphaNumerator,
26 uint256 alphaDenominator
27 ) internal pure returns (uint256) {
28 // Score function is:
29 // xvs^π° * capital^(1-π°)
30 // = capital * capital^(-π°) * xvs^π°
31 // = capital * (xvs / capital)^π°
32 // = capital * (e ^ (ln(xvs / capital))) ^ π°
33 // = capital * e ^ (π° * ln(xvs / capital)) (1)
34 // or
35 // = capital / ( 1 / e ^ (π° * ln(xvs / capital)))
36 // = capital / (e ^ (π° * ln(xvs / capital)) ^ -1)
37 // = capital / e ^ (π° * -1 * ln(xvs / capital))
38 // = capital / e ^ (π° * ln(capital / xvs)) (2)
39 //
40 // To avoid overflows, use (1) when xvs < capital and
41 // use (2) when capital < xvs
42
43 // If any side is 0, exit early
44 if (xvs == 0 || capital == 0) return 0;
45
46 // If both sides are equal, we have:
47 // xvs^π° * capital^(1-π°)
48 // = xvs^π° * xvs^(1-π°)
49 // = xvs^(π° + 1 - π°) = xvs
50 if (xvs == capital) return xvs;
51
52 bool lessxvsThanCapital = xvs < capital;
53
54 // (xvs / capital) or (capital / xvs), always in range (0, 1)
55 int256 ratio = lessxvsThanCapital ? FixedMath.toFixed(xvs, capital) : FixedMath.toFixed(capital, xvs);
56
57 // e ^ ( ln(ratio) * π° )
58 int256 exponentiation = FixedMath.exp(
59 (FixedMath.ln(ratio) * alphaNumerator.toInt256()) / alphaDenominator.toInt256()
60 );
61
62 if (lessxvsThanCapital) {
63 // capital * e ^ (π° * ln(xvs / capital))
64 return FixedMath.uintMul(capital, exponentiation);
65 }
66
67 // capital / e ^ (π° * ln(capital / xvs))
68 return FixedMath.uintDiv(capital, exponentiation);
69 }
70 }
71
GitHub: [22]
By using --via-ir
or {"viaIR": true}
, the compiler is able to use more advanced multi-function optimizations, for extra gas savings.
There are 7 instances of this issue:
Using == 0
, != 0
instead of > 0
, >= 1
, < 1
, <= 0
can save gas.
There are 11 instances of this issue:
File: contracts/Tokens/Prime/Prime.sol
// @audit Replace with != 0
375 } else if (!isAccountEligible && !tokens[user].exists && stakedAt[user] > 0) {
// @audit Replace with != 0
584 if (markets[vToken].sumOfMembersScore > 0) {
// @audit Replace with != 0
828 if (totalScoreUpdatesRequired > 0) totalScoreUpdatesRequired--;
// @audit Replace with != 0
830 if (pendingScoreUpdates > 0 && !isScoreUpdated[nextScoreUpdateRoundId][user]) {
// @audit Replace with != 0
889 supply = supplyUSD > 0 ? (supply * supplyCapUSD) / supplyUSD : 0;
// @audit Replace with != 0
893 borrow = borrowUSD > 0 ? (borrow * borrowCapUSD) / borrowUSD : 0;
GitHub: [375, 584, 828, 830, 889, 893]
File: contracts/Tokens/Prime/PrimeLiquidityProvider.sol
// @audit Replace with != 0
237 if (balance - accrued > 0) {
// @audit Replace with != 0
257 if (deltaBlocks > 0) {
// @audit Replace with != 0
262 if (distributionSpeed > 0 && balanceDiff > 0) {
// @audit Replace with != 0
291 if (initializedBlock > 0) {
acknowledged
Regarding
[Mβ01] The owner is a single point of failure and a centralization risk
, the owner won't be an EOA, but that cannot be specified in the solidity code. The owner of our contracts is always the Normal Timelock contract deployed at 0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396. This contract is used in the Governance process, so after a voting period of 24 hours, and an extra delay of 48 hours if the vote passed, this contract will execute the commands agreed on the Venus Improvement Proposal.