Farseen0/scar-pairs huggingface dataset:
LOW severity: TRST-It’s possible to extend the lock of an expired veSatin. **Description:**
The function `merge()`, which is used to merge two different veSatin into one, doesn’t ensure 
that the veSatin that should be merged is not expired. This allows a user to extend a veSatin 
lock time even after it expires, something the protocol assumes not to be possible. This...

Vulnerable code:
uint end = _locked0.end >= _locked1.end ? _locked0.end : 
          _locked1.end;
       require(end > block.timestamp, "expired");

uint end = _locked0.end >= _locked1.end ? _locked0.end : 
           _locked1.end;
      require(_locked0.end > block.timestamp && _locked1.end > 
  block.timestamp, "expired");
LOW severity: TRST-_notifyRewardAmount() is incompatible with fee on transfer tokens. **Description:** 
The function _notifyRewardAmount() transfers an **amount** of a **token** specified as input to 
itself:

After this, it sets the variable **rewardRate[token]** assuming the amount of token received is 
equal to the **amount** it transferred, which is not true in case of tokens...

Vulnerable code:
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
            rewardRate[token] = (amount * PRECISION) / DURATION;
LOW severity: TRST-Modifier onlyNewEpoch() does not ensure new epoch is reached. **Description:** 
The modifier `onlyNewEpoch()` ensures at least **DURATION** seconds passed since the last 
voting action, but does not ensure it’s a different epoch:

This can result in users being able to execute two voting actions in a single epoch, something 
the protocol is trying to...

