Overview

Kindly
Rank #N/A

Skynet Trust Score

88%
  • Project is Relatively Decentralized
  • Large Market Cap (top 25%)
  • Long-running Project
  • Trust Score is #1 amongst all projects
Security Score
88 / 100
Market & Community
34 / 100

Kindly Info

Audits

Onboarded Date

14/Dec/2022

Contracts

0x999...8b488

How do you feel about this project's security?
Documents
File Name Type Size Upload Date Action
Zip File 4.57 MB 12 Dec 2021
PDF File 8.89 MB 24 Nov 2021
MP4 File 14.62 MB 19 Nov 2021
XSL File 2.38 KB 14 Nov 2021
Floder File 87.24 MB 08 Nov 2021
PNG File 879 KB 02 Nov 2021
Activities
Oliver Phillips New

We talked about a project on linkedin.

Today
N
Nancy Martino In Progress

Create new project Buildng product

Yesterday
Natasha Carey Completed

Adding a new event with attachments

25 Nov
Bethany Johnson

added a new member to velzon dashboard

19 Nov
Your order is placed Out of Delivery

These customers can rest assured their order has been placed.

16 Nov
Lewis Pratt

They all have something to say beyond the words on the page. They can come across as casual or neutral, exotic or graphic.

22 Oct
Monthly sales report

2 days left notification to submit the monthly sales report. Reports Builder

15 Oct
New ticket received Completed

User Erica245 submitted a ticket.

26 Aug
Nancy Martino

Team Leader & HR

225

Projects

197

Tasks

HB
Henry Baird

Full Stack Developer

352

Projects

376

Tasks

Frank Hook

Project Manager

164

Projects

182

Tasks

Jennifer Carter

UI/UX Designer

225

Projects

197

Tasks

ME
Megan Elmore

Team Leader & Web Developer

201

Projects

263

Tasks

Alexis Clarke

Backend Developer

132

Projects

147

Tasks

NC
Nathan Cole

Front-End Developer

352

Projects

376

Tasks

Joseph Parker

Team Leader & HR

64

Projects

93

Tasks

Erica Kernan

Web Designer

345

Projects

298

Tasks

DP
Donald Palmer

Wed Developer

97

Projects

135

Tasks

Showing 1 to 10 of 12 entries

Code Audit History

1 Audit available
Last Audit was delivered on 14 December 2022

Kindly -Audit

View Findings
5

All Findings

0

Acknowledge

0

Partially

5

Resolved

1
Critical privilege
2
Major privilege
1
Medium privilege
1
Minor privilege
0
Optimization none
0
Informational none
0
Discussion none

Method

Audited Files/SHA256

Contracts

0x999e2e604f...8b488

Manual Review Static Analysis
Audit Timeline
Requested on
14 December 2022
Revisioned on
14 December 2022

Formal Verification Result

9 / 38 Properties True
80%

Token Standard

ERC-20

Functions

6

Verified Contract

Kindly (kindlyTokenContract.sol) 1

Kindly Smart Contract Code

                        
                        

// SPDX-License-Identifier: MIT


pragma solidity 0.8.2;


import "@openzeppelin/contracts/utils/Address.sol";

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";


//AccessControlMixin

import "@openzeppelin/contracts/access/AccessControl.sol";

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";


// File: contracts/common/AccessControlMixin.sol


contract AccessControlMixin is AccessControl {

    string private _revertMsg;


    function _setupContractId(string memory contractId) internal {

        _revertMsg = string(

            abi.encodePacked(contractId, ": INSUFFICIENT_PERMISSIONS")

        );

    }


    modifier only(bytes32 role) {

        require(hasRole(role, _msgSender()), _revertMsg);

        _;

    }

}


// File: contracts/child/ChildToken/IChildToken.sol


interface IChildToken {

    function deposit(address user, bytes calldata depositData) external;

}


// File: contracts/common/Initializable.sol


contract Initializable {

    bool initialized = false;


    modifier initializer() {

        require(!initialized, "already initialized");

        _;

        initialized = true;

    }

}


// File: contracts/common/EIP712Base.sol


