Canto Application Specific Dollars and Bonding Curves for 1155s - t0x1c'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: 117/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

Vulnerability details

Impact

When a user calls the buy() function, he can be front-run such that he has to pay way more than he had hoped for, based on the tokenCount at the time of placing the order. There is no slippage or maxPriceLimit parameter inside buy() to protect him from this.

Proof of Concept

Suppose:

  1. The current shareData[_id].tokenCount is 10.
  2. Bob calls buy(1, 1) hoping to pay an amount of 11 * LINEAR_INCREASE as per the calculation inside Market::getBuyPrice() and LinearBondingCurve::getPriceAndFee().
  3. Alice front-runs Bob and calls buy(1, 20).
  4. shareData[_id].tokenCount is now 30.
  5. Bob's transaction executes & he ends up paying 31 * LINEAR_INCREASE, much more than he had planned for.

Tools Used

Manual inspection.

Add a maxPriceLimit parameter inside buy() which causes the function to revert if price exceeds it.

Assessed type

Other

#0 - c4-pre-sort

2023-11-18T10:44:38Z

minhquanym marked the issue as duplicate of #12

#1 - c4-judge

2023-11-28T23:36:29Z

MarioPoneder marked the issue as satisfactory

Lines of code

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

Vulnerability details

Impact

When a user calls the sell() function, she can be front-run such that she receives way less than she had hoped for, based on the tokenCount at the time of placing the order. There is no slippage or minPriceExpected parameter inside sell() to protect her from this.

Proof of Concept

Suppose:

  1. The current shareData[_id].tokenCount is 30.
  2. Alice calls sell(1, 1) hoping to receive an amount of 30 * LINEAR_INCREASE as per the calculation inside Market::getSellPrice() and LinearBondingCurve::getPriceAndFee().
  3. Bob front-runs Alice and calls sell(1, 20).
  4. shareData[_id].tokenCount is now 10.
  5. Alice's transaction executes & she ends up receiving only 10 * LINEAR_INCREASE, much less than she had planned for. She would have rather preferred to wait for tokenCount to go up before selling her tokens.

Tools Used

Manual inspection.

Add a minPriceExpected parameter inside sell() which causes the function to revert if price received is less than it.

Assessed type

Other

#0 - c4-pre-sort

2023-11-18T10:44:29Z

minhquanym marked the issue as duplicate of #12

#1 - c4-judge

2023-11-28T23:36:19Z

MarioPoneder marked the issue as satisfactory

Lines of code

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

Vulnerability details

Impact

An attacker can watch the mempool to wait for a user's buy() call and sandwich it to make immediate profits. Since buy & sell prices depend on the number of tokens, and also since there is no holding-period required before selling, attacker can make immediate profits.

Proof of Concept

Paste the following inside 2023-11-canto/1155tech-contracts/src/test/Market.t.sol and run with forge test --mt test_t0x1c_Sandwich -vv. Alice is the attacker in this example:

    function test_t0x1c_Sandwich() public {
        testCreateNewShare();
        token.approve(address(market), type(uint256).max);
        vm.prank(alice);
        token.approve(address(market), type(uint256).max);
        deal(address(token), alice, 10e18); // give some funds to Alice

        uint256 aliceInitialBalance = token.balanceOf(alice);

        // simulating some pre-existing orders
        market.buy(1, 2);
        market.buy(1, 3);

        //==============================//
        //======= SANDWICH ATTACK ======//
        //==============================//
        vm.prank(alice);
        market.buy(1, 10); // Front-run

        market.buy(1, 1); // BUY order from naive user

        vm.prank(alice);
        market.sell(1, 10); // Back-run

        // Immediate Profit
        assertGt(token.balanceOf(alice), aliceInitialBalance, "no profit");
    }

Tools Used

Foundry.

Protocol can considering adding a delay between buy & sell from the same address so that it's not possible within the same block. Also, slippage protection for users will help keep such situations at bay to some extent.

Assessed type

Timing

#0 - c4-pre-sort

2023-11-18T09:49:52Z

minhquanym marked the issue as duplicate of #12

#1 - c4-judge

2023-11-28T23:17:47Z

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