Some unusual problems arise in the use of the `GoatV1Factory.sol#createPair()` function.
If you create a new pool for tokens and add liquidity using the `GoatRouterV1.sol#addLiquidity()` function, the bootstrap function of the protocol is broken. Therefore, an attacker can perform the front running attack on the `GoatRouterV1.sol#addLiquidity()` function by front calling `GoatV1Factory.sol#createPair()`.\\nIf a pool for the token does not exist, the LP can create a new pool using the `GoatV1Factory.sol#createPair()` function. Next he calls `GoatRouterV1.sol#addLiquidity()` to provide liquidity. At this time, the amount of WETH and ERC20Token provided to the pool is calculated in the `GoatRouterV1.sol#_addLiquidity()` function.\\n```\\n    function _addLiquidity(\\n        address token,\\n        uint256 tokenDesired,\\n        uint256 wethDesired,\\n        uint256 tokenMin,\\n        uint256 wethMin,\\n        GoatTypes.InitParams memory initParams\\n    ) internal returns (uint256, uint256, bool) {\\n        GoatTypes.LocalVariables_AddLiquidity memory vars;\\n        GoatV1Pair pair = GoatV1Pair(GoatV1Factory(FACTORY).getPool(token));\\n        if (address(pair) == address(0)) {\\n            // First time liquidity provider\\n            pair = GoatV1Pair(GoatV1Factory(FACTORY).createPair(token, initParams));\\n            vars.isNewPair = true;\\n        }\\n\\n        if (vars.isNewPair) {\\n// rest of codeSNIP\\n        } else {\\n            /**\\n             * @dev This block is accessed after the presale period is over and the pool is converted to AMM\\n             */\\n       (uint256 wethReserve, uint256 tokenReserve) = pair.getReserves();\\n       uint256 tokenAmountOptimal = GoatLibrary.quote(wethDesired, wethReserve, tokenReserve);\\n       if (tokenAmountOptimal <= tokenDesired) {\\n           if (tokenAmountOptimal < tokenMin) {\\n               revert GoatErrors.InsufficientTokenAmount();\\n           }\\n           (vars.tokenAmount, vars.wethAmount) = (tokenAmountOptimal, wethDesired);\\n       } else {\\n           uint256 wethAmountOptimal = GoatLibrary.quote(tokenDesired, tokenReserve, wethReserve);\\n           assert(wethAmountOptimal <= wethDesired);\\n           if (wethAmountOptimal < wethMin) revert GoatErrors.InsufficientWethAmount();\\n           (vars.tokenAmount, vars.wethAmount) = (tokenDesired, wethAmountOptimal);\\n       }\\n   }\\n   return (vars.tokenAmount, vars.wethAmount, vars.isNewPair);\\n    }\\n```\\n\\nFor simplicity, let's only consider from #L250 to #L256.\\nL250:wethReserve = virtualEth, tokenReserve = initialTokenMatch - (initialTokenMatch - ((virtualEth * initialTokenMatch)/(virtualEth + bootstrapEth)) + + (virtualEthinitialTokenMatchbootstrapEth)/(virtualEth + bootstrapEth) ^ 2) = = ((virtualEth * initialTokenMatch)/(virtualEth + bootstrapEth)) - (virtualEthinitialTokenMatchbootstrapEth)/(virtualEth + bootstrapEth) ^ 2 L251:tokenAmountOptimal = wethDesired * wethReserve / tokenReserve vars.tokenAmount = tokenAmountOptimal vars.wethAmount = wethDesired\\nAt this time, At this time, the calculated balance of ETH and token is sent to the pool, and `GoatV1Pair(vars.pair).mint()` is called in the `GoatRouterV1.sol#addLiquidity()` function.\\n```\\n    function addLiquidity(\\n        address token,\\n        uint256 tokenDesired,\\n        uint256 wethDesired,\\n        uint256 tokenMin,\\n        uint256 wethMin,\\n        address to,\\n        uint256 deadline,\\n        GoatTypes.InitParams memory initParams\\n    ) external nonReentrant ensure(deadline) returns (uint256, uint256, uint256) {\\n// rest of codeSNIP\\n    IERC20(vars.token).safeTransferFrom(msg.sender, vars.pair, vars.actualTokenAmount);\\n    if (vars.wethAmount != 0) {\\n        IERC20(WETH).safeTransferFrom(msg.sender, vars.pair, vars.wethAmount);\\n    }\\n    vars.liquidity = GoatV1Pair(vars.pair).mint(to);\\n// rest of codeSNIP\\n    }\\n```\\n\\nNext, the `GoatV1Pair(vars.pair).mint()` function checks the validity of the transmitted token.\\n```\\n    function mint(address to) external nonReentrant returns (uint256 liquidity) {\\n    // rest of codeSNIP\\n        if (_vestingUntil == _MAX_UINT32) {\\n            // Do not allow to add liquidity in presale period\\n            if (totalSupply_ > 0) revert GoatErrors.PresalePeriod();\\n            // don't allow to send more eth than bootstrap eth\\n            if (balanceEth > mintVars.bootstrapEth) {\\n                revert GoatErrors.SupplyMoreThanBootstrapEth();\\n            }\\n\\n            if (balanceEth < mintVars.bootstrapEth) {\\n                (uint256 tokenAmtForPresale, uint256 tokenAmtForAmm) = _tokenAmountsForLiquidityBootstrap(\\n                    mintVars.virtualEth, mintVars.bootstrapEth, balanceEth, mintVars.initialTokenMatch\\n                );\\n           if (balanceToken != (tokenAmtForPresale + tokenAmtForAmm)) {\\n                    revert GoatErrors.InsufficientTokenAmount();\\n                }\\n                liquidity =\\n                    Math.sqrt(uint256(mintVars.virtualEth) * uint256(mintVars.initialTokenMatch)) - MINIMUM_LIQUIDITY;\\n            } else {\\n                // This means that user is willing to make this pool an amm pool in first liquidity mint\\n           liquidity = Math.sqrt(balanceEth * balanceToken) - MINIMUM_LIQUIDITY;\\n           uint32 timestamp = uint32(block.timestamp);\\n           _vestingUntil = timestamp + VESTING_PERIOD;\\n            }\\n            mintVars.isFirstMint = true;\\n        }\\n    // rest of codeSNIP\\n    }\\n```\\n\\nIn here, `balanceToken = vars.tokenAmount (value:tokenAmountOptimal)` and `tokenAmtForPresale + tokenAmtForAmm` is calculated follows.\\ntokenAmtForPresale = initialTokenMatch - (virtualEth * initialTokenMatch / (virtualEth + bootstrapEth)) - - (balanceEth(value:wethDesired)*initialTokenMatch/(virtualEth+balanceEth)) tokenAmtForAmm = (virtualEth * initialTokenMatch * bootstrapEth) / (virtualEth + bootstrapEth) ^ 2\\nAs a result, `(balanceToken != (tokenAmtForPresale + tokenAmtForAmm)) == true`, the `GoatRouterV1.sol#addLiquidity()` function is reverted. In this case, If the initial LP want to provide liquidity to the pool, he must pay an amount of WETH equivalent to bootstrapEth to execute #L146. As a result, the bootstrap function is broken.\\nBased on this fact, an attacker can front run the `createPair()` function if he finds the `addLiquidity()` function in the mempool.
It is recommended that the `GoatV1Factory.sol#.createPair()` function be called only from the `GoatRouterV1` contract.
The bootstrap function of the protocol is broken and the initial LP must pay an amount of WETH equivalent to bootstrapEth.
```\\n    function _addLiquidity(\\n        address token,\\n        uint256 tokenDesired,\\n        uint256 wethDesired,\\n        uint256 tokenMin,\\n        uint256 wethMin,\\n        GoatTypes.InitParams memory initParams\\n    ) internal returns (uint256, uint256, bool) {\\n        GoatTypes.LocalVariables_AddLiquidity memory vars;\\n        GoatV1Pair pair = GoatV1Pair(GoatV1Factory(FACTORY).getPool(token));\\n        if (address(pair) == address(0)) {\\n            // First time liquidity provider\\n            pair = GoatV1Pair(GoatV1Factory(FACTORY).createPair(token, initParams));\\n            vars.isNewPair = true;\\n        }\\n\\n        if (vars.isNewPair) {\\n// rest of codeSNIP\\n        } else {\\n            /**\\n             * @dev This block is accessed after the presale period is over and the pool is converted to AMM\\n             */\\n       (uint256 wethReserve, uint256 tokenReserve) = pair.getReserves();\\n       uint256 tokenAmountOptimal = GoatLibrary.quote(wethDesired, wethReserve, tokenReserve);\\n       if (tokenAmountOptimal <= tokenDesired) {\\n           if (tokenAmountOptimal < tokenMin) {\\n               revert GoatErrors.InsufficientTokenAmount();\\n           }\\n           (vars.tokenAmount, vars.wethAmount) = (tokenAmountOptimal, wethDesired);\\n       } else {\\n           uint256 wethAmountOptimal = GoatLibrary.quote(tokenDesired, tokenReserve, wethReserve);\\n           assert(wethAmountOptimal <= wethDesired);\\n           if (wethAmountOptimal < wethMin) revert GoatErrors.InsufficientWethAmount();\\n           (vars.tokenAmount, vars.wethAmount) = (tokenDesired, wethAmountOptimal);\\n       }\\n   }\\n   return (vars.tokenAmount, vars.wethAmount, vars.isNewPair);\\n    }\\n```\\n