Submitted by 100su, also found by pontifex, immeas, nazirite, Soul22, bart1e, adriro, Oxsadeeq, ast3ros, osmanozdemir1, ether_sky, T1MOH, bin2chen, 0xpiken, ustas, SpicyMeatball, wangxx2026, and 0xluckhu
The current withdrawCarry function in the contract underestimates the accrued interest due to a miscalculation. This error prevents the rightful owner from withdrawing their accrued interest, effectively locking the assets. The primary issue lies in the calculation of maximumWithdrawable within withdrawCarry.
The following code segment is used to determine maximumWithdrawable:
The problem arises with the scaling of exchangeRate, which is assumed to be scaled by 10^28. However, for CNOTE in the Canto network, the exchangeRate is actually scaled by 10^18. This discrepancy causes the owner to withdraw only 10^(-10) times the actual interest amount. 
Consequently, when a non-zero _amount is specified, withdrawCarry often fails due to the require(_amount <= maximumWithdrawable, "Too many tokens requested"); condition.
CNOTE Scaling Verification
An essential aspect of this audit involves verifying the scaling factor of the CNOTE exchange rate. The exchangeRate scale for CNOTE can be verified in the Canto Networks GitHub repository.  Evidence confirming that the exponent of the CNOTE exchange rate is indeed 18 can be found through this link to the token tracker. The data provided there shows the current value of the stored exchange rate (exchangeRateStored) as approximately 1004161485744613000. This value corresponds to 1.00416 * 1e18, reaffirming the 10^18 scaling factor.
This information is critical for accurately understanding the mechanics of CNOTE and ensuring the smart contracts calculations align with the actual scaling used in the tokens implementation. The verification of this scaling factor supports the recommendations for adjusting the main contracts calculations and the associated test cases, as previously outlined.
Testing with Solidity Codes
Testing with the following Solidity code illustrates the actual CNOTE values:
The corresponding TypeScript logs show a clear discrepancy between the expected and calculated underlying balances:
With the logs:
Using balanceOfUnderlying Function - Replace the flawed calculation with the balanceOfUnderlying function. This function accurately calculates the underlying NOTE balance and is defined in CToken.sol (source).
Proposed Code Modifications - Two alternative implementations are suggested:
The second option is highly recommended for its accuracy and simplicity.
For post-modifications to the main contract code, its essential to update the associated test cases. In the MockCNOTE test contract, all scaling is currently set to 10^28. To align with the main contract changes, the following modifications are recommended for MockCNOTE:
This revised MockCNOTE contract reflects the updated scale factor and aligns with the main contracts logic.
For comprehensive validation, scenario testing using a fork of the mainnet is highly recommended. This approach allows for real-world testing conditions by simulating interactions with existing contracts on the mainnet. It provides a robust environment to verify the correctness and reliability of the contract modifications in real-world scenarios, ensuring that the contract behaves as expected when interfacing with other mainnet contracts.
This step is crucial to identify potential issues that might not be apparent in isolated or simulated environments, enhancing the overall reliability of the contract before deployment.
Math
OpenCoreCH (Canto) confirmed and commented via duplicate issue #227:
OpenCoreCH (Canto) commented:
