Back to blog

IPCM: Solving Dynamic IPFS Content with Blockchain Smart Contracts

IPCM: Solving Dynamic IPFS Content with Blockchain Smart Contracts

Steve

Over the past few years, IPFS has become the default file network for offchain storage. When you combine immutable files with an immutable blockchain ledger, you get a powerful combination. While immutability is key to IPFS, it has also become one of its downsides. There are many crypto and blockchain applications that need the flexibility of mutable files, and they usually end up resorting to a something centralized like S3 which removes the layer of verifiability. There have been other solutions to this problem including IPNS (InterPlanetary Name Service) which is a native IPFS solution. Unfortunately, it has historically been slow and not very user or developer friendly.

This is why Pinata has developed a simple yet elegant open source solution: IPCM, or InterPlanetary CID Mapping. IPCM is a smart contract standard that can be deployed to any EVM chain, and takes advantage of immutable ledgers and cryptography to create dynamic IPFS content. In this post, we’ll go over the smart contract in question as well as some practical applications of IPCM.

Smart Contracts

The IPCM smart contract is truly quite simple and can be see in just a few lines of code below:

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.23;

contract IPCM is Ownable {
    // State to hold the CID mapping
    string private cidMapping;

    constructor(address owner) Ownable(owner) {}

    // Event for updates
    event MappingUpdated(string value);

    // Function to update mapping
    function updateMapping(string memory value) public onlyOwner {
        cidMapping = value;
        emit MappingUpdated(value);
    }

    // Function to get current mapping
    function getMapping() public view returns (string memory) {
        return cidMapping;
    }
}

Let’s go over each detail piece by piece to understand how the contract works.

  • IPCM is Ownable- The only dependency that the contract uses is the Ownable access control by OpenZeppelin, which helps us lock down certain functions to an owner role. In our case, that’s the owner of the contract.
  • private string cidMapping - This is a simple string variable state that stores a CID reference, and acts as a pointer of the latest state of our content.
  • constructor(address owner) Ownable(owner) {} - Here we have the constructor for our contract that takes in an address to be the owner of the contract. This address will be able to use the updateMapping function.
  • event MappingUpdated(string value) - The smart contract event here is crucial as it will help provide the history of the contract without expanding onchain storage; more on this in the next bullet.
  • function updateMapping - This is one of only two functions in the contract, and it’s used to update the cidMapping state stored in the contract with the new value passed in. It also runs emit MappingUpdated(value) which will fire the smart contract event for the new state of cidMapping. This function is also only callable by the owner of the contract.
  • function getMapping This is a simple function that will return the state of cidMapping so anyone can view the latest version of the content.

Putting the pieces together, we have a smart contract that anyone can deploy, set the CID, update the CID, and see the latest version of the CID. As mentioned before, the smart contract events are also crucial here as they can be indexed to create a version history of the content onchain. In this IPCM model, the smart contract is used as the source of truth for a piece of content, secured by the cryptography of blockchain and tied to the onchain identity of the user.

Practical Use Case

One of the biggest use cases for dynamic IPFS content is static website hosting. This has always been a challenge historically because anytime you updated your website it would result in a new CID. You would either have to give the website viewers the new CID or you would need to use something like IPNS, which would be too slow. With IPCM, a user can upload the new version of their website, get the new CID, then update the mapping on the smart contract. With this flow an app can point to the smart contract as the source of truth for the website content, and it will automatically update when the contract is updated.

Anyone will be able to view the history and have it verified with the identity of the user onchain. We’ve actually implemented this with the IPCM website where we use a simple Cloudflare worker to proxy the content to a domain:

import { createPublicClient, http } from "viem";
import { base } from "viem/chains";
import { abi } from "../utils/contract";
import { pinata } from "../utils/pinata";

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext,
  ): Promise<Response> {
    try {
      const publicClient = createPublicClient({
        chain: base,
        transport: http(),
      });

      const cid = await publicClient.readContract({
        address: "0xD5B0CE88928569Cdc4DBF47F0A4a1D8B31f6311D",
        abi: abi,
        functionName: "getMapping",
      });

      if (!cid) {
        throw new Error(`Failed to fetch latest state: ${cid}`);
      }

      const url = await pinata.gateways.convert(cid as string);

      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`Failed to fetch from CDN: ${response.statusText}`);
      }

      const contentType = response.headers.get("content-type");

      return new Response(response.body, {
        status: 200,
        headers: {
          "Content-Type": contentType || "text/html",
          "Cache-Control": "public, max-age=3600",
        },
      });
    } catch (error) {
      console.error("Error:", error);
      return new Response(`Error: ${error}`, {
        status: 500,
        headers: { "Content-Type": "text/plain" },
      });
    }
  },
} satisfies ExportedHandler<Env>;

Of course, static site hosting is just one example. There are many other use cases for dynamic onchain content ranging from gaming, DeFi, NFTs, and more!

Wrapping Up

IPCM is still a brand new contract that we hope to see adopted across multiple chains and applications, and we encourage you to checkout the repo which is open sourced and MIT licensed. Let us know what you build with IPCM, and as always, happy pinning!

Subscribe to paid plan image

Share this post:

Stay up to date

Join our newsletter for the latest stories & product updates from the Pinata community.