Skip to content

Instantly share code, notes, and snippets.

@ClementWalter
Last active September 18, 2022 10:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ClementWalter/5080a886f660697281e81bb537e39e8a to your computer and use it in GitHub Desktop.
Save ClementWalter/5080a886f660697281e81bb537e39e8a to your computer and use it in GitHub Desktop.
Dynamic Programmable NFT white paper

Dynamic Programmable NFTs for a Decentralized Intelligence Protocol

Abstract

NFTs as they are know today are mostly considered as static proofs of ownership of a digital or physical assets. Recently, the emergence of dynamic NFTs (aka dNFTs, NFTs whose metadata can change over time) has been seen as a way for NFTs to bear more real-life value: NFTs evolve as life goes by. However, from the owner point of view, NFTs are still a passive asset: they don’t control how it evolves nor can’t they decide how they should look like.

Because the NFT metadata is actually produced by a function call (the output of tokenURI for instance), we propose to truly see an NFT as a contract call. Following this new approach, we define Programmable NFTs as NFTs where owners can decide which contract call their NFT makes, and Dynamic Programmable NFTs (dpNFT) as Programmable NFTs where the call parameters are dynamic. Especially, they can be the result (the output) of another dpNFT.

In this context, NFTs become a single unit of on-chain computation that can be linked to others, creating a decentralized graph of computation. Such graphs can be used for collaborative permission-less scientific research, public policy modeling, as well as a basic data unit for on-chain dApp.

From NFTs to Dynamic Programmable NFTs

The ERC721 Standard

NFTs originally appeared as a way to represent “digital ownership over digital or physical assets” (see EIP-721 homepage). Back in the early days of the ethereum blockchain, before the EIP-721 ever existed, some team started to think about what could be done on-chain apart from standard cryptocurrencies. As stated by the CrypoPunks founders:

Normally code is run on a server somewhere and you basically need to trust the person running the server. Ethereum lets everyone execute the code, show each other what result they got, and agree that the code was executed properly and fairly.

We have written code that lives on the blockchain that anyone can use to buy and sell Punks with anyone else in the world.

This seminal work, together with some other projects like the CryptoKitties, paves the way to the standardization of on-chain proof-of-ownership, eventually producing the well known ERC721 standard.

📌 The CryptoPunk contract does not follow the ERC721 standard as the project was released beforehand. We also believe that standardization should follow usage and consequently do not propose an EIP yet, but rather only *Protocol* for people to start digging around this innovation.

A general extension used together with the mandatory NFT standard is the NFT Metadata one: it allows the contract to name its collection of assets (all the assets described in the given smart contract), to give it a symbol and, more interesting here, to return metadata for a given asset.

This metadata has to follow the ERC721 Metadata JSON Schema. It imposes three properties:

"name": {
    "type": "string",
    "description": "Identifies the asset to which this NFT represents"
},
"description": {
    "type": "string",
    "description": "Describes the asset to which this NFT represents"
},
"image": {
    "type": "string",
    "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
}

Both from its name (Non-fungible token) and from its mandatory fields, we can see that this standard is clearly strongly influenced by the first existing usages of non-fungible tokens like CryptoPunks, that are indeed a collection of images.

The idea of standardizing the ownership of something that is not a usual token (coin) as known at that time but still bears value drove this standard. As a matter of fact, the “NFT word choice” section of the standard stands:

“NFT” was satisfactory to nearly everyone surveyed and is widely applicable to a broad universe of distinguishable digital assets. We recognize that “deed” is very descriptive for certain applications of this standard (notably, physical property).

Alternatives considered: distinguishable asset, title, token, asset, equity, ticket

and in this context, the Simple Summary of the standard speaks for itself:

A standard interface for non-fungible tokens, also known as deeds.

Dynamic NFTs

The OpenSea metadata standard, that became the de facto standard for NFTs, adds some fields to the core above-mentioned standard; and especially the attributes field that lets the developer add a free list of extra types:

