18.12.25 Base запустила своё Mini App SDK, но я получил доступ к нему немного раньше еще на Beta-тестировании. Мне стало интересно: смогу ли я собрать миниаприложение, в котором каждое действие пользователя оставляет on-chain след. В статье описываю архитектуру, code-level решения, баги, инфраструктуру и все проблемы, которые пришлось пройти. Так появился The Wall Base — мини-приложение внутри Farcaster/BaseAPP

BaseAPP — Это web3 суперапп, эволюция кошелька Coinbase совмещающие в себе социальные сети, блокчейн, торговлю, платежи и мини-приложения

https://basewall.vercel.app/

The Wall Base — состоит из:

  • социальной ленты постов;

  • минта NFT для каждого поста;

  • маркетплейс для торговли этими NFT;

  • prize-pool, который пополняется с каждого действия;

  • оффчейн-розыгрыш, выплачивающий пул случайному пользователю - владельцу поста.

Это мой первый опыт работы с Base SDK и ончейн-разработкой. В статье я делюсь своими наблюдениями и тем, с чем столкнулся. Если у вас есть советы, как можно сделать что-то проще или лучше — обязательно пишите в комментариях. Буду рад обратной связи!
Это мой первый опыт работы с Base SDK и ончейн-разработкой. В статье я делюсь своими наблюдениями и тем, с чем столкнулся. Если у вас есть советы, как можно сделать что-то проще или лучше — обязательно пишите в комментариях. Буду рад обратной связи!

Архитектура: как устроен The Wall Base

Стек:

  • Next.js 14 (App Router) — серверные компоненты, layout-маршруты;

  • TypeScript;

  • Tailwind;

  • Zustand для локального состояния;

  • viem и wagmi для on-chain взаимодействий.

Особенности App Router внутри Base App

Внутри Base App нельзя использовать:

  • полный SSR (часть окружения недоступна)

  • нестабильные fetch'и (некоторые origin блокируются)

  • системные навигации (всё должно быть «внутри mini-app sandbox»)

  • baseapp лоялен к vercel поэтому деплой я делал через него

Поэтому критично:

  • использовать export const dynamic = "force-dynamic" там, где нужно обновлять UI при каждом запросе;

  • избегать window-зависимых штук в серверных компонентах;

  • ограничить heavy API на серверных хэндлерах /api/....


Бекенд-слой: /api, KV и off-chain данные

Посты живут off-chain — иначе мини-апп тормозил бы при каждом загрузке фида.

Хранилище:

  • KV (Vercel KV ) — основной вариант;

  • локальный json-файл в dev-режиме.

Часть данных:

Данные

Где хранятся

Почему

Посты

KV

Быстро, почти бесплатные операции

Реакции

KV

Низкий трафик, нет смысла писать on-chain

История победителей

KV

Не критично быть on-chain

Связь пост ↔ NFT

ForumNFT contract

Это самое ценное и должно быть on-chain


Контракты: ForumNFT и Marketplace на Base mainnet

Все смарт-контракты проекта The Wall Base в финальной версии развернуты в сети Base mainnet.

Что такое Base

Base — это L2-сеть (rollup), построенная поверх Ethereum и разрабатываемая командой Coinbase.
Технически Base использует OP Stack, что даёт:

  • совместимость с EVM (Solidity, Hardhat, Foundry и т.д.);

  • низкие комиссии по сравнению с Ethereum mainnet;

  • быструю финализацию транзакций;

  • нативную интеграцию с экосистемой Coinbase и Base App.

Для мини-приложений Base подходит идеально: газ дешёвый, UX быстрый, а безопасность унаследована от Ethereum.

Несмотря на то, что продакшн-версия проекта работает в Base mainnet, вся разработка и отладка велись в Base Sepolia — тестовой сети Base.

Причины очевидны:

  • деплой контракта стоит реальные ETH;

  • каждая ошибка в логике = потеря денег;

  • тестовая сеть полностью повторяет поведение mainnet.

Для простого ERC-721 контракта деплой в Base mainnet в среднем обходится в 5–10 долларов, в зависимости от сложности и текущей загрузки сети.
Поэтому вся логика — от минта до prize pool — сначала полностью отрабатывалась в тестовой сети.

Важно понимать один момент, который часто путают новички. Смарт-контракт не «деплоится из IDE»

  • пишется локально (Solidity-файл);

  • компилируется локально (Hardhat);

  • отправляется в сеть через EVM-кошелёк.

Для деплоя нужен:

  • приватный ключ EVM-кошелька;

  • RPC-endpoint сети;

  • ETH на балансе (в тестовой или основной сети).

Фактически деплой — это обычная транзакция, в которой байткод контракта отправляется в сеть.

npx hardhat run scripts/deploy.js --network base

Для тестовой сети необходимо взять тестовые ETH - я тянул из крана https://www.alchemy.com/faucets/base-sepolia, но это для тех у кого есть активность в майнете. Для base sepolia я не нашел, альтернативный вариант - взять ETH в тестовой сети эфириум и перегнать их по мосту в base. Такие мосты существуют, но для новичков это тоже отдельный квест, если что спрашивайте в комментариях, я поищу конкретные мосты

После подтверждения транзакции:

  • контракт появляется в сети

  • получает адрес

  • начинает исполнять заложенную в него логику

Разделение on-chain и off-chain частей

В проекте чётко разделены два слоя:

On-chain

  • смарт-контракты (ForumNFT, Marketplace);

  • хранение денег;

  • ownership NFT;

  • prize pool.

Off-chain

  • фронтенд (Next.js);

  • хранение постов;

  • выбор победителя;

  • UI и UX;

  • взаимодействие с RPC.

Контракты не деплоятся вместе с фронтом — они живут отдельно и общаются с off-chain частью через RPC.

Переход с Base Sepolia на Base mainnet

Когда логика контрактов была полностью протестирована в Base Sepolia, переход в mainnet выглядел максимально просто:

  1. Деплой контрактов в Base mainnet

  2. Получение новых адресов контрактов

  3. Замена конфигурации во фронтенде:

# было
NEXT_PUBLIC_BASE_RPC_URL=https://sepolia.base.org
NEXT_PUBLIC_NFT_CONTRACT_ADDRESS=0xTestAddress

# стало
NEXT_PUBLIC_BASE_RPC_URL=https://mainnet.base.org
NEXT_PUBLIC_NFT_CONTRACT_ADDRESS=0xMainnetAddress

Никакой логики переписывать не пришлось — благодаря EVM-совместимости поведение контрактов в testnet и mainnet идентично, но трудно при переходе все же были, о н их я расскажу ниже

ForumNFT: кастомный ERC-721

Основные функции:

  • Минт за фиксированную цену

  • 50% в rewardPool, 50% в ownerBalance

  • Связи:

    • tokenToPost[tokenId]

  • Сервисные значения:

    • nextTokenId

    • MIN_REWARD_RESERVE = 0.0001 ether (оставляем для отправки транзакции)

adminSetNextTokenId

Admin-only, добавлена вручную:

function adminSetNextTokenId(uint256 value) external onlyOwner

Зачем она нужна?

Base mini-apps создают много временных тестовых постов, и я периодически «сжимал» состояние, делая полный burn всей коллекции. Тогда приходилось сбрасывать nextTokenId.

⚠ Но если сделать это без полного burn → коллизии:
один и тот же tokenId может уже существовать.

Marketplace: простая торговля NFT

  • листинг с указанием цены

  • покупка → комиссия

  • часть комиссии уходит в rewardPool

  • все покупки — обычные ETH-транзакции (Base → дешевле, чем в ETH mainnet)

Prize Pool: общая механика

Пул пополняется:

минты (50%) +
комиссии marketplace +
ownerBalance (если вручную отправляю)
прямые переводы на контракт

Резерв 0.0001 ETH всегда остаётся нетронутым, иначе prizePool может «обнулиться» и не будет хватать на курс ETH запросы.

Почему розыгрыш оффчейн

Потому что Base Mini Apps:

  • не предполагают генерацию on-chain randomness;

  • нет встроенного безопасного VRF;

  • нельзя заставить юзера подтверждать chainlink VRF callback.

Реализация:

  1. /api/reward/draw выбирает случайный пост (через PRNG + фильтр used posts).

  2. Создаёт транзакцию payPrize(winner, tokenId, amount).

  3. Записывает победителя в KV.

Интеграция с Farcaster Mini App SDK

Использую:

  • sdk.actions.openUrl() — для открытия профиля юзера в Base App;

  • sdk.actions.openPage() — для переходов внутрь mini-app;

  • sdk.ready() — обязательное условие перед рендером UI.

Особые требования Mini App:

  • mini-app не может делать навигацию по window.location;

  • ссылки должны быть whitelisted в манифесте;

  • base.app и farcaster.xyz работают — остальные нет.

RPC: почему ничего не работает в маинет

Это был один из самых неприятных технических моментов, я долго не мог понять почему в маркетплейс не подгружаются имеющиеся NFT и не обновляется призовой пул. Проблема заключался в пропускной способности публичных RPC

Я начал с публичных RPC:

Проблемы:

  • периодические timeouts (4–7 сек) при чтении состояния;

  • нестабильные ответы на viem/wagmi вызовы;

  • иногда возвращался execution reverted при нормальных функциях;

  • eth_call мог работать, а eth_sendTransaction — падал;

  • Base App (мобильное окружение) имеет свои rate-limits.

Внутри мини-приложения это критично — фид и marketplace должны отвечать мгновенно.

Решение: перейти на приватные RPC от Coinbase Developer Platform

Обычно, такие RPC стоят денег, но на Coinbase Developer Platform я взял их бесплатно, в добавок мне дали 500 USDC на услуги компании и на возврат комиссий сети для пользователей моего приложения

Я завёл приватный endpoint:

https://base-mainnet.g.alchemy.com/v2/<private_key>

и использовал его в переменной:

NEXT_PUBLIC_BASE_RPC_URL=<private RPC>

Результат:

  • время ответа упало до 90–120 мс;

  • ни одного пропущенного eth_call;

  • стабильный work в мобильных окружениях;

  • marketplace начал грузиться без ошибок;

  • prizePool на UI стал обновляться стабильно.

Mini-apps очень чувствительны к задержкам, и разница между публичным и приватным RPC ощущается очень сильно

Админ-панель: технически важные детали

В админке есть:

  • просмотр ownerBalance

  • управление комиссией

  • ручной розыгрыш пула

  • кнопка сброса tokenId

  • скрытие постов - для модерации

  • пометка звездных постов - для пуша звездных пользователей блокчейна

Безопасность:

  • все админ-операции проверяются по адресу ADMIN_ADDRESS

  • front блокирует доступ, но главный контроль — on-chain проверка onlyOwner

Манифест mini-app и ассеты

Файл: public/.well-known/farcaster.json

Свойства:

{
  "name": "The Wall Base",
  "description": "Farcaster mini-app: NFT posts, marketplace, prize pool",
  "iconUrl": "/200_00000.png",
  "splashUrl": "/1024_00000.png",
  "webhookUrl": null,
  "requestedPermissions": [],
  "developer": "base.app/<username>"
}

Важно: Стадия разработки должна использовать noindex=true, иначе кастомный манифест будет кэшироваться у клиента.

Частые проблемы и решения

1. wagmi + mini-app окружение

Нельзя полагаться на window.ethereum. Используется кастомный Base App connector.

2. BigInt в React state

Mini-app WebView может падать, если BigInt попадает в JSON.

Решение — сериализация via string.

3. ETH → USD курс

CMC часто блокирует частые запросы. Решение: кеширование на 30 минут.

Деплой: контракты и фронт

Контракты (Hardhat)

npx hardhat run scripts/deploy.js --network base
npx hardhat verify --network base <ForumNFT> "<deployer>"

Фронт (Vercel)

  • root: /miniapp

  • build: npm run build

  • env: приватный RPC + адреса контрактов

Заключение

The Wall Base стал для меня экспериментом, который неожиданно вышел в полноценный продукт:

  • социальная сеть → но каждый пост — NFT;

  • marketplace → но встроенный в мини-апп;

  • prize-pool → полностью живёт on-chain;

  • интеграция Base App → работает как мобильное нативное приложение.

Главный вывод: Base Mini-apps открывают огромную нишу для гибридных dApps, если вас заинтересовала разработка на baseapp воспользуйтесь ссылками ниже

Приложение BaseAPP (пока доступно по инвайту, но скоро это станет не обязательно)
Документация
The Wall Base