What do you need? | Mint Smart Contract.

By akohad Feb3,2023

[ad_1]

Security, libraries, functions, attributes. Let’s create a basic mint contract in solidity step by step.

Hello strangers, a quick reminder to start, NFTs are just some pieces of code running on a blockchain node somewhere. Smart Contracts are also a piece of code it runs entirely on the Ethereum Virtual Machine (EVM). In this article, I would like to explain what these pieces of code look like about by going a little deeper.

Before continuing, I will talk about the basic requirements that I have come to a consensus on as a result of my own research. I do not claim that this is the most perfect smart contract or that this is the best I will tell.

Firstly, we need to make our structure. What we want from our smart contract. Let’s describe the needs. We want to create a safe secure uncomplicated mint contract right?

I will follow these steps:

  1. Explain the libraries to import step by step
  2. Declaring attributes, modifiers, and events of smart contract
  3. Creating our methods
  4. Deploying our smart contract

Libraries to import

Every developer needs a helper. There is no point to write the same codes again and again. That’s why we will use some very beneficial OpenZeppelin libraries for our project.

ERC721

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

A non-fungible token is one that cannot be divided or immediately swapped for another ERC-721 token, and the Ethereum Request for Comments (ERC) 721 data standard is used to create these non-fungible tokens.

The ERC721 library provides a basic implementation of the ERC721 standard, including the required methods for creating, owning, and transferring NFTs, as well as the optional methods for querying metadata about NFTs. This library can be used to deploy a basic NFT contract. I recommend reading these documents for full understanding.

https://eips.ethereum.org/EIPS/eip-721

https://docs.openzeppelin.com/contracts/2.x/api/token/erc721#ERC721

The main methods of ERC721

ERC721IEnumarable

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

The ERC721Enumerable library extends the functionality provided by the ERC721 library by implementing the IEnumerableERC721 interface, which allows for efficient access and manipulation of a collection of NFTs.

It includes methods for querying the properties of an NFT collection, such as the number of tokens in the collection, the owner of a particular token, and the metadata associated with a token. It also includes methods for modifying the collection, such as adding and removing tokens.

The additional IERC721Enumarable methods

Counters (Utils)

import "@openzeppelin/[email protected]/utils/Counters.sol";

The Counter utility in the OpenZeppelin library is a simple smart contract that allows you to keep track of a count of events. It provides a basic implementation of a counter, with methods for incrementing, decrementing, and getting the current count. The Counter utility can be used in a variety of situations, such as counting the number of times an event occurs, tracking the number of tokens in a contract, or keeping a tally of some other type of value.

The Counter utility is designed to be easy to use and implement and provides a secure and reliable implementation of a counter. It also includes protection against overflow and underflow errors, ensuring that the count never goes below zero or exceeds the maximum value that can be stored in a uint256 variable.

ERC721URIStorage

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

The ERC721URIStorage library from OpenZeppelin is an implementation of a smart contract for storing URIs (Uniform Resource Identifiers) for ERC-721 non-fungible tokens (NFTs) in the Ethereum blockchain.

This library follows the ERC-721 standard for NFTs and implements the basic functions for setting and getting the URI for a specific NFT token ID.

The ERC721URIStorage library from OpenZeppelin is capable of the following:

  • Storing URIs (Uniform Resource Identifiers) for ERC-721 non-fungible tokens (NFTs) in the Ethereum blockchain.
  • Implementing the basic functions for setting and retrieving the URI for a specific NFT token ID.
  • Providing a centralized storage solution for NFT URIs, allowing NFT owners, developers, and users to easily retrieve information about NFTs.
The additional ERC721URIStorage methods

Ownable (Access)

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

The Ownable library from OpenZeppelin is a smart contract implementation for ownership management in the Ethereum blockchain. It provides a basic set of functions for controlling the ownership of a contract, including functions for transferring ownership, checking the current owner of a contract, and setting an operator (an address authorized to perform actions on behalf of the current owner).

The Ownable library from OpenZeppelin is a smart contract implementation for ownership management in the Ethereum blockchain. It provides a basic set of functions for controlling the ownership of a contract, including functions for transferring ownership, checking the current owner of a contract, and setting an operator (an address authorized to perform actions on behalf of the current owner).

