How Can We Model a Building in Web3?
T035 PD799 T9 U07 G5 HBRE7 M 3ba82c6608db 512
Apr 01, 2025
by Nadine Loepfe
Developer Advocate

In our first post in this series, we explored how Web3 technologies could be used to build a modern REIT with tokenized real estate assets. In our second post, we discussed tokenization in general and how both non-fungible tokens (NFTs) and fungible tokens can each play a role in real estate.

Today, let’s focus on a single question:

How do we actually model an individual building on-chain?

In our implementation, a building would - at a high level - be represented by a custom contract that would be the ‘owner’ of connected, supporting contracts that enable the functionalities of the building itself and its stakeholders.

Each building would be a set of contracts that would allow it to independently manage itself in a decentralized fashion. We decided that a NFT should be used to specify all the metadata about the building, so that standard NFT tools could use that data in a common way. Therefore, each building could be thought of as a NFT in a NFT collection. A Building NFT in such a collection will have its assigned metadata, but a building will also have at least one assigned vault, a treasury, building tokens representing shares for this building, and a related governance contract to make the entire solution work.

So while many of the fundamentals are already familiar from the first and second posts in this series, here we introduce the nitty-gritty of “building deployment.” We’ll discuss three key pieces:

  1. Representing the physical building on chain

  2. Representing real-world property data - For example: a pinned JSON file in IPFS (via Pinata, Filecoin, or other decentralized storage solutions) and the on-chain references to that data.

  3. Deploying the building - Using a “building factory” contract that automatically creates the building’s metadata NFT (and other parts to be discussed in later articles).

We’ll walk through code snippets drawn from the Hedera Accelerator DeFi EIP repository to show how these ideas come to life, especially ERC721Metadata.sol & everything in the buildings contract subdirectory.

This codebase is still in progress and therefore unaudited, but the logic of how to deploy a building is there and ready to be examined. As we move on in this series, this repository will grow and be polished on a rolling basis, and we will also introduce the frontend in the coming weeks.

1. Modeling the Building NFT (ERC-721)


A Physical Asset, Off-Chain


At the core of this entire process is a physical property: the actual building sitting at some address in the real world. This building has a certain location and size in square feet, has been built in a certain year, and can be in a residential or urban area.

All these attributes matter tremendously in real estate investments, but the challenge is making them discoverable and provable in the context of the blockchain. That’s where the NFT comes in.

Each property is represented by a NFT. We issue one NFT per building, so each NFT is truly unique, just like the real-world building it represents.

This NFT is more than a collectible image, and serves multiple purposes:

  • Represents ownership/management: The address that holds the NFT can be considered the “manager” or “owner” of the building’s on-chain identity.
  • Enforces permissions: For example, only the NFT holder might be allowed to update the building’s metadata or freeze certain fields.
  • Links to deeper data: The NFT can store small bits of on-chain metadata or reference large documents in decentralized storage like IPFS.

Hence, the NFT acts as a digital title that can be transferred or traded, bridging the physical asset with the programmable world of smart contracts.

2. Modeling the Buildings Metadata


When we talk about “modeling a building”, we’re not talking about 3D graphics. We’re talking about capturing the property’s essential details - location, year built, local regulations, or even high-res images - and representing them on-chain in a consistent way.

They will most probably be key/value pairs, for instance:

  • location: "123 Main Street"
  • size: "850 sqft"
  • yearBuilt: "2010"
  • status: "UnderConstruction" or "Completed"

If we look into our codebase, we can see that the ERC721Metadata contract exposes the following function:

Code Snippet Background
function setMetadata(uint256 tokenId, string memory key, string memory value)
	external
	onlyTokenOwner(tokenId)
	whenUnfrozen(tokenId)
{
	_setMetadata(tokenId, key, value);
}

onlyTokenOwner(tokenId) ensures that whoever sets or updates metadata controls the building’s NFT, and whenUnfrozen(tokenId) means that if the metadata is “frozen,” no further edits can be made, guaranteeing immutability for certain fields once finalized.

From then on, any contract or frontend can query those fields like this:

Code Snippet Background
function getMetadata(
	uint256 tokenId,
	string memory key
)
	public
	view
	returns (string memory)

And retrieve exactly what was stored.

There may actually be multiple NFT collections being created:

  1. The NFT containing the demographics (name/title/size)

  2. The NFT containing the insurance information (also called COPE data).

ERC-721 is flexible enough to represent different categories of metadata. You can mint as many NFTs as needed, some purely for “legal doc references,” others for “demographic info,” etc.

Off-Chain Storage (IPFS)


In most scenarios, you don’t want to store large documents or images directly on-chain (that can get expensive, fast). Instead, we use InterPlanetary File System (IPFS) or a pinning service like Pinata to store those bigger files. Then, the on-chain contract keeps track of the IPFS “content identifier” (CID).

The diagram below (imagine Diagram #1) shows how a typical flow might work:

    HH60061 Blog Illustrations REIT1
    1. You upload your building’s JSON file, perhaps including an address, square footage, images, and any compliance docs, to IPFS/Pinata.

    2. Pinata returns a CID or gateway URL, e.g. ipfs://Qm1234....

    3. Your smart contract stores that CID or URL in a mapping (tokenID → string) so that the building’s NFT can always reference it.


    In the repo, the ERC721Metadata examples show how an NFT can have a tokenURI or a metadata K/V store that points off-chain. A snippet might look like this:

    Code Snippet Background
    function setTokenURI(uint256 tokenId, string memory uri) external onlyOwner {
    	require(_exists(tokenId), "ERC721: URI set of nonexistent token");
    	_tokenURIs[tokenId] = uri;
    	emit MetadataUpdated(tokenId, uri);
    }
    

    Then, when minting the building NFT, you can provide a tokenURI (e.g., ipfs://Qm123abc...) that points to a JSON document in IPFS. For example:

    Code Snippet Background
    {
      "name": "Building at 123 Main Street",
      "description": "A multi-unit residential property built in 2010",
      "image": "ipfs://QmSomeHashForThePhoto",
    }
    

    The NFT’s contract can store just the tokenURI, while the heavy lifting (the actual file or JSON) resides off-chain. This is cost-effective and ensures large data can still be accessed via decentralized means.

    A Note on Dynamic vs. Immutable Metadata


    Buildings in the real world change. They might undergo renovations or expansions; occupancy might vary over time. The NFT’s metadata should be able to reflect that. In many systems, you might have:

    • Mutable metadata: Fields like “status” or “occupancy” that can be updated by the NFT owner.
    • Immutable fields: Some data (like “yearBuilt”) doesn’t really change. You can “freeze” them so the historical record remains intact.

    This balance ensures your on-chain building identity remains accurate over time while preserving critical historical facts.

    The Power of Indexed Metadata


    A real estate platform with thousands of building NFTs needs a way to find properties by certain attributes. Our demo application’s approach can support:

    Code Snippet Background
    function filterTokens(string memory key, string memory value) external view returns (TokenDetails[] memory)
    {
    	uint256[] memory tokenIds = metadataIndex[_keyValueHash(key, value)];
    	return _getTokenDetails(tokenIds);
    }
    

    This means if you’re building a frontend, you can easily run queries like “Show me all buildings where status=UnderConstruction” or “Find all buildings with size >= 1,000 sq ft.”

    Note: Real on-chain filtering may require a bit of indexing or an off-chain indexer. The method above is a simplified example showing how to store references for more advanced filtering in your UI.

    Practical Example: Minting, Updating, and Freezing


    Let’s say you’re onboarding a new property:

    1. Mint the Building NFT:

      Code Snippet Background
      buildingFactory.newBuilding("ipfs://QmXYZ...")
      // Deploys building contract + mints tokenId=1 for that property
      

      2. Set Basic Metadata:

        Code Snippet Background
        building.setMetadata(1, "location", "123 Main Street");
        building.setMetadata(1, "yearBuilt", "2010");
        building.setMetadata(1, "status", "UnderConstruction");
        

        3. Later Update (e.g. construction completed):

        Code Snippet Background
        building.setMetadata(1, "status", "Completed");
        

        4. Freeze (once you want to lock something):

        Code Snippet Background
        building.freezeMetadata(1);
        // "yearBuilt" can no longer be changed, ensuring historical integrity
        

        Meanwhile, the tokenURI might point to an IPFS JSON with pictures and additional off-chain data - like the insurance binder or scanned floor plans.

        3. Deploying the Building (and Its Tokens)


        Storing metadata is just half the story. We also need to mint a "building token" that investors can buy, sell, or hold to represent partial ownership of that property, as well as contracts to receive yield, pay expenses and govern the day to day activities of the properties like spending and policy changes.

        Instead of manually deploying each building's contracts by hand, we can automate the whole process with a "factory" contract. Conceptually, for our building NFT, it might look like this:

        HH60061 Blog Illustrations REIT2 V2
        1. The admin (or DAO) calls the factory contract with info such as the building name, the IPFS CID, and desired token parameters (total supply, share token name, etc.)

        2. The factory mints an ERC-721 building NFT that references the building’s metadata in IPFS.


        At a high level, each building now has an unique NFT that identifies it, with pointers to real-world data.

        From a coding standpoint, you can see how we piece it together in the Hedera Accelerator DeFi EIP repo. The building factory is just one example of how to orchestrate multiple contracts behind a single, user-friendly function call.

        When looking through the repository, you will see references to the vault, as well as the treasury and governance and other pieces. Don’t worry - as this series progresses, more and more will be explained. For the purpose of this week’s blog post, however, we will solely focus on one function of the BuildingFactory contract:

        newBuilding(tokenURI), which deploys a new “Building” contract (via BeaconProxy, for instance), and mints an ERC-721 NFT that references tokenURI for IPFS metadata.

        Code Snippet Background
        /**
         * newBuilding Creates new building with create2, mints NFT and store it.
         * @param tokenURI metadata location
         */
        function newBuilding(string memory tokenURI) public virtual {
        	BuildingFactoryStorage storage $ = _getBuildingFactoryStorage();
        	BeaconProxy buildingProxy = new BeaconProxy(
            	$.buildingBeacon,
            	abi.encodeWithSelector(Building.initialize.selector, $.uniswapRouter, $.uniswapFactory, $.nft)
        	);
        
        	uint256 tokenId = IERC721Metadata($.nft).mint(address(buildingProxy), tokenURI);
        	address identity = IdentityGateway($.onchainIdGateway).deployIdentityForWallet(address(buildingProxy));
        
        	$.buildingDetails[address(buildingProxy)] = BuildingInfo(
            	address(buildingProxy),
            	tokenId,
            	tokenURI,
            	identity,
            	address(0) // ERC3643 token lazy deploy
        	);
        
        	$.buildingsList.push($.buildingDetails[address(buildingProxy)]);   
        
        	emit NewBuilding(address(buildingProxy));
        }
        

        Building Creation at a Glance

        • A proxy contract is deployed for the new building.

        • A unique NFT is minted and owned by that building contract, linking real-world property metadata to this on-chain representation.

        • An optional identity is established for compliance or identity checks.

        • All these details are stored in the factory’s state and logged via an event.

        That’s the entire “birth” process of a new on-chain building in your system!

        The Bigger Picture


        After reading this post, you should see how:

        • Physical attributes map to on-chain metadata (small, crucial fields).
        • Large documents (floor plans, insurance, COPE data) or even the entire metadata sit in IPFS or similar.
        • An NFT ties it all together, acting as the building’s “digital identity.”
        • A Building Factory automates the heavy lifting - deploying a building contract, minting the NFT, and more

        In future posts, we’ll cover more capabilities of the building factory and see how these NFTs connect to vaults that distribute rental income, how governance can let token holders vote on renovations, and how composability unlocks DeFi-style possibilities.

        Happy Building!