В этой статье не будут обсуждаться основы невзаимозаменяемых токенов (NFT) или их цели, поскольку по этому вопросу уже имеется множество материалов. Вместо этого сосредоточимся на технических аспектах NFT, поскольку мне пришлось разрабатывать свои собственные решения из-за отсутствия информации по этой теме.
Проведя небольшое исследование, я смог разделить первоначальный проект «создания коллекции NFT» на более определенные и более мелкие задачи, которые включают в себя:
- сгенерировать 10 000 уникальных изображений
- сгенерировать 10 000 метаданных к каждому изображению
- загрузить 10 000 изображений вместе с метеданными в децентрализованное хранилище
- создать смарт-контракт для NFT токенов
- загрузить созданный смарт-контракт в mainnet Ethereum
- создать сайт, который будет взаимодействовать с нашим смарт-контрактом с помощью web3, где собственно пользователи и смогут менять свои эфиры на наши NFT токены
Мелочи могут казаться незначительными, но, проходя каждый этап, я постоянно сталкивался с неожиданными сюрпризами. Теперь мы можем подробно обсудить эти моменты.
Как с генерировать 10 000 уникальных изображений?
Почему именно 10 000? Ответ достаточно прост, большинство популярных NFT проектов, предлагают коллекции именно из 10 000 NFT токенов. Каждый создатель, сам волен решать сколько NFT токенов он хочет выпустить, но мы решили не отходить от канона и тоже сделали 10 000 токенов.
Итак, как же все таки с генерировать 10 000 уникальных изображений? Конечно же с помощью автоматического наложения слоев друг на друга. Немного поразмыслив, пришли к выводу, что для нашего проекта нам нужны следующие слои:
- фон – 20 шт
- туловище персонажа – 25 шт
- голова – 15 шт
- эмоции – 20 шт
- одежда – 30 шт
- обувь – 25 шт
- аксессуары – 40 шт
В общем количестве, у нас получилось приблизительно 175 уникальных слоев в формате png, что более чем достаточно, чтобы получить 10 000 уникальных персонажей. Теперь осталось совсем ничего, а именно написать утилиту, которая на входе будет принимать заготовки в виде слоев, а на выходе будет отдавать готовых персонажей. Писать я буду на Golang, итак поехали. Для начала, нам нужно описать 2 структуры в пакете domain, одна для слоев, а другая для холста.
package domain
import (
"image"
"image/color"
)
// 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
}
Давайте более подробно рассмотрим обе структуры.
ImageLayer:
- Image – изображение слоя
- Priority – приоритет слоя, т.к. слои нужно накладывать в определенном порядке, сначала фон, потом туловище, потом голова, итд …
- XPos, YPos – позиция слоя на холсте
BgProperty:
- Width – ширина холста
- Length – длина холста
Итак, когда базовые структуры описаны, мы можем перейти к написанию сервиса, который собственно и будет комбинировать наши слои в определенном порядке. Код сервиса достаточно простой, на входе сервис принимает список слоев и параметры холста, а на выходе возвращает байты сгенерированного изображения. Хочется отметить, что Go обладает достаточно хорошей библиотекой для работы с изображениями и именно с ней мы будем работать, собственно код:
package combiner
import (
"bytes"
"image"
"image/draw"
"image/png"
"nft/internal/domain"
"sort"
)
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
}
Отлично, когда код для генерации изображений готов, мы можем переходить к генерации метаданных. Для начала, стоит отметить, что именно в метаданных хранятся все свойства и ссылки на картинки для NFT токенов, а также именно по метаданным, большинство торговых площадок выполняют поиск NFT токенов. Поэтому очень важно выбрать правильный формат для метаданных.
В каком формате должны быть метаданные для NFT токенов?
Т.к. NFT токены основаны на ERC-721 стандарте, а сам стандарт никак не описывает в каком формате должны быть метаданные, мы вольны использовать любой формат какой только захотим. Но если мы хотим, чтобы наши NFT токены могли полноценно торговаться на таких пощадках как opensea, мы должны следовать следующему JSON формату:
{
"image":"ipfs://QmPbxeGcXhYQQNgsC6a36dDyYUcHgMLnGKnF8pVFmGsvqi",
"attributes":[
{
"trait_type":"Mouth",
"value":"Grin"
},
{
"trait_type":"Clothes",
"value":"Vietnam Jacket"
},
{
"trait_type":"Background",
"value":"Orange"
},
{
"trait_type":"Eyes",
"value":"Blue Beams"
},
{
"trait_type":"Fur",
"value":"Robot"
}
]
}
Отлично, когда мы разобрались с форматом, давайте опишем структуру для хранения метаданных на 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"`
}
Перед тем как приступать к генерации метаданных, нам нужны готовые ссылки на загруженные в децентрализованное хранилище изображения.
Читайте больше статей о криптовалютах и майнинге на сайте COOL-MINING.ORG.
Нравится ли вам читать подобные статьи о криптовалютах и майнинге, хотите ли вы поддержать меня как автора или задать вопросы? Узнавайте новости первыми, подписывайтесь на мой telegram-канал