Farseen0/scar-pairs huggingface dataset:
HIGH severity: Rounding error in unstaking esMeta. **Severity:** Medium

**Context:** [`MetaManager.sol#L84`](https://github.com/getmetafinance/meta/blob/00bbac1613fa69e4c180ff53515451df4df9f69e/contracts/meta/MetaManager.sol#L84)

**Description:**
Users can start unstaking esMETA by calling `MetaManager::unstake` providing the amount and vesting...

Vulnerable code:
unstakeRatio = amount / timeToExit = 1000e18/(50 days) = 231481481481481

unstakeRatio * 50 days = 231481481481481 * 50 days = 999999999999997920000

function unstake(uint256 _amount, uint8 _timeInDays) external updateReward(msg.sender) {
  // ...
  unstakeRate[caller] = total * 1e18 / timeToExit;
  //
}

function getClaimable(address _user) public view returns (uint256 amount) {//@audit-info time windowed rewards
    if (time2fullRedemption[_user] > lastWithdrawTime[_user]) {//@audit-info user already withdrawed after full redemption time, no rewards are available to claim
        amount = block.timestamp > time2fullRedemption[_user]
            ? unstakeRate[_user] *
                (time2fullRedemption[_user] - lastWithdrawTime[_user]) / 1e18
            : unstakeRate[_user] *
                (block.timestamp - lastWithdrawTime[_user]) / 1e18;
    }
}
HIGH severity: Cross-function reentrancy leading to double delegation. **Severity:** Critical

**Path:** ERC20Pods.sol#L116-L151

**Description:** 

In the function `_beforeTokenTransfer` that is being overloaded to process
the delegations transfers for various pods, user pods are being read from
`_pods` mapping and then the code checks whether the “`from`” and...

Vulnerable code:
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override virtual {
       super._beforeTokenTransfer(from, to, amount);
       if (amount > 0 && from != to) {
           address[] memory a = _pods[from].items.get();
           address[] memory b = _pods[to].items.get();
           for (uint256 i = 0; i < a.length; i++) {
               address pod = a[i];
               uint256 j;
               for (j = 0; j < b.length; j++) {
                   if (pod == b[j]) {
                       // Both parties are participating of the same Pod
                       _updateBalances(pod, from, to, amount);
                       b[j] = address(0);
                       break;
                   }
               }
               if (j == b.length) {
                   // Sender is participating in a Pod, but receiver is not
                   _updateBalances(pod, from, address(0), amount);
               }
           }
           for (uint256 j = 0; j < b.length; j++) {
               address pod = b[j];
               if (pod != address(0)) {
                   // Receiver is participating in a Pod, but sender is not
                   _updateBalances(pod, address(0), to, amount);
               }
           }
       }
   }
HIGH severity: Reentrancy leading to doubledelegation. **Severity:** Critical

**Path:** ERC20Pods.sol#L85-L96

**Description:** 

In the function `_removeAllPods` contract iterates over the delegated pods
and burns the delegation balances via calling `updateBalances` on pods,
the bug arises since the balance is saved once on line 87, and an illicit...

Vulnerable code:
function _removeAllPods(address account) internal virtual {
       address[] memory items = _pods[account].items.get();
       uint256 balance = balanceOf(account);
       unchecked {
           for (uint256 i = items.length; i > 0; i--) {
               if (balance > 0) {
                   _updateBalances(items[i - 1], account, address(0), balance);
               }
               _pods[account].remove(items[i - 1]);
           }
       }
   }
LOW severity: Frontrunning to block user withdrawal. **Severity:** High

**Path:** St1inch.sol:#L118-L134, L145-L165

**Description:** 

The functions `withdraw` and `withdrawTo` can be frontrunned by any illicit
actor by calling `depositFor` or `depositForWithPermit` and passing the
victims account and 0 as amount and any lock period as duration...

Vulnerable code:
function depositFor(
       address account,
       uint256 amount,
       uint256 duration
   ) external {
       _deposit(account, amount, duration);
   }

   function depositForWithPermit(
       address account,
       uint256 amount,
       uint256 duration,
       bytes calldata permit
   ) public {
       oneInch.safePermit(permit);
       _deposit(account, amount, duration);

   }

function _deposit(
       address account,
       uint256 amount,
       uint256 duration
   ) private {
       if (_deposits[account] > 0 && amount > 0 && duration > 0) revert 
ChangeAmountAndUnlockTimeForExistingAccount();
       if (amount > 0) {
           oneInch.transferFrom(msg.sender, address(this), amount);
           _deposits[account] += amount;
           totalDeposits += amount;
       }
       uint256 lockedTill = Math.max(_unlockTime[account], block.timestamp) + 
duration;
       uint256 lockedPeriod = lockedTill - block.timestamp;
       if (lockedPeriod < MIN_LOCK_PERIOD) revert LockTimeLessMinLock();
       if (lockedPeriod > MAX_LOCK_PERIOD) revert LockTimeMoreMaxLock();
       _unlockTime[account] = lockedTill;
       _mint(account, _balanceAt(_deposits[account], lockedTill) / 
_VOTING_POWER_DIVIDER - balanceOf(account));

   }
LOW severity: Blocking the minimal registrant in the whitelist. **Severity:** High

**Path:** WhitelistRegistry.sol:#L49-L70

**Description:** 

The function `register` is used to register a new resolver if the voting power
meets the threshold requirement, in case the whitelist is full
(`_whitelist.length() == maxWhitelisted`) the function needs to find...

Vulnerable code:
function register() external {
       if (token.votingPowerOf(msg.sender) < resolverThreshold) revert BalanceLessThanThreshold();
       uint256 whitelistLength = _whitelist.length();
       if (whitelistLength < maxWhitelisted) {
           _whitelist.add(msg.sender);
           return;
       }

       address minResolver = msg.sender;
       uint256 minBalance = token.balanceOf(msg.sender);
       for (uint256 i = 0; i < whitelistLength; ++i) {
           address curWhitelisted = _whitelist.at(i);
           uint256 balance = token.balanceOf(curWhitelisted);
           if (balance < minBalance) {
               minResolver = curWhitelisted;
               minBalance = balance;
           }

       }

       if (minResolver == msg.sender) revert NotEnoughBalance();
       _whitelist.remove(minResolver);
       _whitelist.add(msg.sender);
       emit Registered(msg.sender);

   }
LOW severity: Arbitrary external call. **Severity:** Low

**Path:** Settlement.sol:#L96-L102

**Description:** 

In the function `fillOrderInteraction` that is being called as a callback
within `OrderMixin` (limit-order-protocol) the parameter `interactiveData` is
being parsed as arrays of targets and calldata with the purpose of...

Vulnerable code:
uint256 length = targets.length;
           if (length != calldatas.length) revert IncorrectCalldataParams();
           for (uint256 i = 0; i < length; i++) {
               // solhint-disable-next-line avoid-low-level-calls
               (bool success, ) = targets[i].call(calldatas[i]);
               if (!success) revert FailedExternalCall();
           }
HIGH severity: [ONCInfinite voting power. **Severity:** Critical

**Path:** ERC20Pods.sol:_updateBalances#L115-L132

**Description:** 

The 1INCH staking contract `St1inch.sol` implements `ERC20Pods.sol` and therefore supports attaching pods for delegating voting power, such as the pod `PowerPod.sol`. A user can add a pod for themselves...

Vulnerable code:
function _updateBalances(address pod, address from, address to, uint256 amount) private {
    bytes4 selector = IPod.updateBalances.selector;
    bytes4 exception = InsufficientGas.selector;
    uint256 gasLimit = podCallGasLimit;
    assembly {  // solhint-disable-line no-inline-assembly
        let ptr := mload(0x40)
        mstore(ptr, selector)
        mstore(add(ptr, 0x04), from)
        mstore(add(ptr, 0x24), to)
        mstore(add(ptr, 0x44), amount)

        if lt(div(mul(gas(), 63), 64), gasLimit) {
            mstore(0, exception)
            revert(0, 4)
        }
        pop(call(gasLimit, pod, 0, ptr, 0x64, 0, 0))
    }
}
HIGH severity: [ONCArithmetic gas optimisation. **Severity:** Low

**Path:** St1nch.sol:_withdraw#L202

**Description:** 

In the function `St1nch.sol:_withdraw` on line 202 the unlock time of the deposit is set to the minimum of the original unlock time and the current timestamp. However, this arithmetic operation is redundant and the unlock...

Vulnerable code:
function _withdraw(Depositor memory depositor, uint256 amount, uint256 balance) private {
    totalDeposits -= amount;
    depositor.amount = 0;
    // keep unlockTime in storage for next tx optimization
    depositor.unlockTime = uint40(Math.min(depositor.unlockTime, block.timestamp));
    depositors[msg.sender] = depositor; // SSTORE
    _burn(msg.sender, balance);
}
HIGH severity: [ONCEarly withdrawal is impossible if the Fee Receiver is not set. **Severity:** Low

**Path:** St1inch.sol:earlyWithdrawTo#L155-L170

**Description:** 

The function `St1inch.sol:earlyWithdrawTo` is designed to give users the opportunity to withdraw earlier than the staking period end time. However, in the case where the `feeReceiver` address has not yet been...

Vulnerable code:
function earlyWithdrawTo(address to, uint256 minReturn, uint256 maxLoss) public {
    Depositor memory depositor = depositors[msg.sender]; // SLOAD
    if (emergencyExit || block.timestamp >= depositor.unlockTime) revert StakeUnlocked();
    uint256 allowedExitTime = depositor.lockTime + (depositor.unlockTime - depositor.lockTime) * minLockPeriodRatio / _ONE;
    if (block.timestamp < allowedExitTime) revert MinLockPeriodRatioNotReached();

    uint256 amount = depositor.amount;
    if (amount > 0) {
        uint256 balance = balanceOf(msg.sender);
        (uint256 loss, uint256 ret) = _earlyWithdrawLoss(amount, balance);
        if (ret < minReturn) revert MinReturnIsNotMet();
        if (loss > maxLoss) revert MaxLossIsNotMet();
        if (loss > amount * maxLossRatio / _ONE) revert LossIsTooBig();

        _withdraw(depositor, balance); console.log("ret:", ret); console.log("loss:",loss); console.log("feeReceiver: ",feeReceiver);
        oneInch.safeTransfer(to, ret);
        oneInch.safeTransfer(feeReceiver, loss);    
    }
}
HIGH severity: [BAB-3] Stealing ETH using reentrancy in minting pass. **Severity:** Critical

**Path:** BabylonCore.sol:participate (L89-114)

**Description:** In `BabylonCore.sol:participate` a user can buy tickets, which are minting pass NFTs and minted to the user. The minting pass contracts uses `_safeMint`, which makes an external call with `onERC721Received`...

Vulnerable code:
function participate(uint256 id, uint256 tickets) external payable {
    ListingInfo storage listing =  _listingInfos[id];
    require(
        _tokensController.checkApproval(listing.creator, listing.item),
        "BabylonCore: Token is no longer owned or approved to the controller"
    );
    require(listing.state == ListingState.Active, "BabylonCore: Listing state should be active");
    require(block.timestamp >= listing.timeStart, "BabylonCore: Too early to participate");
    uint256 current = listing.currentTickets;
    require(current + tickets <= listing.totalTickets, "BabylonCore: no available tickets");
    uint256 totalPrice = listing.price * tickets;
    require(msg.value == totalPrice, "BabylonCore: msg.value doesn't match price for tickets");

    IBabylonMintPass(listing.mintPass).mint(msg.sender, tickets);

    listing.currentTickets = current + tickets;

    emit NewParticipant(id, msg.sender, tickets);

    if (listing.currentTickets == listing.totalTickets) {
        listing.randomRequestId = _randomProvider.requestRandom(id);
        listing.state = ListingState.Resolving;

        emit ListingResolving(id, listing.randomRequestId);
    }
}
HIGH severity: [BAB-2] Anyone can create a listing for another user's approved/listed ER. **Severity:** High

**Path:** TokensController.sol:checkApproval (L47-61)

**Description:** A user can create a listing for an ERC721 or ERC1155 in the BabylonCore contract. This contract checks whether through `TokensController.sol:checkApproval` whether the controller is approved for the...

Vulnerable code:
function checkApproval(
    address creator,
    IBabylonCore.ListingItem calldata item
) external view returns (bool) {
    if (item.itemType == IBabylonCore.ItemType.ERC721) {
        address operator = IERC721(item.token).getApproved(item.identifier);
        return address(this) == operator;
    } else if (item.itemType == IBabylonCore.ItemType.ERC1155) {
        bool approved = IERC1155(item.token).isApprovedForAll(creator, address(this));
        uint256 amount = IERC1155(item.token).balanceOf(creator, item.identifier);
        return (approved && (amount >= item.amount));
    }

    return false;
}

if (item.itemType == IBabylonCore.ItemType.ERC721) {
    address operator = IERC721(item.token).getApproved(item.identifier);
    address owner = IERC721(item.token).ownerOf(item.identifier);
    return creator == owner && address(this) == operator;
}
LOW severity: [BAB-6] Users can relist their tokens. **Severity:** Medium

**Path:** BabylonCore.sol:startListing (L54-87)

**Description:** When a user creates a listing for their token, the listing ID is stored in `_ids[token][identifier]`. However, this listing ID is never used or checked again, e.g. to check whether the user has already listed...

Vulnerable code:
function startListing(
    ListingItem calldata item,
    IEditionsExtension.EditionInfo calldata edition,
    uint256 timeStart,
    uint256 price,
    uint256 totalTickets,
    uint256 donationBps
) external {
    [..]
    uint256 newListingId = _lastListingId + 1;
    _ids[item.token][item.identifier] = newListingId;
    ListingInfo storage listing = _listingInfos[newListingId];
    [..]
}
LOW severity: [BAB-4] Missing upper limit checks. **Severity:** Low

**Path:** BabylonCore.sol:startListing (L62-68)

**Description:** The function `BabylonCore.sol:startListing` (L62-68) doesn’t check an upper limit for some variables.
For example:

- `timeStart` (creator can start a listing and indicate a timestamp for a very long period of...

Vulnerable code:
function startListing(
    ListingItem calldata item,
    IEditionsExtension.EditionInfo calldata edition,
    uint256 timeStart,
    uint256 price,
    uint256 totalTickets,
    uint256 donationBps
) external;
LOW severity: [BAB-1] Missing zero address checks. **Severity:** Informational

**Path:** TokensController.sol:setBabylonCore (L43-45), BabylonEditionsExtension.sol:setBabylonCore (L60-66), RandomProvider.sol:setBabylonCore (L46-48)

**Description:** In all of the contracts, the parameter core for the function setBabylonCore is not checked against.....

Vulnerable code:
contract TokensController is ITokensController, Ownable {
    [..]
    function setBabylonCore(address core) external onlyOwner {
        _core = core;
    }
    [..]
}

contract RandomProvider is IRandomProvider, Ownable, VRFConsumerBaseV2 {
    [..]
    function setBabylonCore(IBabylonCore core) external onlyOwner {
        _core = core;
    }
    [..]
}

contract BabylonEditionsExtension is Ownable, IEditionsExtension, ICreatorExtensionTokenURI {
    [..]
    function setBabylonCore(address core) external onlyOwner {
        _core = core;
    }
    [..]
}

require(core != address(0), "NOT_ZERO");
HIGH severity: ERRE-ENTRANCY ATTACK. **Severity:** Critical

**Path:** PolygonZkEvmBridge.sol

**Description:**

In the function bridgeAsset() any user can bridge his ERC20 tokens specifying the destination network and the destination address, in case it is not a wrapped token it will call transferFrom (SafeERC20.safeTransferFrom)...

Vulnerable code:
// In order to support fee tokens check the amount received, not the transferred
uint256 balanceBefore = IERC20Upgradeable(token).balanceOf(address(this));
IERC20Upgradeable(token).safeTransferFrom(msg.sender, address(this), amount);
uint256 balanceAfter = IERC20Upgradeable(token).balanceOf(address(this));

// Override leafAmount with the received amount
leafAmount = balanceAfter - balanceBefore;
HIGH severity: MISSING CONSTRAINT IN PIL LEADING TO PROVING FAKE INCLUSION IN THE SMT. **Severity:** Critical

**Path:** storage.pil

**Description:**

The Storage state machine uses SMT (Sparse Merkle Tree) and implements provable CRUD operations in conjunction with Storage ROM.

In order to prove the (Key, Value) inclusion in the SMT, the state machine represents the key as a bit...

Vulnerable code:
sRD {
       SR0 + 2^32*SR1, SR2 + 2^32*SR3, SR4 + 2^32*SR5, SR6 + 2^32*SR7,
       sKey[0], sKey[1], sKey[2], sKey[3],
       op0, op1, op2, op3,
       op4, op5, op6, op7,
       incCounter
   } is
   Storage.iLatchGet {
       Storage.oldRoot0, Storage.oldRoot1, Storage.oldRoot2, Storage.oldRoot3,
       Storage.rkey0, Storage.rkey1, Storage.rkey2, Storage.rkey3,
       Storage.valueLow0, Storage.valueLow1, Storage.valueLow2, Storage.valueLow3,
       Storage.valueHigh0, Storage.valueHigh1, Storage.valueHigh2, Storage.valueHigh3,
       Storage.incCounter + 2
   };

; If next key bit is zero, then the sibling hash must be at the right (sibling's key bit is 1)
${GetNextKeyBit()} => RKEY_BIT
RKEY_BIT                        :JMPZ(Get_SiblingIsRight)

; Update remaining key
                               :ROTATE_LEVEL
                               :CLIMB_RKEY

                               :JMP(Get_ClimbTree)
HIGH severity: INCORRECT CTX ASSIGNATION LEADING TO ADDITION OF RANDOM AMOUNT OF ETHER TO THE SEQUENCER BALANCE. **Severity:** Critical

**Path:** process-tx.zkasm, precompiled/identity.zkasm

**Description:** 

The zkEVM ROM architecture uses Contexts (CTX) to divide and emulate virtual address to physical address translation between call contexts inside one transaction. One CTX address space is used to...

Vulnerable code:
pol addrRel = ind*E0 + indRR*RR + offset;
pol addr = useCTX*CTX*2^18 + isStack*2^16 + isStack*SP + isMem*2^17+ addrRel;

;; Send gas spent to sequencer
sendGasSeq:
       $ => A          :MLOAD(txGasLimit)
       A - GAS => A

       $ => B          :MLOAD(txGasPrice)
       ; Mul operation with Arith
       A               :MSTORE(arithA)
       B               :MSTORE(arithB), CALL(mulARITH)
       $ => D          :MLOAD(arithRes1) ; value to pay the sequencer in D
HIGH severity: MISSING CONSTRAINT IN PIL LEADING TO EXECUTION FLOW HIJACK. **Severity:** Critical

**Path:** utils.zkasm, main.pil

**Description:** 

The combination of the free input checking in zkEVM ROM and a missing constraint in main.pil leads to execution hijack with a possibility to jump to an arbitrary address in ROM.
One of the impacts is the arbitrary increase.....

Vulnerable code:
; C = [c7, c6, ..., c0]
; JMPN instruction assures c0 is within the range [0, 2^32 - 1]
${GAS >> 6} => C        :JMPN(failAssert)
${GAS & 0x3f} => D

; since D is assured to be less than 0x40
; it is enforced that [c7, c6, ..., c1] are 0 since there is no value multiplied by 64
; that equals the field
; Since e0 is assured to be less than 32 bits, c0 * 64 + d0 could not overflow the field
C * 64 + D              :ASSERT

zkPC' = doJMP * (finalJmpAddr - nextNoJmpZkPC) + elseJMP * (finalElseAddr - nextNoJmpZkPC) + nextNoJmpZkPC;

Where both finalJmpAddr and nextNoJmpZkPC are known values in the ROM program compilation phase.

In order to be able to jump to arbitrary zkPC, the attacker needs to calculate corresponding values for isNeg and op0; this can be done using derived formulas:

isNeg = (zkPC_arbitrary - nextNoJmpZkPC) * (finalJmpAddr - nextNoJmpZkPC)-1 mod P

op0 = - isNeg * 232 mod P

The attack breakdown:

At this point attacker has a primitive to jump to an arbitrary address; the next step will be to find a suitable gadget to jump to, the main requirements for the target are to:

- Not to corrupt/revert zkEVM execution

- Impact favourably for the attacker

One of the jump chains found is to use one of the *CALL opcodes as the start of the attack chain to call the computeSendGasCall and subsequently craft a jump into the refundGas label's code:
https://github.com/0xPolygonHermez/zkevm-rom/blob/develop/main/process-tx.zkasm#L496-L498
HIGH severity: BUG IN MAXMEM HANDLING CAN HALT THE BATCH VERIFICATION. **Severity:**  High

**Path:** main.zkasm, create-terminate-context.zkasm

**Description:** 

In the zkEVM ROM the MAXMEM register that is used to set the biggest offset of memory used is being set only once to zero - for the first step of the execution trace.
The contracts that will call...

Vulnerable code:
pol addrRel = ind*E0 + indRR*RR + offset;

pol maxMemRel = isMem * addrRel;

pol maxMemCalculated = isMaxMem*(addrRel - MAXMEM) + MAXMEM;

MAXMEM' = setMAXMEM * (op0 - maxMemCalculated) + maxMemCalculated;

pol diffMem = isMaxMem* ( (maxMemRel - MAXMEM) -
                  (MAXMEM - maxMemRel) ) +
         (MAXMEM - maxMemRel);
isMaxMem * (1 - isMaxMem) = 0;
...
diffMem in Global.BYTE2;

$ => B                      :MLOAD(lastMemOffset)
; If the binary has a carry, means the mem expansion is very big. We can jump to oog directly
; offset + length in B
$ => B                      :ADD, JMPC(outOfGas)
; check new memory length is lower than 2**22 - 31 - 1 (max supported memory expansion for %TX_GAS_LIMIT of gas)
%MAX_MEM_EXPANSION_BYTES => A
$                           :LT,JMPC(outOfGas)
HIGH severity: INCORRECT LIMIT CHECK IN ZKASM ECRECOVER IMPLEMENTATION. **Severity:** Medium

**Path:** [StakeableVestingFactory.sol:deployStakeableVesting#L39-L71](https://github.com/0xPolygonHermez/zkevm-rom/blob/develop/main/ecrecover/ecrecover.zkasm#L61C8-L67)

**Description:** 

In the zkASM implementation of the ecrecover function, there must be a check against...

Vulnerable code:
CONSTL %FNEC_DIV_TWO = 57896044618658097711785492504343953926418782139537452191302581570759080747169n
...
ecrecover_tx:
       %FNEC_DIV_TWO   :MSTORE(ecrecover_s_upperlimit)
...

       ; s in [1, ecrecover_s_upperlimit]
       $ => A      :MLOAD(ecrecover_s_upperlimit)
       $ => B      :MLOAD(ecrecover_s)
       $           :LT,JMPC(ecrecover_s_is_too_big)
       0n => A
       $           :EQ,JMPC(ecrecover_s_is_zero)