Farseen0/scar-pairs huggingface dataset:
HIGH severity: Anyone who is approving BlueprintV5 contract to spend ERC20 can get drained because payWithERC20. The `payWithERC20` function in the Payment contract is public, allowing any external caller to initiate token transfers from any address that has approved the contract. This function is intended to be used internally by the BlueprintV5 contract during agent creation, but its public visibility enable...

Vulnerable code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/interfaces/IERC721.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

using SafeERC20 for IERC20;

contract Payment {
    function checkNFTOwnership(address nftTokenAddress, uint256 nftId, address userAddress)
        public
        view
        returns (bool)
    {
        require(nftTokenAddress != address(0), "Invalid NFT token address");
        require(userAddress != address(0), "Invalid user address");

        IERC721 nftToken = IERC721(nftTokenAddress);
        return nftToken.ownerOf(nftId) == userAddress;
    }

    // This is to support gasless flow: normally, the caller must always be the msg.sender
    // slither-disable-next-line arbitrary-send-erc20
    function payWithERC20(address erc20TokenAddress, uint256 amount, address fromAddress, address toAddress) public {
        // check from and to address
        require(fromAddress != toAddress, "Cannot transfer to self address");
        require(toAddress != address(0), "Invalid to address");
        require(amount > 0, "Amount must be greater than 0");
        IERC20 token = IERC20(erc20TokenAddress);
        token.safeTransferFrom(fromAddress, toAddress, amount);
    }
}


Vulnerability type: CWE-284|CWE-285|CWE-862
MEDIUM severity: OETH Strategy fails to generate yield due to rebasing disabled. The vulnerability is a design flaw in the Origin ETH yield strategy, where OETH is used with rebasing disabled. The root cause is the direct use of OETH without opting into rebasing, as required by the Origin Protocol's design. Smart contracts holding OETH forfeit yield unless they explicitly opt in...

Vulnerable code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import {AbstractWithdrawRequestManager} from "./AbstractWithdrawRequestManager.sol";
import {WETH} from "../utils/Constants.sol";
import "../interfaces/IOrigin.sol";

contract OriginWithdrawRequestManager is AbstractWithdrawRequestManager {

    constructor() AbstractWithdrawRequestManager(address(WETH), address(oETH), address(WETH)) { }

    function _initiateWithdrawImpl(
        address /* account */,
        uint256 oETHToWithdraw,
        bytes calldata /* data */
    ) override internal returns (uint256 requestId) {
        ERC20(YIELD_TOKEN).approve(address(OriginVault), oETHToWithdraw);
        (requestId, ) = OriginVault.requestWithdrawal(oETHToWithdraw);
    }

    function _stakeTokens(uint256 amount, bytes memory stakeData) internal override {
        uint256 minAmountOut;
        if (stakeData.length > 0) (minAmountOut) = abi.decode(stakeData, (uint256));
        WETH.approve(address(OriginVault), amount);
        OriginVault.mint(address(WETH), amount, minAmountOut);
    }

    function _finalizeWithdrawImpl(
        address /* account */,
        uint256 requestId
    ) internal override returns (uint256 tokensClaimed, bool finalized) {
        finalized = canFinalizeWithdrawRequest(requestId);

        if (finalized) {
            uint256 balanceBefore = WETH.balanceOf(address(this));
            OriginVault.claimWithdrawal(requestId);
            tokensClaimed = WETH.balanceOf(address(this)) - balanceBefore;
        }
    }

    function canFinalizeWithdrawRequest(uint256 requestId) public view returns (bool) {
        IOriginVault.WithdrawalRequest memory request = OriginVault.withdrawalRequests(requestId);
        IOriginVault.WithdrawalQueueMetadata memory queue = OriginVault.withdrawalQueueMetadata();
        uint256 withdrawalClaimDelay = OriginVault.withdrawalClaimDelay();

        bool claimDelayMet = request.timestamp + withdrawalClaimDelay <= block.timestamp;
        bool queueLiquidityAvailable = request.queued <= queue.claimable;
        bool notClaimed = request.claimed == false;

        return claimDelayMet && queueLiquidityAvailable && notClaimed;
    }
}

Vulnerability type: CWE-435|CWE-436|CWE-437
HIGH severity: Reducing liquidity in the hardcoded Curve sDAI/sUSDe pool leads to unnecessary slippage loss. The protocol uses a hardcoded path (sUSDe -> sDAI -> asset) for swapping via the Curve sDAI/sUSDe pool (0x167478921b907422F8E88B43C4Af2B8BEa278d3A). Over time, liquidity in this pool has decreased as users migrate from sDAI to sUSDS under the new SKY protocol, where most incentives are now directed....

