selltoken

CertiKFiles named in the report

CertiK reviewed Sell Token (selltoken) on 30 Mar 2023: 16 findings, 0 critical; 12 marked fixed, 4 open.

No open critical findings
Open the reportOpen Sell Token’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings16

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

SeverityOpenAcknowledgedFixed
Critical000
High100
Medium001
Low3011
MediumIncorrect restrictions of withdrawFixed

In the `ShortStart()` function, when the user's bnb is greater than 10% of the pool balance, he cannot call `ShortStart()` for more amount. However, the balance of a specific coin can change, due to the withdrawal from other users. So when he calls `withdraw()`, his balance might be greater than 1-% of the pool balance at that moment and the below restriction is incorrect. ```solidity=498 if(getTokens >= tokens){ mkt.sell(token,Short[_msgSender()][token].token,tokens,_msgSender()); mkt.setPools(token,tokens,false); }else { mkt.sell(token,Short[_msgSender()][token].token,getTokens,_msgSender()); mkt.setPools(token,getTokens,false); } ```

LowDiscussion of the logic purposeFixed

We would like to know what is the purpose of the below logic: ```solidity=455 if(mkt.balanceOf(coin) <=0) return ; ``` This could prevent the first user to call `ShortStart()`.

LowIncompatibility With Deflationary TokensFixed

When transferring standard ERC20 deflationary tokens, the input amount may not be equal to the received amount due to the charged transaction fee. As a result, an inconsistency in the amount will occur and the transaction may fail due to the validation checks. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee) to the target contract, only 90 tokens actually arrive at the contract. In the function `Minerals.buy()`, the swap accepts at most 5% of slippage. The below logic does not consider both deflationary loss and swap slippage loss, and still record the min as the received amount. ```solidity=283 IRouter(_router).swapExactETHForTokensSupportingFeeOnTransferTokens{value: amount0In}(min*95/100,path,address(this),block.timestamp); if(_token!=_USDT){ balanceOf[_token]+=min; } } ```

Lowincomplete logicFixed

In the function `setPool()`, when the input `isPool` is not zero, the logic is as below: ```solidity=374 }else{ require(msg.value>=fee); IERC20(token).transferFrom(_msgSender(),address(this),coin); balanceOf[token]+=coin; balanceOfLook[token]+=coin; uint oldCoin=IERC20(_TRDT).balanceOf(address(this)); if(msg.value>0){ buy(_WBNB,address(_TRDT),msg.value); if(IERC20(_TRDT).balanceOf(address(this))>oldCoin){ uint ut=IERC20(_TRDT).balanceOf(address(this))-oldCoin; balanceOfLook[_TRDT]+=ut; } } if(pair[token]==address(0)){ pair[token]=token1; } } ``` The caller will transfer in a specific type of token, which is not recorded for the caller. The caller will also transfer in some bnb and these bnb will be used to buy _TRDT token. This will also not be recorded for the caller. Only the balanceOf/balanceOfLook will be updated.

LowLack of access controlFixed

The function should be only accessible for the ceo contract.

LowLack of checks for `bnborUsdt` is `_WBNB` or `_USFixed

In the function `ShortStart()`, the value of `bnborUsdt` is retrieved from the Minerals.getPair() function. There is no check to ensure the `bnborUsdt` must be either _WBNB or _USDT. There is only one check to ensure it's not a zero address. In the following get price logic, if the `bnborUsdt` is not _WBNB, it will be treated as _USDT anyway. The admin should carefully manage the setup of pair info.

LowNot able to minerWithdrawFixed

In the `setPool()` function, the caller will transfer a specific type of token, which is decided by the parameter he inputs. And he will get staking rewards after some time for the same type of tokens. However, this type of token might not be one of the tokens in the pair info, then there is no resource for the rewards. And he will be unable to withdraw.

LowPotential Sandwich AttacksFixed

A sandwich attack may happen when an attacker observes a transaction swapping tokens or adding liquidity without setting restrictions on slippage or minimum output amount. The attacker can manipulate the exchange rate by frontrunning (executing before the target) a transaction to purchase one of the assets and make profits by backrunning (executing after the target) a transaction to sell the asset. The following functions are called without setting restrictions on slippage or minimum output amount, so transactions triggering these functions are vulnerable to sandwich attacks, especially when the input amount is large: ```solidity function buy(address bnbOrUsdt,address _token,uint amount0In) public{ ... IRouter(_router).swapExactTokensForTokens(amount0In,0,path,address(this),block.timestamp); ... } ```

LowRedundant logic in setPoolsFixed

The function Minerals.setPools() can only be called by the ceo, which should be the SellToken contract. In the SellToken contract, the all calls of `setPools()` are as below and the last parameter are false. ```solidity if(getTokens >= tokens){ mkt.sell(token,Short[_msgSender()][token].token,tokens,_msgSender()); mkt.setPools(token,tokens,false); }else { mkt.sell(token,Short[_msgSender()][token].token,getTokens,_msgSender()); mkt.setPools(token,getTokens,false); } ``` Hence the logic inside the case `zt is true` is redundant. ```solidity=263 function setPools(address token,uint value,bool zt)public{ require(_msgSender()==ceo,"You are not a routing contract administrator"); if(zt){ balanceOf[token]+=value; }else{ if(balanceOf[token] > value){ balanceOf[token]-=value; }else{ balanceOf[token]=0; } } } ```

LowRedundant StatementsFixed

The below logic in the `getMyPrice()` function is the same in different cases. ```solidity=600 if(nowPrice < Short[_msgSender()][token].tokenPrice){ zt=nowPrice * 1 ether / Short[_msgSender()][token].tokenPrice; }else { zt=nowPrice * 1 ether / Short[_msgSender()][token].tokenPrice; } ```

Show 2 more
Lowunauthorized queryFixed

The function `getMyPriceSell()` can be used to view other users balance in the pool, without any authorizations.

LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaFixed

The return value of the transfer()/transferFrom() call is not checked. ```solidity=339 IERC20(token).transfer(_msgSender(),value); ``` --- ```solidity=357 IERC20(token).transferFrom(_msgSender(),address(this),coin); ``` --- ```solidity=376 IERC20(token).transferFrom(_msgSender(),address(this),coin); ``` --- ```solidity=406 IERC20(token).transfer(owner(),_token); ```

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 (1)

  • sellTokenNew8.18-1.sol

Part of Sell Token’s security record

Sell Token
Audits1 audit by 1 firmLast audit Mar 2023, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open Sell Token’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’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