Guess Meme Solidity

QuillAuditsFiles named in the report

Audit · firm’s term: audit report

QuillAudits reviewed Guess Meme Rust (Guess Meme Solidity) on 26 Feb 2025: 24 findings, 0 critical; 9 marked fixed, 15 open.

No open critical findings
Open the reportOpen Guess Meme Rust’s security record

Report hosted on quillaudits.com.

Trustblock does not review code.

Findings24

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High100
Medium502
Low907
HighThe updated functionality of the max buy limit can by bypassedOpen

The new change added to validate if the user should be able to buy the tokens based on the max limit introduces a bug where <strong>msg.sender </strong>will be able to send his tokens to another address while buying, just to buy more amount and pass the check/validation where the current balance of <strong>msg.sender </strong>plus <strong>_amount </strong>is validated.<br> <br>Example:<br>1. User1 bought the maximum amount of tokens that can be bought by one user (based on the <strong>maxSupplyPercentage </strong>)<br>2. User1 calls <strong>buyTokens()</strong> again but before that the User1 transfers some or the whole amount to another address, so that on <a href="https://github.com/sardar-khan/guess-meme-contracts/blob/main/ethereum/guess_meme_latest.sol#L754" target="_blank">L754</a> when the user’s balance will be fetched, it will be 0. And the user will be able to buy an additional <strong>allowedToBuy </strong>amount of tokens.<br>3. This action can be repeated multiple times.<br><br>(Note: It should be noted that even if other functionality will be used where the user’s bought amount will be stored in the variable for each user’s address, the user can still create different addresses to trick that functionality.)

MediumAdd slippage checksOpen

In <strong>buyTokens()</strong> the ETH amount that the user needs to pay for buying a certain <strong>_amount </strong>of tokens can vary if other <strong>buyTokens() </strong>transactions execute before it. <br>If other <strong>buyTokens()</strong> transactions execute before the user's <strong>buyTokens() </strong>transaction then the amount of eth user needs to pay for buying the same amount of <strong>_amount </strong>would be more than what was calculated before. <br>In this case, care can be taken by sending more amount eth as the <strong>buyTokens() </strong>allows sending <strong>msg.value </strong>greater than <strong>ethToBuy + fee</strong>. <br>(Note: As mentioned in another issue the extra dust ETH amount should be sent back to the user/msg.sender.)<br> <br>In <strong>sellTokens()</strong> the amount of ETH the user gets after selling <strong>_amount </strong>tokens can vary if other <strong>sellTokens() </strong>transactions execute before the user's call where the user was expecting to receive the specific ETH amount calculated with <strong>sellQuote()</strong>. <br> <br>Because of this, it's important to provide the slippage limit while selling tokens so that it can be checked if the amount is not much less than what was expected to be received.

MediumCare should be taken for an incomplete bonding curvesOpen

According to the code <strong>bondingCurve[_token].isCompleted </strong>is set to <strong>true</strong>, if the <strong>bondingCurve[_token].realTokenReserves </strong>becomes 0 on <a href="https://github.com/sardar-khan/guess-meme-contracts/blob/main/ethereum/eth_guessmeme.sol#L739" target="_blank">L739</a> while the user buys tokens.<br> <br>It should be noted that it’s not guaranteed that all the tokens will be sold in the specific period and hence the ETH accumulated by selling those tokens can’t removed as the bonding curve is not complete because the <strong>realTokenReserves </strong>has not reached 0.

MediumRestrict the minimum token amount while buying and sellingOpen

While buying, a very small <strong>_amount </strong>can lead the <strong>ethToBuy </strong>to <strong>0 </strong>and eventually fee calculated based on <strong>ethToBuy </strong>would be 0, the minimum limit should be added while buying the tokens with <strong>buyTokens().</strong> This limit can be added by calculating <strong>ethToBuy </strong>and <strong>fee </strong>for a certain <strong>_amount </strong>and checking that it would not be 0.<br> <br>E.g. In <strong>buyTokens()</strong> it can be checked that if <strong>_amount </strong>is <strong>1000000000000000 </strong>(i.e.<strong> 0.001e18</strong>) the <strong>ethToBuy </strong>and <strong>fee </strong>are non-zero for hardcoded reserve amounts. so based on that the limit can be set for the <strong>_amount </strong>to be greater than or equal to <strong>1000000000000000</strong>.<br> <br>Similarly, while selling with <strong>sellTokens()</strong> very small amount can lead <strong>ethToReceive </strong>to go close to 0. The limit can be added by calculating <strong>ethToReceive </strong>and <strong>fee</strong>, for a certain <strong>_amount </strong>and checking that it would not be 0.<br> <br>E.g In <strong>sellTokens()</strong> if <strong>_amount </strong>is <strong>1000000000000000 </strong>(i.e. <strong>0.001e18</strong>) the <strong>ethToReceive </strong>and <strong>fee</strong> is non-zero for hardcoded reserve amounts. so based on that the limit can be set for the <strong>_amount </strong>to be greater than equal to <strong>1000000000000000</strong>.

