Skip to content

Instantly share code, notes, and snippets.

@thebrittfactor
Created July 22, 2023 13:14
Show Gist options
  • Save thebrittfactor/0cfcacbd1366b927c268d0c41b86781b to your computer and use it in GitHub Desktop.
Save thebrittfactor/0cfcacbd1366b927c268d0c41b86781b to your computer and use it in GitHub Desktop.
Hound-bot-amphora.md
# # # # # # # # # # # # # # # # # # # # # #
#                                         #
#             |\_/|                       #
#             | O O                       #
#             |   <>              _       #
#             |  _/\------____ ((| |))    #
#             |               `--' |      #
#         ____|_       ___|   |___.'      #
#        /_/_____/____/_______|           #
#    _   _                           _    #
#   | | | |                         | |   #
#   | |_| |  ___   _   _  _ __    __| |   #
#   |  _  | / _ \ | | | || '_ \  / _` |   #
#   | | | || (_) || |_| || | | || (_| |   #
#   \_| |_/ \___/  \__,_||_| |_| \__,_|   #
#                                         #
# # # # # # # # # # # # # # # # # # # # # #

Summary


High Risk Issues

Id Title Instances
[H-01] Missing slippage checks 4

Total: 4 instances over 1 issues.

Medium Risk Issues

Id Title Instances
[M-01] Missing staleness checks for Chainlink oracle 2
[M-02] Chainlink oracle will return the wrong price if the aggregator hits minAnswer 2
[M-03] Return values of transfer/transferFrom not checked 2
[M-04] Some functions do not work correctly with fee-on-transfer tokens 2
[M-05] Some ERC20 can revert on a zero value transfer 1
[M-06] Non-compliant IERC20 tokens may revert with transfer 2
[M-07] Centralization issue caused by admin privileges 26

Total: 37 instances over 7 issues.

Low Risk Issues

Id Title Instances
[L-01] Execution at deadlines should be allowed 2
[L-02] Possible reentrancy with callback on transfer tokens 2
[L-03] Use of ecrecover is susceptible to signature malleability 2
[L-04] Low level calls with Solidity before 0.8.14 result in an optimiser bug 1
[L-05] Missing checks in constructor/initialize 9
[L-06] Vulnerability to storage write removal 1
[L-07] Functions calling contracts with transfer hooks are missing reentrancy guards 5
[L-08] Usage of functions that are not part of the IERC20 interface 4
[L-09] Large transfers may not work with some ERC20 tokens 4
[L-10] Large approvals may not work with some ERC20 tokens 1
[L-11] approve can revert if the current approval is not zero 1
[L-12] Return values of approve not checked 1
[L-13] Pausing withdrawals is unfair to the users 1
[L-14] Unsafe downcast may overflow 3
[L-15] Some functions contain the same exact logic 4
[L-16] Possible division by 0 is not prevented 2
[L-17] Truncating block.timestamp can reduce the lifespan of a contract 4
[L-18] Draft imports should be avoided 1
[L-19] Missing limits when setting min/max amounts 14
[L-20] Use of transfer instead of safeTransfer 8
[L-21] Owner can renounce ownership while system is paused 1
[L-22] Missing contract-existence checks before low-level calls 1
[L-23] External calls in an unbounded loop can result in a DoS 10
[L-24] Solidity version 0.8.20 may not work on other chains due to PUSH0 43
[L-25] Lack of two-step update for critical addresses 5
[L-26] Use of ownership with a single step rather than double 7
[L-27] Use of abi.encodePacked with dynamic types inside keccak256 1
[L-28] Loss of precision on division 27
[L-29] Gas griefing/theft is possible on an unsafe external call 1
[L-30] onlyOwner functions not accessible if owner renounces ownership 19
[L-31] Using a vulnerable dependency from some libraries 40
[L-32] Missing checks for address(0) in constructor/initializers 23
[L-33] Missing checks for address(0) when updating state variables 10
[L-34] Use of floating pragma 42
[L-35] Array lacks use of the pop function 1

Total: 301 instances over 35 issues.

Non Critical Issues

Id Title Instances
[NC-01] Missing events in sensitive functions 6
[NC-02] Unused named return 19
[NC-03] Unused error definition 4
[NC-04] Unused event definition 1
[NC-05] Unused modifier definition 1
[NC-06] OpenZeppelin libraries should be upgraded to a newer version 40
[NC-07] Unused/empty receive/fallback 1
[NC-08] Use at least Solidity version 0.8.19 to gain some gas boost 45
[NC-09] Hardcoded address should be avoided 8
[NC-10] Avoid hardcoded index accesses 5
[NC-11] Variable initialization with default value 3
[NC-12] Inconsistent usage of require/error 2
[NC-13] Unbounded loop may run out of gas 14
[NC-14] Public functions not called internally 4
[NC-15] Large multiples of ten should use scientific notation 3
[NC-16] Use of exponentiation instead of scientific notation 2
[NC-17] Use EIP-5627 to describe EIP-712 domains 2
[NC-18] Unused state variables 2
[NC-19] 2**<n> - 1 should be re-written as type(uint<n>).max 5
[NC-20] Use of non-named numeric constants 40
[NC-21] Some functions don't have any logic 1
[NC-22] Non-assembly method available 1
[NC-23] Some contract names don't follow the Solidity naming conventions 1
[NC-24] Some functions don't follow the Solidity naming conventions 1
[NC-25] Variable names don't follow the Solidity naming convention 18
[NC-26] Constant names should be UPPERCASE 4
[NC-27] Don't use uppercase for non constant/immutable variables 2
[NC-28] Use a single file for system wide constants 10
[NC-29] Use named mappings to improve code readability 16
[NC-30] Some events are never emitted 1
[NC-31] Event does not have proper indexed fields 27
[NC-32] Inconsistent method of specifying a floating pragma 6
[NC-33] experimental pragmas may be dangerous/deprecated 2
[NC-34] Old Solidity version 43
[NC-35] Zero as a function argument should have a descriptive meaning 3
[NC-36] Use of abi.encodePacked instead of bytes.concat 3
[NC-37] Use of constant variables instead of immutable 5
[NC-38] require/revert without any message 3
[NC-39] Consider activating via-ir for deploying 1
[NC-40] Imports should be organized more systematically 14
[NC-41] Event is missing msg.sender parameter 5
[NC-42] Parameter omission in events 13
[NC-43] Events may be emitted out of order due to reentrancy 72
[NC-44] Consider disabling renounceOwnership 7
[NC-45] Custom error without details 75
[NC-46] Constants in comparisons should appear on the left side 53
[NC-47] Consider using delete instead of assigning zero/false to clear values 6
[NC-48] Use a ternary statement instead of if/else when appropriate 1
[NC-49] Files not imported in any source code 1
[NC-50] Layout order does not comply with best practices 7
[NC-51] Function visibility order does not comply with best practices 9
[NC-52] Long functions should be refactored into multiple functions 3
[NC-53] Imports should use double quotes rather than single quotes 127
[NC-54] Strings should use double quotes rather than single quotes 19
[NC-55] Lines are too long 19
[NC-56] Avoid the use of sensitive terms 63
[NC-57] Misplaced SPDX identifier 1
[NC-58] Typos in comments 54
[NC-59] Contracts should have full test coverage 1
[NC-60] Large or complicated code bases should implement invariant tests 1
[NC-61] Inconsistent spacing in comments 38
[NC-62] State variables should include comments 20
[NC-63] Complicated functions should have explicit comments 1
[NC-64] Assembly code should have explicit comments 1
[NC-65] Missing NatSpec @param 19
[NC-66] Incomplete NatSpec @return 13

Total: 998 instances over 66 issues.

Gas Optimizations

Id Title Instances Gas Saved
[GAS-01] Use custom errors instead of require/assert 7 203
[GAS-02] Structs can be packed into fewer storage slots 1 20,000
[GAS-03] Structs can be modified to fit in fewer storage slots 1 40,000
[GAS-04] Multiple mappings that share an ID can be combined into a single mapping of ID / struct 2 40,084
[GAS-05] Use of memory instead of storage for struct/array state variables 4 8,400
[GAS-06] State variables access within a loop 5 1,325
[GAS-07] State variables only set in the constructor should be declared immutable 18 360,000
[GAS-08] Cache state variables with stack variables 25 6,700
[GAS-09] Use of memory instead of calldata for immutable arguments 8 2,882
[GAS-10] Avoid updating storage when the value hasn't changed 27 18,900
[GAS-11] Use of emit inside a loop 1 1,026
[GAS-12] Shortcircuit rules can be be used to optimize some gas usage 1 2,100
[GAS-13] Cache multiple accesses of a mapping/array 11 546
[GAS-14] Using private for constants saves gas 18 151,308
[GAS-15] Using bool for storage incurs overhead 2 34,200
[GAS-16] Function calls should be cached instead of re-calling the function 13 -
[GAS-17] Functions that revert when called by normal users can be payable 55 1,155
[GAS-18] Array length is not cached 10 30
[GAS-19] Add unchecked blocks for subtractions where the operands cannot underflow 2 170
[GAS-20] Empty blocks should be removed or emit something 1 4,006
[GAS-21] Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead 11 66
[GAS-22] Using pre instead of post increments/decrements 5 15
[GAS-23] Avoid using _msgSender if not supporting EIP-2771 54 864
[GAS-24] Low level call can be optimized with assembly 1 248
[GAS-25] Use assembly to emit an event 82 3,116
[GAS-26] >=/<= costs less gas than >/< 69 207
[GAS-27] internal/private functions only called once can be inlined to save gas 22 440
[GAS-28] abi.encode is more efficient than abi.encodePacked 3 720
[GAS-29] Avoid contract existence checks by using low level calls 26 2,600
[GAS-30] Unused named return variables without optimizer waste gas 17 60
[GAS-31] Using this.<fn>() wastes gas 8 800
[GAS-32] Function names can be optimized 48 1,056
[GAS-33] Avoid zero transfers calls 6 -
[GAS-34] Using delete instead of setting mapping/struct to 0 saves gas 4 20
[GAS-35] x += y is more expensive than x = x + y for state variables 15 1,695
[GAS-36] Bytes constants are more efficient than string constants 2 756
[GAS-37] Use a more recent version of Solidity 43 -
[GAS-38] Constructors can be marked payable 21 441
[GAS-39] Lack of unchecked in loops 5 300
[GAS-40] uint comparison with zero can be cheaper 10 40
[GAS-41] Use assembly to check for address(0) 6 36
[GAS-42] Use assembly to write hashes 14 1,680

Total: 684 instances over 42 issues with an estimate of 708,195 gas saved.

High Risk Issues


[H-01] Missing slippage checks

The following functions lack slippage protection and they are exposed to front-running, which will lead to the user losing their funds as they get sandwiched.

There are 4 instances of this issue.

File: core/solidity/contracts/core/WUSDA.sol

59: 		  function mint(uint256 _wusdaAmount) external override returns (uint256 _usdaAmount) {
60: 		    _usdaAmount = _wUSDAToUSDA(_wusdaAmount, _usdaSupply());
61: 		    _deposit(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
62: 		  }

70: 		  function mintFor(address _to, uint256 _wusdaAmount) external override returns (uint256 _usdaAmount) {
71: 		    _usdaAmount = _wUSDAToUSDA(_wusdaAmount, _usdaSupply());
72: 		    _deposit(_msgSender(), _to, _usdaAmount, _wusdaAmount);
73: 		  }

119: 		  function deposit(uint256 _usdaAmount) external override returns (uint256 _wusdaAmount) {
120: 		    _wusdaAmount = _usdaToWUSDA(_usdaAmount, _usdaSupply());
121: 		    _deposit(_msgSender(), _msgSender(), _usdaAmount, _wusdaAmount);
122: 		  }

130: 		  function depositFor(address _to, uint256 _usdaAmount) external override returns (uint256 _wusdaAmount) {
131: 		    _wusdaAmount = _usdaToWUSDA(_usdaAmount, _usdaSupply());
132: 		    _deposit(_msgSender(), _to, _usdaAmount, _wusdaAmount);
133: 		  }

[59-62, 70-73, 119-122, 130-133]

Recommended Mitigation Steps

Consider adding an additional parameter to indicate the minimum (or maximum) amount that should be transfered to the user, revert the transaction if this threshold is not met.


Medium Risk Issues


[M-01] Missing staleness checks for Chainlink oracle

latestRoundData may return stale data, and there aren't any checks to ensure that this doesn't happen.

This can be caused by any issues with Chainlink, such as oracle consensus problems or chain congestion, which may result in this contract relying on outdated data.

There are 2 instances of this issue.

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit missing negative price check
58: 		    (,,, uint256 _updatedAt,) = _AGGREGATOR.latestRoundData();

[58]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

// @audit missing stale answer check
12: 		    (, int256 _answer,,,) = _aggregator.latestRoundData();

[12]

Recommended Mitigation Steps

Consider using the following checks:

(uint80 roundId, int256 answer, uint startedAt, uint updatedAt, uint80) = oracle.latestRoundData();

// negative price check
require(answer > 0, "Negative Oracle Price");
// stale answer check
require(updatedAt + TIME_THRESHOLD >= block.timestamp, "Stale pricefeed");

[M-02] Chainlink oracle will return the wrong price if the aggregator hits minAnswer

Chainlink aggregators have a built-in circuit breaker if the price of an asset goes outside of a predetermined price band.

The result is that if an asset experiences a huge drop in value (i.e. LUNA crash) the price of the oracle will continue to return the minPrice instead of the actual price of the asset.

This would allow users to continue borrowing with the asset but at the wrong price. This is exactly what happened to Venus on BSC when LUNA crashed.

There are 2 instances of this issue.

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit missing min/max price check
58: 		    (,,, uint256 _updatedAt,) = _AGGREGATOR.latestRoundData();

[58]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

// @audit missing min/max price check
12: 		    (, int256 _answer,,,) = _aggregator.latestRoundData();

[12]

Recommended Mitigation Steps

Consider using the following checks:

(uint80, int256 answer, uint, uint, uint80) = oracle.latestRoundData();

// minPrice check
require(answer > minPrice, "Min price exceeded");
// maxPrice check
require(answer < maxPrice, "Max price exceeded");

[M-03] Return values of transfer/transferFrom not checked

Not all IERC20 implementations revert when there's a failure while transfering. 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 2 instances of this issue.

File: core/solidity/contracts/core/AMPHClaimer.sol

129: 		    IERC20(_token).transfer(owner(), _amount);

[129]

File: core/solidity/contracts/core/Vault.sol

194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);

[194]


[M-04] Some functions do not work correctly with fee-on-transfer tokens

Some tokens take a transfer fee (e.g. STA, PAXG), some do not currently charge a fee but may do so in the future (e.g. USDT, USDC).

Should a fee-on-transfer token be added as an asset and deposited, it could be abused, as the accounting is wrong. In the current implementation the following function calls do not work well with fee-on-transfer tokens as the amount variable is the pre-fee amount, including the fee, whereas the final balance do not include the fee anymore.

There are 2 instances of this issue.

Expand findings
File: core/solidity/contracts/core/Vault.sol

// @audit missing checks on line 92
89: 		  function depositERC20(address _token, uint256 _amount) external override onlyMinter {
90: 		    if (CONTROLLER.tokenId(_token) == 0) revert Vault_TokenNotRegistered();
91: 		    if (_amount == 0) revert Vault_AmountZero();
92: 		    SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(_token), _msgSender(), address(this), _amount);
93: 		    if (CONTROLLER.tokenCollateralType(_token) == IVaultController.CollateralType.CurveLPStakedOnConvex) {
94: 		      uint256 _poolId = CONTROLLER.tokenPoolId(_token);
95: 		      /// If it's type CurveLPStakedOnConvex then pool id can't be 0
96: 		      IBooster _booster = CONTROLLER.BOOSTER();
97: 		      if (isTokenStaked[_token]) {
98: 		        /// In this case the user's balance is already staked so we only stake the newly deposited amount
99: 		        _depositAndStakeOnConvex(_token, _booster, _amount, _poolId);
100: 		      } else {
101: 		        /// In this case the user's balance isn't staked so we stake the amount + his balance for the specific tokenv
102: 		        isTokenStaked[_token] = true;
103: 		        _depositAndStakeOnConvex(_token, _booster, balances[_token] + _amount, _poolId);
104: 		      }
105: 		    }
106: 		    balances[_token] += _amount;
107: 		    CONTROLLER.modifyTotalDeposited(vaultInfo.id, _amount, _token, true);
108: 		    emit Deposit(_token, _amount);
109: 		  }

[89-109]

File: core/solidity/contracts/core/WUSDA.sol

// @audit missing checks on line 215
214: 		  function _deposit(address _from, address _to, uint256 _usdaAmount, uint256 _wusdaAmount) private {
215: 		    IERC20(USDA).safeTransferFrom(_from, address(this), _usdaAmount);
216: 		    _mint(_to, _wusdaAmount);
217: 		
218: 		    emit Deposit(_from, _to, _usdaAmount, _wusdaAmount);
219: 		  }

[214-219]

Recommended Mitigation Steps

  • Consider comparing before and after balance to get the actual transferred amount.
  • Alternatively, disallow tokens with fee-on-transfer mechanics to be added as valid tokens.

[M-05] Some ERC20 can revert on a zero value transfer

Not all ERC20 implementations are totally compliant, and some (e.g LEND) may fail while transfering a zero amount.

There is 1 instance of this issue.

File: core/solidity/contracts/core/AMPHClaimer.sol

129: 		    IERC20(_token).transfer(owner(), _amount);

[129]


[M-06] Non-compliant IERC20 tokens may revert with transfer

Some IERC20 do not implement the standard properly, but they are still accepted by most code that accepts ERC20 tokens.

For example, USDT transfer functions on L1 do not return booleans: when casted to IERC20, their function signatures do not match, and therefore the calls made will revert.

There are 2 instances of this issue.

File: core/solidity/contracts/core/AMPHClaimer.sol

129: 		    IERC20(_token).transfer(owner(), _amount);

[129]

File: core/solidity/contracts/core/Vault.sol

194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);

[194]

Recommended Mitigation Steps

Use the OpenZeppelin SafeERC20 safeTransfer/safeTransferFrom functions instead of transfer/safeTransfer.


[M-07] Centralization issue caused by admin privileges

The owner role has a single point of failure and onlyOwner can use critical functions, posing a centralization issue. There is always a chance for owner keys to be stolen, and in such a case, the attacker can cause serious damage to the project due to important functions.

There are 26 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

119: 		  function changeVaultController(address _newVaultController) external override onlyOwner {

128: 		  function recoverDust(address _token, uint256 _amount) external override onlyOwner {

136: 		  function changeCvxRewardFee(uint256 _newFee) external override onlyOwner {

144: 		  function changeCrvRewardFee(uint256 _newFee) external override onlyOwner {

[119, 128, 136, 144]

File: core/solidity/contracts/core/USDA.sol

63: 		  function setPauser(address _pauser) external override onlyOwner {

161: 		  function mint(uint256 _susdAmount) external override paysInterest onlyOwner {

182: 		  function burn(uint256 _susdAmount) external override paysInterest onlyOwner {

212: 		  function recoverDust(address _to) external onlyOwner {

278: 		  function addVaultController(address _vaultController) external onlyOwner {

287: 		  function removeVaultController(address _vaultController) external onlyOwner {

297: 		  function removeVaultControllerFromList(address _vaultController) external onlyOwner {

[63, 161, 182, 212, 278, 287, 297]

File: core/solidity/contracts/core/VaultController.sol

305: 		  function registerUSDA(address _usdaAddress) external override onlyOwner {

312: 		  function registerCurveMaster(address _masterCurveAddress) external override onlyOwner {

319: 		  function changeProtocolFee(uint192 _newProtocolFee) external override onlyOwner {

331: 		  function registerErc20(

383: 		  function updateRegisteredErc20(

416: 		  function changeClaimerContract(IAMPHClaimer _newClaimerContract) external override onlyOwner {

425: 		  function changeInitialBorrowingFee(uint192 _newBorrowingFee) external override onlyOwner {

435: 		  function changeLiquidationFee(uint192 _newLiquidationFee) external override onlyOwner {

[305, 312, 319, 331, 383, 416, 425, 435]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

20: 		  function mint(address _dst, uint256 _rawAmount) public onlyOwner {

[20]

File: core/solidity/contracts/periphery/CurveMaster.sol

31: 		  function setVaultController(address _vaultMasterAddress) external override onlyOwner {

41: 		  function setCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {

52: 		  function forceSetCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {

[31, 41, 52]

File: core/solidity/contracts/utils/UFragments.sol

111: 		  function setMonetaryPolicy(address _monetaryPolicy) external onlyOwner {

[111]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

57: 		  function changeAnchoredView(address _anchoredViewUnderlying) external onlyOwner {

[57]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

65: 		  function setStalePriceDelay(uint256 _stalePriceDelay) external onlyOwner {

[65]


Low Risk Issues


[L-01] Execution at deadlines should be allowed

The condition may be wrong in these cases, as when block.timestamp is equal to the compared > or < variable these blocks will not be executed.

There are 2 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

560: 		    return (whitelistAccountExpirations[_account] > block.timestamp);

[560]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

59: 		    if (block.timestamp > _updatedAt + stalePriceDelay) _stale = true;

[59]


[L-02] Possible reentrancy with callback on transfer tokens

The following functions don't apply the CEI pattern. It's possible to reenter after the transfer if the token has some kind of callback functionality (e.g. ERC777/ERC1155).

There are 2 instances of this issue.

File: core/solidity/contracts/core/Vault.sol

// @audit state update on line 93
92: 		    SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(_token), _msgSender(), address(this), _amount);

// @audit state update on line 286
285: 		    SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(_token), _to, _amount);

[92, 285]


[L-03] Use of ecrecover is susceptible to signature malleability

The built-in EVM precompile ecrecover is susceptible to signature malleability, which could lead to replay attacks.

Consider using OpenZeppelin’s ECDSA library instead of the built-in function.

There are 2 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

517: 		    address _signatory = ecrecover(_digest, _v, _r, _s);

[517]

File: core/solidity/contracts/utils/UFragments.sol

333: 		    require(_owner == ecrecover(_digest, _v, _r, _s));

[333]


[L-04] Low level calls with Solidity before 0.8.14 result in an optimiser bug

The project contracts in scope are using low level calls with solidity version before 0.8.14 which can result in an optimizer bug.

This bug causes the optimizer to consider some memory operations in inline assembly as being 'dead' and remove them.

Later operations that would read the values written by these improperly removed memory operations will instead observe the old version of memory.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

166: 		    assembly {

[166]


[L-05] Missing checks in constructor/initialize

There are some missing checks in these functions, and this could lead to unexpected scenarios. Consider always adding a sanity check for state variables.

There are 9 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

// @audit _cvxRewardFee, _crvRewardFee
53: 		  constructor(
54: 		    address _vaultController,
55: 		    IERC20 _amph,
56: 		    IERC20 _cvx,
57: 		    IERC20 _crv,
58: 		    uint256 _cvxRewardFee,
59: 		    uint256 _crvRewardFee

[53-59]

File: core/solidity/contracts/core/Vault.sol

// @audit _id
66: 		  constructor(uint96 _id, address _minter, address _controllerAddress, IERC20 _cvx, IERC20 _crv) {

[66]

File: core/solidity/contracts/core/VaultController.sol

// @audit _initialBorrowingFee, _liquidationFee
93: 		  constructor(
94: 		    IVaultController _oldVaultController,
95: 		    address[] memory _tokenAddresses,
96: 		    IAMPHClaimer _claimerContract,
97: 		    IVaultDeployer _vaultDeployer,
98: 		    uint192 _initialBorrowingFee,
99: 		    address _booster,
100: 		    uint192 _liquidationFee

[93-100]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

// @audit _initialSupply
9: 		  constructor(
10: 		    address _account,
11: 		    uint256 _initialSupply

[9-11]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

// @audit _r0, _r1, _r2, _s1, _s2
33: 		  constructor(int256 _r0, int256 _r1, int256 _r2, int256 _s1, int256 _s2) {

[33]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

// @audit _widthNumerator, _widthDenominator, _staleWidthNumerator, _staleWidthDenominator
36: 		  constructor(
37: 		    address _anchorAddress,
38: 		    address _mainAddress,
39: 		    uint256 _widthNumerator,
40: 		    uint256 _widthDenominator,
41: 		    uint256 _staleWidthNumerator,
42: 		    uint256 _staleWidthDenominator

[36-42]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit _mul, _div, _stalePriceDelay
33: 		  constructor(
34: 		    address _underlying,
35: 		    address _feedAddress,
36: 		    uint256 _mul,
37: 		    uint256 _div,
38: 		    uint256 _stalePriceDelay

[33-38]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

// @audit _lookback
41: 		  constructor(uint32 _lookback, address _poolAddress, bool _quoteTokenIsToken0) OracleRelay(OracleType.Uniswap) {

[41]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

// @audit _lookback
19: 		  constructor(
20: 		    UniswapV3OracleRelay _ethOracle,
21: 		    uint32 _lookback,
22: 		    address _poolAddress,
23: 		    bool _quoteTokenIsToken0

[19-23]


[L-06] Vulnerability to storage write removal

This bug was introduced in Solidity version 0.8.13, and it was fixed in version 0.8.17. This bug is significantly easier to trigger with optimized via-IR code generation, but can theoretically also occur in optimized legacy code generation.. More info can be read in this post.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

166: 		    assembly {

[166]


[L-07] Functions calling contracts with transfer hooks are missing reentrancy guards

Even if the function follows the best practice of check-effects-interaction, not using a reentrancy guard when there may be transfer hooks will open the users of this protocol up to read-only reentrancies with no way to protect against it, except by block-listing the whole protocol.

There are 5 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

129: 		    IERC20(_token).transfer(owner(), _amount);

[129]

File: core/solidity/contracts/core/Vault.sol

92: 		    SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(_token), _msgSender(), address(this), _amount);

129: 		    SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(_tokenAddress), _msgSender(), _amount);

194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);

285: 		    SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(_token), _to, _amount);

[92, 129, 194, 285]


[L-08] Usage of functions that are not part of the IERC20 interface

The name, symbol, and decimals are not part of the IERC20 interface, and they may revert with some tokens if the don't also extend the IERC20Metadata interface.

Even if these addresses are safe and known, consider refactoring the typing to IERC20Metadata nonetheless to represent this fact.

There are 4 instances of this issue.

File: core/solidity/contracts/core/VaultController.sol

// @audit IERC20Metadata(_tokenAddress).decimals()
341: 		    uint8 _tokenDecimals = IERC20Metadata(_tokenAddress).decimals();

[341]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

// @audit IERC20Metadata(cToken.underlying()).decimals()
25: 		    uint256 _underlyingDecimals = cETH_ADDRESS == _cToken ? 18 : IERC20Metadata(cToken.underlying()).decimals();

[25]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

// @audit IERC20Metadata(BASE_TOKEN).decimals()
50: 		    BASE_TOKEN_DECIMALS = IERC20Metadata(BASE_TOKEN).decimals();

// @audit IERC20Metadata(QUOTE_TOKEN).decimals()
51: 		    QUOTE_TOKEN_DECIMALS = IERC20Metadata(QUOTE_TOKEN).decimals();

[50, 51]


[L-09] Large transfers may not work with some ERC20 tokens

Some IERC20 implementations (e.g UNI, COMP) may fail if the valued transferred is larger than uint96. Source

There are 4 instances of this issue.

File: core/solidity/contracts/core/AMPHClaimer.sol

129: 		    IERC20(_token).transfer(owner(), _amount);

[129]

File: core/solidity/contracts/core/Vault.sol

129: 		    SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(_tokenAddress), _msgSender(), _amount);

194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);

285: 		    SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(_token), _to, _amount);

[129, 194, 285]


[L-10] Large approvals may not work with some ERC20 tokens

Not all IERC20 implementations are totally compliant, and some (e.g UNI, COMP) may fail if the valued passed is larger than uint96. Source

There is 1 instance of this issue.

File: core/solidity/contracts/core/Vault.sol

321: 		    IERC20(_token).approve(address(_booster), _amount);

[321]


[L-11] approve can revert if the current approval is not zero

Some tokens like USDT check for the current approval, and they revert if it's not zero. While Tether is known to do this, it applies to other tokens as well, which are trying to protect against this attack vector.

There is 1 instance of this issue.

File: core/solidity/contracts/core/Vault.sol

321: 		    IERC20(_token).approve(address(_booster), _amount);

[321]

Recommended Mitigation Steps

Consider always calling token.safeApprove(addr, 0) before changing the approval, or better yet, use safeIncreaseAllowance/safeDecreaseAllowance.


[L-12] Return values of approve not checked

Not all IERC20 implementations revert when there's a failure in approve. 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 approving anything.

There is 1 instance of this issue.

File: core/solidity/contracts/core/Vault.sol

321: 		    IERC20(_token).approve(address(_booster), _amount);

[321]


[L-13] Pausing withdrawals is unfair to the users

Users should always have the possibility of accessing their own funds, but when these functions are paused, their funds will be locked until the contracts are unpaused.

There is 1 instance of this issue.

File: core/solidity/contracts/core/USDA.sol

147: 		  function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {

[147]


[L-14] Unsafe downcast may overflow

When a type is downcast to a smaller type, the higher order bits are discarded, resulting in the application of a modulo operation to the original value.

If the downcasted value is large enough, this may result in an overflow that will not revert.

There are 3 instances of this issue.

File: core/solidity/contracts/core/VaultController.sol

// @audit uint256 _collateralLiquidated downcasted to uint192
637: 		    _collateralLiquidated -= getLiquidationFee(uint192(_collateralLiquidated), _assetAddress);

// @audit uint256 _tokensToLiquidate downcasted to uint192
681: 		    uint192 _liquidationFee = getLiquidationFee(uint192(_tokensToLiquidate), _assetAddress);

// @audit uint256 _ui18 downcasted to int256
937: 		    int256 _reserveRatio = int256(_ui18);

[637, 681, 937]


[L-15] Some functions contain the same exact logic

These functions might be a problem if the logic changes before the contract is deployed, as the developer must remember to syncronize the logic between all the function instances.

Consider using a single function instead of duplicating the code, for example by using a library, or through inheritance.

There are 10 instances of this issue.

Expand findings
File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CTokenOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/WstEthOracle.sol -> peekValue
42: 		  function peekValue() public view override returns (uint256 _price) {

[42]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol -> peekValue
40: 		  function peekValue() public view override returns (uint256 _value) {

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol -> currentValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> currentValue
46: 		  function currentValue() external override returns (uint256 _currentValue) {

[40, 46]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CTokenOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/WstEthOracle.sol -> peekValue
34: 		  function peekValue() public view override returns (uint256 _price) {

[34]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CTokenOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/WstEthOracle.sol -> peekValue
22: 		  function peekValue() public view override returns (uint256 _price) {

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol -> currentValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> currentValue
28: 		  function currentValue() external override returns (uint256 _currentValue) {

[22, 28]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CTokenOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/WstEthOracle.sol -> peekValue
36: 		  function peekValue() public view virtual override returns (uint256 _price) {

[36]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol -> peekValue
44: 		  function peekValue() public view override returns (uint256 _value) {

[44]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CTokenOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/WstEthOracle.sol -> peekValue
60: 		  function peekValue() public view virtual override returns (uint256 _price) {

[60]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

// @audit duplicated logic in core/solidity/contracts/periphery/oracles/CTokenOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol -> peekValue, core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol -> peekValue, core/solidity/contracts/periphery/oracles/WstEthOracle.sol -> peekValue
23: 		  function peekValue() public view override returns (uint256 _price) {

[23]


[L-16] Possible division by 0 is not prevented

These functions can be called with 0 value in the input and this value is not checked for being bigger than 0, that means in some scenarios this can potentially trigger a division by zero.

There are 2 instances of this issue.

File: core/solidity/contracts/core/WUSDA.sol

// @audit _totalUsdaSupply
247: 		    _wusdaAmount = (_usdaAmount * MAX_wUSDA_SUPPLY) / _totalUsdaSupply;

[247]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

// @audit _run
83: 		    int256 _mE6 = (_rise * 1e6) / _run;

[83]


[L-17] Truncating block.timestamp can reduce the lifespan of a contract

Consider removing the casting to extend the lifespan of these contracts.

There are 4 instances of this issue.

File: core/solidity/contracts/core/VaultController.sol

103: 		    interest = Interest(uint64(block.timestamp), 1 ether);

930: 		    uint64 _timeDifference = uint64(block.timestamp) - interest.lastTime;

957: 		    interest = Interest(uint64(block.timestamp), interest.factor + _e18FactorIncrease);

970: 		    emit InterestEvent(uint64(block.timestamp), _e18FactorIncrease, _curveVal);

[103, 930, 957, 970]


[L-18] Draft imports should be avoided

A draft status means that the EIPs they're based on are not finalized, and thus there may be breaking changes in even minor releases. Consider using a stable version for these libraries instead of a draft.

There is 1 instance of this issue.

File: core/solidity/contracts/core/WUSDA.sol

9: 		import {ERC20Permit} from '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol';

[9]


[L-19] Missing limits when setting min/max amounts

There are some missing limits in these functions, and this could lead to unexpected scenarios. Consider adding a min/max limit for the following values, when appropriate.

There are 14 instances of this issue.

Expand findings
File: core/solidity/contracts/core/VaultController.sol

// @audit missing both checks -> _liquidationIncentive, _cap, _poolId
// @audit missing min check -> _ltv
383: 		  function updateRegisteredErc20(
384: 		    address _tokenAddress,
385: 		    uint256 _ltv,
386: 		    address _oracleAddress,
387: 		    uint256 _liquidationIncentive,
388: 		    uint256 _cap,
389: 		    uint256 _poolId

[383-389]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit missing both checks -> _second
575: 		  function setMaxWhitelistPeriod(uint256 _second) external onlyGov {

// @audit missing both checks -> _proposalTimelockDelay
583: 		  function setDelay(uint256 _proposalTimelockDelay) public override onlyGov {

// @audit missing both checks -> _emergencyTimelockDelay
594: 		  function setEmergencyDelay(uint256 _emergencyTimelockDelay) public override onlyGov {

// @audit missing both checks -> _newVotingDelay
605: 		  function setVotingDelay(uint256 _newVotingDelay) external override onlyGov {

// @audit missing both checks -> _newVotingPeriod
616: 		  function setVotingPeriod(uint256 _newVotingPeriod) external override onlyGov {

// @audit missing both checks -> _newEmergencyVotingPeriod
627: 		  function setEmergencyVotingPeriod(uint256 _newEmergencyVotingPeriod) external override onlyGov {

// @audit missing both checks -> _newProposalThreshold
638: 		  function setProposalThreshold(uint256 _newProposalThreshold) external override onlyGov {

// @audit missing both checks -> _newQuorumVotes
649: 		  function setQuorumVotes(uint256 _newQuorumVotes) external override onlyGov {

// @audit missing both checks -> _newEmergencyQuorumVotes
660: 		  function setEmergencyQuorumVotes(uint256 _newEmergencyQuorumVotes) external override onlyGov {

// @audit missing min check -> _expiration
673: 		  function setWhitelistAccountExpiration(address _account, uint256 _expiration) external override onlyGov {

// @audit missing both checks -> _newOptimisticVotingDelay
695: 		  function setOptimisticDelay(uint256 _newOptimisticVotingDelay) external override onlyGov {

// @audit missing both checks -> _newOptimisticQuorumVotes
706: 		  function setOptimisticQuorumVotes(uint256 _newOptimisticQuorumVotes) external override onlyGov {

[575, 583, 594, 605, 616, 627, 638, 649, 660, 673, 695, 706]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit missing both checks -> _stalePriceDelay
65: 		  function setStalePriceDelay(uint256 _stalePriceDelay) external onlyOwner {

[65]


[L-20] Use of transfer instead of safeTransfer

It is good to add a require statement that checks the return value of token transfers or to use something like OpenZeppelin’s safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.

There are 8 instances of this issue.

File: core/solidity/contracts/core/USDA.sol

103: 		    sUSD.transferFrom(_msgSender(), address(this), _susdAmount);

153: 		    sUSD.transfer(_target, _susdAmount);

206: 		    sUSD.transferFrom(_msgSender(), address(this), _susdAmount);

216: 		    sUSD.transfer(_to, _amount);

246: 		    sUSD.transfer(_target, _susdAmount);

[103, 153, 206, 216, 246]

File: core/solidity/contracts/core/Vault.sol

194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);

223: 		      if (_totalCvxReward > 0) CVX.transfer(_msgSender(), _totalCvxReward);

224: 		      if (_totalCrvReward > 0) CRV.transfer(_msgSender(), _totalCrvReward);

[194, 223, 224]


[L-21] Owner can renounce ownership while system is paused

The contract owner is not prevented from renouncing the ownership while the contract is paused, which would cause any user assets stored in the protocol to be locked indefinitely.

There is 1 instance of this issue.

File: core/solidity/contracts/core/USDA.sol

63: 		  function setPauser(address _pauser) external override onlyOwner {

[63]


[L-22] Missing contract-existence checks before low-level calls

Low-level calls return success if there is no code present at the specified address, and this could lead to unexpected scenarios.

Ensure that the code is initialized by checking <address>.code.length > 0.

There is 1 instance of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

350: 		    (bool _success, /*bytes memory returnData*/ ) = _target.call{value: _value}(_callData);

[350]


[L-23] External calls in an unbounded loop can result in a DoS

Consider limiting the number of iterations in loops that make external calls, as just a single one of them failing will result in a revert.

There are 10 instances of this issue.

File: core/solidity/contracts/core/Vault.sol

// @audit tokenCollateralInfo (170), tokenId (171), collateralType (172), CurveLPStakedOnConvex (172), CollateralType (172), crvRewardsContract (176), earned (177), getReward (182), extraRewardsLength (187), extraRewards (189), rewardToken (190), earned (191), getReward (193), transfer (194)
169: 		    for (uint256 _i; _i < _tokenAddresses.length;) {

// @audit extraRewards (255), rewardToken (256), earned (257)
254: 		    for (_i; _i < _rewardsAmount;) {

[169, 254]

File: core/solidity/contracts/core/VaultController.sol

// @audit tokenId (256), tokenCollateralInfo (260), tokenId (261), totalDeposited (262)
255: 		    for (uint256 _i; _i < _tokenAddresses.length;) {

// @audit ltv (871), balances (876), currentValue (879), oracle (879), ltv (883), decimals (883)
868: 		    for (uint192 _i; _i < enabledTokens.length; ++_i) {

// @audit ltv (899), balances (904), peekValue (907), oracle (907), ltv (911), decimals (911)
896: 		    for (uint192 _i; _i < enabledTokens.length; ++_i) {

// @audit balances (999), vaultLiability (1006)
994: 		    for (uint96 _i = _start; _i <= _stop;) {

[255, 868, 896, 994]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit targets (259), targets (263), values (263), signatures (263), calldatas (263), targets (268), values (269), signatures (270), calldatas (271), delay (273)
259: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {

// @audit targets (313), executeTransaction (314), values (314), targets (315), values (315), signatures (315), calldatas (315), eta (315)
313: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {

// @audit targets (382), targets (384), values (384), signatures (384), calldatas (384), eta (384)
382: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {

[259, 313, 382]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

// @audit min (45), peekValue (45)
44: 		    for (uint256 _i = 1; _i < anchoredUnderlyingTokens.length;) {

[44]


[L-24] Solidity version 0.8.20 may not work on other chains due to PUSH0

In Solidity 0.8.20's compiler, the default target EVM version has been changed to Shanghai. This version introduces a new op code called PUSH0.

However, not all Layer 2 solutions have implemented this op code yet, leading to deployment failures on these chains. To overcome this problem, it is recommended to utilize an earlier EVM version.

There are 43 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/USDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/Vault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultDeployer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/WUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/GovernorCharlie.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/contracts/periphery/CurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/GovernanceStructs.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/UFragments.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IWUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAMPH.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IAnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveAddressesProvider.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBooster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurvePool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurveSlave.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]


[L-25] Lack of two-step update for critical addresses

Add a two-step process for any critical address changes.

There are 5 instances of this issue.

Expand findings
File: core/solidity/contracts/core/USDA.sol

63: 		  function setPauser(address _pauser) external override onlyOwner {
64: 		    pauser = _pauser;
65: 		
66: 		    emit PauserSet(_pauser);
67: 		  }

[63-67]

File: core/solidity/contracts/core/VaultController.sol

383: 		  function updateRegisteredErc20(
384: 		    address _tokenAddress,
385: 		    uint256 _ltv,
386: 		    address _oracleAddress,
387: 		    uint256 _liquidationIncentive,
388: 		    uint256 _cap,
389: 		    uint256 _poolId
390: 		  ) external override onlyOwner {
391: 		    CollateralInfo storage _collateral = tokenAddressCollateralInfo[_tokenAddress];
392: 		    if (_collateral.tokenId == 0) revert VaultController_TokenNotRegistered();
393: 		    // _ltv must be compatible with liquidation incentive
394: 		    if (_ltv >= (EXP_SCALE - _liquidationIncentive)) revert VaultController_LTVIncompatible();
395: 		    if (_poolId != 0) {
396: 		      (address _lpToken,,, address _crvRewards,,) = BOOSTER.poolInfo(_poolId);
397: 		      if (_lpToken != _tokenAddress) revert VaultController_TokenAddressDoesNotMatchLpAddress();
398: 		      _collateral.collateralType = CollateralType.CurveLPStakedOnConvex;
399: 		      _collateral.crvRewardsContract = IBaseRewardPool(_crvRewards);
400: 		      _collateral.poolId = _poolId;
401: 		    }
402: 		    // set the oracle of the token
403: 		    _collateral.oracle = IOracleRelay(_oracleAddress);
404: 		    // set the ltv of the token
405: 		    _collateral.ltv = _ltv;
406: 		    // set the liquidation incentive of the token
407: 		    _collateral.liquidationIncentive = _liquidationIncentive;
408: 		    // set the cap
409: 		    _collateral.cap = _cap;
410: 		
411: 		    emit UpdateRegisteredErc20(_tokenAddress, _ltv, _oracleAddress, _liquidationIncentive, _cap, _poolId);
412: 		  }

[383-412]

File: core/solidity/contracts/periphery/CurveMaster.sol

31: 		  function setVaultController(address _vaultMasterAddress) external override onlyOwner {
32: 		    address _oldCurveAddress = vaultControllerAddress;
33: 		    vaultControllerAddress = _vaultMasterAddress;
34: 		
35: 		    emit VaultControllerSet(_oldCurveAddress, _vaultMasterAddress);
36: 		  }

41: 		  function setCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {
42: 		    if (vaultControllerAddress != address(0)) IVaultController(vaultControllerAddress).calculateInterest();
43: 		    address _oldCurve = curves[_tokenAddress];
44: 		    curves[_tokenAddress] = _curveAddress;
45: 		
46: 		    emit CurveSet(_oldCurve, _tokenAddress, _curveAddress);
47: 		  }

[31-36, 41-47]

File: core/solidity/contracts/utils/UFragments.sol

111: 		  function setMonetaryPolicy(address _monetaryPolicy) external onlyOwner {
112: 		    monetaryPolicy = _monetaryPolicy;
113: 		    emit LogMonetaryPolicyUpdated(_monetaryPolicy);
114: 		  }

[111-114]


[L-26] Use of ownership with a single step rather than double

A single step ownership change is risky due to the fact that the new owner address could be wrong.

Instead, consider using a contract like Ownable2Step, which provides a two-step ownership.

There are 7 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[6]

File: core/solidity/contracts/core/VaultController.sol

17: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[17]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[5]

File: core/solidity/contracts/periphery/CurveMaster.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/utils/UFragments.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[5]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[6]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]


[L-27] Use of abi.encodePacked with dynamic types inside keccak256

abi.encodePacked should not be used with dynamic types when passing the result to a hash function such as keccak256. Use abi.encode instead, which will pad items to 32 bytes, to prevent any hash collisions.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

328: 		    bytes32 _digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR(), _permitDataDigest));

[328]


[L-28] Loss of precision on division

Solidity doesn't support fractions, so division by large numbers could result in the quotient being zero.

To avoid this, it's recommended to require a minimum numerator amount to ensure that it is always greater than the denominator.

There are 27 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

153: 		    _amount = (_total * _fraction) / _BASE;

195: 		    uint256 _foo = (_TWENTY_FIVE_THOUSANDS * BASE_SUPPLY_PER_CLIFF) / Math.max(_distributedAmph, _FIFTY_MILLIONS);

196: 		    uint256 _bar = (_distributedAmph * 1e12) / (BASE_SUPPLY_PER_CLIFF * _FIFTY);

232: 		        uint256 _amountTokenToEnter = (_amphAvailableForThisCliff * 1e18) / _rate;

250: 		    _cliff = _distributedAmph / BASE_SUPPLY_PER_CLIFF;

[153, 195, 196, 232, 250]

File: core/solidity/contracts/core/USDA.sol

262: 		    _gonsPerFragment = _totalGons / _totalSupply;

269: 		    _e18reserveRatio = _safeu192((reserveAmount * EXP_SCALE) / _totalSupply);

[262, 269]

File: core/solidity/contracts/core/Vault.sol

337: 		    uint256 _cliff = _supply / CVX.reductionPerCliff();

343: 		      _cvxAmount = (_crv * _reduction) / _totalCliffs;

[337, 343]

File: core/solidity/contracts/core/VaultController.sol

535: 		    uint192 _baseAmount = _safeu192(uint256((_amount + _fee) * EXP_SCALE) / uint256(interest.factor));

597: 		      _baseAmount = _safeu192((_amountInUSDA * EXP_SCALE) / interest.factor);

667: 		    _vault.modifyLiability(false, (_usdaToRepurchase * 1e18) / interest.factor);

670: 		    totalBaseLiability -= _safeu192((_usdaToRepurchase * 1e18) / interest.factor);

790: 		    uint256 _maxTokensToLiquidate = (_usdaToSolvency * 1e18) / _denominator;

948: 		        _truncate((uint256(_timeDifference) * uint256(1e18) * uint256(_curveVal)) / (365 days + 6 hours))

[535, 597, 667, 670, 790, 948]

File: core/solidity/contracts/core/WUSDA.sol

247: 		    _wusdaAmount = (_usdaAmount * MAX_wUSDA_SUPPLY) / _totalUsdaSupply;

255: 		    _usdaAmount = (_wusdaAmount * _totalUsdaSupply) / MAX_wUSDA_SUPPLY;

[247, 255]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

83: 		    int256 _mE6 = (_rise * 1e6) / _run;

[83]

File: core/solidity/contracts/utils/UFragments.sol

104: 		    _gonsPerFragment = _totalGons / _totalSupply;

129: 		    return _gonBalances[_who] / _gonsPerFragment;

196: 		    uint256 _value = _gonValue / _gonsPerFragment;

245: 		    uint256 _value = _gonValue / _gonsPerFragment;

[104, 129, 196, 245]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

85: 		      _buffer = (staleWidthNumerator * _anchorPrice) / staleWidthDenominator;

87: 		      _buffer = (widthNumerator * _anchorPrice) / widthDenominator;

[85, 87]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

52: 		    _value = (_currentValue * _exchangeRate) / div;

[52]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

75: 		    _value = (uint256(_latest) * MULTIPLY) / DIVIDE;

[75]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

85: 		    _e18Amount = (_decimals > 18) ? _amount / (10 ** (_decimals - 18)) : _amount * (10 ** (18 - _decimals));

[85]


[L-29] Gas griefing/theft is possible on an unsafe external call

A low-level call will copy any amount of bytes to local memory. When bytes are copied from returndata to memory, the memory expansion cost is paid.

This means that when using a standard solidity call, the callee can 'returnbomb' the caller, imposing an arbitrary gas cost.

Because this gas is paid by the caller and in the caller's context, it can cause the caller to run out of gas and halt execution.

Consider replacing all unsafe call with excessivelySafeCall from this repository.

There is 1 instance of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

350: 		    (bool _success, /*bytes memory returnData*/ ) = _target.call{value: _value}(_callData);

[350]


[L-30] onlyOwner functions not accessible if owner renounces ownership

The owner is able to perform certain privileged activities, but it's possible to set the owner to address(0). This can represent a certain risk if the ownership is renounced for any other reason than by design.

Renouncing ownership will leave the contract without an owner, therefore limiting any functionality that needs authority.

There are 19 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

119: 		  function changeVaultController(address _newVaultController) external override onlyOwner {

128: 		  function recoverDust(address _token, uint256 _amount) external override onlyOwner {

136: 		  function changeCvxRewardFee(uint256 _newFee) external override onlyOwner {

144: 		  function changeCrvRewardFee(uint256 _newFee) external override onlyOwner {

[119, 128, 136, 144]

File: core/solidity/contracts/core/VaultController.sol

305: 		  function registerUSDA(address _usdaAddress) external override onlyOwner {

312: 		  function registerCurveMaster(address _masterCurveAddress) external override onlyOwner {

319: 		  function changeProtocolFee(uint192 _newProtocolFee) external override onlyOwner {

338: 		  ) external override onlyOwner {

390: 		  ) external override onlyOwner {

416: 		  function changeClaimerContract(IAMPHClaimer _newClaimerContract) external override onlyOwner {

425: 		  function changeInitialBorrowingFee(uint192 _newBorrowingFee) external override onlyOwner {

435: 		  function changeLiquidationFee(uint192 _newLiquidationFee) external override onlyOwner {

[305, 312, 319, 338, 390, 416, 425, 435]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

20: 		  function mint(address _dst, uint256 _rawAmount) public onlyOwner {

[20]

File: core/solidity/contracts/periphery/CurveMaster.sol

31: 		  function setVaultController(address _vaultMasterAddress) external override onlyOwner {

41: 		  function setCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {

52: 		  function forceSetCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {

[31, 41, 52]

File: core/solidity/contracts/utils/UFragments.sol

111: 		  function setMonetaryPolicy(address _monetaryPolicy) external onlyOwner {

[111]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

57: 		  function changeAnchoredView(address _anchoredViewUnderlying) external onlyOwner {

[57]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

65: 		  function setStalePriceDelay(uint256 _stalePriceDelay) external onlyOwner {

[65]


[L-31] Using a vulnerable dependency from some libraries

This project is using a vulnerable version of some libraries, which have the following issues:

Current @openzeppelin/contracts version: 4.8.2

Risk Title Min Version Max Version
LOW Denial of Service (DoS) >=3.2.0 <4.8.3
MEDIUM Improper Input Validation >=3.3.0 <4.8.3

There are 40 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

4: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

7: 		import {SafeERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

[4, 6, 7]

File: core/solidity/contracts/core/USDA.sol

11: 		import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

12: 		import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';

13: 		import {Context} from '@openzeppelin/contracts/utils/Context.sol';

14: 		import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

[11, 12, 13, 14]

File: core/solidity/contracts/core/Vault.sol

12: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

13: 		import {Context} from '@openzeppelin/contracts/utils/Context.sol';

14: 		import {SafeERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol';

15: 		import {IERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol';

[12, 13, 14, 15]

File: core/solidity/contracts/core/VaultController.sol

16: 		import {IERC20, IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

17: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

18: 		import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';

[16, 17, 18]

File: core/solidity/contracts/core/VaultDeployer.sol

7: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[7]

File: core/solidity/contracts/core/WUSDA.sol

6: 		import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

7: 		import {ERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

9: 		import {ERC20Permit} from '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol';

[6, 7, 9]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

6: 		import {ERC20, ERC20Permit, ERC20VotesComp} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20VotesComp.sol';

[5, 6]

File: core/solidity/contracts/periphery/CurveMaster.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/utils/UFragments.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

6: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[5, 6]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/core/IUSDA.sol

6: 		import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[6]

File: core/solidity/interfaces/core/IVault.sol

5: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[5]

File: core/solidity/interfaces/core/IVaultDeployer.sol

6: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[6]

File: core/solidity/interfaces/core/IWUSDA.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/ICVX.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/ICurvePool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IRoles.sol

4: 		import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol';

[4]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IWStETH.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

7: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[6, 7]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

6: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

[6]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

6: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

[6]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

7: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[7]

Recommended Mitigation Steps

Consider upgrading all the impacted libraries to a newer version.


[L-32] Missing checks for address(0) in constructor/initializers

Check for zero-address to avoid the risk of setting address(0) for state variables when deploying.

There are 23 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

61: 		    vaultController = IVaultController(_vaultController);

62: 		    CVX = _cvx;

63: 		    CRV = _crv;

64: 		    AMPH = _amph;

[61, 62, 63, 64]

File: core/solidity/contracts/core/USDA.sol

58: 		    sUSD = _sUSDAddr;

[58]

File: core/solidity/contracts/core/Vault.sol

68: 		    CONTROLLER = IVaultController(_controllerAddress);

70: 		    CRV = _crv;

[68, 70]

File: core/solidity/contracts/core/VaultController.sol

110: 		    BOOSTER = IBooster(_booster);

[110]

File: core/solidity/contracts/core/VaultDeployer.sol

19: 		    CVX = _cvx;

20: 		    CRV = _crv;

[19, 20]

File: core/solidity/contracts/core/WUSDA.sol

49: 		    USDA = _usdaToken;

[49]

File: core/solidity/contracts/governance/GovernorCharlie.sol

89: 		    amph = IAMPH(_amph);

[89]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

44: 		    anchorRelay = IOracleRelay(_anchorAddress);

46: 		    mainRelay = IOracleRelay(_mainAddress);

[44, 46]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

21: 		    cToken = ICToken(_cToken);

22: 		    anchoredViewUnderlying = _anchoredViewUnderlying;

[21, 22]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

29: 		    CB_ETH_POOL = IV2Pool(_cbETHPool);

[29]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

40: 		    _AGGREGATOR = AggregatorV2V3Interface(_feedAddress);

[40]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

24: 		    AGGREGATOR = ChainlinkOracleRelay(_feedAddress);

25: 		    BASE_AGGREGATOR = ChainlinkOracleRelay(_baseFeedAddress);

[24, 25]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

21: 		    CRV_POOL = IStablePool(_crvPool);

[21]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

32: 		    TRI_CRYPTO = IV2Pool(_triCryptoPool);

[32]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

44: 		    POOL = IUniswapV3Pool(_poolAddress);

[44]


[L-33] Missing checks for address(0) when updating state variables

Check for zero-address to avoid the risk of setting address(0) for state variables after an update.

There are 10 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

120: 		    vaultController = IVaultController(_newVaultController);

[120]

File: core/solidity/contracts/core/USDA.sol

64: 		    pauser = _pauser;

[64]

File: core/solidity/contracts/core/VaultController.sol

306: 		    usda = IUSDA(_usdaAddress);

313: 		    curveMaster = CurveMaster(_masterCurveAddress);

[306, 313]

File: core/solidity/contracts/governance/GovernorCharlie.sol

568: 		    amph = IAMPH(_token);

686: 		    whitelistGuardian = _account;

[568, 686]

File: core/solidity/contracts/periphery/CurveMaster.sol

33: 		    vaultControllerAddress = _vaultMasterAddress;

[33]

File: core/solidity/contracts/utils/UFragments.sol

112: 		    monetaryPolicy = _monetaryPolicy;

[112]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

58: 		    anchoredViewUnderlying = IOracleRelay(_anchoredViewUnderlying);

[58]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

20: 		    underlying = _underlying;

[20]


[L-34] Use of floating pragma

Locking the pragma helps avoid accidental deploys with an outdated compiler version that may introduce bugs and unexpected vulnerabilities.

Floating pragma is meant to be used for libraries and contracts that have external users and need backward compatibility.

There are 42 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/USDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/Vault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultDeployer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/WUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/GovernorCharlie.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/contracts/periphery/CurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/GovernanceStructs.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/UFragments.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IWUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAMPH.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IAnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveAddressesProvider.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBooster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurvePool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurveSlave.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]


[L-35] Array lacks use of the pop function

There are some arrays that can grow indefinitely in size, as they never shrink: this can lead to inefficient memory management and increase the likelihood of out-of-gas errors.

There is 1 instance of this issue.

File: core/solidity/contracts/core/VaultController.sol

264: 		      enabledTokens.push(_tokenAddresses[_i]);

[264]


Non Critical Issues


[NC-01] Missing events in sensitive functions

Events should be emitted when sensitive changes are made to the contracts, but some functions lack them.

There are 6 instances of this issue.

Expand findings
File: core/solidity/contracts/governance/GovernorCharlie.sol

567: 		  function setNewToken(address _token) external onlyGov {
568: 		    amph = IAMPH(_token);
569: 		  }

575: 		  function setMaxWhitelistPeriod(uint256 _second) external onlyGov {
576: 		    maxWhitelistPeriod = _second;
577: 		  }

[567-569, 575-577]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

70: 		  function _updateVirtualPrice() internal {
71: 		    uint256 _virtualPrice = CB_ETH_POOL.get_virtual_price();
72: 		
73: 		    // We remove claim admin fees on the curve pool locked to avoid any manipulation
74: 		    CB_ETH_POOL.claim_admin_fees();
75: 		
76: 		    virtualPrice = _virtualPrice;
77: 		  }

[70-77]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

65: 		  function setStalePriceDelay(uint256 _stalePriceDelay) external onlyOwner {
66: 		    if (_stalePriceDelay == 0) revert ChainlinkOracle_ZeroAmount();
67: 		    stalePriceDelay = _stalePriceDelay;
68: 		  }

[65]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

36: 		  function _updateVirtualPrice() internal {
37: 		    uint256 _virtualPrice = CRV_POOL.get_virtual_price();
38: 		
39: 		    // We remove 0 liquidity to check the curve pool lock and avoid any manipulation
40: 		    uint256[2] memory _amounts;
41: 		    CRV_POOL.remove_liquidity(0, _amounts);
42: 		
43: 		    virtualPrice = _virtualPrice;
44: 		  }

[36-44]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

19: 		  function _setUnderlying(address _underlying) internal {
20: 		    underlying = _underlying;
21: 		  }

[19-21]


[NC-02] Unused named return

Declaring named returns, but not using them, is confusing to the reader. Consider either completely removing them (by declaring just the type without a name), or remove the return statement and do a variable assignment.

This would improve the readability of the code, and it may also help reduce regressions during future code refactors.

There are 19 instances of this issue.

Expand findings
File: core/solidity/contracts/core/VaultController.sol

866: 		  function _getVaultBorrowingPower(IVault _vault) private returns (uint192 _borrowPower) {

894: 		  function _peekVaultBorrowingPower(IVault _vault) private view returns (uint192 _borrowPower) {

[866, 894]

File: core/solidity/contracts/governance/GovernorCharlie.sol

424: 		      address[] memory _targets,

431: 		    return (_proposal.targets, _proposal.values, _proposal.signatures, _proposal.calldatas);

559: 		  function isWhitelisted(address _account) public view override returns (bool _isWhitelisted) {
560: 		    return (whitelistAccountExpirations[_account] > block.timestamp);

[424, 559]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

47: 		  function valueAt(int256 _xValue) external view override returns (int256 _value) {

57: 		      return _linearInterpolation(_rise, _run, _xValue, R0);

63: 		      return _linearInterpolation(_rise, _run, _xValue - S1, R1);

67: 		    return R2;

[47]

File: core/solidity/contracts/utils/UFragments.sol

120: 		  function totalSupply() external view override returns (uint256 __totalSupply) {
121: 		    return _totalSupply;

128: 		  function balanceOf(address _who) external view override returns (uint256 _balance) {
129: 		    return _gonBalances[_who] / _gonsPerFragment;

136: 		  function scaledBalanceOf(address _who) external view returns (uint256 _balance) {
137: 		    return _gonBalances[_who];

144: 		  function scaledTotalSupply() external view returns (uint256 __totalGons) {
145: 		    return _totalGons;

153: 		  function nonces(address _who) public view returns (uint256 _addressNonces) {
154: 		    return _nonces[_who];

163: 		  function DOMAIN_SEPARATOR() public view returns (bytes32 _domainSeparator) {

168: 		    return keccak256(
169: 		      abi.encode(EIP712_DOMAIN, keccak256(bytes(name)), keccak256(bytes(EIP712_REVISION)), _chainId, address(this))
170: 		    );

179: 		  function transfer(address _to, uint256 _value) external override validRecipient(_to) returns (bool _success) {

186: 		    return true;

194: 		  function transferAll(address _to) external validRecipient(_to) returns (bool _success) {

202: 		    return true;

211: 		  function allowance(address _owner, address _spender) external view override returns (uint256 _remaining) {
212: 		    return _allowedFragments[_owner][_spender];

226: 		  ) external override validRecipient(_to) returns (bool _success) {

234: 		    return true;

243: 		  function transferAllFrom(address _from, address _to) external validRecipient(_to) returns (bool _success) {

253: 		    return true;

268: 		  function approve(address _spender, uint256 _value) external override returns (bool _success) {

272: 		    return true;

283: 		  function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool _success) {

287: 		    return true;

297: 		  function decreaseAllowance(address _spender, uint256 _subtractedValue) external returns (bool _success) {

302: 		    return true;

[120, 128, 136, 144, 153, 163, 179, 194, 211, 226, 243, 268, 283, 297]


[NC-03] Unused error definition

The following errors are never used, consider to remove them.

There are 4 instances of this issue.

File: core/solidity/interfaces/core/IVaultController.sol

155: 		  error VaultController_OracleNotRegistered();

[155]

File: core/solidity/interfaces/core/IVaultDeployer.sol

20: 		  error VaultDeployer_OnlyVaultController();

[20]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

12: 		  error TriCryptoOracle_ZeroAmount();

[12]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

14: 		  error UniswapV3OracleRelay_TickTimeDiffTooLarge();

[14]


[NC-04] Unused event definition

The following events are never used, consider to remove them.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

39: 		  event LogRebase(uint256 indexed epoch, uint256 totalSupply);

[39]


[NC-05] Unused modifier definition

The following modifier are never used, consider to remove them.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

51: 		  modifier onlyMonetaryPolicy() {
52: 		    require(msg.sender == monetaryPolicy);
53: 		    _;
54: 		  }

[51-54]


[NC-06] OpenZeppelin libraries should be upgraded to a newer version

These contracts import some OpenZeppelin libraries, but they are using an old version.

There are 40 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

4: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

7: 		import {SafeERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

[4, 6, 7]

File: core/solidity/contracts/core/USDA.sol

11: 		import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

12: 		import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';

13: 		import {Context} from '@openzeppelin/contracts/utils/Context.sol';

14: 		import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

[11, 12, 13, 14]

File: core/solidity/contracts/core/Vault.sol

12: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

13: 		import {Context} from '@openzeppelin/contracts/utils/Context.sol';

14: 		import {SafeERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol';

15: 		import {IERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol';

[12, 13, 14, 15]

File: core/solidity/contracts/core/VaultController.sol

16: 		import {IERC20, IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

17: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

18: 		import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';

[16, 17, 18]

File: core/solidity/contracts/core/VaultDeployer.sol

7: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[7]

File: core/solidity/contracts/core/WUSDA.sol

6: 		import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

7: 		import {ERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

9: 		import {ERC20Permit} from '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol';

[6, 7, 9]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

6: 		import {ERC20, ERC20Permit, ERC20VotesComp} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20VotesComp.sol';

[5, 6]

File: core/solidity/contracts/periphery/CurveMaster.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/utils/UFragments.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

6: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[5, 6]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/core/IUSDA.sol

6: 		import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[6]

File: core/solidity/interfaces/core/IVault.sol

5: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[5]

File: core/solidity/interfaces/core/IVaultDeployer.sol

6: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[6]

File: core/solidity/interfaces/core/IWUSDA.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/ICVX.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/ICurvePool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IRoles.sol

4: 		import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol';

[4]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IWStETH.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

7: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[6, 7]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

6: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

[6]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

6: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

[6]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

7: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[7]

Recommended Mitigation Steps

Consider upgrading from version 4.8.2 to at least version 4.9.0.


[NC-07] Unused/empty receive/fallback

If the intention is for the ETH to be used, the function should call another function, otherwise it should revert (e.g. require(msg.sender == address(weth)))

There is 1 instance of this issue.

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

54: 		  fallback() external {}

[54]


[NC-08] Use at least Solidity version 0.8.19 to gain some gas boost

Upgrade to at least solidity version 0.8.19 to get additional gas savings. Check the documentation for reference.

Some additional details:

In earlier releases and in the default legacy code generation, when an internal library function or a free function accessed via a module was called only during contract creation, e.g. only in the constructor, a copy of the function still also occurred in the contract’s runtime bytecode.

So a function pointer in creation code also refers to the offset of the function in runtime code, which requires the function to actually be present in runtime code.

For direct calls to internal contract functions the full encoding of the function expression is bypassed by the compiler. However, this bypassing did not happen for internal library functions and for free functions called via modules, causing the undesirable behaviour that is now fixed in this release.

There are 45 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/USDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/Vault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultDeployer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/WUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/GovernorCharlie.sol

3: 		pragma solidity ^0.8.9;

4: 		pragma experimental ABIEncoderV2;

[3, 4]

File: core/solidity/contracts/periphery/CurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/GovernanceStructs.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/UFragments.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IWUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAMPH.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

3: 		pragma experimental ABIEncoderV2;

[2, 3]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IAnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveAddressesProvider.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBooster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurvePool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurveSlave.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]


[NC-09] Hardcoded address should be avoided

It's better to declare the hardcoded addresses as immutable state variables, as this will facilitate deployment on other chains.

There are 8 instances of this issue.

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

// @audit 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5
12: 		  address public constant cETH_ADDRESS = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;

// @audit 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5
12: 		  address public constant cETH_ADDRESS = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;

[12, 12]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

// @audit 0x0000000022D53366457F9d5E68Ec105046FC4383
9: 		    ICurveAddressesProvider(0x0000000022D53366457F9d5E68Ec105046FC4383);

// @audit 0x0000000022D53366457F9d5E68Ec105046FC4383
9: 		    ICurveAddressesProvider(0x0000000022D53366457F9d5E68Ec105046FC4383);

[9, 9]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

// @audit 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
8: 		  address public constant wETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

// @audit 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
8: 		  address public constant wETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

[8, 8]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

// @audit 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
10: 		  IWStETH public constant WSTETH = IWStETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);

// @audit 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
10: 		  IWStETH public constant WSTETH = IWStETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);

[10, 10]


[NC-10] Avoid hardcoded index accesses

Consider using an enum instead of hardcoding an index access to make the code easier to understand.

There are 5 instances of this issue.

File: core/solidity/contracts/core/Vault.sol

250: 		    _rewards[0] = Reward(CRV, _crvReward);

251: 		    _rewards[1] = Reward(CVX, _cvxReward);

275: 		    _rewards[0].amount = _crvReward - _takenCRV;

276: 		    if (_cvxReward > 0) _rewards[1].amount = _cvxReward - _takenCVX;

[250, 251, 275, 276]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

43: 		    uint256 _minStable = anchoredUnderlyingTokens[0].peekValue();

[43]


[NC-11] Variable initialization with default value

It's not necessary to initialize a variable with its default value, and it's actually worse in gas terms as it adds an overhead.

There are 3 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

259: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {

313: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {

382: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {

[259, 313, 382]


[NC-12] Inconsistent usage of require/error

Some parts of the codebase use require statements, while others use custom errors. Consider refactoring the code to use the same approach: the following findings represent the minority of require vs error, and they show the first occurance in each file, for brevity.

There are 2 instances of this issue.

File: core/solidity/contracts/utils/UFragments.sol

52: 		    require(msg.sender == monetaryPolicy);

[52]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

77: 		    require(_mainValue > 0, 'invalid oracle value');

[77]


[NC-13] Unbounded loop may run out of gas

Consider limiting the number of iterations in loops with an explicit revert reason to improve the user experience.

The function would eventually revert if out of gas anyway, but by limiting it the user avoids wasting too much gas, as the loop doesn't execute if an excessive value is provided.

There are 14 instances of this issue.

Expand findings
File: core/solidity/contracts/core/Vault.sol

169: 		    for (uint256 _i; _i < _tokenAddresses.length;) {
170: 		      IVaultController.CollateralInfo memory _collateralInfo = CONTROLLER.tokenCollateralInfo(_tokenAddresses[_i]);
171: 		      if (_collateralInfo.tokenId == 0) revert Vault_TokenNotRegistered();
172: 		      if (_collateralInfo.collateralType != IVaultController.CollateralType.CurveLPStakedOnConvex) {
173: 		        revert Vault_TokenNotCurveLP();
174: 		      }
175: 		
176: 		      IBaseRewardPool _rewardsContract = _collateralInfo.crvRewardsContract;
177: 		      uint256 _crvReward = _rewardsContract.earned(address(this));
178: 		
179: 		      if (_crvReward != 0) {
180: 		        // Claim the CRV reward
181: 		        _totalCrvReward += _crvReward;
182: 		        _rewardsContract.getReward(address(this), false);
183: 		        _totalCvxReward += _calculateCVXReward(_crvReward);
184: 		      }
185: 		
186: 		      // Loop and claim all virtual rewards
187: 		      uint256 _extraRewards = _rewardsContract.extraRewardsLength();
188: 		      for (uint256 _j; _j < _extraRewards;) {
189: 		        IVirtualBalanceRewardPool _virtualReward = _rewardsContract.extraRewards(_j);
190: 		        IERC20 _rewardToken = _virtualReward.rewardToken();
191: 		        uint256 _earnedReward = _virtualReward.earned(address(this));
192: 		        if (_earnedReward != 0) {
193: 		          _virtualReward.getReward();
194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);
195: 		          emit ClaimedReward(address(_rewardToken), _earnedReward);
196: 		        }
197: 		        unchecked {
198: 		          ++_j;
199: 		        }
200: 		      }
201: 		      unchecked {
202: 		        ++_i;
203: 		      }
204: 		    }

188: 		      for (uint256 _j; _j < _extraRewards;) {
189: 		        IVirtualBalanceRewardPool _virtualReward = _rewardsContract.extraRewards(_j);
190: 		        IERC20 _rewardToken = _virtualReward.rewardToken();
191: 		        uint256 _earnedReward = _virtualReward.earned(address(this));
192: 		        if (_earnedReward != 0) {
193: 		          _virtualReward.getReward();
194: 		          _rewardToken.transfer(_msgSender(), _earnedReward);
195: 		          emit ClaimedReward(address(_rewardToken), _earnedReward);
196: 		        }
197: 		        unchecked {
198: 		          ++_j;
199: 		        }
200: 		      }

254: 		    for (_i; _i < _rewardsAmount;) {
255: 		      IVirtualBalanceRewardPool _virtualReward = _rewardsContract.extraRewards(_i);
256: 		      IERC20 _rewardToken = _virtualReward.rewardToken();
257: 		      uint256 _earnedReward = _virtualReward.earned(address(this));
258: 		      _rewards[_i + 2] = Reward(_rewardToken, _earnedReward);
259: 		
260: 		      unchecked {
261: 		        ++_i;
262: 		      }
263: 		    }

[169-204, 188-200, 254-263]

File: core/solidity/contracts/core/VaultController.sol

240: 		    for (uint256 _i = _start; _i < _end;) {
241: 		      _collateralsInfo[_i - _start] = tokenAddressCollateralInfo[enabledTokens[_i]];
242: 		
243: 		      unchecked {
244: 		        ++_i;
245: 		      }
246: 		    }

255: 		    for (uint256 _i; _i < _tokenAddresses.length;) {
256: 		      _tokenId = _oldVaultController.tokenId(_tokenAddresses[_i]);
257: 		      if (_tokenId == 0) revert VaultController_WrongCollateralAddress();
258: 		      _tokensRegistered++;
259: 		
260: 		      CollateralInfo memory _collateral = _oldVaultController.tokenCollateralInfo(_tokenAddresses[_i]);
261: 		      _collateral.tokenId = _tokensRegistered;
262: 		      _collateral.totalDeposited = 0;
263: 		
264: 		      enabledTokens.push(_tokenAddresses[_i]);
265: 		      tokenAddressCollateralInfo[_tokenAddresses[_i]] = _collateral;
266: 		
267: 		      unchecked {
268: 		        ++_i;
269: 		      }
270: 		    }

868: 		    for (uint192 _i; _i < enabledTokens.length; ++_i) {
869: 		      CollateralInfo memory _collateral = tokenAddressCollateralInfo[enabledTokens[_i]];
870: 		      // if the ltv is 0, continue
871: 		      if (_collateral.ltv == 0) continue;
872: 		      // get the address of the token through the array of enabled tokens
873: 		      // note that index 0 of enabledTokens corresponds to a vaultId of 1, so we must subtract 1 from i to get the correct index
874: 		      address _tokenAddress = enabledTokens[_i];
875: 		      // the balance is the vault's token balance of the current collateral token in the loop
876: 		      uint256 _balance = _vault.balances(_tokenAddress);
877: 		      if (_balance == 0) continue;
878: 		      // the raw price is simply the oracle price of the token
879: 		      uint192 _rawPrice = _safeu192(_collateral.oracle.currentValue());
880: 		      if (_rawPrice == 0) continue;
881: 		      // the token value is equal to the price * balance * tokenLTV
882: 		      uint192 _tokenValue = _safeu192(
883: 		        _truncate(_truncate(_balance * _collateral.ltv * _getPriceWithDecimals(_rawPrice, _collateral.decimals)))
884: 		      );
885: 		      // increase the ltv of the vault by the token value
886: 		      _borrowPower += _tokenValue;
887: 		    }

896: 		    for (uint192 _i; _i < enabledTokens.length; ++_i) {
897: 		      CollateralInfo memory _collateral = tokenAddressCollateralInfo[enabledTokens[_i]];
898: 		      // if the ltv is 0, continue
899: 		      if (_collateral.ltv == 0) continue;
900: 		      // get the address of the token through the array of enabled tokens
901: 		      // note that index 0 of enabledTokens corresponds to a vaultId of 1, so we must subtract 1 from i to get the correct index
902: 		      address _tokenAddress = enabledTokens[_i];
903: 		      // the balance is the vault's token balance of the current collateral token in the loop
904: 		      uint256 _balance = _vault.balances(_tokenAddress);
905: 		      if (_balance == 0) continue;
906: 		      // the raw price is simply the oracle price of the token
907: 		      uint192 _rawPrice = _safeu192(_collateral.oracle.peekValue());
908: 		      if (_rawPrice == 0) continue;
909: 		      // the token value is equal to the price * balance * tokenLTV
910: 		      uint192 _tokenValue = _safeu192(
911: 		        _truncate(_truncate(_balance * _collateral.ltv * _getPriceWithDecimals(_rawPrice, _collateral.decimals)))
912: 		      );
913: 		      // increase the ltv of the vault by the token value
914: 		      _borrowPower += _tokenValue;
915: 		    }

994: 		    for (uint96 _i = _start; _i <= _stop;) {
995: 		      IVault _vault = _getVault(_i);
996: 		      uint256[] memory _tokenBalances = new uint256[](enabledTokens.length);
997: 		
998: 		      for (uint256 _j; _j < enabledTokens.length;) {
999: 		        _tokenBalances[_j] = _vault.balances(enabledTokens[_j]);
1000: 		
1001: 		        unchecked {
1002: 		          ++_j;
1003: 		        }
1004: 		      }
1005: 		      _vaultSummaries[_i - _start] =
1006: 		        VaultSummary(_i, _peekVaultBorrowingPower(_vault), this.vaultLiability(_i), enabledTokens, _tokenBalances);
1007: 		
1008: 		      unchecked {
1009: 		        ++_i;
1010: 		      }
1011: 		    }

998: 		      for (uint256 _j; _j < enabledTokens.length;) {
999: 		        _tokenBalances[_j] = _vault.balances(enabledTokens[_j]);
1000: 		
1001: 		        unchecked {
1002: 		          ++_j;
1003: 		        }
1004: 		      }

[240-246, 255-270, 868-887, 896-915, 994-1011, 998-1004]

File: core/solidity/contracts/governance/GovernorCharlie.sol

259: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {
260: 		      if (
261: 		        queuedTransactions[keccak256(
262: 		          abi.encode(
263: 		            _proposal.targets[_i], _proposal.values[_i], _proposal.signatures[_i], _proposal.calldatas[_i], _eta
264: 		          )
265: 		        )]
266: 		      ) revert GovernorCharlie_ProposalAlreadyQueued();
267: 		      _queueTransaction(
268: 		        _proposal.targets[_i],
269: 		        _proposal.values[_i],
270: 		        _proposal.signatures[_i],
271: 		        _proposal.calldatas[_i],
272: 		        _eta,
273: 		        _proposal.delay
274: 		      );
275: 		    }

313: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {
314: 		      this.executeTransaction{value: _proposal.values[_i]}(
315: 		        _proposal.targets[_i], _proposal.values[_i], _proposal.signatures[_i], _proposal.calldatas[_i], _proposal.eta
316: 		      );
317: 		    }

382: 		    for (uint256 _i = 0; _i < _proposal.targets.length; _i++) {
383: 		      _cancelTransaction(
384: 		        _proposal.targets[_i], _proposal.values[_i], _proposal.signatures[_i], _proposal.calldatas[_i], _proposal.eta
385: 		      );
386: 		    }

[259-275, 313-317, 382-386]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

22: 		    for (uint256 _i; _i < _anchoredUnderlyingTokens.length;) {
23: 		      anchoredUnderlyingTokens.push(_anchoredUnderlyingTokens[_i]);
24: 		
25: 		      unchecked {
26: 		        ++_i;
27: 		      }
28: 		    }

44: 		    for (uint256 _i = 1; _i < anchoredUnderlyingTokens.length;) {
45: 		      _minStable = Math.min(_minStable, anchoredUnderlyingTokens[_i].peekValue());
46: 		      unchecked {
47: 		        ++_i;
48: 		      }
49: 		    }

[22-28, 44-49]


[NC-14] Public functions not called internally

Those functions should be declared as external instead of public, as they are never called internally by the contract.

Contracts are allowed to override their parents' functions and change the visibility from external to public.

There are 4 instances of this issue.

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

20: 		  function mint(address _dst, uint256 _rawAmount) public onlyOwner {

[20]

File: core/solidity/contracts/utils/UFragments.sol

153: 		  function nonces(address _who) public view returns (uint256 _addressNonces) {

283: 		  function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool _success) {

315: 		  function permit(

[153, 283, 315]


[NC-15] Large multiples of ten should use scientific notation

Use a scientific notation rather than decimal literals (e.g. 1e6 instead of 1000000), for better code readability.

There are 3 instances of this issue.

File: core/solidity/contracts/core/WUSDA.sol

// @audit 10_000_000 -> 1e7
35: 		  uint256 public constant MAX_wUSDA_SUPPLY = 10_000_000 * (10 ** 18); // 10 M

[35]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit 1_000_000_000_000_000_000_000_000 -> 1e24
92: 		    proposalThreshold = 1_000_000_000_000_000_000_000_000;

// @audit 10_000_000_000_000_000_000_000_000 -> 1e25
95: 		    quorumVotes = 10_000_000_000_000_000_000_000_000;

[92, 95]


[NC-16] Use of exponentiation instead of scientific notation

Use a scientific notation rather than exponentiation (e.g. 1e18 instead of 10**18): although the compiler is capable of optimizing it, it is considered good coding practice to utilize idioms that don't rely on compiler optimization, whenever possible.

There are 2 instances of this issue.

File: core/solidity/contracts/core/WUSDA.sol

35: 		  uint256 public constant MAX_wUSDA_SUPPLY = 10_000_000 * (10 ** 18); // 10 M

[35]

File: core/solidity/contracts/utils/UFragments.sol

100: 		    _totalGons = INITIAL_FRAGMENTS_SUPPLY * 10 ** 48;

[100]


[NC-17] Use EIP-5627 to describe EIP-712 domains

EIP-5267 is a standard which allows for the retrieval and description of EIP-712 hash domains. This enable external tools to allow users to view the fields and values that describe their domain.

This is especially useful when a project may exist on multiple chains and or in multiple contracts, and allows users/tools to verify that the signature is for the right fork, chain, version, contract, etc.

There are 2 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

20: 		    keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

[20]

File: core/solidity/contracts/utils/UFragments.sol

88: 		    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');

[88]


[NC-18] Unused state variables

Consider removing any unusued state variable to improve the readability of the codebase.

There are 2 instances of this issue.

File: core/solidity/contracts/utils/UFragments.sol

62: 		  uint256 private constant MAX_UINT256 = 2 ** 256 - 1;

[62]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

8: 		  address public constant wETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

[8]


[NC-19] 2**<n> - 1 should be re-written as type(uint<n>).max

Earlier versions of solidity can use uint<n>(-1) instead. Expressions not including the - 1 can often be re-written to accomodate the change (e.g. by using a > rather than a >=, which will also save some gas).

There are 5 instances of this issue.

File: core/solidity/contracts/core/VaultController.sol

712: 		    ) = _peekLiquidationMath(_id, _assetAddress, 2 ** 256 - 1);

[712]

File: core/solidity/contracts/utils/UFragments.sol

62: 		  uint256 private constant MAX_UINT256 = 2 ** 256 - 1;

62: 		  uint256 private constant MAX_UINT256 = 2 ** 256 - 1;

100: 		    _totalGons = INITIAL_FRAGMENTS_SUPPLY * 10 ** 48;

101: 		    MAX_SUPPLY = 2 ** 128 - 1;

[62, 62, 100, 101]


[NC-20] Use of non-named numeric constants

Constants should be defined instead of using magic numbers.

There are 40 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

18: 		  uint256 internal constant _FIFTY_MILLIONS = 50_000_000 * 1e6;

21: 		  uint256 internal constant _TWENTY_FIVE_THOUSANDS = 25_000 * 1e6;

24: 		  uint256 internal constant _FIFTY = 50 * 1e6;

27: 		  uint256 public constant BASE_SUPPLY_PER_CLIFF = 8_000_000 * 1e6;

88: 		    if (_crvAmountToSend != 0 && _claimedAmph != 0) distributedAmph += (_claimedAmph / 1e12); // scale back to 1e6

177: 		    if (_getCliff((_amphByCrv / 1e12) + distributedAmph) >= TOTAL_CLIFFS) return (0, 0, 0);

196: 		    uint256 _bar = (_distributedAmph * 1e12) / (BASE_SUPPLY_PER_CLIFF * _FIFTY);

198: 		    _rate = 1e6 + (_foo - _bar);

222: 		      _amphForThisTurn = ((_rate * _tempAmountReceived) / 1e12) / 1e6; // 1e6

232: 		        uint256 _amountTokenToEnter = (_amphAvailableForThisCliff * 1e18) / _rate;

241: 		      _amphToMint += (_amphForThisTurn * 1e12); // 1e18

[18, 21, 24, 27, 88, 177, 196, 198, 222, 232, 241]

File: core/solidity/contracts/core/Vault.sol

249: 		    _rewards = new Reward[](_rewardsAmount+3);

[249]

File: core/solidity/contracts/core/VaultController.sol

320: 		    if (_newProtocolFee >= 1e18) revert VaultController_FeeTooLarge();

436: 		    if (_newLiquidationFee >= 1e18) revert VaultController_FeeTooLarge();

501: 		    _fee = _safeu192(_truncate(uint256(_amount * (1e18 + initialBorrowingFee)))) - _amount;

515: 		      _safeu192(_truncate(uint256(_tokensToLiquidate * (1e18 + _liquidationIncentive)))) - _tokensToLiquidate;

518: 		      _safeu192(_truncate(uint256(_liquidatorExpectedProfit * (1e18 + liquidationFee)))) - _liquidatorExpectedProfit;

667: 		    _vault.modifyLiability(false, (_usdaToRepurchase * 1e18) / interest.factor);

670: 		    totalBaseLiability -= _safeu192((_usdaToRepurchase * 1e18) / interest.factor);

780: 		    _badFillPrice = _truncate(_priceWithDecimals * (1e18 - _collateral.liquidationIncentive));

790: 		    uint256 _maxTokensToLiquidate = (_usdaToSolvency * 1e18) / _denominator;

948: 		        _truncate((uint256(_timeDifference) * uint256(1e18) * uint256(_curveVal)) / (365 days + 6 hours))

1040: 		    _priceWithDecimals = _price * 10 ** (18 - _decimals);

[320, 436, 501, 515, 518, 667, 670, 780, 790, 948, 1040]

File: core/solidity/contracts/core/WUSDA.sol

35: 		  uint256 public constant MAX_wUSDA_SUPPLY = 10_000_000 * (10 ** 18); // 10 M

[35]

File: core/solidity/contracts/governance/GovernorCharlie.sol

514: 		    if (uint256(_s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {

[514]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

35: 		    if (!((0 < _s1) && (_s1 < _s2) && (_s2 < 1e18))) revert ThreeLines0_100_InvalidBreakpointValues();

51: 		    if (_xValue > 1e18) _xValue = 1e18;

83: 		    int256 _mE6 = (_rise * 1e6) / _run;

86: 		    _result = (_mE6 * _distance) / 1e6 + _b;

[35, 51, 83, 86]

File: core/solidity/contracts/utils/UFragments.sol

63: 		  uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 1 * 10 ** DECIMALS;

100: 		    _totalGons = INITIAL_FRAGMENTS_SUPPLY * 10 ** 48;

330: 		    if (uint256(_s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {

[63, 100, 330]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

28: 		    div = 10 ** (18 + _underlyingDecimals - cToken.decimals());

[28]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

51: 		    _value = (_aggregatorPrice * _baseAggregatorPrice) / 1e18;

[51]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

53: 		    _value = _lpPrice / 1e18;

[53]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

61: 		    _maxPrice = (3 * _vp * FixedPointMathLib.cbrt(_basePrices)) / 1 ether;

[61]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

75: 		    uint256 _uniswapPrice = _getPriceFromUniswap(_seconds, uint128(10 ** BASE_TOKEN_DECIMALS));

85: 		    _e18Amount = (_decimals > 18) ? _amount / (10 ** (_decimals - 18)) : _amount * (10 ** (18 - _decimals));

[75, 85]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

37: 		    _price = (_ethPrice * _priceInEth) / 1e18;

[37]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

32: 		    _value = (_stETHPrice * _stETHPerWstEth) / 1e18;

[32]


[NC-21] Some functions don't have any logic

Some functions don't have a body, consider commenting why, or refactor the code and remove these functions.

There is 1 instance of this issue.

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

54: 		  fallback() external {}

[54]


[NC-22] Non-assembly method available

There are some automated tools that will flag a project as having higher complexity if there is inline-assembly, so it's better to avoid using assembly if it's not needed.

Consider using the following alternatives:

  • id := chainid() -> uint256 id = block.chainid

  • size := extcodesize() -> uint256 size = address().code.length

  • hash := extcodehash() -> bytes32 hash = address().codehash

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

166: 		      _chainId := chainid()

[166]


[NC-23] Some contract names don't follow the Solidity naming conventions

Follow the Solidity naming convention by naming all the contracts in CamelCase.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/ThreeLines0_100.sol

11: 		contract ThreeLines0_100 is ICurveSlave {

[11]


[NC-24] Some functions don't follow the Solidity naming conventions

Follow the Solidity naming convention by adding an underscore before the function name, for any private and internal functions.

There is 1 instance of this issue.

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

11: 		  function getCurrentPrice(AggregatorV2V3Interface _aggregator) internal view returns (uint256 _price) {

[11]


[NC-25] Variable names don't follow the Solidity naming convention

Use mixedCase for local and state variables that are not constants, and add a trailing underscore for internal variables. Documentation

There are 18 instances of this issue.

Expand findings
File: core/solidity/contracts/core/VaultController.sol

34: 		  IVaultDeployer public immutable VAULT_DEPLOYER;

[34]

File: core/solidity/contracts/utils/UFragments.sol

70: 		  uint256 public MAX_SUPPLY; // = type(uint128).max; // (2^128) - 1

[70]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

13: 		  IV2Pool public immutable CB_ETH_POOL;

16: 		  IOracleRelay public immutable CB_ETH_ORACLE_RELAY;

19: 		  IOracleRelay public immutable ETH_ORACLE_RELAY;

[13, 16, 19]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

15: 		  ChainlinkOracleRelay public immutable BASE_AGGREGATOR;

[15]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

15: 		  IStablePool public immutable CRV_POOL;

[15]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

15: 		  IV2Pool public immutable TRI_CRYPTO;

18: 		  IOracleRelay public immutable WBTC_ORACLE_RELAY;

21: 		  IOracleRelay public immutable ETH_ORACLE_RELAY;

24: 		  IOracleRelay public immutable USDT_ORACLE_RELAY;

[15, 18, 21, 24]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

17: 		  bool public immutable QUOTE_TOKEN_IS_TOKEN0;

26: 		  uint8 public immutable BASE_TOKEN_DECIMALS;

29: 		  uint8 public immutable QUOTE_TOKEN_DECIMALS;

32: 		  address public immutable BASE_TOKEN;

35: 		  address public immutable QUOTE_TOKEN;

[17, 26, 29, 32, 35]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

12: 		  UniswapV3OracleRelay public immutable ETH_ORACLE;

[12]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

12: 		  IOracleRelay public immutable STETH_ANCHORED_VIEW_UNDERLYING;

[12]


[NC-26] Constant names should be UPPERCASE

Constants should be named with all capital letters with underscores separating words. Examples: MAX_BLOCKS, TOKEN_NAME. Documentation

There are 4 instances of this issue.

File: core/solidity/contracts/core/WUSDA.sol

35: 		  uint256 public constant MAX_wUSDA_SUPPLY = 10_000_000 * (10 ** 18); // 10 M

[35]

File: core/solidity/contracts/utils/UFragments.sol

78: 		  uint8 public constant decimals = uint8(DECIMALS);

[78]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

12: 		  address public constant cETH_ADDRESS = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;

[12]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

8: 		  address public constant wETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

[8]


[NC-27] Don't use uppercase for non constant/immutable variables

Variables which are not constants or immutable should not be declared in all uppercase.

There are 2 instances of this issue.

File: core/solidity/contracts/utils/UFragments.sol

70: 		  uint256 public MAX_SUPPLY; // = type(uint128).max; // (2^128) - 1

[70]

File: core/solidity/interfaces/utils/ICurvePool.sol

10: 		  function A() external view returns (uint256 _A);

[10]


[NC-28] Use a single file for system wide constants

Consider grouping all the system constants under a single file. This finding shows only the first constant for each file, for brevity.

There are 10 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

15: 		  uint256 internal constant _BASE = 1 ether;

[15]

File: core/solidity/contracts/core/USDA.sol

21: 		  bytes32 public constant VAULT_CONTROLLER_ROLE = keccak256('VAULT_CONTROLLER');

[21]

File: core/solidity/contracts/core/VaultController.sol

25: 		  uint8 public constant MAX_DECIMALS = 18;

[25]

File: core/solidity/contracts/core/WUSDA.sol

35: 		  uint256 public constant MAX_wUSDA_SUPPLY = 10_000_000 * (10 ** 18); // 10 M

[35]

File: core/solidity/contracts/governance/GovernorCharlie.sol

13: 		  string public constant NAME = 'Amphora Protocol Governor';

[13]

File: core/solidity/contracts/utils/UFragments.sol

61: 		  uint256 private constant DECIMALS = 18;

[61]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

12: 		  address public constant cETH_ADDRESS = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;

[12]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

8: 		  ICurveAddressesProvider public constant CURVE_ADDRESSES_PROVIDER =
9: 		    ICurveAddressesProvider(0x0000000022D53366457F9d5E68Ec105046FC4383);

[8-9]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

8: 		  address public constant wETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

[8]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

10: 		  IWStETH public constant WSTETH = IWStETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);

[10]


[NC-29] Use named mappings to improve code readability

Since solidity 0.8.18 or later it's possible to use named mappings to make it easier to understand the purpose of each mapping. Documentation

There are 16 instances of this issue.

Expand findings
File: core/solidity/contracts/core/Vault.sol

43: 		  mapping(address => uint256) public balances;

46: 		  mapping(address => bool) public isTokenStaked;

[43, 46]

File: core/solidity/contracts/core/VaultController.sol

37: 		  mapping(uint96 => address) public vaultIdVaultAddress;

40: 		  mapping(address => uint96[]) public walletVaultIDs;

43: 		  mapping(address => CollateralInfo) public tokenAddressCollateralInfo;

[37, 40, 43]

File: core/solidity/contracts/governance/GovernorCharlie.sol

53: 		  mapping(uint256 => Proposal) public proposals;

56: 		  mapping(address => uint256) public latestProposalIds;

59: 		  mapping(bytes32 => bool) public queuedTransactions;

65: 		  mapping(address => uint256) public whitelistAccountExpirations;

77: 		  mapping(uint256 => mapping(address => Receipt)) public proposalReceipts;

// @audit mapping(address => Receipt)
77: 		  mapping(uint256 => mapping(address => Receipt)) public proposalReceipts;

[53, 56, 59, 65, 77, 77]

File: core/solidity/contracts/periphery/CurveMaster.sol

13: 		  mapping(address => address) public curves;

[13]

File: core/solidity/contracts/utils/UFragments.sol

74: 		  mapping(address => uint256) public _gonBalances;

82: 		  mapping(address => mapping(address => uint256)) private _allowedFragments;

// @audit mapping(address => uint256)
82: 		  mapping(address => mapping(address => uint256)) private _allowedFragments;

93: 		  mapping(address => uint256) private _nonces;

[74, 82, 82, 93]


[NC-30] Some events are never emitted

The following events are never emitted, consider to remove them.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

39: 		  event LogRebase(uint256 indexed epoch, uint256 totalSupply);

[39]


[NC-31] Event does not have proper indexed fields

Index event fields make the field more quickly accessible to off-chain tools that parse events. 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).

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 fields, all of the fields should be indexed.

There are 27 instances of this issue.

Expand findings
File: core/solidity/contracts/utils/UFragments.sol

40: 		  event LogMonetaryPolicyUpdated(address monetaryPolicy);

[40]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

42: 		  event ChangedCvxRewardFee(uint256 _newCvxReward);

48: 		  event ChangedCrvRewardFee(uint256 _newCrvReward);

[42, 48]

File: core/solidity/interfaces/core/IUSDA.sol

33: 		  event Mint(address _to, uint256 _value);

40: 		  event Burn(address _from, uint256 _value);

68: 		  event VaultControllerTransfer(address _target, uint256 _susdAmount);

[33, 40, 68]

File: core/solidity/interfaces/core/IVault.sol

20: 		  event Deposit(address _token, uint256 _amount);

27: 		  event Withdraw(address _token, uint256 _amount);

34: 		  event ClaimedReward(address _token, uint256 _amount);

41: 		  event Staked(address _token, uint256 _amount);

[20, 27, 34, 41]

File: core/solidity/interfaces/core/IVaultController.sol

23: 		  event InterestEvent(uint64 _epoch, uint192 _amount, uint256 _curveVal);

29: 		  event NewProtocolFee(uint192 _protocolFee);

39: 		  event RegisteredErc20(
40: 		    address _tokenAddress, uint256 _ltv, address _oracleAddress, uint256 _liquidationIncentive, uint256 _cap
41: 		  );

52: 		  event UpdateRegisteredErc20(
53: 		    address _tokenAddress,
54: 		    uint256 _ltv,
55: 		    address _oracleAddress,
56: 		    uint256 _liquidationIncentive,
57: 		    uint256 _cap,
58: 		    uint256 _poolId
59: 		  );

67: 		  event NewVault(address _vaultAddress, uint256 _vaultId, address _vaultOwner);

73: 		  event RegisterCurveMaster(address _curveMasterAddress);

81: 		  event BorrowUSDA(uint256 _vaultId, address _vaultAddress, uint256 _borrowAmount, uint256 _fee);

89: 		  event RepayUSDA(uint256 _vaultId, address _vaultAddress, uint256 _repayAmount);

99: 		  event Liquidate(
100: 		    uint256 _vaultId,
101: 		    address _assetAddress,
102: 		    uint256 _usdaToRepurchase,
103: 		    uint256 _tokensToLiquidate,
104: 		    uint256 _liquidationFee
105: 		  );

112: 		  event ChangedClaimerContract(IAMPHClaimer _oldClaimerContract, IAMPHClaimer _newClaimerContract);

118: 		  event RegisterUSDA(address _usdaContractAddress);

125: 		  event ChangedInitialBorrowingFee(uint192 _oldBorrowingFee, uint192 _newBorrowingFee);

132: 		  event ChangedLiquidationFee(uint192 _oldLiquidationFee, uint192 _newLiquidationFee);

139: 		  event CollateralsMigratedFrom(IVaultController _oldVaultController, address[] _tokenAddresses);

[23, 29, 39-41, 52-59, 67, 73, 81, 89, 99-105, 112, 118, 125, 132, 139]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

16: 		  event VaultControllerSet(address _oldVaultControllerAddress, address _newVaultControllerAddress);

24: 		  event CurveSet(address _oldCurveAddress, address _token, address _newCurveAddress);

32: 		  event CurveForceSet(address _oldCurveAddress, address _token, address _newCurveAddress);

[16, 24, 32]


[NC-32] Inconsistent method of specifying a floating pragma

Some files use >=, while others use ^. The instances below are examples of the method that has the fewest instances for a specific version.

Occurrences: ^: 43 >=: 6

There are 6 instances of this issue.

File: core/solidity/interfaces/core/IVaultDeployer.sol

2: 		pragma solidity >=0.8.4 <0.9.0;

[2]

File: core/solidity/interfaces/utils/ICVX.sol

2: 		pragma solidity >=0.8.4 <0.9.0;

[2]

File: core/solidity/interfaces/utils/IRoles.sol

2: 		pragma solidity >=0.8.4 <0.9.0;

[2]

File: core/solidity/interfaces/utils/IWStETH.sol

2: 		pragma solidity >=0.8.4 <0.9.0;

[2]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

2: 		pragma solidity >=0.5.0 <0.9.0;

[2]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

2: 		pragma solidity >=0.5.0 <0.9.0;

[2]


[NC-33] experimental pragmas may be dangerous/deprecated

Consider using a normal Solidity version instead of an experimental one.

There are 2 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

4: 		pragma experimental ABIEncoderV2;

[4]

File: core/solidity/interfaces/governance/IAmphoraProtocolToken.sol

3: 		pragma experimental ABIEncoderV2;

[3]


[NC-34] Old Solidity version

Use a more recent version of Solidity: the latest version is 0.8.20, so it's safer to use at least the version 0.8.19 to get the latest bugfixes and features.

There are 43 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/USDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/Vault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/VaultDeployer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/core/WUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/governance/GovernorCharlie.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/contracts/periphery/CurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/GovernanceStructs.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/utils/UFragments.sol

3: 		pragma solidity ^0.8.9;

[3]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVault.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IVaultController.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/core/IWUSDA.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAMPH.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IAmphoraProtocolToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IAnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICToken.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveAddressesProvider.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IBooster.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurvePool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/ICurveSlave.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

2: 		pragma solidity ^0.8.9;

[2]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

2: 		pragma solidity ^0.8.9;

[2]


[NC-35] Zero as a function argument should have a descriptive meaning

Consider using descriptive constants or an enum instead of passing zero directly on function calls, as that might be error-prone, to fully describe the caller's intention.

There are 3 instances of this issue.

File: core/solidity/contracts/core/VaultController.sol

576: 		    _repay(_id, 0, true);

[576]

File: core/solidity/contracts/governance/GovernorCharlie.sol

187: 		    Proposal memory _newProposal = Proposal({
188: 		      id: proposalCount,
189: 		      proposer: msg.sender,
190: 		      eta: 0,
191: 		      targets: _targets,
192: 		      values: _values,
193: 		      signatures: _signatures,
194: 		      calldatas: _calldatas,
195: 		      startBlock: block.number + votingDelay,
196: 		      endBlock: block.number + votingDelay + votingPeriod,
197: 		      forVotes: 0,
198: 		      againstVotes: 0,
199: 		      abstainVotes: 0,
200: 		      canceled: false,
201: 		      executed: false,
202: 		      emergency: _emergency,
203: 		      quorumVotes: quorumVotes,
204: 		      delay: proposalTimelockDelay
205: 		    });

[187-205]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

41: 		    CRV_POOL.remove_liquidity(0, _amounts);

[41]


[NC-36] Use of abi.encodePacked instead of bytes.concat

Starting from version 0.8.4, the recommended approach for appending bytes is to use bytes.concat instead of abi.encodePacked.

There are 3 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

347: 		    else _callData = abi.encodePacked(bytes4(keccak256(bytes(_signature))), _data);

512: 		    bytes32 _digest = keccak256(abi.encodePacked('\x19\x01', _domainSeparator, _structHash));

[347, 512]

File: core/solidity/contracts/utils/UFragments.sol

328: 		    bytes32 _digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR(), _permitDataDigest));

[328]


[NC-37] Use of constant variables instead of immutable

Constant variables and immutable variables serve different purposes and should be used accordingly.

To clarify, constant variables are used for literal values in the code, whereas immutable variables are ideal for values calculated or passed into the constructor.

There are 5 instances of this issue.

File: core/solidity/contracts/core/USDA.sol

21: 		  bytes32 public constant VAULT_CONTROLLER_ROLE = keccak256('VAULT_CONTROLLER');

[21]

File: core/solidity/contracts/governance/GovernorCharlie.sol

19: 		  bytes32 public constant DOMAIN_TYPEHASH =
20: 		    keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

23: 		  bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');

[19-20, 23]

File: core/solidity/contracts/utils/UFragments.sol

87: 		  bytes32 public constant EIP712_DOMAIN =
88: 		    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');

89: 		  bytes32 public constant PERMIT_TYPEHASH =
90: 		    keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');

[87-88, 89-90]


[NC-38] require/revert without any message

If a transaction reverts, it can be confusing to debug if there aren't any messages. Add a descriptive message to all require/revert statements.

There are 3 instances of this issue.

File: core/solidity/contracts/utils/UFragments.sol

52: 		    require(msg.sender == monetaryPolicy);

324: 		    require(block.timestamp <= _deadline);

333: 		    require(_owner == ecrecover(_digest, _v, _r, _s));

[52, 324, 333]


[NC-39] Consider activating via-ir for deploying

The IR-based code generator was developed to make code generation more performant by enabling optimization passes that can be applied across functions.

It is possible to activate the IR-based code generator through the command line by using the flag --via-ir or by including the option {"viaIR": true}.

Keep in mind that compiling with this option may take longer. However, you can simply test it before deploying your code. If you find that it provides better performance, you can add the --via-ir flag to your deploy command.


[NC-40] Imports should be organized more systematically

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 are 14 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

5: 		import {IAMPHClaimer} from '@interfaces/core/IAMPHClaimer.sol';

[5]

File: core/solidity/contracts/core/USDA.sol

8: 		import {IUSDA} from '@interfaces/core/IUSDA.sol';

[8]

File: core/solidity/contracts/core/Vault.sol

5: 		import {IVault} from '@interfaces/core/IVault.sol';

[5]

File: core/solidity/contracts/core/VaultController.sol

9: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

[9]

File: core/solidity/contracts/core/VaultDeployer.sol

5: 		import {IVaultDeployer} from '@interfaces/core/IVaultDeployer.sol';

[5]

File: core/solidity/contracts/core/WUSDA.sol

7: 		import {ERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

[7]

File: core/solidity/contracts/governance/GovernorCharlie.sol

7: 		import {IGovernorCharlie} from '@interfaces/governance/IGovernorCharlie.sol';

[7]

File: core/solidity/contracts/periphery/CurveMaster.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[6]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

8: 		import {CurveRegistryUtils} from '@contracts/periphery/oracles/CurveRegistryUtils.sol';

[8]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[7]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

5: 		import {StableCurveLpOracle} from '@contracts/periphery/oracles/StableCurveLpOracle.sol';

[5]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

7: 		import {CurveRegistryUtils} from '@contracts/periphery/oracles/CurveRegistryUtils.sol';

[7]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

7: 		import {CurveRegistryUtils} from '@contracts/periphery/oracles/CurveRegistryUtils.sol';

[7]


[NC-41] Event is missing msg.sender parameter

The following functions are missing some parameters when emitting an event: when dealing with a source address which uses the value of msg.sender, the msg.sender value should be specified in every event.

Otherwise, a contract or web page listening to events cannot react to connected users: basically, those events cannot be used properly.

There are 5 instances of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

353: 		    emit ExecuteTransaction(_txHash, _target, _value, _signature, _data, _eta);

388: 		    emit ProposalCanceledIndexed(_proposalId);

389: 		    emit ProposalCanceled(_proposalId);

[353, 388, 389]

File: core/solidity/contracts/utils/UFragments.sol

233: 		    emit Transfer(_from, _to, _value);

252: 		    emit Transfer(_from, _to, _value);

[233, 252]


[NC-42] Parameter omission in events

Events are generally emitted when sensitive changes are made to the contracts.

However, some are missing important parameters, as they should include both the new value and old value where possible.

There are 13 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

122: 		    emit ChangedVaultController(_newVaultController);

139: 		    emit ChangedCvxRewardFee(_newFee);

147: 		    emit ChangedCrvRewardFee(_newFee);

[122, 139, 147]

File: core/solidity/contracts/core/USDA.sol

66: 		    emit PauserSet(_pauser);

[66]

File: core/solidity/contracts/core/VaultController.sol

322: 		    emit NewProtocolFee(_newProtocolFee);

411: 		    emit UpdateRegisteredErc20(_tokenAddress, _ltv, _oracleAddress, _liquidationIncentive, _cap, _poolId);

420: 		    emit ChangedClaimerContract(_oldClaimerContract, _newClaimerContract);

430: 		    emit ChangedInitialBorrowingFee(_oldBorrowingFee, _newBorrowingFee);

440: 		    emit ChangedLiquidationFee(_oldLiquidationFee, _newLiquidationFee);

[322, 411, 420, 430, 440]

File: core/solidity/contracts/governance/GovernorCharlie.sol

677: 		    emit WhitelistAccountExpirationSet(_account, _expiration);

[677]

File: core/solidity/contracts/periphery/CurveMaster.sol

35: 		    emit VaultControllerSet(_oldCurveAddress, _vaultMasterAddress);

46: 		    emit CurveSet(_oldCurve, _tokenAddress, _curveAddress);

[35, 46]

File: core/solidity/contracts/utils/UFragments.sol

113: 		    emit LogMonetaryPolicyUpdated(_monetaryPolicy);

[113]


[NC-43] Events may be emitted out of order due to reentrancy

If a reentrancy occurs, some events may be emitted in an unexpected order, and this may be a problem if a third party expects a specific order for these events. Ensure that events follow the best practice of CEI.

There are 72 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

// @audit CVX.safeTransferFrom(msg.sender, owner(), _cvxAmountToSend)
96: 		    emit ClaimedAmph(msg.sender, _cvxAmountToSend, _crvAmountToSend, _claimedAmph);

// @audit CVX.safeTransferFrom(msg.sender, owner(), _cvxAmountToSend)
122: 		    emit ChangedVaultController(_newVaultController);

// @audit CVX.safeTransferFrom(msg.sender, owner(), _cvxAmountToSend)
131: 		    emit RecoveredDust(_token, owner(), _amount);

// @audit CVX.safeTransferFrom(msg.sender, owner(), _cvxAmountToSend)
139: 		    emit ChangedCvxRewardFee(_newFee);

// @audit CVX.safeTransferFrom(msg.sender, owner(), _cvxAmountToSend)
147: 		    emit ChangedCrvRewardFee(_newFee);

[96, 122, 131, 139, 147]

File: core/solidity/contracts/core/USDA.sol

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
66: 		    emit PauserSet(_pauser);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
108: 		    emit Deposit(_target, _susdAmount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
156: 		    emit Withdraw(_target, _susdAmount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
176: 		    emit Transfer(address(0), _target, _amount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
177: 		    emit Mint(_target, _amount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
196: 		    emit Transfer(_target, address(0), _amount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
197: 		    emit Burn(_target, _amount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
218: 		    emit RecoveredDust(owner(), _amount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
248: 		    emit VaultControllerTransfer(_target, _susdAmount);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
263: 		    emit Donation(_msgSender(), _amount, _totalSupply);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
282: 		    emit VaultControllerAdded(_vaultController);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
291: 		    emit VaultControllerRemoved(_vaultController);

// @audit IVaultController(_vaultControllers.at(_i)).calculateInterest()
300: 		    emit VaultControllerRemovedFromList(_vaultController);

[66, 108, 156, 176, 177, 196, 197, 218, 248, 263, 282, 291, 300]

File: core/solidity/contracts/core/Vault.sol

// @audit CONTROLLER.tokenId(_token)
108: 		    emit Deposit(_token, _amount);

// @audit CONTROLLER.tokenId(_token)
132: 		    emit Withdraw(_tokenAddress, _amount);

// @audit CONTROLLER.tokenId(_token)
150: 		    emit Staked(_tokenAddress, balances[_tokenAddress]);

// @audit CONTROLLER.tokenId(_token)
195: 		          emit ClaimedReward(address(_rewardToken), _earnedReward);

// @audit CONTROLLER.tokenId(_token)
226: 		      emit ClaimedReward(address(CRV), _totalCrvReward);

// @audit CONTROLLER.tokenId(_token)
227: 		      emit ClaimedReward(address(CVX), _totalCvxReward);

[108, 132, 150, 195, 226, 227]

File: core/solidity/contracts/core/VaultController.sol

// @audit usda.pauser()
273: 		    emit CollateralsMigratedFrom(_oldVaultController, _tokenAddresses);

// @audit usda.pauser()
290: 		    emit NewVault(_vaultAddress, vaultsMinted, _msgSender());

// @audit usda.pauser()
307: 		    emit RegisterUSDA(_usdaAddress);

// @audit usda.pauser()
314: 		    emit RegisterCurveMaster(_masterCurveAddress);

// @audit usda.pauser()
322: 		    emit NewProtocolFee(_newProtocolFee);

// @audit usda.pauser()
373: 		    emit RegisteredErc20(_tokenAddress, _ltv, _oracleAddress, _liquidationIncentive, _cap);

// @audit usda.pauser()
411: 		    emit UpdateRegisteredErc20(_tokenAddress, _ltv, _oracleAddress, _liquidationIncentive, _cap, _poolId);

// @audit usda.pauser()
420: 		    emit ChangedClaimerContract(_oldClaimerContract, _newClaimerContract);

// @audit usda.pauser()
430: 		    emit ChangedInitialBorrowingFee(_oldBorrowingFee, _newBorrowingFee);

// @audit usda.pauser()
440: 		    emit ChangedLiquidationFee(_oldLiquidationFee, _newLiquidationFee);

// @audit usda.pauser()
561: 		    emit BorrowUSDA(_id, address(_vault), _amount, _fee);

// @audit usda.pauser()
609: 		    emit RepayUSDA(_id, address(_vault), _amountInUSDA);

// @audit usda.pauser()
694: 		    emit Liquidate(_id, _assetAddress, _usdaToRepurchase, _tokensToLiquidate - _liquidationFee, _liquidationFee);

// @audit usda.pauser()
970: 		    emit InterestEvent(uint64(block.timestamp), _e18FactorIncrease, _curveVal);

[273, 290, 307, 314, 322, 373, 411, 420, 430, 440, 561, 609, 694, 970]

File: core/solidity/contracts/core/WUSDA.sol

// @audit IERC20(USDA).safeTransferFrom(_from, address(this), _usdaAmount)
218: 		    emit Deposit(_from, _to, _usdaAmount, _wusdaAmount);

// @audit IERC20(USDA).safeTransferFrom(_from, address(this), _usdaAmount)
230: 		    emit Withdraw(_from, _to, _usdaAmount, _wusdaAmount);

[218, 230]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
225: 		    emit ProposalCreatedIndexed(
226: 		      _newProposal.id,
227: 		      msg.sender,
228: 		      _targets,
229: 		      _values,
230: 		      _signatures,
231: 		      _calldatas,
232: 		      _newProposal.startBlock,
233: 		      _newProposal.endBlock,
234: 		      _description
235: 		    );

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
237: 		    emit ProposalCreated(
238: 		      _newProposal.id,
239: 		      msg.sender,
240: 		      _targets,
241: 		      _values,
242: 		      _signatures,
243: 		      _calldatas,
244: 		      _newProposal.startBlock,
245: 		      _newProposal.endBlock,
246: 		      _description
247: 		    );

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
277: 		    emit ProposalQueuedIndexed(_proposalId, _eta);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
278: 		    emit ProposalQueued(_proposalId, _eta);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
302: 		    emit QueueTransaction(_txHash, _target, _value, _signature, _data, _eta);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
318: 		    emit ProposalExecutedIndexed(_proposalId);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
319: 		    emit ProposalExecuted(_proposalId);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
353: 		    emit ExecuteTransaction(_txHash, _target, _value, _signature, _data, _eta);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
388: 		    emit ProposalCanceledIndexed(_proposalId);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
389: 		    emit ProposalCanceled(_proposalId);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
408: 		    emit CancelTransaction(_txHash, _target, _value, _signature, _data, _eta);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
487: 		    emit VoteCastIndexed(msg.sender, _proposalId, _support, _numberOfVotes, '');

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
488: 		    emit VoteCast(msg.sender, _proposalId, _support, _numberOfVotes, '');

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
499: 		    emit VoteCastIndexed(msg.sender, _proposalId, _support, _numberOfVotes, _reason);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
500: 		    emit VoteCast(msg.sender, _proposalId, _support, _numberOfVotes, _reason);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
520: 		    emit VoteCastIndexed(_signatory, _proposalId, _support, _numberOfVotes, '');

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
521: 		    emit VoteCast(_signatory, _proposalId, _support, _numberOfVotes, '');

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
587: 		    emit NewDelay(_oldTimelockDelay, proposalTimelockDelay);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
598: 		    emit NewEmergencyDelay(_oldEmergencyTimelockDelay, emergencyTimelockDelay);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
609: 		    emit VotingDelaySet(_oldVotingDelay, votingDelay);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
620: 		    emit VotingPeriodSet(_oldVotingPeriod, votingPeriod);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
631: 		    emit EmergencyVotingPeriodSet(_oldEmergencyVotingPeriod, emergencyVotingPeriod);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
642: 		    emit ProposalThresholdSet(_oldProposalThreshold, proposalThreshold);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
653: 		    emit NewQuorum(_oldQuorumVotes, quorumVotes);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
664: 		    emit NewEmergencyQuorum(_oldEmergencyQuorumVotes, emergencyQuorumVotes);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
677: 		    emit WhitelistAccountExpirationSet(_account, _expiration);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
688: 		    emit WhitelistGuardianSet(_oldGuardian, whitelistGuardian);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
699: 		    emit OptimisticVotingDelaySet(_oldOptimisticVotingDelay, optimisticVotingDelay);

// @audit amph.getPriorVotes(msg.sender, (block.number - 1))
710: 		    emit OptimisticQuorumVotesSet(_oldOptimisticQuorumVotes, optimisticQuorumVotes);

[225-235, 237-247, 277, 278, 302, 318, 319, 353, 388, 389, 408, 487, 488, 499, 500, 520, 521, 587, 598, 609, 620, 631, 642, 653, 664, 677, 688, 699, 710]

File: core/solidity/contracts/periphery/CurveMaster.sol

// @audit _curve.valueAt(_xValue)
35: 		    emit VaultControllerSet(_oldCurveAddress, _vaultMasterAddress);

// @audit _curve.valueAt(_xValue)
46: 		    emit CurveSet(_oldCurve, _tokenAddress, _curveAddress);

// @audit _curve.valueAt(_xValue)
56: 		    emit CurveForceSet(_oldCurve, _tokenAddress, _curveAddress);

[35, 46, 56]


[NC-44] Consider disabling renounceOwnership

If it is not within the scope of the project to ultimately relinquish all ownership control, it is recommended to override the renounceOwnership function in OpenZeppelin's Ownable contract in order to disable it.

There are 7 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

12: 		contract AMPHClaimer is IAMPHClaimer, Ownable {

[12]

File: core/solidity/contracts/core/VaultController.sol

23: 		contract VaultController is Pausable, IVaultController, ExponentialNoError, Ownable {

[23]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

8: 		contract AmphoraProtocolToken is IAmphoraProtocolToken, ERC20VotesComp, Ownable {

[8]

File: core/solidity/contracts/periphery/CurveMaster.sol

11: 		contract CurveMaster is ICurveMaster, Ownable {

[11]

File: core/solidity/contracts/utils/UFragments.sol

20: 		contract UFragments is Ownable, IERC20Metadata {

[20]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

10: 		contract CTokenOracle is OracleRelay, Ownable {

[10]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

11: 		contract ChainlinkOracleRelay is OracleRelay, Ownable {

[11]


[NC-45] Custom error without details

Consider adding some parameters to the error to indicate which user or values caused the failure.

There are 75 instances of this issue.

Expand findings
File: core/solidity/contracts/utils/ThreeLines0_100.sol

13: 		  error ThreeLines0_100_InvalidCurve();

16: 		  error ThreeLines0_100_InvalidBreakpointValues();

19: 		  error ThreeLines0_100_InputTooSmall();

[13, 16, 19]

File: core/solidity/contracts/utils/UFragments.sol

43: 		  error UFragments_InvalidSignature();

46: 		  error UFragments_InvalidRecipient();

[43, 46]

File: core/solidity/interfaces/core/IUSDA.sol

93: 		  error USDA_ZeroAmount();

96: 		  error USDA_InsufficientFunds();

99: 		  error USDA_EmptyReserve();

102: 		  error USDA_OnlyPauser();

105: 		  error USDA_NotEnoughBalance();

[93, 96, 99, 102, 105]

File: core/solidity/interfaces/core/IVault.sol

49: 		  error Vault_TokenNotRegistered();

54: 		  error Vault_AmountZero();

57: 		  error Vault_OverWithdrawal();

60: 		  error Vault_RepayTooMuch();

63: 		  error Vault_NotMinter();

66: 		  error Vault_NotVaultController();

69: 		  error Vault_DepositAndStakeOnConvexFailed();

72: 		  error Vault_WithdrawAndUnstakeOnConvexFailed();

75: 		  error Vault_TokenNotCurveLP();

78: 		  error Vault_TokenZeroBalance();

81: 		  error Vault_TokenCanNotBeStaked();

84: 		  error Vault_TokenAlreadyStaked();

[49, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84]

File: core/solidity/interfaces/core/IVaultController.sol

146: 		  error VaultController_TooManyDecimals();

149: 		  error VaultController_OnlyPauser();

152: 		  error VaultController_FeeTooLarge();

155: 		  error VaultController_OracleNotRegistered();

158: 		  error VaultController_TokenAlreadyRegistered();

161: 		  error VaultController_TokenNotRegistered();

164: 		  error VaultController_LTVIncompatible();

167: 		  error VaultController_OnlyMinter();

170: 		  error VaultController_VaultInsolvent();

173: 		  error VaultController_RepayTooMuch();

176: 		  error VaultController_LiquidateZeroTokens();

179: 		  error VaultController_OverLiquidation();

182: 		  error VaultController_VaultSolvent();

185: 		  error VaultController_VaultDoesNotExist();

188: 		  error VaultController_WrongCollateralAddress();

191: 		  error VaultController_NotValidVault();

194: 		  error VaultController_CapReached();

197: 		  error VaultController_TokenAddressDoesNotMatchLpAddress();

[146, 149, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185, 188, 191, 194, 197]

File: core/solidity/interfaces/core/IVaultDeployer.sol

20: 		  error VaultDeployer_OnlyVaultController();

[20]

File: core/solidity/interfaces/governance/IAmphoraProtocolToken.sol

12: 		  error AmphoraProtocolToken_InvalidAddress();

15: 		  error AmphoraProtocolToken_InvalidSupply();

[12, 15]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

15: 		  error GovernorCharlie_NotGovernorCharlie();

18: 		  error GovernorCharlie_NotActive();

21: 		  error GovernorCharlie_VotesBelowThreshold();

24: 		  error GovernorCharlie_NoActions();

27: 		  error GovernorCharlie_TooManyActions();

30: 		  error GovernorCharlie_MultipleActiveProposals();

33: 		  error GovernorCharlie_MultiplePendingProposals();

36: 		  error GovernorCharlie_ArityMismatch();

39: 		  error GovernorCharlie_ProposalNotSucceeded();

42: 		  error GovernorCharlie_ProposalAlreadyQueued();

45: 		  error GovernorCharlie_DelayNotReached();

48: 		  error GovernorCharlie_ProposalNotQueued();

51: 		  error GovernorCharlie_TimelockNotReached();

54: 		  error GovernorCharlie_TransactionStale();

57: 		  error GovernorCharlie_TransactionReverted();

60: 		  error GovernorCharlie_ProposalAlreadyExecuted();

63: 		  error GovernorCharlie_WhitelistedProposer();

66: 		  error GovernorCharlie_ProposalAboveThreshold();

69: 		  error GovernorCharlie_InvalidProposalId();

72: 		  error GovernorCharlie_InvalidSignature();

75: 		  error GovernorCharlie_VotingClosed();

78: 		  error GovernorCharlie_InvalidVoteType();

81: 		  error GovernorCharlie_AlreadyVoted();

84: 		  error GovernorCharlie_ExpirationExceedsMax();

[15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

39: 		  error CurveMaster_TokenNotEnabled();

42: 		  error CurveMaster_ZeroResult();

[39, 42]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

8: 		  error OracleRelay_DifferentUnderlyings();

[8]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

13: 		  error ChainlinkOracle_ZeroAmount();

[13]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

8: 		  error Chainlink_NegativePrice();

[8]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

12: 		  error StableCurveLpOracle_TooFewAnchoredOracles();

[12]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

12: 		  error TriCryptoOracle_ZeroAmount();

[12]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

14: 		  error UniswapV3OracleRelay_TickTimeDiffTooLarge();

[14]


[NC-46] Constants in comparisons should appear on the left side

This is useful to avoid doing some typo bugs.

There are 53 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

88: 		    if (_crvAmountToSend != 0 && _claimedAmph != 0) distributedAmph += (_claimedAmph / 1e12); // scale back to 1e6

88: 		    if (_crvAmountToSend != 0 && _claimedAmph != 0) distributedAmph += (_claimedAmph / 1e12); // scale back to 1e6

152: 		    if (_total == 0) return 0;

169: 		    if (_crvTotalRewards == 0 || _amphBalance == 0) return (0, 0, 0);

169: 		    if (_crvTotalRewards == 0 || _amphBalance == 0) return (0, 0, 0);

180: 		    if (_amphByCrv == 0) return (0, 0, 0);

203: 		    if (_tokenAmountToSend == 0) return 0;

[88, 88, 152, 169, 169, 180, 203]

File: core/solidity/contracts/core/USDA.sol

102: 		    if (_susdAmount == 0) revert USDA_ZeroAmount();

148: 		    if (reserveAmount == 0) revert USDA_EmptyReserve();

149: 		    if (_susdAmount == 0) revert USDA_ZeroAmount();

162: 		    if (_susdAmount == 0) revert USDA_ZeroAmount();

183: 		    if (_susdAmount == 0) revert USDA_ZeroAmount();

203: 		    if (_susdAmount == 0) revert USDA_ZeroAmount();

[102, 148, 149, 162, 183, 203]

File: core/solidity/contracts/core/Vault.sol

90: 		    if (CONTROLLER.tokenId(_token) == 0) revert Vault_TokenNotRegistered();

91: 		    if (_amount == 0) revert Vault_AmountZero();

118: 		    if (CONTROLLER.tokenId(_tokenAddress) == 0) revert Vault_TokenNotRegistered();

141: 		    if (_poolId == 0) revert Vault_TokenCanNotBeStaked();

142: 		    if (balances[_tokenAddress] == 0) revert Vault_TokenZeroBalance();

158: 		    if (_poolId != 0 && balances[_token] != 0 && !isTokenStaked[_token]) _canStake = true;

158: 		    if (_poolId != 0 && balances[_token] != 0 && !isTokenStaked[_token]) _canStake = true;

171: 		      if (_collateralInfo.tokenId == 0) revert Vault_TokenNotRegistered();

179: 		      if (_crvReward != 0) {

192: 		        if (_earnedReward != 0) {

211: 		        if (_claimableAmph != 0) {

235: 		    if (CONTROLLER.tokenId(_tokenAddress) == 0) revert Vault_TokenNotRegistered();

[90, 91, 118, 141, 142, 158, 158, 171, 179, 192, 211, 235]

File: core/solidity/contracts/core/VaultController.sol

257: 		      if (_tokenId == 0) revert VaultController_WrongCollateralAddress();

340: 		    if (_collateral.tokenId != 0) revert VaultController_TokenAlreadyRegistered();

343: 		    if (_poolId != 0) {

392: 		    if (_collateral.tokenId == 0) revert VaultController_TokenNotRegistered();

395: 		    if (_poolId != 0) {

625: 		    if (_tokensToLiquidate == 0) revert VaultController_LiquidateZeroTokens();

627: 		    if (tokenAddressCollateralInfo[_assetAddress].tokenId == 0) revert VaultController_TokenNotRegistered();

633: 		    _collateralLiquidated = _tokenAmount != 0 ? _tokenAmount : _tokensToLiquidate;

652: 		    if (_tokensToLiquidate == 0) revert VaultController_LiquidateZeroTokens();

654: 		    if (tokenAddressCollateralInfo[_assetAddress].tokenId == 0) revert VaultController_TokenNotRegistered();

871: 		      if (_collateral.ltv == 0) continue;

877: 		      if (_balance == 0) continue;

880: 		      if (_rawPrice == 0) continue;

899: 		      if (_collateral.ltv == 0) continue;

905: 		      if (_balance == 0) continue;

908: 		      if (_rawPrice == 0) continue;

933: 		    if (_timeDifference == 0) return 0;

1017: 		    if (_collateral.tokenId == 0) revert VaultController_TokenNotRegistered();

[257, 340, 343, 392, 395, 625, 627, 633, 652, 654, 871, 877, 880, 899, 905, 908, 933, 1017]

File: core/solidity/contracts/governance/GovernorCharlie.sol

168: 		    if (quorumVotes == 0) revert GovernorCharlie_NotActive();

176: 		    if (_targets.length == 0) revert GovernorCharlie_NoActions();

180: 		    if (_latestProposalId != 0) {

346: 		    if (bytes(_signature).length == 0) _callData = _data;

474: 		    else if (_proposal.eta == 0) return ProposalState.Succeeded;

543: 		    if (_support == 0) _proposal.againstVotes = _proposal.againstVotes + _votes;

544: 		    else if (_support == 1) _proposal.forVotes = _proposal.forVotes + _votes;

545: 		    else if (_support == 2) _proposal.abstainVotes = _proposal.abstainVotes + _votes;

[168, 176, 180, 346, 474, 543, 544, 545]

File: core/solidity/contracts/periphery/CurveMaster.sol

26: 		    if (_value == 0) revert CurveMaster_ZeroResult();

[26]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

66: 		    if (_stalePriceDelay == 0) revert ChainlinkOracle_ZeroAmount();

[66]


[NC-47] Consider using delete instead of assigning zero/false to clear values

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 6 instances of this issue.

File: core/solidity/contracts/core/AMPHClaimer.sol

236: 		        _tempAmountReceived = 0;

[236]

File: core/solidity/contracts/core/VaultController.sol

262: 		      _collateral.totalDeposited = 0;

352: 		      _collateral.poolId = 0;

[262, 352]

File: core/solidity/contracts/governance/GovernorCharlie.sol

94: 		    proposalCount = 0;

342: 		    queuedTransactions[_txHash] = false;

406: 		    queuedTransactions[_txHash] = false;

[94, 342, 406]


[NC-48] Use a ternary statement instead of if/else when appropriate

The if/else statement can be written in a shorthand way using the ternary operator, as it increases readability and reduces the number of lines of code.

There is 1 instance of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

346: 		    if (bytes(_signature).length == 0) _callData = _data;
347: 		    else _callData = abi.encodePacked(bytes4(keccak256(bytes(_signature))), _data);

[346-347]


[NC-49] Files not imported in any source code

These files are never imported by any other source file. If any one of these files is needed for tests, it should be moved to a test directory.

There is 1 instance of this issue.

File: core/solidity/interfaces/periphery/IAnchoredViewRelay.sol

2: 		pragma solidity ^0.8.9;

[2]


[NC-50] Layout order does not comply with best practices

This is a best practice that should be followed.

Inside each contract, library or interface, use the following order:

  1. Type declarations
  2. State variables
  3. Events
  4. Errors
  5. Modifiers
  6. Functions

There are 7 instances of this issue.

Expand findings
File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit function constructor found on line 88
106: 		  modifier onlyGov() {

[106]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

// @audit error ThreeLines0_100_InputTooSmall found on line 19
21: 		  int256 public immutable R0;

[21]

File: core/solidity/contracts/utils/UFragments.sol

// @audit error UFragments_InvalidRecipient found on line 46
49: 		  address public monetaryPolicy;

[49]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit error ChainlinkOracle_ZeroAmount found on line 13
16: 		  AggregatorV2V3Interface private immutable _AGGREGATOR;

[16]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

// @audit error StableCurveLpOracle_TooFewAnchoredOracles found on line 12
15: 		  IStablePool public immutable CRV_POOL;

[15]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

// @audit error TriCryptoOracle_ZeroAmount found on line 12
15: 		  IV2Pool public immutable TRI_CRYPTO;

[15]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

// @audit error UniswapV3OracleRelay_TickTimeDiffTooLarge found on line 14
17: 		  bool public immutable QUOTE_TOKEN_IS_TOKEN0;

[17]


[NC-51] Function visibility order does not comply with best practices

This is a best practice that should be followed.

Functions should be grouped according to their visibility and ordered:

  1. constructor
  2. receive function (if exists)
  3. fallback function (if exists)
  4. external
  5. public
  6. internal
  7. private

Within a grouping, place the view and pure functions last.

There are 9 instances of this issue.

Expand findings
File: core/solidity/contracts/core/VaultController.sol

// @audit _getPriceWithDecimals on line 1039, which is internal
988: 		  function vaultSummaries(

[988]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit _getBlockTimestamp on line 733, which is internal
721: 		  function delay() external view override returns (uint256 _delay) {

[721]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

// @audit _get on line 48, which is internal
57: 		  function changeAnchoredView(address _anchoredViewUnderlying) external onlyOwner {

[57]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

// @audit peekValue on line 40, which is public
46: 		  function currentValue() external override returns (uint256 _currentValue) {

[46]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit peekValue on line 51, which is public
65: 		  function setStalePriceDelay(uint256 _stalePriceDelay) external onlyOwner {

[65]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

// @audit peekValue on line 34, which is public
40: 		  function isStale() external view returns (bool _stale) {

[40]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

// @audit peekValue on line 22, which is public
54: 		  fallback() external {}

[54]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

// @audit _setUnderlying on line 19, which is internal
24: 		  function currentValue() external virtual returns (uint256 _currentValue) {

// @audit _setUnderlying on line 19, which is internal
31: 		  function peekValue() public view virtual override returns (uint256 _price);

[24, 31]


[NC-52] Long functions should be refactored into multiple functions

Consider splitting long functions into multiple, smaller functions to improve the code readability.

There are 3 instances of this issue.

File: core/solidity/contracts/core/Vault.sol

164: 		  function claimRewards(address[] memory _tokenAddresses) external override onlyMinter {

[164]

File: core/solidity/contracts/core/VaultController.sol

646: 		  function liquidateVault(

[646]

File: core/solidity/contracts/governance/GovernorCharlie.sol

159: 		  function _propose(

[159]


[NC-53] Imports should use double quotes rather than single quotes

According to the documentation imports should use a double quote instead of a single one.

There are 127 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

4: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

5: 		import {IAMPHClaimer} from '@interfaces/core/IAMPHClaimer.sol';

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

7: 		import {SafeERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

8: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

9: 		import {IVault} from '@interfaces/core/IVault.sol';

[4, 5, 6, 7, 8, 9]

File: core/solidity/contracts/core/USDA.sol

4: 		import {ExponentialNoError} from '@contracts/utils/ExponentialNoError.sol';

5: 		import {Roles} from '@contracts/utils/Roles.sol';

6: 		import {UFragments} from '@contracts/utils/UFragments.sol';

8: 		import {IUSDA} from '@interfaces/core/IUSDA.sol';

9: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

11: 		import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

12: 		import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';

13: 		import {Context} from '@openzeppelin/contracts/utils/Context.sol';

14: 		import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

[4, 5, 6, 8, 9, 11, 12, 13, 14]

File: core/solidity/contracts/core/Vault.sol

4: 		import {IUSDA} from '@interfaces/core/IUSDA.sol';

5: 		import {IVault} from '@interfaces/core/IVault.sol';

6: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

7: 		import {IAMPHClaimer} from '@interfaces/core/IAMPHClaimer.sol';

8: 		import {IBooster} from '@interfaces/utils/IBooster.sol';

9: 		import {IBaseRewardPool} from '@interfaces/utils/IBaseRewardPool.sol';

10: 		import {IVirtualBalanceRewardPool} from '@interfaces/utils/IVirtualBalanceRewardPool.sol';

12: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

13: 		import {Context} from '@openzeppelin/contracts/utils/Context.sol';

14: 		import {SafeERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol';

15: 		import {IERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol';

16: 		import {ICVX} from '@interfaces/utils/ICVX.sol';

[4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16]

File: core/solidity/contracts/core/VaultController.sol

4: 		import {ExponentialNoError} from '@contracts/utils/ExponentialNoError.sol';

5: 		import {CurveMaster} from '@contracts/periphery/CurveMaster.sol';

7: 		import {IUSDA} from '@interfaces/core/IUSDA.sol';

8: 		import {IVault} from '@interfaces/core/IVault.sol';

9: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

10: 		import {IOracleRelay} from '@interfaces/periphery/IOracleRelay.sol';

11: 		import {IBooster} from '@interfaces/utils/IBooster.sol';

12: 		import {IBaseRewardPool} from '@interfaces/utils/IBaseRewardPool.sol';

13: 		import {IAMPHClaimer} from '@interfaces/core/IAMPHClaimer.sol';

14: 		import {IVaultDeployer} from '@interfaces/core/IVaultDeployer.sol';

16: 		import {IERC20, IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

17: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

18: 		import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';

[4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18]

File: core/solidity/contracts/core/VaultDeployer.sol

4: 		import {Vault, IVault} from '@contracts/core/Vault.sol';

5: 		import {IVaultDeployer} from '@interfaces/core/IVaultDeployer.sol';

6: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

7: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/core/WUSDA.sol

4: 		import {IWUSDA} from '@interfaces/core/IWUSDA.sol';

6: 		import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

7: 		import {ERC20, IERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

9: 		import {ERC20Permit} from '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol';

[4, 6, 7, 9]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

4: 		import {IAmphoraProtocolToken} from '@interfaces/governance/IAmphoraProtocolToken.sol';

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

6: 		import {ERC20, ERC20Permit, ERC20VotesComp} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20VotesComp.sol';

[4, 5, 6]

File: core/solidity/contracts/governance/GovernorCharlie.sol

6: 		import {IAMPH} from '@interfaces/governance/IAMPH.sol';

7: 		import {IGovernorCharlie} from '@interfaces/governance/IGovernorCharlie.sol';

9: 		import {Receipt, ProposalState, Proposal} from '@contracts/utils/GovernanceStructs.sol';

[6, 7, 9]

File: core/solidity/contracts/periphery/CurveMaster.sol

4: 		import {ICurveMaster} from '@interfaces/periphery/ICurveMaster.sol';

5: 		import {ICurveSlave} from '@interfaces/utils/ICurveSlave.sol';

6: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

4: 		import {ICurveSlave} from '@interfaces/utils/ICurveSlave.sol';

[4]

File: core/solidity/contracts/utils/UFragments.sol

5: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

6: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[5, 6]

File: core/solidity/interfaces/core/IAMPHClaimer.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

5: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

[4, 5]

File: core/solidity/interfaces/core/IUSDA.sol

4: 		import {IRoles} from '@interfaces/utils/IRoles.sol';

6: 		import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[4, 6]

File: core/solidity/interfaces/core/IVault.sol

4: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

5: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

6: 		import {IBaseRewardPool} from '@interfaces/utils/IBaseRewardPool.sol';

7: 		import {ICVX} from '@interfaces/utils/ICVX.sol';

[4, 5, 6, 7]

File: core/solidity/interfaces/core/IVaultController.sol

4: 		import {CurveMaster} from '@contracts/periphery/CurveMaster.sol';

5: 		import {IOracleRelay} from '@interfaces/periphery/IOracleRelay.sol';

6: 		import {IBooster} from '@interfaces/utils/IBooster.sol';

7: 		import {IBaseRewardPool} from '@interfaces/utils/IBaseRewardPool.sol';

8: 		import {IVaultDeployer} from '@interfaces/core/IVaultDeployer.sol';

9: 		import {IAMPHClaimer} from '@interfaces/core/IAMPHClaimer.sol';

10: 		import {IUSDA} from '@interfaces/core/IUSDA.sol';

[4, 5, 6, 7, 8, 9, 10]

File: core/solidity/interfaces/core/IVaultDeployer.sol

4: 		import {IVault} from '@interfaces/core/IVault.sol';

5: 		import {IVaultController} from '@interfaces/core/IVaultController.sol';

6: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4, 5, 6]

File: core/solidity/interfaces/core/IWUSDA.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

4: 		import {IGovernorCharlieEvents} from '@interfaces/governance/IGovernorCharlieEvents.sol';

5: 		import {IAMPH} from '@interfaces/governance/IAMPH.sol';

7: 		import {Receipt, ProposalState, Proposal} from '@contracts/utils/GovernanceStructs.sol';

[4, 5, 7]

File: core/solidity/interfaces/utils/IBaseRewardPool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

5: 		import {IVirtualBalanceRewardPool} from '@interfaces/utils/IVirtualBalanceRewardPool.sol';

[4, 5]

File: core/solidity/interfaces/utils/ICVX.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/ICurvePool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IRoles.sol

4: 		import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol';

[4]

File: core/solidity/interfaces/utils/IVirtualBalanceRewardPool.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/interfaces/utils/IWStETH.sol

4: 		import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

[4]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {IChainlinkOracleRelay} from '@interfaces/periphery/IChainlinkOracleRelay.sol';

[4, 5]

File: core/solidity/contracts/periphery/oracles/CTokenOracle.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {ICToken} from '@interfaces/periphery/ICToken.sol';

6: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

7: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {IV2Pool} from '@interfaces/utils/ICurvePool.sol';

6: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

7: 		import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';

8: 		import {CurveRegistryUtils} from '@contracts/periphery/oracles/CurveRegistryUtils.sol';

[4, 5, 6, 7, 8]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {ChainlinkStalePriceLib} from '@contracts/periphery/oracles/ChainlinkStalePriceLib.sol';

6: 		import {AggregatorV2V3Interface} from '@chainlink/interfaces/AggregatorV2V3Interface.sol';

7: 		import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

4: 		import {AggregatorV2V3Interface} from '@chainlink/interfaces/AggregatorV2V3Interface.sol';

[4]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {ChainlinkOracleRelay} from '@contracts/periphery/oracles/ChainlinkOracleRelay.sol';

[4, 5]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

4: 		import {ICurveRegistry, ICurveAddressesProvider} from '@interfaces/periphery/ICurveAddressesProvider.sol';

[4]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {StableCurveLpOracle} from '@contracts/periphery/oracles/StableCurveLpOracle.sol';

[4, 5]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

4: 		import {IOracleRelay} from '@interfaces/periphery/IOracleRelay.sol';

[4]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {IStablePool} from '@interfaces/utils/ICurvePool.sol';

6: 		import {Math} from '@openzeppelin/contracts/utils/math/Math.sol';

7: 		import {CurveRegistryUtils} from '@contracts/periphery/oracles/CurveRegistryUtils.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {IV2Pool} from '@interfaces/utils/ICurvePool.sol';

6: 		import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';

7: 		import {CurveRegistryUtils} from '@contracts/periphery/oracles/CurveRegistryUtils.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

4: 		import {OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {IUniswapV3Pool} from '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';

6: 		import {OracleLibrary} from '@uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol';

7: 		import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';

[4, 5, 6, 7]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

4: 		import {UniswapV3OracleRelay} from '@contracts/periphery/oracles/UniswapV3OracleRelay.sol';

[4]

File: core/solidity/contracts/periphery/oracles/WstEthOracle.sol

4: 		import {IOracleRelay, OracleRelay} from '@contracts/periphery/oracles/OracleRelay.sol';

5: 		import {IWStETH} from '@interfaces/utils/IWStETH.sol';

[4, 5]


[NC-54] Strings should use double quotes rather than single quotes

According to the documentation strings should use a double quote instead of a single one.

There are 19 instances of this issue.

Expand findings
File: core/solidity/contracts/core/USDA.sol

21: 		  bytes32 public constant VAULT_CONTROLLER_ROLE = keccak256('VAULT_CONTROLLER');

57: 		  constructor(IERC20 _sUSDAddr) UFragments('USDA Token', 'USDA') {

[21, 57]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

12: 		  ) ERC20('Amphora Protocol', 'AMPH') ERC20Permit('Amphora Protocol') {

[12]

File: core/solidity/contracts/governance/GovernorCharlie.sol

13: 		  string public constant NAME = 'Amphora Protocol Governor';

20: 		    keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

23: 		  bytes32 public constant BALLOT_TYPEHASH = keccak256('Ballot(uint256 proposalId,uint8 support)');

487: 		    emit VoteCastIndexed(msg.sender, _proposalId, _support, _numberOfVotes, '');

488: 		    emit VoteCast(msg.sender, _proposalId, _support, _numberOfVotes, '');

512: 		    bytes32 _digest = keccak256(abi.encodePacked('\x19\x01', _domainSeparator, _structHash));

520: 		    emit VoteCastIndexed(_signatory, _proposalId, _support, _numberOfVotes, '');

521: 		    emit VoteCast(_signatory, _proposalId, _support, _numberOfVotes, '');

[13, 20, 23, 487, 488, 512, 520, 521]

File: core/solidity/contracts/utils/UFragments.sol

86: 		  string public constant EIP712_REVISION = '1';

88: 		    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');

90: 		    keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');

328: 		    bytes32 _digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR(), _permitDataDigest));

[86, 88, 90, 328]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

77: 		    require(_mainValue > 0, 'invalid oracle value');

80: 		    require(_anchorPrice > 0, 'invalid anchor value');

95: 		    require(_mainValue < _upperBounds, 'anchor too low');

96: 		    require(_mainValue > _lowerBounds, 'anchor too high');

[77, 80, 95, 96]


[NC-55] Lines are too long

Maximum suggested line length is 120 characters according to the documentation.

There are 19 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

213: 		      // all cliffs start when a certain amount of CRV is accumulated and finish when a certain amount is reached, this is the start of the current cliff

[213]

File: core/solidity/contracts/core/USDA.sol

83: 		  /// the calculations for deposit mimic the calculations done by mint in the ampleforth contract, simply with the susd transfer

169: 		    // the gonbalances of the sender is in gons, therefore we must multiply the deposit amount, which is in fragments, by gonsperfragment

173: 		    // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons

[83, 169, 173]

File: core/solidity/contracts/core/VaultController.sol

873: 		      // note that index 0 of enabledTokens corresponds to a vaultId of 1, so we must subtract 1 from i to get the correct index

901: 		      // note that index 0 of enabledTokens corresponds to a vaultId of 1, so we must subtract 1 from i to get the correct index

918: 		  /// @notice Returns the increase amount of the interest factor. Accrues interest to borrowers and distribute it to USDA holders

[873, 901, 918]

File: core/solidity/contracts/governance/GovernorCharlie.sol

28: 		  /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed

31: 		  /// @notice The number of votes in support of a proposal required in order for an emergency quorum to be reached and for a vote to succeed

681: 		   * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses

[28, 31, 681]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

113: 		///      |                                                             ***--------------------------------------------------------------\

118: 		///      +---------------------------------------------------------------------------------------------------------------------------------

119: 		/// (0,0)                                                                                                                            (100, R2)

[113, 118, 119]

File: core/solidity/interfaces/core/IUSDA.sol

132: 		  /// the calculations for deposit mimic the calculations done by mint in the ampleforth contract, simply with the susd transfer

[132]

File: core/solidity/interfaces/core/IVaultController.sol

447: 		  /// @notice Returns the increase amount of the interest factor. Accrues interest to borrowers and distribute it to USDA holders

[447]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

90: 		  /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed

93: 		  /// @notice The number of votes in support of a proposal required in order for an emergency quorum to be reached and for a vote to succeed

347: 		   * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses

[90, 93, 347]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

9: 		///      quote_token refers to the token we are comparing to, so for an Aave price in ETH, Aave is the target and Eth is the quote

[9]


[NC-56] Avoid the use of sensitive terms

Use alternative variants, e.g. allowlist/denylist instead of whitelist/blacklist.

There are 63 instances of this issue.

Expand findings
File: core/solidity/contracts/governance/GovernorCharlie.sol

64: 		  /// @notice Stores the expiration of account whitelist status as a timestamp

65: 		  mapping(address => uint256) public whitelistAccountExpirations;

67: 		  /// @notice Address which manages whitelisted proposals and whitelist accounts

68: 		  address public whitelistGuardian;

85: 		  /// @notice The maximum number of seconds an address can be whitelisted for

86: 		  uint256 public maxWhitelistPeriod;

102: 		    maxWhitelistPeriod = 31_536_000;

169: 		    // Allow addresses above proposal threshold and whitelisted addresses to propose

170: 		    if (amph.getPriorVotes(msg.sender, (block.number - 1)) < proposalThreshold && !isWhitelisted(msg.sender)) {

207: 		    //whitelist can't make emergency

208: 		    if (_emergency && !isWhitelisted(msg.sender)) {

215: 		    //whitelist can only make optimistic proposals

216: 		    if (isWhitelisted(msg.sender)) {

358: 		   * @notice whitelistGuardian can cancel proposals from whitelisted addresses

368: 		      // Whitelisted proposers can't be canceled for falling below proposal threshold

369: 		      if (isWhitelisted(_proposal.proposer)) {

372: 		            || msg.sender != whitelistGuardian

373: 		        ) revert GovernorCharlie_WhitelistedProposer();

465: 		    bool _whitelisted = isWhitelisted(_proposal.proposer);

470: 		      (_whitelisted && _proposal.againstVotes > _proposal.quorumVotes)

471: 		        || (!_whitelisted && _proposal.forVotes <= _proposal.againstVotes)

472: 		        || (!_whitelisted && _proposal.forVotes < _proposal.quorumVotes)

555: 		   * @notice View function which returns if an account is whitelisted

557: 		   * @return _isWhitelisted If the account is whitelisted

559: 		  function isWhitelisted(address _account) public view override returns (bool _isWhitelisted) {

560: 		    return (whitelistAccountExpirations[_account] > block.timestamp);

572: 		   * @notice Governance function for setting the max whitelist period

573: 		   * @param  _second How many seconds to whitelist for

575: 		  function setMaxWhitelistPeriod(uint256 _second) external onlyGov {

576: 		    maxWhitelistPeriod = _second;

668: 		   * @notice Governance function for setting the whitelist expiration as a timestamp

669: 		   * for an account. Whitelist status allows accounts to propose without meeting threshold

670: 		   * @param _account Account address to set whitelist expiration for

671: 		   * @param _expiration Expiration for account whitelist status as timestamp (if now < expiration, whitelisted)

673: 		  function setWhitelistAccountExpiration(address _account, uint256 _expiration) external override onlyGov {

674: 		    if (_expiration >= (maxWhitelistPeriod + block.timestamp)) revert GovernorCharlie_ExpirationExceedsMax();

675: 		    whitelistAccountExpirations[_account] = _expiration;

677: 		    emit WhitelistAccountExpirationSet(_account, _expiration);

681: 		   * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses

682: 		   * @param _account Account to set whitelistGuardian to (0x0 to remove whitelistGuardian)

684: 		  function setWhitelistGuardian(address _account) external override onlyGov {

685: 		    address _oldGuardian = whitelistGuardian;

686: 		    whitelistGuardian = _account;

688: 		    emit WhitelistGuardianSet(_oldGuardian, whitelistGuardian);

[64, 65, 67, 68, 85, 86, 102, 169, 170, 207, 208, 215, 216, 358, 368, 369, 372, 373, 465, 470, 471, 472, 555, 557, 559, 560, 572, 573, 575, 576, 668, 669, 670, 671, 673, 674, 675, 677, 681, 682, 684, 685, 686, 688]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

62: 		  /// @notice Thrown when trying to cancel a whitelisted proposer's proposal

63: 		  error GovernorCharlie_WhitelistedProposer();

123: 		  /// @notice Stores the expiration of account whitelist status as a timestamp

124: 		  function whitelistAccountExpirations(address _account) external returns (uint256 _expiration);

126: 		  /// @notice Address which manages whitelisted proposals and whitelist accounts

127: 		  function whitelistGuardian() external view returns (address _guardian);

141: 		  function maxWhitelistPeriod() external view returns (uint256 _maxWhitelistPeriod);

217: 		   * @notice whitelistGuardian can cancel proposals from whitelisted addresses

284: 		   * @notice View function which returns if an account is whitelisted

286: 		   * @return _isWhitelisted If the account is whitelisted

288: 		  function isWhitelisted(address _account) external view returns (bool _isWhitelisted);

339: 		   * @notice Governance function for setting the whitelist expiration as a timestamp

340: 		   * for an account. Whitelist status allows accounts to propose without meeting threshold

341: 		   * @param _account Account address to set whitelist expiration for

342: 		   * @param _expiration Expiration for account whitelist status as timestamp (if now < expiration, whitelisted)

344: 		  function setWhitelistAccountExpiration(address _account, uint256 _expiration) external;

347: 		   * @notice Governance function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from whitelisted addresses

348: 		   * @param _account Account to set whitelistGuardian to (0x0 to remove whitelistGuardian)

350: 		  function setWhitelistGuardian(address _account) external;

[62, 63, 123, 124, 126, 127, 141, 217, 284, 286, 288, 339, 340, 341, 342, 344, 347, 348, 350]


[NC-57] Misplaced SPDX identifier

The SPDX identifier should be on the very first line of each source file.

There is 1 instance of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

1: 		// solhint-disable max-states-count

[1]


[NC-58] Typos in comments

Avoid typos, and proper nouns should be capitalized.

There are 54 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

// @audit amph
224: 		      // calculate the amph available for this cliff

// @audit amph
227: 		      // check if the amount of amph to mint surpasses the cliff

[224, 227]

File: core/solidity/contracts/core/USDA.sol

// @audit ampleforth
83: 		  /// the calculations for deposit mimic the calculations done by mint in the ampleforth contract, simply with the susd transfer

// @audit gonsperfragment
169: 		    // the gonbalances of the sender is in gons, therefore we must multiply the deposit amount, which is in fragments, by gonsperfragment

// @audit totalgons, gonsperfragment
173: 		    // and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons

// @audit gonsperfragment
190: 		    // modify the gonbalances of the sender, subtracting the amount of gons, therefore amount * gonsperfragment

[83, 169, 173, 190]

File: core/solidity/contracts/core/Vault.sol

// @audit liabilitiy
39: 		  /// the vaultController in order to find the true liabilitiy

// @audit tokenv
101: 		        /// In this case the user's balance isn't staked so we stake the amount + his balance for the specific tokenv

// @audit crv, cvx
162: 		  /// @dev    Transfers a percentage of the crv and cvx rewards to claim AMPH tokens

[39, 101, 162]

File: core/solidity/contracts/core/VaultController.sol

// @audit crv
382: 		  /// @param _poolId The convex pool id of a crv lp token

// @audit liabilty
593: 		      // get the total USDA liability, equal to the interest factor * vault's base liabilty

// @audit explaination
630: 		    // see _liquidationMath for more detailed explaination of the math

// @audit explaination
657: 		    // see _liquidationMath for more detailed explaination of the math

// @audit registed, indivuduals
867: 		    // loop over each registed token, adding the indivuduals ltv to the total ltv of the vault

// @audit registed, indivuduals
895: 		    // loop over each registed token, adding the indivuduals ltv to the total ltv of the vault

// @audit timedifference
945: 		    // this is equal to (timedifference * (curve value) / (seconds in a year)) * (interest factor)

// @audit accured
960: 		    // valueAfter - valueBefore is now equal to the true amount of interest accured

[382, 593, 630, 657, 867, 895, 945, 960]

File: core/solidity/contracts/core/WUSDA.sol

// @audit exusdae
21: 		 *      For exusdae: 100K wUSDA => 1% of the usda market cap

[21]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit chaid
725: 		  /// @notice Returns the chaid id of the blockchain

[725]

File: core/solidity/contracts/periphery/CurveMaster.sol

// @audit labled
18: 		  /// @notice Returns the value of curve labled _tokenAddress at _xValue

[18]

File: core/solidity/contracts/utils/UFragments.sol

// @audit xn
37: 		  // f(x0) + f(x1) + ... + f(xn) is not always equal to f(x0 + x1 + ... xn).

[37]

File: core/solidity/interfaces/core/IUSDA.sol

// @audit ampleforth
132: 		  /// the calculations for deposit mimic the calculations done by mint in the ampleforth contract, simply with the susd transfer

[132]

File: core/solidity/interfaces/core/IVault.sol

// @audit crv, cvx
191: 		  /// @dev    Transfers a percentage of the crv and cvx rewards to claim AMPH tokens

[191]

File: core/solidity/interfaces/core/IVaultController.sol

// @audit addres
33: 		   * @param _tokenAddress The addres of the erc20 token
34: 		   * @param _ltv The loan to value amount of the erc20
35: 		   * @param _oracleAddress The address of the oracle to use to fetch the price

// @audit addres, crv
45: 		   * @param _tokenAddress The addres of the erc20 token to update
46: 		   * @param _ltv The new loan to value amount of the erc20
47: 		   * @param _oracleAddress The new address of the oracle to use to fetch the price
48: 		   * @param _liquidationIncentive The new liquidation penalty for the token
49: 		   * @param _cap The maximum amount that can be deposited
50: 		   * @param _poolId The convex pool id of a crv lp token

// @audit amounnt
78: 		   * @param _borrowAmount The amounnt that was borrowed

// @audit crv
196: 		  /// @notice Thrown when registering a crv lp token with wrong address

// @audit targetted
361: 		  /// @return _vaultAddress The address of the targetted vault

// @audit crv
556: 		  /// @param _poolId The convex pool id of a crv lp token

[33-35, 45-50, 78, 196, 361, 556]

File: core/solidity/interfaces/governance/IGovernorCharlie.sol

// @audit arity
35: 		  /// @notice Thrown when there is information arity mismatch

[35]

File: core/solidity/interfaces/periphery/ICurveMaster.sol

// @audit labled
51: 		  /// @notice Returns the value of curve labled _tokenAddress at _xValue

[51]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

// @audit underlyings
7: 		  /// @notice Emited when the underlyings are different in the anchored view

[7]

File: core/solidity/contracts/periphery/oracles/AnchoredViewRelay.sol

// @audit chainlink
71: 		  /// @notice Compares the main value (chainlink) to the anchor value (uniswap v3)

[71]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

// @audit chainlink
57: 		    // Get the prices from chainlink and add 10 decimals

[57]

File: core/solidity/contracts/periphery/oracles/ChainlinkOracleRelay.sol

// @audit chainlink
9: 		/// @notice Oracle that wraps a chainlink oracle.

// @audit chainlink
15: 		  /// @notice The chainlink aggregator

// @audit chainlink
29: 		  /// @param _feedAddress The address of chainlink feed

[9, 15, 29]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

// @audit chainlink
8: 		/// @dev Oracle that wraps a chainlink oracle

// @audit chainlink
11: 		  /// @notice The chainlink aggregator

// @audit chainlink
14: 		  /// @notice The chainlink aggregator for the base token

// @audit chainlink
18: 		  /// @param  _feedAddress The address of chainlink feed

// @audit chainlink
19: 		  /// @param  _baseFeedAddress The address of chainlink feed for the base token

[8, 11, 14, 18, 19]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

// @audit inmutable
7: 		  /// @notice The inmutable curve registry

[7]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

// @audit crv
9: 		/// @notice Oracle Relay for crv lps

// @audit crv
14: 		  /// @notice The pool of the crv lp token

// @audit lastest
40: 		  /// @notice Calculates the lastest exchange rate

[9, 14, 40]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

// @audit chainlink
54: 		    // Get the prices from chainlink and add 10 decimals

// @audit qbrt
79: 		  //     discount: uint256 = max(g**2 / 10**18 * a, 10**34)  # handle qbrt nonconvergence

[54, 79]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

// @audit lookback
22: 		  /// @notice The lookback for the oracle

// @audit twap
38: 		  /// @param _lookback How many seconds to twap for

// @audit chainlink
39: 		  /// @param  _poolAddress The address of chainlink feed

// @audit twap
72: 		  /// @param _seconds How many seconds to twap for

[22, 38, 39, 72]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

// @audit ethusdc
15: 		  /// @param _ethOracle The uniswap oracle for ethusdc

// @audit twap
16: 		  /// @param _lookback How many seconds to twap for

[15, 16]


[NC-59] Contracts should have full test coverage

A 100% test coverage is not foolproof, but it helps immensely in reducing the amount of bugs that may occur.


[NC-60] Large or complicated code bases should implement invariant tests

This includes: large code bases, or code with lots of inline-assembly, complicated math, or complicated interactions between multiple contracts.

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 may help significantly.


[NC-61] Inconsistent spacing in comments

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 38 instances of this issue.

Expand findings
File: core/solidity/contracts/core/USDA.sol

16: 		/// @notice USDA token contract, handles all minting/burning of usda

[16]

File: core/solidity/contracts/core/Vault.sol

333: 		    //use current supply to gauge cliff

334: 		    //this will cause a bit of overflow into the next cliff range

335: 		    //but should be within reasonable levels.

336: 		    //requires a max supply check though

338: 		    //mint if below total cliffs

340: 		      //for reduction% take inverse of current cliff

342: 		      //reduce

345: 		      //supply cap check

[333, 334, 335, 336, 338, 340, 342, 345]

File: core/solidity/contracts/core/VaultController.sol

20: 		/// @notice Controller of all vaults in the USDA borrow/lend system

286: 		    //push new vault ID onto mapping

726: 		    //require that the vault is not solvent

748: 		    //require that the vault is not solvent

791: 		    //Cannot liquidate more than is necessary to make vault over-collateralized

796: 		    //Cannot liquidate more collateral than there is in the vault

865: 		  //solhint-disable-next-line code-complexity

893: 		  //solhint-disable-next-line code-complexity

956: 		    // this should save ~5000 gas/call

[20, 286, 726, 748, 791, 796, 865, 893, 956]

File: core/solidity/contracts/governance/GovernorCharlie.sol

207: 		    //whitelist can't make emergency

215: 		    //whitelist can only make optimistic proposals

734: 		    // solium-disable-next-line security/no-block-members

[207, 215, 734]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

70: 		  /// @notice linear interpolation, calculates g(x) = (_rise/_run)x+b

[70]

File: core/solidity/contracts/utils/UFragments.sol

85: 		  // https://eips.ethereum.org/EIPS/eip-2612

99: 		    //set og initial values

103: 		    _gonBalances[address(0x0)] = _totalGons; //send starting supply to a burner address so _totalSupply is never 0

[85, 99, 103]

File: core/solidity/interfaces/periphery/IOracleRelay.sol

27: 		  /// @return _type the type (Chainlink/Uniswap/Price)

[27]

File: core/solidity/contracts/periphery/oracles/CbEthEthOracle.sol

10: 		/// @notice Oracle Relay for the cbETH/ETH pool

15: 		  /// @notice The oracle relay for the cbETH/ETH price

18: 		  /// @notice The oracle relay for the ETH/USD price

[10, 15, 18]

File: core/solidity/contracts/periphery/oracles/ChainlinkTokenOracleRelay.sol

7: 		/// @notice This oracle is for tokens that don't have a USD pair but do have a wETH/ETH pair

[7]

File: core/solidity/contracts/periphery/oracles/TriCrypto2Oracle.sol

9: 		/// @notice Oracle Relay for the TriCrypto pool (USDT/WBTC/WETH)

17: 		  /// @notice The oracle relay for the WBTC/USD price

20: 		  /// @notice The oracle relay for the ETH/USD price

23: 		  /// @notice The oracle relay for the USDT/USD price

76: 		  //     # ((A/A0) * (gamma/gamma0)**2) ** (1/3)

[9, 17, 20, 23, 76]

File: core/solidity/contracts/periphery/oracles/UniswapV3OracleRelay.sol

40: 		  /// @param _quoteTokenIsToken0 The marker for which token to use as quote/base in calculation

[40]

File: core/solidity/contracts/periphery/oracles/UniswapV3TokenOracleRelay.sol

11: 		  /// @notice The oracle for eth/usdc

34: 		    //get price of eth to convert _priceInEth to USD terms

[11, 34]


[NC-62] State variables should include comments

Consider adding some comments on critical state variables to explain what they are supposed to do: this will help for future code reviews.

There are 20 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

15: 		  uint256 internal constant _BASE = 1 ether;

[15]

File: core/solidity/contracts/core/USDA.sol

21: 		  bytes32 public constant VAULT_CONTROLLER_ROLE = keccak256('VAULT_CONTROLLER');

23: 		  EnumerableSet.AddressSet internal _vaultControllers;

[21, 23]

File: core/solidity/contracts/utils/ThreeLines0_100.sol

21: 		  int256 public immutable R0;

22: 		  int256 public immutable R1;

23: 		  int256 public immutable R2;

24: 		  int256 public immutable S1;

25: 		  int256 public immutable S2;

[21, 22, 23, 24, 25]

File: core/solidity/contracts/utils/UFragments.sol

61: 		  uint256 private constant DECIMALS = 18;

62: 		  uint256 private constant MAX_UINT256 = 2 ** 256 - 1;

63: 		  uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 1 * 10 ** DECIMALS;

72: 		  uint256 public _totalSupply;

73: 		  uint256 public _gonsPerFragment;

74: 		  mapping(address => uint256) public _gonBalances;

76: 		  string public name;

77: 		  string public symbol;

78: 		  uint8 public constant decimals = uint8(DECIMALS);

87: 		  bytes32 public constant EIP712_DOMAIN =
88: 		    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');

89: 		  bytes32 public constant PERMIT_TYPEHASH =
90: 		    keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');

[61, 62, 63, 72, 73, 74, 76, 77, 78, 87-88, 89-90]

File: core/solidity/contracts/periphery/oracles/EthSafeStableCurveOracle.sol

9: 		  uint256 public virtualPrice;

[9]


[NC-63] Complicated functions should have explicit comments

Large and/or complex functions should have more comments to better explain the purpose of each logic step.

There is 1 instance of this issue.

File: core/solidity/contracts/governance/GovernorCharlie.sol

159: 		  function _propose(

[159]


[NC-64] Assembly code should have explicit comments

Low-level languages like assembly should require extensive documentation and comments to clarify the purpose of each instruction.

There is 1 instance of this issue.

File: core/solidity/contracts/utils/UFragments.sol

165: 		    assembly {

[165]


[NC-65] Missing NatSpec @param

Some functions have an incomplete NatSpec: add a @param notation to describe the function parameters to improve the code documentation.

There are 19 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

// @audit missing _total, _fraction
150: 		  /// @dev Receives a total and a percentage, returns the amount equivalent of the percentage
151: 		  function _totalToFraction(uint256 _total, uint256 _fraction) internal pure returns (uint256 _amount) {

// @audit missing _sender, _vaultId, _cvxTotalRewards, _crvTotalRewards
156: 		  /// @dev Doesn't revert but returns 0 so the vault contract doesn't revert on calling the claim function
157: 		  /// @dev Returns the claimable amount of AMPH, also the CVX and CRV the contract will take from the user
158: 		  function _claimable(

// @audit missing _distributedAmph
193: 		  /// @dev Returns the rate of the token, denominated in 1e6
194: 		  function _getRate(uint256 _distributedAmph) internal pure returns (uint256 _rate) {

// @audit missing _tokenAmountToSend
201: 		  /// @dev Returns how much AMPH would be minted given a token amount
202: 		  function _calculate(uint256 _tokenAmountToSend) internal view returns (uint256 _amphAmount) {

// @audit missing _distributedAmph
248: 		  /// @dev Returns the current cliff, it will round down but is on purpose
249: 		  function _getCliff(uint256 _distributedAmph) internal pure returns (uint256 _cliff) {

[150-151, 156-158, 193-194, 201-202, 248-249]

File: core/solidity/contracts/core/USDA.sol

// @audit missing _pauser
61: 		  /// @notice Sets the pauser for both USDA and VaultController
62: 		  /// @dev The pauser is a separate role from the owner
63: 		  function setPauser(address _pauser) external override onlyOwner {

// @audit missing _susdAmount, _target
100: 		  /// @notice Business logic to deposit sUSD and mint USDA for the caller
101: 		  function _deposit(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {

136: 		  /// @notice Withdraw sUSD by burning USDA
137: 		  /// @dev This function is effectively just withdraw, but we calculate the amount for the _target
138: 		  /// @param _target should obtain 1 sUSD for every 1 USDA burned from caller
139: 		  /// @param _susdWithdrawn The amount os sUSD withdrawn
140: 		  function withdrawAllTo(address _target) external override returns (uint256 _susdWithdrawn) {

// @audit missing _susdAmount, _target
146: 		  /// @notice business logic to withdraw sUSD and burn USDA from the caller
147: 		  function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {

// @audit missing _target, _amount
166: 		  /// @dev mint a specific `amount` of tokens to the `target`
167: 		  function _mint(address _target, uint256 _amount) internal {

// @audit missing _target, _amount
187: 		  /// @dev burn a specific `amount` of tokens from the `target`
188: 		  function _burn(address _target, uint256 _amount) internal {

[61-63, 100-101, 136-140, 146-147, 166-167, 187-188]

File: core/solidity/contracts/core/Vault.sol

// @audit missing _token, _booster, _amount, _poolId
319: 		  /// @dev Internal function for depositing and staking on convex
320: 		  function _depositAndStakeOnConvex(address _token, IBooster _booster, uint256 _amount, uint256 _poolId) internal {

[319-320]

File: core/solidity/contracts/core/VaultController.sol

// @audit missing _poolId
325: 		  /// @notice Register a new token to be used as collateral
326: 		  /// @param _tokenAddress The address of the token to register
327: 		  /// @param _ltv The ltv of the token, 1e18=100%
328: 		  /// @param _oracleAddress The address of oracle to fetch the price of the token
329: 		  /// @param _liquidationIncentive The liquidation penalty for the token, 1e18=100%
330: 		  /// @param _cap The maximum amount to be deposited
331: 		  function registerErc20(

// @audit missing _amount, _token, _increase
1014: 		  /// @notice Modifies the total deposited in the protocol
1015: 		  function _modifyTotalDeposited(uint256 _amount, address _token, bool _increase) internal {

[325-331, 1014-1015]

File: core/solidity/contracts/governance/AmphoraProtocolToken.sol

// @audit missing _dst, _rawAmount
19: 		  /// @notice Mint a specified amount of tokens to a specified address
20: 		  function mint(address _dst, uint256 _rawAmount) public onlyOwner {

[19-20]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit missing _proposalId, _support, _v, _r, _s
503: 		  /**
504: 		   * @notice Cast a vote for a proposal by signature
505: 		   * @dev External override function that accepts EIP-712 signatures for voting on proposals.
506: 		   */
507: 		  function castVoteBySig(uint256 _proposalId, uint8 _support, uint8 _v, bytes32 _r, bytes32 _s) external override {

[503-507]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

// @audit missing _aggregator
10: 		  /// @notice Returns the current price from the aggregator
11: 		  function getCurrentPrice(AggregatorV2V3Interface _aggregator) internal view returns (uint256 _price) {

[10-11]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

// @audit missing _crvPool
11: 		  /// @notice Returns the lp address of the curve pool
12: 		  function _getLpAddress(address _crvPool) internal view returns (address _lpAddress) {

[11-12]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

// @audit missing _underlying
18: 		  /// @notice set the underlying address
19: 		  function _setUnderlying(address _underlying) internal {

[18-19]


[NC-66] Incomplete NatSpec @return

Some functions have an incomplete NatSpec: add a @return notation to describe the function return value to improve the code documentation.

There are 13 instances of this issue.

Expand findings
File: core/solidity/contracts/core/AMPHClaimer.sol

// @audit missing @return
150: 		  /// @dev Receives a total and a percentage, returns the amount equivalent of the percentage
151: 		  function _totalToFraction(uint256 _total, uint256 _fraction) internal pure returns (uint256 _amount) {

// @audit missing @return
156: 		  /// @dev Doesn't revert but returns 0 so the vault contract doesn't revert on calling the claim function
157: 		  /// @dev Returns the claimable amount of AMPH, also the CVX and CRV the contract will take from the user
158: 		  function _claimable(

// @audit missing @return
193: 		  /// @dev Returns the rate of the token, denominated in 1e6
194: 		  function _getRate(uint256 _distributedAmph) internal pure returns (uint256 _rate) {

// @audit missing @return
201: 		  /// @dev Returns how much AMPH would be minted given a token amount
202: 		  function _calculate(uint256 _tokenAmountToSend) internal view returns (uint256 _amphAmount) {

// @audit missing @return
248: 		  /// @dev Returns the current cliff, it will round down but is on purpose
249: 		  function _getCliff(uint256 _distributedAmph) internal pure returns (uint256 _cliff) {

[150-151, 156-158, 193-194, 201-202, 248-249]

File: core/solidity/contracts/core/USDA.sol

// @audit missing @return
126: 		  /// @notice Withdraw sUSD by burning USDA
127: 		  /// @dev The caller should obtain 1 sUSD for every 1 USDA
128: 		  /// @dev This function is effectively just withdraw, but we calculate the amount for the sender
129: 		  /// @param _susdWithdrawn The amount os sUSD withdrawn
130: 		  function withdrawAll() external override returns (uint256 _susdWithdrawn) {

// @audit missing @return
136: 		  /// @notice Withdraw sUSD by burning USDA
137: 		  /// @dev This function is effectively just withdraw, but we calculate the amount for the _target
138: 		  /// @param _target should obtain 1 sUSD for every 1 USDA burned from caller
139: 		  /// @param _susdWithdrawn The amount os sUSD withdrawn
140: 		  function withdrawAllTo(address _target) external override returns (uint256 _susdWithdrawn) {

[126-130, 136-140]

File: core/solidity/contracts/governance/GovernorCharlie.sol

// @audit missing @return
713: 		  /// @notice Returns the timelock address
714: 		  /// @param _timelock The timelock address
715: 		  function timelock() external view override returns (address _timelock) {

[713-715]

File: core/solidity/contracts/periphery/oracles/ChainlinkStalePriceLib.sol

// @audit missing @return
10: 		  /// @notice Returns the current price from the aggregator
11: 		  function getCurrentPrice(AggregatorV2V3Interface _aggregator) internal view returns (uint256 _price) {

[10-11]

File: core/solidity/contracts/periphery/oracles/CurveRegistryUtils.sol

// @audit missing @return
11: 		  /// @notice Returns the lp address of the curve pool
12: 		  function _getLpAddress(address _crvPool) internal view returns (address _lpAddress) {

[11-12]

File: core/solidity/contracts/periphery/oracles/OracleRelay.sol

// @audit missing @return
23: 		  /// @dev Most oracles don't require a state change for pricing, for those who do, override this function
24: 		  function currentValue() external virtual returns (uint256 _currentValue) {

[23-24]

File: core/solidity/contracts/periphery/oracles/StableCurveLpOracle.sol

// @audit missing @return
40: 		  /// @notice Calculates the lastest exchange rate
41: 		  function _get() internal view returns (uint256 _value) {

// @audit missing @return
56: 		  /// @notice returns the updated virtual price for the pool
57: 		  function _getVirtualPrice() internal view virtual returns (uint256 _value) {

[40-41, 56-57]


Gas Optimizations


[GAS-01] Use custom errors instead of require/assert

Consider the use of a custom error, as it leads to a cheaper deploy cost and run time cost. The run time cost is only relevant when the revert condition is met.

There are 7 instances of this issue.

File: core/solidity/contracts/utils/UFragments.sol

52: 		    require(msg.sender == monetaryPolicy);

324: 		    require(block.timestamp <= _deadline);

333: 		    require(_owner == ecrecover(_digest, _v, _r, _s));

[52, 324, 333]

File: core