import deepEqual from 'fast-deep-equal/es6'
const mirroredExtensions = ['.json']
* Send file to IPFS network.
* @param {string} fileName - The name that will be used to store the file. This is useful to preserve extension type.
* @param {ArrayBuffer} data - The raw data from the file to upload.
* @returns {object} ipfs response. Should include the hash and path of the stored item.
export default async function ipfsPublish(fileName, data) {
if (!mirroredExtensions.some(ext => fileName.endsWith(ext)))
return publishToKlerosNode(fileName, data)
const [klerosResult, theGraphResult] = await Promise.all([
publishToKlerosNode(fileName, data),
publishToTheGraphNode(fileName, data),
// Pin to your own ipfs node here as well.
if (!deepEqual(klerosResult, theGraphResult)) {
console.warn('IPFS upload result is different:', {
throw new Error('IPFS upload result is different.')
* Send file to IPFS network via the Kleros IPFS node
* @param {string} fileName - The name that will be used to store the file. This is useful to preserve extension type.
* @param {ArrayBuffer} data - The raw data from the file to upload.
* @returns {object} ipfs response. Should include the hash and path of the stored item.
async function publishToKlerosNode(fileName, data) {
const buffer = await Buffer.from(data)
const url = `${process.env.REACT_APP_IPFS_GATEWAY}/add`
const response = await fetch(url, {
'content-type': 'application/json'
const body = await response.json()
* Send file to IPFS network via The Graph hosted IPFS node
* @param {string} fileName - The name that will be used to store the file. This is useful to preserve extension type.
* @param {ArrayBuffer} data - The raw data from the file to upload.
* @returns {object} ipfs response. Should include the hash and path of the stored item.
async function publishToTheGraphNode(fileName, data) {
const url = `${process.env.REACT_APP_HOSTED_GRAPH_IPFS_ENDPOINT}/api/v0/add?wrap-with-directory=true`
const payload = new FormData()
payload.append('file', new Blob([data]), fileName)
const response = await fetch(url, {
const result = await jsonStreamToPromise(response.body)
return result.map(({ Name, Hash }) => ({
* Accumulates a JSON stream body into an array of JSON objects.
* @param {ReadableStream} stream The stream to read from.
* @returns {Promise<any>} An array of all JSON objects emitted by the stream.
async function jsonStreamToPromise(stream) {
const reader = stream.getReader()
const decoder = new TextDecoder('utf-8')
const result = new Promise((resolve, reject) => {
deferred.resolve = resolve
const start = async () => {
.then(({ done, value }) => {
if (done) return deferred.resolve(acc)
// Each `read` can produce one or more lines...
const lines = decoder.decode(value).split(/\n/)
.filter(line => line.trim() !== '')
.map(line => JSON.parse(line))
.catch(err => deferred.reject(err))