After a line by line manual analysis and automated review, Guardian Audits has concluded that:
- Published
- Language
- Solidity
- Chains
- Fantom
- Sector
- Gaming and prediction
- 2 Critical
- 3 High
- 8 Medium
- 15 Low
- 0 Informational
Scope
Overview
After a line by line manual analysis and automated review, Guardian Audits has concluded that:
- UltiBet’s smart contracts have an LOW RISK SEVERITY
- UltiBet’s smart contracts have an ACTIVE OWNERSHIP
- UltiBet’s smart contract owner has multiple “write” privileges. Centralization risk correlated to the active ownership is MEDIUM
Findings 28
-
GLOBAL-1 Low Poor Practices Best Practices Resolved
Description
Throughout the contracts there are myriad instances of:
- Lack of camelcase
- Use of SafeMath when Solidity version ^0.8.0 has overflow and underflow checks
- Unnecessary local variables which waste gas e.g.
address to = payable(ultibetsTreasury) - Redundant boolean checks e.g.
== true - Typos in the comments
- Many functions can be declared
external
Recommendation
Use camelcase throughout the contracts, remove redundant and unnecessary local variables, do not perform redundant boolean checks, revise comments, remove SafeMath operations to save gas, declare functions not used internally as
external.Resolution
- Typos, code-style, function declarations, and frivolous code has been addressed.
-
GLOBAL-2 High Centralization Risk Centralization / Privilege Acknowledged
Description
Throughout the contracts there is a risk of admins and oracles using their privilege to benefit themselves or act malicious toward user’s holdings. Contracts utilizing the CustomAdmin access control model face more risk as the number of admins or oracles increase because it only takes one to act mischievous.
Recommendation
Ensure that privileged addresses such as admins are all a multi-sig and/or introduce a timelock for improved community oversight. Secure a KYC for increased community trust.
Resolution
- The risk is acknowledged and will be limited with appropriate measures such as multi-sig addresses and community transparency.
-
UB-1 Critical Total Bets Not Updated Logical Error Resolved
Description
The
betsmapping is not being updated inplaceBet. WhenwithdrawGainis called,.div(bets[result.winner])would lead to a division by 0 error every time. Therefore, no one would be able to withdraw their gains, leading to complete loss of funds.Recommendation
In
placeBet, increment thebetsmapping withbets[_side] += betAmount.Resolution
- The
betsmapping is now updated inplaceBet.
- The
-
UB-2 High Order of Operations Logical Error Resolved
Description
The logic does not correctly calculate the
gaindue to the SafeMath order of operations. With the SafeMath operations the addition is performed first, but what is needed is to first calculate the winner’s part of the loser funds and then finally adding it toBettorBetWinner.For example:
3.add(10).mul(3).div(6) = 6
3 + 10 * 3 / 6 = 8
Recommendation
Replace with
BettorBetWinner + bets[result.loser] * BettorBetWinner / bets[result.winner].Resolution
- The logic has been replaced as suggested.
-
UB-3 Medium Report Result Twice Logical Error Resolved
Description
reportResultdoes not require the event to be finished. Therefore, the result can be reported multiple times and the winner and loser swapped while the treasury fee is taken multiple times.Recommendation
Add
require(!isEventFinished)inreportResultso a result cannot be reported twice.Resolution
- The suggested requires has been added.
-
UB-4 Medium Same Winner and Loser Logical Error Resolved
Description
reportResultdoes not prevent the oracle from setting the winner and loser to the same side which can lead to loss of funds for many users.Consider a 100 ether bet on one side and 10 ether bet on another, but the 100 ether side is chosen as both the winner and loser. From the calculation in
withdrawGain, the contract will attempt to payout a total of 200 ether while it only holds 110. Therefore the users who claim first will deplete the contract and the ones who claim later will experience a complete loss of funds.Recommendation
Add a safety check that the arguments
_winnerand_loserare different.Resolution
- The suggested requires has been added.
-
UB-5 Low Potential DoS Denial-of-Service Resolved
Description
reportResultdepends on the ETH transfer to the treasury to be successful in order for the result to be reported. Such a call can fail, leading to a DoS condition.Recommendation
Remove lines 185-187 as there is no need to have the transfer logic there. External calls should ideally be isolated into another transaction and the admin calling
withdrawEarnedFeesserves that purpose.Resolution
- Unnecessary transfer logic has been removed.
-
UB-6 Low Inaccurate Comment Inaccurate Comments Resolved
Description
The comment for
stopBetstates that “... this action can only be performed by an administrator” but the modifier isOnlyOracle. In addition, the comment states that it is an emergency function, yet it is required to be called in order for a bettor towithdrawGain.Recommendation
Update the comments to accurately reflect the function. In addition, verify if
stopBetis needed. If it is truly only used for emergency situations, it does not make sense to have it required so user’s can withdraw their gains.Resolution
- The comment has been updated.
-
UB-7 Low Superfluous Code Optimization Resolved
Description
The
betAmountForYes, andbetAmountForNovariables are never assigned in thebettorBetHistorymapping.Recommendation
Remove them from the struct or use them to replace the
betsAmountPerBettormapping.Resolution
- The variables have been removed.
-
UB-8 Low Superfluous Code Optimization Resolved
Description
The
inArrayYesandinArrayNovariables are never used.Recommendation
Remove them.
Resolution
- The variables have been removed.
-
UBT-1 Critical Treasury Cannot Receive ETH Logical Error Resolved
Description
Because there is no
receiveor fallbackfunction, any contract that relies on sending ETH to the treasury will face unintended consequences such as stuck fees.Recommendation
Add a
receive() external payable { }function to the contract.Resolution
- A receive function has been added.
-
UBT-2 Low Superfluous Code Optimization Resolved
Description
The
require(msg.sender == Admin)can be deduplicated into anonlyAdminmodifier used for every permissioned function.Recommendation
Deduplicate the
requirelogic into anonlyAdminmodifier and apply theonlyAdminmodifier to every permissioned function.Resolution
- The modifier has been created and added to relevant functions.
-
UBT-3 Low Typo Typo Resolved
Description
The
_descreptionparameter increateAllocationis misspelled.Recommendation
Correct it to
_description.Resolution
- The typo has been fixed.
-
UBT-4 Low Superfluous Code Optimization Resolved
Description
Since the
amountvariable is only used once and theallocations[msg.sender].salaryvalue is not changed before the use ofamount, it is unnecessary to declare theamountvariable.Recommendation
Remove the declaration and use of the
amountvariable for gas optimization.Resolution
- The variable has been removed.
-
UBT-5 Low Vague Event Information Events Resolved
Description
The
SalaryChangedevent is emitted with just theblock.timestampand_newSalary, leaving no record of which address the salary was changed for.Recommendation
Include the
_addressas a part of theSalaryChangedevent data.Resolution
- The address is now included in the
SalaryChangedevent data.
- The address is now included in the
-
UBT-6 Low Inefficient Addition Optimization Resolved
Description
The
allocations[msg.sender].totalPayout = allocations[msg.sender].totalPayout.add(amount)computation inefficiently references the allocations state data twice.Recommendation
Use
+=in order to save gas.Resolution
+=is now used.
-
MS-1 Low Inaccurate Comment Inaccurate Comments Resolved
Description
The comment states “Public Functions” yet
initializedirectly below is internal.Recommendation
Move comment to where the section below is solely public functions.
Resolution
- The comment has been updated.
-
UBF-1 Low Inaccurate Variable Name Best Practices Resolved
Description
The mapping
addressToIdis really the contract id to the address of the contract.Recommendation
Change name to
idToAddress.Resolution
- The name of the mapping has been updated.
-
CA-1 Low Inaccurate Comment Inaccurate Comments Resolved
Description
The comment states that the function adds the specified address to the list of
administratorsbut it updates the address in the mapping ofOracles.Recommendation
Update the comment to reflect what the function does.
Resolution
- The comment has been updated.
-
SQDF-1 Medium Report Result Twice Logical Error Resolved
Description
isResultReportedis not set to true after the call toreportResult. Therefore, the result can be reported multiple times with varying arguments. Furthermore, line 119isVotingClosed = falsecan be used to prevent a winner from ever getting picked inpickWinner().Recommendation
Add
isResultReported = trueto the end of the function.Resolution
- The suggested addition was made.
-
SQDF-2 Medium Arbitrary Voting Results Logical Error Resolved
Description
In the
resultVotefunction, theifstatements that determines whichfinalVoteDecisionto return are placed inside of theforloop that counts the votes. Therefore thefinalVoteDecisionwill be arbitrarily based on whichever votes happen to be first in thevoteslist.Recommendation
Move the
ifstatements determiningfinalVoteDecisionafter theforloop, or preferably refactor the voting entirely per SQDF-3.Resolution
- The
ifstatement has been moved outside of the for loop
- The
-
SQDF-3 Low Potential DoS Denial-of-Service Resolved
Description
The
forloop is reliant on the number of votes. If the number of votes is very high, the calculation may exceed the block gas limit and fail.Recommendation
Another way to approach this problem is to have an int variable that increases by 1 when the player votes one way and decreases by 1 when the player votes the other way in Vote. The sign of the end value once voting is finished will tell you which choice had more votes.Therefore, a for loop can be entirely avoided.
Resolution
- The suggested approach was implemented.
-
SQDF-4 Low Superfluous Code Optimization Resolved
Description
reportResultdoes not need to contain logic for transferring funds to the prize pool becausetransferTotalEntryFeestoPrizePoolexists.Recommendation
Remove transfer logic from
reportResult.Resolution
- Transfer logic was removed from
reportResult.
- Transfer logic was removed from
-
SQDF-5 High Weak Source of Randomness Randomness Acknowledged
Description
pickWinneruses weak sources of on-chain randomness. A validator can exploit this in order to obtain a winner that is beneficial to themselves.Recommendation
Utilize a strong source of randomness whether it be the on-chain randomness pattern or an oracle.
Resolution
- Randomness will be secured with the implementation of Chainlink VRF.
-
SQDR-1 Medium No Check For Max Players Logical Error Resolved
Description
registerPlayerdoes not check if registration exceeds themaxNumberOfPlayers. As a result, contracts that loop over the winners such as SquidBetPrizePool may face a DoS attack.Recommendation
Add the check in
registerPlayer.Resolution
- The suggested requires has been added.
-
SQDR-2 Medium Can’t Update Betting Fee Logical Error Resolved
Description
The betting fee is unable to be updated because
updateBettingFeesupdates themaxNumberofPlayersrather thanbettingFee.Recommendation
Change the function body to
bettingFee = _newBettingFee.Resolution
- The function now updates
bettingFee.
- The function now updates
-
SQPR-1 Medium Winners Can’t Get Prize Denial-of-Service Resolved
Description
winnersClaimPrizePooluses a push pattern such that it loops over all winners and sends them their funds. If there are too many equal winners then no one would be able to claim due to the for loop exceeding the block gas limit. Furthermore, if a winner is a contract that is unable to receive ether upon.transfer(), no one would be able to receive their prize money.Recommendation
Utilize a pull-over-push pattern such that when a user calls
winnersClaimPrizePool, they receive solely their funds and no other funds are dispatched.Resolution
- A pull over push pattern was introduced.
-
SQPR-2 Medium Arbitrary Prize Distribution Arbitrary Control Acknowledged
Description
Anyone can call the function
winnersClaimPrizePooland end the prize pool as long as some winners have been added.Recommendation
Make sure to always add all winners at once.
Resolution
- The risk has been acknowledged.
No findings match.
More from UltiBets
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
