Canto Application Specific Dollars and Bonding Curves for 1155s - 0x3b's results

Tokenizable bonding curves using a Stablecoin-as-a-Service token

General Information

Platform: Code4rena

Start Date: 13/11/2023

Pot Size: $24,500 USDC

Total HM: 3

Participants: 120

Period: 4 days

Judge: 0xTheC0der

Id: 306

League: ETH

Canto

Findings Distribution

Researcher Performance

Rank: 110/120

Findings: 1

Award: $1.37

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L174-L189

Vulnerability details

Impact

No slippage protection when sell can leave sellers with their NFTs selling for much less than they originally intended.

Proof of Concept

With a linear increase, every NFT bought increases the price, and every NFT sold decreases it. The price increases with a buy because it increases the tokenCount. The token count is used to calculate the linear rising price. The same is true for selling, where tokenCount is decreased, thus lowering the price.

This, combined with big mints (huge demand and huge volatility in price), can lead some users to sell their NFTs for much less than they intended.

Example:

  1. "Cats" has a mint starting at 1 wETH with an increase of 0.1 wETH.
  2. Alice is one of the first to mint, so she mints at 1.6 wETH.
  3. The price rises to 1.9 wETH.
  4. Alice decides to sell at a profit of 0.3 wETH.
  5. However, due to the huge volatility (bots buying and selling, huge demand on the network, and so on), when her TX gets executed, the current price is 1.5 wETH.

She accidentally sells her NFT at a loss instead of a profit. Alice is down 0.1 wETH, as she had no way to limit the price her NFT sells for.

Tools Used

Manual review.

My suggestion is to set a minimum selling price on sell.

-   function sell(uint256 _id, uint256 _amount) external {
+   function sell(uint256 _id, uint256 _amount, uint256 _minPrice) external {
        (uint256 price, uint256 fee) = getSellPrice(_id, _amount);
+       require(price >= _minPrice, "Price too low");

        _splitFees(_id, fee, shareData[_id].tokensInCirculation);
        uint256 rewardsSinceLastClaim = _getRewardsSinceLastClaim(_id);
        rewardsLastClaimedValue[_id][msg.sender] = shareData[_id].shareHolderRewardsPerTokenScaled;

        shareData[_id].tokenCount -= _amount;
        shareData[_id].tokensInCirculation -= _amount;
        tokensByAddress[_id][msg.sender] -= _amount; 

        SafeERC20.safeTransfer(token, msg.sender, rewardsSinceLastClaim + price - fee);
        emit SharesSold(_id, msg.sender, _amount, price, fee);
    }

Assessed type

Error

#0 - c4-pre-sort

2023-11-18T10:44:43Z

minhquanym marked the issue as duplicate of #12

#1 - c4-judge

2023-11-28T23:37:40Z

MarioPoneder marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter