Snapshot ― The Value of On-Chain Accessible Ownership History
September 26th, 2023

[ Click here for the original Japanese version of the article / 日本語版はこちら ]

Overview

  • I have created a smart contract on the Flow blockchain that allows for a snapshot of the list of NFTs owned by a particular address.

  • Once a snapshot is created, it becomes easy to prove ownership even later on. This accuracy is guaranteed by the smart contract's code.

  • I have also developed a feature to display the snapshot. This is extendable by anyone.

  • Let's think about the importance of ownership history accessibility.

Awareness of the Issue ― Ownership History Accessibility

One thing that is lacking in every NFT standard is the inability to access past ownership history from within the contract. The primary reason for not providing this feature is likely due to storage capacity. In the past, an Ethereum ERC-20 contract was created that recorded all transfer histories. However, this storage capacity became vast and became an issue within the Ethereum network.

On the other hand, directly retrieving past transaction information from nodes becomes more challenging as the number of blockchain users increases. For instance, in Ethereum, transaction data from over a year ago is no longer retained by nodes (EIP-4444). In Flow, when a periodic update called a "spoke" occurs, past transaction data is only retained in special archive-like nodes.

Considering the composability of smart contracts, it is crucial to be able to retrieve past ownership history on-chain. If you can verify the NFT ownership history within the contract, it becomes easier to enhance the trustworthiness of NFT transactions or offer services based on past histories. There are also secondary benefits. For example, you can keep a cherished NFT that you've let go of in another form forever. Also, it becomes easier to identify the identity of an NFT if you know what NFTs were together in the same address at a certain point. Think of the ownership history snapshot as something akin to a family photo.

Idea to Solve the Issue and the Reason for Choosing It

My idea is to create a standard or reference smart contract that can record a snapshot of which address owned which NFT at runtime. I wanted to show an example of how such open snapshot information can be utilized.

Behind this idea is a hypothesis about the composability of smart contracts. Composability has been touted as important for years, but I haven't seen many great ideas that are fully on-chain. Most go through off-chain, which diminishes the benefits of Web3 services. Perhaps the inability to complete on-chain is due to a lack of NFT identity information accessible from smart contracts?

NFT project communities hope their NFTs are used in various use cases. I recently attended a Doodles meetup in Japan and spoke with holders. They wished to see NFT illustrations and merchandise in many places. However, I didn't hear many specific utilization ideas. I suspect this is because there is still a lack of information on-chain. This is just my speculation.

One reason I wanted to create this snapshot idea on Flow is that Flow's smart contracts are designed to easily access an unspecified number of NFTs. Flow has a common NFT interface mechanism, and you can succinctly check whether it conforms to the standard specification. Also, since NFTs are stored within each account's storage, you can retrieve a complete list without omission.

Hardships Faced

I struggled with how to store NFT ownership information in what data structure.

Flow's NFT standard has been updated several times, and some older NFT contracts couldn't keep up with the latest specifications. Therefore, I couldn't retrieve all NFT information in a common way. For example, accounts from the early days of NBA Top Shot didn't expose the NonFungibleToken.CollectionPublic interface to the Collection, so I had to refer to the TopShot.MomentCollectionPublic interface. This complicates the code.

I also explored whether I could dynamically add processing for specific types, but I couldn't use variables in type-specified places <T> like fun getCapability<T>(_ at: CapabilityPath), making code simplification challenging. I managed to add logic using struct interfaces, but it might not be the best solution.

What I Like About Cadence

I really like the intuitive ownership representation due to resource oriented programming, something not yet realized in most smart contract languages.

I also admire the developers of the Cadence language for their fearless pursuit of the ideal form, even if it means drastic changes. I've experienced several breaking changes so far, but I believe each was a good decision. With its simple and clear syntax and practical features like UTF-8 support, the Cadence language is much more approachable than the Move language.

Additionally, this might be more about the Flow protocol than Cadence, but I really like that the source code string is included directly in the transaction. I believe program code is art, so I feel this specification is crucial for all code artists.

Snapshot Architecture and Implementation Overview

Smart Contract Architecture
Smart Contract Architecture

The most crucial resource within the Snapshot contract is Snap. The Snap resource represents a single snapshot unit, containing the timestamp when the snapshot was created, the target address, the type of logic structure used for retrieval, and multiple NFT information (NFTInfo structure) owned by the address at that point. The NFTInfo includes collection information, type, ID, and metadata for each NFT. The created Snap resources are stored in another resource called Album. The Album resource offers the following three functions:

  1. Create a snapshot

  2. Obtain proof of ownership

  3. Display the snapshot

When creating a snapshot, you need to pass logic as an argument. This logic should be of a type that implements the following struct interface:

pub struct interface ILogic {
    pub fun getOwnedNFTs(address: Address): {String: {UInt64: NFTInfo}}
}

I implemented the basic processing logic type in the SnapshotLogic contract. If someone wants to customize the process, they can freely implement it. However, to actually use the logic type, it needs to be added to the allowed list by the administrator of the Snapshot contract. This is to prevent malicious actions like forging NFT information.

The state of account storage after retrieving the snapshot is as follows:

State of Account Storage
State of Account Storage

Here, this account specifies its own address to store NFT information, such as TopShot and Doodles, within the Album as a Snap resource. However, you can also specify any address other than your own.

The information of the created Snap resource can be output in any format, such as SVG images or HTML, by passing the following viewer type. Anyone can implement and use this.

pub struct interface IViewer {
    pub fun getView(snap: &Snap): AnyStruct
}

This time, I implemented a viewer that renders in HTML format in the SnapshotViewer contract. This implementation embeds the metadata of the NFTs contained in the snapshot into a simple web app in HTML. When I tried to render a snapshot of the NFT information I own, the following HTML is displayed.


The code for the smart contract, transactions, and scripts from this time is stored in the following repository.

The smart contract has already been deployed to the mainnet.

In Conclusion

Being able to visually display a list of owned NFTs is more fun and memorable than I thought. NFTs are supposed to be a technology where you can trace the history, even if you let go of them. However, in reality, it's challenging to verify old transaction information on-chain. I feel that building an ownership history using this contract is essential in the age of composability. I encourage everyone to record their ownership history. I hope this contract serves as a catalyst to improve the accessibility of ownership history.

Subscribe to Ara
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from Ara

Skeleton

Skeleton

Skeleton