Роботи / Крипторобота на відкритий бриф

Tally Invoices: посилання на оплату в USDC і EURC у мережі Arc

Що було

Фрилансер хоче отримати оплату в стейблкоїнах, але в більшості мереж клієнт спочатку мусить купити другу монету на комісію. На цьому кроці багато хто кидає, а продавець потім сам шукає переказ в оглядачі блоків. Circle відкрила бриф на живі проєкти у своїй новій мережі Arc, де комісія платиться тим самим USDC.

Що зробили

Вибрали ідею через консиліум і викинули все зайве. Написали контракт на 124 рядки з 13 тестами, одну статичну сторінку, Telegram-бота, який стежить за гаманцем. Задеплоїли в Arc mainnet, перевірили вихідний код на Sourcify, провели три справжні оплати з власних гаманців і подали заявку.

Що отримав клієнт

Рахунок у USDC оплачується одним підписом, гроші в тій самій транзакції йдуть продавцю, на контракті завжди нуль. Перший живий рахунок на 0,50 USDC оплачено через 6 секунд після створення, бот прислав обидва сповіщення. Від порожньої папки до mainnet, трьох оплат і поданої заявки минув один день.

ЗамовникВідкритий бриф Arc Microgrants, мережа Arc
Моя рольВибір ідеї, смарт-контракт і тести, сайт, Telegram-бот, деплой у mainnet, живі оплати, відео, тексти заявки
СтатусЗаявку подано, на розгляді. Сайт, контракт і бот працюють
Результат0,50 USDC перший живий рахунок, оплачений у mainnet · Відкрити
Відхилено: сторінка рахунку відкривалась понад 5 секунд, бібліотека viem тягнулась із зовнішнього CDN esm.sh. Реконструкція
Відхилено: сторінка рахунку відкривалась понад 5 секунд, бібліотека viem тягнулась із зовнішнього CDN esm.sh. Реконструкція
Прийнято: viem зібрано в один свій файл (103 КБ стиснутий), сторінка відкривається за 0,9-1,1 с. Перший живий рахунок на 0,50 USDC
Прийнято: viem зібрано в один свій файл (103 КБ стиснутий), сторінка відкривається за 0,9-1,1 с. Перший живий рахунок на 0,50 USDC

Завдання

Circle, яка випускає USDC, відкрила бриф на DoraHacks: 20 мікрогрантів по 500 USDC за живі проєкти у своїй новій мережі Arc. Дві умови: проєкт задеплоєно в Arc mainnet і код відкритий.

Ми хотіли перевірити, чи доводимо смарт-контракт до продакшену за день. Не макет, а контракт з тестами, перевіреним вихідним кодом і справжніми грошима, які через нього пройшли. І отримати маленький, але закінчений продукт у портфоліо: сайт, контракт, бот і докази в блокчейні.

Проблема, яку розвʼязували, проста. Дизайнер намалював логотип для кафе в Берліні і хоче отримати 25 USDC. Звичайний шлях у крипті: прислати клієнту адресу гаманця, пояснити мережу, а клієнту ще знайти монету на комісію. Клієнт, який платить уперше, на цьому кроці часто здається. В Arc комісія платиться в USDC. Якщо у клієнта є 25 USDC, він може оплатити рахунок на 25 USDC, і все.

Навіщо і яка ідея

Ідею вибирав консиліум із пʼяти AI-порадників: скептик, архітектор, візіонер, людина збоку, прагматик. Вердикт: сервіс посилань на оплату з нейтральною назвою, не «Arc Pay», бо це звучить як продукт самої Circle. Викинути все зайве: обмінник валют, x402, AI, свопи. Скептик тоді попередив, що платіжних посилань серед заявок буде багато. Він мав рацію, про це нижче.

Три частини. Головна сторінка: сума, валюта (USDC або EURC), призначення платежу, кнопка «підключити гаманець і створити». Сторінка рахунку за посиланням ?i=номер: видно всім, навіть без гаманця, зі статусом, сумою, хто і коли заплатив, і посиланням на транзакцію. Telegram-бот: продавець пише /watch <адреса>, і бот пише, коли на цю адресу створено рахунок і коли його оплачено. Бот тільки читає мережу, ключів у нього немає.

Кожна дія від імені Дмитра на DoraHacks (публікація питання, відправка заявки, згода з умовами) робилась тільки після його прямого «так».

Хід роботи

Коли Що відбувалося
Ранок Консиліум по брифу. Вердикт: посилання на оплату, нейтральна назва, нічого зайвого
Ранок Контракт і 13 тестів, сайт, бот. Чистий гаманець для деплою, чекаємо поповнення
08:50 Реєстрація на DoraHacks, питання в Q&A про виплати для України
09:50 Гаманець поповнено на 2,99 USDC. Деплой за 0,034 USDC, рахунок #1 створено і оплачено, код перевірено на Sourcify. Сайт на Vercel, репозиторій відкрито
09:51 Бот запущено на сервері
10:00 Автоматичне security-ревʼю знайшло призначення платежу без екранування в повідомленні бота. Виправлено
10:05 Наскрізна перевірка: рахунок #2 створено і оплачено. Бот спочатку мовчав, полагодили, прийшли обидва сповіщення
10:20 Заявка на DoraHacks: профіль, опис, анкета з 10 питань. Відправлено по слову Дмитра
12:40 Розвідка: 334 заявки на 20 місць. Доробка: жива оплата в EURC, відео на 61 секунду
12:55 Сторінка прискорена з 5+ до 1 секунди, відео і EURC додано в заявку

