«Корми кота». Как мы запустили NFT-коллекцию для помощи животным и что из этого получилось

«Корми кота». Как мы запустили NFT-коллекцию для помощи животным и что из этого получилось

Меня зовут Александр Чернов, я руководитель продукта в «Много Лосося».

Мы в Много Лосося любим помогать животным. В 2020 году мы запустили категорию еды "Для котика" (кусочки тунца и креветки с возможностью добавить кошачью мяту), 5% с продажи которой мы ежемесячно жертвуем в "Фонд защиты городских животных".

В августе 2022 мы выпустили NFT коллекцию из 2300 токенов, деньги с продажи которых ежемесячно отправляем в Благотворительный фонд «Банк еды «Русь» на поддержку бездомных животных. А чтобы мотивировать людей покупать наш NFT, мы сделали механизм получения реальных подарков (блюдо или скидка на заказ в сети Много Лосося) для большей части токенов.

В этой статье я рассказываю про техническую реализацию, экономику и результаты проекта, а также делюсь всеми материалами проекта, включая код и слои для генерации картинок.

Кстати, проект действует до конца этого года. Помочь животным и получить NFT и, возможно, подарок от Много Лосося еще можно на сайте https://kormikota.mnogolososya.ru/.

Техническая часть

Сразу делюсь ссылками на все материалы проекта.

Технически проект состоит из следующих компонентов:

  • Сгенерированные картинки. Слои и конфиг для Hashlips (что это, читай дальше) в репозитории в папке hashlip_files.
  • Смарт контракт. Код.
  • Сайт с возможностью покупки и просмотра своих NFT, а также запроса подарка. Frontend исходники в этом репозитории.
  • Backend для отправки подарков (промокодов). Backend код в репозитории в папке webapp/nft/app.

Генерация картинок

Примеры слоев:

Примеры сгенерированных картинок:

Каждая картинка сгенерирована автоматически из слоев (будем называть их атрибутами) - фона, тела, глаз, рта и украшения. Таким способом создано много популярных NFT коллекций, например, Bored Ape Yacht Club. Слои рисуются вручную. Это самая кропотливая часть работы - чтобы любые глаза накладывались на любое тело и на любой фон и это смотрелось красиво.

Генерация картинок из слоев выполнялась с помощью Hashlips Art Engine. Это приложение работает так:

  • Картинки-слои называем определенным образом. Название файла должно содержать название атрибута и число, которое указывает, насколько часто этот вариант атрибута будет использован при генерации. Например, если в качестве атрибута “фон” у нас есть 2 варианта - черный и белый, - можем назвать файлы-слои background_white#1.png и background_black#10.png, и при генерации картинок черный фон будет встречаться в 10 раз чаще, чем белый.
  • Картинки-слои для каждого атрибута кладем в отдельную папку. Например, варианты фона находятся в папке layers/Background.

  • В конфигурационном файле приложения перечисляем названия папок со слоями в том порядке, в котором они должны накладываться друг на друга, а также количество картинок, которые должны быть сгенерированы.

  • Выполняем команду node index.js

Приложение также генерирует метаданные о каждой картинке и о коллекции в целом, которые затем используются в смарт контракте. Метаданные - это json файл, который содержит значение атрибутов токена, ссылку на картинку, номер токена и другие описательные поля. NFT маркетплейсы, такие как OpenSea, Rarible и многие другие, берут информацию из метаданных, чтобы отобразить ее на странице коллекции и конкретного токена. Пример файла с метаданными одного из наших токенов:

Пример json файла с метаданными картинки
Пример json файла с метаданными картинки

Файлы картинок и метаданных мы сохранили в децентрализованной распределенной файловой системе - IPFS. Нодами в ней являются устройства самих пользователей. Файл разбивается на несколько кусков, каждый кусок шифруется и хранится на нескольких нодах вместе с индексом (метаданными для поиска этого куска). Концептуально похоже на Torrent.

Но зачем вообще использовать такую систему для хранения файлов? Ответ: где-то нужно хранить картинку (ведь NFT это в первую очередь красивая картинка) так, чтобы

  • ее не могла удалить или подменить одна сторона,
  • хранение не стоило очень дорого,

