# CompoundProvider's balanceUnderlying and calcShares outputs are scaled incorrectly

CompoundProvider's balanceUnderlying and calcShares mis-scale exchangeRate output, returning CToken decimals instead of underlying decimals, misstating balances and shares by magnitudes.

- Payout: Contest reward
- Severity: high
- Program: Derby
- Credited: Jeiwan
- Disclosed: 2022-10-17
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-01-derby-judging/issues/202
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-01-derby%3AH-10
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:precision-loss

**Target:** CompoundProvider.sol (Compound yield provider) in Derby yield optimiser
**Root cause:** exchangeRateStored() is scaled by 10^(18-8+underlyingDecimals); dividing by 10^(10+decimals) cancels the underlying decimals leaving CToken decimals, so balanceUnderlying returns CToken-scaled values. calcShares multiplies by 10^(10+decimals) instead of 1e18, producing underlying-scaled shares.
**Steps:**
1. Read CompoundProvider.balanceUnderlying: returns (balanceShares * price) / 10**(10 + decimals).
2. Note exchangeRateStored is scaled 10^(18-8+underlyingDecimals); balanceShares has CToken decimals (e.g. 8).
3. Compute result decimals: CToken decimals, not underlying decimals (e.g. cDAI 8 vs DAI 18).
4. Read calcShares: shares = (_amount * 10**(10+decimals)) / exchangeRate; result has underlying decimals instead of CToken decimals.
5. Observe both feed Vault.rebalanceCheckProtocols, pullFunds, withdrawFromProtocol, misstating balances/shares by orders of magnitude.
**How found:** Code review of CompoundProvider decimal math against Compound's documented exchangeRate scaling; noticed the divisor 10^(10+decimals) cancels underlying decimals rather than yielding underlying-denominated output.
**Insight:** When integrating Compound cTokens, remember exchangeRateStored is scaled 10^(18-8+underlyingDecimals) so cTokenBalance*rate is already 1e18-scaled; dividing by anything other than 1e18 leaves cToken decimals. Verify decimal algebra per token pair (cDAI 8 vs DAI 18, cUSDC 8 vs USDC 6).