Vulnerable code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import "./PendlePT.sol";
import "../withdraws/Ethena.sol";
import "../interfaces/ITradingModule.sol";

contract PendlePT_sUSDe is PendlePT {
    using SafeERC20 for ERC20;

    constructor(
        address market,
        address tokenInSY,
        address tokenOutSY,
        address asset,
        address yieldToken,
        uint256 feeRate,
        IWithdrawRequestManager withdrawRequestManager
    ) PendlePT(market, tokenInSY, tokenOutSY, asset, yieldToken, feeRate, withdrawRequestManager) {
        require(tokenOutSY == address(sUSDe));
    }

    /// @notice The vast majority of the sUSDe liquidity is in an sDAI/sUSDe curve pool.
    /// sDAI has much greater liquidity once it is unwrapped as DAI so that is done manually
    /// in this method.
    function _executeInstantRedemption(
        uint256 yieldTokensToRedeem,
        bytes memory redeemData
    ) internal override virtual returns (uint256 assetsPurchased) {
        PendleRedeemParams memory params = abi.decode(redeemData, (PendleRedeemParams));
        uint256 netTokenOut = _redeemPT(yieldTokensToRedeem, params.limitOrderData);

        Trade memory sDAITrade = Trade({
            tradeType: TradeType.EXACT_IN_SINGLE,
            sellToken: address(sUSDe),
            buyToken: address(sDAI),
            amount: netTokenOut,
            limit: 0, // NOTE: no slippage guard is set here, it is enforced in the second leg
                        // of the trade.
            deadline: block.timestamp,
            exchangeData: abi.encode(CurveV2SingleData({
                pool: 0x167478921b907422F8E88B43C4Af2B8BEa278d3A,
                fromIndex: 1, // sUSDe
                toIndex: 0 // sDAI
            }))
        });

        (/* */, uint256 sDAIAmount) = _executeTrade(sDAITrade, uint16(DexId.CURVE_V2));

        // Unwraps the sDAI to DAI
        uint256 daiAmount = sDAI.redeem(sDAIAmount, address(this), address(this));
        
        if (asset != address(DAI)) {
            Trade memory trade = Trade({
                tradeType: TradeType.EXACT_IN_SINGLE,
                sellToken: address(DAI),
                buyToken: asset,
                amount: daiAmount,
                limit: params.minPurchaseAmount,
                deadline: block.timestamp,
                exchangeData: params.exchangeData
            });

            // Trades the unwrapped DAI back to the given token.
            (/* */, assetsPurchased) = _executeTrade(trade, params.dexId);
        } else {
            if (params.minPurchaseAmount > daiAmount) revert SlippageTooHigh(daiAmount, params.minPurchaseAmount);
            assetsPurchased = daiAmount;
        }
    }
}


Vulnerability type: CWE-703|CWE-754
MEDIUM severity: Anyone can overwrite factory mapping. The CompoundV3AdapterFactory stores deployed adapter addresses in a mapping keyed only by parentVault and comet, excluding the cometRewards parameter. This allows any user to deploy a new adapter with the same parentVault and comet but a different cometRewards address, overwriting the existing mappi...

Vulnerable code:
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 [Byzantine Finance]
// The implementation of this contract was inspired by Morpho Vault V2, developed by the Morpho Association in 2025.
pragma solidity 0.8.28;

import {CompoundV3Adapter} from "./CompoundV3Adapter.sol";
import {ICompoundV3AdapterFactory} from "./interfaces/ICompoundV3AdapterFactory.sol";

contract CompoundV3AdapterFactory is ICompoundV3AdapterFactory {
    /* STORAGE */

    mapping(address => mapping(address => address)) public compoundV3Adapter;
    mapping(address => bool) public isCompoundV3Adapter;

    /* FUNCTIONS */

    /// @dev Returns the address of the deployed CompoundV3Adapter.
    function createCompoundV3Adapter(address parentVault, address comet, address cometRewards)
        external
        returns (address)
    {
        address _compoundV3Adapter = address(new CompoundV3Adapter{salt: bytes32(0)}(parentVault, comet, cometRewards));
        compoundV3Adapter[parentVault][comet] = _compoundV3Adapter;
        isCompoundV3Adapter[_compoundV3Adapter] = true;
        emit CreateCompoundV3Adapter(parentVault, comet, cometRewards, _compoundV3Adapter);
        return _compoundV3Adapter;
    }
}