и по сути у нас есть 3 варианта хранения:

  • В блокчейне. Например, картинку можно сохранить в base64 формате в переменной смарт контракта. Насчет удаления или подмены в этом случае переживать не придется, но комиссия за запуск смарт контракта зависит в том числе от объема занимаемых контрактом данных, то есть такое хранение будет недешевым.
  • На сервере. Но в этом случае владелец сервера может сделать с файлом что угодно.
  • В распределенной децентрализованной файловой системе. Файл разбит на кусочки и хранится на множестве устройств по всему миру. Никто единолично (а на самом деле благодаря хэшированию даже при сговоре большинства) не сможет изменить файл.

Итого:

  • картинки и метаданные хранятся в IPFS;
  • в файле с метаданными есть ссылка на картинку (из примера выше - в поле image);
  • в переменных смарт контракта (то есть по сути в блокчейне) хранится только ссылка на метаданные.
Ссылка на метаданные в переменной смарт контракта
Ссылка на метаданные в переменной смарт контракта

Кстати, и IPFS, и блокчейн - децентрализованные системы. Почему тогда в блокчейне хранить данные дороже? Поделитесь мыслями в комментариях.

Сам IPFS это open source система, но для упрощения работы с ней есть коммерческие решения. Одно из таких, Pinata, мы используем в нашем проекте. Процесс генерации картинок с помощью Hashlips Art Engine и работы с Pinata очень хорошо описан в этом видео.

Смарт контракт

Тут расскажу, почему решили делать NFT именно на Binance Smart Chain, а не, например, на Ethereum, Solana или Polygon.

Ethereum отпал сразу из-за цен на газ. Хоть мы и понимали, что Ethereum - самый распространенный блокчейн, но при нашей цене за токен в 100-1000 руб комиссия в сотни рублей за mint токена ломает всю суть проекта. Майнеры получили бы денег кратно больше, чем котики.

Solana с ее Proof-of-History и Proof-of-Stake и, как следствие, низкими комиссиями, выглядела очень привлекательно. Но специалиста по Rust (язык, на котором пишутся смарт контракты для Solana) у нас не было.

Polygon с его Layer 2 (блокчейн на блокчейне) тоже предлагает низкие комиссии, но для выполнения транзакций на нем нужно переносить (в терминологии web3 это называют bridge) валюту с, например, блокчейна Ethereum на Polygon, что требует чуть более глубоких знаний от пользователя, а, значит, нашу аудиторию это сократит.

Выбор пал на Binance Smart Chain (BSC) из-за Solidity (язык, на котором пишутся смарт контракты для Ethereum и других блокчейнов, самый популярный язык программирования смарт контрактов), относительно низких комиссий и простоты входа малознакомому с web3 миром пользователю. Под простотой входа имею в виду, что для совершения транзакции в BSC нужно установить Metamask, переключить Metamask на сеть BSC, купить (теперь уже только через P2P) на Binance валюту BNB и вывести ее на Metamask. Мы описали этот процесс подробно в инструкции для наших пользователей.

С точки зрения кода нашего смарт контракта это обычный ERC721 контракт. Отличие лишь в дополнительном функционале подтверждения запроса подарка (что за подтверждение подарка, рассказываю дальше). В коде контракта этот функционал реализован методом getMyPresent и событием PresentIntent.

Сайт и backend

Frontend сделан на Vue.js. Для взаимодействия с интерфейсом смарт контракта используется библиотека ethers.js.

Backend - это FastAPI приложение, которое сервит фронт, умеет получать запросы на подарки и проверку их подтверждения в блокчейне (с помощью python библиотеки web3), а также хранит информацию о запросах подарков и их статусах в базе данных (MongoDB).

Покупка NFT

Изначально покупка (на самом деле выпуск или minting) NFT осуществлялась на сайте с помощью криптокошелька пользователя. По сути так же, как это происходит у большинства NFT коллекций. Пользователь нажимает Купить, открывается криптокошелек, пользователь платит в BNB (криптовалюта от Binance), транзакция валидируется в блокчейне (в Binance Smart Chain обычно это занимает несколько секунд), и пользователь получает NFT на свой кошелек. Именно с таким стандартным в понимании NFT коллекций функционалом мы запустились в августе. Однако, в июле вступил в силу закон о запрете оплаты товаров и услуг цифровыми финактивами. Наши юристы посчитали NFT товаром, то есть согласно новому закону продавать этот товар за криптовалюту нельзя. Хотя формулировка закона явно не называет NFT товаром и за месяц действия закона ни одного прецедента наказаний за подобные действия юристы не нашли, нам все равно настоятельно рекомендовали не принимать оплату за NFT в криптовалюте (даже если эти деньги перечисляют на благотворительность).