...
{
"attributes": [
    {
      "trait_type": "Base", 
      "value": "Starfish"
    },
...
  ]
}

This augmentation paves the way to some sort of mutability of the token: since this list is not restricted in length and can contains any trait_type, it is possible to design mechanisms to update it, based for example on real-life events:

https://twitter.com/77MichaelR/status/1569099253733101571?s=20&t=7UYnN1w80sFwm5nH6sr2TA

Note that this usage is still very uncommon these days and assumes a passive update of the NFT data: the owner doesn’t really control its NFT but can see it evolving as time goes by.

Data storage

As the ERC721 standard states:

The token URI may point to a JSON file that conforms to the "ERC721 Metadata JSON Schema"

Let us dive deeper into the consequences of this guideline:

  1. JSON format: the JSON format is actually not adapted at all for a usage in solidity. There is no native JSON handling library nor even standard community one’s; a tokenURI that returns a JSON string is almost un-usable from within a smart-contract. Furthermore, it is a serialization format that does not really make sense while working on the same (decentralized) computer

    https://twitter.com/ClementWalter/status/1521875745336922117

  2. “The URI may point”: due to the complexity of writing an on-chain JSON and the cost of storing data on-chain as well, this is more to be understood as “The tokenURI should return a URI that point to a JSON file”.

In other word, the NFT data were not meant to be stored on-chain. This point has been later heavily criticized for the reason that it does not comply with the decentralization ethos of the blockchain.

Not only is off-chain data bad from an ethos perspective but especially it is a gigantic hurdle for composability, one of the most, or the most, valuable thing the blockchain brings to the end-user.

The Blitmap project is one of the first and a good example of what composability brings to NFTs when they are on-chain: create few base images and let the user remix them.

On-chain rendering

