Prepared by:
HALBORN
Last Updated 04/26/2024
Date of Engagement by: August 15th, 2022 - August 20th, 2022
100% of all REPORTED Findings have been addressed
All findings
8
Critical
0
High
0
Medium
1
Low
4
Informational
3
Seascape engaged Halborn to conduct a security audit on their smart contracts beginning on August 15th, 2022 and ending on August 20th, 2022. The security assessment was scoped to the smart contract provided in the GitHub repository blocklords3d/smartcontracts/
The team at Halborn was provided a week for the engagement and assigned two full-time security engineers to audit the security of the smart contract. The security engineers are blockchain and smart-contract security experts 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 successfully addressed by Seascape
team.
Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to 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 code and can quickly identify items that do not follow the security best practices. The following phases and associated tools were used during 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
Scanning of solidity files for vulnerabilities, security hotspots or bugs. (MythX
)
Static Analysis of security for scoped contract, and imported functions. (Slither
)
Testnet deployment (Brownie
, Remix IDE
)
IN-SCOPE: The security assessment was scoped to the following smart contracts
Lord.sol
Mead.sol
1st Commit ID: a874a71a9a07a096f82d73442e969b392056db06
2nd Commit ID: 51cf92fbaaad8d07ff4377c0a18be557ee434067
3rd Commit ID: f64fa27b972cd6697b8c851b5586b455c165aec6
4th Commit ID: 09a307a51601cfc5799b63045a22a7c1c479cc20
Critical
0
High
0
Medium
1
Low
4
Informational
3
Impact x Likelihood
HAL-05
HAL-03
HAL-01
HAL-07
HAL-04
HAL-02
HAL-08
HAL-06
Security analysis | Risk level | Remediation Date |
---|---|---|
INTEGER OVERFLOW | Medium | Solved - 09/16/2022 |
EVM STACK LIMIT SURPASSED | Low | Solved - 09/06/2022 |
SAFEMATH LIBRARY IS NOT CORRECTLY IMPLEMENTED | Low | Solved - 09/16/2022 |
TOTALSUPPLY VALUE SHOULD BE OBTAINED BY TOTALSUPPLY() METHOD | Low | Solved - 09/06/2022 |
UNDEFINED VARIABLES ARE USED | Low | Solved - 09/06/2022 |
ONLYBRIDGE MODIFIER IS NEVER USED | Informational | Solved - 09/16/2022 |
SEEDSALE ADDRESS RECEIVES GREATER AMOUNT THAN INTENDED | Informational | Solved - 09/06/2022 |
FUNCTION STATE CAN BE RESTRICTED | Informational | Solved - 09/06/2022 |
// Medium
The Lord.sol
and Mead.sol
smart contracts use an insecure arithmetic operation using the totalSupply()
and amount
variables to determine if it is possible to mint that amount. This operation could lead to an integer overflow if the actual supply of tokens and the amount to mint are high numbers.
function mint(address to, uint256 amount) external onlyBridge {
require(totalSupply() + amount <= limitSupply, "exceeded mint limit");
_mint(to, amount);
}
function mint(uint256 _amount, uint8 _v, bytes32 _r, bytes32 _s) external {
// investor, project verification
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 message = keccak256(abi.encodePacked(msg.sender, address(this), block.chainid, _amount, mintId, mintNonceOf[msg.sender]));
bytes32 hash = keccak256(abi.encodePacked(prefix, message));
address recover = ecrecover(hash, _v, _r, _s);
require(bridges[recover], "sig");
require(totalSupply() + _amount <= limitSupply, "exceeded mint limit");
mintNonceOf[msg.sender]++;
_mint(msg.sender, _amount);
}
to replicate this issue:
in lord.sol:
in mead.sol:
increase limit supply by any number.
try to mint an amount which could cause an overflow, for example '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa'.
mint a high amount, for example '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa'.
let amount1 = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa';
let amount2 = '7';
//..snipped..
await mead.connect(bridge).mint(amount1, sig.v, sig.r, sig.s);
await mead.connect(bridge).mint(amount2, sig.v, sig.r, sig.s);
Error: VM Exception while processing transaction: reverted with panic code 0x11 (Arithmetic operation underflowed or overflowed outside an unchecked block)
at Mead.mint (contracts/erc20/Mead.sol:78)
SOLVED: The SeaScape Team
now implements correctly the SafeMath
library to avoid these overflows.
// Low
The EVM stack is a maximum of 16 deep. Every variable that is created will get pushed onto the stack. This includes function parameters and local variables. The Lord constructor uses too many parameters and local variables, which causes the following error to be displayed at compile time.
CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack.
constructor(
address _seedSale,
address _strategicSale,
address _privateSale,
address _launchpads,
address _ieo,
address _lordsBounty,
address _kingsBounty,
address _dynastyIncentives,
address _liquidity,
address _foundationReserve,
address _advisor,
bool _bridgeAllowed) ERC20("BLOCKLORDS", "LORD") {
bridgeAllowed = _bridgeAllowed;
uint256 _million = 1000 * 1000 * 10 ** 18;
uint256 thousand = 1000 * 10 ** 18;
if (!_bridgeAllowed) {
_mint(_seedSale, 8 * _million + (750 * thousand)); // 8.75% of 100 million
_mint(_seedSale, 6 * _million + (250 * thousand)); // 8.75% of 100 million
_mint(_privateSale, 7 * _million); // 8.75% of 100 million
_mint(_launchpads, 2 * _million); // 8.75% of 100 million
_mint(_ieo, 1 * _million); // 8.75% of 100 million
_mint(_lordsBounty, 25 * _million); // 8.75% of 100 million
_mint(_kingsBounty, 10 * _million); // 8.75% of 100 million
_mint(_dynastyIncentives, 15 * _million); // 8.75% of 100 million
_mint(_liquidity, 10 * _million); // 8.75% of 100 million
_mint(_foundationReserve, 10 * _million); // 8.75% of 100 million
_mint(_advisor, 5 * _million); // 8.75% of 100 million
require(totalSupply() == 100 * _million, "not a 100 million tokens");
}
}
SOLVED: The minting process is now done address by address in several new functions.
// Low
The Lord.sol
and Mead.sol
smart contracts use .add()
and .sub()
functions located in the OpenZeppelin SafeMath library. This library is neither imported nor associated to a variable type (in this case uint256), so the mentioned functions cannot be used.
function mint(address to, uint256 amount) external onlyBridge {
require(totalSupply.add(amount) <= limitSupply, "exceeded mint limit");
_mint(to, amount);
}
function burnFrom(address account, uint256 amount) public onlyBridge {
uint256 currentAllowance = allowance(account, _msgSender());
require(currentAllowance >= amount, "burn amount exceeds allowance");
_approve(account, _msgSender(), currentAllowance
.sub(amount, "transfer amount exceeds allowance"));
_burn(account, amount);
}
function mint(uint256 _amount, uint8 _v, bytes32 _r, bytes32 _s) external {
// investor, project verification
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 message = keccak256(abi.encodePacked(msg.sender, address(this), chainid, _amount, mintId, mintNonceOf[msg.sender]));
bytes32 hash = keccak256(abi.encodePacked(prefix, message));
address recover = ecrecover(hash, _v, _r, _s);
require(bridges[recover], "sig");
require(_totalSupply.add(amount) <= limitSupply, "exceeded mint limit");
mintNonceOf[msg.sender]++;
_mint(msg.sender, _amount);
}
SOLVED: The SeaScape Team
now implements correctly the SafeMath
library.
// Low
The Lord.sol
and Mead.sol
smart contracts use _totalSupply
variable to obtain the tokens total supply. This value is defined as a private variable in the OpenZeppelin ERC20 implementation; therefore, it should be obtained by using the get method
totalSupply()
.
require(totalSupply == 100 * _million, "not a 100 million tokens");
require(totalSupply.add(amount) <= limitSupply, "exceeded mint limit");
require(_totalSupply.add(amount) <= limitSupply, "exceeded mint limit");
SOLVED: The SeaScape team
now uses the totalSupply()
method to retrieve the total token supply.
// Low
The Lord.sol
, Mead.sol
and ImportExportElasticNft.sol
smart contracts use undefined variables, resulting in contracts which do not compile.
limitSupply
(Lord.sol#93)bridgeAllowed
(Mead.sol#36,46)amount
(Mead.sol#73)memory_amount
(Mead.sol#98)chainid
(Mead.sol#67,90)SOLVED: The SeaScape Team
now implements the mentioned variables.
// Informational
The onlyBridge
modifier is never used in the code.
modifier onlyBridge {
require(bridges[msg.sender]);
_;
}
SOLVED: The SeaScape Team
now uses the onlyBridge
modifier on the mint
and burn
functions.
// Informational
Several amounts are minted to the accounts, added as arguments in the constructor. Different amounts are minted twice in the _seedsale
account, making this account 15 million instead of 8.75 million.
constructor(
address _seedSale,
address _strategicSale,
address _privateSale,
address _launchpads,
address _ieo,
address _lordsBounty,
address _kingsBounty,
address _dynastyIncentives,
address _liquidity,
address _foundationReserve,
address _advisor,
bool _bridgeAllowed) ERC20("BLOCKLORDS", "LORD") {
bridgeAllowed = _bridgeAllowed;
uint256 _million = 1000 * 1000 * 10 ** 18;
uint256 thousand = 1000 * 10 ** 18;
if (!_bridgeAllowed) {
_mint(_seedSale, 8 * _million + (750 * thousand)); // 8.75% of 100 million
_mint(_seedSale, 6 * _million + (250 * thousand)); // 8.75% of 100 million
_mint(_privateSale, 7 * _million); // 8.75% of 100 million
_mint(_launchpads, 2 * _million); // 8.75% of 100 million
_mint(_ieo, 1 * _million); // 8.75% of 100 million
_mint(_lordsBounty, 25 * _million); // 8.75% of 100 million
_mint(_kingsBounty, 10 * _million); // 8.75% of 100 million
_mint(_dynastyIncentives, 15 * _million); // 8.75% of 100 million
_mint(_liquidity, 10 * _million); // 8.75% of 100 million
_mint(_foundationReserve, 10 * _million); // 8.75% of 100 million
_mint(_advisor, 5 * _million); // 8.75% of 100 million
require(totalSupply() == 100 * _million, "not a 100 million tokens");
}
}
SOLVED: The seedsale
address now receives the correct amount of tokens.
// Informational
The state mutability of the burn()
function can be restricted to pure.
function burn(uint256 amount) public {
require(false, "Only burnFrom is allowed");
}
SOLVED: The SeaScape team
has removed the affected function.
Halborn used automated testing techniques to enhance the coverage of certain areas of the smart contracts in scope. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified the smart contracts in the repository and was able to compile them correctly into their ABIs and binary format, Slither was run against the contracts. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.
Lord.sol and Mead.sol
No major issues found by Slither.
Halborn used automated security scanners to assist with detection of well-known security issues and to identify low-hanging fruits on the targets for this engagement. Among the tools used was MythX, a security analysis service for Ethereum smart contracts. MythX performed a scan on the smart contracts and sent the compiled results to the analyzers to locate any vulnerabilities.
Lord.sol
Mead.sol
No major issues found by MythX.
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