Vulnerability type: CWE-697|CWE-1023
HIGH severity: Truncated reserves bypass price safety mechanism when no interest accrues. The price safety mechanism in PriceMath.sol uses truncatedReserves to limit rapid price changes by gradually converging toward actual reserves. However, both updateInterests() and getCurrentState() bypass this mechanism when no interest accrual occurs, directly setting truncatedReserves to actual pa...

Vulnerable code:
// SPDX-License-Identifier: BUSL-1.1
// Likwid Contracts
pragma solidity ^0.8.26;

import {Math} from "./Math.sol";
import {SafeCast} from "./SafeCast.sol";
import {PerLibrary} from "./PerLibrary.sol";
import {Reserves, toReserves} from "../types/Reserves.sol";

library PriceMath {
    using SafeCast for *;
    using PerLibrary for *;

    function transferReserves(
        Reserves originReserves,
        Reserves destReserves,
        uint256 timeElapsed,
        uint24 maxPriceMovePerSecond
    ) internal pure returns (Reserves result) {
        if (destReserves.bothPositive()) {
            if (!originReserves.bothPositive()) {
                result = destReserves;
            } else {
                (uint256 truncatedReserve0, uint256 truncatedReserve1) = originReserves.reserves();
                uint256 priceMoved = maxPriceMovePerSecond * (timeElapsed ** 2);
                uint128 newTruncatedReserve0 = 0;
                uint128 newTruncatedReserve1 = destReserves.reserve1();
                uint256 _reserve0 = destReserves.reserve0();

                uint256 reserve0Min =
                    Math.mulDiv(newTruncatedReserve1, truncatedReserve0.lowerMillion(priceMoved), truncatedReserve1);
                uint256 reserve0Max =
                    Math.mulDiv(newTruncatedReserve1, truncatedReserve0.upperMillion(priceMoved), truncatedReserve1);
                if (_reserve0 < reserve0Min) {
                    newTruncatedReserve0 = reserve0Min.toUint128();
                } else if (_reserve0 > reserve0Max) {
                    newTruncatedReserve0 = reserve0Max.toUint128();
                } else {
                    newTruncatedReserve0 = _reserve0.toUint128();
                }
                result = toReserves(newTruncatedReserve0, newTruncatedReserve1);
            }
        } else {
            result = destReserves;
        }
    }
}


Vulnerability type: CWE-693|CWE-424
HIGH severity: Stuck Rebase Rewards Due to Internal Balance Tracking. The contract uses internal balance tracking via userSupply[token][msg.sender] += amount in the _deposit function of EnjoyoorsVaultDeposits, which only records the initial deposit amount. This design fails to account for rebasing tokens (e.g., stETH, aTokens) that automatically increase balances over...

Vulnerable code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.26;

import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

import '../access/Access.sol';
import '../../libraries/Asserts.sol';

/// @title Abstract contract with Enjoyoors vaults deposit functionality
abstract contract EnjoyoorsVaultDeposits is Access {
  using SafeERC20 for IERC20;
  using Asserts for uint256;

  /**
   * @notice Emitted in case of successful deposit call
   * @param token Token that was transferred into the vault
   * @param user User who performed deposit
   * @param amount Deposit amount
   */
  event Deposit(address indexed token, address indexed user, uint256 amount);

  /**
   * @notice Revert reason if deposit amount exceeds maximum token capitalization
   * @param tillLimit Max deposit allowed
   */
  error ExceedsLimit(uint256 tillLimit);
  /**
   * @notice Revert reason if deposit doesn't surpass minimal allowed deposit amount
   * @param minDeposit Minimal allowed deposit amount
   */
  error LessThanMinDeposit(uint256 minDeposit);

  /**
   * @notice Internal method with deposits implementation
   * @param token Token to deposit. Token must be listed in vault
   * @param amount Amount to deposit
   */
  function _deposit(address token, uint256 amount) internal onlyWhitelistedToken(token) whenDepositsNotPaused(token) {
    uint256 minAllowedDeposit = minDeposit[token];
    if (minAllowedDeposit == 0) amount.assertNotZeroAmount();
    if (amount < minAllowedDeposit) revert LessThanMinDeposit(minAllowedDeposit);

    uint256 tillLimit = supplyTillLimit[token];
    if (amount > tillLimit) revert ExceedsLimit(tillLimit);

    IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
    totalSupply[token] += amount;
    userSupply[token][msg.sender] += amount;
    supplyTillLimit[token] -= amount;

    emit Deposit(token, msg.sender, amount);
  }
}