As we saw previously from the ERC721 standard, NFT metadata should include an image field pointing to a resource with mime type image/*.

When the data is stored on-chain, it means that the contract should be able to render some raw data stored in the contract into, say, a PNG or SVG file. So, if we formalize this a bit, it means that an NFT is indeed a container of some on-chain data that passed through a rendering function, eventually return an image of the NFT to the outer world:

  • this on-chain data may be constant for the whole collection or unique to each NFT
  • the rendering function may use data from the contract only or from any other deployed contract
  • the image is indeed a visible representation of the NFT

Usually, the workflow for an on-chain collection is a follows:

  • the contract stores a whole bunch of raw data encoding the traits
  • each NFT embeds a unique seed
  • the contract defines a rendering function that uses the raw data and the unique seed to pick few traits and render the image

Actually, the NFT itself doesn’t embed anything as it is defined as the output of a given function call: it is the rendering function that eventually creates the JSON that describes the NFT.

💡 In conclusion: an on-chain NFT is not an object but the output of a given contract call, or in short: **an NFT is a contract call**.

Programmable NFTs

This new framework allows us to consider a whole new kind of NFTs and NFT collections. Indeed, the most advanced on-chain NFT collections so far apply the following restrictions to this general idea:

  • the function should return an image
  • the function is the same for all the NFTs of the collection
  • the function takes as input some contract data and the token id

So now if we alleviate all these rules, we end up defining what is a programmable NFT:

💡 A programmable NFT is an NFT where the owner can **decide which contract call** is executed to render the NFT

It means that the owner is able:

  • to define which on-chain function should be used to render its NFT
    • this function is not restricted to be part of the contract handling the NFT but can be any function deployed by anyone
  • to define the parameters given to these function

Dynamic Programmable NFTs

The later paragraph let us introduce the programmable NFTs concept. As such, these NFTs are already dynamic in the sense that their value may change even though the NFT itself (the programmed contract call) does not. For example:

  • aTokenContract.totalSupply() may increase over time
  • aTokenContract.ownerOf(0) will change when the token is transferred
  • uniswapPair.getReserves() will change when users swap tokens of a given pair

However, they are not dynamic in their parameters nor their function itself. More precisely, a read-only contract call requires the following information:

  • a contract address
  • calldata (an array of bytes) storing the concatenation of a function selector and the function parameters

For the sake of clarity, we will from now on consider that a contract call, ie also an NFT, stores instead three fields:

  • a contract address
  • a function selector
  • the function parameters

If we reuse the example above, we have:

  • aTokenContract.totalSupply():
    • contract address = aTokenContract
    • function selector = totalSupply (actually its hash, depending on the blockchain used)
    • parameters = [] (no parameters)
  • aTokenContract.ownerOf(0)
    • contract address = aTokenContract
    • function selector = ownerOf
    • parameters = [0]
💡 We consequently define a Dynamic Programmable NFT as an NFT dynamic in its contract call definition

The easiest way to achieve this behavior is to use the output of another NFT (another contract call) as input of these fields. Here are some examples for the sake of clarity:

Example 1

Compute dynamically a holder’s percentage of a given collection:

  • token #0: constant value (an EOA) 0x…
  • token #1: aTokenContract.balanceOf(token #0)
  • token #2: aTokenContract.totalSupply()
  • token #3: math.div(token #1, token #2)

Here, token #3 has its parameters dynamic:

  • contract address = math
  • function selector = div
  • parameters = [token #1, token #2]

Token #3 is a dpNFT which outputs the percentage of tokens hold by token #0 for the hard-coded collection aTokenContract. Consequently, changing token #0 will change token #3 value.

Example 2

Compute dynamically the reserves of a given pair in Uniswap:

  • token #0: OxtokenAddress
  • token #1: OxotherTokenAddress
  • token #2: uniswap.getPair(token #0, token #1)
  • token #3: (token #2).getReserves()

Here, token #3 has its contract address dynamic:

  • contract address: token #2
  • function selector: getReserves
  • parameters: []

Token #3 is a dpNFT which outputs the reserves for a given pair of tokens in Uniswap Dex.

Collections of Dynamic Programmable NFTs

Because a dpNFT can make any contract call, we need to clarify what is a collection in this context. Indeed, for usual NFTs, the concept is almost straightforward, and actually could be defined as the set of NFTs that have the same contract call.

However this does not hold anymore. To underline the concept of programmable NFTs and to borrow from usual programming languages patterns, we will define rather a collection as a namespace for this given set of programmed instructions.

This is consistent with the bare minimum required by the ERC721 Metadata standard:

  • name: a namespace is basically a root name of a group of objects
  • symbol: a library often has a full name and conventional short name used for convenience when coding. While unconstrained, this short name is actually always the same by consensus, for example
    • import pandas as pd
      • name: pandas, symbol: pd
    • import tensorflow as tf
      • name: tensorflow, symbol: tf
    • import numpy as np
      • name: numpy, symbol: np

We consequently define these base rules for a Dynamic Programmable NFT:

  • the owner should be able to call any on-chain contract and function
  • the owner should be able to use constant values
  • the owner should be able to use output values of other NFTs of the same collection
    • more precisely, a token id can be used as a parameter and should be interpreted as the value of this token and not a constant value

With this framework, it is indeed possible to use any rendered value of any collection by simply using two NFTs:

  • the first one stores the call to the external collection in an internal NFT
  • the second one used the internal NFT to retrieve the external value

For example:

  • token #0: externalCollection.tokenURI(0)
  • token #1: constant value 10
  • token #2: math.sum(token #0, token #1)
    • boils down to token #2: math.sum(externalCollection.tokenURI(0), 10)

Link with Multicall

It is worth noticing here that the Multicall library from MakerDAO can dynamically be replaced with this framework. Indeed, as stated by the authors:

Multicall aggregates results from multiple contract constant function calls.

This reduces the number of separate JSON RPC requests that need to be sent, while also providing the guarantee that all values returned are from the same block (like an atomic read)

Dynamic Programmable NFTs create like a graph Multicall where the outputs of any call can be used as input of others. Rendering a such a NFT is indeed potentially making a multicall.

When the goal is only to make sure that a list of unrelated calls are all made with the same RPC request, it is possible to create a Multicall NFT that is actually a call to the deployed Multicall contract. On the other hand, it is also possible and probably easier to use a constant function that returns the list of its parameters. Using such a function for the Multicall NFT allows for rendering a list of NFTs in the same call while still being able to define each call as a single NFT itself.

  • each one of the target calls are stored in one NFT
  • the “multicall” NFT is a call to a constant function that returns its input parameter
  • the “multicall” NFT takes as input the token ids of the target calls

Using this framework, it is easier to develop a complex logic by being able to store each call in its own entity and to aggregate calls as nodes dependencies (see Divide and Conquer algorithms).

The Decentralized Intelligence Protocol

An NFT is an API

In the previous section we detailed how an NFT can be seen indeed as any on-chain single contract call programmed by the owner themselves. The attentive reader will have noticed that the latter example paved to way to the creation of a directed acyclic graph of computation.

Indeed, to compute (to render) token #2 above, one needs first to call token #1 and token #0, which in turns may require multiple calls to render.

However, from the outer world, there is no reason to dig into what is inside token #2. When someone calls tokenURI(2), they expect to receive the value of token #2, and that’s all. In other word, an NFT should be considered as a public API that steadily returns an answer without any other knowledge.

💡 When anyone mints and sets such an NFT, they open a public API, precisely the API that returns the output of a given chain of computation defined both by the owner of the NFT and by all the owners of all the NFTs included in the chain of computation.

Ownership and responsibility

Because an NFT is a public API, any owner can rely on any other NFT as a base input for their own NFTs. There is consequently a chain of computation where any single node may belong to someone else (an individual, a research group, a DAO, etc.).

In this chain, each owner is responsible for the soundness of their node. This creates a clear interface in between subparts of a given system. Indeed, each node can be arbitrarily complex (possibly requiring teams to develop their own smart contract to call it) while its interface remains neat and the same: when synchronizing different subparts it is enough to define in the very beginning which tokens will be bridges in between those parts, like: “Team B will always get results from Team A reading token #0” and whatever Team A makes to create token #0, Team B will always be up-to-date.

Decentralized intelligence

If the latter consideration can ease collaboration in any team of people, it is even more sound when considering any team of anon people working completely asynchronously on big projects.

Using NFT ownership as a true hard limit for defining responsibilities of a given part of a graph of computation, the proposed protocol indeed solves the problem of decentralized contributions to a common goal.

Let us consider that at a given point of time, a given graph exists to solve a given problem. In other words, there is one token whose value is considered an answer to a problem. The Decentralized Intelligence Protocol brings:

  • radical transparency to the path followed to produce an answer
    • consider for examples models used to derive public policies during COVID, this protocol could have been used to legitimate decisions
  • permanent questioning of an established solution
    • everyone can at anytime fork a part of this graph to update it should a better solution be found
    • upon consensus, the main graph could be updated with a merge mechanism
  • up-to-date data for otherwise disconnected group of research
    • researchers often use results from other domains, like Astrophysicists may use results from Computer Vision researchers. However they cannot keep the path to this side field of research. Eventually this lost of information brings sup-optimal researches. Using the Decentralized Intelligence Protocol, the Astrophysicists could get as input the token “latest model parameters” of the OpenAI team and be sure that they remain up-to-date with latest CV research without spending time outside of their main field
  • graph analysis
    • simplify useless branches
    • estimate the impact of the changes of one node to the network
    • compare graphs produced by different teams (companies) targeting similar goals
    • project scaffolding in terms of minimal NFT minting

Using Dynamic Programmable NFTs in dApps

The Decentralized Intelligence Protocol as described before will benefit from a vast adoption: the more people put logic into this protocol, the more nodes there are for others to start from and build on top of.

Considering the need to open this Protocol to non-dev users to broaden its usage, the question of the right interface for designing such graphs arises.

We think that the spreadsheet is indeed the most widespread tool for easily creating such graphs of computation. Actually, when doing:

  • In Sheet 1:
    • A1=10
    • B1=SUM(A1;20)
  • In Sheet 2:
    • A1=Sheet1!B1 * 10

one defines a graph with two collections and four nodes, previously described as:

  • Collection #0
    • token #0: 10
    • token #1: math.sum(token #0, 20)
  • Collection #1
    • token #0: (address of collection #0).tokenURI(1)
    • token #1: math.prod(token #0, 10)

In other words, the above-mentioned protocol fits well for building a web3 native excel; and a web3 native excel seems to be the key ingredient for democratizing the usage of on-chain data. Actually, there are 750 million to 2 billion people in the world who use either Google Sheets or Excel (see this study from 2021).

The need of a web3-native spreadsheet has already been expressed by several twitter influencers

https://twitter.com/balajis/status/1309481405567569920?lang=en

and indeed, spreadsheet are nowadays mostly used for

  • Financial modeling
    • DeFi is one the main use case of blockchain
  • Project management
    • Meaningful for DAOs

In the following sections, we investigate deeper this possibility of building a web3 native spreadsheet.

What a spreadsheet does

Spreadsheets are not only a graph tool (sic), but :

  • a database
    • User-created data
    • Imported from external sources
  • a computation engine
    • Built-in functions
    • Add on Marketplace
  • a programming language
    • High-level excel semantic (=FOO(…))
    • Low-level VBA
  • a “no code” tool
    • Users create a custom program using high-level semantic and click-and-drag features
  • a collaborative workspace
    • Several users can use and update a same spreadsheet

Built-in web3 features

On the other hand, web3 brings by design:

  • A database
    • Readable by anyone
    • Writable by authorized addresses
    • Versioned
  • A computation engine
    • The nodes clients
  • A programming language
    • Low level only (solidity, rust, cairo…)
  • A permission-less cryptographically secured user login

The web3 native spreadsheet

Consequently, a web3-native spreadsheet may

  • use Dynamic Programmable NFTs as its core unit of data (a cell = an NFT)
  • use a naming service (like ENS) to create a smooth UX for community-selected contracts
    • every single contract can be called, but the main one should have a name
  • use token gated content to restrict access to the sheet to the concerned users
  • define a high-level syntax
  • design an interface
📌 Since a web3-native spreadsheet is a dApp, the interface and the high-level syntax may differ from one implementation to another but while users would still *edit the* *same spreadsheet.*

The following table summarizes how Excel splits into what is native in web3 and what should be built

Spreadsheet Blockchain Web3 spreadsheet
Database
Computation engine
Low level syntax vba solidity(rust/cairo/move/etc.)
High level syntax =FOO =(dns of addresses).foo
IDE Excel/Google sheet Starksheet-like dApp
Storage IDE internal storage Dynamic Programmable NFT
Collaborative workplace email login permissionless wallet login

Eventually, a web3 native spreadsheet would also gives as “extra”:

  • better data availability
    • all the on-chain data always up-to-date directly accessible
  • crypto secured environment
  • redundancy
  • logic ownership
  • permission-less collaborative workplace
  • serverless-like scaling
  • model deployment

Conclusion

In this paper, we presented the concept of Dynamic Programmable NFT. We argued that it is possible to generalize the concept of NFT as it is known today by considering it a single contract call.

In this context, minting an NFT is indeed equivalent to buying a single unit of on-chain computation that can be called forever, for free.

When several NFTs are linked together, this creates a graph of computation that can be seen as a decentralized intelligent production of the network. In other words, people can in a decentralized fashion co-create logic and make this logic accessible to the outer world.

The creation of such decentralized graphs of computation on-chain can have application in transparency of public polices, research, but also in consumer apps like spreadsheets.

We hope that this will pave the way to a broader adoption of the blockchain and highlight the value of a decentralized computer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment