Issue | Instances | |
---|---|---|
[H‑01] | get_dy_underlying() is not a flash-loan-resistant price |
1 |
[H‑02] | wstETH 's functions operate on units of stEth, not Eth |
1 |
Total: 2 instances over 2 issues
Issue | Instances | |
---|---|---|
[M‑01] | Contracts are vulnerable to fee-on-transfer accounting-related issues | 1 |
[M‑02] | Insufficient oracle validation | 3 |
[M‑03] | Missing checks for whether the L2 Sequencer is active | 3 |
[M‑04] | The owner is a single point of failure and a centralization risk |
17 |
[M‑05] | Contracts are vulnerable to rebasing accounting-related issues | 1 |
[M‑06] | Some tokens may revert when zero value transfers are made | 2 |
[M‑07] | Unsafe use of transfer() /transferFrom() with IERC20 |
15 |
[M‑08] | Return values of transfer() /transferFrom() not checked |
14 |
Total: 56 instances over 8 issues
Issue | Instances | |
---|---|---|
[L‑01] | Missing checks for address(0x0) when assigning values to address state variables |
34 |
[L‑02] | decimals() is not a part of the ERC-20 standard |
2 |
[L‑03] | Solidity version 0.8.20 may not work on other chains due to PUSH0 |
21 |
[L‑04] | Loss of precision | 50 |
[L‑05] | Array lengths not checked | 1 |
[L‑06] | Use Ownable2Step rather than Ownable |
4 |
[L‑07] | Upgradeable contract is missing a __gap[50] storage variable to allow for new storage variables in later versions |
1 |
Total: 113 instances over 7 issues
Issue | Instances | |
---|---|---|
[N‑01] | Events are missing sender information | 3 |
[N‑02] | Variables need not be initialized to zero | 4 |
[N‑03] | Consider using named mappings | 37 |
[N‑04] | Non-external /public variable and function names should begin with an underscore |
29 |
[N‑05] | Large numeric literals should use underscores for readability | 15 |
[N‑06] | Constants in comparisons should appear on the left side | 33 |
[N‑07] | Consider disabling renounceOwnership() |
4 |
[N‑08] | else -block not required |
3 |
[N‑09] | address s shouldn't be hard-coded |
2 |
[N‑10] | Interfaces should be defined in separate files from their usage | 17 |
[N‑11] | Imports could be organized more systematically | 1 |
[N‑12] | Mixed usage of int /uint with int256 /uint256 |
40 |
[N‑13] | public functions not called by the contract should be declared external instead |
13 |
[N‑14] | constant s should be defined rather than using magic numbers |
118 |
[N‑15] | Event is not properly indexed |
13 |
[N‑16] | Duplicated require() /revert() checks should be refactored to a modifier or function |
9 |
[N‑17] | Import declarations should import specific identifiers, rather than the whole file | 56 |
[N‑18] | Contract implements interface without extending the interface | 7 |
[N‑19] | require() /revert() statements should have descriptive reason strings |
2 |
[N‑20] | Missing event and or timelock for critical parameter change | 11 |
[N‑21] | Events that mark critical parameter changes should contain both the old and the new value | 9 |
[N‑22] | Constant redefined elsewhere | 3 |
[N‑23] | Use @inheritdoc rather than using a non-standard annotation |
1 |
[N‑24] | Inconsistent spacing in comments | 6 |
[N‑25] | Lines are too long | 92 |
[N‑26] | Variable names that consist of all capital letters should be reserved for constant /immutable variables |
4 |
[N‑27] | Non-library/interface files should use fixed compiler versions, not floating ones | 19 |
[N‑28] | Typos | 1 |
[N‑29] | File is missing NatSpec | 11 |
[N‑30] | NatSpec @param is missing |
133 |
[N‑31] | NatSpec @return argument is missing |
20 |
[N‑32] | Function definition modifier order does not follow Solidity style guide | 2 |
[N‑33] | Visibility should be set explicitly rather than defaulting to internal |
18 |
[N‑34] | Function ordering does not follow the Solidity style guide | 30 |
[N‑35] | Contract does not follow the Solidity style guide's suggested layout ordering | 16 |
[N‑36] | Interfaces should be indicated with an I prefix in the contract name |
2 |
[N‑37] | Control structures do not follow the Solidity Style Guide | 12 |
[N‑38] | mapping definitions do not follow the Solidity Style Guide |
1 |
[N‑39] | Expressions for constant values such as a call to keccak256() , should use immutable rather than constant |
7 |
[N‑40] | Numeric values having to do with time should use time units for readability | 2 |
[N‑41] | Consider using delete rather than assigning zero/false to clear values |
9 |
[N‑42] | Contracts should have full test coverage | 1 |
[N‑43] | Large or complicated code bases should implement invariant tests | 1 |
Total: 817 instances over 43 issues
Issue | Instances | Total Gas Saved | |
---|---|---|---|
[G‑01] | Reduce gas usage by moving to Solidity 0.8.19 or later | 21 | - |
[G‑02] | Avoid updating storage when the value hasn't changed | 17 | 13600 |
[G‑03] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , where appropriate |
7 | - |
[G‑04] | State variables only set in the constructor should be declared immutable |
5 | 10485 |
[G‑05] | Using storage instead of memory for structs/arrays saves gas |
1 | 4200 |
[G‑06] | State variables should be cached in stack variables rather than re-reading them from storage | 22 | 2134 |
[G‑07] | Multiple accesses of a mapping/array should use a local variable cache | 13 | 546 |
[G‑08] | The result of function calls should be cached rather than re-calling the function | 1 | - |
[G‑09] | <x> += <y> costs more gas than <x> = <x> + <y> for state variables |
16 | 1808 |
[G‑10] | internal functions only called once can be inlined to save gas |
1 | 20 |
[G‑11] | Add unchecked {} for subtractions where the operands cannot underflow because of a previous require() or if -statement |
16 | 1360 |
[G‑12] | <array>.length should not be looked up in every loop of a for -loop |
3 | 9 |
[G‑13] | ++i /i++ should be unchecked{++i} /unchecked{i++} when it is not possible for them to overflow, as is the case when used in for - and while -loops |
3 | 180 |
[G‑14] | require() /revert() strings longer than 32 bytes cost extra gas |
39 | - |
[G‑15] | keccak256() should only need to be called on a specific string literal once |
3 | 126 |
[G‑16] | Optimize names to save gas | 15 | 330 |
[G‑17] | Using bool s for storage incurs overhead |
7 | 119700 |
[G‑18] | Use a more recent version of solidity | 1 | - |
[G‑19] | Using > 0 costs more gas than != 0 when used on a uint in a require() statement |
3 | 18 |
[G‑20] | >= costs less gas than > |
2 | 6 |
[G‑21] | ++i costs less gas than i++ , especially when it's used in for -loops (--i /i-- too) |
3 | 15 |
[G‑22] | Splitting require() statements that use && saves gas |
3 | 9 |
[G‑23] | Usage of uints /ints smaller than 32 bytes (256 bits) incurs overhead |
1 | - |
[G‑24] | Using private rather than public for constants, saves gas |
8 | - |
[G‑25] | Don't compare boolean expressions to boolean literals | 1 | 9 |
[G‑26] | Don't use SafeMath once the solidity version is 0.8.0 or greater |
1 | - |
[G‑27] | require() or revert() statements that check input arguments should be at the top of the function |
2 | - |
[G‑28] | Empty blocks should be removed or emit something | 20 | - |
[G‑29] | Superfluous event fields | 19 | - |
[G‑30] | Use custom errors rather than revert() /require() strings to save gas |
111 | - |
[G‑31] | Functions guaranteed to revert when called by normal users can be marked payable |
26 | 546 |
[G‑32] | Constructors can be marked payable |
20 | 420 |
[G‑33] | Don't use _msgSender() if not supporting EIP-2771 |
17 | 272 |
[G‑34] | Not using the named return variables anywhere in the function is confusing | 1 | - |
Total: 429 instances over 34 issues with 155793 gas saved
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.
The issues below may be reported by other wardens, but can be ignored since either the rule or the specified instances are invalid
Issue | Instances | |
---|---|---|
[I‑01] | constant variables, which will save gas |
7 |
[I‑02] | 10 | |
[I‑03] | public function visibility to external to save gas |
18 |
Total: 35 instances over 3 issues
get_dy_underlying()
calculates the price based on the contract's underlying reserves, which can be manipulated by sandwiching the call with a flash loan. Therefore, using its output as a price oracle is not safe and will lead to loss of funds. Use a Chainlink oracle instead.
There is one instance of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
297: uint256 price = curvePool.get_dy_underlying(0, 2, 1e18);
wstETH
's functions return values related to units of stEth, not units of Eth. Even after the Shanghai upgrade, the price of stETH is not the same as the prices of ETH
There is one instance of this issue:
File: contracts/lybra/pools/LybraWstETHVault.sol
49: return (_etherPrice() * IWstETH(address(collateralAsset)).stEthPerToken()) / 1e18;
The functions below transfer funds from the caller to the receiver via transferFrom()
, but do not ensure that the actual number of tokens received is the same as the input amount to the transfer. If the token is a fee-on-transfer token, the balance after the transfer will be smaller than expected, leading to accounting issues. Even if there are checks later, related to a secondary transfer, an attacker may be able to use latent funds (e.g. mistakenly sent by another user) in order to get a free credit. One way to solve this problem is to measure the balance before and after the transfer, and use the difference as the amount, rather than the stated amount.
There is one instance of this issue:
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
72 function depositAssetToMint(uint256 assetAmount, uint256 mintAmount) external virtual {
73 require(assetAmount >= 1 ether, "Deposit should not be less than 1 stETH.");
74
75 bool success = collateralAsset.transferFrom(msg.sender, address(this), assetAmount);
76 require(success, "TF");
77
78 totalDepositedAsset += assetAmount;
79 depositedAsset[msg.sender] += assetAmount;
80 depositedTime[msg.sender] = block.timestamp;
81
82 if (mintAmount > 0) {
83 _mintEUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
84 }
85 emit DepositAsset(msg.sender, address(collateralAsset), assetAmount, block.timestamp);
86: }
There is no freshness check on the timestamp of the prices fetched from the Chainlink oracle, so old prices may be used if OCR was unable to push an update in time. Add a staleness threshold number of seconds configuration parameter, and ensure that the price fetched is from within that time range.
There are 3 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
151: (, int etherPrice, , , ) = etherPriceFeed.latestRoundData();
152: (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
212: (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
Chainlink recommends that users using price oracles, check whether the Arbitrum Sequencer is active. If the sequencer goes down, the Chainlink oracles will have stale prices from before the downtime, until a new L2 OCR transaction goes through. Users who submit their transactions via the L1 Dealyed Inbox will be able to take advantage of these stale prices. Use a Chainlink oracle to determine whether the sequencer is offline or not, and don't allow operations to take place while the sequencer is offline.
There are 3 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
151: (, int etherPrice, , , ) = etherPriceFeed.latestRoundData();
152: (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
212: (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
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 17 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
84: function setToken(address _lbr, address _eslbr) external onlyOwner {
89: function setLBROracle(address _lbrOracle) external onlyOwner {
93: function setPools(address[] memory _pools) external onlyOwner {
100: function setBiddingCost(uint256 _biddingRatio) external onlyOwner {
105: function setExtraRatio(uint256 ratio) external onlyOwner {
110: function setPeUSDExtraRatio(uint256 ratio) external onlyOwner {
115: function setBoost(address _boost) external onlyOwner {
119: function setRewardsDuration(uint256 _duration) external onlyOwner {
124: function setEthlbrStakeInfo(address _pool, address _lp) external onlyOwner {
128: function setEUSDBuyoutAllowed(bool _bool) external onlyOwner {
226 function notifyRewardAmount(
227 uint256 amount
228: ) external onlyOwner updateReward(address(0)) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
52: function setTokenAddress(address _eslbr, address _lbr, address _boost) external onlyOwner {
58: function setGrabCost(uint256 _ratio) external onlyOwner {
File: contracts/lybra/miner/esLBRBoost.sol
33: function addLockSetting(esLBRLockSetting memory setting) external onlyOwner {
File: contracts/lybra/miner/stakerewardV2pool.sol
121: function setRewardsDuration(uint256 _duration) external onlyOwner {
127: function setBoost(address _boost) external onlyOwner {
132: function notifyRewardAmount(uint256 _amount) external onlyOwner updateReward(address(0)) {
Rebasing tokens are tokens that have each holder's balanceof()
increase over time. Aave aTokens are an example of such tokens. If rebasing tokens are used, rewards accrue to the contract holding the tokens, and cannot be withdrawn by the original depositor. To address the issue, track 'shares' deposited on a pro-rata basis, and let shares be redeemed for their proportion of the current balance at the time of the withdrawal.
There is one instance of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
190 function getReward() external updateReward(msg.sender) {
191 uint reward = rewards[msg.sender];
192 if (reward > 0) {
193 rewards[msg.sender] = 0;
194 IEUSD EUSD = IEUSD(configurator.getEUSDAddress());
195 uint256 balance = EUSD.sharesOf(address(this));
196 uint256 eUSDShare = balance >= reward ? reward : reward - balance;
197 EUSD.transferShares(msg.sender, eUSDShare);
198 if(reward > eUSDShare) {
199 ERC20 peUSD = ERC20(configurator.peUSD());
200 uint256 peUSDBalance = peUSD.balanceOf(address(this));
201 if(peUSDBalance >= reward - eUSDShare) {
202 peUSD.transfer(msg.sender, reward - eUSDShare);
203 emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);
204 } else {
205 if(peUSDBalance > 0) {
206 peUSD.transfer(msg.sender, peUSDBalance);
207 }
208 ERC20 token = ERC20(configurator.stableToken());
209 uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
210 token.transfer(msg.sender, tokenAmount);
211 emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(token), reward - eUSDShare, block.timestamp);
212 }
213 } else {
214 emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(0), 0, block.timestamp);
215 }
216
217 }
218: }
In spite of the fact that EIP-20 states that zero-valued transfers must be accepted, some tokens, such as LEND will revert if this is attempted, which may cause transactions that involve other tokens (such as batch operations) to fully revert. Consider skipping the transfer if the amount is zero, which will also save gas.
There are 2 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
207 }
208 ERC20 token = ERC20(configurator.stableToken());
209 uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
210: token.transfer(msg.sender, tokenAmount);
199 ERC20 peUSD = ERC20(configurator.peUSD());
200 uint256 peUSDBalance = peUSD.balanceOf(address(this));
201 if(peUSDBalance >= reward - eUSDShare) {
202: peUSD.transfer(msg.sender, reward - eUSDShare);
Some tokens do not implement the ERC20 standard properly but are still accepted by most code that accepts ERC20 tokens. For example Tether (USDT)'s transfer()
and transferFrom()
functions on L1 do not return booleans as the specification requires, and instead have no return value. When these sorts of tokens are cast to IERC20
, their function signatures do not match and therefore the calls made, revert (see this link for a test case). Use OpenZeppelin’s SafeERC20
's safeTransfer()
/safeTransferFrom()
instead
There are 15 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
210: token.transfer(msg.sender, tokenAmount);
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
75: bool success = collateralAsset.transferFrom(msg.sender, address(this), assetAmount);
107: collateralAsset.transfer(onBehalfOf, withdrawal);
169: collateralAsset.transfer(msg.sender, reducedAsset);
172: collateralAsset.transfer(provider, reducedAsset - reward2keeper);
173: collateralAsset.transfer(msg.sender, reward2keeper);
206: collateralAsset.transfer(msg.sender, reward2keeper);
208: collateralAsset.transfer(provider, assetAmount - reward2keeper);
242: collateralAsset.transfer(msg.sender, collateralAmount);
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
61: collateralAsset.transferFrom(msg.sender, address(this), assetAmount);
139: collateralAsset.transfer(msg.sender, reducedAsset);
142: collateralAsset.transfer(provider, reducedAsset - reward2keeper);
143: collateralAsset.transfer(msg.sender, reward2keeper);
166: collateralAsset.transfer(msg.sender, collateralAmount);
215: collateralAsset.transfer(_onBehalfOf, _amount);
Not all IERC20
implementations revert()
when there's a failure in transfer()
/transferFrom()
. The function signature has a boolean
return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually making a payment
There are 14 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
210: token.transfer(msg.sender, tokenAmount);
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
107: collateralAsset.transfer(onBehalfOf, withdrawal);
169: collateralAsset.transfer(msg.sender, reducedAsset);
172: collateralAsset.transfer(provider, reducedAsset - reward2keeper);
173: collateralAsset.transfer(msg.sender, reward2keeper);
206: collateralAsset.transfer(msg.sender, reward2keeper);
208: collateralAsset.transfer(provider, assetAmount - reward2keeper);
242: collateralAsset.transfer(msg.sender, collateralAmount);
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
61: collateralAsset.transferFrom(msg.sender, address(this), assetAmount);
139: collateralAsset.transfer(msg.sender, reducedAsset);
142: collateralAsset.transfer(provider, reducedAsset - reward2keeper);
143: collateralAsset.transfer(msg.sender, reward2keeper);
166: collateralAsset.transfer(msg.sender, collateralAmount);
215: collateralAsset.transfer(_onBehalfOf, _amount);
There are 34 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
81: GovernanceTimelock = IGovernanceTimelock(_dao);
82: curvePool = ICurvePool(_curvePool);
99: if (address(EUSD) == address(0)) EUSD = IEUSD(_eusd);
100: if (address(peUSD) == address(0)) peUSD = IEUSD(_peusd);
138: lybraProtocolRewardsPool = IProtocolRewardsPool(addr);
148: eUSDMiningIncentives = IeUSDMiningIncentives(addr);
262: stableToken = _token;
File: contracts/lybra/governance/LybraGovernance.sol
40: esLBR = IesLBR(_esLBR);
41: GovernanceTimelock = IGovernanceTimelock(address(timelock_));
File: contracts/lybra/miner/EUSDMiningIncentives.sol
65: configurator = Iconfigurator(_config);
66: esLBRBoost = IesLBRBoost(_boost);
68: etherPriceFeed = AggregatorV3Interface(_etherOracle);
69: lbrPriceFeed = AggregatorV3Interface(_lbrOracle);
85: LBR = _lbr;
86: esLBR = _eslbr;
90: lbrPriceFeed = AggregatorV3Interface(_lbrOracle);
116: esLBRBoost = IesLBRBoost(_boost);
125: ethlbrStakePool = _pool;
126: ethlbrLpToken = _lp;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
49: configurator = Iconfigurator(_config);
53: esLBR = IesLBR(_eslbr);
54: LBR = IesLBR(_lbr);
55: esLBRBoost = IesLBRBoost(_boost);
File: contracts/lybra/miner/stakerewardV2pool.sol
50: stakingToken = IERC20(_stakingToken);
51: rewardsToken = IesLBR(_rewardToken);
52: esLBRBoost = IesLBRBoost(_boost);
128: esLBRBoost = IesLBRBoost(_boost);
File: contracts/lybra/pools/LybraRETHVault.sol
24: rkPool = IRkPool(_rkPool);
43: rkPool = IRkPool(addr);
File: contracts/lybra/pools/LybraWstETHVault.sol
26: lido = Ilido(_lido);
File: contracts/lybra/token/EUSD.sol
93: configurator = Iconfigurator(_config);
File: contracts/lybra/token/LBR.sol
19: configurator = Iconfigurator(_config);
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
56: configurator = Iconfigurator(_config);
File: contracts/lybra/token/esLBR.sol
23: configurator = Iconfigurator(_config);
The decimals()
function is not a part of the ERC-20 standard, and was added later as an optional extension. As such, some valid ERC20 tokens do not support this interface, so it is unsafe to blindly cast all tokens to this interface, and then call this function.
There are 2 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
209: uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
236: rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / token.decimals()) / totalStaked();
The compiler for Solidity 0.8.20 switches the default target EVM version to Shanghai, which includes the new PUSH0
op code. This op code may not yet be implemented on all L2s, so deployment on these chains will fail. To work around this issue, use an earlier EVM version. While the project itself may or may not compile with 0.8.20, other projects with which it integrates, or which extend this project may, and those projects will have problems deploying these contracts/libraries.
There are 21 instances of this issue:
see instances
File: contracts/lybra/Proxy/LybraProxy.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/Proxy/LybraProxyAdmin.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/configuration/LybraConfigurator.sol
15: pragma solidity ^0.8.17;
File: contracts/lybra/governance/AdminTimelock.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/governance/GovernanceTimelock.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/governance/LybraGovernance.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/esLBRBoost.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/stakerewardV2pool.sol
2: pragma solidity ^0.8;
File: contracts/lybra/pools/LybraRETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraStETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraWbETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraWstETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/EUSD.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/LBR.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/PeUSD.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
14: pragma solidity ^0.8.17;
File: contracts/lybra/token/esLBR.sol
3: pragma solidity ^0.8.17;
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 50 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
303: uint256 amount = curvePool.exchange_underlying(0, 2, balance, balance * price * 998 / 1e21);
357: return (EUSD.totalSupply() * maxStableRatio) / 10_000;
File: contracts/lybra/governance/LybraGovernance.sol
52: return esLBR.getPastTotalSupply(timepoint) / 3;
File: contracts/lybra/miner/esLBRBoost.sol
67: return ((boostEndTime - userUpdatedAt) * maxBoost) / (time - userUpdatedAt);
File: contracts/lybra/miner/EUSDMiningIncentives.sol
153: uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
154: uint256 lbrInLp = (IEUSD(LBR).balanceOf(ethlbrLpToken) * uint(lbrPrice)) / 1e8;
156: return (userStaked * (lbrInLp + etherInLp)) / totalLp;
168: return rewardPerTokenStored + (rewardRatio * (lastTimeRewardApplicable() - updatedAt) * 1e18) / totalStaked();
185: return ((stakedOf(_account) * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
210: uint256 biddingFee = (reward * biddingFeeRatio) / 10000;
213: biddingFee = biddingFee * uint256(lbrPrice) / 1e8;
231: rewardRatio = amount / duration;
234: rewardRatio = (amount + remainingRewards) / duration;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
134: LBR.burn(msg.sender, (amount * grabFeeRatio) / 10000);
152: amount = (a * (75e18 - ((time2fullRedemption[user] - block.timestamp) * 70e18) / (exitCycle / 1 days - 3) / 1 days)) / 100e18;
168: return ((stakedOf(_account) * (rewardPerTokenStored - userRewardPerTokenPaid[_account])) / 1e18) + rewards[_account];
209: uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
File: contracts/lybra/miner/stakerewardV2pool.sol
107: return ((balanceOf[_account] * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
115: withdrawal = block.timestamp - 3 days >= depositedTime[user] ? amount : (amount * 999) / 1000;
156: uint256 onBehalfOfCollateralRatio = (depositedAsset[onBehalfOf] * assetPrice * 100) / borrowed[onBehalfOf];
161: uint256 eusdAmount = (assetAmount * assetPrice) / 1e18;
171: reward2keeper = (reducedAsset * configurator.vaultKeeperRatio(address(this))) / 110;
195: eusdAmount = (eusdAmount * 1e20) / onBehalfOfCollateralRatio;
205: reward2keeper = ((assetAmount * configurator.vaultKeeperRatio(address(this))) * 1e18) / onBehalfOfCollateralRatio;
236: uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider];
239: uint256 collateralAmount = (((eusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
292: if (((depositedAsset[_user] * _assetPrice * 100) / borrowed[_user]) < configurator.getSafeCollateralRatio(address(this))) revert("collateralRatio is Below safeCollateralRatio");
301: return (poolTotalEUSDCirculation * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - lastReportTime)) / (86400 * 365) / 10000;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
127: uint256 onBehalfOfCollateralRatio = (depositedAsset[onBehalfOf] * assetPrice * 100) / getBorrowedOf(onBehalfOf);
132: uint256 peusdAmount = (assetAmount * assetPrice) / 1e18;
141: reward2keeper = (reducedAsset * configurator.vaultKeeperRatio(address(this))) / 110;
161: uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider];
164: uint256 collateralAmount = (((peusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
226: if (((depositedAsset[user] * price * 100) / getBorrowedOf(user)) < configurator.getSafeCollateralRatio(address(this)))
238: return (borrowed[user] * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - feeUpdatedAt[user])) / (86400 * 365) / 10000;
File: contracts/lybra/pools/LybraRETHVault.sol
47: return (_etherPrice() * IRETH(address(collateralAsset)).getExchangeRatio()) / 1e18;
File: contracts/lybra/pools/LybraStETHVault.sol
66: uint256 payAmount = (((realAmount * getAssetPrice()) / 1e18) * getDutchAuctionDiscountPrice()) / 10000;
104: return 10000 - (time / 30 minutes - 1) * 100;
File: contracts/lybra/pools/LybraWbETHVault.sol
35: return (_etherPrice() * IWBETH(address(collateralAsset)).exchangeRatio()) / 1e18;
File: contracts/lybra/pools/LybraWstETHVault.sol
49: return (_etherPrice() * IWstETH(address(collateralAsset)).stEthPerToken()) / 1e18;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
117: uint256 share = (userConvertInfo[msg.sender].depositedEUSDShares * peusdAmount) / userConvertInfo[msg.sender].mintedPeUSD;
157: return (share * configurator.flashloanFee()) / 10_000;
If the length of the arrays are not required to be of the same length, user operations may not be fully executed due to a mismatch in the number of items iterated over, versus the number of items provided in the second array
There is one instance of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
235: function setTokenMiner(address[] calldata _contracts, bool[] calldata _bools) external checkRole(TIMELOCK) {
Ownable2Step
and Ownable2StepUpgradeable
prevent the contract ownership from mistakenly being transferred to an address that cannot handle it (e.g. due to a typo in the address), by requiring that the recipient of the owner permissions actively accept via a contract call of its own.
There are 4 instances of this issue:
File: contracts/lybra/miner/esLBRBoost.sol
7: contract esLBRBoost is Ownable {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
27: contract EUSDMiningIncentives is Ownable {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
24: contract ProtocolRewardsPool is Ownable {
File: contracts/lybra/miner/stakerewardV2pool.sol
16 contract StakingRewardsV2 is Ownable {
17: // Immutable variables for staking and rewards tokens
[L‑07] 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/lybra/Proxy/LybraProxy.sol
8: contract LybraProxy is TransparentUpgradeableProxy {
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 3 instances of this issue:
File: contracts/lybra/pools/LybraStETHVault.sol
89: emit FeeDistribution(address(configurator), payAmount, block.timestamp);
83: emit FeeDistribution(address(configurator), income, block.timestamp);
94: emit LSDValueCaptured(realAmount, payAmount, getDutchAuctionDiscountPrice(), block.timestamp);
The default value for variables is zero, so initializing them to zero is superfluous.
There are 4 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
236: for (uint256 i = 0; i < _contracts.length; i++) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
94: for (uint i = 0; i < _pools.length; i++) {
138: for (uint i = 0; i < pools.length; i++) {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
30: uint8 immutable vaultType = 0;
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 37 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
38: mapping(address => bool) public mintVault;
39: mapping(address => uint256) public mintVaultMaxSupply;
40: mapping(address => bool) public vaultMintPaused;
41: mapping(address => bool) public vaultBurnPaused;
42: mapping(address => uint256) vaultSafeCollateralRatio;
43: mapping(address => uint256) vaultBadCollateralRatio;
44: mapping(address => uint256) public vaultMintFeeApy;
45: mapping(address => uint256) public vaultKeeperRatio;
46: mapping(address => bool) redemptionProvider;
47: mapping(address => bool) public tokenMiner;
File: contracts/lybra/governance/LybraGovernance.sol
28: mapping(address => Receipt) receipts;
29: mapping(uint8 => uint256) supportVotes;
33: mapping (uint256 => ProposalExtraData) public proposalData;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
46: mapping(address => uint256) public userRewardPerTokenPaid;
48: mapping(address => uint256) public rewards;
49: mapping(address => uint256) public userUpdatedAt;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
33: mapping(address => uint) public userRewardPerTokenPaid;
35: mapping(address => uint) public rewards;
36: mapping(address => uint) public time2fullRedemption;
37: mapping(address => uint) public unstakeRatio;
38: mapping(address => uint) public lastWithdrawTime;
File: contracts/lybra/miner/esLBRBoost.sol
9: mapping(address => LockStatus) public userLockStatus;
File: contracts/lybra/miner/stakerewardV2pool.sol
33: mapping(address => uint256) public userRewardPerTokenPaid;
35: mapping(address => uint256) public rewards;
36: mapping(address => uint256) public userUpdatedAt;
41: mapping(address => uint256) public balanceOf;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
28: mapping(address => uint256) public depositedAsset;
29: mapping(address => uint256) borrowed;
32: mapping(address => uint256) depositedTime;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
21: mapping(address => uint256) public depositedAsset;
22: mapping(address => uint256) borrowed;
23: mapping(address => uint256) feeStored;
24: mapping(address => uint256) feeUpdatedAt;
File: contracts/lybra/token/EUSD.sol
51: mapping(address => uint256) private shares;
56: mapping(address => mapping(address => uint256)) private allowances;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
33: mapping(address => ConvertInfo) public userConvertInfo;
According to the Solidity Style Guide, Non-external
/public
variable and function names should begin with an underscore
There are 29 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
42: mapping(address => uint256) vaultSafeCollateralRatio;
43: mapping(address => uint256) vaultBadCollateralRatio;
46: mapping(address => bool) redemptionProvider;
58: uint256 maxStableRatio = 5_000;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
55: AggregatorV3Interface internal etherPriceFeed;
56: AggregatorV3Interface internal lbrPriceFeed;
132: function totalStaked() internal view returns (uint256) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
39: uint256 immutable exitCycle = 90 days;
64: function totalStaked() internal view returns (uint256) {
69: function stakedOf(address staker) internal view returns (uint256) {
File: contracts/lybra/pools/LybraRETHVault.sol
18: IRkPool rkPool = IRkPool(0xDD3f50F8A6CafbE9b31a427582963f465E745AF8);
File: contracts/lybra/pools/LybraWstETHVault.sol
22: Ilido immutable lido;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
22: IPriceFeed immutable etherOracle;
29: mapping(address => uint256) borrowed;
30: uint8 immutable vaultType = 0;
32: mapping(address => uint256) depositedTime;
114: function checkWithdrawal(address user, uint256 amount) internal view returns (uint256 withdrawal) {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
18: uint8 immutable vaultType = 1;
19: IPriceFeed immutable etherOracle;
22: mapping(address => uint256) borrowed;
23: mapping(address => uint256) feeStored;
24: mapping(address => uint256) feeUpdatedAt;
File: contracts/lybra/token/EUSD.sol
51: mapping(address => uint256) private shares;
56: mapping(address => mapping(address => uint256)) private allowances;
File: contracts/lybra/token/LBR.sol
15: uint256 maxSupply = 100_000_000 * 1e18;
16: uint internal immutable ld2sdRatio;
File: contracts/lybra/token/PeUSD.sol
9: uint internal immutable ld2sdRatio;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
32: uint internal immutable ld2sdRatio;
File: contracts/lybra/token/esLBR.sol
20: uint256 maxSupply = 100_000_000 * 1e18;
There are 15 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
298: if(!premiumTradingEnabled || price <= 1005000) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
189: return (stakedLBRLpValue(user) * 10000) / stakedOf(user) < 500;
210: uint256 biddingFee = (reward * biddingFeeRatio) / 10000;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
134: LBR.burn(msg.sender, (amount * grabFeeRatio) / 10000);
File: contracts/lybra/pools/LybraStETHVault.sol
66: uint256 payAmount = (((realAmount * getAssetPrice()) / 1e18) * getDutchAuctionDiscountPrice()) / 10000;
103: if (time < 30 minutes) return 10000;
104: return 10000 - (time / 30 minutes - 1) * 100;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
239: uint256 collateralAmount = (((eusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
301: return (poolTotalEUSDCirculation * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - lastReportTime)) / (86400 * 365) / 10000;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
164: uint256 collateralAmount = (((peusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
238: return (borrowed[user] * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - feeUpdatedAt[user])) / (86400 * 365) / 10000;
Doing so will prevent typo bugs
There are 33 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
187: require(newFee <= 500, "Max Redemption Fee is 5%");
199: if(IVault(pool).vaultType() == 0) {
214: require(newApy <= 200, "Borrow APY cannot exceed 2%");
225: require(newRatio <= 5, "Max Keeper reward is 5%");
247: require(_ratio <= 10_000, "The maximum value is 10000");
291: if(peUSDBalance >= 1e21) {
298: if(!premiumTradingEnabled || price <= 1005000) {
334: if (vaultSafeCollateralRatio[pool] == 0) return 160 * 1e18;
339: if(vaultBadCollateralRatio[pool] == 0) return vaultSafeCollateralRatio[pool] - 1e19;
File: contracts/lybra/governance/LybraGovernance.sol
79: require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
82: require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
File: contracts/lybra/miner/EUSDMiningIncentives.sol
101: require(_biddingRatio <= 8000, "BCE");
106: require(ratio <= 1e20, "BCE");
111: require(ratio <= 1e20, "BCE");
141: if (pool.getVaultType() == 1) {
164: if (totalStaked() == 0) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
59: require(_ratio <= 8000, "BCE");
151: if (a == 0) return 0;
229: if (totalStaked() == 0) return;
231: if(tokenType == 0) {
234: } else if(tokenType == 1) {
File: contracts/lybra/miner/stakerewardV2pool.sol
75: if (totalSupply == 0) {
File: contracts/lybra/pools/LybraRETHVault.sol
28: require(msg.value >= 1 ether, "DNL");
File: contracts/lybra/pools/LybraStETHVault.sol
38: require(msg.value >= 1 ether, "DNL");
76: if (sharesAmount == 0) {
File: contracts/lybra/pools/LybraWbETHVault.sol
21: require(msg.value >= 1 ether, "DNL");
File: contracts/lybra/pools/LybraWstETHVault.sol
30: require(msg.value >= 1 ether, "DNL");
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
73: require(assetAmount >= 1 ether, "Deposit should not be less than 1 stETH.");
194: if (onBehalfOfCollateralRatio >= 1e20) {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
59: require(assetAmount >= 1 ether, "Deposit should not be less than 1 collateral asset.");
File: contracts/lybra/token/EUSD.sol
301: if (totalMintedEUSD == 0) {
312: if (_totalShares == 0) {
415: if (sharesAmount == 0) {
If the plan for your project does not include eventually giving up all ownership control, consider overwriting OpenZeppelin's Ownable
's renounceOwnership()
function in order to disable it.
There are 4 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
27: contract EUSDMiningIncentives is Ownable {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
24: contract ProtocolRewardsPool is Ownable {
File: contracts/lybra/miner/esLBRBoost.sol
7: contract esLBRBoost is Ownable {
File: contracts/lybra/miner/stakerewardV2pool.sol
16: contract StakingRewardsV2 is Ownable {
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/lybra/miner/esLBRBoost.sol
63 if (finishAt <= boostEndTime || block.timestamp <= boostEndTime) {
64 return maxBoost;
65 } else {
66 uint256 time = block.timestamp > finishAt ? finishAt : block.timestamp;
67 return ((boostEndTime - userUpdatedAt) * maxBoost) / (time - userUpdatedAt);
68: }
File: contracts/lybra/token/EUSD.sol
301 if (totalMintedEUSD == 0) {
302 return 0;
303 } else {
304 return _EUSDAmount.mul(_totalShares).div(totalMintedEUSD);
305: }
312 if (_totalShares == 0) {
313 return 0;
314 } else {
315 return _sharesAmount.mul(_totalSupply).div(_totalShares);
316: }
It is often better to declare address
es as immutable
, and assign them via constructor arguments. This allows the code to remain the same across deployments on different networks, and avoids recompilation when addresses need to change.
There are 2 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
153: uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
File: contracts/lybra/pools/LybraRETHVault.sol
18: IRkPool rkPool = IRkPool(0xDD3f50F8A6CafbE9b31a427582963f465E745AF8);
The interfaces below should be defined in separate files, so that it's easier for future projects to import them, and to avoid duplication later on if they need to be used elsewhere in the project
There are 17 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
20 interface IProtocolRewardsPool {
21 function notifyRewardAmount(uint256 amount, uint256 tokenType) external;
22: }
24 interface IeUSDMiningIncentives {
25 function refreshReward(address user) external;
26: }
28 interface IVault {
29 function vaultType() external view returns (uint8);
30: }
32 interface ICurvePool{
33 function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns (uint256);
34 function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns(uint256);
35: }
File: contracts/lybra/miner/EUSDMiningIncentives.sol
19 interface IesLBRBoost {
20 function getUserBoost(
21 address user,
22 uint256 userUpdatedAt,
23 uint256 finishAt
24 ) external view returns (uint256);
25: }
File: contracts/lybra/miner/ProtocolRewardsPool.sol
18 interface IesLBRBoost {
19 function getUnlockTime(
20 address user
21 ) external view returns (uint256 unlockTime);
22: }
File: contracts/lybra/miner/stakerewardV2pool.sol
8 interface IesLBRBoost {
9 function getUserBoost(
10 address user,
11 uint256 userUpdatedAt,
12 uint256 finishAt
13 ) external view returns (uint256);
14: }
File: contracts/lybra/pools/LybraRETHVault.sol
9 interface IRETH {
10 function getExchangeRatio() external view returns (uint256);
11: }
13 interface IRkPool {
14 function deposit() external payable;
15: }
File: contracts/lybra/pools/LybraStETHVault.sol
8 interface Ilido {
9 function submit(address _referral) external payable returns (uint256 StETH);
10: }
File: contracts/lybra/pools/LybraWbETHVault.sol
9 interface IWBETH {
10 function exchangeRatio() external view returns (uint256);
11
12 function deposit(address referral) external payable;
13: }
File: contracts/lybra/pools/LybraWstETHVault.sol
9 interface IWstETH {
10 function stEthPerToken() external view returns (uint256);
11
12 function wrap(uint256 _stETHAmount) external returns (uint256);
13: }
15 interface Ilido {
16 function submit(address _referral) external payable returns (uint256 StETH);
17
18 function approve(address spender, uint256 amount) external returns (bool);
19: }
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
13 interface IPriceFeed {
14 function fetchPrice() external returns (uint256);
15: }
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
9 interface IPriceFeed {
10 function fetchPrice() external returns (uint256);
11: }
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
21 interface FlashBorrower {
22 /// @notice Flash loan callback
23 /// @param amount The amount of tokens received
24 /// @param data Forwarded data from the flash loan request
25 /// @dev Called after receiving the requested flash loan, should return tokens + any fees before the end of the transaction
26 function onFlashLoan(uint256 amount, bytes calldata data) external;
27: }
File: contracts/lybra/token/esLBR.sol
13 interface IProtocolRewardsPool {
14 function refreshReward(address user) external;
15: }
The contract's interface should be imported first, followed by each of the interfaces it uses, followed by all other files. The examples below do not follow this layout.
There is one instance of this issue:
File: contracts/lybra/token/EUSD.sol
6: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
int256
/uint256
are the preferred type names (they're what are used for function signatures), so they should be used consistently
There are 40 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
94: for (uint i = 0; i < _pools.length; i++) {
140: uint borrowed = pool.getBorrowedOf(user);
138: for (uint i = 0; i < pools.length; i++) {
151: (, int etherPrice, , , ) = etherPriceFeed.latestRoundData();
152: (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
153: uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
154: uint256 lbrInLp = (IEUSD(LBR).balanceOf(ethlbrLpToken) * uint(lbrPrice)) / 1e8;
212: (, int lbrPrice, , , ) = lbrPriceFeed.latestRoundData();
File: contracts/lybra/miner/ProtocolRewardsPool.sol
31: uint public rewardPerTokenStored;
167: function earned(address _account) public view returns (uint) {
191: uint reward = rewards[msg.sender];
227: function notifyRewardAmount(uint amount, uint tokenType) external {
File: contracts/lybra/token/LBR.sol
16: uint internal immutable ld2sdRatio;
38: function circulatingSupply() public view virtual override returns (uint) {
49: function _debitFrom(address _from, uint16, bytes32, uint _amount) internal virtual override returns (uint) {
56: function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns (uint) {
61: function _transferFrom(address _from, address _to, uint _amount) internal virtual override returns (uint) {
70: function _ld2sdRatio() internal view virtual override returns (uint) {
File: contracts/lybra/token/PeUSD.sol
9: uint internal immutable ld2sdRatio;
20: function circulatingSupply() public view virtual override returns (uint) {
31: function _debitFrom(address _from, uint16, bytes32, uint _amount) internal virtual override returns (uint) {
38: function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns (uint) {
43: function _transferFrom(address _from, address _to, uint _amount) internal virtual override returns (uint) {
52: function _ld2sdRatio() internal view virtual override returns (uint) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
32: uint internal immutable ld2sdRatio;
171: function circulatingSupply() public view virtual override returns (uint) {
184: function _debitFrom(address _from, uint16, bytes32, uint _amount) internal virtual override returns (uint) {
191: function _creditTo(uint16, address _toAddress, uint _amount) internal virtual override returns (uint) {
196: function _transferFrom(address _from, address _to, uint _amount) internal virtual override returns (uint) {
205: function _ld2sdRatio() internal view virtual override returns (uint) {
Contracts are allowed to override their parents' functions and change the visibility from external
to public
.
There are 13 instances of this issue:
File: contracts/lybra/governance/GovernanceTimelock.sol
29: function checkOnlyRole(bytes32 role, address _sender) public view returns(bool){
File: contracts/lybra/token/EUSD.sol
99: function name() public pure returns (string memory) {
107: function symbol() public pure returns (string memory) {
134: function balanceOf(address _account) public view returns (uint256) {
153: function transfer(address _recipient, uint256 _amount) public returns (bool) {
200: function approve(address _spender, uint256 _amount) public returns (bool) {
226: function transferFrom(address from, address to, uint256 amount) public returns (bool) {
248: function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) {
285: function getTotalShares() public view returns (uint256) {
292: function sharesOf(address _account) public view returns (uint256) {
334: function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
129: function executeFlashloan(FlashBorrower receiver, uint256 eusdAmount, bytes calldata data) public payable {
165 function getAccruedEUSDInterest(
166 address user
167: ) public view returns (uint256 eusdAmount) {
Even assembly can benefit from using readable constants instead of hex/numeric literals
There are 118 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit 130
/// @audit 1e18
/// @audit 150
/// @audit 1e19
127: require(newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] + 1e19, "LNA");
/// @audit 500
187: require(newFee <= 500, "Max Redemption Fee is 5%");
/// @audit 160
/// @audit 1e18
200: require(newRatio >= 160 * 1e18, "eUSD vault safe collateralRatio should more than 160%");
/// @audit 1e19
202: require(newRatio >= vaultBadCollateralRatio[pool] + 1e19, "PeUSD vault safe collateralRatio should more than bad collateralRatio");
/// @audit 200
214: require(newApy <= 200, "Borrow APY cannot exceed 2%");
/// @audit 5
225: require(newRatio <= 5, "Max Keeper reward is 5%");
/// @audit 10_000
247: require(_ratio <= 10_000, "The maximum value is 10000");
/// @audit 10_000
254: if (fee > 10_000) revert('EL');
/// @audit 1e21
291: if(peUSDBalance >= 1e21) {
/// @audit 1e21
296: if (balance > 1e21) {
/// @audit 1e18
297: uint256 price = curvePool.get_dy_underlying(0, 2, 1e18);
/// @audit 1005000
298: if(!premiumTradingEnabled || price <= 1005000) {
/// @audit 998
/// @audit 1e21
303: uint256 amount = curvePool.exchange_underlying(0, 2, balance, balance * price * 998 / 1e21);
/// @audit 160
/// @audit 1e18
334: if (vaultSafeCollateralRatio[pool] == 0) return 160 * 1e18;
/// @audit 1e19
339: if(vaultBadCollateralRatio[pool] == 0) return vaultSafeCollateralRatio[pool] - 1e19;
/// @audit 10_000
357: return (EUSD.totalSupply() * maxStableRatio) / 10_000;
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit 3
52: return esLBR.getPastTotalSupply(timepoint) / 3;
/// @audit 3
144: return 3;
/// @audit 1e23
173: return 1e23;
File: contracts/lybra/miner/esLBRBoost.sol
/// @audit 30
/// @audit 20
/// @audit 1e18
26: esLBRLockSettings.push(esLBRLockSetting(30 days, 20 * 1e18));
/// @audit 90
/// @audit 30
/// @audit 1e18
27: esLBRLockSettings.push(esLBRLockSetting(90 days, 30 * 1e18));
/// @audit 180
/// @audit 50
/// @audit 1e18
28: esLBRLockSettings.push(esLBRLockSetting(180 days, 50 * 1e18));
/// @audit 365
/// @audit 100
/// @audit 1e18
29: esLBRLockSettings.push(esLBRLockSetting(365 days, 100 * 1e18));
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit 8000
101: require(_biddingRatio <= 8000, "BCE");
/// @audit 1e20
106: require(ratio <= 1e20, "BCE");
/// @audit 1e20
111: require(ratio <= 1e20, "BCE");
/// @audit 1e20
142: borrowed = borrowed * (1e20 + peUSDExtraRatio) / 1e20;
/// @audit 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
/// @audit 1e8
153: uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
/// @audit 1e8
154: uint256 lbrInLp = (IEUSD(LBR).balanceOf(ethlbrLpToken) * uint(lbrPrice)) / 1e8;
/// @audit 1e18
168: return rewardPerTokenStored + (rewardRatio * (lastTimeRewardApplicable() - updatedAt) * 1e18) / totalStaked();
/// @audit 100
/// @audit 1e18
181: return 100 * 1e18 + redemptionBoost + esLBRBoost.getUserBoost(_account, userUpdatedAt[_account], finishAt);
/// @audit 1e38
185: return ((stakedOf(_account) * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
/// @audit 10000
/// @audit 500
189: return (stakedLBRLpValue(user) * 10000) / stakedOf(user) < 500;
/// @audit 10000
210: uint256 biddingFee = (reward * biddingFeeRatio) / 10000;
/// @audit 1e8
213: biddingFee = biddingFee * uint256(lbrPrice) / 1e8;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit 8000
59: require(_ratio <= 8000, "BCE");
/// @audit 3
114: require(block.timestamp + exitCycle - 3 days > time2fullRedemption[msg.sender], "ENW");
/// @audit 10000
134: LBR.burn(msg.sender, (amount * grabFeeRatio) / 10000);
/// @audit 75e18
/// @audit 70e18
/// @audit 3
/// @audit 100e18
152: amount = (a * (75e18 - ((time2fullRedemption[user] - block.timestamp) * 70e18) / (exitCycle / 1 days - 3) / 1 days)) / 100e18;
/// @audit 1e18
168: return ((stakedOf(_account) * (rewardPerTokenStored - userRewardPerTokenPaid[_account])) / 1e18) + rewards[_account];
/// @audit 1e18
209: uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
/// @audit 1e18
233: rewardPerTokenStored = rewardPerTokenStored + (share * 1e18) / totalStaked();
/// @audit 1e36
236: rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / token.decimals()) / totalStaked();
/// @audit 1e18
238: rewardPerTokenStored = rewardPerTokenStored + (amount * 1e18) / totalStaked();
File: contracts/lybra/miner/stakerewardV2pool.sol
/// @audit 1e18
79: return rewardPerTokenStored + (rewardRatio * (lastTimeRewardApplicable() - updatedAt) * 1e18) / totalSupply;
/// @audit 100
/// @audit 1e18
102: return 100 * 1e18 + esLBRBoost.getUserBoost(_account, userUpdatedAt[_account], finishAt);
/// @audit 1e38
107: return ((balanceOf[_account] * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit 3
/// @audit 999
/// @audit 1000
115: withdrawal = block.timestamp - 3 days >= depositedTime[user] ? amount : (amount * 999) / 1000;
/// @audit 100
156: uint256 onBehalfOfCollateralRatio = (depositedAsset[onBehalfOf] * assetPrice * 100) / borrowed[onBehalfOf];
/// @audit 1e18
161: uint256 eusdAmount = (assetAmount * assetPrice) / 1e18;
/// @audit 11
164: uint256 reducedAsset = (assetAmount * 11) / 10;
/// @audit 110
171: reward2keeper = (reducedAsset * configurator.vaultKeeperRatio(address(this))) / 110;
/// @audit 100
189: require((totalDepositedAsset * assetPrice * 100) / poolTotalEUSDCirculation < badCollateralRatio, "overallCollateralRatio should below 150%");
/// @audit 100
190: uint256 onBehalfOfCollateralRatio = (depositedAsset[onBehalfOf] * assetPrice * 100) / borrowed[onBehalfOf];
/// @audit 125
/// @audit 1e18
191: require(onBehalfOfCollateralRatio < 125 * 1e18, "borrowers collateralRatio should below 125%");
/// @audit 1e18
193: uint256 eusdAmount = (assetAmount * assetPrice) / 1e18;
/// @audit 1e20
194: if (onBehalfOfCollateralRatio >= 1e20) {
/// @audit 1e20
195: eusdAmount = (eusdAmount * 1e20) / onBehalfOfCollateralRatio;
/// @audit 1e20
/// @audit 1e18
204: if (msg.sender != provider && onBehalfOfCollateralRatio >= 1e20 + configurator.vaultKeeperRatio(address(this)) * 1e18) {
/// @audit 1e18
205: reward2keeper = ((assetAmount * configurator.vaultKeeperRatio(address(this))) * 1e18) / onBehalfOfCollateralRatio;
/// @audit 100
236: uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider];
/// @audit 100
/// @audit 1e18
237: require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%");
/// @audit 1e18
/// @audit 10000
239: uint256 collateralAmount = (((eusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
/// @audit 100
292: if (((depositedAsset[_user] * _assetPrice * 100) / borrowed[_user]) < configurator.getSafeCollateralRatio(address(this))) revert("collateralRatio is Below safeCollateralRatio");
/// @audit 86400
/// @audit 365
/// @audit 10000
301: return (poolTotalEUSDCirculation * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - lastReportTime)) / (86400 * 365) / 10000;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit 100
127: uint256 onBehalfOfCollateralRatio = (depositedAsset[onBehalfOf] * assetPrice * 100) / getBorrowedOf(onBehalfOf);
/// @audit 1e18
132: uint256 peusdAmount = (assetAmount * assetPrice) / 1e18;
/// @audit 11
135: uint256 reducedAsset = (assetAmount * 11) / 10;
/// @audit 110
141: reward2keeper = (reducedAsset * configurator.vaultKeeperRatio(address(this))) / 110;
/// @audit 100
161: uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider];
/// @audit 100
/// @audit 1e18
162: require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%");
/// @audit 1e18
/// @audit 10000
164: uint256 collateralAmount = (((peusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
/// @audit 100
226: if (((depositedAsset[user] * price * 100) / getBorrowedOf(user)) < configurator.getSafeCollateralRatio(address(this)))
/// @audit 86400
/// @audit 365
/// @audit 10000
238: return (borrowed[user] * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - feeUpdatedAt[user])) / (86400 * 365) / 10000;
File: contracts/lybra/pools/LybraRETHVault.sol
/// @audit 1e18
47: return (_etherPrice() * IRETH(address(collateralAsset)).getExchangeRatio()) / 1e18;
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit 1e18
/// @audit 10000
66: uint256 payAmount = (((realAmount * getAssetPrice()) / 1e18) * getDutchAuctionDiscountPrice()) / 10000;
/// @audit 30
/// @audit 10000
103: if (time < 30 minutes) return 10000;
/// @audit 10000
/// @audit 30
/// @audit 100
104: return 10000 - (time / 30 minutes - 1) * 100;
File: contracts/lybra/pools/LybraWbETHVault.sol
/// @audit 1e18
35: return (_etherPrice() * IWBETH(address(collateralAsset)).exchangeRatio()) / 1e18;
File: contracts/lybra/pools/LybraWstETHVault.sol
/// @audit 1e18
49: return (_etherPrice() * IWstETH(address(collateralAsset)).stEthPerToken()) / 1e18;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit 10_000
157: return (share * configurator.flashloanFee()) / 10_000;
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 are 13 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
46: event ClaimReward(address indexed user, uint256 eUSDAmount, address token, uint256 tokenAmount, uint256 time);
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
34: event DepositEther(address indexed onBehalfOf, address asset, uint256 etherAmount, uint256 assetAmount, uint256 timestamp);
36: event DepositAsset(address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
38: event WithdrawAsset(address sponsor, address asset, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
39: event Mint(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
40: event Burn(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
41: event LiquidationRecord(address provider, address keeper, address indexed onBehalfOf, uint256 eusdamount, uint256 liquidateEtherAmount, uint256 keeperReward, bool superLiquidation, uint256 timestamp);
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
26: event DepositEther(address indexed onBehalfOf, address asset, uint256 etherAmount, uint256 assetAmount, uint256 timestamp);
28: event DepositAsset(address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
29: event WithdrawAsset(address sponsor, address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
30: event Mint(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
31: event Burn(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
32: event LiquidationRecord(address provider, address keeper, address indexed onBehalfOf, uint256 eusdamount, uint256 LiquidateAssetAmount, uint256 keeperReward, bool superLiquidation, uint256 timestamp);
The compiler will inline the function, which will avoid JUMP
instructions usually associated with functions
There are 9 instances of this issue:
see instances
File: contracts/lybra/miner/EUSDMiningIncentives.sol
111: require(ratio <= 1e20, "BCE");
File: contracts/lybra/miner/stakerewardV2pool.sol
94: require(_amount > 0, "amount = 0");
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
97: require(onBehalfOf != address(0), "TZA");
98: require(amount > 0, "ZA");
File: contracts/lybra/pools/LybraStETHVault.sol
86: require(success, "TF");
File: contracts/lybra/token/esLBR.sol
39: require(configurator.tokenMiner(msg.sender), "not authorized");
File: contracts/lybra/token/EUSD.sol
460: require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
File: contracts/lybra/token/LBR.sol
33: require(configurator.tokenMiner(msg.sender), "not authorized");
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
134: require(success, "TF");
Using import declarations of the form import {<identifier_name>} from "some/file.sol"
avoids polluting the symbol namespace making flattened files smaller, and speeds up compilation
There are 56 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
17: import "../interfaces/IGovernanceTimelock.sol";
18: import "../interfaces/IEUSD.sol";
File: contracts/lybra/governance/AdminTimelock.sol
5: import "@openzeppelin/contracts/governance/TimelockController.sol";
File: contracts/lybra/governance/GovernanceTimelock.sol
5: import "@openzeppelin/contracts/governance/TimelockController.sol";
File: contracts/lybra/governance/LybraGovernance.sol
5: import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
6: import "../interfaces/IesLBR.sol";
7: import "../interfaces/IGovernanceTimelock.sol";
File: contracts/lybra/miner/esLBRBoost.sol
5: import "@openzeppelin/contracts/access/Ownable.sol";
File: contracts/lybra/miner/EUSDMiningIncentives.sol
12: import "@openzeppelin/contracts/access/Ownable.sol";
13: import "../interfaces/IesLBR.sol";
14: import "../interfaces/IEUSD.sol";
15: import "../interfaces/ILybra.sol";
16: import "../interfaces/Iconfigurator.sol";
17: import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
File: contracts/lybra/miner/ProtocolRewardsPool.sol
12: import "@openzeppelin/contracts/access/Ownable.sol";
13: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
14: import "../interfaces/IEUSD.sol";
15: import "../interfaces/Iconfigurator.sol";
16: import "../interfaces/IesLBR.sol";
File: contracts/lybra/miner/stakerewardV2pool.sol
4: import "@openzeppelin/contracts/access/Ownable.sol";
5: import "../interfaces/IesLBR.sol";
6: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
5: import "../../interfaces/IEUSD.sol";
6: import "../../interfaces/Iconfigurator.sol";
7: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
5: import "../../interfaces/Iconfigurator.sol";
6: import "../../interfaces/IPeUSD.sol";
7: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
File: contracts/lybra/pools/LybraRETHVault.sol
5: import "../interfaces/IEUSD.sol";
6: import "./base/LybraPeUSDVaultBase.sol";
7: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
File: contracts/lybra/pools/LybraStETHVault.sol
5: import "../interfaces/IEUSD.sol";
6: import "./base/LybraEUSDVaultBase.sol";
File: contracts/lybra/pools/LybraWbETHVault.sol
5: import "../interfaces/IEUSD.sol";
6: import "./base/LybraPeUSDVaultBase.sol";
7: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
File: contracts/lybra/pools/LybraWstETHVault.sol
5: import "../interfaces/IEUSD.sol";
6: import "./base/LybraPeUSDVaultBase.sol";
7: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
File: contracts/lybra/Proxy/LybraProxyAdmin.sol
4: import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
File: contracts/lybra/Proxy/LybraProxy.sol
5: import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
File: contracts/lybra/token/esLBR.sol
10: import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
11: import "../interfaces/Iconfigurator.sol";
File: contracts/lybra/token/EUSD.sol
5: import "@openzeppelin/contracts/utils/math/SafeMath.sol";
6: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7: import "@openzeppelin/contracts/utils/Context.sol";
8: import "../interfaces/Iconfigurator.sol";
File: contracts/lybra/token/LBR.sol
9: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
10: import "../interfaces/Iconfigurator.sol";
11: import "../../OFT/BaseOFTV2.sol";
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
16: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
17: import "../../OFT/BaseOFTV2.sol";
18: import "../interfaces/Iconfigurator.sol";
19: import "../interfaces/IEUSD.sol";
File: contracts/lybra/token/PeUSD.sol
5: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
6: import "../../OFT/BaseOFTV2.sol";
Not extending the interface may lead to the wrong function signature being used, leading to unexpected behavior. If the interface is in fact being implemented, use the override
keyword to indicate that fact
There are 7 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit IAPWineIBT.hasRole()
37: contract Configurator {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit IAPWineIBT.mint()
17: abstract contract LybraEUSDVaultBase {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit IAPWineIBT.mint()
13: abstract contract LybraPeUSDVaultBase {
File: contracts/lybra/token/esLBR.sol
/// @audit IAPWineIBT.mint()
17: contract esLBR is ERC20Votes {
File: contracts/lybra/token/EUSD.sol
/// @audit IAPWineIBT.name(), IAPWineIBT.symbol(), IAPWineIBT.decimals(), IAPWineIBT.totalSupply(), IAPWineIBT.balanceOf(), IAPWineIBT.transfer(), IAPWineIBT.allowance(), IAPWineIBT.approve(), IAPWineIBT.transferFrom(), IAPWineIBT.increaseAllowance(), IAPWineIBT.decreaseAllowance(), IAPWineIBT.mint()
37: contract EUSD is IERC20, Context {
File: contracts/lybra/token/LBR.sol
/// @audit IAPWineIBT.mint()
13: contract LBR is BaseOFTV2, ERC20 {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit IAPWineIBT.mint()
29: contract PeUSDMainnet is BaseOFTV2, ERC20 {
There are 2 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
228: require(msg.sender == address(configurator));
File: contracts/lybra/pools/LybraRETHVault.sol
42: require(configurator.hasRole(keccak256("TIMELOCK"), msg.sender));
Events help non-contract tools to track changes, and events prevent users from being surprised by changes
There are 11 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
84 function setToken(address _lbr, address _eslbr) external onlyOwner {
85 LBR = _lbr;
86 esLBR = _eslbr;
87: }
93 function setPools(address[] memory _pools) external onlyOwner {
94 for (uint i = 0; i < _pools.length; i++) {
95 require(configurator.mintVault(_pools[i]), "NOT_VAULT");
96 }
97 pools = _pools;
98: }
100 function setBiddingCost(uint256 _biddingRatio) external onlyOwner {
101 require(_biddingRatio <= 8000, "BCE");
102 biddingFeeRatio = _biddingRatio;
103: }
105 function setExtraRatio(uint256 ratio) external onlyOwner {
106 require(ratio <= 1e20, "BCE");
107 extraRatio = ratio;
108: }
110 function setPeUSDExtraRatio(uint256 ratio) external onlyOwner {
111 require(ratio <= 1e20, "BCE");
112 peUSDExtraRatio = ratio;
113: }
119 function setRewardsDuration(uint256 _duration) external onlyOwner {
120 require(finishAt < block.timestamp, "reward duration not finished");
121 duration = _duration;
122: }
124 function setEthlbrStakeInfo(address _pool, address _lp) external onlyOwner {
125 ethlbrStakePool = _pool;
126 ethlbrLpToken = _lp;
127: }
128 function setEUSDBuyoutAllowed(bool _bool) external onlyOwner {
129 isEUSDBuyoutAllowed = _bool;
130: }
File: contracts/lybra/miner/ProtocolRewardsPool.sol
58 function setGrabCost(uint256 _ratio) external onlyOwner {
59 require(_ratio <= 8000, "BCE");
60 grabFeeRatio = _ratio;
61: }
File: contracts/lybra/miner/stakerewardV2pool.sol
121 function setRewardsDuration(uint256 _duration) external onlyOwner {
122 require(finishAt < block.timestamp, "reward duration not finished");
123 duration = _duration;
124: }
File: contracts/lybra/pools/LybraStETHVault.sol
25 function setLidoRebaseTime(uint256 _time) external {
26 require(configurator.hasRole(keccak256("ADMIN"), msg.sender), "not authorized");
27 lidoRebaseTime = _time;
28: }
This should especially be done if the new value is not required to be different from the old value
There are 9 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit setBadCollateralRatio()
129: emit SafeCollateralRatioChanged(pool, newRatio);
/// @audit setProtocolRewardsPool()
139: emit ProtocolRewardsPoolChanged(addr, block.timestamp);
/// @audit setEUSDMiningIncentives()
149: emit EUSDMiningIncentivesChanged(addr, block.timestamp);
/// @audit setRedemptionFee()
189: emit RedemptionFeeChanged(newFee);
/// @audit setSafeCollateralRatio()
205: emit SafeCollateralRatioChanged(pool, newRatio);
/// @audit setBorrowApy()
216: emit BorrowApyChanged(pool, newApy);
/// @audit setKeeperRatio()
227: emit KeeperRatioChanged(pool, newRatio);
/// @audit setTokenMiner()
238: emit tokenMinerChanges(_contracts[i], _bools[i]);
/// @audit setFlashloanFee()
255: emit FlashloanFeeUpdated(fee);
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 are 3 instances of this issue:
File: contracts/lybra/governance/GovernanceTimelock.sol
/// @audit seen in contracts/lybra/configuration/LybraConfigurator.sol
10: bytes32 public constant DAO = keccak256("DAO");
/// @audit seen in contracts/lybra/configuration/LybraConfigurator.sol
11: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
/// @audit seen in contracts/lybra/configuration/LybraConfigurator.sol
12: bytes32 public constant ADMIN = keccak256("ADMIN");
There is one instance of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
176 /**
177 * @dev See {IERC165-supportsInterface}.
178 */
179: function supportsInterface(bytes4 interfaceId) public view virtual override(GovernorTimelockControl) returns (bool) {
Some lines use // x
and some use //x
. The instances below point out the usages that don't follow the majority, within each file
There are 6 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
63: //etherOracle = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
File: contracts/lybra/miner/stakerewardV2pool.sol
43: ///events
File: contracts/lybra/pools/LybraStETHVault.sol
39: //convert to steth
77: //EUSD totalSupply is 0: assume that shares correspond to EUSD 1-to-1
80: //Income is distributed to LBR staker.
File: contracts/lybra/token/EUSD.sol
416: //EUSD totalSupply is 0: assume that shares correspond to EUSD 1-to-1
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 maximumum line length of 120 characters, so the lines below should be split when they reach that length.
There are 92 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
5: * @dev The Configurator contract is used to set various parameters and control functionalities of the Lybra Protocol. It is based on OpenZeppelin's Proxy and AccessControl libraries, allowing the DAO to control contract upgrades. There are three types of governance roles:
6: * * DAO: A time-locked contract initiated by esLBR voting, with a minimum effective period of 14 days. After the vote is passed, only the developer can execute the action.
57: // Limiting the maximum percentage of eUSD that can be cross-chain transferred to L2 in relation to the total supply.
73: /// @param fee The new fee for this token as a percentage and multiplied by 100 to avoid decimals (for example, 10% is 10_00)
127: require(newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] + 1e19, "LNA");
202: require(newRatio >= vaultBadCollateralRatio[pool] + 1e19, "PeUSD vault safe collateralRatio should more than bad collateralRatio");
284: * If premiumTradingEnabled is false or the price of the trading pair (0, 2) on the Curve pool is less than or equal to 1005000, eUSD rewards are directly transferred to the LybraProtocolRewardsPool.
285: * Otherwise, a controlled premium trading is performed by exchanging eUSD for the third token in the trading pair on the Curve pool, using a calculated amount to maintain a premium.
File: contracts/lybra/governance/AdminTimelock.sol
8: constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {}
File: contracts/lybra/governance/GovernanceTimelock.sol
15: constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {
File: contracts/lybra/governance/LybraGovernance.sol
38: constructor(string memory name_, TimelockController timelock_, address _esLBR) GovernorTimelockControl(timelock_) Governor(name_) {
60: return proposalData[proposalId].supportVotes[1] + proposalData[proposalId].supportVotes[2] >= quorum(proposalSnapshot(proposalId));
76: function _countVote(uint256 proposalId, address account, uint8 support, uint256 weight, bytes memory) internal override {
106: function _execute(uint256 /* proposalId */, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) internal virtual override {
112: function proposals(uint256 proposalId) external view returns (uint256 id, address proposer, uint256 eta, uint256 startBlock, uint256 endBlock, uint256 forVotes, uint256 againstVotes, uint256 abstainVotes, bool canceled, bool executed) {
129: function getReceipt(uint256 proposalId, address account) external view returns (bool voted, uint8 support, uint256 votes){
170: * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
179: function supportsInterface(bytes4 interfaceId) public view virtual override(GovernorTimelockControl) returns (bool) {
File: contracts/lybra/miner/esLBRBoost.sol
42: require(userStatus.duration <= _setting.duration, "Your lock-in period has not ended, and the term can only be extended, not reduced.");
44: userLockStatus[msg.sender] = LockStatus(block.timestamp + _setting.duration, _setting.duration, _setting.miningBoost);
55: * there are several scenarios that could occur, including no acceleration, full acceleration, and partial acceleration.
File: contracts/lybra/miner/EUSDMiningIncentives.sol
60: event ClaimedOtherEarnings(address indexed user, address indexed Victim, uint256 buyAmount, uint256 biddingFee, bool useEUSD, uint256 time);
153: uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
185: return ((stakedOf(_account) * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
File: contracts/lybra/miner/ProtocolRewardsPool.sol
5: * @title ProtocolRewardsPool is a derivative version of Synthetix StakingRewards.sol, distributing Protocol revenue to esLBR stakers.
88: require(block.timestamp >= esLBRBoost.getUnlockTime(msg.sender), "Your lock-in period has not ended. You can't convert your esLBR now.");
111: * with the lost portion being recorded in the contract and available for others to purchase in LBR at a certain ratio.
152: amount = (a * (75e18 - ((time2fullRedemption[user] - block.timestamp) * 70e18) / (exitCycle / 1 days - 3) / 1 days)) / 100e18;
157: amount = block.timestamp > time2fullRedemption[user] ? unstakeRatio[user] * (time2fullRedemption[user] - lastWithdrawTime[user]) : unstakeRatio[user] * (block.timestamp - lastWithdrawTime[user]);
168: return ((stakedOf(_account) * (rewardPerTokenStored - userRewardPerTokenPaid[_account])) / 1e18) + rewards[_account];
187: * @notice When claiming protocol rewards earnings, if there is a sufficient amount of eUSD in the ProtocolRewards Pool,
188: * the eUSD will be prioritized for distribution. Distributes earnings in the order of peUSD and other stablecoins if the eUSD balance is insufficient..
203: emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);
211: emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(token), reward - eUSDShare, block.timestamp);
221: * @dev Receives stablecoin tokens sent by the configurator contract and records the protocol rewards accumulation per esLBR held.
225: * @dev When receiving stablecoin tokens other than eUSD, the decimals of the token are converted to 18 for consistent calculations.
File: contracts/lybra/miner/stakerewardV2pool.sol
107: return ((balanceOf[_account] * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
34: event DepositEther(address indexed onBehalfOf, address asset, uint256 etherAmount, uint256 assetAmount, uint256 timestamp);
41: event LiquidationRecord(address provider, address keeper, address indexed onBehalfOf, uint256 eusdamount, uint256 liquidateEtherAmount, uint256 keeperReward, bool superLiquidation, uint256 timestamp);
43: event RigidRedemption(address indexed caller, address indexed provider, uint256 eusdAmount, uint256 collateralAmount, uint256 timestamp);
54: * @notice Allowing direct deposits of ETH, the pool may convert it into the corresponding collateral during the implementation.
124: * - `amount` Must be higher than 0. Individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000
146: * @notice When overallCollateralRatio is above 150%, Keeper liquidates borrowers whose collateral ratio is below badCollateralRatio, using EUSD provided by Liquidation Provider.
152: * @dev After liquidation, borrower's debt is reduced by collateralAmount * etherPrice, collateral is reduced by the collateralAmount corresponding to 110% of the value. Keeper gets keeperRatio / 110 of Liquidation Reward and Liquidator gets the remaining stETH.
157: require(onBehalfOfCollateralRatio < badCollateralRatio, "Borrowers collateral ratio should below badCollateralRatio");
175: emit LiquidationRecord(provider, msg.sender, onBehalfOf, eusdAmount, reducedAsset, reward2keeper, false, block.timestamp);
179: * @notice When overallCollateralRatio is below badCollateralRatio, borrowers with collateralRatio below 125% could be fully liquidated.
185: * @dev After Liquidation, borrower's debt is reduced by collateralAmount * etherPrice, deposit is reduced by collateralAmount * borrower's collateralRatio. Keeper gets a liquidation reward of `keeperRatio / borrower's collateralRatio
189: require((totalDepositedAsset * assetPrice * 100) / poolTotalEUSDCirculation < badCollateralRatio, "overallCollateralRatio should below 150%");
197: require(EUSD.allowance(provider, address(this)) >= eusdAmount, "provider should authorize to provide liquidation EUSD");
204: if (msg.sender != provider && onBehalfOfCollateralRatio >= 1e20 + configurator.vaultKeeperRatio(address(this)) * 1e18) {
205: reward2keeper = ((assetAmount * configurator.vaultKeeperRatio(address(this))) * 1e18) / onBehalfOfCollateralRatio;
210: emit LiquidationRecord(provider, msg.sender, onBehalfOf, eusdAmount, assetAmount, reward2keeper, true, block.timestamp);
214: * @notice When stETH balance increases through LSD or other reasons, the excess income is sold for EUSD, allocated to EUSD holders through rebase mechanism.
239: uint256 collateralAmount = (((eusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
259: function _mintEUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
274: * @dev Refresh LBR reward before reducing providers debt. Refresh Lybra generated service fee before reducing totalEUSDCirculation.
289: * @dev Get USD value of current collateral asset and minted EUSD through price oracle / Collateral asset USD value must higher than safe Collateral Ratio.
292: if (((depositedAsset[_user] * _assetPrice * 100) / borrowed[_user]) < configurator.getSafeCollateralRatio(address(this))) revert("collateralRatio is Below safeCollateralRatio");
301: return (poolTotalEUSDCirculation * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - lastReportTime)) / (86400 * 365) / 10000;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
26: event DepositEther(address indexed onBehalfOf, address asset, uint256 etherAmount, uint256 assetAmount, uint256 timestamp);
32: event LiquidationRecord(address provider, address keeper, address indexed onBehalfOf, uint256 eusdamount, uint256 LiquidateAssetAmount, uint256 keeperReward, bool superLiquidation, uint256 timestamp);
34: event RigidRedemption(address indexed caller, address indexed provider, uint256 peusdAmount, uint256 assetAmount, uint256 timestamp);
94: * - `amount` Must be higher than 0. Individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000
117: * @notice When overallCollateralRatio is above 150%, Keeper liquidates borrowers whose collateral ratio is below badCollateralRatio, using PeUSD provided by Liquidation Provider.
123: * @dev After liquidation, borrower's debt is reduced by assetAmount * assetPrice, collateral is reduced by the assetAmount corresponding to 110% of the value. Keeper gets keeperRatio / 110 of Liquidation Reward and Liquidator gets the remaining stETH.
128: require(onBehalfOfCollateralRatio < configurator.getBadCollateralRatio(address(this)), "Borrowers collateral ratio should below badCollateralRatio");
145: emit LiquidationRecord(provider, msg.sender, onBehalfOf, peusdAmount, reducedAsset, reward2keeper, false, block.timestamp);
164: uint256 collateralAmount = (((peusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000;
171: * @dev Refresh LBR reward before adding providers debt. Refresh Lybra generated service fee before adding totalSupply. Check providers collateralRatio cannot below `safeCollateralRatio`after minting.
173: function _mintPeUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
190: * @dev Refresh LBR reward before reducing providers debt. Refresh Lybra generated service fee before reducing totalPeUSDCirculation.
223: * @dev Get USD value of current collateral asset and minted EUSD through price oracle / Collateral asset USD value must higher than safe Collateral Ratio.
226: if (((depositedAsset[user] * price * 100) / getBorrowedOf(user)) < configurator.getSafeCollateralRatio(address(this)))
238: return (borrowed[user] * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - feeUpdatedAt[user])) / (86400 * 365) / 10000;
File: contracts/lybra/pools/LybraStETHVault.sol
55: * @notice When stETH balance increases through LSD or other reasons, the excess income is sold for EUSD, allocated to EUSD holders through rebase mechanism.
98: * @notice Reduces the discount for the issuance of additional tokens based on the rebase time using the Dutch auction method.
File: contracts/lybra/pools/LybraWstETHVault.sol
25: constructor(address _lido, address _peusd, address _oracle, address _asset, address _config) LybraPeUSDVaultBase(_peusd, _oracle, _asset,_config) {
File: contracts/lybra/token/esLBR.sol
5: * @title esLBR is an ERC20-compliant token, but cannot be transferred and can only be minted through the esLBRMinter contract or redeemed for LBR by destruction.
File: contracts/lybra/token/EUSD.sol
77: event SharesBurnt(address indexed account, uint256 preRebaseTokenAmount, uint256 postRebaseTokenAmount, uint256 sharesAmount);
411: function mint(address _recipient, uint256 _mintAmount) external onlyMintVault MintPaused returns (uint256 newTotalShares) {
440: function burn(address _account, uint256 _burnAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
459: function burnShares(address _account, uint256 _sharesAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
File: contracts/lybra/token/LBR.sol
18: constructor(address _config, uint8 _sharedDecimals, address _lzEndpoint) ERC20("LBR", "LBR") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
25: /// @dev Called after receiving the requested flash loan, should return tokens + any fees before the end of the transaction
55: constructor(address _config, uint8 _sharedDecimals, address _lzEndpoint) ERC20("peg-eUSD", "PeUSD") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
76: * @param user The address of the user who wants to deposit eUSD and mint PeUSD. It can only be the contract itself or the msg.sender.
109: * @param peusdAmount The amount of PeUSD tokens to burn and retrieve eUSD. The user's balance of PeUSD tokens must be greater than or equal to this amount.
117: uint256 share = (userConvertInfo[msg.sender].depositedEUSDShares * peusdAmount) / userConvertInfo[msg.sender].mintedPeUSD;
168: return EUSD.getMintedEUSDByShares(userConvertInfo[user].depositedEUSDShares) - userConvertInfo[user].mintedPeUSD;
181: * @dev The following functions are internal functions of Layer Zero OFT, used for internal calls during cross-chain operations.
File: contracts/lybra/token/PeUSD.sol
11: constructor(uint8 _sharedDecimals, address _lzEndpoint) ERC20("peg-eUSD", "peUSD") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
[N‑26] Variable names that consist of all capital letters should be reserved for constant
/immutable
variables
If the variable needs to be different based on which class it comes from, a view
/pure
function should be used instead (e.g. like this).
There are 4 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
54: IEUSD public EUSD;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
32: address public LBR;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
27: IesLBR public LBR;
194: IEUSD EUSD = IEUSD(configurator.getEUSDAddress());
There are 19 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
15: pragma solidity ^0.8.17;
File: contracts/lybra/governance/AdminTimelock.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/governance/GovernanceTimelock.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/governance/LybraGovernance.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/esLBRBoost.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/stakerewardV2pool.sol
2: pragma solidity ^0.8;
File: contracts/lybra/pools/LybraRETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraStETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraWbETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraWstETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/Proxy/LybraProxyAdmin.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/Proxy/LybraProxy.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/esLBR.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/EUSD.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/LBR.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
14: pragma solidity ^0.8.17;
File: contracts/lybra/token/PeUSD.sol
3: pragma solidity ^0.8.17;
There is one instance of this issue:
File: contracts/lybra/governance/GovernanceTimelock.sol
/// @audit contructor
9: // contructor()
There are 11 instances of this issue:
see instances
File: contracts/lybra/governance/AdminTimelock.sol
File: contracts/lybra/governance/GovernanceTimelock.sol
File: contracts/lybra/miner/stakerewardV2pool.sol
File: contracts/lybra/pools/LybraRETHVault.sol
File: contracts/lybra/pools/LybraWbETHVault.sol
File: contracts/lybra/pools/LybraWstETHVault.sol
File: contracts/lybra/Proxy/LybraProxyAdmin.sol
File: contracts/lybra/Proxy/LybraProxy.sol
File: contracts/lybra/token/esLBR.sol
File: contracts/lybra/token/LBR.sol
File: contracts/lybra/token/PeUSD.sol
There are 133 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit Missing: '@param _eusd'
/// @audit Missing: '@param _peusd'
95 /**
96 * @notice Initializes the eUSD and peUSD address. This function can only be executed once.
97 */
98: function initToken(address _eusd, address _peusd) external onlyRole(DAO) {
/// @audit Missing: '@param pool'
/// @audit Missing: '@param newRatio'
123 /**
124 * @notice badCollateralRatio can be decided by DAO,starts at 130%
125 */
126: function setBadCollateralRatio(address pool, uint256 newRatio) external onlyRole(DAO) {
/// @audit Missing: '@param pool'
/// @audit Missing: '@param newRatio'
192 /**
193 * @notice safeCollateralRatio can be decided by TIMELOCK.
194 * The eUSD vault requires a minimum safe collateral rate of 160%,
195 * On the other hand, the PeUSD vault requires a safe collateral rate at least 10% higher
196 * than the liquidation collateral rate, providing an additional buffer to protect against liquidation risks.
197 */
198: function setSafeCollateralRatio(address pool, uint256 newRatio) external checkRole(TIMELOCK) {
/// @audit Missing: '@param _bool'
265 /**
266 * @notice User chooses to become a Redemption Provider
267 */
268: function becomeRedemptionProvider(bool _bool) external {
/// @audit Missing: '@param user'
274 /**
275 * @dev Updates the mining data for the user's eUSD mining incentives.
276 */
277: function refreshMintReward(address user) external {
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit Missing: '@param name_'
/// @audit Missing: '@param timelock_'
/// @audit Missing: '@param _esLBR'
37 // TimelockController timelockAddress;
38: constructor(string memory name_, TimelockController timelock_, address _esLBR) GovernorTimelockControl(timelock_) Governor(name_) {
/// @audit Missing: '@param timepoint'
44 /**
45 * @notice module:user-config
46 * @dev Minimum number of cast voted required for a proposal to be successful.
47 *
48 * NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
49 * quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
50 */
51: function quorum(uint256 timepoint) public view override returns (uint256){
/// @audit Missing: '@param proposalId'
56 /**
57 * @dev Amount of votes already cast passes the threshold limit.
58 */
59: function _quorumReached(uint256 proposalId) internal view override returns (bool){
/// @audit Missing: '@param proposalId'
63 /**
64 * @dev Is the proposal successful or not.
65 */
66: function _voteSucceeded(uint256 proposalId) internal view override returns (bool){
/// @audit Missing: '@param proposalId'
/// @audit Missing: '@param account'
/// @audit Missing: '@param support'
/// @audit Missing: '@param weight'
/// @audit Missing: '@param bytes'
70 /**
71 * @dev Register a vote for `proposalId` by `account` with a given `support`, voting `weight` and voting `params`.
72 *
73 * Note: Support is generic and can represent various things depending on the voting system used.
74 */
75
76: function _countVote(uint256 proposalId, address account, uint8 support, uint256 weight, bytes memory) internal override {
/// @audit Missing: '@param account'
/// @audit Missing: '@param timepoint'
/// @audit Missing: '@param bytes'
94 /**
95 * @dev Get the voting weight of `account` at a specific `timepoint`, for a vote as described by `params`.
96 */
97
98: function _getVotes(address account, uint256 timepoint, bytes memory) internal view override returns (uint256){
/// @audit Missing: '@param uint256'
/// @audit Missing: '@param targets'
/// @audit Missing: '@param values'
/// @audit Missing: '@param calldatas'
/// @audit Missing: '@param descriptionHash'
103 /**
104 * @dev Overridden execute function that run the already queued proposal through the timelock.
105 */
106: function _execute(uint256 /* proposalId */, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) internal virtual override {
/// @audit Missing: '@param interfaceId'
176 /**
177 * @dev See {IERC165-supportsInterface}.
178 */
179: function supportsInterface(bytes4 interfaceId) public view virtual override(GovernorTimelockControl) returns (bool) {
File: contracts/lybra/miner/esLBRBoost.sol
/// @audit Missing: '@param setting'
32 // Function to add a new lock setting
33: function addLockSetting(esLBRLockSetting memory setting) external onlyOwner {
/// @audit Missing: '@param id'
37 // Function to set the user's lock status
38: function setLockStatus(uint256 id) external {
/// @audit Missing: '@param user'
47 // Function to get the user's unlock time
48: function getUnlockTime(address user) external view returns (uint256 unlockTime) {
/// @audit Missing: '@param user'
/// @audit Missing: '@param userUpdatedAt'
/// @audit Missing: '@param finishAt'
52 /**
53 * @notice calculate the user's mining boost based on their lock status
54 * @dev Based on the user's userUpdatedAt time, finishAt time, and the current time,
55 * there are several scenarios that could occur, including no acceleration, full acceleration, and partial acceleration.
56 */
57: function getUserBoost(address user, uint256 userUpdatedAt, uint256 finishAt) external view returns (uint256) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit Missing: '@param _config'
/// @audit Missing: '@param _boost'
/// @audit Missing: '@param _etherOracle'
/// @audit Missing: '@param _lbrOracle'
63 //etherOracle = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419
64: constructor(address _config, address _boost, address _etherOracle, address _lbrOracle) {
/// @audit Missing: '@param _account'
171 /**
172 * @notice Update user's claimable reward data and record the timestamp.
173 */
174: function refreshReward(address _account) external updateReward(_account) {}
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit Missing: '@param staker'
68 // User address => esLBR balance
69: function stakedOf(address staker) internal view returns (uint256) {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit Missing: '@param mintAmount'
53 /**
54 * @notice Allowing direct deposits of ETH, the pool may convert it into the corresponding collateral during the implementation.
55 * While depositing, it is possible to simultaneously mint eUSD for oneself.
56 * Emits a `DepositEther` event.
57 *
58 * Requirements:
59 * - `mintAmount` Send 0 if doesn't mint EUSD
60 * - msg.value Must be higher than 0.
61 */
62: function depositEtherToMint(uint256 mintAmount) external payable virtual;
/// @audit Missing: '@param assetAmount'
/// @audit Missing: '@param mintAmount'
64 /**
65 * @notice Deposit collateral and allow minting eUSD for oneself.
66 * Emits a `DepositAsset` event.
67 *
68 * Requirements:
69 * - `assetAmount` Must be higher than 0.
70 * - `mintAmount` Send 0 if doesn't mint EUSD
71 */
72: function depositAssetToMint(uint256 assetAmount, uint256 mintAmount) external virtual {
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param amount'
88 /**
89 * @notice Withdraw collateral assets to an address
90 * Emits a `WithdrawEther` event.
91 *
92 * Requirements:
93 * - `onBehalfOf` cannot be the zero address.
94 * - `amount` Must be higher than 0.
95 *
96 * @dev Withdraw stETH. Check user’s collateral ratio after withdrawal, should be higher than `safeCollateralRatio`
97 */
98: function withdraw(address onBehalfOf, uint256 amount) external virtual {
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param amount'
118 /**
119 * @notice The mint amount number of EUSD is minted to the address
120 * Emits a `Mint` event.
121 *
122 * Requirements:
123 * - `onBehalfOf` cannot be the zero address.
124 * - `amount` Must be higher than 0. Individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000
125 */
126: function mint(address onBehalfOf, uint256 amount) external {
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param amount'
132 /**
133 * @notice Burn the amount of EUSD and payback the amount of minted EUSD
134 * Emits a `Burn` event.
135 * Requirements:
136 * - `onBehalfOf` cannot be the zero address.
137 * - `amount` Must be higher than 0.
138 * @dev Calling the internal`_repay`function.
139 */
140: function burn(address onBehalfOf, uint256 amount) external {
/// @audit Missing: '@param provider'
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param assetAmount'
145 /**
146 * @notice When overallCollateralRatio is above 150%, Keeper liquidates borrowers whose collateral ratio is below badCollateralRatio, using EUSD provided by Liquidation Provider.
147 *
148 * Requirements:
149 * - onBehalfOf Collateral Ratio should be below badCollateralRatio
150 * - collateralAmount should be less than 50% of collateral
151 * - provider should authorize Lybra to utilize EUSD
152 * @dev After liquidation, borrower's debt is reduced by collateralAmount * etherPrice, collateral is reduced by the collateralAmount corresponding to 110% of the value. Keeper gets keeperRatio / 110 of Liquidation Reward and Liquidator gets the remaining stETH.
153 */
154: function liquidation(address provider, address onBehalfOf, uint256 assetAmount) external virtual {
/// @audit Missing: '@param provider'
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param assetAmount'
178 /**
179 * @notice When overallCollateralRatio is below badCollateralRatio, borrowers with collateralRatio below 125% could be fully liquidated.
180 * Emits a `LiquidationRecord` event.
181 *
182 * Requirements:
183 * - Current overallCollateralRatio should be below badCollateralRatio
184 * - `onBehalfOf`collateralRatio should be below 125%
185 * @dev After Liquidation, borrower's debt is reduced by collateralAmount * etherPrice, deposit is reduced by collateralAmount * borrower's collateralRatio. Keeper gets a liquidation reward of `keeperRatio / borrower's collateralRatio
186 */
187: function superLiquidation(address provider, address onBehalfOf, uint256 assetAmount) external virtual {
/// @audit Missing: '@param payAmount'
213 /**
214 * @notice When stETH balance increases through LSD or other reasons, the excess income is sold for EUSD, allocated to EUSD holders through rebase mechanism.
215 * Emits a `LSDistribution` event.
216 *
217 * *Requirements:
218 * - stETH balance in the contract cannot be less than totalDepositedAsset after exchange.
219 * @dev Income is used to cover accumulated Service Fee first.
220 */
221: function excessIncomeDistribution(uint256 payAmount) external virtual;
/// @audit Missing: '@param provider'
/// @audit Missing: '@param eusdAmount'
223 /**
224 * @notice Choose a Redemption Provider, Rigid Redeem `eusdAmount` of EUSD and get 1:1 value of stETH
225 * Emits a `RigidRedemption` event.
226 *
227 * *Requirements:
228 * - `provider` must be a Redemption Provider
229 * - `provider`debt must equal to or above`eusdAmount`
230 * @dev Service Fee for rigidRedemption `redemptionFee` is set to 0.5% by default, can be revised by DAO.
231 */
232: function rigidRedemption(address provider, uint256 eusdAmount) external virtual {
/// @audit Missing: '@param _provider'
/// @audit Missing: '@param _onBehalfOf'
/// @audit Missing: '@param _amount'
271 /**
272 * @notice Burn _provideramount EUSD to payback minted EUSD for _onBehalfOf.
273 *
274 * @dev Refresh LBR reward before reducing providers debt. Refresh Lybra generated service fee before reducing totalEUSDCirculation.
275 */
276: function _repay(address _provider, address _onBehalfOf, uint256 _amount) internal virtual {
/// @audit Missing: '@param _user'
/// @audit Missing: '@param _assetPrice'
288 /**
289 * @dev Get USD value of current collateral asset and minted EUSD through price oracle / Collateral asset USD value must higher than safe Collateral Ratio.
290 */
291: function _checkHealth(address _user, uint256 _assetPrice) internal view {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit Missing: '@param assetAmount'
/// @audit Missing: '@param mintAmount'
50 /**
51 * @notice Deposit staked ETH, update the interest distribution, can mint PeUSD directly
52 * Emits a `DepositAsset` event.
53 *
54 * Requirements:
55 * - `assetAmount` Must be higher than 0.
56 * - `mintAmount` Send 0 if doesn't mint PeUSD
57 */
58: function depositAssetToMint(uint256 assetAmount, uint256 mintAmount) external virtual {
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param amount'
72 /**
73 * @notice Withdraw collateral assets to an address
74 * Emits a `WithdrawAsset` event.
75 *
76 * Requirements:
77 * - `onBehalfOf` cannot be the zero address.
78 * - `amount` Must be higher than 0.
79 *
80 * @dev Withdraw stETH. Check user’s collateral ratio after withdrawal, should be higher than `safeCollateralRatio`
81 */
82: function withdraw(address onBehalfOf, uint256 amount) external virtual {
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param amount'
88 /**
89 * @notice The mint amount number of PeUSD is minted to the address
90 * Emits a `Mint` event.
91 *
92 * Requirements:
93 * - `onBehalfOf` cannot be the zero address.
94 * - `amount` Must be higher than 0. Individual mint amount shouldn't surpass 10% when the circulation reaches 10_000_000
95 */
96: function mint(address onBehalfOf, uint256 amount) external virtual {
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param amount'
102 /**
103 * @notice Burn the amount of PeUSD and payback the amount of minted PeUSD
104 * Emits a `Burn` event.
105 * Requirements:
106 * - `onBehalfOf` cannot be the zero address.
107 * - `amount` Must be higher than 0.
108 * @dev Calling the internal`_repay`function.
109 */
110: function burn(address onBehalfOf, uint256 amount) external virtual {
/// @audit Missing: '@param provider'
/// @audit Missing: '@param onBehalfOf'
/// @audit Missing: '@param assetAmount'
116 /**
117 * @notice When overallCollateralRatio is above 150%, Keeper liquidates borrowers whose collateral ratio is below badCollateralRatio, using PeUSD provided by Liquidation Provider.
118 *
119 * Requirements:
120 * - onBehalfOf Collateral Ratio should be below badCollateralRatio
121 * - assetAmount should be less than 50% of collateral
122 * - provider should authorize Lybra to utilize PeUSD
123 * @dev After liquidation, borrower's debt is reduced by assetAmount * assetPrice, collateral is reduced by the assetAmount corresponding to 110% of the value. Keeper gets keeperRatio / 110 of Liquidation Reward and Liquidator gets the remaining stETH.
124 */
125: function liquidation(address provider, address onBehalfOf, uint256 assetAmount) external virtual {
/// @audit Missing: '@param provider'
/// @audit Missing: '@param peusdAmount'
148 /**
149 * @notice Choose a Redemption Provider, Rigid Redeem `peusdAmount` of EUSD and get 1:1 value of stETH
150 * Emits a `RigidRedemption` event.
151 *
152 * *Requirements:
153 * - `provider` must be a Redemption Provider
154 * - `provider`debt must equal to or above`peusdAmount`
155 * @dev Service Fee for rigidRedemption `redemptionFee` is set to 0.5% by default, can be revised by DAO.
156 */
157: function rigidRedemption(address provider, uint256 peusdAmount) external virtual {
/// @audit Missing: '@param _provider'
/// @audit Missing: '@param _onBehalfOf'
/// @audit Missing: '@param _mintAmount'
/// @audit Missing: '@param _assetPrice'
170 /**
171 * @dev Refresh LBR reward before adding providers debt. Refresh Lybra generated service fee before adding totalSupply. Check providers collateralRatio cannot below `safeCollateralRatio`after minting.
172 */
173: function _mintPeUSD(address _provider, address _onBehalfOf, uint256 _mintAmount, uint256 _assetPrice) internal virtual {
/// @audit Missing: '@param _provider'
/// @audit Missing: '@param _onBehalfOf'
/// @audit Missing: '@param _amount'
187 /**
188 * @notice Burn _provideramount PeUSD to payback minted PeUSD for _onBehalfOf.
189 *
190 * @dev Refresh LBR reward before reducing providers debt. Refresh Lybra generated service fee before reducing totalPeUSDCirculation.
191 */
192: function _repay(address _provider, address _onBehalfOf, uint256 _amount) internal virtual {
/// @audit Missing: '@param user'
/// @audit Missing: '@param price'
222 /**
223 * @dev Get USD value of current collateral asset and minted EUSD through price oracle / Collateral asset USD value must higher than safe Collateral Ratio.
224 */
225: function _checkHealth(address user, uint256 price) internal view {
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit Missing: '@param _config'
/// @audit Missing: '@param _stETH'
/// @audit Missing: '@param _oracle'
16 // stETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84
17 // oracle = 0x4c517D4e2C851CA76d7eC94B805269Df0f2201De
18: constructor(address _config, address _stETH, address _oracle) LybraEUSDVaultBase(_stETH, _oracle, _config) {
/// @audit Missing: '@param _time'
21 /**
22 * @notice Sets the rebase time for Lido based on the actual situation.
23 * This function can only be called by an address with the ADMIN role.
24 */
25: function setLidoRebaseTime(uint256 _time) external {
/// @audit Missing: '@param stETHAmount'
54 /**
55 * @notice When stETH balance increases through LSD or other reasons, the excess income is sold for EUSD, allocated to EUSD holders through rebase mechanism.
56 * Emits a `LSDValueCaptured` event.
57 *
58 * *Requirements:
59 * - stETH balance in the contract cannot be less than totalDepositedAsset after exchange.
60 * @dev Income is used to cover accumulated Service Fee first.
61 */
62: function excessIncomeDistribution(uint256 stETHAmount) external override {
File: contracts/lybra/token/EUSD.sol
/// @audit Missing: '@param _account'
128 /**
129 * @return the amount of tokens owned by the `_account`.
130 *
131 * @dev Balances are dynamic and equal the `_account`'s share in the amount of the
132 * total Ether controlled by the protocol. See `sharesOf`.
133 */
134: function balanceOf(address _account) public view returns (uint256) {
/// @audit Missing: '@param _recipient'
/// @audit Missing: '@param _amount'
138 /**
139 * @notice Moves `_amount` tokens from the caller's account to the `_recipient` account.
140 *
141 * @return a boolean value indicating whether the operation succeeded.
142 * Emits a `Transfer` event.
143 * Emits a `TransferShares` event.
144 *
145 * Requirements:
146 *
147 * - `_recipient` cannot be the zero address.
148 * - the caller must have a balance of at least `_amount`.
149 * - the contract must not be paused.
150 *
151 * @dev The `_amount` argument is the amount of tokens, not shares.
152 */
153: function transfer(address _recipient, uint256 _amount) public returns (bool) {
/// @audit Missing: '@param _owner'
/// @audit Missing: '@param _spender'
159 /**
160 * @return the remaining number of tokens that `_spender` is allowed to spend
161 * on behalf of `_owner` through `transferFrom`. This is zero by default.
162 *
163 * @dev This value changes when `approve` or `transferFrom` is called.
164 */
165: function allowance(address _owner, address _spender) public view returns (uint256) {
/// @audit Missing: '@param owner'
/// @audit Missing: '@param spender'
/// @audit Missing: '@param amount'
169 /**
170 * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
171 *
172 * Does not update the allowance amount in case of infinite allowance.
173 * Revert if not enough allowance is available.
174 *
175 * Might emit an {Approval} event.
176 */
177: function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @audit Missing: '@param _spender'
/// @audit Missing: '@param _amount'
187 /**
188 * @notice Sets `_amount` as the allowance of `_spender` over the caller's tokens.
189 *
190 * @return a boolean value indicating whether the operation succeeded.
191 * Emits an `Approval` event.
192 *
193 * Requirements:
194 *
195 * - `_spender` cannot be the zero address.
196 * - the contract must not be paused.
197 *
198 * @dev The `_amount` argument is the amount of tokens, not shares.
199 */
200: function approve(address _spender, uint256 _amount) public returns (bool) {
/// @audit Missing: '@param from'
/// @audit Missing: '@param to'
/// @audit Missing: '@param amount'
206 /**
207 * @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
208 * allowance mechanism. `_amount` is then deducted from the caller's
209 * allowance.
210 *
211 * @return a boolean value indicating whether the operation succeeded.
212 *
213 * Emits a `Transfer` event.
214 * Emits a `TransferShares` event.
215 * Emits an `Approval` event indicating the updated allowance.
216 *
217 * Requirements:
218 *
219 * - `_sender` and `_recipient` cannot be the zero addresses.
220 * - `_sender` must have a balance of at least `_amount`.
221 * - the caller must have allowance for `_sender`'s tokens of at least `_amount`.
222 * - the contract must not be paused.
223 *
224 * @dev The `_amount` argument is the amount of tokens, not shares.
225 */
226: function transferFrom(address from, address to, uint256 amount) public returns (bool) {
/// @audit Missing: '@param _spender'
/// @audit Missing: '@param _addedValue'
235 /**
236 * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
237 *
238 * This is an alternative to `approve` that can be used as a mitigation for
239 * problems described in:
240 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
241 * Emits an `Approval` event indicating the updated allowance.
242 *
243 * Requirements:
244 *
245 * - `_spender` cannot be the the zero address.
246 * - the contract must not be paused.
247 */
248: function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) {
/// @audit Missing: '@param spender'
/// @audit Missing: '@param subtractedValue'
254 /**
255 * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
256 *
257 * This is an alternative to `approve` that can be used as a mitigation for
258 * problems described in:
259 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
260 * Emits an `Approval` event indicating the updated allowance.
261 *
262 * Requirements:
263 *
264 * - `_spender` cannot be the zero address.
265 * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
266 * - the contract must not be paused.
267 */
268: function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
/// @audit Missing: '@param _account'
289 /**
290 * @return the amount of shares owned by `_account`.
291 */
292: function sharesOf(address _account) public view returns (uint256) {
/// @audit Missing: '@param _EUSDAmount'
296 /**
297 * @return the amount of shares that corresponds to `_EUSDAmount` protocol-supplied EUSD.
298 */
299: function getSharesByMintedEUSD(uint256 _EUSDAmount) public view returns (uint256) {
/// @audit Missing: '@param _sharesAmount'
308 /**
309 * @return the amount of EUSD that corresponds to `_sharesAmount` token shares.
310 */
311: function getMintedEUSDByShares(uint256 _sharesAmount) public view returns (uint256) {
/// @audit Missing: '@param _recipient'
/// @audit Missing: '@param _sharesAmount'
319 /**
320 * @notice Moves `_sharesAmount` token shares from the caller's account to the `_recipient` account.
321 *
322 * @return amount of transferred tokens.
323 * Emits a `TransferShares` event.
324 * Emits a `Transfer` event.
325 *
326 * Requirements:
327 *
328 * - `_recipient` cannot be the zero address.
329 * - the caller must have at least `_sharesAmount` shares.
330 * - the contract must not be paused.
331 *
332 * @dev The `_sharesAmount` argument is the amount of shares, not tokens.
333 */
334: function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
/// @audit Missing: '@param _sender'
/// @audit Missing: '@param _recipient'
/// @audit Missing: '@param _amount'
343 /**
344 * @notice Moves `_amount` tokens from `_sender` to `_recipient`.
345 * Emits a `Transfer` event.
346 * Emits a `TransferShares` event.
347 */
348: function _transfer(address _sender, address _recipient, uint256 _amount) internal {
/// @audit Missing: '@param _owner'
/// @audit Missing: '@param _spender'
/// @audit Missing: '@param _amount'
355 /**
356 * @notice Sets `_amount` as the allowance of `_spender` over the `_owner` s tokens.
357 *
358 * Emits an `Approval` event.
359 *
360 * Requirements:
361 *
362 * - `_owner` cannot be the zero address.
363 * - `_spender` cannot be the zero address.
364 * - the contract must not be paused.
365 */
366: function _approve(address _owner, address _spender, uint256 _amount) internal {
/// @audit Missing: '@param _account'
374 /**
375 * @return the amount of shares owned by `_account`.
376 */
377: function _sharesOf(address _account) internal view returns (uint256) {
/// @audit Missing: '@param _sender'
/// @audit Missing: '@param _recipient'
/// @audit Missing: '@param _sharesAmount'
381 /**
382 * @notice Moves `_sharesAmount` shares from `_sender` to `_recipient`.
383 *
384 * Requirements:
385 *
386 * - `_sender` cannot be the zero address.
387 * - `_recipient` cannot be the zero address.
388 * - `_sender` must hold at least `_sharesAmount` shares.
389 * - the contract must not be paused.
390 */
391: function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal {
/// @audit Missing: '@param _recipient'
/// @audit Missing: '@param _mintAmount'
402 /**
403 * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
404 * @dev This operation also increases the total supply of tokens.
405 *
406 * Requirements:
407 *
408 * - `_recipient` cannot be the zero address.
409 * - the contract must not be paused.
410 */
411: function mint(address _recipient, uint256 _mintAmount) external onlyMintVault MintPaused returns (uint256 newTotalShares) {
/// @audit Missing: '@param _account'
/// @audit Missing: '@param _burnAmount'
430 /**
431 * @notice Destroys `sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
432 * @dev This operation also decrease the total supply of tokens.
433 *
434 * Requirements:
435 *
436 * - `_account` cannot be the zero address.
437 * - `_account` must hold at least `sharesAmount` shares.
438 * - the contract must not be paused.
439 */
440: function burn(address _account, uint256 _burnAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
/// @audit Missing: '@param _account'
/// @audit Missing: '@param _sharesAmount'
449 /**
450 * @notice Destroys `sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
451 * @dev This doesn't decrease the token total supply.
452 *
453 * Requirements:
454 *
455 * - `_account` cannot be the zero address.
456 * - `_account` must hold at least `sharesAmount` shares.
457 * - the contract must not be paused.
458 */
459: function burnShares(address _account, uint256 _sharesAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit Missing: '@param share'
154 /// @notice Calculate the fee owed for the loaned tokens
155 /// @return The amount of shares you need to pay as a fee
156: function getFee(uint256 share) public view returns (uint256) {
/// @audit Missing: '@param _from'
/// @audit Missing: '@param uint16'
/// @audit Missing: '@param bytes32'
/// @audit Missing: '@param _amount'
179 /************************************************************************
180 * internal functions
181 * @dev The following functions are internal functions of Layer Zero OFT, used for internal calls during cross-chain operations.
182 ************************************************************************/
183
184: function _debitFrom(address _from, uint16, bytes32, uint _amount) internal virtual override returns (uint) {
There are 20 instances of this issue:
see instances
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit Missing: '@return'
44 /**
45 * @notice module:user-config
46 * @dev Minimum number of cast voted required for a proposal to be successful.
47 *
48 * NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
49 * quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
50 */
51: function quorum(uint256 timepoint) public view override returns (uint256){
/// @audit Missing: '@return'
56 /**
57 * @dev Amount of votes already cast passes the threshold limit.
58 */
59: function _quorumReached(uint256 proposalId) internal view override returns (bool){
/// @audit Missing: '@return'
63 /**
64 * @dev Is the proposal successful or not.
65 */
66: function _voteSucceeded(uint256 proposalId) internal view override returns (bool){
/// @audit Missing: '@return'
94 /**
95 * @dev Get the voting weight of `account` at a specific `timepoint`, for a vote as described by `params`.
96 */
97
98: function _getVotes(address account, uint256 timepoint, bytes memory) internal view override returns (uint256){
/// @audit Missing: '@return'
141 * duration compared to the voting delay.
142 */
143: function votingPeriod() public pure override returns (uint256){
/// @audit Missing: '@return'
170 * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_.
171 */
172: function proposalThreshold() public pure override returns (uint256) {
/// @audit Missing: '@return'
176 /**
177 * @dev See {IERC165-supportsInterface}.
178 */
179: function supportsInterface(bytes4 interfaceId) public view virtual override(GovernorTimelockControl) returns (bool) {
File: contracts/lybra/miner/esLBRBoost.sol
/// @audit Missing: '@return'
47 // Function to get the user's unlock time
48: function getUnlockTime(address user) external view returns (uint256 unlockTime) {
/// @audit Missing: '@return'
52 /**
53 * @notice calculate the user's mining boost based on their lock status
54 * @dev Based on the user's userUpdatedAt time, finishAt time, and the current time,
55 * there are several scenarios that could occur, including no acceleration, full acceleration, and partial acceleration.
56 */
57: function getUserBoost(address user, uint256 userUpdatedAt, uint256 finishAt) external view returns (uint256) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit Missing: '@return'
63 // Total staked
64: function totalStaked() internal view returns (uint256) {
/// @audit Missing: '@return'
68 // User address => esLBR balance
69: function stakedOf(address staker) internal view returns (uint256) {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit Missing: '@return'
305 * @dev Return USD value of current ETH through Liquity PriceFeed Contract.
306 */
307: function _etherPrice() internal returns (uint256) {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit Missing: '@return'
242 * @dev Return USD value of current ETH through Liquity PriceFeed Contract.
243 */
244: function _etherPrice() internal returns (uint256) {
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit Missing: '@return'
99 * The specific rule is that the discount rate increases by 1% every 30 minutes after the rebase occurs.
100 */
101: function getDutchAuctionDiscountPrice() public view returns (uint256) {
File: contracts/lybra/token/EUSD.sol
/// @audit Missing: '@return'
235 /**
236 * @notice Atomically increases the allowance granted to `_spender` by the caller by `_addedValue`.
237 *
238 * This is an alternative to `approve` that can be used as a mitigation for
239 * problems described in:
240 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
241 * Emits an `Approval` event indicating the updated allowance.
242 *
243 * Requirements:
244 *
245 * - `_spender` cannot be the the zero address.
246 * - the contract must not be paused.
247 */
248: function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) {
/// @audit Missing: '@return'
254 /**
255 * @notice Atomically decreases the allowance granted to `_spender` by the caller by `_subtractedValue`.
256 *
257 * This is an alternative to `approve` that can be used as a mitigation for
258 * problems described in:
259 * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L42
260 * Emits an `Approval` event indicating the updated allowance.
261 *
262 * Requirements:
263 *
264 * - `_spender` cannot be the zero address.
265 * - `_spender` must have allowance for the caller of at least `_subtractedValue`.
266 * - the contract must not be paused.
267 */
268: function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
/// @audit Missing: '@return'
402 /**
403 * @notice Creates `_sharesAmount` shares and assigns them to `_recipient`, increasing the total amount of shares.
404 * @dev This operation also increases the total supply of tokens.
405 *
406 * Requirements:
407 *
408 * - `_recipient` cannot be the zero address.
409 * - the contract must not be paused.
410 */
411: function mint(address _recipient, uint256 _mintAmount) external onlyMintVault MintPaused returns (uint256 newTotalShares) {
/// @audit Missing: '@return'
430 /**
431 * @notice Destroys `sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
432 * @dev This operation also decrease the total supply of tokens.
433 *
434 * Requirements:
435 *
436 * - `_account` cannot be the zero address.
437 * - `_account` must hold at least `sharesAmount` shares.
438 * - the contract must not be paused.
439 */
440: function burn(address _account, uint256 _burnAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
/// @audit Missing: '@return'
449 /**
450 * @notice Destroys `sharesAmount` shares from `_account`'s holdings, decreasing the total amount of shares.
451 * @dev This doesn't decrease the token total supply.
452 *
453 * Requirements:
454 *
455 * - `_account` cannot be the zero address.
456 * - `_account` must hold at least `sharesAmount` shares.
457 * - the contract must not be paused.
458 */
459: function burnShares(address _account, uint256 _sharesAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit Missing: '@return'
179 /************************************************************************
180 * internal functions
181 * @dev The following functions are internal functions of Layer Zero OFT, used for internal calls during cross-chain operations.
182 ************************************************************************/
183
184: function _debitFrom(address _from, uint16, bytes32, uint _amount) internal virtual override returns (uint) {
See this link for an explanation of the correct ordering
There are 2 instances of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit function COUNTING_MODE() public override view virtual returns (){ return "support=bravo&quorum=for,abstain"; } : override before virtual
156: function COUNTING_MODE() public override view virtual returns (string memory){
/// @audit function hasVoted() public override view virtual returns (){ return proposalData[proposalId].receipts[account].hasVoted; } : override before virtual
165: function hasVoted(uint256 proposalId, address account) public override view virtual returns (bool){
There are 18 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
42: mapping(address => uint256) vaultSafeCollateralRatio;
43: mapping(address => uint256) vaultBadCollateralRatio;
46: mapping(address => bool) redemptionProvider;
58: uint256 maxStableRatio = 5_000;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
39: uint256 immutable exitCycle = 90 days;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
22: IPriceFeed immutable etherOracle;
29: mapping(address => uint256) borrowed;
30: uint8 immutable vaultType = 0;
32: mapping(address => uint256) depositedTime;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
18: uint8 immutable vaultType = 1;
19: IPriceFeed immutable etherOracle;
22: mapping(address => uint256) borrowed;
23: mapping(address => uint256) feeStored;
24: mapping(address => uint256) feeUpdatedAt;
File: contracts/lybra/pools/LybraRETHVault.sol
18: IRkPool rkPool = IRkPool(0xDD3f50F8A6CafbE9b31a427582963f465E745AF8);
File: contracts/lybra/pools/LybraWstETHVault.sol
22: Ilido immutable lido;
File: contracts/lybra/token/esLBR.sol
20: uint256 maxSupply = 100_000_000 * 1e18;
File: contracts/lybra/token/LBR.sol
15: uint256 maxSupply = 100_000_000 * 1e18;
According to the Solidity style guide, functions should be laid out in the following order :constructor()
, receive()
, fallback()
, external
, public
, internal
, private
, but the cases below do not follow this pattern
There are 30 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit exchange_underlying() came earlier
80: constructor(address _dao, address _curvePool) {
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit _execute() came earlier
112: function proposals(uint256 proposalId) external view returns (uint256 id, address proposer, uint256 eta, uint256 startBlock, uint256 endBlock, uint256 forVotes, uint256 againstVotes, uint256 abstainVotes, bool canceled, bool executed) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit getUserBoost() came earlier
64: constructor(address _config, address _boost, address _etherOracle, address _lbrOracle) {
/// @audit totalStaked() came earlier
136: function stakedOf(address user) public view returns (uint256) {
/// @audit rewardPerToken() came earlier
174: function refreshReward(address _account) external updateReward(_account) {}
/// @audit isOtherEarningsClaimable() came earlier
192: function getReward() external updateReward(msg.sender) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit getUnlockTime() came earlier
48: constructor(address _config) {
/// @audit stakedOf() came earlier
73: function stake(uint256 amount) external {
/// @audit withdraw() came earlier
113 function unlockPrematurely() external {
114: require(block.timestamp + exitCycle - 3 days > time2fullRedemption[msg.sender], "ENW");
/// @audit earned() came earlier
171: function getClaimAbleUSD(address user) external view returns (uint256 amount) {
File: contracts/lybra/miner/stakerewardV2pool.sol
/// @audit getUserBoost() came earlier
49: constructor(address _stakingToken, address _rewardToken, address _boost) {
/// @audit rewardPerToken() came earlier
83: function stake(uint256 _amount) external updateReward(msg.sender) {
/// @audit earned() came earlier
111: function getReward() external updateReward(msg.sender) {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit fetchPrice() came earlier
46: constructor(address _collateralAsset, address _etherOracle, address _configurator) {
/// @audit checkWithdrawal() came earlier
126: function mint(address onBehalfOf, uint256 amount) external {
/// @audit _etherPrice() came earlier
311: function getBorrowedOf(address user) external view returns (uint256) {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit fetchPrice() came earlier
37: constructor(address _peusd, address _etherOracle, address _collateral, address _configurator) {
/// @audit totalDepositedAsset() came earlier
48: function depositEtherToMint(uint256 mintAmount) external payable virtual;
/// @audit _etherPrice() came earlier
253: function getBorrowedOf(address user) public view returns (uint256) {
/// @audit getPoolTotalPeUSDCirculation() came earlier
261: function getAsset() external view returns (address) {
File: contracts/lybra/pools/LybraRETHVault.sol
/// @audit deposit() came earlier
22 constructor(address _peusd, address _config, address _rETH, address _oracle, address _rkPool)
23: LybraPeUSDVaultBase(_peusd, _oracle, _rETH, _config) {
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit submit() came earlier
18: constructor(address _config, address _stETH, address _oracle) LybraEUSDVaultBase(_stETH, _oracle, _config) {
File: contracts/lybra/pools/LybraWbETHVault.sol
/// @audit deposit() came earlier
17 constructor(address _peusd, address _oracle, address _asset, address _config)
18: LybraPeUSDVaultBase(_peusd, _oracle, _asset, _config) {}
File: contracts/lybra/pools/LybraWstETHVault.sol
/// @audit approve() came earlier
25: constructor(address _lido, address _peusd, address _oracle, address _asset, address _config) LybraPeUSDVaultBase(_peusd, _oracle, _asset,_config) {
File: contracts/lybra/token/esLBR.sol
/// @audit refreshReward() came earlier
22: constructor(address _config) ERC20Permit("esLBR") ERC20("esLBR", "esLBR") {
/// @audit _transfer() came earlier
30: function mint(address user, uint256 amount) external returns (bool) {
File: contracts/lybra/token/EUSD.sol
/// @audit _spendAllowance() came earlier
200: function approve(address _spender, uint256 _amount) public returns (bool) {
/// @audit _transferShares() came earlier
411: function mint(address _recipient, uint256 _mintAmount) external onlyMintVault MintPaused returns (uint256 newTotalShares) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit onFlashLoan() came earlier
55: constructor(address _config, uint8 _sharedDecimals, address _lzEndpoint) ERC20("peg-eUSD", "PeUSD") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
/// @audit convertToPeUSD() came earlier
97 function convertToPeUSDAndCrossChain(
98 uint256 eusdAmount,
99 uint16 dstChainId,
100 bytes32 toAddress,
101: LzCallParams calldata callParams
The style guide says that, within a contract, the ordering should be 1) Type declarations, 2) State variables, 3) Events, 4) Modifiers, and 5) Functions, but the contract(s) below do not follow this ordering
There are 16 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit function exchange_underlying came earlier
38 mapping(address => bool) public mintVault;
39 mapping(address => uint256) public mintVaultMaxSupply;
40 mapping(address => bool) public vaultMintPaused;
41 mapping(address => bool) public vaultBurnPaused;
42 mapping(address => uint256) vaultSafeCollateralRatio;
43 mapping(address => uint256) vaultBadCollateralRatio;
44 mapping(address => uint256) public vaultMintFeeApy;
45 mapping(address => uint256) public vaultKeeperRatio;
46 mapping(address => bool) redemptionProvider;
47: mapping(address => bool) public tokenMiner;
/// @audit event FlashloanFeeUpdated came earlier
76: bytes32 public constant DAO = keccak256("DAO");
/// @audit function constructor came earlier
85 modifier onlyRole(bytes32 role) {
86 GovernanceTimelock.checkOnlyRole(role, msg.sender);
87 _;
88: }
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit function getUserBoost came earlier
28: Iconfigurator public immutable configurator;
/// @audit function constructor came earlier
72 modifier updateReward(address _account) {
73 rewardPerTokenStored = rewardPerToken();
74 updatedAt = lastTimeRewardApplicable();
75
76 if (_account != address(0)) {
77 rewards[_account] = earned(_account);
78 userRewardPerTokenPaid[_account] = rewardPerTokenStored;
79 userUpdatedAt[_account] = block.timestamp;
80 }
81 _;
82: }
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit function getUnlockTime came earlier
25: Iconfigurator public immutable configurator;
/// @audit function getClaimAbleUSD came earlier
178 modifier updateReward(address account) {
179 rewards[account] = earned(account);
180 userRewardPerTokenPaid[account] = rewardPerTokenStored;
181 _;
182: }
File: contracts/lybra/miner/stakerewardV2pool.sol
/// @audit function getUserBoost came earlier
18: IERC20 public immutable stakingToken;
/// @audit function constructor came earlier
56 modifier updateReward(address _account) {
57 rewardPerTokenStored = rewardPerToken();
58 updatedAt = lastTimeRewardApplicable();
59
60 if (_account != address(0)) {
61 rewards[_account] = earned(_account);
62 userRewardPerTokenPaid[_account] = rewardPerTokenStored;
63 userUpdatedAt[_account] = block.timestamp;
64 }
65 _;
66: }
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit function fetchPrice came earlier
18: IEUSD public immutable EUSD;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit function fetchPrice came earlier
14: IPeUSD public immutable PeUSD;
File: contracts/lybra/pools/LybraRETHVault.sol
/// @audit function deposit came earlier
18: IRkPool rkPool = IRkPool(0xDD3f50F8A6CafbE9b31a427582963f465E745AF8);
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit function submit came earlier
14: uint256 public lidoRebaseTime = 12 hours;
File: contracts/lybra/pools/LybraWstETHVault.sol
/// @audit function approve came earlier
22: Ilido immutable lido;
File: contracts/lybra/token/esLBR.sol
/// @audit function refreshReward came earlier
18: Iconfigurator public immutable configurator;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit function onFlashLoan came earlier
30: IEUSD public immutable EUSD;
There are 2 instances of this issue:
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
9: interface LbrStakingPool {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
21: interface FlashBorrower {
See the control structures section of the Solidity Style Guide
There are 12 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
199: if(IVault(pool).vaultType() == 0) {
291: if(peUSDBalance >= 1e21) {
298: if(!premiumTradingEnabled || price <= 1005000) {
339: if(vaultBadCollateralRatio[pool] == 0) return vaultSafeCollateralRatio[pool] - 1e19;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
204: if(useEUSD) {
211: if(useEUSD) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
198: if(reward > eUSDShare) {
201: if(peUSDBalance >= reward - eUSDShare) {
205: if(peUSDBalance > 0) {
231: if(tokenType == 0) {
234: } else if(tokenType == 1) {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
197: if(amount >= totalFee) {
See the mappings section of the Solidity Style Guide
There is one instance of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
33: mapping (uint256 => ProposalExtraData) public proposalData;
[N‑39] Expressions for constant values such as a call to keccak256()
, should use immutable
rather than constant
While it doesn't save any gas because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant
variables and immutable
variables, and they should each be used in their appropriate contexts. constants
should be used for literal values written into the code, and immutable
variables should be used for expressions, or values calculated in, or passed into the constructor.
There are 7 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
76: bytes32 public constant DAO = keccak256("DAO");
77: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
78: bytes32 public constant ADMIN = keccak256("ADMIN");
File: contracts/lybra/governance/GovernanceTimelock.sol
10: bytes32 public constant DAO = keccak256("DAO");
11: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
12: bytes32 public constant ADMIN = keccak256("ADMIN");
13: bytes32 public constant GOV = keccak256("GOV");
There are units for seconds, minutes, hours, days, and weeks, and since they're defined, they should be used
There are 2 instances of this issue:
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit 86400
301: return (poolTotalEUSDCirculation * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - lastReportTime)) / (86400 * 365) / 10000;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit 86400
238: return (borrowed[user] * configurator.vaultMintFeeApy(address(this)) * (block.timestamp - feeUpdatedAt[user])) / (86400 * 365) / 10000;
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 9 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
196: rewards[msg.sender] = 0;
209: rewards[user] = 0;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
120: unstakeRatio[msg.sender] = 0;
121: time2fullRedemption[msg.sender] = 0;
144: unstakeRatio[msg.sender] = 0;
145: time2fullRedemption[msg.sender] = 0;
193: rewards[msg.sender] = 0;
File: contracts/lybra/miner/stakerewardV2pool.sol
114: rewards[msg.sender] = 0;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
198: feeStored[_onBehalfOf] = 0;
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:
File: Various Files
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:
File: Various Files
See this link for the full details
There are 21 instances of this issue:
see instances
File: contracts/lybra/Proxy/LybraProxy.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/Proxy/LybraProxyAdmin.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/configuration/LybraConfigurator.sol
15: pragma solidity ^0.8.17;
File: contracts/lybra/governance/AdminTimelock.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/governance/GovernanceTimelock.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/governance/LybraGovernance.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/esLBRBoost.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/miner/stakerewardV2pool.sol
2: pragma solidity ^0.8;
File: contracts/lybra/pools/LybraRETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraStETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraWbETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/LybraWstETHVault.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/EUSD.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/LBR.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/PeUSD.sol
3: pragma solidity ^0.8.17;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
14: pragma solidity ^0.8.17;
File: contracts/lybra/token/esLBR.sol
3: pragma solidity ^0.8.17;
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 are 17 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
167 function setPremiumTradingEnabled(bool isActive) external checkRole(TIMELOCK) {
168 premiumTradingEnabled = isActive;
169: }
186 function setRedemptionFee(uint256 newFee) external checkRole(TIMELOCK) {
187 require(newFee <= 500, "Max Redemption Fee is 5%");
188 redemptionFee = newFee;
189 emit RedemptionFeeChanged(newFee);
190: }
246 function setMaxStableRatio(uint256 _ratio) external checkRole(TIMELOCK) {
247 require(_ratio <= 10_000, "The maximum value is 10000");
248 maxStableRatio = _ratio;
249: }
253 function setFlashloanFee(uint256 fee) external checkRole(TIMELOCK) {
254 if (fee > 10_000) revert('EL');
255 emit FlashloanFeeUpdated(fee);
256 flashloanFee = fee;
257: }
261 function setProtocolRewardsToken(address _token) external checkRole(TIMELOCK) {
262 stableToken = _token;
263: }
File: contracts/lybra/miner/EUSDMiningIncentives.sol
84 function setToken(address _lbr, address _eslbr) external onlyOwner {
85 LBR = _lbr;
86 esLBR = _eslbr;
87: }
93 function setPools(address[] memory _pools) external onlyOwner {
94 for (uint i = 0; i < _pools.length; i++) {
95 require(configurator.mintVault(_pools[i]), "NOT_VAULT");
96 }
97 pools = _pools;
98: }
100 function setBiddingCost(uint256 _biddingRatio) external onlyOwner {
101 require(_biddingRatio <= 8000, "BCE");
102 biddingFeeRatio = _biddingRatio;
103: }
105 function setExtraRatio(uint256 ratio) external onlyOwner {
106 require(ratio <= 1e20, "BCE");
107 extraRatio = ratio;
108: }
110 function setPeUSDExtraRatio(uint256 ratio) external onlyOwner {
111 require(ratio <= 1e20, "BCE");
112 peUSDExtraRatio = ratio;
113: }
119 function setRewardsDuration(uint256 _duration) external onlyOwner {
120 require(finishAt < block.timestamp, "reward duration not finished");
121 duration = _duration;
122: }
124 function setEthlbrStakeInfo(address _pool, address _lp) external onlyOwner {
125 ethlbrStakePool = _pool;
126 ethlbrLpToken = _lp;
127: }
128 function setEUSDBuyoutAllowed(bool _bool) external onlyOwner {
129 isEUSDBuyoutAllowed = _bool;
130: }
File: contracts/lybra/miner/ProtocolRewardsPool.sol
58 function setGrabCost(uint256 _ratio) external onlyOwner {
59 require(_ratio <= 8000, "BCE");
60 grabFeeRatio = _ratio;
61: }
File: contracts/lybra/miner/stakerewardV2pool.sol
121 function setRewardsDuration(uint256 _duration) external onlyOwner {
122 require(finishAt < block.timestamp, "reward duration not finished");
123 duration = _duration;
124: }
File: contracts/lybra/pools/LybraStETHVault.sol
25 function setLidoRebaseTime(uint256 _time) external {
26 require(configurator.hasRole(keccak256("ADMIN"), msg.sender), "not authorized");
27 lidoRebaseTime = _time;
28: }
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
72 function depositAssetToMint(uint256 assetAmount, uint256 mintAmount) external virtual {
73 require(assetAmount >= 1 ether, "Deposit should not be less than 1 stETH.");
74
75 bool success = collateralAsset.transferFrom(msg.sender, address(this), assetAmount);
76 require(success, "TF");
77
78 totalDepositedAsset += assetAmount;
79 depositedAsset[msg.sender] += assetAmount;
80 depositedTime[msg.sender] = block.timestamp;
81
82 if (mintAmount > 0) {
83 _mintEUSD(msg.sender, msg.sender, mintAmount, getAssetPrice());
84 }
85 emit DepositAsset(msg.sender, address(collateralAsset), assetAmount, block.timestamp);
86: }
[G‑03] 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 7 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
38 mapping(address => bool) public mintVault;
39 mapping(address => uint256) public mintVaultMaxSupply;
40 mapping(address => bool) public vaultMintPaused;
41 mapping(address => bool) public vaultBurnPaused;
42 mapping(address => uint256) vaultSafeCollateralRatio;
43 mapping(address => uint256) vaultBadCollateralRatio;
44 mapping(address => uint256) public vaultMintFeeApy;
45 mapping(address => uint256) public vaultKeeperRatio;
46 mapping(address => bool) redemptionProvider;
47: mapping(address => bool) public tokenMiner;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
46 mapping(address => uint256) public userRewardPerTokenPaid;
47 // User address => rewards to be claimed
48 mapping(address => uint256) public rewards;
49: mapping(address => uint256) public userUpdatedAt;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
33 mapping(address => uint) public userRewardPerTokenPaid;
34 // User address => rewards to be claimed
35 mapping(address => uint) public rewards;
36 mapping(address => uint) public time2fullRedemption;
37 mapping(address => uint) public unstakeRatio;
38: mapping(address => uint) public lastWithdrawTime;
File: contracts/lybra/miner/stakerewardV2pool.sol
33 mapping(address => uint256) public userRewardPerTokenPaid;
34 // User address => rewards to be claimed
35 mapping(address => uint256) public rewards;
36: mapping(address => uint256) public userUpdatedAt;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
28 mapping(address => uint256) public depositedAsset;
29: mapping(address => uint256) borrowed;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
21 mapping(address => uint256) public depositedAsset;
22 mapping(address => uint256) borrowed;
23 mapping(address => uint256) feeStored;
24: mapping(address => uint256) feeUpdatedAt;
File: contracts/lybra/token/EUSD.sol
51 mapping(address => uint256) private shares;
52
53 /**
54 * @dev Allowances are nominated in tokens, not token shares.
55 */
56: mapping(address => mapping(address => uint256)) private allowances;
Avoids a Gsset (20000 gas) in the constructor, and replaces the first access in each transaction (Gcoldsload - 2100 gas) and each access thereafter (Gwarmacces - 100 gas) with a PUSH32
(3 gas).
While string
s are not value types, and therefore cannot be immutable
/constant
if not hard-coded outside of the constructor, the same behavior can be achieved by making the current contract abstract
with virtual
functions for the string
accessors, and having a child contract override the functions with the hard-coded implementation-specific values.
There are 5 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit GovernanceTimelock (constructor)
81: GovernanceTimelock = IGovernanceTimelock(_dao);
/// @audit curvePool (constructor)
82: curvePool = ICurvePool(_curvePool);
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit esLBR (constructor)
40: esLBR = IesLBR(_esLBR);
/// @audit GovernanceTimelock (constructor)
41: GovernanceTimelock = IGovernanceTimelock(address(timelock_));
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit etherPriceFeed (constructor)
68: etherPriceFeed = AggregatorV3Interface(_etherOracle);
When fetching data from a storage location, assigning the data to a memory
variable causes all fields of the struct/array to be read from storage, which incurs a Gcoldsload (2100 gas) for each field of the struct/array. If the fields are read from the new memory variable, they incur an additional MLOAD
rather than a cheap stack read. Instead of declearing the variable with the memory
keyword, declaring the variable with the storage
keyword and caching any fields that need to be re-read in stack variables, will be much cheaper, only incuring the Gcoldsload for the fields actually read. The only time it makes sense to read the whole struct/array into a memory
variable, is if the full struct/array is being returned by the function, is being passed to a function that requires memory
, or if the array/struct is being read from another memory
array/struct
There is one instance of this issue:
File: contracts/lybra/miner/esLBRBoost.sol
40: LockStatus memory userStatus = userLockStatus[msg.sender];
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 22 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit lybraProtocolRewardsPool on line 292
293: lybraProtocolRewardsPool.notifyRewardAmount(peUSDBalance, 2);
/// @audit lybraProtocolRewardsPool on line 293
299: EUSD.transfer(address(lybraProtocolRewardsPool), balance);
/// @audit lybraProtocolRewardsPool on line 299
300: lybraProtocolRewardsPool.notifyRewardAmount(balance, 0);
/// @audit lybraProtocolRewardsPool on line 304
305: lybraProtocolRewardsPool.notifyRewardAmount(amount, 1);
/// @audit EUSD on line 295
299: EUSD.transfer(address(lybraProtocolRewardsPool), balance);
/// @audit peUSD on line 290
292: peUSD.transfer(address(lybraProtocolRewardsPool), peUSDBalance);
/// @audit curvePool on line 297
302: EUSD.approve(address(curvePool), balance);
/// @audit curvePool on line 302
303: uint256 amount = curvePool.exchange_underlying(0, 2, balance, balance * price * 998 / 1e21);
File: contracts/lybra/miner/esLBRBoost.sol
/// @audit esLBRLockSettings on line 26
27: esLBRLockSettings.push(esLBRLockSetting(90 days, 30 * 1e18));
/// @audit esLBRLockSettings on line 27
28: esLBRLockSettings.push(esLBRLockSetting(180 days, 50 * 1e18));
/// @audit esLBRLockSettings on line 28
29: esLBRLockSettings.push(esLBRLockSetting(365 days, 100 * 1e18));
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit duration on line 234
239: finishAt = block.timestamp + duration;
/// @audit rewardRatio on line 231
233: uint256 remainingRewards = (finishAt - block.timestamp) * rewardRatio;
/// @audit rewardRatio on line 234
237: require(rewardRatio > 0, "reward ratio = 0");
/// @audit ethlbrLpToken on line 150
153: uint256 etherInLp = (IEUSD(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2).balanceOf(ethlbrLpToken) * uint(etherPrice)) / 1e8;
/// @audit ethlbrLpToken on line 153
154: uint256 lbrInLp = (IEUSD(LBR).balanceOf(ethlbrLpToken) * uint(lbrPrice)) / 1e8;
File: contracts/lybra/miner/stakerewardV2pool.sol
/// @audit duration on line 137
142: finishAt = block.timestamp + duration;
/// @audit rewardRatio on line 134
136: uint256 remainingRewards = (finishAt - block.timestamp) * rewardRatio;
/// @audit rewardRatio on line 137
140: require(rewardRatio > 0, "reward ratio = 0");
/// @audit totalSupply on line 75
79: return rewardPerTokenStored + (rewardRatio * (lastTimeRewardApplicable() - updatedAt) * 1e18) / totalSupply;
File: contracts/lybra/token/EUSD.sol
/// @audit _totalShares on line 312
315: return _sharesAmount.mul(_totalSupply).div(_totalShares);
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit userConvertInfo[<etc>].mintedPeUSD on line 115
117: uint256 share = (userConvertInfo[msg.sender].depositedEUSDShares * peusdAmount) / userConvertInfo[msg.sender].mintedPeUSD;
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 13 instances of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit proposalData[proposalId] on line 60
60: return proposalData[proposalId].supportVotes[1] + proposalData[proposalId].supportVotes[2] >= quorum(proposalSnapshot(proposalId));
/// @audit proposalData[proposalId] on line 67
67: return proposalData[proposalId].supportVotes[1] > proposalData[proposalId].supportVotes[0];
/// @audit proposalData[proposalId] on line 120
121: againstVotes = proposalData[proposalId].supportVotes[1];
/// @audit proposalData[proposalId] on line 121
122: abstainVotes = proposalData[proposalId].supportVotes[2];
/// @audit proposalData[proposalId] on line 130
131: support = proposalData[proposalId].receipts[account].support;
/// @audit proposalData[proposalId] on line 131
132: votes = proposalData[proposalId].receipts[account].votes;
File: contracts/lybra/miner/esLBRBoost.sol
/// @audit userLockStatus[user] on line 58
59: uint256 maxBoost = userLockStatus[user].miningBoost;
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit userConvertInfo[user] on line 85
86: userConvertInfo[user].mintedPeUSD += eusdAmount;
/// @audit userConvertInfo[<etc>] on line 115
/// @audit userConvertInfo[<etc>] on line 117
117: uint256 share = (userConvertInfo[msg.sender].depositedEUSDShares * peusdAmount) / userConvertInfo[msg.sender].mintedPeUSD;
/// @audit userConvertInfo[<etc>] on line 117
118: userConvertInfo[msg.sender].mintedPeUSD -= peusdAmount;
/// @audit userConvertInfo[<etc>] on line 118
119: userConvertInfo[msg.sender].depositedEUSDShares -= share;
/// @audit userConvertInfo[user] on line 168
168: return EUSD.getMintedEUSDByShares(userConvertInfo[user].depositedEUSDShares) - userConvertInfo[user].mintedPeUSD;
The instances below point to the second+ call of the function within a single function
There is one instance of this issue:
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit configurator.distributeRewards() on line 73
87: try configurator.distributeRewards() {} catch {}
Using the addition operator instead of plus-equals saves 113 gas
There are 16 instances of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
122: grabableAmount += burnAmount;
133: grabableAmount -= amount;
File: contracts/lybra/miner/stakerewardV2pool.sol
88: totalSupply += _amount;
96: totalSupply -= _amount;
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
78: totalDepositedAsset += assetAmount;
102: totalDepositedAsset -= amount;
165: totalDepositedAsset -= reducedAsset;
201: totalDepositedAsset -= assetAmount;
241: totalDepositedAsset -= collateralAmount;
266: poolTotalEUSDCirculation += _mintAmount;
284: poolTotalEUSDCirculation -= amount;
296: feeStored += _newFee();
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
182: poolTotalPeUSDCirculation += _mintAmount;
207: poolTotalPeUSDCirculation -= amount;
File: contracts/lybra/token/EUSD.sol
425: _totalSupply += _mintAmount;
444: _totalSupply -= _burnAmount;
Not inlining costs 20 to 40 gas because of two extra JUMP
instructions and additional stack operations needed for function calls.
There is one instance of this issue:
File: contracts/lybra/miner/ProtocolRewardsPool.sol
69: function stakedOf(address staker) internal view returns (uint256) {
[G‑11] 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 16 instances of this issue:
see instances
File: contracts/lybra/token/EUSD.sol
/// @audit require() on line 180
182: _approve(owner, spender, currentAllowance - amount);
/// @audit require() on line 271
273: _approve(owner, spender, currentAllowance - subtractedValue);
File: contracts/lybra/token/LBR.sol
/// @audit require() on line 21
22: ld2sdRatio = 10 ** (decimals - _sharedDecimals);
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit require() on line 59
60: ld2sdRatio = 10 ** (decimals - _sharedDecimals);
File: contracts/lybra/token/PeUSD.sol
/// @audit require() on line 13
14: ld2sdRatio = 10 ** (decimals - _sharedDecimals);
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit if-condition on line 92
93: total += unstakeRatio[msg.sender] * (time2fullRedemption[msg.sender] - block.timestamp);
/// @audit if-condition on line 156
157: amount = block.timestamp > time2fullRedemption[user] ? unstakeRatio[user] * (time2fullRedemption[user] - lastWithdrawTime[user]) : unstakeRatio[user] * (block.timestamp - lastWithdrawTime[user]);
/// @audit if-condition on line 162
163: amount = unstakeRatio[user] * (time2fullRedemption[user] - block.timestamp);
/// @audit if-condition on line 198
201: if(peUSDBalance >= reward - eUSDShare) {
/// @audit if-condition on line 198
202: peUSD.transfer(msg.sender, reward - eUSDShare);
/// @audit if-condition on line 198
203: emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(peUSD), reward - eUSDShare, block.timestamp);
/// @audit if-condition on line 198
209: uint256 tokenAmount = (reward - eUSDShare - peUSDBalance) * token.decimals() / 1e18;
/// @audit if-condition on line 198
211: emit ClaimReward(msg.sender, EUSD.getMintedEUSDByShares(eUSDShare), address(token), reward - eUSDShare, block.timestamp);
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit if-condition on line 197
200: PeUSD.burn(_provider, amount - totalFee);
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit if-condition on line 69
75: uint256 sharesAmount = EUSD.getSharesByMintedEUSD(payAmount - income);
/// @audit if-condition on line 69
78: sharesAmount = (payAmount - income);
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)
Caching the length changes each of these to a DUP<N>
(3 gas), and gets rid of the extra DUP<N>
needed to store the stack offset
There are 3 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
236: for (uint256 i = 0; i < _contracts.length; i++) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
94: for (uint i = 0; i < _pools.length; i++) {
138: for (uint i = 0; i < pools.length; i++) {
[G‑13] ++i
/i++
should be unchecked{++i}
/unchecked{i++}
when it is not possible for them to overflow, as is the case when used in for
- and while
-loops
The unchecked
keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop
There are 3 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
236: for (uint256 i = 0; i < _contracts.length; i++) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
94: for (uint i = 0; i < _pools.length; i++) {
138: for (uint i = 0; i < pools.length; i++) {
Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas
There are 39 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
200: require(newRatio >= 160 * 1e18, "eUSD vault safe collateralRatio should more than 160%");
202: require(newRatio >= vaultBadCollateralRatio[pool] + 1e19, "PeUSD vault safe collateralRatio should more than bad collateralRatio");
File: contracts/lybra/governance/LybraGovernance.sol
78: require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
79: require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
82: require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
File: contracts/lybra/miner/esLBRBoost.sol
42: require(userStatus.duration <= _setting.duration, "Your lock-in period has not ended, and the term can only be extended, not reduced.");
File: contracts/lybra/miner/EUSDMiningIncentives.sol
193: require(!isOtherEarningsClaimable(msg.sender), "Insufficient DLP, unable to claim rewards");
203: require(isOtherEarningsClaimable(user), "The rewards of the user cannot be bought out");
205: require(isEUSDBuyoutAllowed, "The purchase using EUSD is not permitted.");
File: contracts/lybra/miner/ProtocolRewardsPool.sol
88: require(block.timestamp >= esLBRBoost.getUnlockTime(msg.sender), "Your lock-in period has not ended. You can't convert your esLBR now.");
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
73: require(assetAmount >= 1 ether, "Deposit should not be less than 1 stETH.");
101: require(depositedAsset[msg.sender] >= amount, "Withdraw amount exceeds deposited amount.");
157: require(onBehalfOfCollateralRatio < badCollateralRatio, "Borrowers collateral ratio should below badCollateralRatio");
159: require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
160: require(EUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");
189: require((totalDepositedAsset * assetPrice * 100) / poolTotalEUSDCirculation < badCollateralRatio, "overallCollateralRatio should below 150%");
191: require(onBehalfOfCollateralRatio < 125 * 1e18, "borrowers collateralRatio should below 125%");
192: require(assetAmount <= depositedAsset[onBehalfOf], "total of collateral can be liquidated at most");
197: require(EUSD.allowance(provider, address(this)) >= eusdAmount, "provider should authorize to provide liquidation EUSD");
233: require(configurator.isRedemptionProvider(provider), "provider is not a RedemptionProvider");
234: require(borrowed[provider] >= eusdAmount, "eusdAmount cannot surpass providers debt");
237: require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%");
292: if (((depositedAsset[_user] * _assetPrice * 100) / borrowed[_user]) < configurator.getSafeCollateralRatio(address(this))) revert("collateralRatio is Below safeCollateralRatio");
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
59: require(assetAmount >= 1 ether, "Deposit should not be less than 1 collateral asset.");
128: require(onBehalfOfCollateralRatio < configurator.getBadCollateralRatio(address(this)), "Borrowers collateral ratio should below badCollateralRatio");
130: require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
131: require(PeUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");
158: require(configurator.isRedemptionProvider(provider), "provider is not a RedemptionProvider");
159: require(borrowed[provider] >= peusdAmount, "peusdAmount cannot surpass providers debt");
162: require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%");
213: require(depositedAsset[_provider] >= _amount, "Withdraw amount exceeds deposited amount.");
227: revert("collateralRatio is Below safeCollateralRatio");
File: contracts/lybra/pools/LybraStETHVault.sol
64: require(excessAmount > 0 && stETHAmount > 0, "Only LSD excess income can be exchanged");
File: contracts/lybra/token/esLBR.sol
32: require(totalSupply() + amount <= maxSupply, "exceeding the maximum supply quantity.");
File: contracts/lybra/token/EUSD.sol
271: require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
File: contracts/lybra/token/LBR.sol
21: require(_sharedDecimals <= decimals, "OFT: sharedDecimals must be <= decimals");
27: require(totalSupply() + amount <= maxSupply, "exceeding the maximum supply quantity.");
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
59: require(_sharedDecimals <= decimals, "OFT: sharedDecimals must be <= decimals");
File: contracts/lybra/token/PeUSD.sol
13: require(_sharedDecimals <= decimals, "OFT: sharedDecimals must be <= decimals");
It should be saved to an immutable variable, and the variable used instead. If the hash is being used as a part of a function selector, the cast to bytes4
should also only be done once
There are 3 instances of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
107: require(GovernanceTimelock.checkOnlyRole(keccak256("TIMELOCK"), msg.sender), "not authorized");
File: contracts/lybra/pools/LybraRETHVault.sol
42: require(configurator.hasRole(keccak256("TIMELOCK"), msg.sender));
File: contracts/lybra/pools/LybraStETHVault.sol
26: require(configurator.hasRole(keccak256("ADMIN"), msg.sender), "not authorized");
public
/external
function names and public
member variable names can be optimized to save gas. See this link for an example of how it works. Below are the interfaces/abstract contracts that can be optimized so that the most frequently-called functions use the least amount of gas possible during method lookup. Method IDs that have two leading zero bytes can save 128 gas each during deployment, and renaming functions to have lower method IDs will save 22 gas per call, per sorted position shifted
There are 15 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
/// @audit get_dy_underlying(), exchange_underlying()
32: interface ICurvePool{
/// @audit initToken(), setMintVault(), setMintVaultMaxSupply(), setBadCollateralRatio(), setProtocolRewardsPool(), setEUSDMiningIncentives(), setvaultBurnPaused(), setPremiumTradingEnabled(), setvaultMintPaused(), setRedemptionFee(), setSafeCollateralRatio(), setBorrowApy(), setKeeperRatio(), setTokenMiner(), setMaxStableRatio(), setFlashloanFee(), setProtocolRewardsToken(), becomeRedemptionProvider(), refreshMintReward(), distributeRewards(), getEUSDAddress(), getProtocolRewardsPool(), getSafeCollateralRatio(), getBadCollateralRatio(), isRedemptionProvider(), getEUSDMaxLocked()
37: contract Configurator {
File: contracts/lybra/governance/GovernanceTimelock.sol
/// @audit checkRole(), checkOnlyRole()
8: contract GovernanceTimelock is TimelockController {
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit proposals(), getReceipt()
9: contract LybraGovernance is GovernorTimelockControl {
File: contracts/lybra/miner/esLBRBoost.sol
/// @audit addLockSetting(), setLockStatus(), getUnlockTime(), getUserBoost()
7: contract esLBRBoost is Ownable {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
/// @audit setToken(), setLBROracle(), setPools(), setBiddingCost(), setExtraRatio(), setPeUSDExtraRatio(), setBoost(), setRewardsDuration(), setEthlbrStakeInfo(), setEUSDBuyoutAllowed(), stakedOf(), stakedLBRLpValue(), lastTimeRewardApplicable(), rewardPerToken(), refreshReward(), getBoost(), earned(), isOtherEarningsClaimable(), getReward(), purchaseOtherEarnings(), notifyRewardAmount()
27: contract EUSDMiningIncentives is Ownable {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit setTokenAddress(), setGrabCost(), stake(), unstake(), withdraw(), unlockPrematurely(), grabEsLBR(), reStake(), getPreUnlockableAmount(), getClaimAbleLBR(), getReservedLBRForVesting(), earned(), getClaimAbleUSD(), refreshReward(), getReward(), notifyRewardAmount()
24: contract ProtocolRewardsPool is Ownable {
File: contracts/lybra/miner/stakerewardV2pool.sol
/// @audit lastTimeRewardApplicable(), rewardPerToken(), stake(), getBoost(), earned(), getReward(), setRewardsDuration(), setBoost(), notifyRewardAmount()
16: contract StakingRewardsV2 is Ownable {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
/// @audit depositEtherToMint(), depositAssetToMint(), withdraw(), liquidation(), superLiquidation(), excessIncomeDistribution(), rigidRedemption(), getBorrowedOf(), getPoolTotalEUSDCirculation(), getAsset(), getVaultType(), getAssetPrice()
17: abstract contract LybraEUSDVaultBase {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
/// @audit totalDepositedAsset(), depositEtherToMint(), depositAssetToMint(), withdraw(), liquidation(), rigidRedemption(), getBorrowedOf(), getPoolTotalPeUSDCirculation(), getAsset(), getVaultType(), getAssetPrice()
13: abstract contract LybraPeUSDVaultBase {
File: contracts/lybra/pools/LybraStETHVault.sol
/// @audit setLidoRebaseTime(), getDutchAuctionDiscountPrice()
12: contract LybraStETHDepositVault is LybraEUSDVaultBase {
File: contracts/lybra/pools/LybraWbETHVault.sol
/// @audit exchangeRatio(), deposit()
9: interface IWBETH {
File: contracts/lybra/pools/LybraWstETHVault.sol
/// @audit stEthPerToken(), wrap()
9: interface IWstETH {
File: contracts/lybra/token/EUSD.sol
/// @audit getTotalShares(), sharesOf(), getSharesByMintedEUSD(), getMintedEUSDByShares(), transferShares(), burnShares()
37: contract EUSD is IERC20, Context {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit convertToPeUSD(), convertToPeUSDAndCrossChain(), convertToEUSD(), executeFlashloan(), getFee(), getAccruedEUSDInterest()
29: contract PeUSDMainnet is BaseOFTV2, ERC20 {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/58f635312aa21f947cae5f8578638a85aa2519f5/contracts/security/ReentrancyGuard.sol#L23-L27
Use uint256(1)
and uint256(2)
for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from false
to true
, after having been true
in the past
There are 7 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
38: mapping(address => bool) public mintVault;
40: mapping(address => bool) public vaultMintPaused;
41: mapping(address => bool) public vaultBurnPaused;
46: mapping(address => bool) redemptionProvider;
47: mapping(address => bool) public tokenMiner;
61: bool public premiumTradingEnabled;
File: contracts/lybra/miner/EUSDMiningIncentives.sol
57: bool public isEUSDBuyoutAllowed = true;
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/lybra/miner/stakerewardV2pool.sol
2: pragma solidity ^0.8;
This change saves 6 gas per instance. The optimization works until solidity version 0.8.13 where there is a regression in gas costs.
There are 3 instances of this issue:
File: contracts/lybra/miner/stakerewardV2pool.sol
84: require(_amount > 0, "amount = 0");
94: require(_amount > 0, "amount = 0");
140: require(rewardRatio > 0, "reward ratio = 0");
The compiler uses opcodes GT
and ISZERO
for solidity code that uses >
, but only requires LT
for >=
, which saves 3 gas
There are 2 instances of this issue:
File: contracts/lybra/miner/esLBRBoost.sol
66: uint256 time = block.timestamp > finishAt ? finishAt : block.timestamp;
File: contracts/lybra/pools/LybraStETHVault.sol
65: uint256 realAmount = stETHAmount > excessAmount ? excessAmount : stETHAmount;
Saves 5 gas per loop
There are 3 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
236: for (uint256 i = 0; i < _contracts.length; i++) {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
94: for (uint i = 0; i < _pools.length; i++) {
138: for (uint i = 0; i < pools.length; i++) {
See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper by 3 gas
There are 3 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
127: require(newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] + 1e19, "LNA");
File: contracts/lybra/pools/LybraStETHVault.sol
64: require(excessAmount > 0 && stETHAmount > 0, "Only LSD excess income can be exchanged");
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
115: require(peusdAmount <= userConvertInfo[msg.sender].mintedPeUSD &&peusdAmount > 0, "PCE");
When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html
Each operation involving a uint8
costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8
) as compared to ones involving uint256
, due to the compiler having to clear the higher bits of the memory word before operating on the uint8
, as well as the associated stack operations of doing so. Use a larger size then downcast where needed
There is one instance of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit uint8 support
131: support = proposalData[proposalId].receipts[account].support;
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 8 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
76: bytes32 public constant DAO = keccak256("DAO");
77: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
78: bytes32 public constant ADMIN = keccak256("ADMIN");
File: contracts/lybra/governance/GovernanceTimelock.sol
10: bytes32 public constant DAO = keccak256("DAO");
11: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
12: bytes32 public constant ADMIN = keccak256("ADMIN");
13: bytes32 public constant GOV = keccak256("GOV");
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
21: uint256 public immutable badCollateralRatio = 150 * 1e18;
if (<x> == true)
=> if (<x>)
, if (<x> == false)
=> if (!<x>)
There is one instance of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
82: require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
Version 0.8.0 introduces internal overflow checks, so using SafeMath
is redundant and adds overhead
There is one instance of this issue:
File: contracts/lybra/token/EUSD.sol
5: import "@openzeppelin/contracts/utils/math/SafeMath.sol";
[G‑27] require()
or revert()
statements that check input arguments should be at the top of the function
Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting a Gcoldsload (2100 gas*) in a function that may ultimately revert in the unhappy case.
There are 2 instances of this issue:
File: contracts/lybra/governance/LybraGovernance.sol
/// @audit expensive op on line 78
79: require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
File: contracts/lybra/miner/ProtocolRewardsPool.sol
/// @audit expensive op on line 229
230: require(amount > 0, "amount = 0");
The code should be refactored such that they no longer exist, or the block should do something useful, such as emitting an event or reverting. If the contract is meant to be extended, the contract should be abstract
and the function signatures be added without any default implementation. If the block is an empty if
-statement block to avoid doing subsequent checks in the else-if/else conditions, the else-if/else conditions should be nested under the negation of the if-statement, because they involve different classes of checks, which may lead to the introduction of errors when the code is later modified (if (x) {...} else if (y) {...} else {...}
=> if (!x) { if (y) {...} else {...} }
). Empty receive()
/fallback() payable
functions that are not used, can be removed to save deployment gas.
There are 20 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
216: try configurator.distributeRewards() {} catch {}
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
261: try configurator.refreshMintReward(_provider) {} catch {}
280: try configurator.refreshMintReward(_onBehalfOf) {} catch {}
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
177: try configurator.refreshMintReward(_provider) {} catch {}
193: try configurator.refreshMintReward(_onBehalfOf) {} catch {}
205: try configurator.distributeRewards() {} catch {}
File: contracts/lybra/pools/LybraStETHVault.sol
73: try configurator.distributeRewards() {} catch {}
87: try configurator.distributeRewards() {} catch {}
File: contracts/lybra/token/esLBR.sol
33: try IProtocolRewardsPool(configurator.getProtocolRewardsPool()).refreshReward(user) {} catch {}
40: try IProtocolRewardsPool(configurator.getProtocolRewardsPool()).refreshReward(user) {} catch {}
block.timestamp
and block.number
are added to event information by default so adding them manually wastes gas
There are 19 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
66: event ProtocolRewardsPoolChanged(address indexed pool, uint256 timestamp);
67: event EUSDMiningIncentivesChanged(address indexed pool, uint256 timestamp);
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
34: event DepositEther(address indexed onBehalfOf, address asset, uint256 etherAmount, uint256 assetAmount, uint256 timestamp);
36: event DepositAsset(address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
38: event WithdrawAsset(address sponsor, address asset, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
39: event Mint(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
40: event Burn(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
41: event LiquidationRecord(address provider, address keeper, address indexed onBehalfOf, uint256 eusdamount, uint256 liquidateEtherAmount, uint256 keeperReward, bool superLiquidation, uint256 timestamp);
42: event LSDValueCaptured(uint256 stETHAdded, uint256 payoutEUSD, uint256 discountRate, uint256 timestamp);
43: event RigidRedemption(address indexed caller, address indexed provider, uint256 eusdAmount, uint256 collateralAmount, uint256 timestamp);
44: event FeeDistribution(address indexed feeAddress, uint256 feeAmount, uint256 timestamp);
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
26: event DepositEther(address indexed onBehalfOf, address asset, uint256 etherAmount, uint256 assetAmount, uint256 timestamp);
28: event DepositAsset(address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
29: event WithdrawAsset(address sponsor, address indexed onBehalfOf, address asset, uint256 amount, uint256 timestamp);
30: event Mint(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
31: event Burn(address sponsor, address indexed onBehalfOf, uint256 amount, uint256 timestamp);
32: event LiquidationRecord(address provider, address keeper, address indexed onBehalfOf, uint256 eusdamount, uint256 LiquidateAssetAmount, uint256 keeperReward, bool superLiquidation, uint256 timestamp);
34: event RigidRedemption(address indexed caller, address indexed provider, uint256 peusdAmount, uint256 assetAmount, uint256 timestamp);
35: event FeeDistribution(address indexed feeAddress, uint256 feeAmount, uint256 timestamp);
Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hit by avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas
There are 111 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
127: require(newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] + 1e19, "LNA");
187: require(newFee <= 500, "Max Redemption Fee is 5%");
200: require(newRatio >= 160 * 1e18, "eUSD vault safe collateralRatio should more than 160%");
202: require(newRatio >= vaultBadCollateralRatio[pool] + 1e19, "PeUSD vault safe collateralRatio should more than bad collateralRatio");
214: require(newApy <= 200, "Borrow APY cannot exceed 2%");
225: require(newRatio <= 5, "Max Keeper reward is 5%");
247: require(_ratio <= 10_000, "The maximum value is 10000");
File: contracts/lybra/governance/LybraGovernance.sol
78: require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
79: require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
82: require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
107: require(GovernanceTimelock.checkOnlyRole(keccak256("TIMELOCK"), msg.sender), "not authorized");
152: require(clock() == block.number, "Votes: broken clock mode");
File: contracts/lybra/miner/esLBRBoost.sol
42: require(userStatus.duration <= _setting.duration, "Your lock-in period has not ended, and the term can only be extended, not reduced.");
File: contracts/lybra/miner/EUSDMiningIncentives.sol
95: require(configurator.mintVault(_pools[i]), "NOT_VAULT");
101: require(_biddingRatio <= 8000, "BCE");
106: require(ratio <= 1e20, "BCE");
111: require(ratio <= 1e20, "BCE");
120: require(finishAt < block.timestamp, "reward duration not finished");
193: require(!isOtherEarningsClaimable(msg.sender), "Insufficient DLP, unable to claim rewards");
203: require(isOtherEarningsClaimable(user), "The rewards of the user cannot be bought out");
205: require(isEUSDBuyoutAllowed, "The purchase using EUSD is not permitted.");
215: require(success, "TF");
229: require(amount > 0, "amount = 0");
237: require(rewardRatio > 0, "reward ratio = 0");
File: contracts/lybra/miner/ProtocolRewardsPool.sol
59: require(_ratio <= 8000, "BCE");
88: require(block.timestamp >= esLBRBoost.getUnlockTime(msg.sender), "Your lock-in period has not ended. You can't convert your esLBR now.");
114: require(block.timestamp + exitCycle - 3 days > time2fullRedemption[msg.sender], "ENW");
132: require(amount > 0, "QMG");
230: require(amount > 0, "amount = 0");
File: contracts/lybra/miner/stakerewardV2pool.sol
84: require(_amount > 0, "amount = 0");
86: require(success, "TF");
94: require(_amount > 0, "amount = 0");
122: require(finishAt < block.timestamp, "reward duration not finished");
140: require(rewardRatio > 0, "reward ratio = 0");
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
73: require(assetAmount >= 1 ether, "Deposit should not be less than 1 stETH.");
76: require(success, "TF");
99: require(onBehalfOf != address(0), "TZA");
100: require(amount > 0, "ZERO_WITHDRAW");
101: require(depositedAsset[msg.sender] >= amount, "Withdraw amount exceeds deposited amount.");
127: require(onBehalfOf != address(0), "MINT_TO_THE_ZERO_ADDRESS");
128: require(amount > 0, "ZERO_MINT");
141: require(onBehalfOf != address(0), "BURN_TO_THE_ZERO_ADDRESS");
157: require(onBehalfOfCollateralRatio < badCollateralRatio, "Borrowers collateral ratio should below badCollateralRatio");
159: require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
160: require(EUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");
189: require((totalDepositedAsset * assetPrice * 100) / poolTotalEUSDCirculation < badCollateralRatio, "overallCollateralRatio should below 150%");
191: require(onBehalfOfCollateralRatio < 125 * 1e18, "borrowers collateralRatio should below 125%");
192: require(assetAmount <= depositedAsset[onBehalfOf], "total of collateral can be liquidated at most");
197: require(EUSD.allowance(provider, address(this)) >= eusdAmount, "provider should authorize to provide liquidation EUSD");
233: require(configurator.isRedemptionProvider(provider), "provider is not a RedemptionProvider");
234: require(borrowed[provider] >= eusdAmount, "eusdAmount cannot surpass providers debt");
237: require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%");
260: require(poolTotalEUSDCirculation + _mintAmount <= configurator.mintVaultMaxSupply(address(this)), "ESL");
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
59: require(assetAmount >= 1 ether, "Deposit should not be less than 1 collateral asset.");
62: require(collateralAsset.balanceOf(address(this)) >= preBalance + assetAmount, "");
83: require(onBehalfOf != address(0), "TZA");
84: require(amount > 0, "ZA");
97: require(onBehalfOf != address(0), "TZA");
98: require(amount > 0, "ZA");
111: require(onBehalfOf != address(0), "TZA");
112: require(amount > 0, "ZA");
128: require(onBehalfOfCollateralRatio < configurator.getBadCollateralRatio(address(this)), "Borrowers collateral ratio should below badCollateralRatio");
130: require(assetAmount * 2 <= depositedAsset[onBehalfOf], "a max of 50% collateral can be liquidated");
131: require(PeUSD.allowance(provider, address(this)) > 0, "provider should authorize to provide liquidation EUSD");
158: require(configurator.isRedemptionProvider(provider), "provider is not a RedemptionProvider");
159: require(borrowed[provider] >= peusdAmount, "peusdAmount cannot surpass providers debt");
162: require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%");
174: require(poolTotalPeUSDCirculation + _mintAmount <= configurator.mintVaultMaxSupply(address(this)), "ESL");
213: require(depositedAsset[_provider] >= _amount, "Withdraw amount exceeds deposited amount.");
File: contracts/lybra/pools/LybraRETHVault.sol
28: require(msg.value >= 1 ether, "DNL");
File: contracts/lybra/pools/LybraStETHVault.sol
26: require(configurator.hasRole(keccak256("ADMIN"), msg.sender), "not authorized");
38: require(msg.value >= 1 ether, "DNL");
41: require(sharesAmount > 0, "ZERO_DEPOSIT");
64: require(excessAmount > 0 && stETHAmount > 0, "Only LSD excess income can be exchanged");
71: require(success, "TF");
86: require(success, "TF");
File: contracts/lybra/pools/LybraWbETHVault.sol
21: require(msg.value >= 1 ether, "DNL");
File: contracts/lybra/pools/LybraWstETHVault.sol
30: require(msg.value >= 1 ether, "DNL");
33: require(sharesAmount > 0, "ZERO_DEPOSIT");
File: contracts/lybra/token/esLBR.sol
31: require(configurator.tokenMiner(msg.sender), "not authorized");
32: require(totalSupply() + amount <= maxSupply, "exceeding the maximum supply quantity.");
39: require(configurator.tokenMiner(msg.sender), "not authorized");
File: contracts/lybra/token/EUSD.sol
80: require(configurator.mintVault(msg.sender), "RCP");
84: require(!configurator.vaultMintPaused(msg.sender), "MPP");
88: require(!configurator.vaultBurnPaused(msg.sender), "BPP");
180: require(currentAllowance >= amount, "ERC20: insufficient allowance");
271: require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
367: require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
368: require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");
392: require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
393: require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");
396: require(_sharesAmount <= currentSenderShares, "TRANSFER_AMOUNT_EXCEEDS_BALANCE");
412: require(_recipient != address(0), "MINT_TO_THE_ZERO_ADDRESS");
441: require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
460: require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");
466: require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");
File: contracts/lybra/token/LBR.sol
21: require(_sharedDecimals <= decimals, "OFT: sharedDecimals must be <= decimals");
26: require(configurator.tokenMiner(msg.sender), "not authorized");
27: require(totalSupply() + amount <= maxSupply, "exceeding the maximum supply quantity.");
33: require(configurator.tokenMiner(msg.sender), "not authorized");
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
43: require(configurator.mintVault(msg.sender), "RCP");
47: require(!configurator.vaultMintPaused(msg.sender), "MPP");
51: require(!configurator.vaultBurnPaused(msg.sender), "BPP");
59: require(_sharedDecimals <= decimals, "OFT: sharedDecimals must be <= decimals");
64: require(to != address(0), "TZA");
80: require(_msgSender() == user || _msgSender() == address(this), "MDM");
81: require(EUSD.balanceOf(address(this)) + eusdAmount <= configurator.getEUSDMaxLocked(),"ESL");
83: require(success, "TF");
115: require(peusdAmount <= userConvertInfo[msg.sender].mintedPeUSD &&peusdAmount > 0, "PCE");
134: require(success, "TF");
File: contracts/lybra/token/PeUSD.sol
13: require(_sharedDecimals <= decimals, "OFT: sharedDecimals must be <= decimals");
If a function modifier such as onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function as payable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are
CALLVALUE
(2),DUP1
(3),ISZERO
(3),PUSH2
(3),JUMPI
(10),PUSH1
(3),DUP1
(3),REVERT
(0),JUMPDEST
(1),POP
(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost
There are 26 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
98: function initToken(address _eusd, address _peusd) external onlyRole(DAO) {
109: function setMintVault(address pool, bool isActive) external onlyRole(DAO) {
119: function setMintVaultMaxSupply(address pool, uint256 maxSupply) external onlyRole(DAO) {
126: function setBadCollateralRatio(address pool, uint256 newRatio) external onlyRole(DAO) {
File: contracts/lybra/miner/esLBRBoost.sol
33: function addLockSetting(esLBRLockSetting memory setting) external onlyOwner {
File: contracts/lybra/miner/EUSDMiningIncentives.sol
84: function setToken(address _lbr, address _eslbr) external onlyOwner {
89: function setLBROracle(address _lbrOracle) external onlyOwner {
93: function setPools(address[] memory _pools) external onlyOwner {
100: function setBiddingCost(uint256 _biddingRatio) external onlyOwner {
105: function setExtraRatio(uint256 ratio) external onlyOwner {
110: function setPeUSDExtraRatio(uint256 ratio) external onlyOwner {
115: function setBoost(address _boost) external onlyOwner {
119: function setRewardsDuration(uint256 _duration) external onlyOwner {
124: function setEthlbrStakeInfo(address _pool, address _lp) external onlyOwner {
128: function setEUSDBuyoutAllowed(bool _bool) external onlyOwner {
226 function notifyRewardAmount(
227 uint256 amount
228: ) external onlyOwner updateReward(address(0)) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
52: function setTokenAddress(address _eslbr, address _lbr, address _boost) external onlyOwner {
58: function setGrabCost(uint256 _ratio) external onlyOwner {
File: contracts/lybra/miner/stakerewardV2pool.sol
121: function setRewardsDuration(uint256 _duration) external onlyOwner {
127: function setBoost(address _boost) external onlyOwner {
132: function notifyRewardAmount(uint256 _amount) external onlyOwner updateReward(address(0)) {
File: contracts/lybra/token/EUSD.sol
411: function mint(address _recipient, uint256 _mintAmount) external onlyMintVault MintPaused returns (uint256 newTotalShares) {
440: function burn(address _account, uint256 _burnAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
459: function burnShares(address _account, uint256 _sharesAmount) external onlyMintVault BurnPaused returns (uint256 newTotalShares) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
63: function mint(address to, uint256 amount) external onlyMintVault MintPaused returns (bool) {
69: function burn(address account, uint256 amount) external onlyMintVault BurnPaused returns (bool) {
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 20 instances of this issue:
see instances
File: contracts/lybra/configuration/LybraConfigurator.sol
80: constructor(address _dao, address _curvePool) {
File: contracts/lybra/governance/AdminTimelock.sol
8: constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {}
File: contracts/lybra/governance/GovernanceTimelock.sol
15: constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) TimelockController(minDelay, proposers, executors, admin) {
File: contracts/lybra/governance/LybraGovernance.sol
38: constructor(string memory name_, TimelockController timelock_, address _esLBR) GovernorTimelockControl(timelock_) Governor(name_) {
File: contracts/lybra/miner/esLBRBoost.sol
25 constructor() {
26: esLBRLockSettings.push(esLBRLockSetting(30 days, 20 * 1e18));
File: contracts/lybra/miner/EUSDMiningIncentives.sol
64: constructor(address _config, address _boost, address _etherOracle, address _lbrOracle) {
File: contracts/lybra/miner/ProtocolRewardsPool.sol
48: constructor(address _config) {
File: contracts/lybra/miner/stakerewardV2pool.sol
49: constructor(address _stakingToken, address _rewardToken, address _boost) {
File: contracts/lybra/pools/base/LybraEUSDVaultBase.sol
46: constructor(address _collateralAsset, address _etherOracle, address _configurator) {
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
37: constructor(address _peusd, address _etherOracle, address _collateral, address _configurator) {
File: contracts/lybra/pools/LybraRETHVault.sol
22 constructor(address _peusd, address _config, address _rETH, address _oracle, address _rkPool)
23: LybraPeUSDVaultBase(_peusd, _oracle, _rETH, _config) {
File: contracts/lybra/pools/LybraStETHVault.sol
18: constructor(address _config, address _stETH, address _oracle) LybraEUSDVaultBase(_stETH, _oracle, _config) {
File: contracts/lybra/pools/LybraWbETHVault.sol
17 constructor(address _peusd, address _oracle, address _asset, address _config)
18: LybraPeUSDVaultBase(_peusd, _oracle, _asset, _config) {}
File: contracts/lybra/pools/LybraWstETHVault.sol
25: constructor(address _lido, address _peusd, address _oracle, address _asset, address _config) LybraPeUSDVaultBase(_peusd, _oracle, _asset,_config) {
File: contracts/lybra/Proxy/LybraProxy.sol
9: constructor(address _logic,address _admin,bytes memory _data) TransparentUpgradeableProxy(_logic, _admin, _data) {}
File: contracts/lybra/token/esLBR.sol
22: constructor(address _config) ERC20Permit("esLBR") ERC20("esLBR", "esLBR") {
File: contracts/lybra/token/EUSD.sol
92: constructor(address _config) {
File: contracts/lybra/token/LBR.sol
18: constructor(address _config, uint8 _sharedDecimals, address _lzEndpoint) ERC20("LBR", "LBR") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
55: constructor(address _config, uint8 _sharedDecimals, address _lzEndpoint) ERC20("peg-eUSD", "PeUSD") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
File: contracts/lybra/token/PeUSD.sol
11: constructor(uint8 _sharedDecimals, address _lzEndpoint) ERC20("peg-eUSD", "peUSD") BaseOFTV2(_sharedDecimals, _lzEndpoint) {
Use msg.sender
if the code does not implement EIP-2771 trusted forwarder support
There are 17 instances of this issue:
File: contracts/lybra/token/EUSD.sol
154: address owner = _msgSender();
201: address owner = _msgSender();
227: address spender = _msgSender();
249: address owner = _msgSender();
269: address owner = _msgSender();
335: address owner = _msgSender();
File: contracts/lybra/token/LBR.sol
50: address spender = _msgSender();
62: address spender = _msgSender();
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
80: require(_msgSender() == user || _msgSender() == address(this), "MDM");
103: convertToPeUSD(_msgSender(), eusdAmount);
104: sendFrom(_msgSender(), dstChainId, toAddress, eusdAmount, callParams);
142: address spender = _msgSender();
185: address spender = _msgSender();
197: address spender = _msgSender();
File: contracts/lybra/token/PeUSD.sol
32: address spender = _msgSender();
44: address spender = _msgSender();
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 is one instance of this issue:
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
/// @audit eusdAmount
165 function getAccruedEUSDInterest(
166 address user
167: ) public view returns (uint256 eusdAmount) {
The issues below may be reported by other wardens, but can be ignored since either the rule or the specified instances are invalid
The compiler automatically expands simple expressions into their literal values, so manually doing the expansion doesn't save gas, and this finding is invalid
There are 7 instances of this issue:
File: contracts/lybra/configuration/LybraConfigurator.sol
76: bytes32 public constant DAO = keccak256("DAO");
77: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
78: bytes32 public constant ADMIN = keccak256("ADMIN");
File: contracts/lybra/governance/GovernanceTimelock.sol
10: bytes32 public constant DAO = keccak256("DAO");
11: bytes32 public constant TIMELOCK = keccak256("TIMELOCK");
12: bytes32 public constant ADMIN = keccak256("ADMIN");
13: bytes32 public constant GOV = keccak256("GOV");
Using delete instead of assigning zero to state variables does not save any extra gas - they both refund the same amount, so this finding is invalid
There are 10 instances of this issue:
File: contracts/lybra/miner/EUSDMiningIncentives.sol
196: rewards[msg.sender] = 0;
209: rewards[user] = 0;
File: contracts/lybra/miner/ProtocolRewardsPool.sol
120: unstakeRatio[msg.sender] = 0;
121: time2fullRedemption[msg.sender] = 0;
144: unstakeRatio[msg.sender] = 0;
145: time2fullRedemption[msg.sender] = 0;
193: rewards[msg.sender] = 0;
File: contracts/lybra/miner/stakerewardV2pool.sol
114: rewards[msg.sender] = 0;
File: contracts/lybra/pools/LybraStETHVault.sol
82: feeStored = 0;
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
198: feeStored[_onBehalfOf] = 0;
Both public
and external
functions use the same amount of gas (both deployment and runtime gas), so this finding is invalid
There are 18 instances of this issue:
File: contracts/lybra/governance/GovernanceTimelock.sol
25: function checkRole(bytes32 role, address _sender) public view returns(bool){
29: function checkOnlyRole(bytes32 role, address _sender) public view returns(bool){
File: contracts/lybra/pools/base/LybraPeUSDVaultBase.sol
44: function totalDepositedAsset() public view returns (uint256) {
257: function getPoolTotalPeUSDCirculation() public view returns (uint256) {
File: contracts/lybra/token/EUSD.sol
99: function name() public pure returns (string memory) {
107: function symbol() public pure returns (string memory) {
114: function decimals() public pure returns (uint8) {
124: function totalSupply() public view returns (uint256) {
134: function balanceOf(address _account) public view returns (uint256) {
153: function transfer(address _recipient, uint256 _amount) public returns (bool) {
200: function approve(address _spender, uint256 _amount) public returns (bool) {
226: function transferFrom(address from, address to, uint256 amount) public returns (bool) {
248: function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) {
285: function getTotalShares() public view returns (uint256) {
292: function sharesOf(address _account) public view returns (uint256) {
334: function transferShares(address _recipient, uint256 _sharesAmount) public returns (uint256) {
File: contracts/lybra/token/PeUSDMainnetStableVision.sol
129: function executeFlashloan(FlashBorrower receiver, uint256 eusdAmount, bytes calldata data) public payable {
165 function getAccruedEUSDInterest(
166 address user
167: ) public view returns (uint256 eusdAmount) {