Skip to content

Zora IPFS upload CID differs from other IPFS services #527

@azf20

Description

@azf20

I wanted to pin Coin images and metadata JSON files to additional IPFS services for redundancy, but I am finding that the Zora IPFS upload gives a different CID for the same content.

Below is a minimal reproduction script for Filebase as an alternative provider. I have additionally verified with Pinata and bgipfs.com (which return the same CID as Filebase).

/**
 * Zora IPFS Upload Bug Report - Proof of Concept
 *
 * This script demonstrates that Zora's IPFS upload endpoint behaves differently
 * from other IPFS services when using the same multipart form data approach.
 *
 * Issue: Zora produces different CIDs and file sizes compared to other IPFS services
 * for the same uploaded content.
 */

import fs from 'node:fs/promises'
import path from 'node:path'

// Test file - replace with your actual file path
const TEST_FILE_PATH = './FILE_TO_UPLOAD'

// API endpoints
const ZORA_UPLOAD_URL = 'https://ipfs-uploader.zora.co/api/v0/add?cid-version=1'
const FILEBASE_UPLOAD_URL = 'https://rpc.filebase.io/api/v0/add?cid-version=1'

// Authentication - replace with your actual keys
const ZORA_JWT = 'JWT_GOES_HERE'
const FILEBASE_API_KEY = 'API_KEY_GOES_HERE'

async function uploadToService(url, authHeader, serviceName) {
  try {
    // Read the test file
    const fileBuffer = await fs.readFile(TEST_FILE_PATH)
    const filename = path.basename(TEST_FILE_PATH)

    console.log(`\n--- ${serviceName} Upload ---`)

    // Create multipart form data
    const formData = new FormData()
    const file = new File([fileBuffer], filename, {type: 'image/jpeg'})
    formData.append('file', file)

    // Upload to service
    const response = await fetch(url, {
      body: formData,
      headers: {
        Accept: '*/*',
        ...authHeader,
      },
      method: 'POST',
    })

    if (!response.ok) {
      const errorText = await response.text()
      throw new Error(`HTTP ${response.status}: ${errorText}`)
    }

    const result = await response.json()

    console.log(`Upload successful!`)
    console.log(`Response CID: ${result.Hash || result.cid}`)
    console.log(`Response size: ${result.Size || result.size} bytes`)

    return {
      cid: result.Hash || result.cid,
      responseSize: Number.parseInt(result.Size || result.size, 10),
      service: serviceName,
    }
  } catch (error) {
    console.error(`${serviceName} upload failed:`, error.message)
    return {
      error: error.message,
      service: serviceName,
    }
  }
}

async function main() {
  console.log('=== Zora IPFS Upload Bug Report ===')
  console.log(`Test file: ${TEST_FILE_PATH}`)

  // Check if test file exists
  try {
    await fs.access(TEST_FILE_PATH)
  } catch {
    console.error(`\n❌ Test file not found: ${TEST_FILE_PATH}`)
    console.log('Please update TEST_FILE_PATH with a valid file path')
    return
  }

  // Upload to both services
  const zoraResult = await uploadToService(ZORA_UPLOAD_URL, {Authorization: `Bearer ${ZORA_JWT}`}, 'Zora')

  const filebaseResult = await uploadToService(
    FILEBASE_UPLOAD_URL,
    {Authorization: `Bearer ${FILEBASE_API_KEY}`},
    'Filebase',
  )

  // Compare results
  console.log('\n=== COMPARISON RESULTS ===')

  if (zoraResult.error || filebaseResult.error) {
    console.log('❌ One or more uploads failed')
    if (zoraResult.error) console.log(`Zora error: ${zoraResult.error}`)
    if (filebaseResult.error) console.log(`Filebase error: ${filebaseResult.error}`)
    return
  }

  console.log(`\nZora:`)
  console.log(`  Response size: ${zoraResult.responseSize} bytes`)
  console.log(`  CID: ${zoraResult.cid}`)

  console.log(`\nFilebase:`)
  console.log(`  Response size: ${filebaseResult.responseSize} bytes`)
  console.log(`  CID: ${filebaseResult.cid}`)

  // Analysis
  console.log('\n=== ANALYSIS ===')

  if (zoraResult.responseSize === filebaseResult.responseSize) {
    console.log('✅ Same file sizes returned')
  } else {
    console.log(
      `❌ Different file sizes: Zora (${zoraResult.responseSize}) vs Filebase (${filebaseResult.responseSize})`,
    )
  }

  if (zoraResult.cid === filebaseResult.cid) {
    console.log('✅ Same CID generated')
  } else {
    console.log('❌ Different CIDs generated for the same content')
  }
}

main().catch(console.error)

Example output:

=== Zora IPFS Upload Bug Report ===
Test file: ./bafybeiazilb4btabwlatsqsnyop4itqfy3f2onqnhw6d7gyb5pjew2a66i.jpeg

--- Zora Upload ---
Upload successful!
Response CID: bafybeiazilb4btabwlatsqsnyop4itqfy3f2onqnhw6d7gyb5pjew2a66i
Response size: 282787 bytes

--- Filebase Upload ---
Upload successful!
Response CID: bafybeibnpauqvbq37bq2hpbq2gjechf4aubbidey6q5ex6zyncgpmkjutm
Response size: 282823 bytes

=== COMPARISON RESULTS ===

Zora:
  Response size: 282787 bytes
  CID: bafybeiazilb4btabwlatsqsnyop4itqfy3f2onqnhw6d7gyb5pjew2a66i

Filebase:
  Response size: 282823 bytes
  CID: bafybeibnpauqvbq37bq2hpbq2gjechf4aubbidey6q5ex6zyncgpmkjutm

=== ANALYSIS ===
❌ Different file sizes: Zora (282787) vs Filebase (282823)
❌ Different CIDs generated for the same content

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions