# Neo N3 NFT Creation

## Introduction

This page provides step-by-step instructions for deploying GhostMarket compatible NFTs on the Neo N3 blockchain. The Neo 2 legacy blockchain is **not** supported.

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

The snippets used in this guide are javascript.

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

See our [Metadata Reference](https://docs.ghostmarket.io/developer-guides/metadata-specification) 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 Metadata and persist it.
2. Assemble On-Chain Metadata and build a transaction.
3. Sign the transaction.
4. Broadcast to the blockchain.

### 1. Prepare Off-Chain Metadata

The preferred decentralised 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.

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

### 2. Assemble On-Chain Metadata

GhostMarket Neo N3 NFT contract stores all metadata on-chain as a JSON string.

We will use the [neon-js npm](https://dojo.coz.io/neo3/neon-js) module to work with the Neo N3 blockchain. The full neon-js docs can be found [here](https://dojo.coz.io/neo3/neon-js/docs)

#### Install the needed modules

```bash
% npm install @cityofzion/neon-js@next
% npm install unicode-encode
```

#### Import the library

```javascript
const { utoa, atou } = require("unicode-encode");
const { CONST, rpc, sc, wallet, tx, u } = require("@cityofzion/neon-core");
```

#### Assemble the Minting Parameter JS Object

GhostMarket Neo N3 NFT contract allows you to serialize the NFT's metadata object to JSON and store On-Chain. Let's create the Metadata.

In this example, for clarity, we will create the *attribute* and *properties* objects separately:

> Royalties and locked contend do not form part of the MetaData, they are specified later in the contract parameters.

#### Example using On-Chain Metadata

```javascript
// Define the NFT attributes
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
];

// Define the NFT properties
const properties = {
  has_locked: true, // Is there locked content
  type: 1,
};

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

#### Build the transaction script

Define account and RPC Network objects needed later

```javascript
const creatorPrivateKey =
  "L1QqQJnpBwbsPGAuutuzPTac8piqvbR1HRjrY5qHup48TBCBFe4g";
const creatorAccount = new wallet.Account(creatorPrivateKey);

const rpcClient = new rpc.RPCClient("http://neo3.edgeofneo.com:10332");

// 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"
);
```

Define the royalties that the creator wishes to receive from each sale of the NFT.

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

const contractRoyalties = JSON.stringify([
  {
    address: creatorAccount.address,
    value: royaltyBPS.toString(),
  },
  // Additional royalty address/value pairs may be added here
]);
```

```javascript
// Define the minting arguments to be used in the transaction script
const mintArgs = [
  sc.ContractParam.hash160(creatorAccount.address),
  sc.ContractParam.byteArray(utoa(jsonMetadata)),
  sc.ContractParam.byteArray(utoa(lockedContent)),
  sc.ContractParam.byteArray(btoa(contractRoyalties.toString())),
  sc.ContractParam.string(""), //Data - empty
];

// Build the script - ready for transaction
const script = sc.createScript({
  scriptHash: "577a51f7d39162c9de1db12a6b319c848e4c54e5", // GhostMarket NFT contract
  operation: "mint",
  args: mintArgs,
});

let currentHeight = 0;
// Retrieve the current block height to calculate expiry
currentHeight = await rpcClient.getBlockCount();

// specify the contracts that this transaction may interract with
const allowedContracts = [
  "d2a4cff31913016155e38e474a2c06d08be276cf", // GAS script hash
  "577a51f7d39162c9de1db12a6b319c848e4c54e5", // GhostMarket NFT Contract
];
```

### 3. Construct & Sign the transaction

```javascript
// Build the transaction
txn = new tx.Transaction({
  sender: creatorAccount.scriptHash,
  signers: [
    {
      account: creatorAccount.scriptHash,
      scopes: tx.WitnessScope.CustomContracts,
      allowedContracts,
    },
  ],
  validUntilBlock: currentHeight + 1000000,
  systemFee: 0,
  script: script,
});

// Sign the transaction
const signedTransaction = txn.sign(
  creatorAccount,
  CONST.MAGIC_NUMBER["MainNet"]
);
```

### 4. Finally, broadcast to the network

```javascript
const result = await rpcClient.sendRawTransaction(
  signedTransaction.serialize(true)
);

console.log("Txn ID: ", result);
```

Congratulations, you have successfully deployed a GhostMarket compatible NFT to GhostMarket contract on Neo N3 blockchain.

> 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.