Усього 5 комітів, близько 900 рядків коду разом із тестами і скриптами. Робочих днів: один.

Чим робили

Контракт TallyInvoices.sol, 124 рядки на Solidity. У рахунку три стани: відкритий, оплачений, скасований. Скасувати може тільки продавець. Гроші не затримуються на контракті: у тій самій транзакції вони йдуть продавцю. Стан рахунку міняється до переказу, тому повторний вхід знаходить рахунок уже оплаченим. Адреса токена без коду відхиляється, інакше рахунок можна було б «оплатити» викликом, який нічого не переказує. Неправильна сума, повторна оплата і надто довге призначення відкочуються.

Тести у Foundry, 13 штук: серед них атака повторним входом, продавець, який відмовляється приймати гроші, і fuzz-тест суми на 256 прогонів. Вихідний код перевірено на Sourcify з точним збігом байткоду.

Сайт: одна статична сторінка на Vercel, viem зібрано в один файл. Читання мережі йде через свій проксі /api/rpc, який пропускає тільки 10 методів читання і не більше 20 викликів у пачці. Записувати через нього не можна. Бот на Python з web3 і SQLite для підписок, опитує мережу кожні 3 секунди, крутиться службою на нашому сервері.

Ключі: контракт задеплоєно з окремого чистого гаманця, створеного під цей проєкт. Підпис транзакцій локально, ключі в файлах з правами тільки для власника, в репозиторій не потрапляють. Токен бота так само. Сервіс не тримає нічиїх грошей.

Результат

Три справжні оплати в Arc mainnet. Рахунок #1 на 0,50 USDC створено і оплачено з іншого гаманця з різницею в 6 секунд, у продавця рівно +0,50, на контракті 0. Рахунок #2 на 0,25 USDC: бот прислав обидва сповіщення, про створення і про оплату. Рахунок #3 на 0,50 EURC оплачено двома підписами (дозвіл на списання і оплата), бо EURC в Arc звичайний токен ERC-20. У продавця рівно +0,50 EURC.

Комісія за одну оплату у покупця близько 0,0014 USDC, деплой контракту коштував 0,034 USDC. Хеші всіх транзакцій є в README репозиторію. Оплачений рахунок відкривається без гаманця: tally-invoices.vercel.app/?i=1.

Швидкість: сторінка рахунку в чистому браузері відкривалась понад 5 секунд, після збірки viem локально 0,9-1,1 секунди за заміром Playwright.

Слабкі місця кажемо прямо. Реальних користувачів немає, це демо з нашими власними оплатами: лічильник на сайті показував 3 створені рахунки, всі наші. Відриву від інших заявок по функціях теж немає, продукт свідомо маленький.

Помилки і що винесли

Бот мовчав при першій справжній оплаті.Нова версія web3 на сервері вимагає поле "anonymous": false в описі подій контракту, без нього падає з KeyError. На тестах не спливло, спливло тільки на живому прогоні. Додали поле, бот сам дочитав пропущені блоки і прислав обидва сповіщення. Урок: бота перевіряти живою оплатою до подачі, а не по коду.
Призначення платежу без екранування.Продавець пише призначення сам, а бот вставляв його в HTML-повідомлення як є. Знайшло автоматичне security-ревʼю, виправили одним рядком. Урок: будь-який текст від користувача, що йде в розмітку, екранувати одразу.
Circle блокує українські IP.Публічний RPC мережі Arc і частина документації відповідали з нашого Мака помилкою 403. Тому читання на сайті йде через свій проксі на Vercel, а транзакції підписувались локально і відправлялись через сервер за межами України. Урок: у проєкті на чужій інфраструктурі першим ділом перевіряти, чи доступна вона з нашої країни.
Сторінка вантажилась понад 5 секунд.viem підтягувався з зовнішнього CDN esm.sh. Для сторінки оплати це задовго. Зібрали бібліотеку в один свій файл (317 КБ, 103 КБ стиснутий), стало близько 1 секунди.
Заявок виявилось набагато більше, ніж здавалось.Публічний лічильник показував 0, а перебір через API DoraHacks знайшов 334 заявки на 20 місць, близько 30 із них про рахунки і платіжні посилання, одна навіть називається «Tally». 37% заявок мали відео, у нас його не було. За годину додали оплату в EURC і 61-секундне відео. Урок: дивитись на інших до подачі, а не після.
Форми DoraHacks.Візуальний редактор обрізав посилання на .vercel.app, довелось перейти в markdown і перевірити всі 8 посилань у превʼю. Випадаючі списки лишались невидимими і перекривали сусідні поля, поле відео не приймало звичайний ввід. Урок: після такої форми звіряти збережене через API, а не на око. Опис і відео звірили запитом до їхнього API.
Сцена Telegram у відео зібрана за шаблоном бота, а не знята з екрана. Самі сповіщення приходили по-справжньому, але в ролику це реконструкція. Кажемо чесно.

Інструменти

SolidityFoundryArc mainnetUSDC і EURCSourcifyviemVercelPython і web3SQLiteTelegram Bot APIPlaywrightffmpegClaude Code