The Ownable library from OpenZeppelin is capable of the following:

  • Implementing the basic functionality for ownership management in the Ethereum blockchain.
  • Providing functions for transferring ownership, checking the current owner of a contract, and setting an operator (an address authorized to perform actions on behalf of the current owner).
  • Following best practices for security and code reuse in the Ethereum ecosystem.
The only modifier of Ownable
The main methods of Ownable

ReentrancyGuard (Security)

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

First of all, I would like to give brief information about the reentrancy attack. A reentrancy attack is a type of attack in blockchain technology that takes advantage of the ability of a contract to call other contracts. An attacker can repeatedly call a vulnerable contract and execute its functions multiple times before the first call has finished, draining its resources or altering its state.

In a reentrancy attack, the attacker calls the vulnerable contract, which in turn calls another contract controlled by the attacker. The second contract can then call the first contract again, causing an endless loop of calls and resource consumption. This can result in the depletion of the vulnerable contract’s funds or a change in its state, potentially leading to significant financial or operational damage.

Reentrancy attacks can be prevented by using techniques such as mutex locks, state variables, or the ReentrancyGuard library from OpenZeppelin.

ReentrancyGuard is a smart contract security tool in the OpenZeppelin library that helps protect against reentrancy attacks. It works by allowing only a single call to the contract at a time, ensuring that any recursive call is blocked until the first call has finished executing. This helps prevent attackers from repeatedly calling the contract and draining its resources.

The ReentrancyGuard library from OpenZeppelin is capable of:

  • Protecting against reentrancy attacks: It ensures that a contract is not vulnerable to attackers who can repeatedly call it and drain its resources.
  • Allowing only one call at a time: It allows only a single call to the contract at a time, blocking any recursive calls until the first call has finished executing.
  • Providing a simple and effective solution: ReentrancyGuard offers a simple solution to a complex security issue, making it easier for developers to implement secure smart contracts.
  • Integrating with other OpenZeppelin tools: It can be combined with other security tools from OpenZeppelin to provide a comprehensive security solution.

Attributes & Constants

Okay, the theoric parts are almost done. You need to code it to fully understand what you are doing. I’ll explain the attributes of our mint contract. Then we go coding after all the parts are done.

  bool public publicSaleActive;

The boolean we will use to control the public sale.

address public operator;

The operator is the authorized address that comes after the owner. Some methods need authentication and only the operators can call them. In big projects, there could be several operators.

uint256 public publicSaleStartTime;

We will set a public start time when we start the minting process. It may be beneficial to log the public sale start time in a smart contract in Solidity. By logging the start time, we can ensure that the start time is recorded on the blockchain and cannot be altered.

mapping(address => uint256) public mintedPerAddress;

We have a mapping data structure for keeping track of which address has how many nfts. We will use it to prevent wallets from minting more than the number of nft mints we set during the mint.

uint256 constant public MAX_MINT_PER_ADDRESS;

This is a constant for setting the maximum number of nfts per address during mint.

uint256 constant public MAX_NFTS;

This is a constant for setting the maximum number of NFTs.

In Solidity, a “constant” variable is a state variable that has a fixed value and cannot be modified once it has been set. The “constant” keyword is used to declare a constant variable.

Modifiers

In Solidity, a “modifier” is a way to modify the behavior of a function or to add additional conditions that must be met before the function can be executed. Modifiers are defined as separate functions with the “modifier” keyword, and they can be applied to other functions.

We will have the following modifiers:

 modifier whenPublicSaleActive() {
require(publicSaleActive, "Public sale is not active");
_;
}

Some methods need to be executed when public sales are open.

 modifier onlyOperator() {
require(operator == msg.sender, "Only operator can call this method");
_;
}

And some methods should be called only by the operator.

Events

event NFTPublicSaleStart(
uint256 indexed _saleDuration,
uint256 indexed _saleStartTime
);

It will be beneficial to log the public sale start time in a smart contract in Solidity. By logging the start time, you can ensure that the start time is recorded on the blockchain and cannot be altered. We should declare an event to log the public sale start time in our smart contract.

event NFTPublicSaleStop(
uint256 indexed _currentPrice,
uint256 indexed _timeElapsed
);

When we stop the public sale it should also be logged in the blockchain for transparency and accountability to users and to ensure that the conditions of the public sale are followed as intended.

Methods

A mint smart contract in the Ethereum blockchain using OpenZeppelin libraries could have the following methods:

  1. constructor: A method that is called only when deploying our contract to the blockchain.
  2. mint: The mint method allows new tokens to be minted and added to the total supply. Sets the token URI and sends it to the minter address.
  3. setOperator: After deploying the smart contract owner executes this method and sets the operator for operating the smart contract. Usually used by large developer teams.
  4. startPublicSale: This method starts the public sale and emits a public sale start event. Only executed by the operator.
  5. stopPublicSale: This method stops the public sale and emits a public sale stop event. Only executed by the operator.
  6. withdraw: A method for withdrawing all the ether in the contract to the specific address. Only called by the contract owner.
  7. totalSupply: A method that returns the total token supply that comes from ERC721Enumerable.
  8. balanceOf: A method that returns the number of tokens owned by a specific address that comes from ERC721.
  9. owner: A method that returns the contract owner’s address comes from Ownable.

More methods come from interfaces. When you deploy it you can find it all inside the contract methods.

The Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@openzeppelin/[email protected]/token/ERC721/ERC721.sol";
import "@openzeppelin/[email protected]/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/[email protected]/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/[email protected]/access/Ownable.sol";
import "@openzeppelin/[email protected]/utils/Counters.sol";

contract MyToken is
ERC721,
ERC721Enumerable,
ERC721URIStorage,
Ownable,
ReentrancyGuard
{
using Counters for Counters.Counter;

Counters.Counter private _tokenIdCounter;

//attributes
// Standart for ERC721
bool public publicSaleActive;
address public operator;
uint256 public publicSaleStartTime;
//Mapping for holding which address mint how much nft ?
mapping(address => uint256) public mintedPerAddress;

// constants
uint256 public constant MAX_NFT = 10000;
uint256 public constant MAX_MINT_PER_ADDRESS = 3;
uint256 public constant MINT_PRICE = 0.002 ether;

// modifiers
modifier whenPublicSaleActive() {
require(publicSaleActive, "Public sale is not active");
_;
}

modifier onlyOperator() {
require(operator == msg.sender, "Only operator can call this method");
_;
}

// events
event NFTPublicSaleStart(
uint256 indexed _saleDuration,
uint256 indexed _saleStartTime
);

event NFTPublicSaleStop(
uint256 indexed _currentPrice,
uint256 indexed _timeElapsed
);

function setOperator(address _operator) external onlyOwner {
operator = _operator;
}

function getElapsedSaleTime() private view returns (uint256) {
return
publicSaleStartTime > 0 ? block.timestamp - publicSaleStartTime : 0;
}

function startPublicSale() external onlyOperator {
publicSaleStartTime = block.timestamp;
emit NFTPublicSaleStart(MINT_PRICE, publicSaleStartTime);
publicSaleActive = true;
}

function stopPublicSale() external onlyOperator whenPublicSaleActive {
emit NFTPublicSaleStop(MINT_PRICE, getElapsedSaleTime());
publicSaleActive = false;
}

constructor() ERC721("MyToken", "MTK") {}

function safeMint(address to, string memory uri)
public
payable
whenPublicSaleActive
nonReentrant
{
uint256 tokenId = _tokenIdCounter.current();
uint256 mintIndex = totalSupply();
require(mintIndex < MAX_NFT, " All NFTs are already sold");
require(MINT_PRICE <= msg.value, " Ether value sent is not correct");
//Check for minted addres has more than max mint per address
require(
mintedPerAddress[msg.sender] > MAX_MINT_PER_ADDRESS,
"sender address cannot mint more than maxMintPerAddress"
);
mintedPerAddress[msg.sender] += 1;
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}

// The following functions are overrides required by Solidity.
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId,
uint256 batchSize
) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId, batchSize);
}

function _burn(uint256 tokenId)
internal
override(ERC721, ERC721URIStorage)
{
super._burn(tokenId);
}

function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}

function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}

Deploy

If you are using REMIX IDE set your environment to MetaMask then deploy it.

After deployment, you can call your methods.

Congrats, this is a simple safe mint contract for your project.

Thank you for following along, please feel free to drop your thoughts in the comments below.

w to trading? Try crypto trading bots or copy trading on best crypto exchanges

Follow me on Twitter

Cheers!



[ad_2]

Source link

By akohad

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *