Розробка системи обліку NFT-операцій для податків

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи обліку NFT-операцій для податків
Складний
~1-2 тижні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

Система обліку NFT-операцій для податків

Оподаткування NFT — спірна область з юрисдикційними відмінностями. У США IRS трактує NFT як власність (capital asset) — продаж = приріст/збиток капіталу. Мінтинг з колекції — cost basis = комісія газу + ціна мінту. Royalties = звичайний дохід. Безплатні drops — дохід за справедливою вартістю при отриманні.

Специфіка обліку NFT

Відмінність від fungible токенів: кожен NFT унікальний. Cost basis для конкретного #1234 з колекції — ціна саме цього токена, не середня для колекції.

Floor price проти ціни продажу: податкова база при отриманні безплатного NFT — справедлива ринкова вартість. Зазвичай це floor price на час мінту/отримання. Проблема: floor price волатильна, а для рідких ознак реальна вартість вище floor.

Wash trading: покупка та продаж NFT самому собі для штучного завищення ціни — податкове шахрайство.

Схема даних

interface NFTTaxRecord {
  tokenAddress: string;
  tokenId: string;
  collectionName: string;
  
  // Набуття
  acquiredAt: Date;
  acquiredFrom: string;     // адреса або "mint"
  acquisitionType: "MINT" | "PURCHASE" | "AIRDROP" | "GIFT" | "TRANSFER_IN";
  acquisitionPrice: number; // у ETH
  gasAtAcquisition: number;
  costBasisUSD: number;     // acquisitionPrice + газ (у USD за курсом)
  
  // Розпорядження
  disposedAt?: Date;
  disposedTo?: string;
  dispositionType?: "SALE" | "GIFT" | "BURN" | "TRANSFER_OUT";
  salePrice?: number;
  royaltyPaid?: number;     // royalty комісія творцю
  gasAtDisposition?: number;
  proceedsUSD?: number;     // salePrice - royalty - gas (у USD)
  
  // P&L
  realizedGainUSD?: number; // proceedsUSD - costBasisUSD
  isLongTerm?: boolean;
  
  // Royalties отримані (якщо власник — творець)
  royaltiesReceived?: RoyaltyPayment[];
}

Імпорт NFT транзакцій

class NFTTransactionImporter {
  async importNFTHistory(walletAddress: string): Promise<NFTTaxRecord[]> {
    // Використовуємо Moralis / Alchemy для історії передач NFT
    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";
    
    // Отримуємо ціну з value транзакції або marketplace подіяю
    const { price, royalty } = await this.extractPriceFromTx(transfer.txHash);
    
    // Отримуємо FMV для безплатного мінту/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 {
      // Безплатний мінт/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,
    };
  }
}

Джерела Floor Price

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;
  }
}

Облік Royalties для творців

async function trackRoyaltyIncome(creatorAddress: string): Promise<RoyaltyIncome[]> {
  // Знаходимо всі платежі royalty ERC-2981 з подій
  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, // звичайний дохід
      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 дані Moralis + Alchemy NFT API
Floor prices Reservoir Protocol API
Виявлення продажів Seaport подіїї + Blur подіїї
Історія цін CoinGecko ETH
Сховище PostgreSQL

Система обліку NFT податків з імпортом, відстеженням floor price, доходом royalties та мультиюрисдикційними звітами: 4-6 тижнів розробки.