Git Product home page Git Product logo

flow-nft's Introduction

Flow Non-Fungible Token Standard

This standard defines the minimum functionality required to implement a safe, secure, and easy-to-use non-fungible token contract on the Flow blockchain.

What is Cadence?

Cadence is the resource-oriented programming language for developing smart contracts on Flow.

Before reading this standard, we recommend completing the Cadence tutorials to build a basic understanding of the programming language.

Resource-oriented programming, and by extension Cadence, provides an ideal programming model for non-fungible tokens (NFTs). Users are able to store their NFT objects directly in their accounts and transact peer-to-peer. Learn more in this blog post about resources.

Core features

The NonFungibleToken contract defines the following set of functionality that must be included in each implementation.

Contracts that implement the NonFungibleToken interface are required to implement two resource interfaces:

  • NFT - A resource that describes the structure of a single NFT.

  • Collection - A resource that can hold multiple NFTs of the same type.

    Users typically store one collection per NFT type, saved at a well-known location in their account storage.

    For example, all NBA Top Shot Moments owned by a single user are held in a TopShot.Collection stored in their account at the path /storage/MomentCollection.

Create a new NFT collection

Create a new collection using the createEmptyCollection function.

This function MUST return an empty collection that contains no NFTs.

Users typically save new collections to a well-known location in their account and link the NonFungibleToken.CollectionPublic interface as a public capability.

let collection <- ExampleNFT.createEmptyCollection()

account.save(<-collection, to: /storage/ExampleNFTCollection)

// create a public capability for the collection
account.link<&{NonFungibleToken.CollectionPublic}>(
    /public/ExampleNFTCollection,
    target: /storage/ExampleNFTCollection
)

Withdraw an NFT

Withdraw an NFT from a Collection using the withdraw function. This function emits the Withdraw event.

let collectionRef = account.borrow<&ExampleNFT.Collection>(from: /storage/ExampleNFTCollection)
    ?? panic("Could not borrow a reference to the owner's collection")

// withdraw the NFT from the owner's collection
let nft <- collectionRef.withdraw(withdrawID: 42)

Deposit an NFT

Deposit an NFT into a Collection using the deposit function. This function emits the Deposit event.

This function is available on the NonFungibleToken.CollectionPublic interface, which accounts publish as public capability. This capability allows anybody to deposit an NFT into a collection without accessing the entire collection.

let nft: ExampleNFT.NFT

// ...

let collection = account.getCapability(/public/ExampleNFTCollection)
    .borrow<&{NonFungibleToken.CollectionPublic}>()
    ?? panic("Could not borrow a reference to the receiver's collection")

collection.deposit(token: <-nft)

⚠️ Important

In order to comply with the deposit function in the interface, an implementation MUST take a @NonFungibleToken.NFT resource as an argument. This means that anyone can send a resource object that conforms to @NonFungibleToken.NFT to a deposit function. In an implementation, you MUST cast the token as your specific token type before depositing it or you will deposit another token type into your collection. For example:

let token <- token as! @ExampleNFT.NFT

List NFTs in an account

Return a list of NFTs in a Collection using the getIDs function.

This function is available on the NonFungibleToken.CollectionPublic interface, which accounts publish as public capability.

let collection = account.getCapability(/public/ExampleNFTCollection)
    .borrow<&{NonFungibleToken.CollectionPublic}>()
    ?? panic("Could not borrow a reference to the receiver's collection")

let ids = collection.getIDs()

NFT Metadata

NFT metadata is represented in a flexible and modular way using the standard proposed in FLIP-0636.

When writing an NFT contract, you should implement the MetadataViews.Resolverinterface, which allows your NFT to implement one or more metadata types called views.

Each view represents a different type of metadata, such as an on-chain creator biography or an off-chain video clip.

How to read metadata

This example shows how to read basic information about an NFT including the name, description, image and owner.

Source: get_nft_metadata.cdc

import ExampleNFT from "..."
import MetadataViews from "..."

// ...

let collection = account.getCapability(ExampleNFT.CollectionPublicPath)
    .borrow<&{ExampleNFT.ExampleNFTCollectionPublic}>()
    ?? panic("Could not borrow a reference to the collection")

let nft = collection.borrowExampleNFT(id: 42)
    ?? panic("Could not borrow a reference to the NFT")
    
if let view = nft.resolveView(Type<MetadataViews.Display>()) {
    let display = view as! MetadataViews.Display

    log(display.name)
    log(display.description)
    log(display.thumbnail)
}

// The owner is stored directly on the NFT object
let owner: Address = nft.owner!.address!

