Canto Application Specific Dollars and Bonding Curves for 1155s - HChang26'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: 119/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#L150 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L174 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L132 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/Market.sol#L141 https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/bonding_curve/LinearBondingCurve.sol#L14

Vulnerability details

Impact

Malicious actors can exploit the buy() and sell() functions to instantly profit from front-running or executing a sandwich attack.

Proof of Concept

The buy() function allows users to acquire shares by providing asD tokens. The share price increases linearly based on the provided Bonding Curve. As shareData[_id].tokenCount grows, so does the share price, calculated within getBuyPrice().

    function getBuyPrice(uint256 _id, uint256 _amount) public view returns (uint256 price, uint256 fee) {
        address bondingCurve = shareData[_id].bondingCurve;
        (price, fee) = IBondingCurve(bondingCurve).getPriceAndFee(shareData[_id].tokenCount + 1, _amount);
    }

This function utilizes getPriceAndFee() to determine each additional share's price increase.

        for (uint256 i = shareCount; i < shareCount + amount; i++) {
            uint256 tokenPrice = priceIncrease * i;
            price += tokenPrice;
            fee += (getFee(i) * tokenPrice) / 1e18;
        }

sell() follows similar reverse logic, where the larger shareData[_id].tokenCount, each share can be sold for more. Price decreases as shareData[_id].tokenCount decreases.

Based on current implementation with linear curve, its safe to assume there will be many MEV bots monitoring the mempool to take advantage of every call made by users.

Attackers can immediately profit from following attacks:

  1. Sandwich Attack: Attackers front-run a user's buy() by buying at a lower price and back-run the same function by calling sell(), securing instant profit.

    1. The share has no tokens yet (assuming priceIncrease = 1).
    2. User calls buy() for 1 share.
    3. Attacker front-runs the user and purchases 1 share at price = 1.
    4. User's transaction executes, buying 1 share at price = 2.
    5. Attacker swiftly executes sell() for 1 share at price = 2.
  2. Front-running: Users intending to sell their shares for profit are vulnerable to attackers who preemptively sell their own shares at a higher price. Attacker can also use this attack to re-adjust their position for a lower price.

    1. User owns 1 share purchased at price = 1.
    2. Attacker also owns 1 share acquired at price = 2.
    3. User initiates sell() seeing a profitable opportunity.
    4. Attacker anticipates and executes sell() at a price = 2.
    5. User's transaction is executed, selling 1 share at a price = 1.
    6. Attacker takes advantage by purchasing back 1 share at a price = 1.

Tools Used

Manual Review

Consider introducing max price parameter in both buy() and sell()

Assessed type

MEV

#0 - c4-pre-sort

2023-11-18T10:07:59Z

minhquanym marked the issue as duplicate of #12

#1 - c4-judge

2023-11-28T23:32:19Z

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