A malicious operator can steal all user deposits
In the Orbital architecture, each Vault user has a numerator which represents their share of the vault holdings. The denominator is by design the sum of all numerators of users, an invariant kept at deposits and withdrawals. For maximum precision, the denominator should be a very large value. Intuitively, numerators could be spread across different users without losing precision. The critical calculations occur in these lines in deposit():\\n```\\n         if (D == 0) { //initial deposit\\n                   uint256 sumDenoms = 0; \\n                        for (uint256 i = 0; i < tkns.length; i++) {\\n                              sumDenoms += \\n                        AI.getAllowedTokenInfo(tkns[i]).initialDenominator;\\n                      }\\n                   require(sumDenoms > 0 && sumDenoms <= maxInitialDenominator, \\n         "invalid sumDenoms");\\n                   deltaN = sumDenoms; //initial numerator and denominator are the \\n                same, and are greater than any possible balance in the vault.\\n             //this ensures precision in the vault's \\n         balances. User Balance = (N*T)/D will have rounding errors always 1 \\n         wei or less. \\n         } else { \\n            // deltaN = (amt * D)/T;\\n            deltaN = Arithmetic.overflowResistantFraction(amt, D, T);\\n         }\\n```\\n\\nIn the initial deposit, Vault sums all token initialDenominators to get the final denominator. It is assumed that the vault will never have this amount in total balances (each token denominator is worth around $100m dollars).\\nIn any other deposit, the deltaN (numerator) credited to the depositor is (denominator * deposit amount / existing balance). When denominator is huge, this calculation is highly precise. However, when denominator is 1, a serious issue oc**curs. If user's deposit amount is one wei smaller than existing balance, deltaN would be zero. This property has lead to the well-known ERC4626 inflation attack, where an attacker donates (sends directly to the contract) an amount so that the following deposit is consumed without any shares given to the user. In fact, it is possible to reduce the denominator to 1 and resurrect that attack. The root cause is that the initial deposit denominator is not linear to the deposit amount. Consider the attack flow below, done by a malicious operator:\\nDeploy an ETH/BTC pool\\nFlash loan $100mm in ETH and BTC each\\nPerform an initial deposit of $100mm in ETH/BTC\\nFrom another account, deposit 1 wei ETH / BTC -> receive 1 deltaN\\nWithdraw 100% as operator, reducing denominator to 1.\\nPay off flash loan\\nWait for victim deposits\\nWhen a deposit arrives at the mempool, frontrun with a donation of an equivalent amount. The victim will not receive any shares ( numerator).\\nAny future deposits can be frontran again. Any deposit of less than the current balance will be lost.
Consider checking that user's received deltaN is reasonable. Calculate the expected withdrawable value (deltaN / denominator * balance), and verify that is close enough to the deposited amount.
```\\n         if (D == 0) { //initial deposit\\n                   uint256 sumDenoms = 0; \\n                        for (uint256 i = 0; i < tkns.length; i++) {\\n                              sumDenoms += \\n                        AI.getAllowedTokenInfo(tkns[i]).initialDenominator;\\n                      }\\n                   require(sumDenoms > 0 && sumDenoms <= maxInitialDenominator, \\n         "invalid sumDenoms");\\n                   deltaN = sumDenoms; //initial numerator and denominator are the \\n                same, and are greater than any possible balance in the vault.\\n             //this ensures precision in the vault's \\n         balances. User Balance = (N*T)/D will have rounding errors always 1 \\n         wei or less. \\n         } else { \\n            // deltaN = (amt * D)/T;\\n            deltaN = Arithmetic.overflowResistantFraction(amt, D, T);\\n         }\\n```\\n