Skip to content

Commit

Permalink
Add core nft view (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
bshahid331 authored Jul 8, 2022
1 parent 94f0bf2 commit 4a62e70
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 4 deletions.
46 changes: 46 additions & 0 deletions contracts/MetadataViews.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,39 @@ pub contract MetadataViews {
pub fun getIDs(): [UInt64]
}

/// NFTView is a group of views used to give a complete picture of an NFT
///
pub struct NFTView {
pub let id: UInt64
pub let uuid: UInt64
pub let display: Display?
pub let externalURL: ExternalURL?
pub let collectionData: NFTCollectionData?
pub let collectionDisplay: NFTCollectionDisplay?
pub let royalties: Royalties?
pub let traits: Traits?

init(
id : UInt64,
uuid : UInt64,
display : Display?,
externalURL : ExternalURL?,
collectionData : NFTCollectionData?,
collectionDisplay : NFTCollectionDisplay?,
royalties : Royalties?,
traits: Traits?
) {
self.id = id
self.uuid = uuid
self.display = display
self.externalURL = externalURL
self.collectionData = collectionData
self.collectionDisplay = collectionDisplay
self.royalties = royalties
self.traits = traits
}
}

/// Display is a basic view that includes the name, description and
/// thumbnail for an object. Most objects should implement this view.
///
Expand Down Expand Up @@ -619,4 +652,17 @@ pub contract MetadataViews {
return nil
}

pub fun getNFTView(id: UInt64, viewResolver: &{Resolver}) : NFTView {
return NFTView(
id : id,
uuid: viewResolver.uuid,
display: self.getDisplay(viewResolver),
externalURL : self.getExternalURL(viewResolver),
collectionData : self.getNFTCollectionData(viewResolver),
collectionDisplay : self.getNFTCollectionDisplay(viewResolver),
royalties : self.getRoyalties(viewResolver),
traits : self.getTraits(viewResolver)
)
}

}
6 changes: 3 additions & 3 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions lib/go/templates/internal/assets/assets.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions lib/go/templates/script_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const (
filenameGetCollectionLength = "scripts/get_collection_length.cdc"
filenameGetTotalSupply = "scripts/get_total_supply.cdc"
filenameGetNFTMetadata = "scripts/get_nft_metadata.cdc"
filenameGetNFTView = "scripts/get_nft_view.cdc"
)

// GenerateBorrowNFTScript creates a script that retrieves an NFT collection
Expand All @@ -27,6 +28,12 @@ func GenerateGetNFTMetadataScript(nftAddress, exampleNFTAddress, metadataAddress
return replaceAddresses(code, nftAddress, exampleNFTAddress, metadataAddress, flow.EmptyAddress)
}

// GenerateGetNFTViewScript creates a script that returns the rollup NFT View for an NFT.
func GenerateGetNFTViewScript(nftAddress, exampleNFTAddress, metadataAddress flow.Address) []byte {
code := assets.MustAssetString(filenameGetNFTView)
return replaceAddresses(code, nftAddress, exampleNFTAddress, metadataAddress, flow.EmptyAddress)
}

// GenerateGetCollectionLengthScript creates a script that retrieves an NFT collection
// from storage and tries to borrow a reference for an NFT that it owns.
// If it owns it, it will not fail.
Expand Down
129 changes: 128 additions & 1 deletion lib/go/test/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestGetNFTMetadata(t *testing.T) {
exampleNFTAccountKey,
exampleNFTSigner)

