Web3 Security Review: Superseed Token Audit

Cantina Security Report

Organization

@superseed-xyz

Engagement Type

Cantina Reviews

Period

-

Repositories

N/A

Researchers


Findings

Low Risk

2 findings

2 fixed

0 acknowledged

Informational

3 findings

3 fixed

0 acknowledged

Gas Optimizations

1 findings

1 fixed

0 acknowledged


Low Risk2 findings

  1. Missing input validation in constructors

    Severity

    Severity: Low

    Submitted by

    undefined avatar image

    0xWeiss


    Description

    Both contracts in scope (TokenClaim and SuperseedToken) lack input validation on their constructors.

    Recommendation

    Validate the following parameters:

    • TokenClaim:
    constructor(address _initialOwner, address _token, address _treasury) Ownable(_initialOwner) {
    + require(_token != address(0), "invalid address");
    + require(_treasury != address(0), "invalid address");
    token = IERC20(_token);
    treasury = _treasury;
    }
    • SuperseedToken:
    constructor(
    address superAdmin,
    address minter,
    address treasury
    )
    ERC20("Superseed", "SUPR")
    ERC20Permit("Superseed")
    {
    + require(treasury != address(0), "invalid address");
    + require(superAdmin != address(0), "invalid address");
    + require(minter != address(0), "invalid address");
    _mint(treasury, 10_000_000_000e18);
    _grantRole(DEFAULT_ADMIN_ROLE, superAdmin);
    _grantRole(MINTER_ROLE, minter);
    }
  2. Missing event when updating the merkle root

    Severity

    Severity: Low

    Submitted by

    undefined avatar image

    0xWeiss


    Description

    The merkle root function makes a key state change but lacks to emit an event:

    function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
    if (_merkleRoot == bytes32(0)) revert InvalidInput("_merkleRoot");
    merkleRoot = _merkleRoot;
    }

    Recommendation

    Emit an event with the old and the new merkle roots as arguments:

    function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
    if (_merkleRoot == bytes32(0)) revert InvalidInput("_merkleRoot");
    + emit merkleRootUpdated(merkleRoot,_merkleRoot);
    merkleRoot = _merkleRoot;
    }

Informational3 findings

  1. ERC20 import is redundant

    Severity

    Severity: Informational

    Submitted by

    undefined avatar image

    0xWeiss


    Description

    The SuperseedToken contract imports multiple ERC20 extensions: ERC20, ERC20Burnable, AccessControl, ERC20Permit, ERC20Votes which under the hood already use the original ERC20 contract, making it redundant.

    Recommendation

    Remove the ERC20 import:

    - contract SuperseedToken is ERC20, ERC20Burnable, AccessControl, ERC20Permit, ERC20Votes {
    + contract SuperseedToken is ERC20Burnable, AccessControl, ERC20Permit, ERC20Votes {
  2. Avoid Unnecessary Use of SafeERC20 for SuperseedToken

    Severity

    Severity: Informational

    Submitted by

    undefined avatar image

    Kankodu


    Description

    Since SuperseedToken strictly follows the ERC20 standard and reverts on failed transfers, the safety checks provided by the SafeERC20 library are unnecessary.

    Recommendation

    Use direct ERC20 transferFrom call to avoid unnecessary overhead.

  3. Use Named Constants for Improved Readability

    Severity

    Severity: Informational

    Submitted by

    undefined avatar image

    Kankodu


    Description

    Named constants enhance code clarity and maintainability by replacing hardcoded values with meaningful identifiers.

    Recommendation

    Define TEN_BILLION_TOKENS as a constant:

    uint256 internal constant TEN_BILLION_TOKENS = 10_000_000_000e18;

    Use this constant instead of directly writing the value to improve readability and prevent errors.

Gas Optimizations1 finding

  1. Improve Custom Error Definitions for Clarity and Efficiency

    Severity

    Severity: Gas optimization

    Submitted by

    undefined avatar image

    Kankodu


    Description

    The InvalidInput error currently includes a string parameter, which increases bytecode size and provides no significant advantage over using a standard revert with a string message.

    Recommendation

    Instead of using a string parameter, define multiple custom errors with structured and informative parameters. This improves clarity while reducing bytecode size. For example:

    error MerkleRootCannotBeEmpty();
    error ZeroBalanceForProvidedToken(address token);
    error InputAmountCannotBeZero();

    By using structured parameters, errors become more meaningful and cost-efficient while maintaining clarity.