// Inspect the type of this NFT to verify its origin
let nftType = nft.getType()

// `nftType.identifier` is `A.f3fcd2c1a78f5eee.ExampleNFT.NFT`

How to implement metadata

The example NFT contract shows how to implement metadata views.

List of common views

Name Purpose Status Source
Display Return the basic representation of an NFT. Implemented MetadataViews.cdc
HTTPFile A file available at an HTTP(S) URL. Implemented MetadataViews.cdc
IPFSFile A file stored in IPFS. Implemented MetadataViews.cdc

How to propose a new view

Please open a pull request to propose a new metadata view or changes to an existing view.

Feedback

As Flow and Cadence are still new, we expect this standard to evolve based on feedback from both developers and users.

We'd love to hear from anyone who has feedback. For example:

  • Are there any features that are missing from the standard?
  • Are the current features defined in the best way possible?
  • Are there any pre and post conditions that are missing?
  • Are the pre and post conditions defined well enough? Error messages?
  • Are there any other actions that need an event defined for them?
  • Are the current event definitions clear enough and do they provide enough information?
  • Are the variable, function, and parameter names descriptive enough?
  • Are there any openings for bugs or vulnerabilities that we are not noticing?

Please create an issue in this repository if there is a feature that you believe needs discussing or changing.

Comparison to other standards on Ethereum

This standard covers much of the same ground as ERC-721 and ERC-1155, but without most of the downsides.

  • Tokens cannot be sent to contracts that don't understand how to use them, because an account needs to have a Receiver or Collection in its storage to receive tokens.
  • If the recipient is a contract that has a stored Collection, the tokens can just be deposited to that Collection without having to do a clunky approve, transferFrom.
  • Events are defined in the contract for withdrawing and depositing, so a recipient will always be notified that someone has sent them tokens with their own deposit event.
  • This version can support batch transfers of NFTs. Even though it isn't explicitly defined in the contract, a batch transfer can be done within a transaction by just withdrawing all the tokens to transfer, then depositing them wherever they need to be, all atomically.
  • Transfers can trigger actions because users can define custom Receivers to execute certain code when a token is sent.
  • Easy ownership indexing: rathing than iterating through all tokens to find which ones you own, you have them all stored in your account's collection and can get the list of the ones you own instantly.

How to test the standard

If you want to test out these contracts, we recommend either testing them with the Flow Playground or with the Visual Studio Code Extension.

The steps to follow are:

  1. Deploy NonFungibleToken.cdc
  2. Deploy ExampleNFT.cdc, importing NonFungibleToken from the address you deployed it to.

Then you can experiment with some of the other transactions and scripts in transactions/ or even write your own. You'll need to replace some of the import address placeholders with addresses that you deploy to, as well as some of the transaction arguments.

Running automated tests

You can find automated tests in the lib/go/test/nft_test.go file. It uses the transaction templates that are contained in the lib/go/templates/templates.go file. Currently, these rely on a dependency from a private dapper labs repository to run, so external users will not be able to run them. We are working on making all of this public so anyone can run tests, but haven't completed this work yet.

Bonus features

(These could each be defined as a separate interface and standard and are probably not part of the main standard) They are not implemented in this repository yet

10- Withdrawing tokens from someone else's Collection by using their Provider reference.

  • approved withdraw event
  • Providing a resource that only approves an account to withdraw a specific amount per transaction or per day/month/etc.
  • Returning the list of tokens that an account can withdraw for another account.
  • Reading the balance of the account that you have permission to send tokens for
  • Owner is able to increase and decrease the approval at will, or revoke it completely
    • This is much harder than anticipated

11 - Standard for Composability/Extensibility

12 - Minting a specific amount of tokens using a specific minter resource that an owner can control

  • tokens minted event
  • Setting a cap on the total number of tokens that can be minted at a time or overall
  • Setting a time frame where this is allowed

13 - Burning a specific amount of tokens using a specific burner resource that an owner controls

  • tokens burnt event
  • Setting a cap on the number of tokens that can be burned at a time or overall
  • Setting a time frame where this is allowed

14 - Pausing Token transfers (maybe a way to prevent the contract from being imported? probably not a good idea)

15 - Cloning the token to create a new token with the same distribution

License

The works in these files:

are under the Unlicense.

flow-nft's People

Contributors

psiemens avatar turbolent avatar joshuahannan avatar albeethekid avatar brodieve avatar solipsis avatar jkan2 avatar laynelafrance avatar 10thfloor avatar michaelgold avatar nasir-ali-shah avatar timmymmit avatar phi-line avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.