Sharing the experience of creating an NFT collection using Python

22 July 2024

next article
Serhiy Tadlya

Backend Developer

Serhiy Tadlya
Sharing the experience of creating an NFT collection using Python

About the author: Serhiy, an experienced backend developer of Avivi, speaks many programming languages and participates in complex projects in PHP and Python. The latest programming language is quite suitable for creating and publishing your own collections, and now the author will explain everything to you.

In today's world of digital technology and blockchain development, non-fungible tokens (NFTs) have become one of the hottest topics in art, collectibles, and even the gaming industry. NFTs enable artists and content creators to present their work as unique digital assets that can be bought, sold and exchanged on decentralized platforms. Also, the collections created a real boom among people who are far from creativity, but who understand trading and auctions and earn money purely from the resale of virtual values.

In this article, I will describe the process of creating your own NFT collection using the Python programming language and creating metadata, for the possibility of uploading the collection to the blockchain. Thanks to Python, this process becomes accessible even to those without much experience in programming or blockchain technology.

General training

The raw material for generating any NFT collection is an image. First of all, you should prepare layers of interchangeable characteristics of your character (or the main object) in the form of PNG files. With their help, in the future, it will be possible to generate and combine these layers into the final version of the NFT in the form of a full-fledged image. For example, I use a basic body, several hats, various mustaches, and objects "held" by my character.

unnamed (2).png

PNG files corresponding to the image layer.

Collections of NFTs usually have a common base—a body or shape that usually includes color. In the future, various characteristics are added: from appendages and accessories to weapons, clothes, jewelry, etc. In my case, the layer will consist of a body, which is the same for all NFTs, a hat, a mustache, and one of the accessories. Each layer is represented as a folder, each of which contains a number of characteristics in PNG format.

As for the item selection process, this will be done using a random selection generator that will return the path to the PNG file from the current root and its name. If, for example, a hat is selected, the path will look like this:

static/nft_options/items/hats/cap.png and its name is “cap”.

1
2
3
4
5
6
7
8
@staticmethod
def get_item(folder_name: str) -> list or None:
   """ Getting a random item from a folder """

   item_list = os.listdir(f"static/nft_options/items/{folder_name}")
   if len(item_list) > 0:
       random_choice = random.choice(item_list)
       return [random_choice, random_choice.split(".")[0]]

A function to select an arbitrary object from a layer, which is passed as a parameter.

NFT generation using random selection

The generation will start from the base layer, i.e. the body. This is the base to which everything else will be added as progressive layers. Since the body file is one, the generator is not used here. After obtaining the base layer, with the help of a generator, the background is superimposed and the color of the body changes. Additionally, an identification number is generated that will be unique for each NFT. This number will also be layered.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Image background
color_dict = random.choice(self.RGB_BASE)
background_color_name = next(iter(color_dict.keys()))
background = next(iter(color_dict.values()))

# Create an image object in memory
img = Image.new("RGB", (self.WIDTH, self.HEIGHT), background)
original_slime = Image.open("static/nft_options/original.png").convert("RGBA")

# Generate the text and edit the slime
overlay = self.generate_text()
self.change_color(original_slime)

Background generation, color change and identification number assignment.

Then, after generating the background and assigning an identification number, layers are applied in the form of a hat, mustache and accessory. All accessory images must have a transparent PNG background so they can be easily overlaid on the base image. The functionality of overlapping items is the same. After selecting an item from each background, they will be recolored like the base body.

1
2
3
4
5
6
7
def add_mustache(self) -> list:
   """ If successful, returns a moustache object
   Also in the 2nd argument is name of the item """

   item = self.get_item("mustache")
   if item is not None:
       return [Image.open(f"static/nft_options/items/mustache/{item[0]}").convert("RGBA"), item[1]]

Mustache generation function for the character.  

After all the layers are applied one by one, the finished image for NFT is stored in the root of the project as a PNG file and given a name corresponding to its identification number.

unnamed (1).png

Generated by NFT

Generation of NFT collection

Well, the image is there, but technically it's not yet a collection. NFT collection generation is used to create a large number of unique digital assets, which can be collectibles, art objects, or virtual items in games. Each NFT in the collection has unique characteristics and an identifier that confirms its authenticity and uniqueness on the blockchain.

Generating a collection of NFTs is opposed to generating individual NFTs because of several key advantages:

  • First, collections allow you to create a greater variety of digital assets, which attracts the attention of collectors and increases their interest in the project.

  • Second, the automated generation of many NFTs using templates and accessories helps save time and resources compared to manually creating each NFT.

  • A third advantage is scalability, where collection generation allows large numbers of NFTs to be created quickly, which is especially beneficial for mass projects or games.

  • Further, having a large collection can help increase potential income, as the ability to sell many unique items increases the earning potential.

  • Finally, collections can be used in gamification, where users collect and exchange NFTs, driving engagement and interaction with the project.

Therefore, generating a collection of NFTs is an effective approach to create interesting and attractive digital assets that can attract a larger audience and increase the value of the project.

Below is the functionality to generate a whole collection of NFTs. To create collections, on the page in the form, you need to enter the name of the collection and the amount of NFT to generate. As a result, the NFT will be formed and the metadata will be created, which is necessary for the subsequent publication of the collection on the exchange.

unnamed.png

