Users deposit assets to the vault but receives no strategy token in return
Due to a rounding error in Solidity, it is possible that a user deposits assets to the vault, but receives no strategy token in return due to issues in the following functions:\\nStrategyUtils._convertBPTClaimToStrategyTokens\\nBoosted3TokenPoolUtils._deposit\\nTwoTokenPoolUtils._deposit\\nThis affects both the TwoToken and Boosted3Token vaults\\n```\\nint256 internal constant INTERNAL_TOKEN_PRECISION = 1e8;\\nuint256 internal constant BALANCER_PRECISION = 1e18;\\n```\\n\\nWithin the `StrategyUtils._convertBPTClaimToStrategyTokens` function, it was observed that the numerator precision (1e8) is much smaller than the denominator precision (1e18).\\n```\\nFile: StrategyUtils.sol\\n    /// @notice Converts BPT to strategy tokens\\n    function _convertBPTClaimToStrategyTokens(StrategyContext memory context, uint256 bptClaim)\\n        internal pure returns (uint256 strategyTokenAmount) {\\n        if (context.vaultState.totalBPTHeld == 0) {\\n            // Strategy tokens are in 8 decimal precision, BPT is in 18. Scale the minted amount down.\\n            return (bptClaim * uint256(Constants.INTERNAL_TOKEN_PRECISION)) / \\n                BalancerConstants.BALANCER_PRECISION;\\n        }\\n\\n        // BPT held in maturity is calculated before the new BPT tokens are minted, so this calculation\\n        // is the tokens minted that will give the account a corresponding share of the new bpt balance held.\\n        // The precision here will be the same as strategy token supply.\\n        strategyTokenAmount = (bptClaim * context.vaultState.totalStrategyTokenGlobal) / context.vaultState.totalBPTHeld;\\n    }\\n```\\n\\nAs a result, the `StrategyUtils._convertBPTClaimToStrategyTokens` function might return zero strategy tokens under the following two conditions:\\nIf the `totalBPTHeld` is zero (First Deposit)\\nIf the `totalBPTHeld` is zero, the code at Line 31 will be executed, and the following formula is used:\\n```\\nstrategyTokenAmount = (bptClaim * uint256(Constants.INTERNAL_TOKEN_PRECISION)) / BalancerConstants.BALANCER_PRECISION;\\nstrategyTokenAmount = (bptClaim * 1e8) / 1e18\\nstrategyTokenAmount = ((10 ** 10 - 1) * 1e8) / 1e18 = 0\\n```\\n\\nDuring the first deposit, if the user deposits less than 1e10 BPT, Solidity will round down and `strategyTokenAmount` will be zero.\\nIf the `totalBPTHeld` is larger than zero (Subsequently Deposits)\\nIf the `totalBPTHeld` is larger than zero, the code at Line 38 will be executed, and the following formula is used:\\n```\\nstrategyTokenAmount = (bptClaim * context.vaultState.totalStrategyTokenGlobal) / context.vaultState.totalBPTHeld;\\nstrategyTokenAmount = (bptClaim * (x * 1e8))/ (y * 1e18)\\n```\\n\\nIf the numerator is less than the denominator, the `strategyTokenAmount` will be zero.\\nTherefore, it is possible that the users deposited their minted BPT to the vault, but received zero strategy tokens in return.\\n```\\nFile: Boosted3TokenPoolUtils.sol\\n    function _deposit(\\n        ThreeTokenPoolContext memory poolContext,\\n        StrategyContext memory strategyContext,\\n        AuraStakingContext memory stakingContext,\\n        BoostedOracleContext memory oracleContext,\\n        uint256 deposit,\\n        uint256 minBPT\\n    ) internal returns (uint256 strategyTokensMinted) {\\n        uint256 bptMinted = poolContext._joinPoolAndStake({\\n            strategyContext: strategyContext,\\n            stakingContext: stakingContext,\\n            oracleContext: oracleContext,\\n            deposit: deposit,\\n            minBPT: minBPT\\n        });\\n\\n        strategyTokensMinted = strategyContext._convertBPTClaimToStrategyTokens(bptMinted);\\n\\n        strategyContext.vaultState.totalBPTHeld += bptMinted;\\n        // Update global supply count\\n        strategyContext.vaultState.totalStrategyTokenGlobal += strategyTokensMinted.toUint80();\\n        strategyContext.vaultState.setStrategyVaultState(); \\n    }\\n```\\n\\nProof-of-Concept\\nAssume that Alice is the first depositor, and she forwarded 10000 BPT. During the first mint, the strategy token will be minted in a 1:1 ratio. Therefore, Alice will receive 10000 strategy tokens in return. At this point in time, `totalStrategyTokenGlobal` = 10000 strategy tokens and `totalBPTHeld` is 10000 BPT.\\nWhen Bob deposits to the vault after Alice, he will be subjected to the following formula:\\n```\\nstrategyTokenAmount = (bptClaim * context.vaultState.totalStrategyTokenGlobal) / context.vaultState.totalBPTHeld;\\nstrategyTokenAmount = (bptClaim * (10000 * 1e8))/ (10000 * 1e18)\\nstrategyTokenAmount = (bptClaim * (1e12))/ (1e22)\\n```\\n\\nIf Bob deposits less than 1e10 BPT, Solidity will round down and `strategyTokenAmount` will be zero. Bob will receive no strategy token in return for his BPT.\\nAnother side effect of this issue is that if Alice withdraws all her strategy tokens, she will get back all her 10000 BPT plus the BPT that Bob deposited earlier.
Consider reverting if zero strategy token is minted. This check has been implemented in many well-known vault designs as this is a commonly known issue (e.g. Solmate)\\n```\\nfunction _deposit(\\n    ThreeTokenPoolContext memory poolContext,\\n    StrategyContext memory strategyContext,\\n    AuraStakingContext memory stakingContext,\\n    BoostedOracleContext memory oracleContext,\\n    uint256 deposit,\\n    uint256 minBPT\\n) internal returns (uint256 strategyTokensMinted) {\\n    uint256 bptMinted = poolContext._joinPoolAndStake({\\n        strategyContext: strategyContext,\\n        stakingContext: stakingContext,\\n        oracleContext: oracleContext,\\n        deposit: deposit,\\n        minBPT: minBPT\\n    });\\n\\n    strategyTokensMinted = strategyContext._convertBPTClaimToStrategyTokens(bptMinted);\\n// Add the line below\\n require(strategyTokensMinted != 0, "zero strategy token minted");    \\n\\n    strategyContext.vaultState.totalBPTHeld // Add the line below\\n= bptMinted;\\n    // Update global supply count\\n    strategyContext.vaultState.totalStrategyTokenGlobal // Add the line below\\n= strategyTokensMinted.toUint80();\\n    strategyContext.vaultState.setStrategyVaultState(); \\n}\\n```\\n
Loss of assets for the users as they deposited their assets but receive zero strategy tokens in return.
```\\nint256 internal constant INTERNAL_TOKEN_PRECISION = 1e8;\\nuint256 internal constant BALANCER_PRECISION = 1e18;\\n```\\n