contract EIP712Base is Initializable {

    string public constant ERC712_VERSION = "1";


    bytes32 internal constant EIP712_DOMAIN_TYPEHASH =

        keccak256(

            bytes(

                "EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)"

            )

        );

    bytes32 internal domainSeperator;


    // supposed to be called once while initializing.

    // one of the contracts that inherits this contract follows proxy pattern

    // so it is not possible to do this in a constructor

    function _initializeEIP712(string memory name) internal initializer {

        _setDomainSeperator(name);

    }


    function _setDomainSeperator(string memory name) internal {

        domainSeperator = keccak256(

            abi.encode(

                EIP712_DOMAIN_TYPEHASH,

                keccak256(bytes(name)),

                keccak256(bytes(ERC712_VERSION)),

                address(this),

                bytes32(getChainId())

            )

        );

    }


    function getDomainSeperator() public view returns (bytes32) {

        return domainSeperator;

    }


    function getChainId() public view returns (uint256) {

        uint256 id;

        assembly {

            id := chainid()

        }

        return id;

    }


    /**

     * Accept message hash and returns hash message in EIP712 compatible form

     * So that it can be used to recover signer from signature signed using EIP712 formatted data

     * https://eips.ethereum.org/EIPS/eip-712

     * "\\x19" makes the encoding deterministic

     * "\\x01" is the version byte to make it compatible to EIP-191

     */

    function _toTypedMessageHash(bytes32 messageHash)

        internal

        view

        returns (bytes32)

    {

        return

            keccak256(

                abi.encodePacked("\x19\x01", getDomainSeperator(), messageHash)

            );

    }

}


// File: contracts/common/NativeMetaTransaction.sol


contract NativeMetaTransaction is EIP712Base {

    bytes32 private constant META_TRANSACTION_TYPEHASH =

        keccak256(

            bytes(

                "MetaTransaction(uint256 nonce,address from,bytes functionSignature)"

            )

        );

    event MetaTransactionExecuted(

        address userAddress,

        address payable relayerAddress,

        bytes functionSignature

    );

    mapping(address => uint256) nonces;


    /*

     * Meta transaction structure.

     * No point of including value field here as if user is doing value transfer then he has the funds to pay for gas

     * He should call the desired function directly in that case.

     */

    struct MetaTransaction {

        uint256 nonce;

        address from;

        bytes functionSignature;

    }


    function executeMetaTransaction(

        address userAddress,

        bytes memory functionSignature,

        bytes32 sigR,

        bytes32 sigS,

        uint8 sigV

    ) public payable returns (bytes memory) {

        MetaTransaction memory metaTx = MetaTransaction({

            nonce: nonces[userAddress],

            from: userAddress,

            functionSignature: functionSignature

        });


        require(

            _verify(userAddress, metaTx, sigR, sigS, sigV),

            "Signer and signature do not match"

        );


        // increase nonce for user (to avoid re-use)

        nonces[userAddress] = nonces[userAddress] + 1;


        emit MetaTransactionExecuted(

            userAddress,

            payable(msg.sender),

            functionSignature

        );


        // Append userAddress and relayer address at the end to extract it from calling context

        (bool success, bytes memory returnData) = address(this).call(

            abi.encodePacked(functionSignature, userAddress)

        );

        require(success, "Function call not successful");


        return returnData;

    }


    function _hashMetaTransaction(MetaTransaction memory metaTx)

        internal

        pure

        returns (bytes32)

    {

        return

            keccak256(

                abi.encode(

                    META_TRANSACTION_TYPEHASH,

                    metaTx.nonce,

                    metaTx.from,

                    keccak256(metaTx.functionSignature)

                )

            );

    }


    function getNonce(address user) external view returns (uint256 nonce) {

        nonce = nonces[user];

    }


    function _verify(

        address signer,

        MetaTransaction memory metaTx,

        bytes32 sigR,

        bytes32 sigS,

        uint8 sigV

    ) internal view returns (bool) {

        require(signer != address(0), "NativeMetaTransaction: INVALID_SIGNER");

        return

            signer ==

            ecrecover(

                _toTypedMessageHash(_hashMetaTransaction(metaTx)),

                sigV,

                sigR,

                sigS

            );

    }

}


// File: contracts/common/ContextMixin.sol


abstract contract ContextMixin {

    function msgSender() internal view returns (address payable sender) {

        if (msg.sender == address(this)) {

            bytes memory array = msg.data;

            uint256 index = msg.data.length;

            assembly {

                // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.

                sender := and(

                    mload(add(array, index)),

                    0xffffffffffffffffffffffffffffffffffffffff

                )

            }

        } else {

            sender = payable(msg.sender);

        }

        return sender;

    }

}


// File: contracts/child/ChildToken/Kindly.sol


