Froggies Token

CertiKFiles named in the report

CertiK reviewed Froggies (Froggies Token) on 4 Aug 2023: 19 findings, 1 critical; 9 marked fixed, 10 open.

1 critical finding, fixed
Open the reportOpen Froggies’s security record

Report hosted on skynet.certik.com.

Statuses are the security firm’s own, from this report. Trustblock does not review code.

Findings19

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

SeverityOpenAcknowledgedFixed
Critical001
High302
Medium001
Low705
HighCentralization Risks in froggies_stake.solNot fixed

In the contract `StakingContract` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority. - Function `setPeriodRates()`: Sets the reward rate for the specified `period`. - Function `addToStakingPool()`: Provides tokens for distributing rewards. - Function `setEmergencyWithdrawalRates()`: Set the following state variables - `emergencyWithdrawalPenalty`: Records the rate of penalty when a user uses emergency withdrawal. - `burnRateEmergency`: Records the percentage of tokens which will be burned from penalty. - `poolRateEmergency`: Never used state variable. ![](https://accelerator-tasks-prod.acc.corp.certik.com/0f82e29070e24fe7b3008126564d91df/diagrams/centralization_froggies_stake-StakingContract-_owner.svg)

HighCentralization Risks in Token.solNot fixed

In the contract `Token` the role `_owner` has authority over the functions shown in the diagram below. - `setPair()` and `_setPair()`: Manage the usage status of pairs in this contract. The owner can add new pairs, or disable an active pair. - `setBuyTaxes()`: Set the fee rate when users get the `LLFR2` token from a pair. - `setSellTaxes()`: Set the fee rate when users use the `LLFR2` token to get other tokens from a pair. - `setLPThreshold()`: Set the value of the state variable `lpThreshold`. Transactions with the pair will accumulate `lp` fee, once the `lp` fee meets the `lpThreshold`, the contract will use all of the `lp` fees to add liquidity to the `LLFR2-WBNB` pair - `setMarketingThreshold()`: Set the value of the state variable `marketingThreshold`. - `setMarketingSwap()`: Set the value of the state variable `isSwap`. When the `isSwap` is true, the contract will swap all of the market fees to BNB. - `excludeFromFees()`: Manage the addresses that don't need to pay transaction fees. - `withdrawBNB()`: Withdraw the BNBs swapped from the market fees. - `withdrawMarketingTokens()`: Withdraw the `LLFR2`s that earned from the market fees. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority. ![](https://accelerator-tasks-prod.acc.corp.certik.com/9f83256a82cb4cfdb4791de25637bbbf/diagrams/centralization_Token-Token-_owner.svg)

HighCentralized Token Holding Position RiskNot fixed

In the contract `Token`, the EOA address `owner` has `100000000000000 * 10 ** decimals()` tokens minted at the time of the contract deployment, which has severe centralization risk and can manipulate the tokenomics of the project. ```solditiy=53 constructor(address _router) ERC20("LLFR2", "LLFR2") { _mint(msg.sender, 100000000000000 * 10 ** decimals()); ``` Also, the contract will use a part of the transaction fee to add liquidity, but the LP tokens are transferred to the EOA address `owner`, this contract logic results in a centralized token holding position over time. ```solidity=94 function addLiquidity(uint256 tokenAmount, uint256 bnbAmount) private { _approve(address(this), address(uniswapV2Router), tokenAmount); uniswapV2Router.addLiquidityETH{value: bnbAmount}( address(this), tokenAmount, 0, 0, owner(), block.timestamp ); } ``` Any compromise to the private keys of the `owner` EOA address may allow a hacker to take advantage of the EOA address authority and sell tokens on the market and/or remove the pool liquidity, causing a severe price drop.

LowDivide Before MultiplyNot fixed

Performing integer division before multiplication truncates the low bits, losing the precision of calculation. ```solidity=77 uint256 totalAllocation = _amount.mul(totalRate).div(100); ``` ```solidity=83 uint256 burnAmount = totalAllocation.mul(_burnRate).div(100); ``` --- ```solidity=104 uint256 rewardPerSecond = stakers[_staker].totalRewardAllocation.div(stakers[_staker].stakePeriod); ``` ```solidity=105 uint256 newReward = rewardPerSecond.mul(timeElapsed); ``` --- ```solidity=156 uint256 penalty = total.mul(emergencyWithdrawalPenalty).div(100); ``` ```solidity=157 uint256 burnAmount = penalty.mul(burnRateEmergency).div(100); ``` --- ```solidity=209 uint256 rewardPerSecond = stakers[_staker].totalRewardAllocation.div(stakers[_staker].stakePeriod); ``` ```solidity=210 uint256 accumulatedReward = rewardPerSecond.mul(timeElapsed); ```

LowIncorrect State Variables UpdateNot fixed

The field `stakeTime` in the struct `Staker` is not updated in the function `unstake()`. --- The field `stakePeriod` in the struct `Staker` is not updated in the functions `unstake()` and `emergencyUnstake()`. --- The reward amount has been mistakenly added 2 times to the state variable `totalAllocated`. The total reward amount `totalAllocation` a user can receive is added to the state variable `totalAllocated` in the `stake()` function. ```solidity=95 totalAllocated = totalAllocated.add(totalAllocation); ``` Furthermore, in the `calculateReward()` function, the newly generated rewards are added to the state variable `totalAllocated`. This portion of the rewards is already included in the total rewards, so the code at line 110 duplicates the addition. ```solidity=110 totalAllocated = totalAllocated.add(newReward); ``` In addition, the state variable `totalAllocated` is also updated in the functions `unstake()` and `emergencyUnstake()`. ```solidity=138 totalAllocated = totalAllocated.sub(reward); ``` ```solidity=175 totalAllocated = totalAllocated.sub(uncollectedReward).add(poolAmount); ``` --- The amount of burned tokens has been mistakenly added 3 times to the state variable `totalBurned`. The amount of tokens that will be burned is added to the state variable `totalBurned` in the `stake()` function. ```solidity=96 totalBurned = totalBurned.add(burnAmount); ``` And this amount is also added to the state variable `totalBurned` in the `unstake()` function before executing the `burn` function. ```solidity=139 totalBurned = totalBurned.add(burnAmount); burn(burnAmount); ``` Finally, when the function `burn()` executes, this amount is added to the state variable `totalBurned` again. ```solidity=203 totalBurned = totalBurned.add(amount); ```

LowLack of Sanity CheckNot fixed

Based on our understanding, when the period has not ended, and a user wants to withdraw their staked tokens, they need to call the `emergencyUnstake()` function and accept the associated penalty. Therefore, it is necessary to validate the current time to prevent users from mistakenly calling the function and incurring financial losses.

LowLow-level Call UsageNot fixed

The smart contract contains low-level calls such as `.call()`, `.delegatecall()`, and `.staticcall()`. Since these calls bypass some of the automatic checks that Solidity provides, like function type checks, they can introduce vulnerabilities, logic errors, or unexpected behavior. ```solidity=201 (bool success, ) = address(token).call(abi.encodeWithSignature("publicBurn(uint256)", amount)); ```

LowMissing Emit EventsNot fixed

There should always be events emitted in the sensitive functions that are controlled by centralization roles.

LowPotential Sandwich AttacksNot fixed

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 back running (executing after the target) a transaction to sell the asset. The following functions are called without setting restrictions on slippage or minimum output amount. Hence, transactions triggering these functions are vulnerable to sandwich attacks, especially when the input amount is large : - `addLiquidity()` - `_swapTokensForBNB()` - `_swapTokensForBNBSimple()`

LowVariables That Could Be Declared as ImmutableNot fixed

The linked variables assigned in the constructor can be declared as `immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.

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

  • contracts/Token.sol
  • contracts/froggies_stake.sol

Part of Froggies’s security record

Froggies
Audits1 audit by 1 firmLast audit Aug 2023, by CertiK.
Critical findingsNo open critical findingsIn that report, 1 critical finding was reported. It is marked fixed by the firm that reported it.
Open Froggies’s security record

Security firm

CertiK

1,044 projects reviewed · 1,393 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