Chef.fun

Smart Contract Audit Report


Executive Summary

Chef.fun is developing a new platform that allows authorized users to generate custom ERC-20 tokens through a set of smart contracts.

This report presents the outcomes of our collaborative engagement with the Chef.fun team, focusing on the comprehensive evaluation of the Chefdotfun, ChefFun, ReferralRegistry, ChefTokenFactory, ChefToken, and ChefFunSplitter contracts.

Our team conducted an initial security assessment from August 12th to August 16th, 2024. On August 19th, our team amended this report to reflect updates made to the contracts by the project team that resolves all findings. On August 29th, our team amended this report to reflect the project's mainnet addresses. On September 27th, our team amended this report to reflect the project's updated mainnet addresses.


Audit Scope

* Although the ChefFun, ReferralRegistry, ChefToken, and ChefTokenFactory contracts are unverified, we have confirmed that the deployed bytecode matches the bytecode generated from the source code provided for the audit scope.


Audit Findings

All findings have been resolved. In addition, some centralized aspects are present.

Finding #1

ChefFunSplitter

LowResolved

Finding #1 - ChefFunSplitter
LowResolved

Description: In the updateDistribution() function, the sum of the distribution percentages is incorrectly calculated by mixing state variables (distributionPercs) with newly assigned memory variables (distributionPercsMem). The buybackPerc percentage from the state variable is summed with the other percentages from the memory variable, which could lead to an incorrect total percentage check.

