Farseen0/scar-pairs huggingface dataset:
HIGH severity: Gauge point constants in `InitBipSeedGauge` should be scaled by the ratio of deposited BDV. **Description:** The [current initial Gauge Point (GP) distribution](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/beanstalk/init/InitBipSeedGauge.sol#L68-L69) is based solely on the grown stalk per season per BDV for each LP, whereas...

Vulnerable code:
// InitBipSeedGauge.sol
uint128 beanEthGp = uint128(s.ss[C.BEAN_ETH_WELL].stalkEarnedPerSeason) * 500 * 1e12;
uint128 bean3crvGp = uint128(s.ss[C.CURVE_BEAN_METAPOOL].stalkEarnedPerSeason) * 500 * 1e12

//  InitBipSeedGauge.sol
+   // BDV has 6 decimals
+   uint256 beanEthBDV = s.siloBalances[C.BEAN_ETH_WELL].depositedBdv
+   uint256 bean3crvBDV = s.siloBalances[C.CURVE_BEAN_METAPOOL].depositedBdv
+   uint256 lpTotalBDV = beanEthBDV + bean3crvGp
-   uint128 beanEthGp = uint128(s.ss[C.BEAN_ETH_WELL].stalkEarnedPerSeason) * 500 * 1e12;
-   uint128 bean3crvGp = uint128(beanEthBDV) * 500 * 1e12
+   // Assume 1 BDV = 1GP for initialization
+   uint128 beanEthGp = uint128(beanEthBDV * 10e6).div(lpTotalBDV) * 1e12;
+   uint128 bean3crvGp = uint128(bean3crvBDV * 10e6).div(lpTotalBDV) * 1e12
HIGH severity: Incorrect calculation of unmigrated BDVs for use in `InitBipSeedGauge::init`. **Description:** The current values for the [constants](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/beanstalk/init/InitBipSeedGauge.sol#L35-L39) in `InitBipSeedGauge::init` are an estimation and not finalized. To correctly calculate...

Vulnerable code:
unmigrated:  {
  '0x1BEA0050E63e05FBb5D8BA2f10cf5800B6224449': BigNumber { value: "3209210313166" },
  '0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D': BigNumber { value: "6680992571569" },
  '0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab': BigNumber { value: "304630107407" },
  '0xc9C32cd16Bf7eFB85Ff14e0c8603cc90F6F2eE49': BigNumber { value: "26212521946" }
}

unmigrated:  {
  '0x1BEA0050E63e05FBb5D8BA2f10cf5800B6224449': BigNumber { value: "3736196158417" },
  '0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D': BigNumber { value: "7119564766493" },
  '0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab': BigNumber { value: "689428296238" },
  '0xc9C32cd16Bf7eFB85Ff14e0c8603cc90F6F2eE49': BigNumber { value: "26512602424" }
}

// L645
- const END_BLOCK = 18480579;
+ const END_BLOCK = BLOCK_NUMBER;

// L811-812
- //get every transaction that emitted the RemoveDeposit event after block 17251905
+ //get every transaction that emitted the RemoveDeposit event after block 17671557
- let events = await queryEvents("RemoveDeposit(address,address,uint32,uint256)", removeDepositInterface, 17251905); //update this block to latest block when running actual script, in theory someone could have migrated meanwhile
+ let events = await queryEvents("RemoveDeposit(address,address,uint32,uint256)", removeDepositInterface, 17671557);
HIGH severity: Missing validation in `LibWhitelist::verifyTokenInLibWhitelistedTokens`. **Description:** Prior to the introduction of [`LibWhitelistedToken.sol`](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/libraries/Silo/LibWhitelistedTokens.sol), Beanstalk did not have a way of iterating through its whitelisted tokens......

Vulnerable code:
} else {
    checkTokenNotInArray(token, LibWhitelistedTokens.getWhitelistedLpTokens());
+   checkTokenNotInArray(token, LibWhitelistedTokens.getWhitelistedWellLpTokens());
}
HIGH severity: Potentially unsafe cast from negative `int96` values. **Description:** Where calculations are performed on `int96` values, for example when manipulating stems in [`LibSilo::stalkReward`](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/libraries/Silo/LibSilo.sol#L634-L638),...

Vulnerable code:
contract TestStemsUnsafeCasting is Test {
    using LibSafeMathSigned96 for int96;

    function stalkReward(int96 startStem, int96 endStem, uint128 bdv)
        internal
        view
        returns (uint256)
    {
        int96 reward = endStem.sub(startStem).mul(int96(bdv));
        console.logInt(reward);
        console.logUint(uint128(reward));

        return uint128(reward);
    }

    function test_stalk_reward() external {
        uint256 reward = stalkReward(1200, 1000, 1337);
        console.logUint(reward);
    }
}
LOW severity: Both reserves should be checked in `LibWell::getWellPriceFromTwaReserves`. **Description:**

Currently, `LibWell::getWellPriceFromTwaReserves` sets the price to zero if the time-weighted average reserves of the zeroth reserve (for Wells, Bean) is zero. Given the implementation of `LibWell::setTwaReservesForWell`, and that a Pump failure will return an empty reserves...

Vulnerable code:
function getWellPriceFromTwaReserves(address well) internal view returns (uint256 price) {
    AppStorage storage s = LibAppStorage.diamondStorage();
    // s.twaReserve[well] should be set prior to this function being called.
    // 'price' is in terms of reserve0:reserve1.
    if (s.twaReserves[well].reserve0 == 0) {
        price = 0;
    } else {
        price = s.twaReserves[well].reserve0.mul(1e18).div(s.twaReserves[well].reserve1);
    }
}

function setTwaReservesForWell(address well, uint256[] memory twaReserves) internal {
    AppStorage storage s = LibAppStorage.diamondStorage();
    // if the length of twaReserves is 0, then return 0.
    // the length of twaReserves should never be 1, but
    // is added for safety.
    if (twaReserves.length < 1) {
        delete s.twaReserves[well].reserve0;
        delete s.twaReserves[well].reserve1;
    } else {
        // safeCast not needed as the reserves are uint128 in the wells.
        s.twaReserves[well].reserve0 = uint128(twaReserves[0]);
        s.twaReserves[well].reserve1 = uint128(twaReserves[1]);
    }
}

// LibWell::getWellPriceFromTwaReserves`
- if (s.twaReserves[well].reserve0 == 0) {
+ if (s.twaReserves[well].reserve0 == 0 || s.twaReserves[well].reserve1 == 0) {
        price = 0;
} else {

// LibWell::setTwaReservesForWell
- if (twaReserves.length < 1) {
+ if (twaReserves.length <= 1) {
    delete s.twaReserves[well].reserve0;
    delete s.twaReserves[well].reserve1;
} else {
HIGH severity: Potential DoS of `SeasonFacet::gm` due to division by zero in `LibGauge::updateGaugePoints`. There currently exists an edge case in [`LibGauge::updateGaugePoints`](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/libraries/LibGauge.sol#L84) where it is possible to unintentionally DoS `SeasonFacet::gm` due to a potential division...

Vulnerable code:
...
// if there is only one pool, there is no need to update the gauge points.
if (whitelistedLpTokens.length == 1) {
    // Assumes that only Wells use USD price oracles.
    if (LibWell.isWell(whitelistedLpTokens[0]) && s.usdTokenPrice[whitelistedLpTokens[0]] == 0) {
        return (maxLpGpPerBdv, lpGpData, totalGaugePoints, type(uint256).max);
    }
    uint256 gaugePoints = s.ss[whitelistedLpTokens[0]].gaugePoints;
+   if (s.siloBalances[whitelistedLpTokens[0]].depositedBdv != 0) {
        lpGpData[0].gpPerBdv = gaugePoints.mul(BDV_PRECISION).div(
            s.siloBalances[whitelistedLpTokens[0]].depositedBdv
        );
+   }
    return (
        lpGpData[0].gpPerBdv,
        lpGpData,
        gaugePoints,
        s.siloBalances[whitelistedLpTokens[0]].depositedBdv
    );
}
...
HIGH severity: Incorrect storage slot annotation in `Storage::SiloSettings`. While it appears that the order struct members in storage have not changed, the storage slot annotation of [`Storage::SiloSettings`](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/beanstalk/AppStorage.sol#L393-L404) in `AppStorage.sol`...

Vulnerable code:
struct SiloSettings {
    bytes4 selector; // ─────────────┐ 4
-   uint32 stalkEarnedPerSeason; //  │ 4  (16)
+   uint32 stalkEarnedPerSeason; //  │ 4  (8)
-   uint32 stalkIssuedPerBdv; //     │ 4  (8)
+   uint32 stalkIssuedPerBdv; //     │ 4  (12)
-   uint32 milestoneSeason; //       │ 4  (12)
+   uint32 milestoneSeason; //       │ 4  (16)
    int96 milestoneStem; //          │ 12 (28)
    bytes1 encodeType; // ───────────┘ 1  (29)
    // 3 bytes are left here.
    uint128 gaugePoints; //   ────--------───┐ 16
    bytes4 gpSelector; //                    │ 4   (20)
    uint96 optimalPercentDepositedBdv; // ───┘ 12  (32)
}
HIGH severity: Consolidate unnecessary code duplication in `ConvertFacet::_withdrawTokens`. `ConvertFacet::_withdrawTokens` duplicates the following code in [L119-132](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/beanstalk/silo/ConvertFacet.sol#L119-L132) then again in...

Vulnerable code:
if (a.tokensRemoved.add(amounts[i]) < maxTokens) {
    //keeping track of stalk removed must happen before we actually remove the deposit
    //this is because LibTokenSilo.grownStalkForDeposit() uses the current deposit info
    // @audit start duplicated code
    depositBDV = LibTokenSilo.removeDepositFromAccount(
        msg.sender,
        token,
        stems[i],
        amounts[i]
    );
    bdvsRemoved[i] = depositBDV;
    a.stalkRemoved = a.stalkRemoved.add(
        LibSilo.stalkReward(
            stems[i],
            LibTokenSilo.stemTipForToken(token),
            depositBDV.toUint128()
        )
    );
    // @audit end duplicated code

} else {
    amounts[i] = maxTokens.sub(a.tokensRemoved);

    // @audit start duplicated code
    depositBDV = LibTokenSilo.removeDepositFromAccount(
        msg.sender,
        token,
        stems[i],
        amounts[i]
    );

    bdvsRemoved[i] = depositBDV;
    a.stalkRemoved = a.stalkRemoved.add(
        LibSilo.stalkReward(
            stems[i],
        LibTokenSilo.stemTipForToken(token),
            depositBDV.toUint128()
        )
    );
    // @audit end duplicated code
}

while ((i < stems.length) && (a.tokensRemoved < maxTokens)) {
    if (a.tokensRemoved.add(amounts[i]) >= maxTokens) {
        amounts[i] = maxTokens.sub(a.tokensRemoved);
    }

    //keeping track of stalk removed must happen before we actually remove the deposit
    //this is because LibTokenSilo.grownStalkForDeposit() uses the current deposit info
    depositBDV = LibTokenSilo.removeDepositFromAccount(
        msg.sender,
        token,
        stems[i],
        amounts[i]
    );
    bdvsRemoved[i] = depositBDV;
    a.stalkRemoved = a.stalkRemoved.add(
        LibSilo.stalkReward(
            stems[i],
            LibTokenSilo.stemTipForToken(token),
            depositBDV.toUint128()
        )
    );

    a.tokensRemoved = a.tokensRemoved.add(amounts[i]);
    a.bdvRemoved = a.bdvRemoved.add(depositBDV);

    depositIds[i] = uint256(LibBytes.packAddressAndStem(token, stems[i]));
    i++;
}
HIGH severity: `SiloFacet::transferDeposits` should only call `LibSiloPermit::_spendDepositAllowance` once. `SiloFacet::transferDeposits` currently loops through the input `amounts` array and [calls](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/beanstalk/silo/SiloFacet/SiloFacet.sol#L185) `LibSiloPermit::_spendDepositAllowance` once for...

Vulnerable code:
uint256 s_allowance = 10;

function _spendAllowance(uint256 amount) private {s_allowance-=amount;}

function testBulkTransfer1() public {
    // prepare input
    uint256[10] memory amounts;
    for(uint256 i=0; i<10; i++){amounts[i] = 1;}

    // function implementation; update storage 1-by-1
    for (uint256 i = 0; i < amounts.length; ++i) {
        _spendAllowance(amounts[i]);
    }

    assert(s_allowance == 0);
}

function testBulkTransfer2() public {
    // prepare input
    uint256[10] memory amounts;
    for(uint256 i=0; i<10; i++){amounts[i] = 1;}

    // function implementation; cache total amount, update storage once
    uint256 totalSpend;
    for (uint256 i = 0; i < amounts.length; ++i) {
        totalSpend += amounts[i];
    }

    _spendAllowance(totalSpend);

    assert(s_allowance == 0);
}

[PASS] testBulkTransfer1() (gas: 5494)
[PASS] testBulkTransfer2() (gas: 3435)
HIGH severity: Cache updated remaining amount to prevent extra storage read. `FundraiserFacet::fund` should save the calculated [`remaining - amount`](https://github.com/BeanstalkFarms/Beanstalk/blob/dfb418d185cd93eef08168ccaffe9de86bc1f062/protocol/contracts/beanstalk/field/FundraiserFacet.sol#L124) then use it to set storage in...

Vulnerable code:
remaining = remaining - amount; // Note: SafeMath is redundant here.
s.fundraisers[id].remaining = remaining;
emit FundFundraiser(msg.sender, id, amount);

// If completed, transfer tokens to payee and emit an event
if (remaining == 0) {
    _completeFundraiser(id);
}

uint256 private s_remainingDebt = 10;

function _onDebtRepayment() private {}

function testRemaining1() public {
    uint256 repaymentAmount = 10;

    // update storage
    s_remainingDebt -= repaymentAmount;

    // use storage read for check
    if(s_remainingDebt == 0) {
        _onDebtRepayment();
    }

    assert(s_remainingDebt == 0);
}

function testRemaining2() public {
    uint256 repaymentAmount = 10;

    // cache remaining debt
    uint256 remainingDebt = s_remainingDebt - repaymentAmount;
    // update storage
    s_remainingDebt = remainingDebt;

    // use cache for check
    if(remainingDebt == 0) {
        _onDebtRepayment();
    }

    assert(s_remainingDebt == 0);
}

[PASS] testRemaining1() (gas: 621)
[PASS] testRemaining2() (gas: 563)
HIGH severity: Locks can be created with expiry in the past. **Description:** Due to a silent overflow in [`SolidityV2ERC42069::lockToken`](https://github.com/SolidlyLabs/v2-core/blob/757b18ad05780d2af22018b0c2c9d59422dc59d3/contracts/SolidlyV2-memecore.sol#L387), a sufficiently large duration will cause the unlock date to be in the past. This could allow...

Vulnerable code:
it("lock can be created in the past", async function () {
  const { user1, test0, test1, router, pair } = await loadFixture(deploySolidlyV2Fixture);

  let token0 = test0;
  let token1 = test1;

  // Approve tokens for liquidity provision
  await token0.connect(user1).approve(router.address, ethers.constants.MaxUint256);
  await token1.connect(user1).approve(router.address, ethers.constants.MaxUint256);

  // Provide liquidity
  await router.connect(user1).addLiquidity(
    token0.address,
    token1.address,
    ethers.utils.parseUnits("100", 18),
    ethers.utils.parseUnits("100", 18),
    0,
    0,
    user1.address,
    ethers.constants.MaxUint256
  );

  const liquidityBalance = await pair.balanceOf(user1.address);

  let blockTimestamp = (await ethers.provider.getBlock('latest')).timestamp;

  let maxUint128 = ethers.BigNumber.from("340282366920938463463374607431768211455");

  // Lock LP tokens
  await pair.connect(user1).lockToken(user1.address, liquidityBalance, maxUint128.sub(blockTimestamp));


  let ret = await pair.getLock(user1.address, 0);
  expect(ret.date).to.be.eq(0);
});

locks[from].push(LockData({
    amount: uint128(amount - fee),
-     date: uint128(block.timestamp + duration)
+     date: uint128(block.timestamp) + duration
}));
LOW severity: Accounts blocked by the token cannot claim their fees. **Description:** In `SolidlyV2`, liquidity providers accrue fees from trades made in the pool. These fees are bound to the account holding the liquidity position at the time of trading as the fees are synced on transfers.

When the liquidity provider wants to claim their fees they call...

Vulnerable code:
if (feeState.feeOwed0 > 1) {
    amount0 = feeState.feeOwed0 - 1;
    _safeTransfer(token0, msg.sender, uint256(amount0));
    feesClaimedLP0 += amount0;
    feesClaimedTotal0 += amount0;
    feeState.feeOwed0 = 1;
}
LOW severity: Consider a two-step ownership transfer. **Description:** In the `SolidtlyV2-memecore` contracts the owner is set as `msg.sender` in `SolidlyV2Factory` when deployed.

It can also be changed using `SolidlyV2Factory::setOwner`:

Here there's a risk that the owner is lost if the new address not correct. This would result in some...

Vulnerable code:
function setOwner(address _newOwner) external {
        require(msg.sender == owner);
        owner = _newOwner;
    }
LOW severity: `SolidlyV2Pair::setPoolFee` fails to adequately consider fee setters defined by `SolidlyV2Factory`. **Description:** If a new `feeSetter` is registered by the owner of `SolidlyV2Factory`, then it is possible for the pool fee to be set below the minimum specified by the factory. This breaks the invariant that the pool fee should always be between the defined min/max values since it is only...

Vulnerable code:
function setPoolFee(uint16 _poolFee) external {
    require(ISolidlyV2Factory(factory).isFeeSetter(msg.sender) || msg.sender == copilot, 'UA');
    if (msg.sender == copilot) {
        require(_poolFee >= ISolidlyV2Factory(factory).minFee() && !copilotRevoked); // minimum fee enforced for copilot
    } else {
        require(!protocolRevoked);
    }
    require(_poolFee <= 1000); // pool fee capped at 10%
    uint16 feeOld = poolFee;
    poolFee = _poolFee;
    emit SetPoolFee(feeOld, _poolFee);
}

it("fee below min", async function () {
  const { user1, factory, pair } = await loadFixture(deploySolidlyV2Fixture);

  await factory.setFeeSetter(user1.address, true);
  pair.connect(user1).setPoolFee(0);
  await factory.minFee().then(minFee => console.log(`SolidlyV2Factory::minFee: ${minFee}`));
  await pair.poolFee().then(poolFee => console.log(`SolidlyV2Pair::poolFee: ${poolFee}`));

  await pair.revokeFeeRole();
  const minFee = await factory.minFee();
  await expect(pair.connect(user1).setPoolFee(minFee)).to.revertedWithoutReason();
});
HIGH severity: `TokenBridge::bridgeToken` allows 1-way `ER` bridging causing users to permanently lose their nfts. **Description:** `TokenBridge::bridgeToken` is only intended to support `ERC20` tokens however it quite happily accepts `ERC721` tokens and is able to successfully bridge them over to the L2. But when users attempt to bridge back to the L1 this always reverts resulting in user nfts being...

Vulnerable code:
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MockERC721 is ERC721 {
  constructor(string memory _tokenName, string memory _tokenSymbol) ERC721(_tokenName, _tokenSymbol) {}

  function mint(address _account, uint256 _tokenId) public returns (bool) {
    _mint(_account, _tokenId);
    return true;
  }
}

it("Can erroneously bridge ERC721 and can't bridge it back", async function () {
  const {
    user,
    l1TokenBridge,
    l2TokenBridge,
    tokens: { L1DAI },
    chainIds,
  } = await loadFixture(deployContractsFixture);

  // deploy ERC721 contract
  const ERC721 = await ethers.getContractFactory("MockERC721");
  let mockERC721 = await ERC721.deploy("MOCKERC721", "M721");
  await mockERC721.waitForDeployment();

  // mint user some NFTs
  const bridgedNft = 5;
  await mockERC721.mint(user.address, 1);
  await mockERC721.mint(user.address, 2);
  await mockERC721.mint(user.address, 3);
  await mockERC721.mint(user.address, 4);
  await mockERC721.mint(user.address, bridgedNft);

  const mockERC721Address = await mockERC721.getAddress();
  const l1TokenBridgeAddress = await l1TokenBridge.getAddress();
  const l2TokenBridgeAddress = await l2TokenBridge.getAddress();

  // user approves L1 token bridge to spend nft to be bridged
  await mockERC721.connect(user).approve(l1TokenBridgeAddress, bridgedNft);

  // user bridges their nft
  await l1TokenBridge.connect(user).bridgeToken(mockERC721Address, bridgedNft, user.address);

  // verify user has lost their nft which is now owned by L1 token bridge
  expect((await mockERC721.ownerOf(bridgedNft))).to.be.equal(l1TokenBridgeAddress);

  // verify user has received 1 token on the bridged erc20 version which
  // the token bridge created
  const l2TokenAddress = await l2TokenBridge.nativeToBridgedToken(chainIds[0], mockERC721Address);
  const bridgedToken = await ethers.getContractFactory("BridgedToken");
  const l2Token = bridgedToken.attach(l2TokenAddress) as BridgedToken;

  const l2ReceivedTokenAmount = 1;

  expect(await l2Token.balanceOf(user.address)).to.be.equal(l2ReceivedTokenAmount);
  expect(await l2Token.totalSupply()).to.be.equal(l2ReceivedTokenAmount);

  // now user attempts to bridge back which fails
  await l2Token.connect(user).approve(l2TokenBridgeAddress, l2ReceivedTokenAmount);
  await expectRevertWithReason(
    l2TokenBridge.connect(user).bridgeToken(l2TokenAddress, l2ReceivedTokenAmount, user.address),
    "SafeERC20: low-level call failed",
  );
});
HIGH severity: Save 2 storage reads in `TokenBridge::bridgeToken` by caching `sourceChainId`. **Description:** `sourceChainId` is always read once at the beginning of `TokenBridge::bridgeToken` [L153](https://github.com/Consensys/zkevm-monorepo/blob/364b64d2a2704567adac00e3fa428a1901717666/contracts/contracts/tokenBridge/TokenBridge.sol#L153).

Then in the `else` branch of...

Vulnerable code:
function bridgeToken(
    address _token,
    uint256 _amount,
    address _recipient
  ) public payable nonZeroAddress(_token) nonZeroAddress(_recipient) nonZeroAmount(_amount) whenNotPaused nonReentrant {
    // @audit cache `sourceChainId` and use it when setting `nativeMappingValue`
    uint256 sourceChainIdCache = sourceChainId;
    address nativeMappingValue = nativeToBridgedToken[sourceChainIdCache][_token];

    if (nativeMappingValue == RESERVED_STATUS) {
      // Token is reserved
      revert ReservedToken(_token);
    }

    // @audit remove `bridgedMappingValue` and
    // instead initialize `nativeToken`
    address nativeToken = bridgedToNativeToken[_token];
    uint256 chainId;
    bytes memory tokenMetadata;

    // @audit use `nativeToken` in the comparison
    if (nativeToken != EMPTY) {
      // Token is bridged
      BridgedToken(_token).burn(msg.sender, _amount);
      // @audit remove assignment `nativeToken = bridgedMappingValue`
      chainId = targetChainId;
    } else {
      // Token is native

      // For tokens with special fee logic, ensure that only the amount received
      // by the bridge will be minted on the target chain.
      uint256 balanceBefore = IERC20Upgradeable(_token).balanceOf(address(this));
      IERC20Upgradeable(_token).safeTransferFrom(msg.sender, address(this), _amount);
      _amount = IERC20Upgradeable(_token).balanceOf(address(this)) - balanceBefore;

      nativeToken = _token;

      if (nativeMappingValue == EMPTY) {
        // New token
        // @audit using cached `sourceChainIdCache`
        nativeToBridgedToken[sourceChainIdCache][_token] = NATIVE_STATUS;
        emit NewToken(_token);
      }

      // Send Metadata only when the token has not been deployed on the other chain yet
      if (nativeMappingValue != DEPLOYED_STATUS) {
        tokenMetadata = abi.encode(_safeName(_token), _safeSymbol(_token), _safeDecimals(_token));
      }
      // @audit using cached `sourceChainIdCache`
      chainId = sourceChainIdCache;
    }

    messageService.sendMessage{ value: msg.value }(
      remoteSender,
      msg.value, // fees
      abi.encodeCall(ITokenBridge.completeBridging, (nativeToken, _amount, _recipient, chainId, tokenMetadata))
    );
    emit BridgingInitiated(msg.sender, _recipient, _token, _amount);
  }
LOW severity: Save 1 storage read in `MessageServiceV1::setMinimumFee` by emitting `MinimumFeeChanged` event using `_feeInWei` parameter. **Description:** Save 1 storage read in `L2MessageServiceV1::setMinimumFee` by emitting the `MinimumFeeChanged` event using the `_feeInWei` parameter eg:

**Linea:**
Acknowledged; will be part of a future release.

Vulnerable code:
function setMinimumFee(uint256 _feeInWei) external onlyRole(MINIMUM_FEE_SETTER_ROLE) {
    uint256 previousMinimumFee = minimumFeeInWei;
    minimumFeeInWei = _feeInWei;

    emit MinimumFeeChanged(previousMinimumFee, _feeInWei, msg.sender);
  }