Vulnerability type: CWE-664|CWE-221
MEDIUM severity: Incorrect Price Freshness Validation in PythOracle. The contract incorrectly validates the freshness of price data from the Pyth Oracle by using a flawed comparison logic in the `checkInRange` function. The root cause is a reversed comparison operator in the require statement, where it checks `price.publishTime < block.timestamp - noOlderThan` instea...

Vulnerable code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "../IPythRelay.sol";
import "../../interfaces/pyth/IPyth.sol";


contract PythOracle is IPythRelay {
    IPyth public immutable pythOracle;
    bytes32 public immutable tokenId;
    uint256 public immutable noOlderThan;
    address public immutable underlying;

    constructor(
        IPyth _pythOraclContract,
        bytes32 _tokenId,
        uint256 _noOlderThan,
        address _underlying
    ) {
        pythOracle = _pythOraclContract;
        tokenId = _tokenId;
        noOlderThan = _noOlderThan;
        underlying = _underlying;
    }

    function currentValue() external view override returns (uint256) {
        IPyth.Price memory price = pythOracle.getPriceUnsafe(tokenId);
        require(
            price.publishTime < block.timestamp - noOlderThan,
            "Stale Price"
        );
        return uint256(uint64(price.price));
    }

    function updatePrice(
        bytes[] calldata priceUpdate
    ) external payable override returns (uint256 updatedPrice) {
        // Submit a priceUpdate to the Pyth contract to update the on-chain price.
        // Updating the price requires paying the fee returned by getUpdateFee.
        // WARNING: These lines are required to ensure the getPriceNoOlderThan call below succeeds. If you remove them, transactions may fail with "0x19abf40e" error.
        uint fee = pythOracle.getUpdateFee(priceUpdate);
        pythOracle.updatePriceFeeds{value: fee}(priceUpdate);

        IPyth.Price memory price = pythOracle.getPriceNoOlderThan(
            tokenId,
            uint256(uint64(noOlderThan))
        );
        updatedPrice = uint256(uint64(price.price));
    }

    function getUpdateFee(bytes[] calldata priceUpdate) external view override returns (uint fee){
        return pythOracle.getUpdateFee(priceUpdate);
    }
}


Vulnerability type: CWE-697
MEDIUM severity: Incorrect condition for determining dust removal. The RemoteCustodian contract fails to remove dust from share amounts when the custodian token's decimals are less than or equal to six, due to a conditional check based on custodianTknDecimals() rather than the frxUSD token's decimals. This causes inconsistencies in cross-chain transfers where dust...

Vulnerable code:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { OFTUpgradeable } from "@fraxfinance/layerzero-v2-upgradeable/oapp/contracts/oft/OFTUpgradeable.sol";
import { SendParam } from "@fraxfinance/layerzero-v2-upgradeable/oapp/contracts/oft/interfaces/IOFT.sol";

import {EIP3009Module} from "contracts/modules/EIP3009Module.sol";
import {PermitModule} from "contracts/modules/PermitModule.sol";

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

contract SFrxUSDOFTUpgradeable is OFTUpgradeable, EIP3009Module, PermitModule {
    constructor(address _lzEndpoint) OFTUpgradeable(_lzEndpoint) {
        _disableInitializers();
    }

   function version() public pure returns (string memory) {
        return "1.1.0";
    }

    // Admin
    /// @dev This method is called specifically when upgrading an existing OFT
    function initializeV110() external reinitializer(3) {
        __EIP712_init(name(), version());
    }


    function name() public pure override returns (string memory) {
        return "Staked Frax USD";
    }

    function symbol() public pure override returns (string memory) {
        return "sfrxUSD";
    }

    // Helper views

    function toLD(uint64 _amountSD) external view returns (uint256 amountLD) {
        return _toLD(_amountSD);
    }

    function toSD(uint256 _amountLD) external view returns (uint64 amountSD) {
        return _toSD(_amountLD);
    }

    function removeDust(uint256 _amountLD) public view returns (uint256 amountLD) {
        return _removeDust(_amountLD);
    }

    function debitView(uint256 _amountLD, uint256 _minAmountLD, uint32 _dstEid) external view returns (uint256 amountSentLD, uint256 amountReceivedLD) {
        /// @dev: _dstEid is unused in _debitView
        return _debitView(_amountLD, _minAmountLD, _dstEid);
    }

    function buildMsgAndOptions(
        SendParam calldata _sendParam,
        uint256 _amountLD
    ) external view returns (bytes memory message, bytes memory options) {
        return _buildMsgAndOptions(_sendParam, _amountLD);
    }

    //==============================================================================
    // Overrides
    //==============================================================================

    /// @dev supports EIP3009
    function _transfer(address from, address to, uint256 amount) internal override(EIP3009Module, ERC20Upgradeable) {
        return ERC20Upgradeable._transfer(from, to, amount);
    }

    /// @dev supports EIP2612
    function _approve(address owner, address spender, uint256 amount) internal override(PermitModule, ERC20Upgradeable) {
        return ERC20Upgradeable._approve(owner, spender, amount);
    }
}


