Smart Contract Audit Report
ReBaked is creating a new presale platform and staking platform where users are rewarded in "Mystery Boxes".
For this audit, we reviewed Playshare's TokenWrapper, BakedStaking, Mysterybox, BakedVault, and BakedVaultFactory contracts using code provided by the project team.
Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: March 8th, 2022.
Updated: March 21st, 2022 to support changes made by the project team.
Updated: March 30th, 2022 to support additional changes made by the project team.
Finding #6 - BakedStaking, Mysterybox, & BakedVault - Informational (Resolved)Description: The following functions are declared public, but are never called internally:Recommendation: These functions can be declared external for additional gas savings on each call.
mysterybox.setHoldingadd(), mysterybox.setPaymentToken(), mysterybox.setstakepool(), mysterybox.createmysterybox(), mysterybox.setItem(), mysterybox.buyTreasurybox(), mysterybox.claimTreasuryBox(), mysterybox.allItems(), mysterybox.openBoxCount(), mysterybox.claimBoxCount(), mysterybox.airdropList(), mysterybox.UserTreasuryBoxes() BakedStaking.adminSetMinTokenAmount(), BakedStaking.setTokenContract(), BakedStaking.adminSetDuration(), BakedStaking.changeStakePoolStatus(), BakedStaking.stake(), BakedStaking.unStake(), BakedStaking.release(), BakedStaking.batchUserTicketUpdates(), BakedStaking.totalTicket(), BakedStaking.getAllStakes() BakedVault.adminApproval(), BakedVault.adminWhitelist(), BakedVault.batchAdminWhitelisted(), BakedVault.upComing(), BakedVault.success(), BakedVault.buyTokens(), BakedVault.withdrawFunds(), BakedVault.withDrawRemainingAssetsFailed(), BakedVault.adminAddLiquidityManual(), BakedVault.adminReleseToken(), BakedVault.manualWithDrawRemainingAssets(), BakedVault.manualWithdrawTokens(), BakedVault.bakedVAULT(), BakedVault.unlock(), BakedVault.automaticWithDrawRemainingAssets(), BakedVault.automaticWithdrawTokens(), BakedVault.withdrawFee(), BakedVault.getRemaining().
Resolution: The team has declared the above functions external.
- This contract allows users to stake a specified token in exchange for tickets which can be used for Mystery Boxes.
- A new "Stake" will be added to the pool each time a user stakes an amount.
- The number of tickets corresponding to the Stake is calculated by the amount staked divided by the cost of a ticket at the time of staking.
- The new Stake will also have a tracked ID, owner, and start time.
- Users must stake at least the cost of one ticket when staking.
- In order to withdraw tokens, users must first call the unstake() function on a specified Stake, which will start a timer at which the tokens can be released.
- Once the specified amount of time has passed, users can release the Stake and withdraw its associated tokens.
- Stakes that have been released are marked as inactive.
- The team should not use any ERC-777 tokens as the staking token.
- The team should also exercise caution and ensure the proper exemptions are made if using a fee-on-transfer token as the staking token.
- The owner can update the minimum stake amount at any time.
- The owner can update the staking token at any time, potentially disabling users from withdrawing in the same token they originally staked with.
- The owner can grant any amount of additional tickets to any address at any time.
- The owner can update the wait duration to withdraw unstaked tokens at any time.
- The owner can disable staking, unstaking, and withdraws at any time.
- As the contract is implemented with Solidity v0.8.0, it is safe from any possible overflows/underflows.
- For each active ticket a user has earned from the BakedStaking contract, they are able to purchase 1 Mystery Box from each released Mystery Box series.
- Before a Mystery Box series becomes active, the owner has the ability to add possible items that can be inside it.
- Items added to a series can be a specified number of tokens, an NFT, or both. An Item can also instead add the user to an Airdrop list.
- An Item can also have a specified price associated with it.
- If the Item has a price, users must pay in USDC at the time of claiming in order to have the Item sent to them.
- The Item's supply is also specified, limiting the number of times that the Item can be rewarded to users.
- Users can purchase a Mystery Box of a specified series if the current time is between the series' start and end times.
- When a user purchases a box, one of the Mystery Box series' Items will be pseudorandomly assigned to it using hashed block attributes.
- The box's claim period will also begin at the time of purchase; users must claim before this period ends or their reward will be lost.
- All payments are sent to an associated "hold wallet", and all rewards are sent from this hold wallet.
- The team should exercise caution and only use tokens with 18 decimals for Mystery Box rewards.
- The team should not use ERC-777 tokens for Mystery Box rewards.
- The team should ensure that the hold wallet has the appropriate rewards to be distributed.
- The owner can create a new Mystery Box series with a specified start time, end time, and claim period length at any time.
- The owner can update the associated staking contract used to fetch user's ticket counts at any time.
- The owner can update the USDC address to any address at any time.
- The owner can update the hold wallet address at any time.
- As the contract is implemented with Solidity v0.8.0, it is safe from any possible overflows/underflows.
- This contract allows users to create their own BakedVaults.
- A Playshare fee is set upon deployment that is used to calculate fees in each BakedVault deployed through this contract.
- Users will specify a "campaign token", soft cap, hard cap, start and end dates, minimum and maximum purchase amounts, lock duration, vesting settings, total token amount, purchase rates, and percentages of funds to be added to liquidity.
- A new BakedVault contract will be deployed, and the campaign tokens will be transferred from the user to this new contract.
- The owner address will be set to the user creating the BakedVault, and the factory owner will be set to the deployer of this contract.
- Campaigns should not be created using fee-on-transfer or ERC-777 tokens.
- This contract allows "campaigns" to be created that are used to raise USDT liquidity for a specified token.
- Upon initialization, a total token amount, rate at which tokens are to be purchased at, percentage of tokens and collected USDT to be added to liquidity, LP token unlock date, and other properties are defined.
- Users can purchase future tokens with USDT at a rate specified at the time of campaign creation.
- In order to purchase, the campaign must be within its start and end dates, and must have not exceeded its hard cap.
- The campaign must also first be approved by the factory owner.
- Whitelisted users are be able to begin purchasing tokens 5 minutes before a campaign's start date.
- Users must purchase within the minimum and maximum allowed amounts.
- If a user is whitelisted, their total amount invested also cannot exceed their specified whitelisted maximum amount.
- The factory owner can add any address to the whitelist with an associated maximum amount at any time.
- Non-Whitelisted users' total purchases cannot exceed the contract's maximum allowed amount.
- If a campaign has ended and the soft cap has been reached, the owner or factory owner has the ability to trigger the "automatic" liquidity add.
- When liquidity is added using the automatic setting, a specified percentage of collected USDT along a the specified percentage of tokens are added as liquidity to the USDT/token pair.
- The resulting LP tokens will be locked in this contract until a specified unlock duration has passed.
- Once the unlock duration has passed, the owner is permitted to call the unlock function to withdraw these LP tokens one time. The owner can instead use the unlock function to withdraw any non-campaign token in the contract, however; they can only call this function once as mentioned.
- After liquidity has been automatically added and LP tokens have been withdrawn, the owner can withdraw any remaining USDT and unreserved tokens. Reserved tokens include tokens that will be vested to users and a "Playshare fee".
- Users will also be able to begin to withdraw their tokens based on the specified vesting schedule.
- If liquidity is not added automatically, the owner or factory owner can trigger a manual liquidity add instead, transferring the contract's collected USDT to a "liquidity address".
- After a manual liquidity add, the owner or factory owner can then set the token as released.
- Once the token has been manually released, the owner can manually withdraw any remaining unreserved tokens. Reserved tokens include tokens that will be vested to users and the Playshare fee.
- After release, users will also be permitted to begin to withdraw tokens based on the specified vesting schedule.
- For both manual and automatic settings, users will be able to withdraw an initial percentage of their tokens once withdraws are enabled, then must wait the defined lock duration before withdrawing the next percentage.
- A user's lock duration is updated each time tokens are withdrawn, so users should withdraw as soon as possible in order to receive their next vest as soon as possible.
- A campaign is considered "failed" if its end time has passed and the soft cap has not been reached.
- If the percentage of tokens to be added to liquidity in the automatic liquidity add is set to 0, a refund will be initiated at the time of the automatic liquidity add.
- If a campaign has failed or been refunded, users can withdraw their spent USDT.
- If liquidity has been added or a campaign has failed or been refunded, the owner can withdraw any of the campaign's remaining tokens.
- The factory owner can withdraw the Playshare fee once at any time, which will transfer a percentage of the equivalent USDT amount in campaign tokens to a specified address.
|Arbitrary Jump/Storage Write||N/A||PASS|
|Centralization of Control||WARNING|
|Delegate Call to Untrusted Contract||N/A||PASS|
|Dependence on Predictable Variables||A Mystery Box Item can be predicted as it is reliant on block.timestamp, block.difficulty and a global variable; however, the likelihood of this along with its use for exploitation is very low.||PASS|
|Improper Authorization Scheme||N/A||PASS|
|Outdated Compiler Version||N/A||PASS|
|Overall Contract Safety||PASS|
($) = payable function # = non-constant function Int = Internal Ext = External Pub = Public + [Lib] Math - [Int] max - [Int] min - [Int] average + [Lib] SafeMath - [Int] add - [Int] sub - [Int] sub - [Int] mul - [Int] div - [Int] div - [Int] mod - [Int] mod + [Lib] Address - [Int] isContract - [Int] sendValue # - [Int] functionCall # - [Int] functionCall # - [Int] functionCallWithValue # - [Int] functionCallWithValue # - [Int] functionStaticCall - [Int] functionStaticCall - [Int] functionDelegateCall # - [Int] functionDelegateCall # - [Int] verifyCallResult + [Lib] SafeERC20 - [Int] safeTransfer # - [Int] safeTransferFrom # - [Int] safeApprove # - [Int] safeIncreaseAllowance # - [Int] safeDecreaseAllowance # - [Prv] _callOptionalReturn # + [Int] IERC20 - [Ext] totalSupply - [Ext] decimals - [Ext] symbol - [Ext] name - [Ext] getOwner - [Ext] balanceOf - [Ext] transfer # - [Ext] allowance - [Ext] approve # - [Ext] transferFrom # + Context - [Pub]
# - [Int] _msgSender_ - [Int] _msgData_ + Ownable (Context) - [Pub] # - [Pub] owner - [Pub] isOwner - [Pub] renounceOwnership # - modifiers: onlyOwner - [Pub] transferOwnership # - modifiers: onlyOwner - [Int] _transferOwnership # + TokenWrapper (Ownable) - [Int] stakeTransfer # - [Int] stakeWithdraw # + BakedStaking (TokenWrapper) - [Pub] adminSetMinTokenAmount # - modifiers: onlyOwner - [Pub] setTokenContract # - modifiers: onlyOwner - [Pub] adminSetDuration # - modifiers: onlyOwner - [Pub] changeStakePoolStatus # - modifiers: onlyOwner - [Pub] stake # - [Pub] unStake # - [Pub] release # - [Pub] batchUserTicketUpdates # - modifiers: onlyOwner - [Pub] totalTicket - [Pub] getAllStakes
($) = payable function # = non-constant function Int = Internal Ext = External Pub = Public + [Int] IERC20 - [Ext] totalSupply - [Ext] decimals - [Ext] symbol - [Ext] name - [Ext] getOwner - [Ext] balanceOf - [Ext] transfer # - [Ext] allowance - [Ext] approve # - [Ext] transferFrom # + [Int] IERC721 - [Ext] balanceOf - [Ext] ownerOf - [Ext] safeTransferFrom # - [Ext] transferFrom # - [Ext] approve # - [Ext] getApproved - [Ext] setApprovalForAll # - [Ext] isApprovedForAll - [Ext] safeTransferFrom # + Ownable - [Pub]
# - [Pub] owner - [Pub] isOwner - [Pub] renounceOwnership # - modifiers: onlyOwner - [Pub] transferOwnership # - modifiers: onlyOwner - [Int] _transferOwnership # + [Int] stakePool - [Ext] totalTicket + mysterybox (Ownable) - [Pub] setHoldingadd # - modifiers: onlyOwner - [Pub] setPaymentToken # - modifiers: onlyOwner - [Pub] setstakepool # - modifiers: onlyOwner - [Pub] createmysterybox # - modifiers: onlyOwner - [Pub] setItem # - modifiers: onlyOwner - [Pub] buyTreasurybox # - [Pub] claimTreasuryBox # - [Prv] isBoxClaimed # - [Int] randomItem - [Pub] allItems - [Pub] openBoxCount - [Pub] claimBoxCount - [Pub] airdropList - modifiers: onlyOwner - [Pub] totalticket - [Pub] UserTreasuryBoxes
BakedVault & BakedVaultFactory Contracts
($) = payable function # = non-constant function Int = Internal Ext = External Pub = Public + [Int] IBEP20 - [Ext] balanceOf - [Ext] transfer # - [Ext] approve # - [Ext] transferFrom # + [Lib] SafeMath - [Int] add - [Int] sub - [Int] sub - [Int] mul - [Int] div - [Int] div + [Lib] Address - [Int] isContract - [Int] sendValue # - [Int] functionCall # - [Int] functionCall # - [Int] functionCallWithValue # - [Int] functionCallWithValue # - [Prv] _functionCallWithValue # + [Int] IPancakePair - [Ext] name - [Ext] symbol - [Ext] decimals - [Ext] totalSupply - [Ext] balanceOf - [Ext] allowance - [Ext] approve # - [Ext] transfer # - [Ext] transferFrom # - [Ext] DOMAIN_SEPARATOR - [Ext] PERMIT_TYPEHASH - [Ext] nonces - [Ext] permit # - [Ext] MINIMUM_LIQUIDITY - [Ext] factory - [Ext] token0 - [Ext] token1 - [Ext] getReserves - [Ext] price0CumulativeLast - [Ext] price1CumulativeLast - [Ext] kLast - [Ext] mint # - [Ext] burn # - [Ext] swap # - [Ext] skim # - [Ext] sync # - [Ext] initialize # + [Int] IPancakeRouter02 - [Ext] addLiquidity # + [Int] IPancakeFactory - [Ext] getPair + [Int] IPool - [Ext] notifyReward ($) + [Int] ICakeVaultFactory - [Ext] fee - [Ext] pancake_router - [Ext] toFee + [Int] BakePool - [Ext] iswhitelisted - [Ext] AllStakes - [Ext] Totalticket + BakedVault - [Pub]
# - [Ext] initilaize # - [Pub] adminApproval # - modifiers: onlyAdmin - [Pub] campaignApprovalStatus - [Pub] adminWhitelist # - modifiers: onlyAdmin - [Pub] batchAdminWhitelisted # - modifiers: onlyAdmin - [Pub] checkWhitelisted - [Pub] failed - [Pub] upComing - [Pub] success - [Pub] isLive - [Pub] buyTokens # - [Pub] withdrawFunds # - [Pub] withDrawRemainingAssetsFailed # - modifiers: campaignConclude - [Pub] adminAddLiquidityManual # - modifiers: only_factory_authorised - [Pub] adminReleseToken # - modifiers: only_factory_authorised - [Pub] manualWithDrawRemainingAssets # - modifiers: campaignConclude - [Pub] manualWithdrawTokens # - [Pub] bakedVAULT # - modifiers: only_factory_authorised - [Int] addLiquidity # - [Pub] unlock # - [Pub] automaticWithDrawRemainingAssets # - modifiers: campaignConclude - [Pub] automaticWithdrawTokens # - [Pub] withdrawFee # - modifiers: onlyAdmin - [Pub] calculateAmount - [Pub] getRemaining - [Pub] getGivenAmount + BakedVaultFactory - [Pub] # - [Ext] createCampaign # - [Pub] calculateTokenforManual - [Pub] calculateTokenforAutomatic
About Solidity Finance
Solidity Finance was founded in 2020 and quickly grew to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1000+ 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 across 1500 projects!.
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 Solidity Audit?
Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A Solidity 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.