contract Kindly is

    ERC20,

    IChildToken,

    AccessControlMixin,

    NativeMetaTransaction,

    ContextMixin

{

    bytes32 public constant DEPOSITOR_ROLE = keccak256("DEPOSITOR_ROLE");

    address mulSign = 0x8a52a9179Ac923ee924e805551621ec6a31c385A;


    event Minted(address user, uint256 amount);

    event Deposited(address user, uint256 amount);


    constructor(

        string memory name_,

        string memory symbol_,

        address childChainManager

    ) ERC20(name_, symbol_) {

        _setupContractId("Kindly");

        _setupRole(DEPOSITOR_ROLE, childChainManager);

        _initializeEIP712(name_);

        _mint(mulSign, 108e24);

    }


    /**

     * @notice called when token is deposited on root chain

     * @dev Should be callable only by ChildChainManager

     * Should handle deposit by minting the required amount for user

     * Make sure minting is done only by this function

     * @param user user address for whom deposit is being done

     * @param depositData abi encoded amount

     */

    function deposit(address user, bytes calldata depositData)

        external

        override

        only(DEPOSITOR_ROLE)

    {

        uint256 amount = abi.decode(depositData, (uint256));

        _mint(user, amount);

        emit Deposited(user, amount);

    }


    /**

     * @notice called when user wants to withdraw tokens back to root chain

     * @dev Should burn user's tokens. This transaction will be verified when exiting on root chain

     * @param amount amount of tokens to withdraw

     */

    function withdraw(uint256 amount) external {

        _burn(_msgSender(), amount);

    }


    function isMember(address account) public view virtual returns (bool) {

        return hasRole(DEFAULT_ADMIN_ROLE, account);

    }


    function _msgSender() internal view override returns (address sender) {

        return ContextMixin.msgSender();

    }

}

Code Audit Findings

Audits Overview

Context
Project Name
Kindly
Platform
Language
Codebase
Commit
About Xamer Audits
Delivery Date
Audit Methodology
Core Components
Vulnerability Summary
VULNERABILITY LEVEL PENDING DECLINED ACKNOWLEDGED PARTIALLY RESOLVED MITIGATED RESOLVED TOTAL
Critical 0 0 0 0 0 1 1
Major 0 0 0 0 0 2 2
Medium 0 0 0 0 0 1 1
Minor 0 0 0 0 0 1 1
Optimization 0 0 0 0 0 0 0
Informational 0 0 0 0 0 0 0
Discussion 0 0 0 0 0 0 0
Review Notes

Overview

The "Kindly" Solidity contract is a versatile Ethereum-based smart contract designed for a Layer 2 scaling solution. Leveraging OpenZeppelin libraries, it incorporates ERC-20 token functionality and implements a meta-transaction pattern for executing transactions on behalf of users. The contract defines roles such as DEPOSITOR_ROLE for handling deposits from a ChildChainManager. Access control is enforced using the AccessControl library, and the contract utilizes EIP-712 for structured data hashing.

The constructor initializes the contract, mints initial tokens to a specified address, and sets up the EIP-712 domain. The contract provides functions for depositing and withdrawing tokens, emitting relevant events, and checking membership status based on access control roles. Overall, the Kindly contract demonstrates a modular and secure approach to token management within a Layer 2 scaling environment.

Privileged Roles

In the provided Solidity code, the privileged roles are managed through the OpenZeppelin AccessControl library, specifically using the `DEPOSITOR_ROLE` and `DEFAULT_ADMIN_ROLE`.

DEPOSITOR_ROLE:

  • This role is defined with the constant `DEPOSITOR_ROLE` and is associated with the ChildChainManager address.
  • The modifier `only(DEPOSITOR_ROLE)` is used to restrict access to certain functions, allowing only addresses with the `DEPOSITOR_ROLE` to perform actions such as deposits.

DEFAULT_ADMIN_ROLE:

  • The contract checks if an address is a member with the `DEFAULT_ADMIN_ROLE` using the `isMember` function.
  • This role typically represents the default admin or owner of the contract, having broader privileges than the `DEPOSITOR_ROLE`.
  • It is used in the `hasRole` function from the AccessControl library to determine if an address has administrative rights.

Roles play a crucial role in access control, allowing certain addresses to execute specific functions and restricting access to others. These roles are essential for managing the security and functionality of the smart contract. In this context, `DEPOSITOR_ROLE` is granted to the ChildChainManager, enabling it to deposit tokens, while `DEFAULT_ADMIN_ROLE` is used for more general administrative control.

Audits Scope

ID FILE SHA256 CHECKSUM
KDY kindlyTokenContract.sol 7c4a2f05c2c43066d7682ff4bf9d8a1f56f06bd45347a59fbec661aeb64809f1

KDY-01 | Reentrancy Vulnerability

CATEGORY SEVERITY LOCATIONS STATUS
privilege Critical

  function executeMetaTransaction(
     address userAddress,
 bytes memory functionSignature,
       bytes32 sigR,
       bytes32 sigS,
        uint8 sigV
 ) public payable returns (bytes memory) {
 MetaTransaction memory metaTx = MetaTransaction({
        nonce: nonces[userAddress],
        from: userAddress,
 functionSignature: functionSignature });
       require(
_verify(userAddress, metaTx, sigR, sigS, sigV),
   "Signer and signature do not match" );
     // increase nonce for user (to avoid re-use)
 nonces[userAddress] = nonces[userAddress] + 1;
   emit MetaTransactionExecuted(
       userAddress,
      payable(msg.sender),
      functionSignature
               );
 // Append userAddress and relayer address at the end to extract it from calling context
    (bool success, bytes memory returnData) = address(this).call(
        abi.encodePacked(functionSignature, userAddress)
              );
  require(success, "Function call not successful");
         return returnData;
            }

RESOLVED
Description

Location in code: Inside the `executeMetaTransaction` function
Line number: 253-316
Description:
The `executeMetaTransaction` function does not handle reentrancy properly. Consider using the reentrancyGuard pattern to prevent reentrancy attacks.

KDY-02 | Lack of Input Validation

CATEGORY SEVERITY LOCATIONS STATUS
privilege Major

 function deposit(address user, bytes calldata depositData)
         external
       override
 only(DEPOSITOR_ROLE)
   {
uint256 amount = abi.decode(depositData, (uint256));
      _mint(user, amount);
 emit Deposited(user, amount);
             }

RESOLVED
Description

Location in code: Inside the `deposit` function
Line number: 498-514
Description:
The contract does not validate input parameters in the `deposit` function, which could lead to unexpected behavior or vulnerabilities.

KDY-03 | Potential Reentrancy Vulnerability

CATEGORY SEVERITY LOCATIONS STATUS
privilege Major

 function executeMetaTransaction(
      address userAddress,
  bytes memory functionSignature,
         bytes32 sigR,
         bytes32 sigS,
        uint8 sigV
 ) public payable returns (bytes memory) {
  MetaTransaction memory metaTx = MetaTransaction({
    nonce: nonces[userAddress],
         from: userAddress,
   functionSignature: functionSignature   });
        require(  _verify(userAddress, metaTx, sigR, sigS, sigV),
      "Signer and signature do not match"  );
 // increase nonce for user (to avoid re-use)
     nonces[userAddress] = nonces[userAddress] + 1;
         emit MetaTransactionExecuted(
      userAddress,payable(msg.sender),
        functionSignature );
   // Append userAddress and relayer address at the end to extract it from calling context
       (bool success, bytes memory returnData) = address(this).call(
     abi.encodePacked(functionSignature, userAddress)
              );
 require(success, "Function call not successful");
      return returnData;
           }

RESOLVED
Description

Location in code: Inside the executeMetaTransaction function
Line number: 253-316
Description:
The executeMetaTransaction function allows a user to execute arbitrary external calls after verifying a signature. Reentrancy vulnerabilities may arise if the external call results in state changes before the state is updated in the current contract. Consider implementing reentrancy protection mechanisms.

KDY-04 | Unused Variable id

CATEGORY SEVERITY LOCATIONS STATUS
privilege Minor

function getChainId() public view returns (uint256) {

        uint256 id;

        assembly {

            id := chainid()

        }

        return id;

    }

RESOLVED
Description

Location in code: Inside the getChainId function
Line number: 150-162
Description:
The variable id in the getChainId function is declared but not used. Consider removing unused variables to improve code readability.

KDY-05 | Lack of Input Validation

CATEGORY SEVERITY LOCATIONS STATUS
privilege Medium

 function withdraw(uint256 amount) external {

        _burn(_msgSender(), amount);

    }

RESOLVED
Description

Location in code: Inside the `withdraw` function
Line number: 527-531
Description:
The `withdraw` function does not perform sufficient input validation on the `amount` parameter, which could lead to unexpected behavior or vulnerabilities.