# EVM NFT Creation

## Introduction

This page provides a step-by-step guide for deploying GhostMarket compatible NFTs on supported EVM compatible blockchains (Avalanche, BSC, Polygon, Ethereum, Base).

The NFT creation process requires interaction with the blockchain. For retrieving existing NFT metadata, see [Accessing NFT](/developer-guides/accessing-nft-data.md) data, which uses the GhostMarket APIs.

The snippets used in this guide are javascript and the contract addresses used are for the BSC contracts. For other EVM chains, please adjust using the appropriate contract addresses specified in the [Contracts](/smart-contracts/nft-smart-contracts.md) page.

The code snippets also assume a [node.js](https://nodejs.org) environment.

See our [Metadata Reference](/developer-guides/metadata-specification.md) for full details and requirements of GhostMarket NFT metadata.

## NFT Creation

The NFT creation process on all blockchains broadly consists of the following steps.

1. Prepare Off-Chain Media and persist it
2. Assemble On/Off-Chain Metadata
3. Persist Off-Chain metadata
4. Build a transaction
5. Sign the transaction
6. Broadcast to the blockchain

### 1. Prepare your Off-Chain Media

The preferred decentralized platform for persisting image/media data for your NFT is [IPFS](https://ipfs.io/).

This guide will use Piñata to upload and pin your images to IPFS.

Set up a Piñata account and get started [here](https://www.pinata.cloud/).

You can either manually upload you metadata, noting the IPFS hash, or dive into the [Pinata js-sdk](https://docs.pinata.cloud/pinata-node-sdk) to interact with Piñata programatically.

> Be sure to note the IPFS hash of the uploaded metadata, you will need it in the following step.

### Prepare the Web3 javascript environment

**Install the web3 javascript module**

```bash
% npm install web3
```

**Import the Web3 module and set up Web3**

This sample assumes node.js, for browser and metamask development, adapt the provider and wallet connection appropriately.

```javascript
const Web3 = require("web3");

// connect the sample provider - set your own provider appropriately
const web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");

// Create a sample PK & account
const privateKey =
  "0x32af9b58580fa3acc3639d280f4acaf3575970114025fe8fd9fa9a5efd24d086";
account = web3.eth.accounts.privateKeyToAccount(privateKey);

// "ghost" NFT contracts for mainnet BSC
const erc721ContractAdd = "0xf41db445d7eaf45536985ce185ce131fa4b42e68";
const erc1155ContractAdd = "0x44c5ce28c29934b71a2a0447745d551dfc7b5133";
```

Download and save the contact ABI using the relevant explorer (e.g. [BSC ERC721](https://api.bscscan.com/api?module=contract\&action=getabi\&address=0xf41db445d7eaf45536985ce185ce131fa4b42e68)) and store it locally. Node.js allows us to "require" it directly into the ABI objects.

> After downloading the contract ABI, be sure to edit the file so it contains only the value of the "result" object
>
> e.g. \[{ "anonymous" true ... }]

```javascript
const erc721ABI = require("./ERC721Abi.json");
const erc1155ABI = require("./ERC1155Abi.json");
```

### 2. Assemble On/Off-Chain Metadata

GhostMarket supports both ERC721 and ERC1155 NFT token standards on EVM platforms. The metadata setup is identical, however the contract parameters differ. We highlight these differences below.

GhostMarket EVM NFT contracts provide the option to store metadata on, or off chain as a JSON string. However, because of the high cost of storing Metadata on-chain, we will be storing the metadata off-chain.

Build the metadata:

```javascript
const attributes = [
  {
    trait_type: "color", // The attribute type/key
    value: "red", // The attribute value
    display_type: "", // The display format
  },
  {
    trait_type: "size", // The attribute type/key
    value: "small", // The attribute value
    display_type: "", // The display format
  },
  // No third attribute
  // An arbitrary number of attributes may be added for custom apps
];

const properties = {
  has_locked: true, // Is there locked content?
  type: 1, // Taken from the predefined genres
};

// Put it together
const jsonMetadata = JSON.stringify({
  name: "My Shiny NFT",
  description: "This NFT will be a classic",
  image: "ipfs://QmTy8w65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStJb", //URI from Media step
  tokenURI: "",
  attributes,
  properties,
});
```

### 3. Persist metadata to IPFS

Since this example uses Off-chain persistence of the NFT metadata. Upload the metadata to IPFS using the same process as persisting the media and again.&#x20;

Be sure to note the IPFS hash, we will use this below.

### 4. Build the transaction

The *royalties* and *lockedContent* are parameters to the contract method, so these are prepared first.

This code snippet constructs the contract & method objects for both the ERC721 & ERC1155 contracts. Use the contract which corresponds to your use case.

```javascript
// construct the royalties ARRAY.
const royaltyBPS = 2000; // royalties - 20% expressed in Basis Points (BPS) (0.01%)

// Royalties is an array of 2 element arrays in [address, percentage] format
const royalties = [
  [account.address, royaltyBPS],
  // Additional royalty address/value pairs may be added here
];

// The locked content - ToDo - Describe fully
// Node.js only - add module "npm i buffer" for browsers
const lockedContent = Buffer.from("My secret Locked Content", "utf8").toString(
  "hex"
);

// Create the contract object
const erc721contract = new web3.eth.Contract(erc721ABI, erc721ContractAdd);
const erc1155contract = new web3.eth.Contract(erc1155ABI, erc1155ContractAdd);

const methodData1155 = erc1155contract.methods.mintGhost(
  account.address,
  1,
  [],
  royalties,
  externalURI,
  "",
  lockedContent
);

const methodData721 = erc721contract.methods.mintGhost(
  account.address,
  royalties,
  externalURI,
  "",
  lockedContent
);
```

#### 5. Sign the Transaction

Not needed here. In this sample, signing is done as part of the Web3 Broadcast.

#### 6. Broadcast the Transaction to the network

```javascript
// Broadcast the Txn - Identical call for both ERC-721 and ERC1155 contract types
methodData721.send({ from: account.address, value: 0 });
```

That's it, you've created and persisted a GhostMarket compatible NFT.

> For clarity, all of the above snippets exclude error and promise handling for clarity. Production code should include thorough exception management and handle promises appropriately.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ghostmarket.io/developer-guides/minting-tokens/evm-nft-creation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