function updateDistribution(
...
require(distributionPercs.elyPerc + distributionPercs.projectOwnerPerc
+ distributionPercs.marketingPerc + distributionPercsMem.buybackPerc == FEE_DIVISOR, "Must equal 100%");

Risk/Impact: This issue can result in the distribution percentages not correctly summing to 100%, potentially causing incorrect or inconsistent ETH distributions. Additionally, the team may not be able to update the percentages as they intend.

Recommendation: Modify the updateDistribution() function to sum all distribution percentages using the newly assigned memory variables only. This ensures that the total percentage check accurately reflects the intended distribution:

require(
distributionPercsMem.elyPerc + distributionPercsMem.projectOwnerPerc
+ distributionPercsMem.marketingPerc + distributionPercsMem.buybackPerc == FEE_DIVISOR, "Must equal 100%");
Alternatively, The updateDistribution() function uses a memory struct to update the distributionPercs state variable. This approach is not gas-efficient as it involves unnecessary memory allocation and copying. The updateDistribution() function could be modified to update the distributionPercs struct directly in storage rather than using a memory struct as follows:
function updateDistribution(uint24 _ely, uint24 _projectOwner, uint24 _marketing, uint24 _buyback) external onlyOwner {
    require(_ely + _projectOwner + _marketing + _buyback == FEE_DIVISOR, "Must equal 100%");

    distributionPercs.elyPerc = _ely;
    distributionPercs.projectOwnerPerc = _projectOwner;
    distributionPercs.marketingPerc = _marketing;
    distributionPercs.buybackPerc = _buyback;
}

Resolution: The team has modified the updateDistribution() function to sum all distribution percentages using the newly assigned memory variables only.

Finding #2

ChefToken

InformationalResolved

Finding #2 - ChefToken
InformationalResolved

Description: A new TokenHandler contract is created in the constructor but remains unused, as its only function, sendTokenToOwner(), is never called.

Recommendation: The TokenHandler contract should either be removed from the platform to optimize gas usage or utilized in a way that fits the project team's intended functionality.

Resolution: The team has removed the TokenHandler contract.

Finding #3

ChefToken

InformationalResolved

Finding #3 - ChefToken
InformationalResolved

Description: The tokensForPlatform and tokensForProject state variables are redundant because they only track fixed proportions (95% and 5%, respectively) of the total tax amount. These variables are solely used as numerators in the swapBack() function to calculate the ETH distribution between the platform and project addresses.

Recommendation: Remove the tokensForPlatform and tokensForProject state variables and replace their usage in the swapBack() function with direct proportional calculations (95% and 5%) for ETH distribution. totalTokensToSwap could be simplified to a fixed divisor representing the total 100%. This will reduce storage costs and simplify the contract logic.

Resolution: The team has implemented the above recommendation.

Finding #4

ChefToken

InformationalResolved

Finding #4 - ChefToken
InformationalResolved

Description: The swapping condition in the following if-statement is redundant as the swapping state variable is permanently set to false.

if(canSwap && !swapping && isAMMPair[to] && lastSwapBackBlock + 1 <= block.number) {

Recommendation: The swapping state variable should either be removed for additional gas savings or utilized in a way that fits the project team's intended functionality.

Resolution: The team has removed the swapping state variable.

Finding #5

ChefToken

InformationalResolved

Finding #5 - ChefToken
InformationalResolved

Description: The projectTax and totalTax state variables are always assigned the same value, making projectTax redundant in its current usage.

Recommendation: The projectTax state variable could be removed to optimize gas usage by relying solely on the totalTax variable for all related calculations.

Resolution: The team has properly adjusted their fee structure to improve gas-efficiency.

Finding #6

ChefToken

InformationalResolved

Finding #6 - ChefToken
InformationalResolved

Description: The exemptFromLimits mapping is not enforced in the contract.

Recommendation: The exemptFromLimits mapping should either be removed to reduce contract size and deployment costs or utilized in a way that fits the project team's intended functionality.

Resolution: The team has removed the exemptFromLimits mapping.

Finding #7

Chefdotfun & ChefToken

InformationalResolved

Finding #7 - Chefdotfun & ChefToken
InformationalResolved

Description: The gasSaver state variable is not used in the contract.

Recommendation: The gasSaver state variable could be removed to reduce contract size and deployment costs.

Resolution: The team has removed the gasSaver state variable.

Finding #8

ChefToken

InformationalResolved

Finding #8 - ChefToken
InformationalResolved

Description: The following state variables can only be set one time in the constructor but are not declared immutable:

dexRouter, lpPair, platformAddress, projectAddress, swapTokensAtAmt

Recommendation: The above state variables could be declared immutable for additional gas savings on each reference.

Resolution: The team has declared the above state variables immutable.

Finding #9

ReferralRegistry

InformationalResolved

Finding #9 - ReferralRegistry
InformationalResolved

Description: The following state variables are not used in the contract:

referreeOwedAmount, referreeTotalRaisedAmount, walletReferralAmount

Recommendation: The above state variables could be removed to reduce contract size and deployment costs.

Resolution: The team has removed the above state variables.


System Overview

Chefdotfun Token

Tokenomics

The total supply of the token is set to 100 million $CHEF [100,000,000] and is minted to the deployer. No mint or burn functions are publicly accessible, though the circulating supply can be decreased by sending tokens to the 0x..dead address.

Transfers

Trading must be enabled by the owner before all transfers can take place on the platform. Only transfers where either the sender or recipient is excluded from fees can occur when trading is disabled. Once trading is enabled, it can never be disabled.

The contract's whitelist is active for five minutes after trading is enabled. During this period, the recipient must be whitelisted for buys, the sender must be whitelisted for sells, and both the sender and recipient must be whitelisted for peer-to-peer transfers. After five minutes have passed, the whitelist is permanently disabled and the contract's public launch block is recorded.

The contract enforces a maximum transaction amount (determined by the owner) which imposes a limit to the number of tokens that can be bought or sold in a single transaction. The contract enforces a maximum wallet amount that prevents a transfer from occurring if the recipient's token balance will exceed the limit number of tokens (determined by the owner) after the transfer occurs.

The contract features a transfer delay that prevents buys or peer-to-peer transfers from occurring if they are attempted within the same block as the user's previous buy or peer-to-peer transfer. Additionally, the recipient address must match the address that initiated the transaction.

The contract features a cool-down mechanism that prevents a sell from occurring if the sender is initiating the sell within the same block as their previous buy or receipt of tokens through a peer-to-peer transfer.

There is a Liquidity fee, Marketing fee, and Dev fee on all buys and sells via Uniswap where neither the sender nor the recipient is excluded from fees.

The contract features a dynamic tax system, which, when enabled, automatically adjusts the total buy/sell fees and maximum transaction/wallet amounts based on the number of blocks elapsed since public trading was enabled. The contract's default tax rates and limits are used for the first 9 blocks. If the number of blocks passed ranges from 10 to 109, the buy and sell fees are reduced by 1% for every 10-block period, starting from an initial rate of 20%. The maximum transaction and wallet amounts are adjusted to a percentage of the total supply based on the elapsed block interval. After 110 blocks have passed, the dynamic tax is permanently disabled, the maximum transaction and wallet amounts are set to 100% of the total supply, and the total buy and sell fee are both set to 5%.

When the dynamic tax is triggered, 80% of the updated total buy and sell fee is allocated to the Marketing fund, and the remaining 20% is allocated to the Dev fund. The Liquidity fund allocation is set to zero.

The tokens collected through fees are stored in the contract address. The tokens are swapped for ETH for the purpose of funding Uniswap liquidity and addresses set by the team when the following conditions are met:

  • The threshold number of tokens in the contract (determined by the owner) has been reached.
  • The caller is initiating a sell transaction via Uniswap.
  • At least 1 block has passed since the previous swap has occurred.

The tokens collected through the Liquidity fee are transferred to the Pair address. The tokens collected through the Dev fee and Marketing fee are swapped for ETH and sent to the team's Dev wallet and Marketing wallet respectively.

As the contract is implemented with Solidity v0.8.x, it is protected from overflows/underflows. There are no fees associated with transferring tokens. The contract complies with the ERC-20 standard.

Ownership Controls
  • The owner can set the Liquidity fee, Marketing fee, and Dev fee for both the buy and sell fee structures to any percentages as long as the total fee percentage combined does not exceed 10%.
  • The owner can exclude and include accounts from fees, the maximum transaction amount, and the maximum wallet amount at any time.
  • The owner can set the maximum transaction and maximum wallet amounts to any values greater than or equal to 100,000 tokens at any time.
  • The owner can disable the maximum transaction amount and maximum wallet amount restrictions at any time.
  • The owner can disable the dynamic tax functionality at any time.
  • The owner can enable/disable the cool-down block restriction at any time.
  • The owner can update the threshold number of tokens needed to trigger the automatic token swapping functionality to any value between 1,000 and 500,000 at any time.
  • The owner can transfer any number of tokens from their own address to any number of addresses in a single transaction at any time.
  • The owner can add/remove any address from the contract's whitelist at any time.
  • The owner can permanently disable the contract's whitelist functionality before the initial five-minute whitelist period has passed at any time.
  • The owner can permanently disable the transfer delay functionality at any time.
  • The owner can withdraw any tokens from the contract at any time.
  • The owner can set the Marketing address and Dev address to any addresses at any time.
  • The Dev address can set the Dev fee to zero and allocate its current percentage to the Marketing fee at any time.

ChefFun

Create Launch

Users can create a new token on the platform by specifying the token's name, symbol, maximum wallet percentage, buy and sell fee percentages, tax wallets, tax percentages, and Router address. The caller must provide an amount of ETH to cover the contract's fee. The fee value is sent to the team's Fee receiver address. A new token contract is generated through the Factory contract.

If the caller sends more ETH than the required fee, the excess amount is used to automatically purchase tokens in a bundle-buy process. A percentage of the ETH (up to 10%) used for the bundle-buy is deducted and sent to the Fee receiver address. If a buy tax is set for the token, an additional percentage is deducted from the remaining ETH. This tax is then distributed among two specified tax wallets according to predetermined percentages, with any remainder sent to the token's project address. The tax amounts for the wallets are converted to WETH (depending on the blockchain) before being transferred.

After these deductions, the amount of tokens to be distributed are calculated based on the remaining ETH, using the virtual liquidity pool mechanism. These tokens are allocated to specified wallet addresses according to their predefined amounts, with any leftover tokens sent to the user that initiated the transaction. The remaining ETH used for buying is added to the token's active liquidity value. If the token's market cap in USD reaches the target value after this process, the contract's token balance is transferred to the token contract. The accumulated ETH and the full token balance is added as liquidity to the Pair. The token is marked as launched and ownership is renounced.

Buy Transactions

Users can initiate a buy for any token address before the token is launched by providing an amount of ETH for the purchase. The platform fee is deducted from the ETH and sent to the Fee receiver address. If a referral address is included, a portion of the fee is allocated to the referrer. If the token has a platform-specific buy tax, this amount is also deducted, converted to WETH, and distributed to the specified tax wallets, with any remainder in ETH sent to the token’s project address. The remaining ETH is used to calculate the token amount, which is then transferred to the user. The contract verifies that the token amount does not exceed the maximum wallet limit before completing the transaction.

If the token's market cap in USD reaches the target value after this process, the contract's token balance is transferred to the token contract. The accumulated ETH and the full token balance is added as liquidity to the Pair. The token is marked as launched and ownership is renounced.

Sell Transactions

Users can initiate a sell for any token address before the token is launched by specifying the token amount to be sold. The contract calculates the corresponding ETH amount based on the virtual liquidity pool and current token reserves. The specified number of tokens are transferred from the caller to the contract. The caller must grant the contract a sufficient allowance for the transaction to successfully occur.

The ETH amount is then subject to the platform fee, which is deducted and sent to the Fee receiver address. If the user has a referee, a portion of the fee is allocated as a referral reward. If the token has a platform-specific sell tax, it is deducted from the ETH amount, converted to WETH, and distributed to the specified tax wallets, with any remainder in ETH sent to the token’s project address. The remaining ETH, after all fees and taxes, is sent to the user.

Access-Restricted Functionality

The creator of a token can decrease the Platform buy fee and sell fee from its current value at any time. The creator of a token can increase the max wallet value at any time.

The owner can update the Fee dollar amount, Base virtual liquidity dollar amount, and Marketcap target dollar amount to any values at any time. The owner can update the contract's Platform fee to any value up to 10% at any time. The owner can update the Base virtual liquidity amount to any value greater than zero at any time.

The owner can update the creation fee to any value at any time. The owner can enable/disable the automatic fee update functionality at any time. The owner can update the Token factory address and Fee receiver address referenced in the contract to any addresses at any time.

The owner can update the referral anchor for a user and referee at any time. The owner can add or remove any address as an Anchor Manager at any time.

Chef Token Factory

Any authorized address can generate a new token by specifying the token's info. A new ChefToken is created and 100% of the total supply is transferred to the ChefFun contract and the ChefFun contract is granted ownership.

The owner can update the Platform address referenced in the contract to any address at any time. The owner can update the authorized status of any address at any time.

Chef Token

Tokenomics

The total supply of the token is set to 1 billion and is minted to the factory and subsequently transferred to the ChefFun contract. No mint or burn functions are publicly accessible, though the circulating supply can be decreased by sending tokens to the 0x..dead address.

Transfers

Trading must be enabled by the owner before buys and sells can take place on the platform. Once trading is enabled, it can never be disabled.

There is a fee charged on all buys and sells via Uniswap where neither the sender nor the recipient is excluded from fees.

The tokens collected through fees are stored in the contract address. The tokens are swapped for ETH when the following conditions are met:

  • The threshold number of tokens in the contract (determined by the owner) has been reached.
  • The caller is initiating a sell transaction via Uniswap.
  • At least 1 block has passed since the previous swap has occurred.

After the swap occurs, 5% of the contract's ETH balance is sent to the Platform address. A portion of the remaining ETH is converted to WETH and sent to two tax wallets according to their specified percentages. Any ETH left after these distributions is sent to the Project address.

As the contract is implemented with Solidity v0.8.x, it is protected from overflows/underflows. There are no fees associated with transferring tokens. The contract complies with the ERC-20 standard.

Ownership Controls

The token creator (Project address) can update the total Buy fee and Sell fee as long as their original values are not exceeded. The owner can exclude and include accounts from fees at any time.

The first tax wallet address can set its tax percentage to zero and potentially redistribute its share to the second tax wallet if their combined percentage was 100%. The second tax wallet address can set its tax percentage to zero and potentially redistribute its share to the first tax wallet if their combined percentage was 100%. The Project address can redistribute the tax percentages between the two tax wallets, ensuring the new percentages sum to 100%.

The owner can add liquidity to the Pair address using the contract's full token and ETH balances, along with any additional funds supplied during the function call. Before this process, 3% of the contract's initial ETH balance is sent to the Platform address. After liquidity is added, the resulting LP tokens are minted to the contract and then locked in a Pinksale token locking contract for 30 days. Ownership of this lock is assigned to the Project address.

ChefFun Splitter

ETH Distribution

Any ETH sent directly to the contract is automatically split amongst the Ely address, Project owner address, and Marketing address based on their assigned percentages. If the remaining ETH in the contract exceeds the contract's minimum swap threshold it is swapped for the Token address set by the owner and the received tokens are sent to the 0x..dead address.

Ownership Controls

The owner can set the Ely address, Project Owner address, Marketing address, and Token address referenced in the contract to any addresses at any time. The owner can set the distribution percentages for each fund to any values at any time as long as the total percentage equals 100%. The owner can set the minimum swap threshold to any value at any time. The owner can withdraw all of the ETH in the contract at any time.


Vulnerability Analysis

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control The team has control over the owner-restriced functionality described above. PASS
Compiler Issues N/A PASS
Delegate Call to Untrusted Contract N/A PASS
Dependence on Predictable Variables N/A PASS
Ether/Token Theft N/A PASS
Flash Loans N/A PASS
Front Running The automatic buyback functionality in the ChefFunSplitter contract may be susceptible to front-running. To mitigate this risk, it is recommended that the team minimize the amount of ETH used in each buyback when ETH is sent to the contract. PASS
Improper Events N/A PASS
Improper Authorization Scheme N/A PASS
Integer Over/Underflow N/A PASS
Logical Issues N/A PASS
Oracle Issues N/A PASS
Outdated Compiler Version N/A PASS
Race Conditions N/A PASS
Reentrancy N/A PASS
Signature Issues N/A PASS
Sybil Attack N/A PASS
Unbounded Loops N/A PASS
Unused Code N/A PASS
Overall Contract Safety   PASS

About SourceHat

SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

Contact us today to get a free quote for a smart contract audit of your project!

What is a SourceHat Audit?

Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

How Do I Interpret the Findings?

Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

  • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
  • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
  • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
  • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.