Vulnerability type: CWE-682|CWE-1339
HIGH severity: Wrong capTokenDecimals value used in StakedCapAdapter.price causes inaccurate prices. The StakedCapAdapter library incorrectly uses the decimal count (e.g., 18) instead of the corresponding scaling factor (10^18) in price calculations, leading to wildly inaccurate prices. The root cause is a fundamental misunderstanding of how decimal scaling works in the price function: it passes th...

Vulnerable code:
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;

import { IOracle } from "../../interfaces/IOracle.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @title Staked Cap Token Adapter
/// @notice Prices are calculated based on the underlying cap token price and accrued yield
library StakedCapAdapter {
    /// @notice Fetch price for a staked cap token
    /// @param _asset Staked cap token address
    /// @return latestAnswer Price of the staked cap token fixed to 8 decimals
    /// @return lastUpdated Latest timestamp of the price
    function price(address _asset) external view returns (uint256 latestAnswer, uint256 lastUpdated) {
        address capToken = IERC4626(_asset).asset();
        (latestAnswer, lastUpdated) = IOracle(msg.sender).getPrice(capToken);
        uint256 capTokenDecimals = IERC20Metadata(capToken).decimals();
        uint256 pricePerFullShare = IERC4626(_asset).convertToAssets(capTokenDecimals);
        latestAnswer = latestAnswer * pricePerFullShare / capTokenDecimals;
    }
}


Vulnerability type: CWE-682
MEDIUM severity: Unchecked Return Value of `transfer` Call. The `sweepErc20` function in the `SwapHandler` contract calls `transfer` on an ERC-20 token but does not check its boolean return value. Some ERC-20 tokens return `false` on failure instead of reverting, which would cause the transfer to fail silently while the transaction succeeds.
The root cause i...

Vulnerable code:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { HyperCoreLib } from "../../libraries/HyperCoreLib.sol";
import { FinalTokenInfo } from "./Structs.sol";

contract SwapHandler {
    address public immutable parentHandler;

    constructor() {
        parentHandler = msg.sender;
    }

    modifier onlyParentHandler() {
        require(msg.sender == parentHandler, "Not parent handler");
        _;
    }

    function activateCoreAccount(
        address erc20EVMAddress,
        uint64 erc20CoreIndex,
        uint256 amountEVM,
        int8 decimalDiff
    ) external onlyParentHandler {
        HyperCoreLib.transferERC20EVMToSelfOnCore(erc20EVMAddress, erc20CoreIndex, amountEVM, decimalDiff);
    }

    function transferFundsToSelfOnCore(
        address erc20EVMAddress,
        uint64 erc20CoreIndex,
        uint256 amountEVM,
        int8 decimalDiff
    ) external onlyParentHandler {
        HyperCoreLib.transferERC20EVMToSelfOnCore(erc20EVMAddress, erc20CoreIndex, amountEVM, decimalDiff);
    }

    function transferFundsToUserOnCore(
        uint64 erc20CoreIndex,
        address to,
        uint64 amountCore
    ) external onlyParentHandler {
        HyperCoreLib.transferERC20CoreToCore(erc20CoreIndex, to, amountCore);
    }

    function submitLimitOrder(
        FinalTokenInfo memory finalTokenInfo,
        uint64 limitPriceX1e8,
        uint64 sizeX1e8,
        uint128 cloid
    ) external onlyParentHandler {
        HyperCoreLib.submitLimitOrder(
            finalTokenInfo.assetIndex,
            finalTokenInfo.isBuy,
            limitPriceX1e8,
            sizeX1e8,
            false,
            HyperCoreLib.Tif.GTC,
            cloid
        );
    }

    function cancelOrderByCloid(uint32 assetIndex, uint128 cloid) external onlyParentHandler {
        HyperCoreLib.cancelOrderByCloid(assetIndex, cloid);
    }

    function sweepErc20(address token, uint256 amount) external onlyParentHandler {
        IERC20(token).transfer(msg.sender, amount);
    }
}


Vulnerability type: CWE-703|CWE-754|CWE-252