Build
ONCHAIN STORAGE
Features
Onchain Folders

Onchain Folders

Onchain folders are powerful way to organize transactions on Irys. Use them to reference onchain data by logical names instead of transaction IDs.

Why Use Onchain Folders?

  • Logical Grouping: Create organized and readable structures for onchain data.
  • Human-Readable Referencing: Replace transaction IDs with logical names, improving accessibility.
  • Cross-Ownership Grouping: Include any transactions on Irys, even if they weren’t created by you.
  • Flexibility: Add new files to existing folders at any time.

How The Irys Gateway Resolves Onchain Folders

Each onchain folder is uniquely identifiable by a manifest ID. To download transactions in an onchain folder, request them from the Irys gateway using a URL formatted as:

https://gateway.irys.xyz/:manifestId/:pathName

The gateway then:

  1. Looks up the manifest by ID.
  2. Looks in the manifest to see if the path exists.
  3. Returns the transaction associated with the path if found.
  4. Returns 404 if not found.

For example, if you have a manifest with ID 8eNpkShMwdbiNBtGuVGBKp8feDZCa21VppX2eDi3eLME containing the following:

Tx IDPath Name
DTMcqFqwaDukaYxs7iK2fa6CuMtyi7sN93rBGSAa13Ugfoo1.png
5TQU2ETHGRPjJKPoeQkkgMB6zRpK8ptheWF8jdkbtJHRfoo2.png
8nond6kkdYS14QjA5tZNCRDQQrgVNd7gdhx3L4XRJD1bfoo3.png

https://gateway.irys.xyz/8eNpkShMwdbiNBtGuVGBKp8feDZCa21VppX2eDi3eLME/foo1.png

Creating Onchain Folders

Automatically

When you upload groups of files using the Irys SDK's uploadFolder() function or the CLI's upload-dir command, an onchain folder for you is automatically created for you. The return value contains the manifest ID, which can be combined with the original file names as above.

Manually

To manually create an onchain folder:

  1. Create a JavaScript Map object where each entry maps a unique transaction ID to a unique path. (Paths are arbitrary; you can use anything that conforms to valid URL syntax.)
  2. Create a Manifest object by passing the Map object to irys.uploader.generateFolder().
  3. Upload the Manifest object to Irys.
const createOnchainFolder = async () => {
  const irysUploader = await getIrysUploader();
 
  // You can map ANY logical name to ANY transaction ID
  const map = new Map();
  map.set("image-1.png", "DTMcqFqwaDukaYxs7iK2fa6CuMtyi7sN93rBGSAa13Ug");
  map.set("image-2.png", "5TQU2ETHGRPjJKPoeQkkgMB6zRpK8ptheWF8jdkbtJHR");
  map.set("image-3.png", "8nond6kkdYS14QjA5tZNCRDQQrgVNd7gdhx3L4XRJD1b");
 
  const manifest = await irysUploader.uploader.generateFolder({ items: map });
  console.log({ manifest });
 
  const tags = [
    { name: "Type", value: "manifest" },
    { name: "Content-Type", value: "application/x.irys-manifest+json" },
  ];
  const receipt = await irysUploader.upload(JSON.stringify(manifest), { tags });
  console.log(`Manifest uploaded to https://gateway.irys.xyz/${receipt.id}`);
  console.log(`File 1 available at https://gateway.irys.xyz/${receipt.id}/image-1.png`);
  console.log(`File 2 available at https://gateway.irys.xyz/${receipt.id}/image-2.png`);
  console.log(`File 3 available at https://gateway.irys.xyz/${receipt.id}/image-3.png`);
};

Mutable Onchain Folders

Mutable onchain folders let you add new files to an existing folder after it’s created. By using a single, static URL that always points to the most recent version, you can expand an NFT collection, add updated documents, or manage evolving datasets without needing to change the original reference. This approach ensures consistency while allowing dynamic updates over time, with the security that only the original creator can modify the folder's contents.

How to Create Mutable Onchain Folders

  1. Upload the Initial Folder: Use the SDK or CLI to upload your initial set of files. This will create a base manifest ID that uniquely identifies the folder.

  2. Reference Files Using a Mutable URL: Reference files in this folder using a URL formatted as https://gateway.irys.xyz/mutable/:manifestId/:fileName. This URL will always resolve to the most recent version of the folder.

  3. Upload New Files: When you need to add new files, upload them individually using the SDK or CLI.

  4. Create a Onchain Folder: Manually create a new onchain folder that includes both the original files and any new files as outlined above.

  5. Tag the New Manifest: Upload the new onchain folder to Irys, tagging it with Root-TX equal to the original manifest ID. This links the new onchain folder to the original folder; the "mutable" URL remains consistent.

  6. Access the Updated Folder: The URL https://gateway.irys.xyz/mutable/:manifestId/:fileName will now point to the latest version of the folder, including all newly added files.

import { Uploader } from "@irys/upload";
import { Ethereum } from "@irys/upload-ethereum";
import "dotenv/config";
import fetch from "node-fetch"; 
 
const getIrysUploader = async () => {
  const irysUploader = await Uploader(Ethereum).withWallet(process.env.PRIVATE_KEY);
  return irysUploader;
};
 
const downloadOriginalManifest = async (originalManifestId: string) => {
  try { 
    const response = await fetch(`https://gateway.irys.xyz/${originalManifestId}`);
    if (!response.ok) throw new Error("Failed to fetch original manifest");
    return response.json();
  } catch (error) {
    console.error("Error downloading original manifest", error);
    throw error;
  }
};
 
const appendToManifest = (originalManifest: any, newFiles: Map<string, string>)  => {
  newFiles.forEach((txId, fileName) => {
    originalManifest.paths[fileName] = { id: txId };
  });
  return originalManifest;
};
 
const uploadManifest = async (manifest: any, originalManifestId: string): Promise<void> => {
  const irysUploader = await getIrysUploader();
 
  const manifestTags = [
    { name: "Type", value: "manifest" },
    { name: "Content-Type", value: "application/x.irys-manifest+json" },
    { name: "Root-TX", value: originalManifestId },
  ];
 
  try {
    const manifestResponse = await irysUploader.upload(JSON.stringify(manifest), { tags: manifestTags });
    console.log(`Manifest uploaded ==> https://gateway.irys.xyz/mutable/${originalManifestId}`);
  } catch (e) {
    console.error("Error uploading manifest", e);
  }
};
 
const main = async () => {
  try {
    // Your original manifest ID
    const originalManifestId = "8eNpkShMwdbiNBtGuVGBKp8feDZCa21VppX2eDi3eLME";
 
    // Step 1: Download the original manifest
    const originalManifest = await downloadOriginalManifest(originalManifestId);
 
    // Step 2: Prepare new files to add to the manifest
    const newFiles = new Map<string, string>();
    newFiles.set("new-1.png", "4pTiwGwur38s4vyVD8EERxDYgAGDM8kyzEh9c5QPF9Zw");
    newFiles.set("new-2.png", "BieiKJE1Nh6ydCYqxmDjHFGzuG7enRr6NYmKisqSwUQo");
 
    // Step 3: Append new files to the manifest
    const updatedManifest = appendToManifest(originalManifest, newFiles);
 
    // Step 4: Upload the updated manifest
    await uploadManifest(updatedManifest, originalManifestId);
  } catch (e) {
    console.error("Error in main execution", e);
  }
};
 
main();