MediumSend dust/extra eth amount back in buyTokens()Open

<strong>buyTokens() </strong>allows sending <strong>msg.value </strong>that is greater than or equal to <strong>ethToBuy + fee. </strong><br>This means an extra amount can be sent than what is required to buy <strong>_amount</strong>. Which will help users to handle some slippage (if ETH price for token goes up than what was calculated).<br> <br>However, since the extra amount will not be utilized every time. It is necessary to send the extra/dust ETH amount back to the <strong>msg.sender</strong>.<br>

MediumUse multisig wallet to mitigate centralization concernOpen

Since some critical functionality like withdrawals of the fund is managed by a single EOA/owner address. It's important that a multi-sig should be used as an owner for managing the functionality.<br><br>Additionally, it should be noted that some functions like <strong>withdrawStuckEth()</strong>, <strong>transferStuckTokens()</strong> create centralization concerns as they grant direct access to the owner to remove the ETH and tokens without any restrictions.

LowAdd restrictions to avoid token dumpingOpen

Users can sell the tokens after buying even while the sale is live.<br>We encourage your team to analyze and understand the implications of this scenario and add certain restrictions for the users before they start moving the funds.

LowAdd validation in sellTokens()Open

Validation should be added to check if the contract has enough amount of ETH available when the user sells a certain amount of tokens back to the contract to avoid unexpected failing.

LowNote about ownership of the smart contractOpen

It should be noted that when deploying this smart contract the ownership is transferred to the deployer of the smart contract, unlike the current OpenZeppelin ownable implementation where you get the option to pass the <strong>initialOwner </strong>argument. So the deployer would be able to call the authorized functions like <strong>withdraw()</strong>, <strong>changeFeeBasisPoints()</strong>, <strong>withdrawStuckEth()</strong>, <strong>transferStuckTokens()</strong>.

LowNote about updateMetadataOpen

<strong>updateMetadata() </strong>updates/reveals the token name and symbol. This should be noted that the <strong>updateMetadata() </strong>is owner only function and for every token that is created by users, the owner of the factory contract can change the name and symbol, and not the user who created that token.

Show 5 more
LowNote regarding hardcoded reservesOpen

It should be noted that the reserves are hardcoded in the contract that means every time a new token sale is created, the same reserve values will be used.

LowNote regarding withdraw functionalityOpen

This should be noted that for every created token sale/pool only the protocol owner can withdraw the tokens and not the creator of that pool.

LowPause functionality can be addedOpen

While the pausing functionality should be normally avoided. Given that the contracts are non-upgradeable, it would be a good idea to add pausing functionality for buy and sell functionality to ensure the safe withdrawal of funds by the owner in any unexpected event.

LowUnused variableOpen

<strong>tokenCount </strong>is declared on <strong>L652 </strong>is increamented in <strong>createToken()</strong> on <strong>L720</strong>. This variable is then never used anywhere and the variable is declared as private so no one can directly know how many tokens are created as there would be no getter method created for private variable.

LowVariables can be declared as constant and immutableOpen

<strong>initialVirtualEthReserves </strong>set the value of <strong>1900000000000000000 </strong>and is never changing hence it can be declared as a constant.<br><br><strong>feeRecipient </strong>and <strong>tokenFeeRecipient </strong>can be declared as immutable variables.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (3)

  • eth_guessmeme.sol
  • guess_meme_latest.sol
  • ts/blob/main/ethereum/guess_meme_latest.sol

Part of Guess Meme Rust’s security record

Guess Meme Rust
Audits2 audits by 1 firmLast audit Feb 2025, by QuillAudits.
Critical findingsNo open critical findingsAcross 2 reports.
Open Guess Meme Rust’s security record

Security firm

QuillAudits

350 projects reviewed · 459 reports · latest Apr 2025

See QuillAudits’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us