Що ми робитимемо
Так як DipDup і Dappetizer досить складні в налаштуванні, ми будемо індексувати простий контракт і виконувати прості запити. Для прикладу ми вибрали tzBTC — обернутий Bitcoin на Tezos як токена стандарту FA1.2.
Спочатку ми встановимо кожен індексер та створимо порожні проєкти. Потім налаштуємо індексер та правила індексування балансів власників tzBTC із сховища або обсяг транзакцій tzBTC по точці входу "transfer". Потім ми відобразимо збережені в базі даних значення на простій веб-сторінці.
Встановлення та налаштування DipDup
DipDup працює тільки на Unix системах: macOS та дистрибутивах Linux. Якщо у вас Windows, використовуйте WSL або Linux у дуалбуті.
Спочатку потрібно встановити середовище Python будь-яким зручним способом. Потім відкрити термінал і встановити DipDup за допомогою команди:
python -c "$(curl -sSL https://dipdup.io/install.py)"
Якщо у вас встановлена macOS 12 або вище, замініть python
на python3
.
Створення проєкту у DipDup
Тепер почнемо налаштовувати DipDup. Спочатку створимо папку dipdup_smartlink, в якій створюватимемо проєкт, і перейдемо до неї в терміналі. Потім виконаємо команду dipdup new, щоб створити новий проєкт.
Наприклад проіндексуємо договір токена tzBTC, оскільки він влаштований легко і створення індексера не займе багато часу. Для цього при виборі конфігураційного шаблону візьмемо варіант tzBTC.
Далі потрібно заповнити інформацію про проєкт: назву, версію, власника, вибрати базу даних і таке інше. Наприкінці DipDup самостійно згенерує структуру та необхідні файли для роботи проєкту.
Конфігурація індексу
У папці з проєктом з'явиться файл dipdup.yml — конфігурація індексу для проєкту. Його структура наступна:
- spec і package — дані про сам DipDup;
- database — налаштування бази даних, що використовується. За замовчуванням sqlite, але можна підключити PostgreSQL;
- contracts — назва контракту, його адреса та typename для подальшого використання в коді;
- datasources — джерела даних для індексування. За замовчуванням — TzKT, але можна додати сторонні API і брати дані з інших сайтів, наприклад, щоб отримувати ціни від бірж, або IPFS для отримання метаданих NFT;
- indexes — список індексів, які створить і використовуватиме DipDup. У нашому випадку ми збиратимемо інформацію про власників tzBTC;
- templates — схеми операцій, які потрібно індексувати. Усередині є handlers, які по суті є інструкціями, як DipDup повинен обробити виклики точок входу: які дані взяти і як їх записати в базу. До речі,
та вказують на ключі "datasource" та "contract" усередині словника "indexes".
Найважливіше це templates. У цих модулях ми вказуємо точки входу, які потрібно індексувати: для даних on_transfer потрібно перевіряти операції з точкою входу transfer, on_mint — операції з mint. Це важливо, оскільки в коді обробників (handlers) не буде прямих посилань на точки входу або на контракт, і ця особливість може викликати питання на кшталт «А звідки взагалі беруться дані?».
У цьому прикладі ми збиратимемо баланси власників tzBTC. Вони змінюються при викликах точок входу "transfer" та "mint", тому ми додали два обробники за назвами точок входу в контракті — on_mint та on_transfer.
Перегляньте документацію DipDup Config, якщо ви хочете дізнатися більше про можливі конфігурації індексера.
Моделі та обробники даних
Потім потрібно ініціювати створення індексів, схем, моделей та обробників. Для цього в терміналі перейти в папку tzbtc і виконати команду:
dipdup init
DipDup створить кілька файлів — моделі та обробники, які описують роботу з базою даних та індексування ончейн-даних, і нам потрібно їх модифікувати для налаштування процесу індексування.
Перший файл — models.py. У ньому зберігаються моделі даних, грубо кажучи — опис таблиць і стовпців у базі даних. У моделі Holder ми оголосимо кілька типів стовпців:
- адреса завдовжки 36 символів;
- баланс та оборот токенів у числовому форматі, при цьому вкажемо 8 знаків після ком (decimal_places);
- кількість транзакцій;
- час останньої активності.
Обробники (handlers) — це інструкції, які говорять DipDup як обробити ончейн-дані, коли він знайде в блоці виклик потрібних точок входу tzBTC. У папці handlers зберігаються обробники сховища та викликів точок входу контракту tzBTC.
Перший обробник — on_balance_update. Ми будемо викликати його при появі операції, яка змінить баланси власників tzBTC, тобто при мінтингу та передачі токенів, і оновлюватимемо відповідні баланси в базі даних.
У ньому ми оголосили функцію on_balance_update, якій передамо три параметри:
- address — адреса власника;
- balance_update — суму зміни балансу власника;
- timestamp — час операції.
Функція спочатку перевірить, чи є така адреса власника в базі даних, а потім створить новий рядок і запише в неї нового власника, або оновить старий запис.
Другий обробник on_mint буде зчитувати параметри виклику точки входу mint, а потім викликати функцію on_balance_update для оновлення записів у базі даних.
Зверніть увагу: змінну amount ми зберігаємо значення mint.parameter.value, яке беремо з параметрів виклику точки входу mint.
Аналогічно беремо адресу одержувача нових токенів: address = mint.parameter.to;
Третій обробник on_transfer розбирається з транзакціями, тобто операціями з точкою входу transfer. Він улаштований складніше. По-перше, потрібно порівняти адреси відправника та одержувача. Якщо вони збігаються, то баланси власників не змінилися і базу даних можна не оновлювати. По-друге, переказ токенів означає зміну балансів відправника та одержувача. Тому функцію on_balance_update потрібно викликати двічі для кожної адреси, при цьому від балансу відправника відняти суму транзакції, а до балансу одержувача — додати.
Як і в обробнику onmint, ми отримуємо ончейн-дані з параметрів виклику точки входу transfer: адреса відправника from, адреса одержувача to, суму переказу value.
Запуск індексера
У терміналі виконайте команду:
dipdup run
DipDup почне завантажувати потрібні дані з публічного API TzKT. По завершенню він почне обробляти нові блоки.
Під’єднання бази даних до проєкту
Зробимо схожий проєкт, як у минулому уроці, де ми відображали пропозиції про позики під заставу NFT і генерували посилання на закладені токени.
Створимо у папці ttzbtc_dipdup файл tzbtc_dipdup.php. Спочатку під’єднаємось до бази даних. DipDup за замовчуванням записує проіндексовані дані до бази даних SQLite. Під’єднання до неї трохи відрізняється від Postgres: потрібно вказати повний шлях до файлу з базою даних, але не потрібно вказувати ім'я користувача, пароль і порт.
У змінній $db_dir вкажемо шлях до бази даних на жорсткому диску, а $db_handle — метод підключення до бази даних new PDO($db_dir).
Потім напишемо SQL-запит, у якому виберемо всі адреси та баланси власників tzBTC з ненульовим балансом та сортуванням за зростанням.
Щоб вивчити структуру бази даних SQLite і точно знати, що запитувати, можна встановити графічний інтерфейс SQLite Browser.
За допомогою функції перебору масиву foreach виведемо на сторінку адреси та баланси користувачів із масиву $row.
Потім закриємо з'єднання з базою даних командою $db_handle = null.
У терміналі перейдемо до папки з tzbtc_dipdup.php і запустимо PHP-сервер командою:
php -S localhost:8000
У браузері відкриємо сторінку за адресою http://localhost:8000/tzbtc_dipdup.php
Оскільки при написанні обробників транзакцій та мінтингу tzBTC ми вже ділили баланс на 10^8, то в базі даних вони відображаються з правильною кількістю знаків після коми.
Встановлення та налаштування Dappetizer
Спочатку встановити Node.js для роботи з пакетним менеджером npm. Потім створимо папку для проєкту та встановимо туди Dappetizer. Найпростіше зробити все з терміналу:
mkdir dappetizer
cd dappetizer
npm install @tezos-dappetizer/cli
Перевіримо, чи пройшла установка успішно командою перевірки версії:
npx dappetizer --version
Тепер ініціалізуємо новий проєкт із зазначенням назви tzBTC та адреси контракту:
npx dappetizer init --contractName=tzBTC KT1PWx2mnDueood7fEmfbBDKx1D9BAnnXitn
Конфігурація індексера
Dappetizer створить у папці проєкту необхідні для роботи файли. Серед них чотири головні:
- dappetizer.config.ts — параметри для підключення до бази даних;
- index.ts — основний модуль, в нього ми імпортуємо конфігурацію індексування;
- tz-btc-indexer.ts — логіка індексування контракту tzBTC. Dappetizer генерує список із шаблонами для всіх точок входу контракту, і потрібно лише видалити невикористовувані та дописати логіку обробника точки входу "on_transfer";
- entities.ts — його потрібно створити самому та описати в ньому структуру таблиць та стовпців, у які Dappetizer буде записувати дані.
У dappetizer.config.ts записано параметри для під’єднання до бази даних. За замовчуванням Dappetizer використовує SQLite, але можна використати Postgres.
entities.ts потрібно створити самому, і в ньому вказати клас (таблицю) та стовпці бази даних. Ми будемо записувати транзакції з токеном tzBTC, тому створимо клас Transaction зі стовпцями id (внутрішній id у базі даних), sender, receiver та amount.
У папці src знаходиться tz-btc-indexer.ts, де Dappetizer згенерує функції для індексування всіх точок входу зазначеного контракту. У коді відповідної точки входу потрібно описати логіку індексування: який параметр виклику точки входу записати до бази даних.
У tzBTC точка входу для відправки токенів називається transfer. Відповідно, знайдемо код цієї точки входу і додамо до нього логіку:
- спочатку імпортуємо клас Transaction, щоб використовувати його для запису даних;
- у функції indexTransfer опишемо логіку індексування: створимо константу tzBTCtransfer, в якій запишемо адреси відправника та одержувача, а також обсяг транзакції;
- Наприкінці функції indexTransfer викликаємо функцію insert, щоб записати зміст tzBTCtransfer до бази даних.
Тепер потрібно додати модель Transaction у модуль index.ts. для цього імпортуємо Transaction з файлу entities.ts і додамо Transaction до масиву dbEntities.
Запуск індексера
У терміналі перейдіть до папки з проєктом і виконайте команду для складання проєкту:
npm run build
Потім запустіть індексування:
npx dappetizer start
Dappetizer почне індексувати блоки:
Під’єднання бази даних до проєкту
Зробимо схожу сторінку, як у проєкті з DipDup. Створимо файл tzbtc_dappetizer.php та опишемо в ньому структуру сторінки. Оскільки ми використовуємо ту ж базу даних SQLite, можемо безпосередньо скопіювати код із проєкту з DipDup. Потрібно змінити лише шлях до бази даних, SQL-запит та функцію для виведення даних.
Запустимо PHP-сервер у папці Dappetizer та відкриємо сторінку в браузері:
Домашнє завдання
Підрахуйте загальний обсяг транзакцій tzBTC за проіндексований період. Підказка: краще працювати з готовими даними, а не з тими, що ви додаєте.
Рішення
Робота з індексерами тісно пов'язана з базами даних та SQL-запитами. Для розробки програм обов'язково треба вивчити SQL!
Якщо у прикладі з DipDup ми рахували обсяг (turnover) через додавання обсягу проіндексованої транзакції до колишнього значення turnover, то зараз простіше скористатися SQL-командою SUM().
Відкрийте файл tzbtc_dappetizer і створіть SQL-запит $sql_sum з командою SELECT SUM(amount) FROM "transaction":
Потім додайте функцію query($sql_sum) для запиту до бази даних та виведіть значення на сторінку.