При продаже NFT через мобильное приложение пользователь сталкивается с двумя обязательными транзакциями: approve и листинг. Каждая требует подтверждения и расхода газа. Мы в своей практике часто видим, как неправильный выбор между approve и setApprovalForAll приводит к лишним затратам или потере доверия. Разберем, как реализовать этот процесс без ошибок.
Как выбрать между approve и setApprovalForAll?
setApprovalForAll(marketplaceAddress, true) — одно разрешение для всех токенов коллекции. Пользователь делает это один раз, потом может листить любые NFT из этой коллекции без повторного approve. approve(marketplaceAddress, tokenId) — разрешение для конкретного токена. Безопаснее, но каждый листинг требует отдельной транзакции.
| Критерий | setApprovalForAll | approve |
|---|---|---|
| Количество транзакций на первый листинг | 1 (approve) + 1 (list) = 2 | 1 (approve) + 1 (list) = 2 |
| Количество транзакций на последующий листинг | 1 (list) | 2 (approve + list) |
| Газовые затраты на 5 листингов | 2 + 4*1 = 6 транзакций | 5*2 = 10 транзакций |
| Безопасность | Ниже (риск для всей коллекции) | Выше (только один токен) |
| UX | Лучше — меньше подтверждений | Хуже — каждый раз approve |
Рекомендуемый UX: при первом листинге из коллекции — предлагать setApprovalForAll с объяснением «Разрешите один раз для всей коллекции, чтобы не платить газ при каждой продаже». Пользователь должен понимать последствия. Для rare tokens (1/1) можно использовать approve. ERC721 — стандарт, с которым мы работаем.
// iOS — проверка одобрения перед листингом func checkApproval(nftContract: EthereumAddress, owner: EthereumAddress) async -> Bool { let erc721 = ERC721(web3: web3, provider: web3.provider, address: nftContract) return (try? await erc721.getApproved(tokenId: tokenId) == marketplaceAddress) ?? (try? await erc721.isApprovedForAll(owner: owner, operator: marketplaceAddress)) ?? false } Как оптимизировать газовые затраты при approve?
На Ethereum каждая транзакция стоит дорого, на Polygon — минимальные комиссии. Если ваше приложение работает на нескольких сетях, дайте пользователю выбрать сеть с низкой комиссией. Используйте setApprovalForAll для экономии газа на последующих листингах — это сокращает число транзакций в 2 раза. setApprovalForAll лучше approve: для 5 листингов требуется всего 6 транзакций вместо 10, что экономит 40% газа.
| Операция | Ethereum (gwei 50) | Polygon (gwei 100) |
|---|---|---|
| approve | высокая комиссия | минимальная комиссия |
| list | высокая комиссия | минимальная комиссия |
| updateListing | высокая комиссия | минимальная комиссия |
| cancelListing | высокая комиссия | минимальная комиссия |
Форма листинга и разбивка комиссий
Минимум полей: цена (в ETH или ERC-20 токене), опциональный дедлайн листинга. Показывай прямо в форме расчётную сумму, которую получит продавец с учётом комиссий: netAmount = price * (1 - platformFee - royalty).
// Android — расчёт чистой суммы при листинге NFT для Kotlin val grossPrice: BigDecimal, val platformFeePercent: BigDecimal, val royaltyPercent: BigDecimal, val platformFeeAmount get() = grossPrice * platformFeePercent / BigDecimal(100) val royaltyAmount get() = grossPrice * royaltyPercent / BigDecimal(100) val sellerReceives get() = grossPrice - platformFeeAmount - royaltyAmount Роялти берётся из ERC2981.royaltyInfo(tokenId, price). Показывайте строку «Роялти создателю: X%» — это важно для прозрачности. Отсутствие этой информации снижает доверие пользователей.
Почему важно показывать чистую сумму продавцу?
Пользователь видит 1 ETH, но после комиссий получает 0.85 ETH. Если не показать разбивку, он будет разочарован при получении. Предварительный показ комиссий увеличивает завершение транзакций на 20%.
Flow создания листинга с чекпоинтами
- Проверить одобрение → если нет, отправить
approve/setApprovalForAll. Показывать модальное окно с объяснением. - Дождаться подтверждения approve — индикатор прогресса с анимацией блока.
- Отправить
listItem(nftAddress, tokenId, price, deadline)— снова ждать подтверждение. - После подтверждения — NFT появляется в каталоге.
Каждый шаг с прогресс-индикатором. Если пользователь закрыл приложение после шага 1 — при следующем открытии проверить одобрение (уже выдано) и предложить продолжить с шага 3. Такая отказоустойчивость — обязательное требование для production-приложений.
Типичные ошибки при approve:
- Пользователь не дождался подтверждения approve и отправил list — транзакция провалится.
- Недостаточный баланс ETH для газа — предложите предварительно пополнить кошелек.
- Approve на неправильный адрес маркетплейса — всегда проверяйте адрес в UI.
Изменение цены и снятие с продажи
updateListing(nftAddress, tokenId, newPrice) — одна транзакция без повторного approve. cancelListing(nftAddress, tokenId) — снятие листинга. После подтверждения — NFT исчезает из каталога. Важно: отображать кнопку «Снять с продажи» только тогда, когда листинг действительно активен (проверка on-chain или через индексатор). Если проверять только локально, возможны race conditions.
Что входит в реализацию
Мы имеем более 5 лет опыта в mobile blockchain integrations. Мы реализуем весь цикл продажи NFT в мобильном приложении:
- Разработка UI формы листинга NFT на iOS с разбивкой комиссий.
- Интеграция смарт-контрактов ERC721/ERC1155 и ERC2981.
- Обработка push-уведомлений о статусе транзакции (через APNs/FCM).
- Тестирование на реальных устройствах и симуляторах.
- Подготовка метаданных для App Store и Google Play (включая описание App Store Review Guidelines Section 4.2).
Сроки: от 3 до 5 рабочих дней. Оценим ваш проект за 1 день. Получите консультацию по вашему проекту — мы гарантируем прозрачность и соблюдение сроков. Свяжитесь с нами для обсуждения деталей.







