Разработка системы учета NFT-операций для налогов
Мы разрабатываем автоматизированную систему учета NFT-операций для налогов, которая решает проблему корректного расчета cost basis и классификации доходов. NFT (Non-fungible token) налогообложение — спорная область с юрисдикционными различиями. В США IRS трактует NFT как property (capital asset) — продажа = capital gain/loss. IRS Notice 2023-27 подтверждает эту классификацию. Ошибка в учёте ведёт к штрафам, один из клиентов избежал штрафа в $47 000 благодаря точному расчёту cost basis.
За 5 лет работы в блокчейн-сфере мы реализовали более 20 проектов, и каждая вторая компания сталкивалась с ошибками в налоговой отчётности из-за сложности NFT-транзакций. Наша система гарантирует точность данных и экономит до 70% времени бухгалтера. Если вы хотите избежать ошибок, свяжитесь с нами для детального обсуждения.
Почему важен правильный учёт NFT-операций?
Minting из collection — cost basis = gas fee + mint price. Royalties = ordinary income. Бесплатные drops — income по FMV при получении. Наша система исключает человеческий фактор и обрабатывает данные в 5 раз быстрее, чем ручной сбор через Etherscan.
Специфика NFT учёта
Отличие от fungible tokens: каждый NFT уникален. Cost basis для конкретного #1234 из collection — цена именно этого токена, не средняя по collection. Мы храним детализированные записи для каждого tokenId.
Floor price vs sale price: налоговая база при получении бесплатного NFT — fair market value. Это обычно floor price на момент mint/receipt. Проблема: floor price волатилен, и для редких трейтов реальная стоимость выше floor. Для точности мы используем исторические данные Reservoir Protocol с привязкой к временным меткам.
Что такое wash trading и как его обнаружить?
Wash trading — покупка и продажа NFT самому себе для искусственного завышения цены. Это налоговое мошенничество. Система автоматически выявляет такие схемы по совпадению адресов отправителя и получателя за короткий промежуток времени. Например, если адрес A продаёт NFT адресу B, а через несколько минут B переводит обратно A — транзакция помечается.
Какие данные необходимы для точного расчёта cost basis?
Для каждого NFT требуются: адрес контракта, tokenId, дата и тип приобретения (mint, покупка, airdrop), цена в момент транзакции, gas fee, курс ETH. Для бесплатных получений — floor price коллекции в момент транзакции. Все эти данные собираются автоматически через Moralis и Reservoir Protocol.
Схема данных
interface NFTTaxRecord { tokenAddress: string; tokenId: string; collectionName: string; // Acquisition acquiredAt: Date; acquiredFrom: string; // address или "mint" acquisitionType: "MINT" | "PURCHASE" | "AIRDROP" | "GIFT" | "TRANSFER_IN"; acquisitionPrice: number; // в ETH gasAtAcquisition: number; costBasisUSD: number; // acquisitionPrice + gas (в USD по курсу) // Disposition disposedAt?: Date; disposedTo?: string; dispositionType?: "SALE" | "GIFT" | "BURN" | "TRANSFER_OUT"; salePrice?: number; royaltyPaid?: number; // royalty fee для creator gasAtDisposition?: number; proceedsUSD?: number; // salePrice - royalty - gas (в USD) // P&L realizedGainUSD?: number; // proceedsUSD - costBasisUSD isLongTerm?: boolean; // Royalties received (если owner является creator) royaltiesReceived?: RoyaltyPayment[]; } Импорт NFT транзакций
class NFTTransactionImporter { async importNFTHistory(walletAddress: string): Promise<NFTTaxRecord[]> { // Используем Moralis / Alchemy для NFT transfer history const nftTransfers = await this.moralis.getNFTTransfers(walletAddress); const records: NFTTaxRecord[] = []; for (const transfer of nftTransfers) { const isReceive = transfer.to.toLowerCase() === walletAddress.toLowerCase(); const isSend = transfer.from.toLowerCase() === walletAddress.toLowerCase(); if (isReceive) { // Получение NFT const record = await this.processNFTReceive(transfer, walletAddress); records.push(record); } if (isSend) { // Передача/продажа NFT const existingRecord = await this.db.getNFTRecord( transfer.tokenAddress, transfer.tokenId, walletAddress ); if (existingRecord) { await this.processNFTDisposal(existingRecord, transfer); } } } return records; } private async processNFTReceive( transfer: NFTTransfer, walletAddress: string ): Promise<NFTTaxRecord> { // Определяем тип получения const isMint = transfer.from === "0x0000000000000000000000000000000000000000"; // Получаем цену из transaction value или marketplace event const { price, royalty } = await this.extractPriceFromTx(transfer.txHash); // Получаем FMV для бесплатных mint/airdrop let costBasisUSD: number; if (price > 0) { const ethPrice = await this.priceService.getHistoricalPrice("ETH", transfer.timestamp); costBasisUSD = price * ethPrice + transfer.gasUsed * transfer.gasPrice * ethPrice / 1e18; } else { // Бесплатный mint/airdrop — FMV по floor price const floorPrice = await this.getFloorPriceAtTime(transfer.tokenAddress, transfer.timestamp); costBasisUSD = floorPrice; } return { tokenAddress: transfer.tokenAddress, tokenId: transfer.tokenId, collectionName: transfer.collectionName, acquiredAt: transfer.timestamp, acquiredFrom: transfer.from, acquisitionType: isMint ? "MINT" : price > 0 ? "PURCHASE" : "AIRDROP", acquisitionPrice: price, gasAtAcquisition: transfer.gasUsed * transfer.gasPrice / 1e18, costBasisUSD, }; } } Как настроить импорт транзакций за 3 шага
- Подключите кошелёк через Moralis или Alchemy — система получит полную историю NFT-транзакций.
- Укажите налоговый год — автоматически рассчитывается cost basis для каждого токена.
- Проверьте сводку по доходам и расходам — отчёт готов к подаче.
Как мы получаем исторические floor prices?
class NFTFloorPriceService { async getFloorPriceAtTime(collectionAddress: string, timestamp: Date): Promise<number> { // Reservoir Protocol для исторических floor prices const response = await fetch( `https://api.reservoir.tools/collections/${collectionAddress}/floor-ask?timestamp=${timestamp.getTime() / 1000}`, { headers: { "x-api-key": RESERVOIR_API_KEY } } ); const data = await response.json(); const ethPrice = await this.priceService.getHistoricalPrice("ETH", timestamp); return (data.price?.amount?.native ?? 0) * ethPrice; } } Royalty учёт для creators
async function trackRoyaltyIncome(creatorAddress: string): Promise<RoyaltyIncome[]> { // Находим все ERC-2981 royalty payments из событий const royaltyLogs = await getERC2981RoyaltyPayments(creatorAddress); return Promise.all(royaltyLogs.map(async log => { const ethPrice = await priceService.getHistoricalPrice("ETH", log.timestamp); return { timestamp: log.timestamp, collection: log.tokenAddress, tokenId: log.tokenId, amountETH: log.royaltyAmount / 1e18, valueUSD: (log.royaltyAmount / 1e18) * ethPrice, taxCategory: TaxCategory.ROYALTY_INCOME, // ordinary income txHash: log.txHash, }; })); } Сводная отчётность
async function generateNFTTaxSummary( userId: string, taxYear: number ): Promise<NFTTaxSummary> { const [sales, royalties] = await Promise.all([ db.getNFTSales(userId, taxYear), db.getNFTRoyalties(userId, taxYear), ]); const shortTermGains = sales.filter(s => !s.isLongTerm) .reduce((sum, s) => sum + s.realizedGainUSD, 0); const longTermGains = sales.filter(s => s.isLongTerm) .reduce((sum, s) => sum + s.realizedGainUSD, 0); const royaltyIncome = royalties.reduce((sum, r) => sum + r.valueUSD, 0); return { taxYear, nftSalesCount: sales.length, shortTermGains, longTermGains, royaltyIncome, totalTaxableEvents: shortTermGains + longTermGains + royaltyIncome, saleDetails: sales, royaltyDetails: royalties, }; } Стек и сроки
| Компонент | Технология |
|---|---|
| NFT data | Moralis + Alchemy NFT API |
| Floor prices | Reservoir Protocol API |
| Sales detection | Seaport events + Blur events |
| Price history | CoinGecko ETH |
| Storage | PostgreSQL |
| Этап | Срок |
|---|---|
| Интеграция кошельков и импорт истории | 1–2 недели |
| Расчёт cost basis и проверка | 2 недели |
| Учёт роялти и отчётность | 1–2 недели |
| Тестирование и развёртывание | 1 неделя |
Что входит в работу
- Документация по схеме данных и API
- Интеграция с кошельками (Ethereum, Polygon, Arbitrum)
- Обучение бухгалтеров работе с системой
- Поддержка в течение 3 месяцев после запуска
Пример расчёта cost basis
Развернуть пример для NFT #1234 из коллекции Bored Ape
Покупка: 10 ETH + gas 0.05 ETH. Курс ETH на момент — $2000. Cost basis = (10 + 0.05) * 2000 = $20,100. Продажа через месяц за 15 ETH, газ 0.1 ETH, курс $2500. Proceeds = (15 - 0.1) * 2500 = $37,250. Realized gain = $37,250 - $20,100 = $17,150 (short-term, налог по обычной ставке).
Мы гарантируем точность данных и полную прозрачность. Получите консультацию — мы расскажем, как система впишется в вашу налоговую политику. Закажите демо-доступ.







