Skip to content

Commit

Permalink
FEATURE: V2 NonFungibleToken Standard (#126)
Browse files Browse the repository at this point in the history
* first work on new standard

* first draft of v2 NFT standard

* add type parameter to createEmptyCollection

* merge from master

* PR comments

* PR comments

* make test

* move everything into the interface

* use getID and proper transfer

* use views in events, return optionals, and dont hardcode

* add createEmptyCollection

* isAcceptedType

* mar13 update

* PR comments

* separate nft views, add uuid, event args, resolver, and get contracts to deploy

* use events in interfaces

* integrate stable cadence changes

* comment out events

* modify view functions

* remove deposit impl

* remove view from dictToTraits

* remove view modifier from resolveView

* remove restricted types

* remove AnyStruct

* update dependencies and fix example NFT

* fix import cadence issue and remove view

* comment out default method conflicts

* comment out usedUUID

* comment out CollectionPublic

* generate assets

* remove CollectionPublic from metadata views

* add get length

* fixed missing auth for provider type

* update NFTForwarding for Cadence 1.0

* update NFTForwarding transactions for new implementation

* bump ci flow cli version to Cadence 1.0 pre-release

* fix ExampleNFT-v2 account storage access bug

* update FungibleToken Cadence 1.0 implementation

* update flow.json with v2 contract implementations

* update txns & scripts for Cadence 1.0 + supporting ExampleNFT-v2 Cadence tests

* update Makefile & go tests

* update NFT-v2 to emit events from interface pre/post conditions

* update ExampleNFT-v2 Cadence tests & go assets

* update ExampleNFT-v2 providerPath

* update go tests and supporting txns

* update go assets

* fix test cases to correspond with new contract implementations

* add metadata trait test validation to go suite

* update flow.json with emulator aliases

* fix NFTForwarding.Forwarder conformance to .Receiver & rename events

* add NFTForwarding Cadence tests & fix txn bugs

* complete NFTForwarding cadence tests + supporting txns & scripts

* update .gitignore to include coverage.json

* fix contract.go placeholder variable name

* update NFT.transfer() pre-conditions on recevier.check() & .getIDs().contains(id)

* update instances of Collection.getIDs().length with .getLength()

* add comments to test_helpers methods

* update go assets

* re-add .transfer() pre-condition on .getIDs().contains(id)

* update go assets

* remove transfer, withdraw methods, and default implementations

* add universal collection and other small changes

* added updated event

* Update contracts/NonFungibleToken-v2.cdc

* add sub NFT, remove borrowNFTSafe

* change updated to use entitled reference

* remove custom destructors

* remove resource destructor

* generate

* default destroy event, event args, id field, remove getID, go test updates

* remove path functions, add view function, update view resolver names

* add getIDs

* remove view, add getIDs

* add subNFTs method, update forwarder events

* add support for multiple type definitions

* remove get NFT types, update basic NFT and UniversalCollection, and update transactions

* Update Cadence version

* add event emission functions

* revert event emission functions

* update sdk and cadence deps

* update to SDK v1.0.0-M1

* update to SDK v1.0.0-M1

* remove event functions, make update access contract, fix some tests

* update transactions get get Go tests passing

* add collection public interface back and remove some go tests

* use receiver and collection in public linked type

* use correct public linked types and get most tests passing

* add updated FT

* remove burner

* add generic setup and transfer transactions

* update generic transfer transactions and add tests

* change address arguments to strings

* use withhexprefix

* use latest import syntax update readme and use correct borrow syntax

* update ci to use flow-c1

* add info about old branch to README

* update previewnet address

* update view resolver comments

* remove basic NFT and UniversalCollection

* Make ExampleNFT compatible for contract update

* Add back the type assertion for the deposited token

* Make the collection path backward compatible

Co-authored-by: Navid TehraniFar <navid.tehranifar@flowfoundation.org>

* remove docs, INFT, and update paths

* make ci

* Cleanup redundant paths

* re-add royalties to mint and address PR comments

* fix FungibleToken import address

* add pre-conditions to createEmpty and update dependencies

* make emitNFTUpdated access all

* reorg transactions and scripts

* fix import placeholders

* add correct getLength impl

* add NFT storage requirement and forEachID function

* add def impl for getLength and fix iterator test

* update iterate script and test access(all)

* update comments

* use capabilities borrow

* make ownedNFTs access(all)

* remove Owner entitlement

* Update ExampleNFT v2 with EVMBridgedMetadata view from master (#215)

* implement EVMBridgedMetadata in ExampleNFT

* add updated MetadataViews EVMBridgedMetadata view to go tests

* add docs covering new views

* fix EVMBridgedMetadata.symbol assignment

* add EVMBridgedMetadata implementation test coverage

* update Flow-CLI install step in ci workflow action

* remove merged docs

* remove merged script

* update to latest CLI and emulator and fix txs and tests to not use optional capabilities get

---------

Co-authored-by: Bjarte Stien Karlsen <bjarte@bjartek.org>
Co-authored-by: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com>
Co-authored-by: Bastian Müller <bastian@turbolent.com>
Co-authored-by: Supun Setunga <supun.setunga@gmail.com>
Co-authored-by: Navid TehraniFar <navid.tehranifar@flowfoundation.org>
  • Loading branch information
6 people authored May 6, 2024
1 parent d8db9b2 commit 0ceed9b
Show file tree
Hide file tree
Showing 101 changed files with 9,639 additions and 4,055 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '1.19'
go-version: '1.20'
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install Flow CLI
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)"
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/feature/stable-cadence/install.sh)"
- name: Flow CLI Version
run: flow version
run: flow-c1 version
- name: Update PATH
run: echo "/root/.local/bin" >> $GITHUB_PATH
- name: Run tests
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
.vscode
node_modules
node_modules
coverage.json
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
test:
$(MAKE) generate -C lib/go
$(MAKE) test -C lib/go
flow test --cover --covercode="contracts" tests/test_*.cdc
flow-c1 test --cover --covercode="contracts" tests/*.cdc

.PHONY: ci
ci:
$(MAKE) ci -C lib/go
flow test --cover --covercode="contracts" tests/test_*.cdc
flow-c1 test --cover --covercode="contracts" tests/*.cdc
134 changes: 43 additions & 91 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

This standard defines the minimum functionality required to
implement a safe, secure, and easy-to-use non-fungible token
contract on the [Flow blockchain](https://flow.com/
contract on the [Flow blockchain](https://flow.com/)

The version of the contracts in the `master` branch is the
Cadence 1.0 version of the contracts and is not the same
as the ones that are currently deployed to testnet and mainnet.
See the `cadence-0.42` branch for the currently deployed versions.

## What is Cadence?

Expand All @@ -26,18 +31,21 @@ There is no need to deploy them yourself.

Note: With the emulator, you must use the -contracts flag to deploy these contracts.

| Network | Contract Address |
| --------------- | -------------------- |
| Emulator/Canary | `0xf8d6e0586b0a20c7` |
| Testnet | `0x631e88ae7f1d7c20` |
| Mainnet | `0x1d7e57aa55817448` |
| Network | Contract Address |
| ------------------| -------------------- |
| Emulator/Canary | `0xf8d6e0586b0a20c7` |
| PreviewNet | `0xb6763b4399a888c8` |
| Testnet/Crescendo | `0x631e88ae7f1d7c20` |
| Mainnet | `0x1d7e57aa55817448` |

## Core Types

Contracts that implement the `NonFungibleToken` interface are required to implement two resource interfaces:
Contracts that implement the `NonFungibleToken` interface are expected
to utilize 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 and defines ways
- `NFT` - A resource interface that describes the structure of a single NFT.
- `Collection` - A resource interface that describes an object
that can hold multiple NFTs of the same type and defines ways
to deposit, withdraw, and query information about the stored NFTs.

Users typically store one collection per NFT type, saved at a well-known location in their account storage.
Expand All @@ -47,69 +55,32 @@ Contracts that implement the `NonFungibleToken` interface are required to implem
## Core Features

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

### Create a new NFT collection

Create a new collection using the `Token.createEmptyCollection()` function.
Create a new collection using the `Token.createEmptyCollection(nftType: Type)` function.

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

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

```cadence
let collection <- ExampleNFT.createEmptyCollection()
account.save(<-collection, to: ExampleNFT.CollectionStoragePath)
// create a public capability for the collection
account.link<&{NonFungibleToken.CollectionPublic}>(
ExampleNFT.CollectionPublicPath,
target: ExampleNFT.CollectionStoragePath
)
```
and public a capability to their collection.

### Withdraw an NFT

Withdraw an `NFT` from a `Collection` using the [`withdraw()`](contracts/ExampleNFT.cdc#L36-L42) function.
This function emits the [`Withdraw`](contracts/ExampleNFT.cdc#L12) event.

```cadence
let collectionRef = account.borrow<&ExampleNFT.Collection>(from: ExampleNFT.CollectionStoragePath)
?? 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)
```
Withdraw an `NFT` from a `Collection` using the [`withdraw()`](contracts/ExampleNFT.cdc#L160) function.
This function emits the [`NonFungibleToken.Withdrawn`](contracts/NonFungibleToken.cdc#L78) event automatically.

### Deposit an NFT

Deposit an `NFT` into a `Collection` using the [`deposit()`](contracts/ExampleNFT.cdc#L46-L57) function.
This function emits the [`Deposit`](contracts/ExampleNFT.cdc#L13) 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.

```cadence
let nft: ExampleNFT.NFT
// ...
let collection = account.getCapability(ExampleNFT.CollectionPublicPath)
.borrow<&{NonFungibleToken.CollectionPublic}>()
?? panic("Could not borrow a reference to the receiver's collection")
collection.deposit(token: <-nft)
```
Deposit an `NFT` into a `Collection` using the [`deposit()`](contracts/ExampleNFT.cdc#L169-L176) function.
This function emits the [`NonFungibleToken.Deposited`](contracts/NonFungibleToken.cdc#L86) event automatically.

#### ⚠️ 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.
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:

Expand All @@ -120,18 +91,17 @@ let token <- token as! @ExampleNFT.NFT

### List NFTs in an account

Return a list of NFTs in a `Collection` using the [`getIDs`](contracts/ExampleNFT.cdc#L59-L62) function.
Return a list of NFTs in a `Collection` using the [`getIDs`](contracts/ExampleNFT.cdc#L179) function.

This function is available on the `NonFungibleToken.CollectionPublic` interface,
which accounts publish as public capability.
### Return the NFT type that a collection can accept in a deposit

```cadence
let collection = account.getCapability(ExampleNFT.CollectionPublicPath)
.borrow<&{NonFungibleToken.CollectionPublic}>()
?? panic("Could not borrow a reference to the receiver's collection")
Return types of NFTs that a `Collection` can accept in a deposit
using the [`getSupportedNFTTypes`](contracts/ExampleNFT.cdc#L143-L157) functions.

let ids = collection.getIDs()
```
### Get Available SubNFTs, if any

Some NFTs can own other NFTs, the standard provides a [function](contracts/NonFungibleToken.cdc#L111-L131) that
projects can optionally implement to return information the owned NFTs.

## NFT Metadata

Expand All @@ -157,29 +127,6 @@ including the name, description, image and owner.

**Source: [get_nft_metadata.cdc](scripts/get_nft_metadata.cdc)**

```cadence
// Get the regular public capability
let collection = account.getCapability(ExampleNFT.CollectionPublicPath)
.borrow<&{ExampleNFT.ExampleNFTCollectionPublic}>()
?? panic("Could not borrow a reference to the collection")
// Borrow a reference to the NFT as usual
let nft = collection.borrowExampleNFT(id: 42)
?? panic("Could not borrow a reference to the NFT")
// Call the resolveView method
// Provide the type of the view that you want to resolve
// View types are defined in the MetadataViews contract
// You can see if an NFT supports a specific view type by using the `getViews()` method
if let view = nft.resolveView(Type<MetadataViews.Display>()) {
let display = view as! MetadataViews.Display
log(display.name)
log(display.description)
log(display.thumbnail)
}
```

### How to implement metadata

The [example NFT contract](contracts/ExampleNFT.cdc) shows a basic example
Expand Down Expand Up @@ -245,13 +192,18 @@ but without most of the downsides.
## How to test the standard

If you want to test out these contracts, we recommend either testing them
with the [Flow Playground](https://play.onflow.org)
with the [Flow Playground](https://play.flow.com)
or with the [Visual Studio Code Extension](https://github.com/onflow/flow/blob/master/docs/vscode-extension.md#cadence-visual-studio-code-extension).

The steps to follow are:
If you are not making/testing any modifications to the standard contracts,
they are already deployed to the addresses listed above and you can just import
from those directly instead of deploying them yourself.

If you want to test changes to the standards, the steps to follow are:

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

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.
Expand Down
Loading

0 comments on commit 0ceed9b

Please sign in to comment.