Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A malicious pool creator or first depositor can make depositing and removing liquidity unfavorable for other users. #25

Open
howlbot-integration bot opened this issue Jun 21, 2024 · 2 comments
Labels
bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-13 edited-by-warden grade-a QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax 🤖_02_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality

Comments

@howlbot-integration
Copy link

Lines of code

https://github.com/code-423n4/2024-05-canto/blob/d1d51b2293d4689f467b8b1c82bba84f8f7ea008/canto-main/x/coinswap/keeper/keeper.go#L149

Vulnerability details

Impact

Users who add liquidity to the pool can lose funds and also, amount of max tokens that users can deposit would be severely truncated for a period of time.

Proof of Concept

There are 2 ways for the pool creator or first depositor to make using a pool unfavourable to users.

  1. On creation of the pool, user mints 100,000 tokens and deposits just 1 deposit token. This causes the pool to have a liquidity balance of 100,000, standard reserve balance of 100,000 but a tokenReserveAmount of 1. Subsequently when users want to deposit tokens their token deposit would be severely limited.
else {
			if standardReserveAmt.GTE(params.MaxStandardCoinPerPool) {
				return sdk.Coin{}, errorsmod.Wrap(types.ErrMaxedStandardDenom, fmt.Sprintf("pool standard coin is maxed out: %s", params.MaxStandardCoinPerPool.String()))
			}

			maxStandardInputAmt := sdkmath.MinInt(msg.ExactStandardAmt, params.MaxStandardCoinPerPool.Sub(standardReserveAmt))
			mintLiquidityAmt = (liquidity.Mul(maxStandardInputAmt)).Quo(standardReserveAmt)
			if mintLiquidityAmt.LT(msg.MinLiquidity) {
				return sdk.Coin{}, errorsmod.Wrap(types.ErrConstraintNotMet, fmt.Sprintf("liquidity amount not met, user expected: no less than %s, actual: %s", msg.MinLiquidity.String(), mintLiquidityAmt.String()))
			}

			@> depositAmt := (tokenReserveAmt.Mul(maxStandardInputAmt)).Quo(standardReserveAmt).AddRaw(1)
			depositToken = sdk.NewCoin(msg.MaxToken.Denom, depositAmt)
			standardCoin = sdk.NewCoin(standardDenom, maxStandardInputAmt)
			if depositAmt.GT(msg.MaxToken.Amount) {
				return sdk.Coin{}, errorsmod.Wrap(types.ErrConstraintNotMet, fmt.Sprintf("token amount not met, user expected: no more than %s, actual: %s", msg.MaxToken.String(), depositToken.String()))
			}

Let's assume a user wants to make a standard deposit of 50,000 tokens. The deposit token is calculated as 1 * 50,000/100,000+1. This gives the user the ability to only deposit 1 max tokens despite probably expecting to deposit more.
This limits the addliquidity functionality of that pool for many transactions until tokenReserveAmount eventually becomes a significant value. This action can serve to deter users from using the pool.
2. Pool creator creates a pool with standardInputAmount of 100,000 and tokenReserve of 100,000. Then sends 50,000 standard coins directly to the pool. This causes the standardReserveAmount to become larger than the lpLiquidity. When another user deposits 100,000 tokens, they get 66666 of mintliquidity and deposit 66667 of max tokens. If that user wants to remove their liquidity, they only get 99,999 of their standardCoin deposit back, having lost 1 token to the pool. The original pool creator retains all his assets. If sufficient number of users lose assets to the pool, the pool creator gains the lost assets added to his liquidity.

Tools Used

Manual Review

Recommended Mitigation Steps

  1. For initial Pool creator there should be validation that the max token deposit and standardReserveAmount do not surpass a given threshold.
    For the 2nd, consider preventing direct transfers of tokens to the pool. This would ensure correct accounting

Assessed type

Math

@howlbot-integration howlbot-integration bot added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value 🤖_02_group AI based duplicate group recommendation bug Something isn't working duplicate-13 edited-by-warden sufficient quality report This report is of sufficient quality labels Jun 21, 2024
howlbot-integration bot added a commit that referenced this issue Jun 21, 2024
@c4-judge c4-judge added downgraded by judge Judge downgraded the risk level of this issue QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax and removed 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value labels Jun 27, 2024
@c4-judge
Copy link

3docSec changed the severity to QA (Quality Assurance)

@c4-judge
Copy link

3docSec marked the issue as grade-b

@C4-Staff C4-Staff reopened this Jul 9, 2024
@C4-Staff C4-Staff added grade-a and removed grade-b labels Jul 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working downgraded by judge Judge downgraded the risk level of this issue duplicate-13 edited-by-warden grade-a QA (Quality Assurance) Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax 🤖_02_group AI based duplicate group recommendation sufficient quality report This report is of sufficient quality
Projects
None yet
Development

No branches or pull requests

2 participants