Farseen0/scar-pairs huggingface dataset:
LOW severity: [LOGLAB-7] Missing Asset/Product Check When Setting New Strategy. **Severity:** Low

**Path:** src/vault/LogarithmVault.sol#L232-L248

**Description:** The `setStrategy` function in the vault allows setting a new strategy without asserting the new asset/product stay unchanged. This means a new strategy can be applied to the vault without validating if its asset...

Vulnerable code:
function setStrategy(address _strategy) external onlyOwner {
    LogarithmVaultStorage storage $ = _getLogarithmVaultStorage();

    IERC20 _asset = IERC20(asset());
    address prevStrategy = strategy();

    if (prevStrategy != address(0)) {
        IStrategy(prevStrategy).stop();
        _asset.approve(prevStrategy, 0);
    }

    require(_strategy != address(0));
    $.strategy = _strategy;
    _asset.approve(_strategy, type(uint256).max);

    emit StrategyUpdated(_msgSender(), _strategy);
}
LOW severity: [LOGLAB-4] Invalidation of setLimitDecreaseCollateral Validation Logic when setting new setCollateralMinMax. **Severity:** Low

**Path:** src/hedge/offchain/OffChainConfig.sol#L63-L79 

**Description:** `limitDecreaseCollateral()`  is designed to optimize gas usage and cost efficiency by enforcing a minimum threshold for decreasing collateral.  `decreaseCollateralMinMax()` sets the allowable minimum and...

Vulnerable code:
function setLimitDecreaseCollateral(uint256 limit) external onlyOwner {
        OffChainConfigStorage storage $ = _getOffChainConfigStorage();
        require(limit > $.decreaseCollateralMinMax[0]);
        $.limitDecreaseCollateral = limit;
    }

function setCollateralMinMax(
        uint256 increaseCollateralMin,
        uint256 increaseCollateralMax,
        uint256 decreaseCollateralMin,
        uint256 decreaseCollateralMax
    ) external onlyOwner {
        require(increaseCollateralMin < increaseCollateralMax && decreaseCollateralMin < decreaseCollateralMax);
        OffChainConfigStorage storage $ = _getOffChainConfigStorage();
        $.increaseCollateralMinMax = [increaseCollateralMin, increaseCollateralMax];
        $.decreaseCollateralMinMax = [decreaseCollateralMin, decreaseCollateralMax];
    }

function setCollateralMinMax(
    uint256 increaseCollateralMin,
    uint256 increaseCollateralMax,
    uint256 decreaseCollateralMin,
    uint256 decreaseCollateralMax
) external onlyOwner {
    require(increaseCollateralMin < increaseCollateralMax && decreaseCollateralMin < decreaseCollateralMax);
    OffChainConfigStorage storage $ = _getOffChainConfigStorage();
    $.increaseCollateralMinMax = [increaseCollateralMin, increaseCollateralMax];
    $.decreaseCollateralMinMax = [decreaseCollateralMin, decreaseCollateralMax];
}


function setLimitDecreaseCollateral(uint256 limit) external onlyOwner {
    OffChainConfigStorage storage $ = _getOffChainConfigStorage();
    require(limit > $.decreaseCollateralMinMax[0]);
    $.limitDecreaseCollateral = limit;
}
LOW severity: [LOGLAB-1] Missing disableinitializers() to prevent uninitialized contracts. **Severity:** Low

**Description:** All contracts in the project that use OpenZeppelin’s `Initializable` don't call `_disableInitializers` in the constructor per the OpenZeppelin documentation:

[Writing Upgradeable Contracts - OpenZeppelin...

Vulnerable code:
function initialize(
        address _config,
        address _product,
        address _vault,
        address _oracle,
        address _operator,
        uint256 _targetLeverage,
        uint256 _minLeverage,
        uint256 _maxLeverage,
        uint256 _safeMarginLeverage
@>  ) external initializer {
        __Ownable_init(_msgSender());
        
  [.....]

constructor() {
	_disableInitializers();
}
LOW severity: [LOGLAB-20] `BasisStrategy::_afterIncreasePosition` may send asset to vault without `LogarithmVault.processingPendingWithdrawRequests`. **Severity:** Low

**Description:** The exit fee is calculated based on the amount of assets to be deutilized for the redeem/withdraw. It means only the asset amount above the `idleAssets` is subject to exit fee.

Also note that if asset is sent to the vault, it should call the...

Vulnerable code:
$.asset.safeTransferFrom(hedgeManager(), vault(), uint256(-collateralDeviation));

function maxWithdraw(address owner) public view virtual override returns (uint256) {
        if (paused()) {
            return 0;
        }
        uint256 assets = super.maxWithdraw(owner);
        uint256 withdrawableAssets = idleAssets();
        return assets > withdrawableAssets ? withdrawableAssets : assets;
    }

// BasisStrategy::_afterIncreasePosition
if (requestParams.collateralDeltaAmount > 0) {
  (bool exceedsThreshold, int256 collateralDeviation) = _checkDeviation(
      responseParams.collateralDeltaAmount, requestParams.collateralDeltaAmount, _responseDeviationThreshold
  );
  if (exceedsThreshold) {
      if (collateralDeviation < 0) {
          shouldPause = true;
          $.asset.safeTransferFrom(hedgeManager(), vault(), uint256(-collateralDeviation));
      }
  }
LOW severity: [LOGLAB-2] Constant variables should be marked as private. **Severity:** Informational

**Path:** src/vault/ManagedVault.sol#L27-L30, src/oracle/LogarithmOracle.sol#L18

**Description:** In the following locations, there are constant variables that are declared `public`. However, setting these constants to `private` will save deployment gas. This is...

Vulnerable code:
/// @notice The maximum value of management fee that can be configured.
    uint256 public constant MAX_MANAGEMENT_FEE = 5e16; // 5%
    /// @notice The maximum value of performance fee that can be configured.
    uint256 public constant MAX_PERFORMANCE_FEE = 5e17; // 50%

uint256 public constant FLOAT_PRECISION = 1e30;
HIGH severity: [RUS8] Utilisation ratio can exceed 100% to inflate fees and exploit the sponsor. **Severity:** High

**Path:** FeeCalculator.sol:calculateFee#L55-L84

**Description:** The `calculateFee` function calculates the required fee for RushERC20 deployment by taking the liquidity amount, duration and utilisation ratio into account.

However, it receives the snapshotted total assets of.....

Vulnerable code:
function calculateFee(FC.CalculateFeeParams calldata params)
        external
        view
        override
        returns (uint256 totalFee, uint256 reserveFee)
    {
        FC.CalculateFeeLocalVars memory vars;

        vars.feeRate = BASE_FEE_RATE;
        vars.utilizationRatio =
            Math.mulDiv(params.outstandingLiquidity + params.newLiquidity, 1e18, params.totalLiquidity); 
        if (vars.utilizationRatio > OPTIMAL_UTILIZATION_RATIO) {
            // If U > U_optimal, formula is:
            //                                                        U - U_optimal
            // R_fee = BASE_FEE_RATE + RATE_SLOPE_1 + RATE_SLOPE_2 * ----------------
            //                                                        1 - U_optimal
            vars.feeRate += RATE_SLOPE_1
                + Math.mulDiv(RATE_SLOPE_2, vars.utilizationRatio - OPTIMAL_UTILIZATION_RATIO, MAX_EXCESS_UTILIZATION_RATIO);
        } else {
            // Else, formula is:
            //                                             U
            // R_fee = BASE_FEE_RATE + RATE_SLOPE_1 *  -----------
            //                                          U_optimal
            vars.feeRate += Math.mulDiv(RATE_SLOPE_1, vars.utilizationRatio, OPTIMAL_UTILIZATION_RATIO);
        }

        totalFee = Math.mulDiv(vars.feeRate * params.duration, params.newLiquidity, 1e18);
        reserveFee = Math.mulDiv(totalFee, params.reserveFactor, 1e18);
    }
HIGH severity: [RUS4] Lack of slippage protection in withdraw functions. **Severity:** Medium

**Path:** src/periphery/RushRouter.sol, src/periphery/RushRouterAlpha.sol

**Description:** The `withdraw()` and `withdrawETH()` functions both call `redeem()` on the LiquidityPool. However, these functions don't have a slippage protection feature, meaning the depositor may...

Vulnerable code:
function withdraw(uint256 amount) external {
        // Calculate the amount of shares to redeem.
        uint256 shares = LIQUIDITY_POOL.previewWithdraw(amount);

        // Transfer the amount of shares to this contract.
        IERC20(LIQUIDITY_POOL).transferFrom({ from: msg.sender, to: address(this), value: shares });

        // Redeem the amount of shares from the LiquidityPool and transfer the corresponding amount of WETH to the
        // sender.
        LIQUIDITY_POOL.redeem({ shares: shares, receiver: msg.sender, owner: address(this) });
    }

    /**
     * @notice Withdraw lent ETH from the LiquidityPool.
     * @param amount The amount of ETH to withdraw.
     */
    function withdrawETH(uint256 amount) external {
        // Calculate the amount of shares to redeem.
        uint256 shares = LIQUIDITY_POOL.previewWithdraw(amount);

        // Transfer the amount of shares to this contract.
        IERC20(LIQUIDITY_POOL).transferFrom({ from: msg.sender, to: address(this), value: shares });

        // Redeem the amount of shares from the LiquidityPool and transfer the corresponding amount of WETH to this
        // contract.
        uint256 received = LIQUIDITY_POOL.redeem({ shares: shares, receiver: address(this), owner: address(this) });

        // Withdraw the received WETH to ETH.
        IWETH(WETH).withdraw(received);

        // Transfer the amount of WETH to the sender.
        payable(msg.sender).transfer(received);
    }
HIGH severity: [RUS1] Rewards in StakingRewards are lost if total supply is zero. **Severity:** Low

**Path:** StakingRewards.sol:rewardPerToken#L90-L96

**Description:** The function rewardPerToken calculates the current amount of reward tokens per share inside of the StakingRewards. It is calculated using the initially set rewardRate and the delta time of...

Vulnerable code:
function rewardPerToken() public view override returns (uint256) {
    if (totalSupply == 0) {
        return rewardPerTokenStored;
    }
    return rewardPerTokenStored
        + Math.mulDiv((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate, 1e18, totalSupply);
}
HIGH severity: [RUS6] _getIsUnwindThresholdMet should include subsidyAmount for threshold calculation. **Severity:** Low

**Description:** To unwind liquidity before the deadline, the current reserves must exceed the EARLY_UNWIND_THRESHOLD. This threshold should also include the subsidyAmount, since the initialWETHReserve is the sum of deployment.amount and...

Vulnerable code:
function _getIsUnwindThresholdMet(address uniV2Pair) internal view returns (bool isUnwindThresholdMet) {
        LD.LiquidityDeployment storage deployment = _liquidityDeployments[uniV2Pair];
        (uint256 currentReserve,,) = _getOrderedReserves(uniV2Pair);
        uint256 targetReserve = deployment.amount + EARLY_UNWIND_THRESHOLD;
        isUnwindThresholdMet = currentReserve >= targetReserve;
    }

function _getIsUnwindThresholdMet(address uniV2Pair) internal view returns (bool isUnwindThresholdMet) {
        LD.LiquidityDeployment storage deployment = _liquidityDeployments[uniV2Pair];
        (uint256 currentReserve,,) = _getOrderedReserves(uniV2Pair);
--      uint256 targetReserve = deployment.amount + EARLY_UNWIND_THRESHOLD;
++      uint256 targetReserve = deployment.amount + deployment.subsidyAmount EARLY_UNWIND_THRESHOLD;
        isUnwindThresholdMet = currentReserve >= targetReserve;
    }
HIGH severity: [LYSWP2-7] The LP can steal the user's funds by initiating a refund before the user executes the redeem function. **Severity:** High

**Path:** chains/evm/solidity/contracts/HashedTimeLockERC20.sol#L172-L176 

**Description:** The timelock for all user and LP locks is enforced to be at least 15 minutes. However, LPs can exploit this by setting their lock duration to the minimum (15 minutes). This is because...

Vulnerable code:
/// @dev Modifier to ensure the provided timelock is at least 15 minutes in the future.
  modifier _validTimelock(uint48 timelock) {
    if (block.timestamp + 900 > timelock) revert InvalidTimelock();
    _;
  }
HIGH severity: [LYSWP2-6] Discrepancy between the actual locked and stored amount of tokens when using fee-on-transfer tokens. **Severity:** Medium

**Path:** chains/evm/solidity/contracts/HashedTimeLockERC20.sol#L337-L350

**Description:** To bridge the funds the user needs to lock their funds on one chain to later unlock their funds on another chain. To do so the user can call the `lock()` function in the...

Vulnerable code:
if (token.balanceOf(msg.sender) < amount + reward) revert InsufficientBalance();
if (token.allowance(msg.sender, address(this)) < amount + reward) revert NoAllowance();

token.safeTransferFrom(msg.sender, address(this), amount + reward);
contracts[Id] = HTLC(
  amount,
  hashlock,
  uint256(1),
  tokenContract,
  timelock,
  uint8(1),
  payable(msg.sender),
  payable(srcReceiver)
);
HIGH severity: [LYSWP2-8] Unchecked ERtransfer return value may cause HTLC inconsistency across chains. **Severity:** Medium

**Path:** chains/starknet/src/HashTimeLockedERC20.cairo#L391-L464, HashTimeLockedERC20.cairo#L339, HashTimeLockedERC20.cairo#L423

**Description:** The contract does not check the return value of `transferFrom()` and `transfer()` in multiple places, assuming that all ERC20...

Vulnerable code:
fn commit(
            ref self: ContractState,
            Id: u256,
            amount: u256,
            sender_key: felt252,
            dstChain: felt252,
            dstAsset: felt252,
            dstAddress: ByteArray,
            srcAsset: felt252,
            srcReceiver: ContractAddress,
            timelock: u64,
            tokenContract: ContractAddress,
        ) -> u256 {
            //Check that the ID is unique
            assert!(!self.hasHTLC(Id), "Commitment Already Exists");
            assert!(self.validTimelock(timelock), "Invalid TimeLock");
            assert!(amount != 0, "Funds Can Not Be Zero");

            // transfer the token from the user into the contract
            let token: IERC20Dispatcher = IERC20Dispatcher { contract_address: tokenContract };
            assert!(token.balance_of(get_caller_address()) >= amount, "Insufficient Balance");
            assert!(
                token.allowance(get_caller_address(), get_contract_address()) >= amount,
                "Not Enough Allowence"
            );
            token.transfer_from(get_caller_address(), get_contract_address(), amount);

            //Write the PreHTLC data into the storage
            self
                .contracts
                .write(
                    Id,
                    HTLC {
                        amount: amount,
                        hashlock: 0,
                        secret: 0,
                        tokenContract: tokenContract,
                        timelock: timelock,
                        claimed: 1,
                        sender: get_caller_address(),
                        sender_key: sender_key,
                        srcReceiver: srcReceiver,
                    }
                );

            let hop_chains = array!['null'].span();
            let hop_assets = array!['null'].span();
            let hop_addresses = array!['null'].span();

            self
                .emit(
                    TokenCommitted {
                        Id: Id,
                        hopChains: hop_chains,
                        hopAssets: hop_assets,
                        hopAddress: hop_addresses,
                        dstChain: dstChain,
                        dstAddress: dstAddress,
                        dstAsset: dstAsset,
                        sender: get_caller_address(),
                        srcReceiver: srcReceiver,
                        srcAsset: srcAsset,
                        amount: amount,
                        timelock: timelock,
                        tokenContract: tokenContract,
                    }
                );
            Id
        }
HIGH severity: [LYSWP2-13] Id should be checked by the LP before redeeming. **Severity:** Low

**Path:** chains/evm/solidity/contracts/HashedTimeLockERC20.sol#L253-L272 

**Description:** When a user calls the `addLock()` function, an event containing the HTLC ID, hashlock, and timelock is emitted. The Solver implementation documentation recommends verifying the...

Vulnerable code:
function addLock(
    bytes32 Id,
    bytes32 hashlock,
    uint48 timelock
  ) external _exists(Id) _validTimelock(timelock) nonReentrant returns (bytes32) {
    HTLC storage htlc = contracts[Id];
    if (htlc.claimed == 2 || htlc.claimed == 3) revert AlreadyClaimed();
    if (msg.sender == htlc.sender) {
      if (htlc.hashlock == bytes32(bytes1(0x01))) {
        htlc.hashlock = hashlock;
        htlc.timelock = timelock;
      } else {
        revert HashlockAlreadySet(); // Prevent overwriting hashlock.
      }
      emit TokenLockAdded(Id, hashlock, timelock);
      return Id;
    } else {
      revert NoAllowance(); // Ensure only allowed accounts can add a lock.
    }
  }
HIGH severity: [ETRP-3] Missing BabyJubJub Base Point Subgroup order check constraints on scalar numbers. **Severity:** Critical

**Description:** The application leverages Gnark circuits to validate the correctness of operations such as Mint, Transfer, Withdraw, and Registration. These circuits are compiled using the BN254 elliptic curve.

The scalar field represents the set of scalars used in...

Vulnerable code:
a = 168700
d = 168696

prime_finite_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617
curve_order = 21888242871839275222246405745257275088614511777268538073601725287587578984328
base_point_order = 2736030358979909402780800718157159386076813972158567259200215660948447373041

base_point_x = 5299619240641551281634865583518297030282874472190772894086521144482721001553
base_point_y = 16950150798460657717958625567821834550301663161624707787222815936182638968203

scalar_number * BasePoint == (scalar_number + base_point_order) * BasePoint

/*
CheckBalance checks if the sender's balance is a well-formed ElGamal ciphertext by decryption
*/
func CheckBalance(api frontend.API, bj *babyjub.BjWrapper, sender Sender) {
	decSenderBalanceP := bj.ElGamalDecrypt([2]frontend.Variable{sender.BalanceEGCT.C1.X, sender.BalanceEGCT.C1.Y}, [2]frontend.Variable{sender.BalanceEGCT.C2.X, sender.BalanceEGCT.C2.Y}, sender.PrivateKey)
	givenSenderBalanceP := bj.MulWithBasePoint(sender.Balance)
	bj.AssertPoint(givenSenderBalanceP, decSenderBalanceP.X, decSenderBalanceP.Y)
}
HIGH severity: [ETRP-1] Unsafe usage of transfer/transferFrom in EncryptedERC deposit/withdraw methods. **Severity:** Medium

**Description:** The `EncryptedERC` token enables private transfers by utilizing partially homomorphic encryption, ensuring confidentiality during transactions. Moreover, it provides an opportunity for other ERC-20 tokens to be transferred securely in an encrypted manner. To...

Vulnerable code:
function deposit(
    uint256 _amount,
    address _tokenAddress,
    uint256[7] memory _amountPCT
) public {
    ...
    IERC20 token = IERC20(_tokenAddress);
    uint256 dust;
    uint256 tokenId;
    address to = msg.sender;
    ...
    // this function reverts if the transfer fails
    token.transferFrom(to, address(this), _amount);

    (dust, tokenId) = _convertFrom(to, _amount, _tokenAddress, _amountPCT);

    // transfer the dust back to the user
    token.transfer(to, dust);

    emit Deposit(to, _amount, dust, tokenId);
}

function _convertFrom(
    address _to,
    uint256 _amount,
    address _tokenAddress,
    uint256[7] memory _amountPCT
) internal returns (uint256 dust, uint256 tokenId) {
    uint8 tokenDecimals = IERC20Metadata(_tokenAddress).decimals();

    uint256 value = _amount;
    dust = 0;
    ...
    {
        uint256[2] memory publicKey = registrar.getUserPublicKey(_to);

        EGCT memory _eGCT = BabyJubJub.encrypt(
            Point({X: publicKey[0], Y: publicKey[1]}),
            value
        );

        EncryptedBalance storage balance = balances[_to][tokenId];

        if (balance.eGCT.c1.X == 0 && balance.eGCT.c1.Y == 0) {
            balance.eGCT = _eGCT;
        } else {
            balance.eGCT.c1 = BabyJubJub._add(balance.eGCT.c1, _eGCT.c1);
            balance.eGCT.c2 = BabyJubJub._add(balance.eGCT.c2, _eGCT.c2);
        }

        balance.amountPCTs.push(
            AmountPCT({pct: _amountPCT, index: balance.transactionIndex})
        );
        balance.transactionIndex++;

        _commitUserBalance(_to, tokenId);
    }

    return (dust, tokenId);
}
LOW severity: [ETRP-5] Discrepancy between clear text and amount PCT when depositing fee on transfer tokens. **Severity:** Medium

**Path:** contracts/EncryptedERC.sol#L472-L506

**Description:** The `EncryptedERC.sol` contract will not work properly with a fee on transfer tokens, as when depositing such one via the `deposit()` function it will transfer to the contract the `_amount`:

but the balance of...

Vulnerable code:
// this function reverts if the transfer fails
        token.transferFrom(to, address(this), _amount);

EGCT memory _eGCT = BabyJubJub.encrypt(
                Point({X: publicKey[0], Y: publicKey[1]}),
                value
            );

function deposit(
        uint256 _amount,
        address _tokenAddress,
        uint256[7] memory _amountPCT
    ) public {
        // revert if auditor key is not set
        if (!isAuditorKeySet()) {
            revert AuditorKeyNotSet();
        }

        // revert if contract is not a converter
        if (!isConverter) {
            revert InvalidOperation();
        }

        IERC20 token = IERC20(_tokenAddress);
        uint256 dust;
        uint256 tokenId;
        address to = msg.sender;

        // revert if the user is not registered to registrar contract
        if (!registrar.isUserRegistered(to)) {
            revert UserNotRegistered();
        }

        // this function reverts if the transfer fails
        token.transferFrom(to, address(this), _amount);

        (dust, tokenId) = _convertFrom(to, _amount, _tokenAddress, _amountPCT);

        // transfer the dust back to the user
        token.transfer(to, dust);

        emit Deposit(to, _amount, dust, tokenId);
    }
LOW severity: [ETRP-12] Proof Reuse and Malleability in privateMint Function Bypasses Auditor Controls in privateMint. **Severity:** Low

**Path:** ava-labs/EncryptedERC/contracts/EncryptedERC.sol

**Description:** The `privateMint` function in the EncryptedERC contract, despite being restricted to the owner, allows the reuse of zero-knowledge proofs which affects the intended auditor control mechanism. While the...

Vulnerable code:
function privateMint(
        address _user,
        uint256[8] calldata proof,
        uint256[22] calldata input
    ) external onlyOwner {
        if (isConverter) {
            revert InvalidOperation();
        }

        if (!isAuditorKeySet()) {
            revert AuditorKeyNotSet();
        }

        if (!registrar.isUserRegistered(_user)) {
            revert UserNotRegistered();
        }

        {
            // user public key should match
            uint256[2] memory userPublicKey = registrar.getUserPublicKey(_user);
            if (userPublicKey[0] != input[0] || userPublicKey[1] != input[1]) {
                revert InvalidProof();
            }
        }

        {
            // auditor public key should match
            if (
                auditorPublicKey.X != input[13] ||
                auditorPublicKey.Y != input[14]
            ) {
                revert InvalidProof();
            }
        }

        mintVerifier.verifyProof(proof, input);
        _privateMint(_user, input);
    }