Skip to content

ERC-721 smart contract implementation to persist NFTs that represent items in an inventory

Notifications You must be signed in to change notification settings

p-ssanders/nft-store-smart-contract

Repository files navigation

NFT Store

This repository contains an ERC-721 smart contract implementation called NftStore that can be used to persist NFTs that represent items in an inventory.

The contract is an extension of OpenZeppelin's ERC721 Contracts.

The contract implements simple access control using OpenZeppelin's Access API to ensure that only the contract deployer can mint new tokens. The reason behind that decision is that each instance of this contract could digitally represent the unique items of inventory of some kind of store, and only the store owner can add new inventory. The unique inventory items, represented as NFTs, can then be transferred to buyers. Seems like it could make sense for a diamond dealer where each stone is unique, or a horse breeder, or anyone who sells unique things where pedigree and provenance matter.

Compile

npm install
npm run compile

Test

The NftStore contract is tested using ethers.js and Waffle. The tests are a great place to learn the functionality of the contract.

npm run test

Deploy

This project is built with Hardhat so the deployment of the contract to a local blockchain can be performed using Hardhat and the included deployment script via npm. This assumes you have a blockchain that can support Solidity 0.7.4 and above. For this project I used Ganache running the Muir Glacier hardfork on port 8545.

npm run deploy-local

NftStore deployed to: 0x0...

Interact

You can interact with the deployed contract instance by using the Hardhat console to connect to the local Ganache blockchain:

npx hardhat console --network localhost

And then you can mint a new NFT using the deployed contract:

const contractAddress = '<contract address from deployment>';
const NftStore = require('./artifacts/contracts/NftStore.sol/NftStore.json');
const provider = ethers.providers.getDefaultProvider('http://127.0.0.1:8545');

const contract = new ethers.Contract(contractAddress, NftStore.abi, provider.getSigner(0));

const receipt = await contract.mint('some-token-uri');
const [event] = await contract.queryFilter('Mint');

ethers.BigNumber.from(event.args.tokenId).toString(); # 1
event.args.tokenUri # some-token-uri

Thoughts

In this example, an "NFT" is just a unique identifer (it's literally a number) mapped to an Etheruem address. The address to which the number is mapped is considered the "owner" of the token identified by that number. This mapping is persisted to the blockchain as contract state each time the contract is updated, so a token will always be associated to a specific address until it's transferred to a different address, or "burned" (deleted entirely).

But the token must be more than just an identifier, right?

Not really.

This particular contract implements the "ERC-721 metadata extension" in a way that requires a minter (creator) to map an NFT to a URI. The URI is supposed to refer to a JSON document that contains three properties: name, description, and image (per the "ERC721 Metadata JSON Schema"). So you can create an NFT, which is really just an identifier, and associate some metadata to add some details to it.

Interestingly, only the URI of the metadata is persisted on the blockchain as contract state. You could update the contract implementation to persist the JSON itself on the blockchain as contract state, but since transaction cost is a function of data size I guess the authors of ERC-721 figured no one would put JSON data on a blockchain? So the specification calls for a URI that points to JSON.

But where do you store the JSON?

In the spirit of decentralization the only "correct" answer is to put the JSON on IPFS. But that isn't as easy as putting the JSON as static content on some HTTP server, and it probably costs more. So to me, the weirdest part of all of this is that when you buy an NFT, you're buying an identifier -- there's nothing manditorily enforcing that the metadata is immutable, unless the metadata is on IPFS, and even then nothing is enforcing it stays there. So one day you could have a digital pair of shoes, and the next you could have a PNG, and the next you could have a 404 Not Found. The one thing you'll always have is the identifier.

In classic finance terminology, this technology facilitates trades, but not settlement. I'm not sure how you settle an NFT trade. That is, as a buyer, how do you truly take ownership of the asset represented by the NFT that you bought? And of course there's no centralization of token identifiers (like CUSIPs), so the identifier of your token is only valid to the contract that minted it.

Stack

About

ERC-721 smart contract implementation to persist NFTs that represent items in an inventory

Topics

Resources

Stars

Watchers

Forks