NFT collection generation form

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def generate_collection(self, request, context):
   """ Generate collection """

   context = {}
   if request.method == "POST":
       form = ImageForm(request.POST or None)
       if form.is_valid():
           # save collection data
           collection = form.save()
           quantity = form.cleaned_data.get("quantity")
           threads = []
           # generate nft
           for i in range(quantity):
               thread = threading.Thread(name="thread", target=self.create_image, args=[collection, i])
               thread.daemon = True
               thread.start()
               threads.append(thread)

           # Waiting for all threads to complete
           for thread in threads:
               thread.join()

           # set json
           set_metadata = Metadata()
           set_metadata.set_nft_metadata(collection)

           context.update({
               "collection": collection
           })
   return context

Functional generation of NFT collection


Metadata for NFT collection

Assigning metadata to a collection of NFTs, or even a single NFT, is necessary because NFT data is typically not stored on-chain—it's too expensive. To solve this problem, a link is provided for each NFT pointing to a specific repository. This repository contains metadata in the form of a .json file . In addition, this metadata also contains a link to the NFT itself in the form of a PNG file.

Essentially, the JSON file tells each marketplace everything it needs to know about the NFT: the characteristic, the uniqueness, including where to find the image. This approach complies with the ERC 721 standard, which allows developers and marketplaces to agree in advance on the required functionality. For example, about the transfer of property rights.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
   "name": "nft#383435",
   "description": "Nft nft#383435 belongs to the \"nft\" collection\n-> Items\nMoustache image name: type one\nHat image name: cap\nSubject image name: sword\n\n-> Colors\nBackground: Turkish pink",
   "image": "ipfs://QmXAQBJBQ9rMAQUBeCWAyLqx6xJWyRRJqZfqdLyMb8DjYh/nft#383435.png",
   "attributes": [
       [
           {
               "trait_type": "Background",
               "value": "Turkish pink"
           },
           {
               "trait_type": "Moustache",
               "value": "type one"
           },
           {
               "trait_type": "Hat",
               "value": "cap"
           },
           {
               "trait_type": "Subject",
               "value": "sword"
           }
       ]
   ]
}

An example of the metadata of one NFT

After generating the metadata based on the NFT characteristics, it is necessary to programmatically upload the json files and the NFT images themselves to IPFS immediately after creation. IPFS, or Interplanetary File System, is a decentralized network that provides an immutable, decentralized way to store our data.

Due to the vulnerability to data loss when metadata is stored on a centralized server or in a single database, in a decentralized system, NFT metadata will be distributed across different network nodes. This ensures loss resistance and data security. In addition, images and other important files can be stored in a distributed file storage system, which guarantees their availability even if some nodes fail.

In this case, the NFT collection will be uploaded to the Pinata system, which is an IPFS provider in the Web3 space. With the help of the Pinata API, the collection will be uploaded to the IPFS system for the possibility of its further publication on the exchange.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def set_nft_metadata(self, collection: Collection) -> None or dict:
   """ Set metadata for nft collection and save it in json"""

   # pin in pinata images from collection
   img_ipfs_hash = self.pin_folder(collection.folder_name, 'img')
   if img_ipfs_hash['status'] == 200:
       json_files = []
       for image in collection.images.all():
           # create json with metadata
           base_json = {
               "name": image.name,
               "description": image.description,
               "image": f"ipfs://{img_ipfs_hash['hash']}/{image.name}.png",
               "attributes": [image.attributes]
           }
           # save json
           json_filename = f"{image.name}.json"
           json_file_path = os.path.join(BASE_DIR, json_filename)
           with open(json_file_path, "w") as json_file:
               json.dump(base_json, json_file, indent=4)
           if os.path.exists(json_file_path):
               image.json.save(json_filename, File(open(json_filename, 'rb')), save=True)
               image.save()
               os.remove(json_filename)

       # pin in pinata json files from collection
       json_ipfs_hash = self.pin_folder(collection.folder_name, 'json')
       if json_ipfs_hash['status'] == 200:
           collection.pinata_img_hash = img_ipfs_hash['hash']
           collection.pinata_json_hash = json_ipfs_hash['hash']
           collection.save()

       else:
           return {'error': json_ipfs_hash['error']}
   else:
       return {'error': img_ipfs_hash['error']}

Generation of NFT metadata in json format and subsequent loading into Pinata

Results

Generating NFT collections, creating metadata for them, and storing them in the IPFS system using Python is a powerful and flexible approach to creating and managing digital assets. This process allows for the creation of unique collections of NFTs with various characteristics and properties, and ensures safe and efficient storage of data in a decentralized network.

Using Python, you can automate the process of generating NFT collections, using templates and accessories to create a variety of digital assets. Once created, Python can be used to generate and format metadata for each NFT, including name, description, authorship, and other important characteristics.

Also, with the availability of the IPFS API, it is possible to store this data in a distributed file storage system. IPFS provides resilience to data loss and security by distributing copies of files across different network nodes.

Therefore, using Python to generate collections of NFTs, create metadata for them, and store them in the IPFS system provides the ability to quickly and efficiently create, manage, and provide access to NFTs in a decentralized environment. In addition, through the use of smart contracts on the blockchain, the generated NFTs can be automatically published on an exchange for sale, where they can be purchased by interested participants.

I hope you enjoyed the article and the clear technical explanation. Remember: if you don't want to deal with it yourself, Avivi will always help you create and publish your own NFT collection.

Baner articles 2023.png



Similar articles
Apply for a consultation

We will reach out to within 10 minutes