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

Findings: 2

Award: $12.35

QA:
grade-b
Gas:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

4.0797 USDC - $4.08

Labels

bug
downgraded by judge
grade-b
insufficient quality report
primary issue
QA (Quality Assurance)
Q-08

External Links

Lines of code

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

Vulnerability details

Using tx.origin can lead to security vulnerabilities. It's generally safer to use msg.sender for authentication purposes.

constructor(string memory _uri, address _paymentToken) ERC1155(_uri) Ownable() {
    token = IERC20(_paymentToken);

    if (block.chainid == 7700 || block.chainid == 7701) {
// Register CSR on Canto main- and testnet
        Turnstile turnstile = Turnstile(0xEcf044C5B4b867CFda001101c617eCd347095B44);
        turnstile.register(msg.sender);
    }
}

Assessed type

Access Control

#0 - c4-pre-sort

2023-11-20T02:23:58Z

minhquanym marked the issue as primary issue

#1 - c4-pre-sort

2023-11-20T04:49:25Z

minhquanym marked the issue as insufficient quality report

#2 - minhquanym

2023-11-20T04:49:26Z

QA

#3 - c4-judge

2023-11-29T16:01:43Z

MarioPoneder changed the severity to QA (Quality Assurance)

#4 - c4-judge

2023-11-29T21:15:39Z

MarioPoneder marked the issue as grade-b

Findings Information

Awards

8.2749 USDC - $8.27

Labels

bug
G (Gas Optimization)
grade-b
high quality report
sponsor confirmed
G-02

External Links

Reorder Conditions in Modifier to potentially save gas (GAS)

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

The condition msg.sender == owner() is moved to the beginning. Since the owner of the contract should always have permission, checking this condition first can potentially save gas if the sender is the owner, as it avoids the other checks.

First, check if the sender is the owner (most privileged role), then check if the share creation is not restricted (general condition), and finally, check if the sender is a whitelisted share creator (specific privilege).

modifier onlyShareCreator() {
    require(
        msg.sender == owner() || !shareCreationRestricted || whitelistedShareCreators[msg.sender],
        "Not allowed"
    );
    _;
}

Use a storage pointer for the shareData[id] struct to save gas in createNewShare function. (GAS)

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

function createNewShare(
    string calldata _shareName,
    address _bondingCurve,
    string calldata _metadataURI
) external onlyShareCreator returns (uint256 id) {
    require(whitelistedBondingCurves[_bondingCurve], "Bonding curve not whitelisted");
    require(shareIDs[_shareName] == 0, "Share already exists");

    id = ++shareCount;
    shareIDs[_shareName] = id;

    ShareData storage newShare = shareData[id];
    newShare.bondingCurve = _bondingCurve;
    newShare.creator = msg.sender;
    newShare.metadataURI = _metadataURI;

    emit ShareCreated(id, _shareName, _bondingCurve, msg.sender);
}

Minimize Calls to Storage in getBuyPrice function (GAS)

The shareData[_id] storage is accessed twice. We can optimize this by storing the needed data in a local variable.

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

function getBuyPrice(uint256 _id, uint256 _amount) public view returns (uint256 price, uint256 fee) {
    ShareData storage share = shareData[_id];
    require(share.bondingCurve != address(0), "Invalid ID");

    (price, fee) = IBondingCurve(share.bondingCurve).getPriceAndFee(share.tokenCount + 1, _amount);
}

Minimize Calls to Storage in getSellPrice function (GAS)

function getSellPrice(uint256 _id, uint256 _amount) public view returns (uint256 price, uint256 fee) {
    ShareData storage share = shareData[_id];
    require(share.bondingCurve != address(0), "Invalid ID");

    (price, fee) = IBondingCurve(share.bondingCurve).getPriceAndFee(tokenCount - _amount + 1, _amount);
}

Reduce repeated storage access by caching frequently accessed data in local variables in buy and sell functions (GAS)

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

ShareData storage share = shareData[_id];
uint256 sellerBalance = tokensByAddress[_id][msg.sender];

Utilize unchecked for operations that are known not to underflow in buy and sell functions to save gas. (GAS)

For subtraction operations like shareData[_id].tokensInCirculation -= _amount; that cannot underflow, we can use unchecked {} to avoid the safe math check and save gas.

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

Store shareData[_id] in a local share variable in getNFTMintingPrice function. (GAS)

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

 ShareData storage share = shareData[_id];

Inline Interface Call in getNFTMintingPrice function (GAS)

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

Directly call the interface function without storing the intermediate values.

function getNFTMintingPrice(uint256 _id, uint256 _amount) public view returns (uint256 fee) {
    ShareData storage share = shareData[_id];
    require(share.bondingCurve != address(0), "Invalid ID");

    unchecked {
        (, uint256 priceForOne) = IBondingCurve(share.bondingCurve).getPriceAndFee(share.tokenCount, 1);
        fee = (priceForOne * _amount * NFT_FEE_BPS) / 10_000;
    }
}

Use unchecked in getNFTMintingPrice function calculation to sava gas (GAS)

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

function getNFTMintingPrice(uint256 _id, uint256 _amount) public view returns (uint256 fee) {
    ShareData storage share = shareData[_id];
    require(share.bondingCurve != address(0), "Invalid ID");

    unchecked {
        (, uint256 priceForOne) = IBondingCurve(share.bondingCurve).getPriceAndFee(share.tokenCount, 1);
        fee = (priceForOne * _amount * NFT_FEE_BPS) / 10_000;
    }
}

Store shareData[_id] and tokensByAddress[_id][msg.sender] in local variables in mintNFT and burnNFT function (GAS)

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

ShareData storage share = shareData[_id];
uint256 sellerBalance = tokensByAddress[_id][msg.sender];

Consider using Unchecked blocks in mintNFT and burnNFT function (GAS)

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

unchecked {
        shareData[_id].tokensInCirculation -= _amount;
        tokensByAddress[_id][msg.sender] = sellerBalance - _amount;
    }

Add a conditional rewards transfer in burnNFT function to potentially save gas (GAS)

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

  uint256 rewardsSinceLastClaim = _getRewardsSinceLastClaim(_id);
  
  if (rewardsSinceLastClaim > 0) {
        SafeERC20.safeTransfer(token, msg.sender, rewardsSinceLastClaim);
    }

#0 - c4-pre-sort

2023-11-24T06:52:33Z

minhquanym marked the issue as high quality report

#1 - c4-sponsor

2023-11-27T09:28:23Z

OpenCoreCH (sponsor) confirmed

#2 - c4-judge

2023-11-29T19:43:38Z

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