Vulnerable code:
modifier onlyNewEpoch(uint _tokenId) {
        require((block.timestamp / DURATION) * DURATION > 
          lastVoted[_tokenId], "TOKEN_ALREADY_VOTED_THIS_EPOCH");
LOW severity: TRST-_derivedBalance() doesn’t properly apply boosted rewards based on voting power. **Description:** 
The function `_derivedBalance()` is used to adjust the balance of a pool voter based on his 
percentage of voting power and LP deposited. However the adjustment is only applied to 
the Satin / $CASH LP gauge because the function returns earlier in any other scenario: 

This means.....

Vulnerable code:
if (underlying != IVe(ve).token()) {
        return _derived;
    }

if (underlying == IVe(ve).token()) { //⇐ IF EQUAL
          return _derived;
    }
HIGH severity: TRST-Reentrancy protection can likely be bypassed. **Description:**
The KeyManager offers reentrancy protection for interactions with the associated account. 
Through the LSP20 callbacks or through the `execute()` calls, it will call `_nonReentrantBefore()`
before execution, and `_nonReentrantAfter()` post-execution. The latter will always reset...

Vulnerable code:
function _nonReentrantAfter() internal virtual {
    // By storing the original value once again, a refund is triggered 
             (see // https://eips.ethereum.org/EIPS/eip-2200)
        _reentrancyStatus = false;
     }
MEDIUM severity: TRST-LSP20 verification library deviates from spec and will accept fail values. **Description:**
The functions `lsp20VerifyCall()` and `lsp20VerifyCallResult()` are called to validate the owner 
accepts some account interaction. The specification states they must return a specific 4 byte 
magic value. 
However, the implementation will accept any byte array that starts with...

Vulnerable code:
function _verifyCall(address logicVerifier) internal virtual returns (bool verifyAfter) {
        (bool success, bytes memory returnedData) = logicVerifier.call(
            abi.encodeWithSelector(ILSP20.lsp20VerifyCall.selector, msg.sender, msg.value, msg.data)
    );
    if (!success) _revert(false, returnedData);
    if (returnedData.length < 32) revert 
      LSP20InvalidMagicValue(false, returnedData);
        bytes32 magicValue = abi.decode(returnedData, (bytes32));
    if (bytes3(magicValue) != 
      bytes3(ILSP20.lsp20VerifyCall.selector))
        revert LSP20InvalidMagicValue(false, returnedData);
    return bytes1(magicValue[3]) == 0x01 ? true : false;
    }
MEDIUM severity: TRST-Deviation from spec will result in dislocation of receiver delegate. **Description:**
The LSP0 `universalReceiver()` function looks up the receiver delegate by crafting a mapping key 
type.

Mapping keys are constructed of a 10-byte prefix, 2 zero bytes and a 20-byte suffix. However, 
followers of the specification will use an incorrect suffix.
The docs do not...

Vulnerable code:
bytes32 lsp1typeIdDelegateKey = LSP2Utils.generateMappingKey(
  _LSP1_UNIVERSAL_RECEIVER_DELEGATE_PREFIX, bytes20(typeId));
MEDIUM severity: TRST-Incorrect decoding of universalReceiver() call. **Description:**
Some contracts use the utility below to interact with the registered universal receiver 
delegate.

The `universalReceiver()` function has to return a **bytes** type.

  However, the wrapper above will only decode the low-level call into a **bytes** type when **result**
in...

Vulnerable code:
function callUniversalReceiverWithCallerInfos(address universalReceiverDelegate,
        bytes32 typeId, bytes calldata receivedData, address msgSender, uint256 msgValue) internal            returns (bytes memory) {
    bytes memory callData = abi.encodePacked(
           abi.encodeWithSelector(
ILSP1UniversalReceiver.universalReceiver.selector, typeId,receivedData ),
    msgSender,
       msgValue
    );
    (bool success, bytes memory result) = 
        universalReceiverDelegate.call(callData);
    Address.verifyCallResult(success, result, "Call to universalReceiver failed");
          return result.length != 0 ? abi.decode(result, (bytes)) : result;
        }

function universalReceiver(bytes32 typeId, bytes memory
       /* data */
      )   public payable virtual returns (bytes memory result) {
HIGH severity: TRST-KeyManager ERdoes not support LSP20. **Description:** 
LSP6KeyManager supports LSP20 call verification. However, in `supportInterface()` it does not 
return the LSP20 **interfaceId**.

As a result, clients which correctly check for support of LSP20 methods will not operate with 
the KeyManager implementation.

**Recommended...

Vulnerable code:
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return
    interfaceId == _INTERFACEID_LSP6 || interfaceId == _INTERFACEID_ERC1271 ||
        super.supportsInterface(interfaceId);
           }
MEDIUM severity: TRST-Incorrect permission check due to confusion between empty call and the zero selector. **Description:**
In case the caller of LSP6 does not have **_PERMISSION_SUPER_CALL**, `_verifyAllowedCall()` is 
called to check they have specific permissions for the call. It will use the function 
`_extractExecuteParameters()` to get the call selector.

Due to the behavior above, the 0x00000000.....

Vulnerable code:
// CHECK if there is at least a 4 bytes function selector
    bytes4 selector = executeCalldata.length >= 168
       ? bytes4(executeCalldata[164:168]) : bytes4(0);
           return (operationType, to, value, selector);

if (operationType == OPERATION_0_CALL) {
      if (
      // CHECK if we are doing an empty call
          (selector == bytes4(0) && value == 0) ||
      // we do not require callType CALL
      // if we are just transferring value without `data`
          selector != bytes4(0)
       ) {
           requiredCallTypes = _ALLOWEDCALLS_WRITE;
           }
      }
LOW severity: TRST-LSP0 ownership functions deviate from specification and reject native tokens. **Description:**
The LSP specifications define the following functions for LSP0:

However, their implementations are not payable.

This may break interoperation between conforming and non-confirming contracts.

**Recommended Mitigation:**
Remove the payable keyword in the specification for the...

Vulnerable code:
function transferOwnership(address newPendingOwner) external payable;
    function renounceOwnership() external payable;

function transferOwnership(address newOwner) public virtual
        override(LSP14Ownable2Step, OwnableUnset)
    {

function renounceOwnership() public virtual override(LSP14Ownable2Step, OwnableUnset) {
         address _owner = owner();
LOW severity: TRST-Transfers of vaults from an invalid source are not treated correctly by receiver delegate. **Description:**
In the universalReceiver() function, if the notifying contract does not support LSP9, yet the 
**typeID** corresponds to an LSP9 transfer, the function will return instead of reverting.

**Recommended Mitigation:**
Revert when dealing with transfers that cannot be valid.

**Team...

Vulnerable code:
if (
      mapPrefix == _LSP10_VAULTS_MAP_KEY_PREFIX && notifier.code.length > 0 &&
         !notifier.supportsERC165InterfaceUnchecked(_INTERFACEID_LSP9)
    ) {
        return "LSP1: not an LSP9Vault ownership transfer";
     }
LOW severity: TRST-Relayer can choose amount of gas for delivery of message. **Description:**
LSP6 supports relaying of calls using a supplied signature. The encoded message is defined as:
 
The message doesn't include a gas parameter, which means the relayer can specify any gas 
amount. If the provided gas is insufficient, the entire transaction will revert. However, if...

Vulnerable code:
bytes memory encodedMessage = abi.encodePacked( LSP6_VERSION,
           block.chainid,
              nonce,
                   msgValue,
         payload
     );
HIGH severity: Improve code documentation. 1. In LSP0ERC725AccountInitAbstract, the `_initialize()` call doesn't initialize the parents 
as they don't have non-zero initial state. It is worth explicitly noting this as usually 
derived Proxy classes have to initialize parent contracts.
2. Copy-paste error in documentation of...

Vulnerable code:
@dev retrieve the permission required to set some AllowedCalls  for a controller.
HIGH severity: Better verification. 1. In LSP0ERC725AccountCore's `universalReceiver()`, the delegate value can be over 20 
bytes.

It would be better to avoid errors and only call the delegate if length is precisely 20 
bytes.
2. In LSP6ExecuteModule's `_verifyCanExecute()`, the 12 upper bytes of the address 
**uint256** are never...

Vulnerable code:
bytes memory lsp1DelegateValue = 
       _getData(_LSP1_UNIVERSAL_RECEIVER_DELEGATE_KEY);
           bytes memory resultDefaultDelegate;
              if (lsp1DelegateValue.length >= 20) {

// MUST be one of the ERC725X operation types.
             uint256 operationType = uint256(bytes32(payload[4:36]));
                address to = address(bytes20(payload[48:68]));
HIGH severity: TRST-User fee  token  balance  can  be  drained in  a  single  operation  by a  malicious bot. **Description:**
In `_buildFeeExecutable()`,  BrahRouter  calculates  the  total  fee  charged  to  the  wallet.  It  uses tx. gas price to get the gas price specified by the bot.
 

**Impact:**
The issue is that a malicious bot can manipulate tx.gasprice to be as high as they wish. This value is...

Vulnerable code:
if (feeToken == ETH) 
   {uint256 totalFee = (gasUsed + GAS_OVERHEAD_NATIVE) * tx.gasprice;
     totalFee = _applyMultiplier(totalFee);
       return (totalFee, recipient, TokenTransfer._nativeTransferExec(recipient, totalFee));
            } else {uint256 totalFee = (gasUsed + GAS_OVERHEAD_ERC20) * tx.gasprice;
      // Convert fee amount value in fee tokenuint256 feeToCollect =PriceFeedManager(_addressProvider.priceFeedManager()).getTokenXPriceInY(totalFee, ETH, feeToken);
  feeToCollect = _applyMultiplier(feeToCollect);
 return (feeToCollect, recipient, TokenTransfer._erc20TransferExec(feeToken, recipient, feeToCollect));}
HIGH severity: TRST-Users can drain Gelato deposit at little cost. **Description:**
In Console automation, fees are collected via the `claimExecutionFees()` modifier:

   
   **Impact:**
 The actual strategy processing happens in _; , then the total fee is calculated using the gas used up to this point.
 The issue is that a malicious user can get arbitrary...

Vulnerable code:
modifier claimExecutionFees(address _wallet) {
     uint256 startGas = gasleft();
  _;
 if (feeMultiplier > 0) {
    address feeToken = FeePayer._feeToken(_wallet);
      uint256 gasUsed = startGas -gasleft();
       (uint256 feeAmount, address recipient, Types.Executable memory feeTransferTxn)=FeePayer._buildFeeExecutable
         (gasUsed, feeToken);
 emit FeeClaimed(_wallet, feeToken, feeAmount);
   if (feeToken != ETH) {uint256 initialBalance = IERC20(feeToken).balanceOf(recipient);_
       executeSafeERC20Transfer(_wallet, feeTransferTxn);
             if (IERC20(feeToken).balanceOf(recipient) -initialBalance < feeAmount){
        revert UnsuccessfulFeeTransfer(_wallet, feeToken);}
     } else {
    uint256 initialBalance = recipient.balance;
  Executor._executeOnWallet(_wallet, feeTransferTxn);
 if (recipient.balance -initialBalance < feeAmount) {
      revert UnsuccessfulFeeTransfer(_wallet, feeToken);
                }
            }
        }
    }

// Keeper network overhead -150k
      uint256 internal constant GAS_OVERHEAD_NATIVE = 150_000 + 40_000;
        uint256 internal constant GAS_OVERHEAD_ERC20 = 150_000 + 90_000;
HIGH severity: TRST-Attackers can drain users over time by donating negligible ERamount. **Description:**
In the Console automation model, a strategy shall keep executing until its trigger check fails. For DCA strategies, the swapping trigger is defined as:

**Impact:**
Note that every execution will charge the user for gas costs, which can be expensive. The issue is that whenever the.....

Vulnerable code:
function canInitSwap(address subAccount, address inputToken, uint256 interval, uint256 lastSwap)
          external view returns (bool)
        {
      if (hasZeroBalance(subAccount, inputToken)) 
          { return false;
        }
      return ((lastSwap + interval) < block.timestamp);
      }
HIGH severity: TRST-Executors can drain the Gelato deposit while profiting from free gas. **Description:**
As discussed, `claimExecutionFees()` charges the user for gas fees. Since wallet is not trusted, the payment is wrapped and checked that the balance increased by the required amount.

**Impact:**

While the check is sound, it can be bypassed because the execution functions are not.....

Vulnerable code:
if (feeToken != ETH) {
      uint256 initialBalance = IERC20(feeToken).balanceOf(recipient);
      _executeSafeERC20Transfer(_wallet, feeTransferTxn);
        if (IERC20(feeToken).balanceOf(recipient) - initialBalance < feeAmount)
    {
    revert UnsuccessfulFeeTransfer(_wallet, feeToken);
    }
    } else {
    uint256 initialBalance = recipient.balance; Executor._executeOnWallet(_wallet, feeTransferTxn); if (recipient.balance - initialBalance < feeAmount) {
    revert UnsuccessfulFeeTransfer(_wallet, feeToken);
            }
      }
MEDIUM severity: TRST-When FeePayer is subsidizing, users can steal gas. **Description:**

**Impact:**
The issue is that Brahma stands to lose up to Gelato deposit x subsidization %. Gas can be stolen by setting up a malicious wallet and programming the adapter function (likely execTransactionFromModuleReturnData()), to mint a popular gas token such as CHI or GST2....

Vulnerable code:
The feeMultiplier enables the admin to subsidize or upcharge for the automation service.
/**
⦁	@notice feeMultiplier represents the total fee to be charged on the transaction
⦁	Is set to 100% by default
⦁	@dev In case feeMultiplier is less than BASE_BPS, fees charged will be less than 100%,
⦁	subsidizing the transaction
⦁	In case feeMultiplier is greater than BASE_BPS, fees charged will be greater than 100%,
⦁	charging the user for the transaction
*/ 
      uint16 public feeMultiplier = 10_000;
        // The normal fee is calculated and then processed by the multiplier.
  if (feeToken == ETH) {
         uint256 totalFee = (gasUsed + GAS_OVERHEAD_NATIVE) * tx.gasprice; 
      totalFee = _applyMultiplier(totalFee);
    return (totalFee, recipient, TokenTransfer._nativeTransferExec(recipient, totalFee));
  } else {
MEDIUM severity: TRST-Strategy actions could be executed out of order due to lack of reentrancy guard. **Description:**
The Execute module performs automation of the fetched Executable array on wallet subaccounts.

**Impact:**
Note that there are no uses of reentrancy guards in this function or any above it in the call chain. As a result, a bot or a keeper can execute operations out of sequence....

Vulnerable code:
function _executeAutomation( address _wallet, address _subAccount, address _strategy,
       Types.Executable[] memory _actionExecs ) internal {
        uint256 actionLen = _actionExecs.length;
    if (actionLen == 0) {
           revert InvalidActions();
        } else {
      uint256 idx = 0;
    do {
    _executeOnSubAccount(_wallet, _subAccount, _strategy,
    _actionExecs[idx]);
    unchecked {
    ++idx;
            }
          } while (idx < actionLen);
        }
      }
MEDIUM severity: TRST-Anyone can make creating strategies extremely expensive for the user. **Description:**
In Console architecture, users can deploy spare subaccounts (Gnosis Safes) so that when they will subscribe to a strategy most of the gas spending would have been spent at a low-gas phase.

**Impact**
The issue is that anyone can call the deploy function and specify another user's.....

Vulnerable code:
function deploySpareSubAccount(address _wallet) external { address subAccount =
        SafeDeployer(addressProvider.safeDeployer()).deploySubAccount(_wallet);
             subAccountToWalletMap[subAccount] = _wallet; walletToSubAccountMap[_wallet].push(subAccount);
           // No need to update subAccountStatus as it is already set to false
              emit SubAccountAllocated(_wallet, subAccount);
                }

function requestSubAccount(address _wallet) external returns (address) {
      if (msg.sender != subscriptionRegistry) 
        revert OnlySubscriptionRegistryCallable();
          // Try to find a subAccount which already exists
           address[] memory subAccountList = walletToSubAccountMap[_wallet];
MEDIUM severity: TRST-DCA Strategies build orders that may not be executable, wasting fees. **Description:** 
In `_buildInitiateSwapExecutable()`, DCA strategies determine the swap parameters for the CoW Swap. The code has recently been refactored so that there may be more than one active order simultaneously. The issue is that the function assumes the user's entire ERC20 balance to be...

Vulnerable code:
// Check if enough balance present to swap, else swap entire balance
        uint256 amountIn = (inputTokenBalance < params.amountToSwap) ? 
          inputTokenBalance : params.amountToSwap;
MEDIUM severity: TRST-User will lose all Console functionality when upgrading their wallet and an upgrade target has not been set up. **Description:** 
Console supports upgrading of the manager wallet using the `upgradeWalletType()` function.

Note that upgradablePaths are set by governance. There is a lack of check that the upgradable path is defined before performing the upgrade.

If _upgradablePaths[fromWalletType] is zero...

Vulnerable code:
function upgradeWalletType() external {
    if (!isWallet(msg.sender)) 
    revert WalletDoesntExist(msg.sender); uint8 fromWalletType = _walletDataMap[msg.sender].walletType;
      _setWalletType(msg.sender, _upgradablePaths[fromWalletType]);
      emit WalletUpgraded(msg.sender, fromWalletType,
  _upgradablePaths[fromWalletType]);
  }

function _setWalletType(address _wallet, uint8 _walletType) private {
    _walletDataMap[_wallet].walletType = _walletType;
    }

function isWallet(address _wallet) public view returns (bool) 
    { WalletData memory walletData = _walletDataMap[_wallet];
  if (walletData.walletType == 0 || walletData.feeToken == address(0)){
          return false;
        }
    return true;
  }
LOW severity: TRST-CoW Swap orders are seen as filled although they are cancelled. **Description:**
CoW Swap orders prepared by DCAStrategy can be cancelled using the following function.

Order status is viewed by `isOrderFullyFilled()`:

An issue arises from the fact invalidateOrder on GPv2Settlement sets the filledAmount to MAX_UINT256.

**Impact:**
As a result, the order is...

Vulnerable code:
function cancelCowOrder(bytes calldata orderUid) internal
      { settlement.setPreSignature(orderUid, false); 
    settlement.invalidateOrder(orderUid);
    }

function isOrderFullyFilled(bytes calldata orderUid) internal view returns (bool) {
      return settlement.filledAmount(orderUid) > 0;
    }

function invalidateOrder(bytes calldata orderUid) external {
        (, address owner, ) = orderUid.extractOrderUidParams();
           require(owner == msg.sender, "GPv2: caller does not own order");
               filledAmount[orderUid] = uint256(-1);
  emit OrderInvalidated(owner, orderUid);
    }
LOW severity: TRST-Rounding error causes an additional iteration of DCA strategies. **Description:**
Both CoW strategies receive an interval and total amountIn of tokens to swap. They calculate the amount per iteration as below:

**Impact:**
There is a precision loss error in the amountIn calculation. It will result in an additional iteration to be executed with the division...

Vulnerable code:
Types.TokenRequest[] memory tokens = new Types.TokenRequest[](1); 
      tokens[0] = Types.TokenRequest({token: inputToken, amount: amountIn});
       amountIn = amountIn / iterations;
  StrategyParams memory params = StrategyParams({ tokenIn: inputToken,
    tokenOut: outputToken, amountToSwap: amountIn, interval: interval, remitToOwner: remitToOwner
   });
LOW severity: TRST-Fee mismatch between contracts can make strategies unusable. **Description:**

In CoW Swap strategies, fee is set in the strategy contracts and then passed to `initiateSwap()`. It is built in `_buildInitiateSwapExecutable()`:
 

There is a mismatch between the constraints around fees between the strategy contracts and the `initiateSwap()`...

Vulnerable code:
// Generate executable to initiate swap on DCACoWAutomation return Types.Executable({
        callType: Types.CallType.DELEGATECALL, target: dcaCoWAutomation,
     value: 0,
  data: abi.encodeCall( DCACoWAutomation.initiateSwap,
        (params.tokenIn, params.tokenOut, swapRecipient, amountIn, minAmountOut, swapFee)
       )
    });

function setSwapFee(uint256 _swapFee) external {
        _onlyGov();
    if (_swapFee > 10_000) { revert InvalidSlippage();
       }
    swapFee = _swapFee;
      }
    if (feeBps > 0) {
    if (feeBps > 1_000) revert FeeTooHigh();
      amountIn = amountToSwap * (MAX_BPS - feeBps) / MAX_BPS;