Halborn Logo

Block Lords - Seascape


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: August 15th, 2022 - August 20th, 2022

Summary

100% of all REPORTED Findings have been addressed

All findings

8

Critical

0

High

0

Medium

1

Low

4

Informational

3


1. INTRODUCTION

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/

2. AUDIT SUMMARY

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.

3. TEST APPROACH & METHODOLOGY

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)

4. SCOPE

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

5. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL

6. SCOPE

Out-of-Scope: New features/implementations after the remediation commit IDs.

7. Assessment Summary & Findings Overview

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 analysisRisk levelRemediation Date
INTEGER OVERFLOWMediumSolved - 09/16/2022
EVM STACK LIMIT SURPASSEDLowSolved - 09/06/2022
SAFEMATH LIBRARY IS NOT CORRECTLY IMPLEMENTEDLowSolved - 09/16/2022
TOTALSUPPLY VALUE SHOULD BE OBTAINED BY TOTALSUPPLY() METHODLowSolved - 09/06/2022
UNDEFINED VARIABLES ARE USEDLowSolved - 09/06/2022
ONLYBRIDGE MODIFIER IS NEVER USEDInformationalSolved - 09/16/2022
SEEDSALE ADDRESS RECEIVES GREATER AMOUNT THAN INTENDEDInformationalSolved - 09/06/2022
FUNCTION STATE CAN BE RESTRICTEDInformationalSolved - 09/06/2022

8. Findings & Tech Details

8.1 INTEGER OVERFLOW

// Medium

Description

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.

Code Location

Lord.sol

    function mint(address to, uint256 amount) external onlyBridge {
        require(totalSupply() + amount <= limitSupply, "exceeded mint limit");
        _mint(to, amount);
    }

Mead.sol

    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:

    • increase limit supply by any number.
    • try to mint an amount which could cause an overflow, for example '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa'.
  • in mead.sol:

    • mint a high amount, for example '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa'.
    • mint again any amount greater than 5 to cause overflow.
  • 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'.

  • mint again any amount greater than 5 to cause overflow.

pentest.js

    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);

Output

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)
Score
Impact: 3
Likelihood: 3
Recommendation

SOLVED: The SeaScape Team now implements correctly the SafeMath library to avoid these overflows.

8.2 EVM STACK LIMIT SURPASSED

// Low

Description

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.

StackLimit

CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack.

Code Location

Lord.sol

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");
        }
    }
Score
Impact: 2
Likelihood: 3
Recommendation

SOLVED: The minting process is now done address by address in several new functions.

8.3 SAFEMATH LIBRARY IS NOT CORRECTLY IMPLEMENTED

// Low

Description

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.

Code Location

Lord.sol

function mint(address to, uint256 amount) external onlyBridge {
        require(totalSupply.add(amount) <= limitSupply, "exceeded mint limit");
        _mint(to, amount);
    }

Lord.sol

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);
    }

Mead.sol

    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);
    }
Score
Impact: 3
Likelihood: 2
Recommendation

SOLVED: The SeaScape Team now implements correctly the SafeMath library.

8.4 TOTALSUPPLY VALUE SHOULD BE OBTAINED BY TOTALSUPPLY() METHOD

// Low

Description

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

Code Location

Lord.sol

require(totalSupply == 100 * _million, "not a 100 million tokens");

Lord.sol

require(totalSupply.add(amount) <= limitSupply, "exceeded mint limit");

Mead.sol

require(_totalSupply.add(amount) <= limitSupply, "exceeded mint limit");
Score
Impact: 2
Likelihood: 2
Recommendation

SOLVED: The SeaScape team now uses the totalSupply() method to retrieve the total token supply.

8.5 UNDEFINED VARIABLES ARE USED

// Low

Description

The Lord.sol, Mead.sol and ImportExportElasticNft.sol smart contracts use undefined variables, resulting in contracts which do not compile.

Code Location

  • limitSupply (Lord.sol#93)
  • bridgeAllowed (Mead.sol#36,46)
  • amount (Mead.sol#73)
  • memory_amount (Mead.sol#98)
  • chainid (Mead.sol#67,90)
Score
Impact: 3
Likelihood: 1
Recommendation

SOLVED: The SeaScape Team now implements the mentioned variables.

8.6 ONLYBRIDGE MODIFIER IS NEVER USED

// Informational

Description

The onlyBridge modifier is never used in the code.

Code Location

Mead.sol

    modifier onlyBridge {
            require(bridges[msg.sender]);
        _;
    }
Score
Impact: 1
Likelihood: 2
Recommendation

SOLVED: The SeaScape Team now uses the onlyBridge modifier on the mint and burn functions.

8.7 SEEDSALE ADDRESS RECEIVES GREATER AMOUNT THAN INTENDED

// Informational

Description

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.

Code Location

Lord.sol

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");
        }
    }
Score
Impact: 2
Likelihood: 1
Recommendation

SOLVED: The seedsale address now receives the correct amount of tokens.

8.8 FUNCTION STATE CAN BE RESTRICTED

// Informational

Description

The state mutability of the burn() function can be restricted to pure.

Code Location

Lord.sol

    function burn(uint256 amount) public {
        require(false, "Only burnFrom is allowed");
    }
Score
Impact: 1
Likelihood: 1
Recommendation

SOLVED: The SeaScape team has removed the affected function.

9. Automated Testing

STATIC ANALYSIS REPORT

Description

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.

Slither results

Lord.sol and Mead.sol

  • No major issues found by Slither.

AUTOMATED SECURITY SCAN

Description

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.

MythX results

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.