В итоге через месяц работы проекта мы изменили механику покупки NFT. Теперь пользователь оплачивает NFT на сайте картой через платежную форму, указывая адрес своего криптокошелька, а наша техподдержка вручную (через кошелек владельца смарт контракта) выпускает NFT на указанный адрес. Ручной процесс, и пользователь ждет свой токен по несколько часов, зато перед законом чисты.

Запрос подарка

За каждый атрибут полученного NFT может полагаться 0 или 1 подарок. Список подарков для каждого значения атрибута сгенерирован заранее и хранится в базе данных. На странице “Мои котики” на сайте пользователь может увидеть своих NFT. Если за конкретный NFT полагается хотя бы 1 подарок, его можно запросить, введя номер телефона, на который придет смс с промокодом. После ввода номера телефона пользователь должен подписать транзакцию в блокчейне. Сделано это для того, чтобы злоумышленник не мог просто выполнить запрос к нашему backend-у, заявив, что он якобы владеет конкретным NFT. Технически он может это сделать, и мы даже сгенерируем, сохраним и вернем ему в ответе идентификатор запроса, но до тех пор, пока в блокчейне не появится валидированная транзакция с этим идентификатором запроса, смс мы ему не отправим.

Процесс получения подарка выглядит следующим образом:

Архитектура запроса подарка
Архитектура запроса подарка
  1. Пользователь на сайте подключает криптокошелек и видит список своих NFT котиков. Если за данный NFT полагается хотя бы 1 подарок, он может ввести номер телефона (куда затем будет отправлено смс с промокодами), после чего выполняется запрос к backend-у (FastAPI приложение).
  2. Backend генерирует идентификатор запроса, сохраняет связку id_запроса + номер телефона + id_токена в базу данных и возвращает на frontend идентификатор запроса.
  3. Frontend инициирует транзакцию в блокчейне и открывает окно криптокошелька. Пользователь должен подписать транзакцию, заплатив небольшую комиссию (за каждую транзакцию в блокчейне нужно платить комиссию). В транзакции закодирован id токена и идентификатор запроса. Пользователь не сможет подписать транзакцию, если он не является владельцем NFT с данным id (эту проверку осуществляет смарт-контракт).

  4. Backend регулярно опрашивает блокчейн с помощью python библиотеки web3.
  5. Когда транзакция с идентификатором запроса, который сохранен в базе данных в статусе “ожидает подтверждения” найдена в блокчейне, backend обращается в микросервис уведомлений, передавая текст сообщения с промокодами, а также меняет статус запроса на “выполнен”, чтобы последующие запросы с этим же id токена не обрабатывались (и нельзя было бы запросить подарок за один и тот же NFT несколько раз).

  6. Сервис уведомлений пытается отправить сообщение в мессенджеры или в виде смс.

  7. Пользователь получает сообщение с промокодами. Применив промокод в мобильном приложении Много Лосося пользователь получит скидку или блюдо в подарок.

Экономика проекта

На самом деле наша коллекция - это 3 отдельные коллекции.

  • Коллекция обычных токенов. В ней 1000 токенов по 100 руб каждый. Половина из этих токенов не содержат подарка вообще, а те, которые содержат, почти все с одним подарком.
  • Коллекция редких токенов. 800 токенов по 500 руб. Тут подарки есть уже у 80% токенов, и большинство токенов содержат по несколько подарков. Инсайт - это самая выгодная коллекция по соотношению цена/стоимость подарков.
  • Коллекция "чумачечих" токенов. 500 токенов по 1000 руб. От 1 до 5 подарков есть у каждого токена.