t.Run("Should be able to verify the metadata of the minted NFT through the Display View", func(t *testing.T) {
t.Run("Should be able to verify the metadata of the minted NFT", func(t *testing.T) {

// Run a script to get the Display view for the specified NFT ID
script := templates.GenerateGetNFTMetadataScript(nftAddress, exampleNFTAddress, metadataAddress)
Expand Down Expand Up @@ -193,6 +193,133 @@ func TestGetNFTMetadata(t *testing.T) {
})
}

func TestGetNFTView(t *testing.T) {
b, accountKeys := newTestSetup(t)

// Create new keys for the NFT contract account
// and deploy all the NFT contracts
exampleNFTAccountKey, exampleNFTSigner := accountKeys.NewWithSigner()
nftAddress, metadataAddress, exampleNFTAddress := deployNFTContracts(t, b, exampleNFTAccountKey)

// Mint a single NFT with standard royalty cuts and metadata
mintExampleNFT(t, b,
accountKeys,
nftAddress, metadataAddress, exampleNFTAddress,
exampleNFTAccountKey,
exampleNFTSigner)

t.Run("Should be able to verify the nft metadata view of the minted NFT", func(t *testing.T) {

// Run a script to get the Display view for the specified NFT ID
script := templates.GenerateGetNFTViewScript(nftAddress, exampleNFTAddress, metadataAddress)
result := executeScriptAndCheck(
t, b,
script,
[][]byte{
jsoncdc.MustEncode(cadence.NewAddress(exampleNFTAddress)),
jsoncdc.MustEncode(cadence.NewUInt64(0)),
},
)

// Expected metadata
const (
name = "Example NFT 0"
description = "This is an example NFT"
thumbnail = "example.jpeg"
externalURL = "https://example-nft.onflow.org/0"
)

nftResult := result.(cadence.Struct)

assert.Equal(t, cadence.NewUInt64(0), nftResult.Fields[0])
assert.Equal(t, cadence.String(name), nftResult.Fields[2])
assert.Equal(t, cadence.String(name), nftResult.Fields[2])
assert.Equal(t, cadence.String(description), nftResult.Fields[3])
assert.Equal(t, cadence.String(thumbnail), nftResult.Fields[4])

royalties := toJson(t, nftResult.Fields[5])
// Declared an empty interface of type Array
var results map[string]interface{}

// Unmarshal or Decode the JSON to the interface.
json.Unmarshal([]byte(royalties), &results)

// Verify external URL view result is as expected
assert.Equal(t, cadence.String(externalURL), nftResult.Fields[6])

// Verify NFTCollectionData results are as expected
const (
pathName = "exampleNFTCollection"
collectionType = "A.f3fcd2c1a78f5eee.ExampleNFT.Collection"
collectionPublicType = "A.f3fcd2c1a78f5eee.ExampleNFT.ExampleNFTCollectionPublic"
nftCollectionPublicType = "A.01cf0e2f2f715450.NonFungibleToken.CollectionPublic"
nftReceiverType = "A.01cf0e2f2f715450.NonFungibleToken.Receiver"
resolverCollectionType = "A.179b6b1cb6755e31.MetadataViews.ResolverCollection"
providerType = "A.01cf0e2f2f715450.NonFungibleToken.Provider"
)
assert.Equal(t, cadence.Path{Domain: "public", Identifier: pathName}, nftResult.Fields[7])
assert.Equal(t, cadence.Path{Domain: "storage", Identifier: pathName}, nftResult.Fields[8])
assert.Equal(t, cadence.Path{Domain: "private", Identifier: pathName}, nftResult.Fields[9])
assert.Equal(t, cadence.String(fmt.Sprintf("&%s{%s}", collectionType, collectionPublicType)), nftResult.Fields[10])
assert.Equal(t, cadence.String(fmt.Sprintf("&%s{%s,%s,%s,%s}", collectionType, collectionPublicType, nftCollectionPublicType, nftReceiverType, resolverCollectionType)), nftResult.Fields[11])
assert.Equal(t, cadence.String(fmt.Sprintf("&%s{%s,%s,%s,%s}", collectionType, collectionPublicType, nftCollectionPublicType, providerType, resolverCollectionType)), nftResult.Fields[12])

// Verify NFTCollectionDisplay results are as expected
const (
collectionName = "The Example Collection"
collectionDescription = "This collection is used as an example to help you develop your next Flow NFT."
collectionImage = "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
collectionExternalURL = "https://example-nft.onflow.org"
)
assert.Equal(t, cadence.String(collectionName), nftResult.Fields[13])
assert.Equal(t, cadence.String(collectionDescription), nftResult.Fields[14])
assert.Equal(t, cadence.String(collectionExternalURL), nftResult.Fields[15])
assert.Equal(t, cadence.String(collectionImage), nftResult.Fields[16])
assert.Equal(t, cadence.String(collectionImage), nftResult.Fields[17])

minterName, _ := cadence.NewString("minter")

traitsView := nftResult.Fields[19].(cadence.Struct)
traits := traitsView.Fields[0].(cadence.Array)

mintTrait := traits.Values[0].(cadence.Struct)
assert.Equal(t, minterName, mintTrait.Fields[0])
assert.Equal(t, fmt.Sprintf("0x%s", exampleNFTAddress.String()), mintTrait.Fields[1].String())
assert.Equal(t, cadence.NewOptional(nil), mintTrait.Fields[2])
assert.Equal(t, cadence.NewOptional(nil), mintTrait.Fields[3])

blockNumberName, _ := cadence.NewString("mintedBlock")
blockNumberTrait := traits.Values[1].(cadence.Struct)
assert.Equal(t, blockNumberName, blockNumberTrait.Fields[0])
assert.Equal(t, cadence.NewUInt64(13), blockNumberTrait.Fields[1])
assert.Equal(t, cadence.NewOptional(nil), blockNumberTrait.Fields[2])
assert.Equal(t, cadence.NewOptional(nil), blockNumberTrait.Fields[3])

mintedTimeName, _ := cadence.NewString("mintedTime")
mintedTimeDisplayType, _ := cadence.NewString("Date")
mintedTimeTrait := traits.Values[2].(cadence.Struct)
assert.Equal(t, mintedTimeName, mintedTimeTrait.Fields[0])
assert.Equal(t, cadence.NewOptional(mintedTimeDisplayType), mintedTimeTrait.Fields[2])

fooName, _ := cadence.NewString("foo")
fooValue, _ := cadence.NewString("bar")
fooTrait := traits.Values[3].(cadence.Struct)
fooRarityOptional := fooTrait.Fields[3].(cadence.Optional)
fooRarity := fooRarityOptional.Value.(cadence.Struct)
rarityDescription, _ := cadence.NewString("Common")
assert.Equal(t, fooName, fooTrait.Fields[0])
assert.Equal(t, cadence.NewOptional(fooValue), fooTrait.Fields[1])
fooRarityScore := fooRarity.Fields[0].(cadence.Optional).Value
score, _ := cadence.NewUFix64("10.0")
assert.Equal(t, fooRarityScore, score)
fooRarityMax := fooRarity.Fields[1].(cadence.Optional).Value
max, _ := cadence.NewUFix64("100.0")
assert.Equal(t, max, fooRarityMax)
assert.Equal(t, fooRarity.Fields[2], cadence.NewOptional(rarityDescription))

})
}

func TestSetupCollectionFromNFTReference(t *testing.T) {
b, accountKeys := newTestSetup(t)

Expand Down
Loading

0 comments on commit 4a62e70

Please sign in to comment.