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

Findings: 1

Award: $4.08

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

4.0797 USDC - $4.08

Labels

bug
downgraded by judge
grade-b
QA (Quality Assurance)
sponsor disputed
sufficient quality report
edited-by-warden
Q-22

External Links

Lines of code

https://github.com/code-423n4/2023-11-canto/blob/main/1155tech-contracts/src/bonding_curve/LinearBondingCurve.sol#L42#L59

Vulnerability details

Impact

According to the document the cost of buy a specific amount of tokens for a given share ID is linear:

1155tech allows to create arbitrary shares with an arbitrary bonding curve. At the moment, only a linear bonding curve (i.e. a linear price increase based on the total supply of a share) is supported

As we can see the cost of mint consists of 2 parts :

    1. Price
    1. Fee

Although the price is linear, the fee is not. This results in the actual cost incurred not being linear。This will lead to a situation where the fees consumed by later users are less than those consumed by earlier users

Proof of Concept

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";

import "src/bonding_curve/LinearBondingCurve.sol";
import "src/Market.sol";

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20{
    constructor() ERC20("asD ","asD "){}
    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }
}

contract LinearTest is Test {
    MockERC20 public paymentToken ;
    Market public market;
    LinearBondingCurve public curve;
    uint256 constant LINEAR_INCREASE = 1e18 / 1000;
    uint256[] public fees;
    
    address alice = vm.addr(123);

    function setUp() public {
        curve = new LinearBondingCurve(LINEAR_INCREASE);
        paymentToken = new MockERC20();
        market = new Market("",address(paymentToken));

        paymentToken.mint(alice, 100 ether);
        market.restrictShareCreation(false);
        market.changeBondingCurveAllowed(address(curve),true);
    }

    function testMintTokenCostFee() public {
        uint256 id = market.createNewShare("newShare",address(curve),"_metadataURI");
        for(uint256 i = 1;i<1000;i++){
            (,uint256 fee) = curve.getPriceAndFee(i,1);
            fees.push(fee);
        }

        for(uint256 i;i<fees.length;i++){
            console2.logUint(fees[i]);
        }
    }
}

The above code iterates 999 times, recording the actual amount of fees consumed for different share counts。 Then, we place the fee prices into a line chart. From the final results, it can be observed that the performance of the fee is not linear.

alt chart

If above image not show up try this url: https://raw.githubusercontent.com/coffiasd/images/main/fees.png

    function testMintTokenCostFee2() public {
        uint256 id = market.createNewShare("newShare",address(curve),"_metadataURI");

        (,uint256 costFee1) = curve.getPriceAndFee(1,1);
        (,uint256 costFee2) = curve.getPriceAndFee(2,1);
        (,uint256 costFee3) = curve.getPriceAndFee(3,1);
        (,uint256 costFee4) = curve.getPriceAndFee(4,1);
        (,uint256 costFee5) = curve.getPriceAndFee(5,1);
        (,uint256 costFee6) = curve.getPriceAndFee(6,1);
        (,uint256 costFee7) = curve.getPriceAndFee(7,1);

        assert(costFee1 < costFee2 && costFee2 < costFee3);
        assert(costFee4 < costFee5 && costFee5 < costFee6);

        assert(costFee3 > costFee4);
    }

In the test code above, we can observe that costFee3 is greater than costFee4. This situation can result in scenarios where the gas fees for later token IDs are lower than those for earlier token IDs when calculating getNFTMintingPrice.

this is output:

Running 1 test for test/Linear.t.sol:LinearTest
[PASS] testMintTokenCostFee2() (gas: 143307)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.40ms

Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)

Tools Used

FOUNDRY,VSCODE

The adjustment should be made to the fee calculation so that the final cost aligns with linearity

Assessed type

Math

#0 - c4-pre-sort

2023-11-20T16:12:24Z

minhquanym marked the issue as sufficient quality report

#1 - OpenCoreCH

2023-11-27T12:03:23Z

Yes, the fee percentage decreases with more shares, resulting in this graph for the fees. This is intended.

#2 - c4-sponsor

2023-11-27T12:03:27Z

OpenCoreCH (sponsor) disputed

#3 - MarioPoneder

2023-11-29T14:29:01Z

I acknowledge the validity of those concerns. However, this is rather a design choice than a design flaw or a bug of the protocol per se, therefore QA seems appropriate.

#4 - c4-judge

2023-11-29T14:29:07Z

MarioPoneder changed the severity to QA (Quality Assurance)

#5 - c4-judge

2023-11-29T22:38:04Z

MarioPoneder marked the issue as grade-b

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