Blog

Beyond a JPEG: NFT as a Primary Key

01.12.2022 | Blockchain Web3 Tutorial Backend | Austin Vance

hero

These days, we most often see NFTs used to record the ownership information of artwork. There are the pie-in-the-sky folks who think NFTs will become a new standard for ownership for everything from houses, to music, to in-game items - only time will tell.

NFTs are powerful, but they ​get a bad rap. I don't have strong opinions about the disruptive nature of NFTs or how they affect art, games, or anything else. I do see that the "Non-Fungible" attribute of an NFT has some remarkable properties that allow smart contract developers to store state in a permissionless manner and enable that state to be transferred or sold. ​ Ok, I admit that's rather abstract and confusing. Since this is a dev blog, let's build an app that uses an NFT contract and holds information specific to a user.

Before we dive in, this is not revolutionary. Several projects already do this. The QiDAO uses NFTs to manage loan ownership and Uniswap V3 uses NFTs to manage a Liquidity Position. Before the release of V3, Uniswap recorded Liquidity by issuing the liquidity provider an LP Token. These tokens are an ERC-20 token, and ERC-20's are fungible. Fungibility means that one token is the same as another. Think if someone hands you a 1€ note. That note is the same as the other 1€'s in your wallet. The ERC-20's are 100% transferable, farmable, and sellable, but they cannot store any secondary information about a Liquidity Position.

icon

NFTs allow for encapsulation and transfer and sale of metadata in a permissionless manner

Uniswap V3 enables the addition of metadata to a liquidity position. Instead of issuing an ERC-20 token to represent a liquidity position, they issue an ERC-721 (NFT). With that ERC-721, Uniswap can now add unique features (like impermanent loss protection) to each liquidity position.

How cool is that! But how does it work? Let's build it.


Without giving too much background, at Focused Labs, we offer a blog bounty and a bonus for blogging streaks. The more frequently the company blogs, the larger the blog bounty becomes.

I want to move this bounty from a spreadsheet to the blockchain and use an NFT to identify wallets contributing a blog to the streak.

We will need an ERC-721 contract. Let's use OpenZeppelin to enforce the correct interface for our NFT.

// contracts/FocusedBlogPost.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract FocusedBlogPost is ERC721 {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    const 
    constructor() ERC721("FocusedBlogPost", "FCSD") {}

    function publishBlog(address blogger)
        public
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(blogger, newItemId);

        return newItemId;
    }
}

Whenever we call publishBlog() with an address, we will mint and transfer an NFT to the blogger.

A new blog must be published every two weeks to earn a streak. Each week at least one new blog post goes out our streak counter increases. This streak counter is a multiple on our Blog Bounty! Now let’s set up our contract to add logic around a streak.

Let's start by tracking when new posts are published.

contract FocusedBlogPost is ERC721 {
    // ...

    struct BlogPost {
        string postUri;
        uint256 publishedAt;
        address originalAuthor;
    }

    // ...

    // two weeks in seconds
    uint constant twoWeeks = 60 * 60 * 24 * 14;

    // map of NFT ids to Blog Posts
    mapping(uint256 => BlogPost) public blogPosts;

    function publishBlog(address blogger, string memory postUri)
    public
    returns (uint256)
    {
        // ...
        _mint(player, newPostId);

        blogPosts[newPostId] = BlogPost({
            postUri: postUri,
            publishedAt: block.timestamp,
            originalAuthor: blogger
        });

        // ...
    }
}

Now every time a new blog post is published, we not only create an NFT for the post, but we record when it was published and keep track of the original author.

We still don't track a streak, though, so let's add a new method to our token contract.

contract FocusedBlogPost is ERC721Enumerable {
    using SafeMath for uint256;

    // ...

     function getCurrentStreak() public view returns (uint) {
        uint streak = 0;
        if (totalSupply() == 0 || totalSupply() == 1) {
            return streak;
        }

        for (uint256 i = totalSupply().sub(1); i > 0; i--) {
            BlogPost memory currentBlog = blogPosts[tokenByIndex(i)];
            BlogPost memory previousBlog = blogPosts[tokenByIndex(i).sub(1)];

            if (currentBlog.publishedAt - previousBlog.publishedAt >= twoWeeks) {
                break;
            }

            streak++;
        }
        return streak;
    }

We use OpenZeppelin's ERC721Enumerable; this gives us a few new methods to loop through each NFT we have minted. Then we can check the timestamp of each BlogPosts.publishedAt. Pretty easy, right?!

In a future part of this series, we will continue to add features to the NFT, allowing for payouts to streak contributors and adding validations like only increasing a streak if the author isn't in the current streak.


Although this example is a bit contrived, honestly, why would someone want to transfer their streak? I think the practical applications are straightforward.

The NFT can act as a unique identifier, recording metadata about an event or individual actions. A dApp can then use that NFT to make decisions like providing access to a "secret" website or paying a dividend for contributing a blogpost.

Source code is available on GitHub where there is a complete example with passing tests.

Share

Read More

Related Posts

related_image

01.13.2022 | Culture | Kate Courtney

Pair programming is a way to develop software. It used to involve two software developers sitting down at one workstation and writing code together.

related_image

01.03.2022 | Blockchain Testing Web3 | Austin Vance

There are two ways: you can deploy "mock contracts" or you can use a mocking library. There are tradeoffs, but for this post I am going to focus on using a Smock's...

related_image

06.30.2021 | Culture | Katy Scott

At Focused Labs, collaboration is key to how we work together; it helps our teams learn from each other, brings us closer and helps us become more efficient...

related_image

06.23.2021 | Culture | Austyn

Late-night feedings and diaper changes, the 3-4 month sleep regression, teething, and a growth spurt all mean I'm getting less sleep than...

related_image

05.12.2021 | Culture Backend Frontend | Ryan Taylor

Temporarily disrupts "normal" business operations and allow self-organized teams to rapid prototype around their interest areas

related_image

04.27.2021 | Culture | Erin Hochstatter

Several years ago, I'd been trying to find an approach to software consulting that made sense for me [...]

additional accent
accent
FocusedLabs

433 W Van Buren St
Suite 1100-A
Chicago, IL 60607
(708) 303-8088

[email protected]

© 2022 Focused Labs, All Rights Reserved.

  • facebook icon
  • twitter icon
  • linkedin icon
  • github icon