ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Сразу оговоримся, что в статье использовано много упрощений и ручной работы, материал не подходит для профессионального использования. Код можно улучшить, а некоторые процессы автоматизировать. Однако нашей целью было подготовить подробное руководство по созданию генеративной коллекции для начинающих на русском языке.
В сети есть примеры и куски кода, но простого материала на русском языке нет, поэтому мы решили поделиться тем, как мы начинали нашу коллекцию, и предоставить готовые скрипты с комментариями для абсолютных новичков.
Contents
Что нам понадобится?
Сначала определимся с блокчейном для запуска сбора и установки необходимого ПО.
Исторически блокчейн Polygon использовался как дешевая альтернатива Ethereum. Сама сеть является сайдчейном сети эфира, т.е. имеет полную совместимость и целую кучу шлюзов, и все основные рынки ее полностью поддерживают. Это идеально подходит для нашей задачи, если у вас есть другие цели, вам придется изменить несколько строк в коде, мы отметим это позже в тексте.
Для развертывания контракта мы будем использовать онлайн-версию среды разработки Remix, поэтому установка программного обеспечения не требуется. Но для чеканки токенов по заранее сформированному списку получателей нам нужно использовать какое-то программное обеспечение — Python оказался удобным, и его мод предполагает, что даже неопытные пользователи знают основы языка.
Мы использовали бесплатную версию среды разработки PyCharm с предустановленным языком 3.11.
В дальнейшем по необъяснимым причинам не удалось правильно установить библиотеку web3 (вероятно, для корректной установки нужны были дополнительные библиотеки), но времени и желания разбираться в причинах ошибок не было, поэтому попытка была повторена для версии 3.9. (что осталось от предыдущих проектов), где все шло как по маслу. Так что имейте в виду, что код должен работать в обеих версиях, но если вы не можете установить библиотеку для интерпретатора 3.11, попробуйте скачать более старую версию.
Мы используем бесплатный хостинг IPFS для хранения данных: ФАЙЛОВАЯ ОСНОВА
И последнее, что нам нужно, это расширение для браузера. Метамаска с зарегистрированным кошельком.
Генерация метаданных для NFT
Чтобы коллекция была канонической, метаданные будут храниться децентрализованно, поэтому первое, что мы делаем, — заранее генерируем изображения и необходимые файлы описания.
В нашем случае для всех токенов использовалось только одно изображение, поэтому процесс генерации готовых изображений из слоев мы опустим (при необходимости примеры и советы можно найти в сети).
А вот простейший пример кода для генерации кучи нужных файлов с метаданными:
<?php
$nums=555; $i=0;
while ($i <= $nums) { file_put_contents(str_pad($i, 3, "0", STR_PAD_LEFT).'.json', '{
"name": "Unit #'.str_pad($i++, 3, "0", STR_PAD_LEFT).'",
"description": "The award for loyal BestChange fans who have actively interacted with the service.",
"image": "ipfs://QmYBrWr1MCMCxNB78RF96EzJHypZ658nUGtAQYq8VnWfqj"
}');}
?>
В нашем примере файлы создаются 000.json, 001.json и так далее, пока 555.json. В поле name имя токена, содержащее его серийный номер, description содержит описание, которое размещается непосредственно под изображением на биржах, а поле image содержит ссылку на изображение. В нашем случае это ссылка на децентрализованное хранилище, поэтому адрес начинается с ipfs://
хотя ссылки на централизованные сервера тоже подойдут.
Содержимое файлов будет выглядеть так:
{
"name": "Unit #002",
"description": "The award for loyal BestChange fans who have actively interacted with the service.",
"image": "ipfs://QmYBrWr1MCMCxNB78RF96EzJHypZ658nUGtAQYq8VnWfqj"
}
Нам не нужно было комплектовать токены разными атрибутами, но если у вас стоит такая задача, советуем ознакомиться с описанием стандарта метаданных, который использует OpenSea: https://docs.opensea.io/docs/metadata-standards
Пример более сложных метаданных:
{
"name": "Example #1",
"description": "Name: BestChange Creator",
"image": "ipfs://Qmc3qZmHiYw6qiViDPhSX1wxjaeGwVuLg6TUde3zxdAbLS",
"attributes": [
{
"trait_type": "Position",
"value": "Senior analyst"
},
{
"trait_type": "Level",
"value": 5,
"max_value": 10
}
]
}
Если ваша коллекция содержит общие изображения, вы должны сначала сгенерировать их, параллельно записывая информацию об используемых слоях в базу данных. Затем загрузите изображения в IPFS, загрузите список хэшей, добавьте их в строки базы данных и только после этого сгенерируйте файлы с метаданными.
После того, как все файлы json сгенерированы, мы также загружаем их в IPFS и собираем список хэшей для будущего сбора.
Смарт-контракт для сбора NFT
Перейдем к самому «страшному» и в то же время самому простому этапу — созданию и публикации смарт-контракта.
На самом деле добрые разработчики из OpenZeppelin позаботились о нас и создали простейший генератор смарт-контрактов: https://docs.openzeppelin.com/contracts/4.x/wizard

Здесь все просто. Для нашей задачи нам нужно открыть вкладку ERC721наполнять Имя И Символ (позже видно только в браузерах) и поставьте галочки:
Обналичиваемый – держателю контракта чеканить токены;
Идентификатор автоматического увеличения — добавляет автоматический счетчик жетонов;
Исчисляемый – добавляет необходимые функции для браузеров;
хранилище URI – добавляет возможность устанавливать URL с метаданными для каждого отдельного токена.
Поле Базовый URI оставить нетронутым, изменить контроль доступа по умолчанию, проверьте масштабируемость определять его не нужно (создает прокси-контракт, чтобы в дальнейшем можно было подменить содержимое принципала, это не по канонам децентрализации, мы этого делать не будем).
Еще заполнить Сведения о безопасности (будет добавлено в код контракта в качестве комментария), а тип лицензии оставьте С.
После этого нажмите кнопку Открыть в ремиксеонлайн-версия IDE уже откроется с нашим кодом контракта.

Затем перейдите на третью вкладку сверху и нажмите на кнопку Составить договор-…

Результат компиляции (появится контракт и в третьем разделе появится зеленая галочка):

Далее вам необходимо авторизоваться в MetaMask:

Теперь на четвертой вкладке сверху можно подключиться к сети, определенной в MetaMask (Внедренный провайдер – MetaMask). Обязательно проверьте, что Polygon Mainnet включен в MetaMask.

Если все было установлено правильно, то под строкой с сетью будет указан идентификатор цепочки 137 (для основной сети Polygon).

После нажатия кнопки РазвертыватьMetaMask попросит вас подписать транзакцию, после чего отправит ее в сеть, где после подтверждения контракт получит свой адрес, он нам понадобится в дальнейшем.

! Есть необъяснимая ошибка. Ваша транзакция может уйти в небытие (Вероятно, MetaMask время от времени отправляет транзакции на резервную ноду, либо Remix передает неверную цену на газ, но данные поступят в сеть через несколько часов).
Если повезет, транзакция сразу получит подтверждение, если не повезет, подождите, не верьте MetaMask, что транзакция отменена, она не бесследно исчезла, а подтвердится через несколько часов (если за это время вы сгенерируете еще несколько попыток, то они тоже будут “отменены” в МетаМаске, затем будут подтверждены одним большим пакетом в тот момент, когда вы этого уже не ждете).
Не удалось понять происхождение этого бага, поэтому мы не можем вам ничего посоветовать, чтобы его избежать.
Теперь, когда наш контракт размещен в блокчейне, нам нужно отследить тег и проверить его. Для этого идем в https://polygonscan.com, здесь нужно создать учетную запись, а затем указать адрес только что созданного контракта в браузере. Адрес контракта вы можете найти в списке транзакций MetaMask, по ссылке “Показать в обозревателе блоков“

Данные о транзакции будут видны на открывшейся странице, нас интересует строка “Для:“, затем перейдите по ссылке в этой строке.

Без проверки браузер видит только байт-код. Давайте исправим это, нажав на “Проверить и опубликовать“.

Далее заполните все поля в соответствии с настройками Remix и нажмите “Продолжать“:

Теперь нам нужно вернуться к Remix и на нашем контракте в контекстном меню выбрать “сгладить“. Он сгенерирует нам единый документ со всеми используемыми библиотеками.

Обязательно добавьте первую строку: // SPDX-License-Identifier: MIT

Вставьте полученный текст в окно проверки. Пройдите рекапчу и нажмите “Проверить и опубликовать“

Если все прошло хорошо, мы увидим что-то вроде этого:

Кстати, созданный здесь ABI пригодится нам в будущем, поэтому советуем скопировать его и сохранить в отдельный документ.

После выполнения всех процедур контракт получил свое имя и галочку для подтверждения кода.
Забастовка коллекции NFT
Теперь самое интересное — создание коллекции с помощью Python-скрипта.
Для работы скрипта нам потребуется следующее:
-
Адрес кошелька
-
Закрытый ключ
-
Адрес контракта
-
Контракт ABI

Ищем первые две точки в MetaMask.
Если скопировать адрес не сложно, то для получения приватного ключа заходим в “Детали учетной записи” в настройках кошелька и нажмите на кнопку “Экспорт закрытого ключа“.
После этого вам нужно будет ввести пароль от кошелька и вы получите приватный ключ (хранить его очень бережнос ним у любого будет полный доступ к вашему кошельку).

Адрес контракта можно взять в браузере и АБИ мы предварительно скопировали в отдельный файл, когда проверяли договор.
Далее берем этот готовый скрипт и вставляем в него необходимые данные:
import csv
import requests
from web3 import Web3
# Определение URL ноды
node_url = "
# Связь с нодой
web3 = Web3(Web3.HTTPProvider(node_url))
# Проверка соединения с нодой
if web3.isConnected():
print("-" * 50)
print("Connection Successful")
print("-" * 50)
else:
print("Connection Failed")
# Определение адреса отправителя (должен быть владелец контракта) и приватного ключа
caller = "0x24......"
private_key = "2f6......."
# Определение ABI и адреса контракта
abi = '[ { "inputs": [], .........'
contract_address = "0xc73........"
# Создание объекта с данными контракта
contract = web3.eth.contract(address=contract_address, abi=abi)
# Определение ID цепочки
Chain_id = web3.eth.chain_id
# Определение номера будущей транзакции для этого кошелька
nonce = web3.eth.getTransactionCount(caller)
# Читаем построчно данные из документа list.csv
with open('list.csv') as csvfile:
# Разделитель полей в скрипте запятая, иногда excel может устанавливать разделителем точку с запятой, проверить можно открыв csv-файл в блокноте
readCSV = csv.reader(csvfile, delimiter=",")
for row in readCSV:
# Получаем текущие цены за газ для сети Polygon
gaspricearr = requests.get('
gPrice = int(float(gaspricearr['fast']['maxFee'])*1000000000)
# Проверяем формат кошелька, если он без контрольной суммы, меняем на правильный
if web3.is_checksum_address(row[0]):
toAddress = row[0]
else:
toAddress = web3.to_checksum_address(row[0])
# Определяем параметры транзакции
call_function = contract.functions.safeMint(toAddress, row[1]).buildTransaction({"chainId": Chain_id, "from": caller, "gas": 1000000, "gasPrice": gPrice, "nonce": nonce})
# Увеличиваем номер будущей транзакции на единицу
nonce += 1
# Подписываем транзакцию приватным ключём
signed_tx = web3.eth.account.sign_transaction(call_function, private_key=private_key)
# Отправляем транзакцию в блокчейн
send_tx = web3.eth.send_raw_transaction(signed_tx.rawTransaction)
# Дожидаемся обработки транзакции
tx_receipt = web3.eth.wait_for_transaction_receipt(send_tx)
# Печатаем в терминал данные опубликованной транзакции
print(tx_receipt)
В дополнение к скрипту нам нужен список получателей с их URI токенов в формате CSV. Содержимое файла должно выглядеть так:
0x4C3A0d41cB765c47D3933F1a05160B7BA085DfCa,ipfs://QmQPe3Kg7FQJdAbANw6gWy8c9qT6r7LKWKFKbE5E1zSAjA
0x2e8B06982c01dc8604060F3604b544AB33bfe69D,ipfs://QmajZpQWNo1pjKCkWp1UyM43oFiv7tX3deJw23LvuHW9bJ
0x636CDce269C60E20bFBa65D3C88F6EF73F8A1a28,ipfs://QmaNF3dWa19k2YJRCErYRjn9fB9EGmPLWm6bwX76VpUHib
0x32FeB01eE4387a18cf4051F2ef7C943Bee51E2c5,ipfs://QmPU2FFPojFei38LDFqQQaQXUPL27r9PKneyozkXYUTMk6
0x275D40dFD150270C7212fc626798dde0176EFaBE,ipfs://QmUfFA9vuhjTDQW29EpA5U6R6HjRRcBMNLaLDm8yXwnGVD
0xbb1338d34740Dd6c0342e46c4c51cDf38C1CceD8,ipfs://QmahVcksCjoaxNbYuk2wqyNY2CbnYxmj4Zovq5iVH9xTei
Обратите внимание на разделитель полей, его нужно будет правильно указать в скрипте.
Финал
Все готово, запускаем скрипт и ждем пока токены полетят к своим получателям.
! Иногда раз в 100-300 транзакций случаются неуловимые баги и скрипт прерывается ошибкой (каждый раз новая, закономерность определить не удалось). Для продолжения нужно подождать минуту, зайти в браузер и найти ID последнего отправленного токена в истории транзакций вашего кошелька, удалить все ранее обработанные строки в CSV, сохранить и перезапустить скрипт.
Сбор был заблокирован и тут же разошелся по адресатам, нам это удалось. Если вы хотите быть единственным владельцем, укажите свой собственный кошелек в поле получателя CSV-файла, не оставляйте его пустым.
Основной:
Если вы используете IPFS для хранения метаданных, одним из популярных сайтов является Rarible (используется ВКонтакте для импорта данных) имеет свойство иногда не загружать метаданные из этой файловой системы, если шлюз не отрендерил их своевременно.
Для проверки точности рекомендуем использовать собственный API:
<?php
$body = file_get_contents(' //Получаем в $body json строку с нужным количеством записей
$arr = json_decode($body, true); //Разбираем json запрос на массив в переменную $arr
foreach ($arr['items'] as $nums){
echo $nums['meta']['name'].'<br>'; //Построчно выводим имена токенов, если какого-то не хватает, значит Rarible не скачал данные
}
?>
Если нет нескольких строк, напишите в их поддержку через форма для обратной связи. Они исправят это в течение нескольких дней.