White Prompt
EngineeringMay 9, 2021 · 5 min read

Modeling Supply Chain Updates with ERC-721 NFTs and Solidity

By Fabricio Quagliariello

Introduction

According to Wikipedia, a non-fungible token (or NFT) is a unit of data stored on the blockchain that certifies a digital asset to be unique and therefore not interchangeable with another NFT (or non-fungible token).

NFTs can be used as a representation of digital files (like media) or physical assets, such as:

  • Physical property — real estate, unique artwork, or batches of raw materials
  • Virtual collectibles — unique pictures of kittens, collectible cards, videogame items
  • “Negative value” assets — loans, burdens, and other responsibilities

When the NFT is used to track digital assets, copies of these are available for anyone to obtain. Still, NFTs provide the owner with an immutable proof of ownership that is separate from the copyright.

The NFT market value tripled in 2020, reaching more than $250 million, so we figured that it would be a great idea to explore the standard further, looking for scenarios in which it would make sense.

Problem to Solve

One of our clients provides logistics, distribution, and transportation services in a supply-chain-related business. Certain supplies distributed have stringent regulation and compliance rules around how long a particular package has been on each step of the distribution chain.

For such a scenario, we want to provide immutable audit logs for tracking each package history while it transitions from one stage of the supply chain to another. Given that these audit logs need to be accessible to many parties and need to be immutable, we figured that using a blockchain solution is ideal.

Roadmap

Our first thought was to deploy a custom smart contract every time we needed to persist an audit log. However, this approach is suboptimal because it makes it hard to track the history for a single package since the records would be scattered across several deployed contracts. We would need a controller contract to keep track of them.

Instead of deploying a smart contract to anchor the audit data on-chain, we thought we could use NFTs, minting a new non-fungible token for every entity we want to track audits. That approach allows us to model our domain more closely.

We studied Solidity ERC-721 and its most notable implementations, leverage a well-known third-party service API and SDK.

Journey

We’ve found that in Solidity, as in any other programming language, it is not a good idea to reinvent the wheel. So to avoid that, we will use OpenZeppelin.

OpenZeppelin provides a complete suite of security products to build, manage, and inspect all aspects of software development and operations for Ethereum projects. In particular, OpenZeppelin Contracts is a library for secure smart contract development. Build on a solid foundation of community-vetted code.

According to their documentation for ERC-721, this standard is more complex than ERC-20 since it has multiple optional extensions split across several different contracts.

One of the critical aspects of ERC-721 is that these contracts include a state variable to store a URI to hold the token’s metadata stored in JSON format. This metadata describes the attributes that make the NFT different than others. According to the official specification for the schema of the NFT metadata, it should include a name, a description, and an image. Still, it is open to having other fields as well. The specification does not dictate where this metadata should be stored. If the desire is to enforce real decentralization, it should be stored in IPFS, but we will just use standard web URIs for simplicity.

An excellent OpenZeppelin feature is their online wizard.

It is a code generator tool based on parameters given by the user will produce solidity starter code based on OpenZeppelin’s standard base contracts and extensions.

We went ahead with the wizard and used it to generate the base code for the ERC721 smart contract, with the following features:

  • Mintable, to create (to mint) new NFTs. The base code comes with a safeMint method that requires being called by the same address that deploys the contract (the MINTER role).
  • URI Storage (to keep a URI with the token metadata). The ERC721Storage interface requires overriding two methods: _baseURI which returns the base URI for the token URI), and tokenURI, which returns the full URI based on the base URI. The logic to upload the token metadata file needs to be externally handled.
  • Roles to have Role-Based Access Control. By default MINTER_ROLE and ADMIN_ROLE. We just keep the MINTER_ROLE, but we will handle the admin roles differently since we will require different permissions for different NFTs.
  • A counter to assign a package id to each token allowed us to track updates and admins for that particular token.

Take a look at the smart contract code we generated based on the wizard starter code.

Additionally, we added an Enum of the possible Stages in the supply chain and a Struct to reflect the data structure for an Update to the NFTs.

For the sample code, the Stages Enum:

  • WaitingAtOrigin
  • PickedUpFromOrigin
  • InTransitToWarehouse
  • ReceivedAtWarehouse
  • DepositAtWarehouse
  • DepartedFromWarehouse
  • InTransitToDestination
  • ReceivedAtDestination

The Update struct has the following field

  • packageId (the particular id of the specific NFT that is reporting updates)
  • updatedAt (to reflect the point in time in which the update is created)
  • And then a previous stage and a current phase, to reflect the transition that happened during the update, and the possible values come from the Stages Enum.

The updates are stored in a mapping indexed by token id that holds an array of update structs.

Since we needed to handle different admins for each particular NFT, we created an addUpdateForPackage method to push these updates for each NFT, which expects the token id and the latest stage in which the package is. So the message sender for this method is required to be the same address for the particular token id in the admins mapping.

b35c6cdb4d8ad088207e96b73e89c6fa0d95a30db6f1cfd55dd37feb8d27caff.webp

When we first imagined using NFTs for this scenario, we devised the Update struct updatedAt to be a DateTime or Timestamp. We realized that handling these data types at the Smart Contract level. When the method is executed, it happens within the context of a block being added to the chain. And it already contains a timestamp that can be accessed in the smart contract with “block.timestamp” (or the “now” keyword in legacy solidity versions).

Conclusion

Secure and immutable tracking of the history of real-world objects can be done over the Blockchain thanks to ERC721 and Non-fungible Tokens.

To implement the Smart Contract is a good idea to use Open Zeppelin Contracts to leverage good practices.

For handling timestamps, the correct way is to rely on the timestamp from the block.

At White Prompt, we love to improve our clients’ business processes by using blockchain technologies. Do you have a project that requires the implementation of NFTs? Talk to us! We can help.

Further Reading and Links

Share

Ready to Build Something That Lasts?

Let's talk about your project. We'll bring the engineering judgment and the speed to ship.