NFT Smart Contract Audit: Project NANOPASS

Xyz Zyx
2 min readDec 16, 2021

Disclaimer: The information presented in this post is for general discussion purposes only and is not intended to provide legal security guarantees to any individual or entity. Me, my affiliates, partners, subcontractors or employees, or anyone involved in the auditing process cannot, under any circumstances, be held responsible for any loss of funds in any currency, cryptocurrency or government-backed, resulting or connected to the above-mentioned smart contacts not behaving as expected.

The smart contract is located at:

Low/Discussion Items

  1. Solidity version could be 0.8.10
  2. import “./Ownable.sol”; could be replaced with opezeppelinOwnable
  3. import “./@rarible/royalties/contracts/LibRoyaltiesV2.sol”; doesn’t work on opensea, a bit useless
  4. All this

address public constant DEV_ADDRESS_1 = 0xc73ab340a7d523EC7b1b71fE3d3494F94283b4B1; // J

address public constant DEV_ADDRESS_2 = 0x09b2741feD63B7Fcc5E577cDc9d0cf0D85127A32; // P

address public constant DEV_ADDRESS_3 = 0xC929b6bD739a8564BcB35e978Afe4ffF5b6c3cEF; // R

address public constant DEV_ADDRESS_4 = 0x5bF9BE0B32ba09B449eD5d1EaBB864Cf2317629F; // D

address public constant DEV_ADDRESS_5 = 0x3e8f4639E926f36f7309836F6D018a9ea59B345e; // H

address public constant DEV_ADDRESS_6 = 0x5bB1396E3EC5E31E12DC7846c4c94eea25083f35; // L

address public constant DEV_FUND_ADDRESS = 0x7955cF321a420f7c245cEfB748E0F303756Ae2A6;

are used here

uint256 balance = address(this).balance;

require(balance > 0, “Balance is 0”);

payable(DEV_FUND_ADDRESS).transfer(balance * 5000 / 10000);

payable(DEV_ADDRESS_1).transfer(balance * 1666 / 10000);

payable(DEV_ADDRESS_2).transfer(balance * 1229 / 10000);

payable(DEV_ADDRESS_3).transfer(balance * 805 / 10000);

payable(DEV_ADDRESS_4).transfer(balance * 900 / 10000);

payable(DEV_ADDRESS_5).transfer(balance * 200 / 10000);


could be replaced with PaymentSplitter for a MUCH clearer view of where the money flows

5. require(numberOfMints * price <= msg.value, “Amount of ether is not enough”);

I would replace ≤ with ==. so I don’t have to handle refunds after

6. function airdrop(address[] calldata addresses) external onlyOwner {

should include a check to not allow more than 50 addresses or risk going out of gas

7. missing contractURI method

8. They seem to limit from the dapp the gas limit to 102,543 (a usual mint is 80,653). I would let the wallet calculate so I don’t scare users with too much $$$ showed there.


not a bug but a very wrong way to do the whitelist

function editPresale(address[] calldata presaleAddresses, uint256[] calldata amount) external onlyOwner {

for(uint256 i; i < presaleAddresses.length; i++){

presaleWhitelist[presaleAddresses[i]] = amount[i];


The standard nowadays is to use Merkle Trees for whitelist (or the backend signer). They choose to do the whitelist in the blockchain and spend useless ETH in the process.
From the behalf of the miners we thank you!

Want more smart contract audits? Subscribe and like this post
I’ll try to post very often this.

Do you want an audit for your smart contract ? Check