Farseen0/scar-pairs huggingface dataset:
LOW severity: Readme Typos / Small Fixes. **75% as SP incentive**

The code uses 72%

**Code says 72%**
https://github.com/liquity/bold/blob/c84585881d8c6a5d11d38deee2943ba949a39962/README.md#L492

**fn name has changeds**

https://github.com/liquity/bold/blob/c84585881d8c6a5d11d38deee2943ba949a39962/README.md#L316

https://github.com/liqui...

Vulnerable code:
- The pending aggregate interest is minted by the ActivePool as fresh BOLD. This is considered system “yield”.  A fixed part (75%) of it is immediately sent to the branch’s SP and split proportionally between depositors, and the remainder is sent to a router to be used as LP incentives on DEXes (determined by governance).

- `setRemoveManager(uint256 _troveId, address _manager, address _receiver)`: sets a “Remove” manager for the caller’s chosen Trove, who has permission to remove collateral from and draw new BOLD from their Trove to the provided `_receiver` address.

- `setInterestIndividualDelegate(
        uint256 _troveId,
        address _delegate,
        uint128 _minInterestRate,
        uint128 _maxInterestRate,
        uint256 _newAnnualInterestRate,
        uint256 _upperHint,
        uint256 _lowerHint,
        uint256 _maxUpfrontFee
    )`: the Trove owner sets an individual delegate who will have permission to update the interest rate for that Trove in range `[ _minInterestRate,  _minInterestRate]`
HIGH severity: Anyone Can Burn Any NFTs of Other Users in the `Material`, `UGC` and `Craft` Objects. **Severity**

High Risk

**Description**

The `MaterialObject` and `CraftObject` contracts represent distinct digital assets and the corresponding materials required for their creation, respectively. These assets can take various forms, such as in-game items or characters.

The same principle...

Vulnerable code:
// Function to burn an object
function burnObject(address from, uint256 tokenid, uint256 amount) external override {
    super._burn(from, tokenid, amount);
}

// Function to burn a batch of objects
function burnBatchObject(address from, uint256[] memory tokenids, uint256[] memory amounts) external override {
    super._burnBatch(from, tokenids, amounts);
}

function burnObject(uint256 tokenid, uint256 amount) external override {
    super._burn(msg.sender, tokenid, amount);
}

function burnObject(address from, uint256 tokenid, uint256 amount) external override onlyCraftLogic {
    super._burn(from, tokenid, amount);
}
MEDIUM severity: Signature Does Not Contain a Deadline, Making It Reusable. **Severity**

Medium Risk

**Description**

The `PhiDaily.sol` contract has the `claimMaterialObject` function that allow a user to claim a material object directly. These then call the private `_processClaim()` function to check that the coupon has been signed by the admin signer.
The `digest`...

Vulnerable code:
// Function to claim a material object.
function claimMaterialObject(
    uint32 eventid,
    uint16 logicid,
    Coupon memory coupon
)
    external
    onlyIfAlreadyClaimed(eventid, logicid)
    nonReentrant
{

function _processClaim(uint32 eventid, uint16 logicid, Coupon memory coupon) private {
    // Check that the coupon sent was signed by the admin signer
    bytes32 digest = keccak256(abi.encode(eventid, logicid, _msgSender()));

function claimMaterialObject(
        uint32 eventid,
        uint16 logicid,
++      uint256 expiresIn
        Coupon memory coupon
    )
        external
        onlyIfAlreadyClaimed(eventid, logicid)
        nonReentrant
    {
++     if (expiresIn <= block.timestamp){
++        revert SignatureExpired()
++     }
        _processClaim(eventid, logicid, coupon, expiresIn);
    }

    function _processClaim(uint32 eventid, uint16 logicid, Coupon memory coupon, expiresIn) private {
    // Check that the coupon sent was signed by the admin signer
++   bytes32 digest = keccak256(abi.encode(eventid, logicid, _msgSender(), expiresIn));
MEDIUM severity: Insecure Generation of Randomness Used for Token Determination Logic. **Severity**

Medium Risk

**Description**

`EmissionLogic.sol` contains the `determineTokenByLogic()` function that determines the rarity and `tokenId` of a `MaterialObject`.
It generates a `uint256 random` value that relies on variables like `block.timestamp` and `tx.origin` as a source of...

Vulnerable code:
uint256 random = uint256(keccak256(abi.encodePacked(block.prevrandao, block.timestamp, tx.origin)));
MEDIUM severity: Centralization Risk in Multiple Places. **Severity**

Medium Risk

**Description**

For `setEmissionLogic` and `setMaterialObject` in `PhiDaily.sol`, it's documented that `emissionLogic` and `materialObject` should be contracts but in reality, the admin can set any address.

With `setTreasuryAddress`, `setMaxClaimed`, `setRoyalityFee`...

Vulnerable code:
function setEmissionLogic(address _emissionLogic) external onlyOwner {
function setMaterialObject(address _materialObject) external onlyOwner {

function setTreasuryAddress(address payable newTreasuryAddress) external onlyOwner {
function setMaxClaimed(uint256 tokenId, uint256 newMaxClaimed) public virtual onlyOwner {
function setRoyalityFee(uint256 newRoyalityFee) external onlyOwner {
function setSecondaryRoyalityFee(uint256 newSecondaryRoyalty) external onlyOwner {
LOW severity: Usage of `ecrecover` Should Be Replaced with Usage of OpenZeppelin's `ECDSA` Library. **Severity**

Low Risk

**Description**

[Signature malleability](https://swcregistry.io/docs/SWC-117) is one of the potential issues with `ecrecover`. The `_isVerifiedCoupon` function calls the Solidity `ecrecover()` function directly to verify the given signatures. However, the `ecrecover()`...

Vulnerable code:
address signer = ecrecover(digest, coupon.v, coupon.r, coupon.s);
LOW severity: Lack of Consistency and Misleading Naming in Checks. **Severity**

Low Risk

**Description**

There is a different check implementation in `onlyIfAlreadyClaimed` and `onlyIfAlreadyClaimedMultiple` modifiers for the same check. Additionally, both modifier names ensure that the execution will revert if the user hasn't claimed already but actually, it...

Vulnerable code:
if (dailyClaimedStatus[_msgSender()][eventid][logicid] > 0) {

if (dailyClaimedStatus[_msgSender()][eventids[i]][logicids[i]] == _CLAIMED) {
HIGH severity: Description. The current implementation of the [FeeDistributor](https://github.com/leNFT/contracts/blob/master/contracts/protocol/FeeDistributor.sol) requires an always increasing balance for correct operation. This happens since the `_accountedFees[token]` mapping can never be decreased. It is only updated in.....

Vulnerable code:
function checkpoint(address token) external override {
    (...)
    // Find the current balance of the token in question
    uint256 balance = IERC20Upgradeable(token).balanceOf(address(this));

    // Add unaccounted fees to current epoch
    _epochFees[token][currentEpoch] += balance - _accountedFees[token];

    // Update total fees accounted for
    _accountedFees[token] = balance;
}
LOW severity: Description. The current implementation of the GaugeController has the following [function](https://github.com/leNFT/contracts/blob/master/contracts/protocol/Gauges/GaugeController.sol#L478-L491) to calculate the rewards ceiling per epoch:

The issue here is that the `inflationEpoch` is a memory variable...

Vulnerable code:
function getRewardsCeiling(uint256 epoch) public view returns (uint256) {
    uint256 inflationEpoch;
    // If we are in the loading period, return smaller rewards
    if (epoch < LOADING_PERIOD) {
        return (_initialRewards * epoch) / LOADING_PERIOD;
    } else if (inflationEpoch > MAX_INFLATION_PERIODS) {
        inflationEpoch = MAX_INFLATION_PERIODS;
    } else {
        inflationEpoch = epoch / INFLATION_PERIOD;
    }

    return
        (_initialRewards * (3 ** inflationEpoch)) / (4 ** inflationEpoch);
}
LOW severity: Description. The [documentation](https://lenft.gitbook.io/lenft-docs/genesis-mint) mentions that the Genesis Mint is limited to just 1337 tokens, which is corroborated by the [constant](https://github.com/leNFT/contracts/blob/master/contracts/protocol/GenesisNFT.sol#L42) set in the GenesisNFT contract:...

Vulnerable code:
contract testCap {

    uint256 constant MAX_CAP = 10;
    uint256 counter = 1;

    function mint(uint256 amount) external {
        require(counter + amount <= MAX_CAP, "G:M:CAP_EXCEEDED");
        
        for (uint256 i = 0; i < amount; i++) {
            // Mint
            counter ++;
        }
    }
}
LOW severity: Recommendation. We understand that sometimes saving variables from storage to memory is not possible due to the possibility of the "Stack too deep" error; nevertheless, this pattern should still be followed whenever possible. Below is an example implementation in TradingPool, where fee distributor is loaded twice.....

Vulnerable code:
address feeDistributor_ = _addressProvider.getFeeDistributor();
IERC20(_token).safeTransfer(feeDistributor_ , PercentageMath.percentMul(totalFee, protocolFeePercentage));
IFeeDistributor(feeDistributor_ ).checkpoint(_token);
HIGH severity: Description. At the moment, the Bribes contract implements the following logic to deposit and withdraw bribes:

Here, the issue is that the withdrawBribe function removes the bribe from the mapping `_userBribes` corresponding to the "msg.sender", but emits the event with the "receiver" address. This means the...

Vulnerable code:
function depositBribe(address briber, address token, address gauge, uint256 amount){

    _userBribes[token][gauge][nextEpoch][briber] += amount;

    IERC20Upgradeable(token).safeTransferFrom(msg.sender, address(this), amount);

    emit DepositBribe(briber, token, gauge, amount);
}

function withdrawBribe(address receiver, address token, address gauge, uint256 amount){

    _userBribes[token][gauge][nextEpoch][msg.sender] -= amount;

    IERC20Upgradeable(token).safeTransfer(receiver, amount);

    emit WithdrawBribe(receiver, token, gauge, amount);
}
LOW severity: Description. On the TradingPool contract, the buy() and sell() functions use the following logic for the protocol fee:

This logic is not optimized gas-wise, and will lead to mathematical rounding erros, which result in a sum of protocolFee's subtracted from the LPs slightly smaller than the total fee sent to...

Vulnerable code:
function buy()
    for (uint i = 0; i < nftIds.length; i++) {
        fee = spotPrice * fee;
        protocolFee = fee * protocolFeePercentage;

        tokenAmount += (spotPrice + fee - protocolFee);
        totalFee += fee;
    }
    protocoTotalFee = totalFee * protocolFeePercentage
}
LOW severity: Recommendation. Since totalFee is only used to compute the  protocoTotalFee, store the totalProtocolFee instead of totalFee to improve readability, save gas and protect against rounding errors, changing the above logic to:

Vulnerable code:
function buy()
    for (uint i = 0; i < nftIds.length; i++) {
        fee = spotPrice * fee;
        protocolFee = fee * protocolFeePercentage;

        tokenAmount += (spotPrice + fee - protocolFee);
        totalProtocolFee += protocolFee ;
    }
}
LOW severity: Description. Currently, the [InterestRateConfig](https://github.com/leNFT/contracts/blob/master/contracts/libraries/types/ConfigTypes.sol#L24) struct has the following variables:

These variables are either rates or slopes, with values in the order of magnitude of a few thousand on the test parameters. Since...

Vulnerable code:
struct InterestRateConfig {
    uint256 optimalUtilizationRate;
    uint256 baseBorrowRate;
    uint256 lowSlope;
    uint256 highSlope;
}
LOW severity: Description. The current implementation of the _checkpoint() function of the LendingGauge contract has the following [code](https://github.com/leNFT/contracts/blob/master/contracts/protocol/Gauges/LendingGauge.sol#L272-L277) to load the WorkingBalance struct from storage:

Since the oldWorkingBalance variable...

Vulnerable code:
DataTypes.WorkingBalance memory oldWorkingBalance;
if (_workingBalanceHistory[user].length > 0) {
    oldWorkingBalance = _workingBalanceHistory[user][
        _workingBalanceHistory[user].length - 1
    ];
}
LOW severity: Recommendation. Replace the above code with: 

And change variable `oldWorkingBalance.weight` to `oldWorkingBalanceWeight` on [line 288](https://github.com/leNFT/contracts/blob/master/contracts/protocol/Gauges/LendingGauge.sol#L288)

Vulnerable code:
uint256 oldWorkingBalanceWeight;
if (_workingBalanceHistory[user].length > 0) {
    oldWorkingBalanceWeight = _workingBalanceHistory[user][
        _workingBalanceHistory[user].length - 1].weight;
}
HIGH severity: Description. The genesis NFT contract, function _getCircleColor() has the [following statement](https://github.com/leNFT/contracts/blob/9b1ae95f97ec5d2423c022179642714cee470a68/contracts/protocol/GenesisNFT.sol#L243-L245):

Since `MAX_LOCKTIME` is a constant equal to 180 days it can never be zero.

Vulnerable code:
if (MAX_LOCKTIME == 0) {
    return "000000"; // return black
}
LOW severity: Description. The current implementation of the VotingEscrow has the [following function](https://github.com/leNFT/contracts/blob/master/contracts/protocol/VotingEscrow.sol#L323-L335):

Here, the ` require(index < _lockHistory[tokenId].length, "VE:GLHP:INDEX_TOO_HIGH");` isn't necessary since solidity...

Vulnerable code:
function getLockHistoryPoint(uint256 tokenId,uint256 index) public view returns (DataTypes.Point memory) {
    require(index < _lockHistory[tokenId].length, "VE:GLHP:INDEX_TOO_HIGH");
    return _lockHistory[tokenId][index];
}
HIGH severity: Description. The current implementation of the SwapRouter, function swap() has the following statement:

Here, the variable `change`, i.e., `sellPrice + priceDiff - buyPrice` is being computed twice.

Vulnerable code:
// If the price difference + sell price is greater than the buy price, return the difference to the user
if (sellPrice + priceDiff > buyPrice) {
    IERC20(sellPoolToken).safeTransfer(
        msg.sender,
        sellPrice + priceDiff - buyPrice
    );
    change = sellPrice + priceDiff - buyPrice;
}
HIGH severity: Recommendation. Change the statement to the following to improve readability and improve gas usage:

Vulnerable code:
// If the price difference + sell price is greater than the buy price, return the difference to the user
if (sellPrice + priceDiff > buyPrice) {
    change = sellPrice + priceDiff - buyPrice;
    IERC20(sellPoolToken).safeTransfer(
        msg.sender,
        change
    );
}
HIGH severity: TRST-Linear vesting users may not receive vested amount. **Description:**
TokenTransmuter supports two types of transmutations, linear and instant. In linear, 
allocated amount is released across time until fully vested, while in instant the entire 
amount is released immediately. **transmuteLinear()** checks that there is enough output 
tokens left in...

Vulnerable code:
require(IERC20(outputTokenAddress).balanceOf(address(this)) >= 
            (totalAllocatedOutputToken - totalReleasedOutputToken), 
        "INSUFFICIENT_OUTPUT_TOKEN");
             IERC20(inputTokenAddress).transferFrom(msg.sender, address(0), 
        _inputTokenAmount);

IERC20(inputTokenAddress).transferFrom(msg.sender, address(0), 
             _inputTokenAmount);
        SafeERC20.safeTransfer(IERC20(outputTokenAddress), msg.sender, 
             allocation);
        emit OutputTokenInstantReleased(msg.sender, allocation, 
             outputTokenAddress);
LOW severity: TRST-Zero duration time causes divide-by-zero exception. **Description:**
linearVestingDuration is used as the total period from start to end of vesting in linear 
transmutation. It is set in the constructor and is fixed. There is no validation in construction 
that the variable is not set to zero. When users call `releaseTransmutedLinear()` to claim...

Vulnerable code:
} else {
        return (totalAllocation * (timestamp - start(_vester))) / 
         duration();
        }
LOW severity: TRST-vestedAmount and vestedAmountAtTimestamp return false information for instant transmutations. **Description:**
**vestedAmount()** and **vestedAmountAtTimestamp()** are view functions for user to see their
vested **amount currently and in a given timestamp respectively. The issue is that they 
assume user is on a linear vesting plan, rather than instant vesting. Therefore, they will...

Vulnerable code:
if (addressToVestingCode[_vester] == 1) {
                 return addressToTotalAllocatedOutputToken[_vester];
                    } else if (addressToVestingCode[_vester] == 2) {
                        return 
                    _vestingSchedule(addressToTotalAllocatedOutputToken[_vester],
            uint64(_timestamp), _vester);
         }
LOW severity: TRST-Multiplier implementation causes limited functionality. **Description:**
linearMultiplier and instantMultiplier are used to calculate output token amount from input 
token amount in transmute functions. 

The issue is that they are uint256 variables and can only multiply _inputTokenAmount, not 
divide it. It results in limited functionality of the...

Vulnerable code:
uint256 allocation = (_inputTokenAmount * linearMultiplier) / 
        tokenDecimalDivider;
    …
    uint256 allocation = (_inputTokenAmount * instantMultiplier) / 
        tokenDecimalDivider;
LOW severity: TRST-tokenDecimalDivider implementation causes limited functionality. **Description:**
tokenDecimalDivider stores the decimals difference between input token and output token. 
However, this value assumes input decimals is always larger or equal to output decimals. As 
a result, the contract cannot be used for many input/output token pairs.
 

**Mitgation...

Vulnerable code:
uint256 allocation = (_inputTokenAmount * linearMultiplier) / 
             tokenDecimalDivider;
LOW severity: TRST-transmute functions may charge input tokens but not allocate any output tokens. **Description:** 
`transmuteInstant()` calculates and distributes allocation like so:

The issue is that allocation result could be zero due to division by tokenDecimalDivider
which trims many decimal points. If user does not provide a sufficient input amount, 
allocation will be zero but the...

Vulnerable code:
uint256 allocation = (_inputTokenAmount * instantMultiplier) / 
             tokenDecimalDivider;
        …
        IERC20(inputTokenAddress).transferFrom(msg.sender, address(0), 
                _inputTokenAmount);
        SafeERC20.safeTransfer(IERC20(outputTokenAddress), msg.sender, 
        allocation);
LOW severity: TRST-emergencyPull introduces substantial risks. The **emergencyPull()** function is only callable by owner, and transfers the entire output 
token balance to a controlled destination. Use of **emergencyPull()** will make any linear 
vesting be cancelled without refund to the user. It is recommended that only the non allocated output tokens can...

Vulnerable code:
uint256 outputTokenBalance = 
             IERC20(outputTokenAddress).balanceOf(address(this));
                uint256 vestingRequiredBalance = totalAllocatedOutputToken -
        totalReleasedOutputToken;
        require(outputTokenBalance > vestingRequiredBalance, 
            "NO_UNALLOCATED_TOKENS");
        SafeERC20.safeTransfer(IERC20(outputTokenAddress), 
             _emergencyOutputDestination, 
        IERC20(outputTokenAddress).balanceOf(address(this)));
HIGH severity: TRST-createUniswapRangeOrder() charges manager instead of pool. **Description:**
**_createUniswapRangeOrder()** can be called either from manager flow, with **createUniswapRangeOrder()**, or pool-induced from **hedgeDelta()**. The issue is that the 
function assumes the sender is the parentLiquidityPool, for example:

Balance check is done on pool, but money...

Vulnerable code:
if (inversed && balance < amountDesired) {
             // collat = 0
        uint256 transferAmount = amountDesired - balance;
         uint256 parentPoolBalance = 
             ILiquidityPool(parentLiquidityPool).getBalance(address(token0));
        if (parentPoolBalance < transferAmount) { revert 
            CustomErrors.WithdrawExceedsLiquidity(); 
        }
        SafeTransferLib.safeTransferFrom(address(token0), msg.sender, 
         address(this), transferAmount);
            }

function createUniswapRangeOrder(
         RangeOrderParams calldata params,
             uint256 amountDesired
              ) external {
           require(!_inActivePosition(), "RangeOrder: active position");
         _onlyManager();
    bool inversed = collateralAsset == address(token0);
    _createUniswapRangeOrder(params, amountDesired, inversed);
    }
HIGH severity: TRST-hedgeDelta() priceToUse is calculated wrong, which causes bad hedges. **Description:**
When _delta parameter is negative for `hedgeDelta()`, priceToUse will be the minimum 
between quotePrice and underlyingPrice. 

This works fine when direction is BELOW, because the calculated lowerTick and upperTick 
from _getTicksAndMeanPriceFromWei are guaranteed to be lower...

Vulnerable code:
// buy wETH
    // lowest price is best price when buying
        uint256 priceToUse = quotePrice < underlyingPrice ? quotePrice : 
            underlyingPrice;
    RangeOrderDirection direction = inversed ? RangeOrderDirection.ABOVE 
        : RangeOrderDirection.BELOW;
    RangeOrderParams memory rangeOrder = 
        _getTicksAndMeanPriceFromWei(priceToUse, direction);

int24 lowerTick = direction == RangeOrderDirection.ABOVE ? 
         nearestTick + tickSpacing : nearestTick - (2 * tickSpacing);
     int24 tickUpper = direction ==RangeOrderDirection.ABOVE ? lowerTick + 
        tickSpacing : nearestTick - tickSpacing;
MEDIUM severity: TRST-multiplication overflow in getPoolPrice() likely. **Description:**
`getPoolPrice()` is used in hedgeDelta to get the price directly from Uniswap v3 pool:

The issue is that calculation of p is likely to overflow. sqrtPriceX96 has 96 bits for decimals, 
10** `token0.decimals()` will have 60 bits when decimals is 18, therefore there is only 
(256 –.....

Vulnerable code:
function getPoolPrice() public view returns (uint256 price, uint256 
         inversed){
            (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
        uint256 p = uint256(sqrtPriceX96) * uint256(sqrtPriceX96) * (10 
        ** token0.decimals());
     // token0/token1 in 1e18 format
          price = p / (2 ** 192);
              inversed = 1e36 / price;
         }
MEDIUM severity: TRST-Hedging won't work if token1.decimals() < token0.decimals(). **Description:**
`tickToToken0PriceInverted()` performs some arithmetic calculations. It's called by 
`_getTicksAndMeanPriceFromWei()`, which is called by `hedgeDelta()`. This line can overflow:

Also, this line would revert even if the above calculation was done correctly:

The impact is that...

Vulnerable code:
uint256 intermediate = inWei.div(10**(token1.decimals() -
         token0.decimals()));

meanPrice = OptionsCompute.convertFromDecimals(meanPrice, 
         token0.decimals(), token1.decimals());

function convertFromDecimals(uint256 value, uint8 decimalsA, uint8 decimalsB) internal pure
        returns (uint256) {
    if (decimalsA > decimalsB) {
          revert();
        }
        …
MEDIUM severity: TRST-Overflow danger in _sqrtPriceX96ToUint. **Description:**
_sqrtPriceX96ToUint will only work when the non-fractional component of sqrtPriceX96 
takes up to 32 bits. This represents a price ratio of 18446744073709551616. With different 
token digits it is not unlikely that this ratio will be crossed which will make hedgeDelta()...

Vulnerable code:
function _sqrtPriceX96ToUint(uint160 sqrtPriceX96) private pure returns (uint256)
    {
        uint256 numerator1 = uint256(sqrtPriceX96) * 
         uint256(sqrtPriceX96);
    return FullMath.mulDiv(numerator1, 1, 1 << 192);
         }

if (sqrtPrice > Q96) {
             uint256 sqrtP = FullMath.mulDiv(sqrtPrice, 10 ** token0Decimals, 
                Q96);
        return FullMath.mulDiv(sqrtP, sqrtP, 10 ** token0Decimals);
            } else {
        uint256 numerator1 = FullMath.mulDiv(sqrtPrice, sqrtPrice, 1);
        uint256 numerator2 = 10 ** token0Decimals;
             return FullMath.mulDiv(numerator1, numerator2, 1 << 192);
            }