Prepared by:
HALBORN
Last Updated 04/26/2024
Date of Engagement by: May 11th, 2022 - May 12th, 2022
83% of all REPORTED Findings have been addressed
All findings
6
Critical
0
High
0
Medium
2
Low
1
Informational
3
\client engaged Halborn to conduct a security audit on their smart contracts beginning on 2022-05-11 and ending on May 12th, 2022. The security assessment was scoped to the smart contracts provided to the Halborn team.
The contract in scope was an updated version of the Staking Saloon minigame, with a new reward mechanism.
The team at Halborn was provided one week for the engagement and assigned a full-time security engineer to audit the security of the smart contract. The security engineer is a blockchain and smart-contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.
The purpose of this audit is to:
Ensure that smart contract functions operate as intended
Identify potential security issues with the smart contracts
In summary, Halborn identified some security risks that were mostly addressed by the \client team.
Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy regarding the scope of this audit. While manual testing is recommended to uncover flaws in logic, process, and implementation, automated testing techniques help enhance coverage of the bridge code and can quickly identify items that do not follow security best practices. The following phases and associated tools were used throughout the term of the audit:
Research into architecture and purpose
Smart contract manual code review and walkthrough
Graphing out functionality and contract logic/connectivity/functions (solgraph
)
Manual assessment of use and safety for the critical Solidity variables and functions in scope to identify any arithmetic related vulnerability classes
Manual testing by custom scripts
Static Analysis of security for scoped contract, and imported functions (Slither
)
Testnet deployment (Brownie
, Remix IDE
)
The assessment was scoped to the smart contract available in \href{https://github.com/liumaosong/seascape-smartcontracts/blob/main/contracts/mini-game/game-3/NewStakingSaloon.sol}{GitHub} at commit ID 3a438cc3d4f7dc615b0609c67785d340c820da62.
Remediations were checked up until commit f4d62567a58fdbcb582cb83bac36eca846917148.
Critical
0
High
0
Medium
2
Low
1
Informational
3
Impact x Likelihood
HAL-01
HAL-02
HAL-03
HAL-04
HAL-05
HAL-06
Security analysis | Risk level | Remediation Date |
---|---|---|
ADMIN CAN CHANGE SESSION DETAILS AFTER THE START OF A SESSION | Medium | Solved - 05/30/2022 |
NOT CHECKING BALANCE BEFORE AND AFTER UNTRUSTED TOKENS TRANSFERS | Medium | Risk Accepted |
UNCHECKED CONTRACT BALANCE | Low | Solved - 05/30/2022 |
COMMENTED OUT CODE | Informational | Solved - 05/30/2022 |
POSSIBLE SIGNATURE REPLAY | Informational | - |
USING ++I CONSUMES LESS GAS THAN I++ IN LOOPS | Informational | Solved - 05/30/2022 |
// Medium
Testing revealed that the setType
function allowed the owner of the contract to change details regarding running sessions. Should this privilege be abused or the private key of the owner be stolen, users might risk of burning their NFTs without their knowledge when unstaking. This is due to the function allowing to set the NFTs in a certain session to burn and whether users get a bonus by playing the game.
NewStakingSaloon.sol
Lines#
//change type about session
function setType(uint256 _sessionId, bool _burn, bool _specify) external onlyOwner{
Session storage _session = sessions[_sessionId];
require(block.timestamp <= (_session.startTime + _session.period), "saloon: session end");
_session.burn = _burn;
_session.specify = _specify;
}
SOLVED: The Seascape team
removed the function.
\newpage
// Medium
None
NewStakingSaloon.sol
Lines# 512-540
//Get check-in revenue
function getSignInReward(uint256 _sessionId, uint8 _tagNum, uint8 v, bytes32 r, bytes32 s) external
Params storage params = signinRewards[_sessionId][_tagNum];
Session storage _session = sessions[_sessionId];
require(received[_sessionId][msg.sender][_tagNum] == false, "saloon: this signid reward is already received");
require(_tagNum <= _session.signinRewardNum, "saloon: this reward do not _tagNum");
IERC20 token = IERC20(params.token);
uint256 NftId = 0;
{
bytes32 _messageNoPrefix = keccak256(abi.encodePacked(_sessionId, _tagNum, msg.sender));
bytes32 _message = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageNoPrefix));
address _recover = ecrecover(_message, v, r, s);
require(_recover == verifier, "Verification failed");
}
if (params.quality > 0) {
NftId = nftFactory.mintQuality(msg.sender, params.generation, params.quality);
}
if (params.amount > 0) {
require(token.transferFrom(owner(), msg.sender, params.amount), "saloon: transferFrom reward failed");
}
received[_sessionId][msg.sender][_tagNum] = true;
emit GetSignInReward(msg.sender, _sessionId, _tagNum, NftId, params.amount);
}
RISK ACCEPTED: The Seascape team
accepted the risk, as they will only use a trusted token, Crowns.
\newpage
// Low
Testing revealed that when a session is created, there are no checks to ensure that the contract balance is enough to pay the reward. This could lead to failed transactions when users unstake or claim their rewards.
NewStakingSaloon.sol
Lines# 117-156
function startSession(
address _rewardToken,
uint256 _totalReward,
uint256 _period,
uint256 _startTime,
address _verifier
)
external
onlyOwner
{
require(_rewardToken != address(0), "Token can't be zero address");
require(_startTime > block.timestamp, "Seascape Staking: Seassion should start in the future");
require(_period > 0, "Seascape Staking: Session duration should be greater than 0");
require(_totalReward > 0, "Seascape Staking: Reward amount should be greater than 0");
require(_verifier != address(0), "verifier can't be zero address");
if (lastSessionId > 0) {
require(!isActive(lastSessionId), "Seascape Staking: Can't start when session is active");
}
/// @dev required CWS balance of this contract
// require(crowns.balanceOf(address(this)) >= _totalReward, "Seascape Staking: Not enough balance of Crowns for reward");
//--------------------------------------------------------------------
// creating the session
//--------------------------------------------------------------------
uint256 _sessionId = sessionId.current();
uint256 _rewardUnit = _totalReward.mul(MULTIPLIER).div(_period);
sessions[_sessionId] = Session(_totalReward, _period, _startTime, 0, 0, _rewardUnit, 0, 0, _startTime, true, false, 0);
//--------------------------------------------------------------------
// updating rest of session related data
//--------------------------------------------------------------------
sessionId.increment();
rewardToken = IERC20(_rewardToken);
lastSessionId = _sessionId;
verifier = _verifier;
emit SessionStarted(_sessionId, _totalReward, _startTime, _startTime + _period);
}
SOLVED: The Seascape team
amended the code to add contract balance checks.
\newpage
// Informational
There are instances within the code where commented code is left from previous development iterations. While this does not cause any security concerns, it makes the contract less readable.
NewStakingSaloon.sol
Lines# 137-138
SOLVED: The Seascape team
removed commented code for readability.
\newpage
// Informational
// Informational
In the loop below, the variable i
is incremented using i++
. It is known that, in loops, using ++i
costs less gas per iteration than i++
. This does not only apply to the iterator variable. It also applies to variables declared within the loop code block.
NewStakingSaloon.sol
Line #372-374
for(uint _index = 0; _index < 3; _index++){
_interests = _interests.add(calculateInterest(_sessionId, msg.sender, _index));
}
SOLVED: The Seascape team
amended the code as suggested to optimize the contract.
\newpage
Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.
// Download the full report
* Use Google Chrome for best results
** Check "Background Graphics" in the print settings if needed