Vulnerability: If the vault's side token balance is 0 or a tiny amount, then most if not all IG Bear trades will revert due to incorrect check of computation error during delta hedge amount calculation
Severity: medium

Description:
When delta hedge amount is calculated in `FinanceIGDelta.deltaHedgeAmount`, the last step is to verify that delta hedge amount to sell is slightly more than vault's side token due to computation error. The check is the following:\\n```\\n    if (SignedMath.abs(tokensToSwap) > params.sideTokensAmount) {\\n        if (SignedMath.abs(tokensToSwap) - params.sideTokensAmount < params.sideTokensAmount / 10000) {\\n            tokensToSwap = SignedMath.revabs(params.sideTokensAmount, true);\\n        }\\n    }\\n```\\n\\nThe check works correctly most of the time, but if the vault's side token (param.sideTokensAmount) is 0 or close to it, then the check will always fail, because `0 / 10000 = 0` and unsigned amount can not be less than 0. This means that even tiny amount to sell (like 1 wei) will revert the transaction if the vault has 0 side tokens.\\nVault's side token is 0 when:\\nthe current price trades above high boundary (Kb)\\nand IG Bull used liquidity equals 0\\nIn such situation, any IG bear trade doesn't impact hedge amount, but due to computation error will almost always result in tiny but non-0 side token amount to sell value, which will revert due to incorrect comparision described above.

Impact:
Almost all IG Bear trades will revert in certain situations, leading to core protocol function being unavailable and potentially loss of funds to the users who expected to do these trades.\\nProof Of Concept\\nCopy to attack.t.sol:\\n```\\n// SPDX-License-Identifier: UNLICENSED\\npragma solidity ^0.8.15;\\n\\nimport {Test} from "forge-std/Test.sol";\\nimport {console} from "forge-std/console.sol";\\nimport {UD60x18, ud, convert} from "@prb/math/UD60x18.sol";\\n\\nimport {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";\\nimport {IPositionManager} from "@project/interfaces/IPositionManager.sol";\\nimport {Epoch} from "@project/lib/EpochController.sol";\\nimport {AmountsMath} from "@project/lib/AmountsMath.sol";\\nimport {EpochFrequency} from "@project/lib/EpochFrequency.sol";\\nimport {OptionStrategy} from "@project/lib/OptionStrategy.sol";\\nimport {AddressProvider} from "@project/AddressProvider.sol";\\nimport {MarketOracle} from "@project/MarketOracle.sol";\\nimport {FeeManager} from "@project/FeeManager.sol";\\nimport {Vault} from "@project/Vault.sol";\\nimport {TestnetToken} from "@project/testnet/TestnetToken.sol";\\nimport {TestnetPriceOracle} from "@project/testnet/TestnetPriceOracle.sol";\\nimport {DVPUtils} from "./utils/DVPUtils.sol";\\nimport {TokenUtils} from "./utils/TokenUtils.sol";\\nimport {Utils} from "./utils/Utils.sol";\\nimport {VaultUtils} from "./utils/VaultUtils.sol";\\nimport {MockedIG} from "./mock/MockedIG.sol";\\nimport {MockedRegistry} from "./mock/MockedRegistry.sol";\\nimport {MockedVault} from "./mock/MockedVault.sol";\\nimport {TestnetSwapAdapter} from "@project/testnet/TestnetSwapAdapter.sol";\\nimport {PositionManager} from "@project/periphery/PositionManager.sol";\\n\\n\\ncontract IGTradeTest is Test {\\n    using AmountsMath for uint256;\\n\\n    address admin = address(0x1);\\n\\n    // User of Vault\\n    address alice = address(0x2);\\n    address bob = address(0x3);\\n\\n    //User of DVP\\n    address charlie = address(0x4);\\n    address david = address(0x5);\\n\\n    AddressProvider ap;\\n    TestnetToken baseToken;\\n    TestnetToken sideToken;\\n    FeeManager feeManager;\\n\\n    MockedRegistry registry;\\n\\n    MockedVault vault;\\n    MockedIG ig;\\n    TestnetPriceOracle priceOracle;\\n    TestnetSwapAdapter exchange;\\n    uint _strike;\\n\\n    function setUp() public {\\n        vm.warp(EpochFrequency.REF_TS);\\n        //ToDo: Replace with Factory\\n        vm.startPrank(admin);\\n        ap = new AddressProvider(0);\\n        registry = new MockedRegistry();\\n        ap.grantRole(ap.ROLE_ADMIN(), admin);\\n        registry.grantRole(registry.ROLE_ADMIN(), admin);\\n        ap.setRegistry(address(registry));\\n\\n        vm.stopPrank();\\n\\n        vault = MockedVault(VaultUtils.createVault(EpochFrequency.WEEKLY, ap, admin, vm));\\n        priceOracle = TestnetPriceOracle(ap.priceOracle());\\n\\n        baseToken = TestnetToken(vault.baseToken());\\n        sideToken = TestnetToken(vault.sideToken());\\n\\n        vm.startPrank(admin);\\n       \\n        ig = new MockedIG(address(vault), address(ap));\\n        ig.grantRole(ig.ROLE_ADMIN(), admin);\\n        ig.grantRole(ig.ROLE_EPOCH_ROLLER(), admin);\\n        vault.grantRole(vault.ROLE_ADMIN(), admin);\\n        vm.stopPrank();\\n        ig.setOptionPrice(1e3);\\n        ig.setPayoffPerc(0.1e18); // 10 % -> position paying 1.1\\n        ig.useRealDeltaHedge();\\n        ig.useRealPercentage();\\n        ig.useRealPremium();\\n\\n        DVPUtils.disableOracleDelayForIG(ap, ig, admin, vm);\\n\\n        vm.prank(admin);\\n        registry.registerDVP(address(ig));\\n        vm.prank(admin);\\n        MockedVault(vault).setAllowedDVP(address(ig));\\n        feeManager = FeeManager(ap.feeManager());\\n\\n        exchange = TestnetSwapAdapter(ap.exchangeAdapter());\\n    }\\n\\n    // try to buy/sell ig bull below strike for user's profit\\n    // this will not be hedged, and thus the vault should lose funds\\n    function test() public {\\n        _strike = 1e18;\\n        VaultUtils.addVaultDeposit(alice, 1e18, admin, address(vault), vm);\\n        VaultUtils.addVaultDeposit(bob, 1e18, admin, address(vault), vm);\\n\\n        Utils.skipWeek(true, vm);\\n\\n        vm.prank(admin);\\n        ig.rollEpoch();\\n\\n        VaultUtils.logState(vault);\\n        DVPUtils.debugState(ig);\\n\\n        testBuyOption(1.24e18, 1, 0); // re-balance to have 0 side tokens\\n        testBuyOption(1.24e18, 0, 0.1e18); // reverts due to computation error and incorrect check to fix it\\n    }\\n\\n    function testBuyOption(uint price, uint128 optionAmountUp, uint128 optionAmountDown) internal {\\n\\n        vm.prank(admin);\\n        priceOracle.setTokenPrice(address(sideToken), price);\\n\\n        (uint256 premium, uint256 fee) = _assurePremium(charlie, _strike, optionAmountUp, optionAmountDown);\\n\\n        vm.startPrank(charlie);\\n        premium = ig.mint(charlie, _strike, optionAmountUp, optionAmountDown, premium, 10e18, 0);\\n        vm.stopPrank();\\n\\n        console.log("premium", premium);\\n    }\\n\\n    function testSellOption(uint price, uint128 optionAmountUp, uint128 optionAmountDown) internal returns (uint) {\\n        vm.prank(admin);\\n        priceOracle.setTokenPrice(address(sideToken), price);\\n\\n        uint256 charliePayoff;\\n        uint256 charliePayoffFee;\\n        {\\n            vm.startPrank(charlie);\\n            (charliePayoff, charliePayoffFee) = ig.payoff(\\n                ig.currentEpoch(),\\n                _strike,\\n                optionAmountUp,\\n                optionAmountDown\\n            );\\n\\n            charliePayoff = ig.burn(\\n                ig.currentEpoch(),\\n                charlie,\\n                _strike,\\n                optionAmountUp,\\n                optionAmountDown,\\n                charliePayoff,\\n                0.1e18\\n            );\\n            vm.stopPrank();\\n\\n            console.log("payoff received", charliePayoff);\\n        }\\n    }\\n\\n    function _assurePremium(\\n        address user,\\n        uint256 strike,\\n        uint256 amountUp,\\n        uint256 amountDown\\n    ) private returns (uint256 premium_, uint256 fee) {\\n        (premium_, fee) = ig.premium(strike, amountUp, amountDown);\\n        TokenUtils.provideApprovedTokens(admin, address(baseToken), user, address(ig), premium_*5, vm);\\n    }\\n}\\n```\\n\\nNotice: execution will revert when trying to buy IG Bear options.

Recommended mitigation:
Possibly check both relative (sideToken / 10000) and absolute (e.g. 1000 or side token UNIT / 10000) value. Alternatively, always limit side token to sell amount to max of side token balance when hedging (but needs additional research if that might create issues).