Экономика такова, что подарками в денежном эквиваленте мы выдаем немного больше, чем получим с продажи всех токенов. Немного больше, потому что блюдо ценой, например, 500 руб, которое мы даем в подарок, нам стоит на самом деле не 500 руб, а 500 руб минус наша маржа и НДС. В цифрах получается так

  • Стоимость всех 2300 токенов - 1 000 000 руб (1000 обычных токенов по 100 руб, 800 редких токенов по 500 руб и 500 "чумачечих" токенов по 1000 руб)
  • Выдаем подарками в денежном эквиваленте - 1 234 200 руб.
  • Нам это стоит (себестоимость) - 981 690 руб

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

При этом за некоторые токены пользователь не получит подарок вообще, а за некоторые может получить подарков на кратно больший денежный эквивалент, чем стоимость токена. Например, можно купить токен за 500 руб и получить 2 блюда в подарок и скидку в суммарном эквиваленте на 1410 руб. Но это не лотерея, и на самом деле никакой случайности тут нет. Зная последнюю транзакцию по смарт контракту (это открытия информация) и видя метаданные коллекции (тоже открытая), можно понять, токен с каким номером будет выпущен следующим и посмотреть атрибуты этого токена. Понятно, что мало кто будет заниматься такими поисками, поэтому для пользователей имеет место определенный азарт.

Результаты

С момента запуска в начале августа по текущий момент

  • 167 уникальных кошельков купили наш NFT;

  • 211 NFT продано;
  • 60400₽ собрано и отправлено в фонд на помощь котикам, а это 20 кото-месяцев сытной и радостной жизни (≈3000 руб обходится фонду месяц ухода за одним котиком). Платежные поручения тут;
  • Подарков в виде блюд и скидок на общую сумму 63300₽ выдали обладателям NFT.

Несмотря на публикации в нескольких тематических телеграмм каналах и сайтах вирусного эффекта проект не вызвал, и за 5 месяцев работы из планируемых 1 млн было собрано только 60 тысяч рублей.

Кстати, еще до старта проекта мы примерно оценили долю пользователей Много Лосося, знакомых с крипто-миром. Мы разместили на нашем сайте скрипт, который проверял наличие у пользователя плагина криптокошелька (например, Chrome расширение Metamask) и записывал эти данные в атрибуты пользователя Яндекс Метрики. Metamask и некоторые другие кошельки-расширения для браузера добавляют на каждую страницу глобальный объект ethereum, наличие которого и проверял скрипт. Вот сам скрипт:

window.onload = function() { let hasMetamask = false; if (typeof window.ethereum !== 'undefined') { hasMetamask = true; } ym(<номер счетчика Яндекс Метрики>, 'userParams', { metamask_installed: hasMetamask }); }
Результат сбора данных о наличии установленного расширения криптокошелька среди посетителей сайта mnogolososya.ru
Результат сбора данных о наличии установленного расширения криптокошелька среди посетителей сайта mnogolososya.ru

В результате оказалось, что плагин криптокошелька установлен у 0.6% наших пользователей. Конечно, это оценка снизу доли знакомых с крипто-миром среди наших пользователей, ведь многие используют крипто-кошельки не в виде расширений к браузеру, а кто-то хранит валюту на бирже. Но по итогу этой хоть и неточной оценки мы понимали, что 95%+ наших пользователей будет сложно поучаствовать в проекте. Также, зная нашу аудиторию (в основном москвичи в возрасте 25-45 лет), мы понимали, что среди всех москвичей наша аудитория как раз таки наиболее прокачена в вопросах криптовалют, то есть за пределами нашей аудитории доля участников проекта будет еще меньше.

Также явно не на руку нам сыграла крипто-зима, начавшаяся незадолго до запуска проекта.

Заключение

Несмотря на отсутствие вирального эффекта и довольно низкие охваты, вместе с нашими клиентами мы помогли нескольким котам и, вероятно, некоторых из них спасли от смерти на улице. Ну и надеюсь, что наш технический опыт окажется кому-то полезен.

Повторюсь, что проект действует до конца года и еще можно успеть сделать подарок котикам и себе :)

Credits участникам проекта:

66
4 комментария

Классно, но сложно для обычных людей. Я просто покупаю корм и кормлю уличных котов 😼

3
Ответить

ну здорово же)

1
Ответить

Саша, ты молодец!)

1
Ответить

Всем привет а как запабатывать тут

Ответить