Build an AI-powered NFT generator with TS, GPT, Polygon and CASE (Part 1/2)

Bruno Pérez - Sep 7 '23 - - Dev Community

Let's build an instant NFTs generator! ✨✨

We will create a web app that will let users mint a NFT in one click: creating an AI art from a prompt, storing it on IPFS and mint the unique NFT in Polygon so you can see it on OpenSea. Pretty cool right ?

The source code is available on this Github repo.

Steps summary

Part 1

  • A. Create a dashboard with a "create NFT" form
  • B. Generate AI Art with OPEN AI Image generation
  • C. Store it on IPFS

Part 2

  • A. Create NFT contract
  • B. Deploy it to Polygon
  • C. Mint an NFT on the flow with image and metadata

Part 1

A. Create a dashboard with a "create NFT" form

First let's generate our dashboard with CASE by running this command:

npx create-case-app my-nft-app
Enter fullscreen mode Exit fullscreen mode

This will create a CASE CRUD app where you can add your NFT entity.

CASE login page

Then from the root of the project run:

npm run case:entity nft
Enter fullscreen mode Exit fullscreen mode

This command will create the nft.entity.ts file in the entities folder. You should see in your browser that you have a new NFT link in the sidebar.

You can click on it and you will see that the NFT entity has already one "name" property: a simple text field. Let's add 2 more:

  • The owner of the NFT that will receive the wallet address of the owner
  • The image that will receive the url of the image on IPFS (hidden in the form as it will be automatically filled)
  // entities/nft.entity.ts
  [...]
  @Prop({
    type: PropType.Text,
  })
  owner: string;

  @Prop({
    type: PropType.Link,
    options: {
      isHiddenInCreateEdit: true,
    },
  })
  image: string;
Enter fullscreen mode Exit fullscreen mode

Create NFT form on CASE

And one more thing, we can add a function below the @BeforeInsert() decorator to hook our custom logic right before the moment where the entity gets stored in the database:

@BeforeInsert()
async mintNFT() {
  console.log("Before Insert...");

  // Mint that NFT !
}
Enter fullscreen mode Exit fullscreen mode

B. Generate AI Art with OPEN AI Image generation

It is now the time to call the OPEN AI Image generation API to generate an image based on our prompt: the name field of the NFT entity.

We are going to use axios to make the HTTP requests. Let's install it:

npm i axios
Enter fullscreen mode Exit fullscreen mode

Then you can create the file that will call the API to generate our AI art. Let's call it generate-ai-art.ts You will need to generate your OpenAI API key and store it in the .env file at your project root as OPENAI_API_KEY

// utils/generate-ai-art.ts
import axios from "axios";

export const generateAiArt = async (prompt: string): Promise<string> => {
  const response = await axios.post(
    "https://api.openai.com/v1/images/generations",
    {
      // We pass the prompt as the prompt argument.
      prompt,
      // Smallest size available, let's limit our footprint.
      size: "256x256",
    },
    {
      headers: {
        // Get your own key and at it to the .env file
        Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      },
    }
  );

  // Get the image URL from the API response.
  return response.data.data[0].url;
};
Enter fullscreen mode Exit fullscreen mode

Back to our nft.entity.ts file, we can import the function and trigger it:

import { generateAiArt } from "../utils/generate-ai-art";
[...]

@BeforeInsert()
async mintNFT() {
  console.log("Before Insert...");

  // "This" refers to the NFT entity we are creating.
  this.image = generateAiArt(this.name);
}
Enter fullscreen mode Exit fullscreen mode

If you go back to your CASE dashboard and create a new NFT, you will see that the submission loads a bit and once done, the image field is field with a link to the AI generated image like my "red bear". Cheers !

AI generated image with OpenAi for

C. Store it on IPFS

IPFS is a great way for storing NFT related data: it is decentralized and resilient. If your NFT image url points out to your server, it looses some of its value as you can change it whenever you want.

Pinata homepage

We will use Pinata as a gateway for IPFS. Once again you will need to create an accout to get your secret token for API authentication.

npm i @pinata/sdk
Enter fullscreen mode Exit fullscreen mode

We are going to create a new file called pin-to-IPFS.ts to hold the script.

I recommend to create a storage folder in order to store your temporary files. Indeed we are downloading the file first and the we will pin it to IPFS.

In this script, we are using a basic download function to get the image from the OpenAI servers and the we use the Pinata SDK to upload it to IPFS.

// utils/pin-to-IPFS.ts
const axios = require("axios");
const fs = require("fs");
const pinataSDK = require("@pinata/sdk");

export const pin = async (url: string): Promise<string> => {
  const imagePath: string = await downloadFile(url, "storage/file.png");

  // Init the SDK.
  const pinata = new pinataSDK({ pinataJWTKey: process.env.PINATA_JWT });

  // Read the downloaded file.
  const stream = fs.createReadStream(imagePath);

  // Push it to the IPFS network.
  const pinataRes = await pinata.pinFileToIPFS(stream, {
    pinataMetadata: {
      name: "NFT art",
    },
  });

  // Delete local file.
  fs.unlinkSync(imagePath);

  // We return a link with the hash.
  return "ipfs://" + pinataRes.IpfsHash;
};

// Simple download file function.
const downloadFile = async (url: string, path: string): Promise<string> => {
  const res = await axios.get(url, { responseType: "stream" });

  return new Promise((resolve, reject) => {
    res.data
      .pipe(fs.createWriteStream(path))
      .on("error", reject)
      .once("close", () => resolve(path));
  });
};
Enter fullscreen mode Exit fullscreen mode

And now we just need to connect the dots too have the full experience:

// entities/nft.entity.ts
  [...]
  @BeforeInsert()
  async mintNFT() {
    console.log("Before Insert...");

    const generatedImage: string = await generateAiArt(this.name);
    this.image = await pin(generatedImage);
  }
Enter fullscreen mode Exit fullscreen mode

CASE detail page

Conclusion

That's it for the first part ! Then we will use Hardhat to deploy our Solidity Contract on the Polygon testnet, and then overload our mintNFT() function with the actual minting. If you want to see the Part 2 quickly, give us some love and we will execute ! ❤️❤️❤️

CASE dashboards

And if you thought that CASE is pretty cool to build your CRUD apps (it really is), ⭐ star us on Github to follow the project. The product has so much room to improve, we are really looking to create a great community around it. 🫶

. . . .