Pinata
Published in

Pinata

How To Prevent NFT Trait Sniping In Your PFP Project

With Solidity and Pinata

Getting Set Up

The solution to the problem outlined above is a technical one, so we’re going to reach into our technical toolbox and use:

  • Solidity to write our smart contract
  • OpenZeppelin for pre-built contract libraries
  • Hardhat to test and deploy our contract code
mkdir no-more-trait-sniping && cd no-more-trait-sniping
npm init -y
npx hardhat

Writing The Contract

Open the contracts folder, and you’ll see a Greeter.sol contract. This is the sample that comes with the Hardhat sample project generator. It’s a simple contract that we are going to rewrite entirely. Let’s start that by changing the file name. Let’s call our contract PFP.sol.

pragma solidity ^0.8.0;
npm install @openzeppelin/contracts
  1. A way to update both the status of our indicator and the URI that should be returned for each token.
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract PFP is ERC721Enumerable, Ownable {
using Counters for Counters.Counter;
using Strings for uint256;
constructor(string memory name, string memory symbol) ERC721(name, symbol) {}}
uint256 MAX_TO_MINT = 5000;
uint256 totalMinted;
string public URI;
bool public REVEAL = false;
constructor(string memory name, string memory symbol, string memory initialURI) ERC721(name, symbol) {
URI = initialURI;
}
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
}
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
if (REVEAL) {
return string(abi.encodePacked(URI, tokenId.toString()));
}
return URI;
}
function toggleReveal(string memory updatedURI) public onlyOwner {
REVEAL = !REVEAL;
URI = updatedURI;
}
function setPrice(uint256 price) public onlyOwner {
tokenPrice = price;
}
using Counters for Counters.Counter;
using Strings for uint256;
uint256 totalMinted;
uint256 MAX_TO_MINT = 5000;
string public URI;
bool public REVEAL = false;
uint256 tokenPrice;
constructor(
string memory name,
string memory symbol,
string memory initialURI,
uint256 initialPrice
) ERC721(name, symbol) {
URI = initialURI;
setPrice(initialPrice);
}
function mint(address addr) public payable returns (uint256) {}
function mint(address addr) public payable returns (uint256) {
require(msg.value >= tokenPrice, "insufficient funds");
require(totalMinted < MAX_TO_MINT, "Would exceed max supply");
uint256 tokenId = totalMinted + 1;
totalMinted += 1;
_safeMint(addr, tokenId);

return tokenId;
}

Uploading Metadata and Files

We’ve already established that we want to start with a placeholder image and metadata for our project. For this, we just need to pick an image and upload it through Pinata’s upload tool.

{
"name": "PFP",
"description": "Placeholder for the upcoming PFP drop",
"image": "QmXwECqnS7eEeg8tvwL7w963MhqV5vvQwD5UZGkxZM21E2"
}

Wrapping Up

Trait sniping is a problem with NFT PFP projects, but the same problems can apply to other NFT projects. The ability to update data on a smart contract is core to Solidity, so making use of this functionality in a smart way will help prevent trait sniping.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store