Farseen0/scar-pairs huggingface dataset:
LOW severity: [FUE6] Funds can get stuck, because of block header recommitting. **Severity:** Medium

**Path:** FuelChainState:commit()

**Description:**

When committing a block header, the `COMMITTER_ROLE` invokes the `commit()` function, which writes the `blockHash` and `timestamp` to storage. To access messages generated within the L2 (Fuel) blockchain, users are required.....

Vulnerable code:
function relayMessage(
        Message calldata message,
        FuelBlockHeaderLite calldata rootBlockHeader,
        FuelBlockHeader calldata blockHeader,
        MerkleProof calldata blockInHistoryProof,
        MerkleProof calldata messageInBlockProof
    ) external payable virtual override whenNotPaused {
        if (withdrawalsPaused) {
            revert WithdrawalsPaused();
        }

        //verify root block header
        if (!_fuelChainState.finalized(rootBlockHeader.computeConsensusHeaderHash(), rootBlockHeader.height)) {
            revert UnfinalizedBlock();
        }

        //verify block in history
        if (
            !verifyBinaryTree(
                rootBlockHeader.prevRoot,
                abi.encodePacked(blockHeader.computeConsensusHeaderHash()),
                blockInHistoryProof.proof,
                blockInHistoryProof.key,
                rootBlockHeader.height
            )
        ) revert InvalidBlockInHistoryProof();

        //verify message in block
        bytes32 messageId = CryptographyLib.hash(
            abi.encodePacked(message.sender, message.recipient, message.nonce, message.amount, message.data)
        );

        if (messageIsBlacklisted[messageId]) {
            revert MessageBlacklisted();
        }

        if (
            !verifyBinaryTree(
                blockHeader.outputMessagesRoot,
                abi.encodePacked(messageId),
                messageInBlockProof.proof,
                messageInBlockProof.key,
                blockHeader.outputMessagesCount
            )
        ) revert InvalidMessageInBlockProof();

        //execute message
        _executeMessage(messageId, message);
    }

function commit(bytes32 blockHash, uint256 commitHeight) external whenNotPaused onlyRole(COMMITTER_ROLE) {//@audit should add require check for rewriting cases
        uint256 slot = commitHeight % NUM_COMMIT_SLOTS;
        Commit storage commitSlot = _commitSlots[slot];
        commitSlot.blockHash = blockHash;
        commitSlot.timestamp = uint32(block.timestamp);

        emit CommitSubmitted(commitHeight, blockHash);
    }
LOW severity: [FUE2] Sent funds may get stuck inside of the bridge. **Severity:** Low

**Path:** bridge-fungible-token/src/main.sw#L343-L361, bridge-fungible-token/src/main.sw#L170-L187

**Description:**

In the `bridge-fungible-token/src/main.sw` contract while processing the message there are a couple of points in the contract where if the check isn’t passing...

Vulnerable code:
fn register_refund(
    from: b256,
    token_address: b256,
    token_id: b256,
    amount: b256,
) {
    let asset = sha256((token_address, token_id));

    let previous_amount = storage.refund_amounts.get(from).get(asset).try_read().unwrap_or(ZERO_U256);
    let new_amount = amount.as_u256() + previous_amount;

    storage.refund_amounts.get(from).insert(asset, new_amount);
    log(RefundRegisteredEvent {
        from,
        token_address,
        token_id,
        amount,
    });
}

match message_data.len {
    ADDRESS_DEPOSIT_DATA_LEN => {
        transfer(message_data.to, asset_id, amount);
    },
    CONTRACT_DEPOSIT_WITHOUT_DATA_LEN => {
        transfer(message_data.to, asset_id, amount);
    },
    _ => {
        if let Identity::ContractId(id) = message_data.to {
            let dest_contract = abi(MessageReceiver, id.into());
            dest_contract
                .process_message {
                    coins: amount,
                    asset_id: asset_id.into(),
                }(msg_idx);
        };
    },
}
LOW severity: [FUE18] Redundant payable modifiers. **Severity:** Low

**Path:** fuel-bridge/packages/solidity-contracts/contracts/messaging/gateway/FuelERC20Gateway/FuelERC20GatewayV4.sol

**Description:**

The functions `deposit()`, `depositWithData()`, and `sendMetadata()` within the `FuelERC20GatewayV4` contract are marked as payable.

However,.....

Vulnerable code:
function depositWithData(
    bytes32 to,
    address tokenAddress,
    uint256 amount,
    bytes calldata data
) external payable virtual whenNotPaused {
    uint8 decimals = _getTokenDecimals(tokenAddress);
    uint256 l2MintedAmount = _adjustDepositDecimals(decimals, amount);

    bytes memory depositMessage = abi.encodePacked(
        assetIssuerId,
        uint256(data.length == 0 ? MessageType.DEPOSIT_TO_CONTRACT : MessageType.DEPOSIT_WITH_DATA),
        bytes32(uint256(uint160(tokenAddress))),
        uint256(0), // token_id = 0 for all erc20 deposits
        bytes32(uint256(uint160(msg.sender))),
        to,
        l2MintedAmount,
        data
    );
    _deposit(tokenAddress, amount, l2MintedAmount, depositMessage);
}
HIGH severity: [TOKE-28] Positive price movements of vault assets can be directly stolen through withdraw. **Severity:** High

**Description:**

Assume there is 1 Autopool with 2 Destination Vaults with assets A and B respectively, each priced 1:1 with ETH. The Autopool holds 100 dvA shares and 900 dvB shares. The attacker holds 100 Autopool shares out of a total supply of 1000 shares.

So currently,...

Vulnerable code:
function withdraw(
        uint256 assets,
        uint256 applicableTotalAssets,
        IAutopool.AssetBreakdown storage assetBreakdown,
        StructuredLinkedList.List storage withdrawalQueue,
        mapping(address => AutopoolDebt.DestinationInfo) storage destinationInfo
    ) public returns (uint256 actualAssets, uint256 actualShares, uint256 debtBurned) {
        [..]
    }
LOW severity: [TOKE-4] Incorrect updating of lastUpdateBlock could result in a loss of rewards. **Severity:** High

**Path:** src/rewarders/AbstractRewarder.sol#L120-L135 

**Description:**

The function `AbstractRewarder::_updateReward()` updates the user's reward. In this function, `lastUpdateBlock` is updated only when `rewardPerTokenStored > 0`. This condition is meant to prevent reward...

Vulnerable code:
function _updateReward(address account) internal {
    uint256 earnedRewards = 0;
    rewardPerTokenStored = rewardPerToken();


    // Do not update lastUpdateBlock if rewardPerTokenStored is 0, to prevent the loss of rewards when supply is 0
    if (rewardPerTokenStored > 0) {
        if (account != address(0)) {
            lastUpdateBlock = lastBlockRewardApplicable();
            earnedRewards = earned(account);
            rewards[account] = earnedRewards;
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
    }


    emit UserRewardUpdated(account, earnedRewards, rewardPerTokenStored, lastUpdateBlock);
}

function _updateReward(address account) internal {
    uint256 earnedRewards = 0;
    rewardPerTokenStored = rewardPerToken();

-    if (rewardPerTokenStored > 0) {
+    if (totalSupply() > 0) {
        if (account != address(0)) {
            lastUpdateBlock = lastBlockRewardApplicable(); // --> [$solve-med] immediate reward for first depositor
            earnedRewards = earned(account);
            rewards[account] = earnedRewards;
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
    }
+    else {
+        uint256 newRewards = (lastBlockRewardApplicable() - lastUpdateBlock) * rewardRate;
+        queuedRewards += newRewards;
+        lastUpdateBlock = lastBlockRewardApplicable(); 
+    }

    emit UserRewardUpdated(account, earnedRewards, rewardPerTokenStored, lastUpdateBlock);
}
LOW severity: [TOKE-6] Loss of Reward from DestinationVaultMainRewarder if the Destination is Empty After Rebalance. **Severity:** High

**Path:** src/vault/libs/AutoPoolDestinations.sol#L137-L144, src/vault/DestinationVault.sol#L400-L408 

**Description:**

The function `AutoPoolETH::flashRebalance()` is used to rebalance liquidity between two destination vaults, `destinationIn` and `destinationOut`. Within...

Vulnerable code:
// If we no longer have a balance we don't need to continue to report
// on it and we also have nothing to withdraw from it
debtReportQueue.popAddress(destination);
withdrawalQueue.popAddress(destination);


if (removalQueue.remove(destination)) {
    emit RemovedFromRemovalQueue(destination);
}

function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
    if (from == to) {
        return;
    }


    if (from != address(0)) {
        _rewarder.withdraw(from, amount, false);
    }
}
LOW severity: [TOKE-14] Incorrect Streaming Fee Share Calculation. **Severity:** High

**Path:** src/vault/libs/AutoPoolFees.sol#L238-L240

**Description:**

The streaming fee is intended to be applied to the profit generated during the rebalancing and debt reporting process. However, in the current implementation of the function...

Vulnerable code:
uint256 streamingFeeShares =
    _calculateSharesToMintFeeCollection(streamingFeeBps, totalAssets, currentTotalSupply);
tokenData.mint(sink, streamingFeeShares);

uint256 streamingFeeShares 
  = Math.mulDiv(
        fees, 
        currentTotalSupply, 
        (totalAssets * FEE_DIVISOR) - (fees), 
        Math.Rounding.Up
    );
LOW severity: [TOKE-20] Incorrect update for safeTotalSupply when the distribution is ended in function IncentiveCalculatorBase._snapshotRewarder(). **Severity:** High

**Path:** src/stats/calculators/base/IncentiveCalculatorBase.sol#L403-L424 

**Description:**

Within the function `IncentiveCalculatorBase._snapshotRewarder()`, if the `status` is `shouldFinalize`, the `safeTotalSupply` is calculated by dividing `rewardRate *...

Vulnerable code:
function rewardPerToken() public view returns (uint256) {
    uint256 total = totalSupply();
    if (total == 0) {
        return rewardPerTokenStored;
    }

    return rewardPerTokenStored + (
        (lastBlockRewardApplicable() - lastUpdateBlock) * rewardRate * 1e18 / total
    );
}

if (status == SnapshotStatus.shouldFinalize) {
        uint256 lastSnapshotTimestamp = lastSnapshotTimestamps[_rewarder];
        uint256 lastRewardPerToken = lastSnapshotRewardPerToken[_rewarder];
        // Subtract one, added during initialization, to ensure 0 is only used as an uninitialized value flag.
        uint256 diff = rewardPerToken - (lastRewardPerToken - 1);


        uint256 timeBetweenSnapshots = block.timestamp - lastSnapshotTimestamp;


        // Set safe total supply only when we are able to calculate it
        if (diff > 0) {
            safeTotalSupplies[_rewarder] = rewardRate * timeBetweenSnapshots * 1e18 / diff;
        }
        lastSnapshotRewardPerToken[_rewarder] = 0;
        lastSnapshotTimestamps[_rewarder] = block.timestamp;


        // slither-disable-next-line reentrancy-events
        emit RewarderSafeTotalSupplySnapshot(
            _rewarder, rewardRate, timeBetweenSnapshots, diff, safeTotalSupplies[_rewarder]
        );


        return;
    }

- uint256 timeBetweenSnapshots = block.timestamp - lastSnapshotTimestamp;
+ (,, uint256 periodFinish) = _getRewardPoolMetrics(address(rewarder));
+ if (periodFinish > block.timestamp) {
+  periodFinish = block.timestamp 
+ }
+ uint256 timeBetweenSnapshots = periodFinish - lastSnapshotTimestamp;
MEDIUM severity: [TOKE-27] Fee configuration update does not sync to latest timestamp. **Severity:** Medium

**Path:** AutopoolFees:setStreamingFeeBps, setPeriodicFeeBps

**Description:**

The `AutopoolFees` contract has various configuration variables for the fees in the protocol, such as `streamingFeeBps` and `periodicFeeBps`. Both of these are set through library function calls...

Vulnerable code:
function setStreamingFeeBps(IAutopool.AutopoolFeeSettings storage feeSettings, uint256 fee) external {
        if (fee >= FEE_DIVISOR) {
            revert InvalidFee(fee);
        }

        feeSettings.streamingFeeBps = fee;

        IAutopool vault = IAutopool(address(this));

        // Set the high mark when we change the fee so we aren't able to go farther back in
        // time than one debt reporting and claim fee's against past profits
        uint256 ts = vault.totalSupply();
        if (ts > 0) {
            uint256 ta = vault.totalAssets();
            if (ta > 0) {
                feeSettings.navPerShareLastFeeMark = (ta * FEE_DIVISOR) / ts;
            } else {
                feeSettings.navPerShareLastFeeMark = FEE_DIVISOR;
            }
        }
        emit StreamingFeeSet(fee);
    }

    function setPeriodicFeeBps(IAutopool.AutopoolFeeSettings storage feeSettings, uint256 fee) external {
        if (fee > MAX_PERIODIC_FEE_BPS) {
            revert InvalidFee(fee);
        }

        // Fee checked to fit into uint16 above, able to be wrapped without safe cast here.
        emit PeriodicFeeSet(fee);
        feeSettings.periodicFeeBps = uint16(fee);
    }
LOW severity: [TOKE-5] Immediate Reward for the First Staker if Time Has Passed. **Severity:** Medium

**Path:** src/rewarders/AbstractRewarder.sol#L120-L135

**Description:**

In the function `AbstractRewarder::_updateReward()`, the `lastUpdateBlock` is only updated if `rewardPerTokenStored` is greater than 0. Consider a scenario where only the first reward program has been...

Vulnerable code:
function _updateReward(address account) internal {
    uint256 earnedRewards = 0;
    rewardPerTokenStored = rewardPerToken();


    // Do not update lastUpdateBlock if rewardPerTokenStored is 0, to prevent the loss of rewards when supply is 0
    if (rewardPerTokenStored > 0) {
        if (account != address(0)) {
            lastUpdateBlock = lastBlockRewardApplicable();
            earnedRewards = earned(account);
            rewards[account] = earnedRewards;
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
    }


    emit UserRewardUpdated(account, earnedRewards, rewardPerTokenStored, lastUpdateBlock);
}
LOW severity: [TOKE-8] Unclaimed rewards in ExtraRewarder can be stolen if removed from MainRewarder. **Severity:** Medium

**Path:** rewarders/MainRewarder.sol:removeExtraRewards#L73-L81

**Description:**

The MainRewarder allows for the addition of ExtraRewarder contracts that can distribute extra rewards to users using the user balance and total supply from the MainRewarder.

It is important...

Vulnerable code:
function removeExtraRewards(address[] calldata _rewards) external hasRole(rewardRole) {
        uint256 length = _rewards.length;
        for (uint256 i = 0; i < length; ++i) {
            if (!_extraRewards.remove(_rewards[i])) {
                revert Errors.ItemNotFound();
            }
            emit ExtraRewardRemoved(_rewards[i]);
        }
    }
LOW severity: [TOKE-11] AutopoolETH redeem incorrectly calculates remaining pull from idle. **Severity:** Medium

**Path:** vault/libs/AutopoolDebt.sol:redeem#L791

**Description:**

This library function is responsible for pulling assets back from DestinationVaults to let the user redeem their shares for underlying assets.

The algorithm loops over DestinationVaults in the...

Vulnerable code:
if (
    info.assetsPulled < assets && info.debtMinDecrease < assets && info.currentIdle > 0 && exhaustedDestinations
) {
    uint256 remaining = assets - Math.max(info.assetsPulled, info.debtMinDecrease); 
    if (remaining < info.currentIdle) {
        info.assetsFromIdle = remaining;
    } else {
        info.assetsFromIdle = info.currentIdle;
    }
}
LOW severity: [TOKE-15] Potential revert in AutoPoolDebt.withdraw() function. **Severity:** Medium

**Path:** src/vault/libs/AutoPoolDebt.sol#L566-L577 

**Description:**

Within the `AutoPoolDebt.withdraw()` function, the `asset` is withdrawn from the destination vault by invoking `destVault.withdrawBaseAsset(dvSharesToBurn, address(this))` on line 577. The shares to be...

Vulnerable code:
dvSharesToBurn = (dvShares * info.assetsToPull) / dvSharesValue;

if (dvSharesValue > info.assetsToPull) {
        dvSharesToBurn = (dvShares * info.assetsToPull) / dvSharesValue;
        // Only need to set it here because the only time we'll use it is if
        // we don't exhaust all shares and have to try the destination again
        info.expectedAssets = info.assetsToPull;
    } else {
        dvSharesToBurn = dvShares;
    }
}


// Destination Vaults always burn the exact amount we instruct them to
uint256 pulledAssets = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));

- uint256 pulledAssets = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));
+ uint256 pulledAssets;
+ if (dvSharesToBurn > 0) {
+   pulledAssets = destVault.withdrawBaseAsset(dvSharesToBurn, address(this));
+ }
LOW severity: [TOKE-16] Update debt reporting does not validate whether spot price is safe. **Severity:** Medium

**Path:** vault/AutopoolETH.sol:updateDebtReporting#L782-811

**Description:**

The function `updateDebtReporting` is responsible for rebalancing the total idle and debt from each Destination Vault. It uses `AutopoolDebt._updateDebtReporting` to calculate the increases and...

Vulnerable code:
AutopoolDebt.IdleDebtUpdates memory debtResult = _recalculateDestInfo(
    destinationInfo[address(destVault)], destVault, currentShareBalance, currentShareBalance
);

function _recalculateDestInfo(
        DestinationInfo storage destInfo,
        IDestinationVault destVault,
        uint256 originalShares,
        uint256 currentShares
    ) private returns (IdleDebtUpdates memory result) {
        // TODO: Trace the use of this fn and ensure that every is handling is pricesWereSafe

        // Figure out what to back out of our totalDebt number.
        // We could have had withdraws since the last snapshot which means our
        // cached currentDebt number should be decreased based on the remaining shares
        // totalDebt is decreased using the same proportion of shares method during withdrawals
        // so this should represent whatever is remaining.

        // Prices are per LP token and whether or not the prices are safe to use
        // If they aren't safe then just continue and we'll get it on the next go around
        (uint256 spotPrice, uint256 safePrice, bool isSpotSafe) = destVault.getRangePricesLP();
LOW severity: [TOKE-19] The discounts on LST stats may be missed. **Severity:** Medium

**Path:** src/stats/calculators/base/LSTCalculatorBase.sol#L105-L120, src/strategy/libs/PriceReturn.sol#L59-L77

**Description:**

In SummeryStats contract, the `SummaryStats::getDestinationSummaryStats` function triggers `PriceReturn.calculatePriceReturns` with the current...

Vulnerable code:
IDexLSTStats.DexLSTStatsData memory stats = dest.getStats().current();
...
interimStats.priceReturns = PriceReturn.calculatePriceReturns(stats);

uint256 timeSinceDiscountSec =
    uint256(uint40(block.timestamp) - discountTimestampByPercent[j - 1]);
scalingFactor >>= (timeSinceDiscountSec / halfLifeSec);
// slither-disable-next-line weak-prng
timeSinceDiscountSec %= halfLifeSec;
scalingFactor -= scalingFactor * timeSinceDiscountSec / halfLifeSec / 2;

function initialize(bytes32[] calldata, bytes memory initData) public virtual override initializer {
    InitData memory decodedInitData = abi.decode(initData, (InitData));
    lstTokenAddress = decodedInitData.lstTokenAddress;
    _aprId = Stats.generateRawTokenIdentifier(lstTokenAddress);

    uint256 currentEthPerToken = calculateEthPerToken();
    lastBaseAprEthPerToken = currentEthPerToken;
    lastBaseAprSnapshotTimestamp = block.timestamp;
    baseAprFilterInitialized = false;
    lastSlashingEthPerToken = currentEthPerToken;
    lastSlashingSnapshotTimestamp = block.timestamp;

    // slither-disable-next-line reentrancy-benign
    updateDiscountHistory(currentEthPerToken);
    updateDiscountTimestampByPercent();
}
LOW severity: [TOKE-10] AccToke minimum stake duration can be zero. **Severity:** Low

**Path:** staking/AccToke.sol

**Description:**

The `AccToke` contract allows users to stake their TOKE for rewards. The user can provide a duration until when the TOKE is locked from the current timestamp.

The contract does have an immutable configuration variable...

Vulnerable code:
constructor(
        ISystemRegistry _systemRegistry,
        uint256 _startEpoch,
        uint256 _minStakeDuration
    )
        SystemComponent(_systemRegistry)
        ERC20("Staked Toke", "accToke")
        ERC20Permit("accToke")
        SecurityBase(address(_systemRegistry.accessController()))
    {
        startEpoch = _startEpoch;
        minStakeDuration = _minStakeDuration; 

        toke = systemRegistry.toke();
        weth = systemRegistry.weth();
    }
LOW severity: [TOKE-25] Gas optimisation in fetching total assets. **Severity:** Low

**Path:** AutopoolETH.sol:deposit, mint, withdraw, redeem

**Description:**

In the function `deposit` of the AutopoolETH contract, the given amount in assets is checked against `maxDeposit`.  This function `maxDeposit` calls `_maxDeposit` internally with the result of calling...

Vulnerable code:
function deposit(
        uint256 assets,
        address receiver
    )
        public
        virtual
        override
        nonReentrant
        noNavPerShareDecrease(TotalAssetPurpose.Deposit)
        ensureNoNavOps
        returns (uint256 shares)
    {
        Errors.verifyNotZero(assets, "assets");

        // Handles the vault being paused, returns 0
        if (assets > maxDeposit(receiver)) {
            revert ERC4626DepositExceedsMax(assets, maxDeposit(receiver));
        }

        uint256 ta = _totalAssetsTimeChecked(TotalAssetPurpose.Deposit);
        shares = convertToShares(assets, ta, totalSupply(), Math.Rounding.Down);

        Errors.verifyNotZero(shares, "shares");

        _baseAsset.safeTransferFrom(msg.sender, address(this), assets);
        _assetBreakdown.totalIdle += assets;
        _tokenData.mint(receiver, shares);
    }
LOW severity: [TOKE-13] Some dust rewards will be stuck in LiquidationRow. **Severity:** Low

**Path:** src/liquidation/LiquidationRow.sol#L360-L364

**Description:**

In `LiquidationRow::_performLiquidation` function, it swaps rewards in `fromToken` to `mainRewarder.rewardToken()`, then distributes them to all of destination vaults by calling `queueNewRewards` function...

Vulnerable code:
uint256 amount = amountReceived * vaultsBalances[i] / totalBalanceToLiquidate;

uint256 amount = amountReceived * vaultsBalances[i] / totalBalanceToLiquidate;

// approve main rewarder to pull the tokens
LibAdapter._approve(IERC20(params.buyTokenAddress), address(mainRewarder), amount);
mainRewarder.queueNewRewards(amount);
LOW severity: [TOKE-18] TellorOracle freshness duration configuration. **Severity:** Low

**Path:** src/oracles/providers/TellorOracle.sol#L55

**Description:**

The `TellorOracle.sol` contract currently uses a constant value (TELLOR_PRICING_FRESHNESS) set to 15 minutes to determine the freshness of the Tellor price data: [Calculate TRB to ETH live today (TRB-ETH) |...

Vulnerable code:
uint256 public constant TELLOR_PRICING_FRESHNESS = 15 minutes;
LOW severity: [TOKE-29] Incorrect logic of catching getFee() failing. **Severity:** Low

**Path:** src/messageProxy/MessageProxy.sol#L196

**Description:**

The `sendMessage` function sends messages to multiple destination chains. It attempts to retrieve the fee for each destination chain using the `getFee()` function within a try-catch block. If the `getFee()`...

Vulnerable code:
try routerClient.getFee(destChainSelector, ccipMessage) returns (uint256 _fee) {
                fee = _fee;
            } catch {
                emit GetFeeFailed(destChainSelector, messageHash);
                break;
            }