How to create an NFT collection for Ethereum in Golang

This article will not discuss the basics of non-interchangeable tokens (NFT) or their purposes, since there is already a lot of material on this subject. Instead, we will focus on the technical aspects of NFT, since I had to develop my own solutions due to the lack of information on this topic.

By doing a little research, I was able to break down the original “building an NFT collection” project into more specific and smaller tasks that include:

  • Generate 10,000 unique images
  • Generate 10,000 metadata for each image
  • Upload 10,000 images along with metadata to a decentralized repository
  • Create a smart contract for NFT tokens
  • upload the created smart contract to the mainnet Ethereum
  • create a website that will interact with our smart contract via web3, where users will be able to exchange their ethers for our NFT tokens

The little things may seem insignificant, but as I went through each stage, I constantly encountered unexpected surprises. Now we can discuss these points in detail.

How to generate 10,000 unique images?

Why 10,000? The answer is quite simple, most popular NFT projects offer collections of 10,000 NFT tokens. Each creator is free to decide how many NFT tokens he wants to release, but we decided not to deviate from the canon and also made 10 000 tokens.

So, how do you generate 10,000 unique images? Of course, by automatically superimposing layers on top of each other. After a bit of thought, we came to the conclusion that for our project we need the following layers:

  1. background – 20 pcs.
  2. character torso – 25 pcs.
  3. head – 15 pcs.
  4. emotions – 20 pcs
  5. clothes – 30 pcs.
  6. shoes – 25 pcs
  7. accessories – 40 pcs.

In total, we have about 175 unique layers in png format, which is more than enough to get 10,000 unique characters. Now there’s nothing left, namely to write a utility, which will take as input blank layers, and will output ready-made characters. I will write it in Golang, so let’s go. First, we need to describe 2 structures in the domain package, one for layers and one for the canvas.

package domain

import (

// ImageLayer struct.
type ImageLayer struct {
	Image    image.Image
	Priotiry int
	XPos     int
	YPos     int

//BgProperty is background property struct.
type BgProperty struct {
	Width   int
	Length  int
	BgColor color.Color

Let’s take a closer look at both structures.


  • Image – layer image
  • Priority – layer priority, because layers have to be applied in a certain order, first the background, then the body, then the head, etc. …
  • XPos, YPos – layer position on the canvas


  • Width – the width of the canvas
  • Length – length of the canvas

So, when the basic structures are described, we can move on to writing the service, which will actually combine our layers in a certain order. It takes a list of layers and canvas parameters as input, and returns the bytes of the generated image as output. It should be noted that Go has a pretty good library for working with images and that is where we will work with the actual code:

package combiner
import (
type service struct {
func NewBasicImageCombiner() domain.ImageCombiner {
   return &service{}
func (s *service) CombineLayers(layers []*domain.ImageLayer, bgProperty *domain.BgProperty) ([]byte, error) {
   // Sort list by position.
   layers = sortByPriotiry(layers)
   // Create image's background.
   bgImg := image.NewRGBA(image.Rect(0, 0, bgProperty.Width, bgProperty.Length))
   // Set the background color.
   draw.Draw(bgImg, bgImg.Bounds(), &image.Uniform{bgProperty.BgColor}, image.Point{}, draw.Src)
   // Looping image layers, higher position -> upper layer.
   for _, img := range layers {
       // Set the image offset.
       offset := image.Pt(img.XPos, img.YPos)
       // Combine the image.
       draw.Draw(bgImg, img.Image.Bounds().Add(offset), img.Image, image.Point{}, draw.Over)
   // Encode image to buffer.
   buff := new(bytes.Buffer)
   if err := png.Encode(buff, bgImg); err != nil {
       return nil, err
   return buff.Bytes(), nil
func sortByPriotiry(list []*domain.ImageLayer) []*domain.ImageLayer {
   sort.Slice(list, func(i, j int) bool {
       return list[i].Priotiry < list[j].Priotiry
   return list

Great, when the code for generating images is ready, we can move on to generating metadata. To begin with, it is worth noting that it is in the metadata that all the properties and links to images for NFT tokens are stored, and it is also by the metadata that most marketplaces search for NFT tokens. Therefore, it is very important to choose the right format for the metadata.

In what format should there be metadata for NFT tokens?

Since NFT tokens are based on the ERC-721 standard, and the standard itself doesn’t describe in what format the metadata should be, we are free to use any format we want. But if we want our NFT tokens to be fully tradable on venues like opensea, we should follow the following JSON format:

         "value":"Vietnam Jacket"
         "value":"Blue Beams"

Great, now that we’ve figured out the format, let’s describe the structure for storing metadata on Go:

package domain

// ERC721Trait - ERC721 trait format.
type ERC721Trait struct {
	TraitType string `json:"trait_type"`
	Value     string `json:"value"`

// ERC721Metadata - metadata schema.
type ERC721Metadata struct {
	Image      string         `json:"image"`
	Attributes []*ERC721Trait `json:"attributes"`

Before we start generating metadata, we need ready-made links to the images uploaded to the decentralized repository.

Read more articles about cryptocurrencies and mining at COOL-MINING.ORG.

Do you like to read similar articles about cryptocurrencies and mining, do you want to support me as an author or ask questions? Be the first to know the news, subscribe to my telegram channel CRYPTO WIKIES | Bitcoin & Altcoins Mining

Leave a Reply

Your email address will not be published. Required fields are marked *