hats-finance / Catalyst-Exchange-0x3026c1ea29bf1280f99b41934b2cb65d053c9db4

Other
1 stars 2 forks source link

Rounding error leads to loss of tokens when transferring tokens to the contract #48

Open hats-bug-reporter[bot] opened 10 months ago

hats-bug-reporter[bot] commented 10 months ago

Github username: -- Twitter username: 97Sabit Submission hash (on-chain): 0x6088f578250a0daaccace35a03351d199fd433516b65778ffafd593fe1654a51 Severity: high

Description: Description\ In the underwrite function, a rounding error occurs when transferring tokens to the contract due to integer division truncating decimals.

ERC20(toAsset).safeTransferFrom(
  msg.sender,
  address(this),
  purchasedTokens * (
    UNDERWRITING_COLLATERAL_DENOMINATOR + UNDERWRITING_COLLATERAL
  ) / UNDERWRITING_COLLATERAL_DENOMINATOR  
);

For example:

purchasedTokens is 112. UNDERWRITING_COLLATERAL_DENOMINATOR is 1000. UNDERWRITING_COLLATERAL is 35.

purchasedTokens * 1035 / 1000;

112 * 1.035 = 115.92 Solidity truncates, so 115 tokens is transferred instead of 116.

  1. Proof of Concept (PoC) File

    https://github.com/catalystdao/catalyst/blob/27b4d0a2bca177aff00def8cd745623bfbf7cb6b/evm/src/CatalystChainInterface.sol#L788-L795

  2. Revised Code File (Optional)

    Amount received should be rounded up.

reednaa commented 10 months ago
  1. It is going to be off by 1 wei at most. (or whatever you call the lowest value).
  2. We only cover issues when the accuracy is bad AND the asset has more than 6 decimals. In case of 6 decmials and 1 Token, the difference is max 10^-6 = 0,0001% which is nothing.
  3. The "soft fix" is simply to set UNDERWRITING_COLLATERAL higher. Unless you can provide very good arguments for why the constants are set incorrectly, it is a won't fix.
ololade97 commented 10 months ago

It's not going to be off by 1 wei. It almost one token. purchasedTokens is stored in uint256.

For instance, in the example here: 112 * 1.035 = 115.92

.92 will be 1 token when 0.08 is added to it. This will result to a bad loss overtime as it will accumulate.

ololade97 commented 10 months ago

This may require a "soft fix", but as it is, it's a vulnerability causing a loss. "won't fix" tag shouldn't be attached.

reednaa commented 10 months ago

Tokens have "decimals". What that means is when we are talking about 1 token, we are actually talking about 1 * 10**decimals units. That implies that 1 Ether = 1000000000000000000 (wei). So in your example:

Purchased tokens is 112000000000000000000 UNDERWRITING_COLLATERAL_DENOMINATOR is 1000. UNDERWRITING_COLLATERAL is 35.

112000000000000000000 * 1035 / 1000;

112000000000000000000 * 1.035 = 115920000000000000000

In your example there is no loss of precision.

reednaa commented 10 months ago

Feel free to post this in the Discord for a discussion or find another auditor to provide an independent opinion.

ololade97 commented 10 months ago

No, the calculation is wrong.

purchasedTokens is stored in uint256. By this, if 112000000000000000000 is stored in purchasedTokens, it doesn't mean 112 anymore. It's a huge sum.

This is where the error in your calculation comes from.

ololade97 commented 10 months ago

I've asked around. The conclusion was that ERC20 tokens won't have a rounding error here. However, tokens with small decimals may have a rounding issue.

reednaa commented 10 months ago

We limit issues to 6 decimals or more. (USDC equiv)