Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backfill play tagline for NFT Catalog #180

Merged
merged 11 commits into from
Nov 16, 2022
37 changes: 35 additions & 2 deletions contracts/TopShot.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ pub contract TopShot: NonFungibleToken {
self.playID = TopShot.nextPlayID
self.metadata = metadata
}

// This function is intended to backfill the Play on blockchain with a more detailed
// description of the Play. The benefit of having the description is that anyone would
// be able to know the story of the Play directly from Flow
access(contract) fun updateTagline(tagline: String): UInt32 {
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
self.metadata["Tagline"] = tagline

TopShot.playDatas[self.playID] = self
return self.playID
}
}

// A Set is a grouping of Plays that have occured in the real world
Expand Down Expand Up @@ -683,8 +693,8 @@ pub contract TopShot: NonFungibleToken {
.concat(" ")
.concat(playType)
}

pub fun description(): String {
access(self) fun buildDescString(): String {
let setName: String = TopShot.getSetName(setID: self.data.setID) ?? ""
let serialNumber: String = self.data.serialNumber.toString()
let seriesNumber: String = TopShot.getSetSeries(setID: self.data.setID)?.toString() ?? ""
Expand All @@ -696,6 +706,19 @@ pub contract TopShot: NonFungibleToken {
.concat(serialNumber)
}

// The description of the Moment. If Tagline property of the play is empty, compose it using the buildDescString function
// If the Tagline property is not empty, use that as the description
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
pub fun description(): String {
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
let playDesc: String? = TopShot.getPlayMetaDataByField(playID: self.data.playID, field: "Tagline")
zihehuang marked this conversation as resolved.
Show resolved Hide resolved

if let desc = playDesc {
if desc.length > 0 {
return desc
}
}
return self.buildDescString()
}

// All supported metadata views for the Moment including the Core NFT Views
pub fun getViews(): [Type] {
return [
Expand Down Expand Up @@ -943,6 +966,16 @@ pub contract TopShot: NonFungibleToken {
return newID
}

// Temporarily enabled so the description of the play can be backfilled
// Parameters: playID: The ID of the play to update
// tagline: A string to be used as the tagline for the play
// Returns: The ID of the play
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
pub fun updatePlayTagline(playID: UInt32, tagline: String): UInt32 {
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
let tmpPlay = TopShot.playDatas[playID] ?? panic("playID does not exist")
tmpPlay.updateTagline(tagline: tagline)
return playID
}

// createSet creates a new Set resource and stores it
// in the sets mapping in the TopShot contract
//
Expand Down
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.

9 changes: 9 additions & 0 deletions lib/go/templates/topshot_admin_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
const (
transactionsPath = "../../../transactions/"
createPlayFilename = "admin/create_play.cdc"
updateTaglineFilename = "admin/update_tagline.cdc"
createSetFilename = "admin/create_set.cdc"
addPlayFilename = "admin/add_play_to_set.cdc"
addPlaysFilename = "admin/add_plays_to_set.cdc"
Expand Down Expand Up @@ -36,6 +37,14 @@ func GenerateMintPlayScript(env Environment) []byte {
return []byte(replaceAddresses(code, env))
}

// GenerateMintPlayScript creates a new play data struct
// and initializes it with metadata
func GenerateUpdateTaglineScript(env Environment) []byte {
code := assets.MustAssetString(transactionsPath + updateTaglineFilename)

return []byte(replaceAddresses(code, env))
}

// GenerateMintSetScript creates a new Set struct and initializes its metadata
func GenerateMintSetScript(env Environment) []byte {
code := assets.MustAssetString(transactionsPath + createSetFilename)
Expand Down
25 changes: 23 additions & 2 deletions lib/go/test/topshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ func TestMintNFTs(t *testing.T) {
tb.CreatePlay(t, []cadence.KeyValuePair{{Key: firstName, Value: lebron}, {Key: playType, Value: dunk}})
})

t.Run("Should be able to update an existing Play", func(t *testing.T) {
result := executeScriptAndCheck(t, b, templates.GenerateGetPlayMetadataFieldScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt32(1)), jsoncdc.MustEncode(cadence.String("FullName"))})
assert.Equal(t, CadenceString("Lebron"), result)

tb.UpdateTagline(t, []cadence.KeyValuePair{{Key: cadence.UInt32(1), Value: CadenceString("lorem ipsum")}})
result = executeScriptAndCheck(t, b, templates.GenerateGetPlayMetadataFieldScript(env), [][]byte{jsoncdc.MustEncode(cadence.UInt32(1)), jsoncdc.MustEncode(cadence.String("Tagline"))})
assert.Equal(t, CadenceString("lorem ipsum"), result)
})

// Admin sends transactions to create multiple plays
t.Run("Should be able to create multiple new Plays", func(t *testing.T) {
metadatas := [][]cadence.KeyValuePair{
Expand Down Expand Up @@ -321,7 +330,7 @@ func TestMintNFTs(t *testing.T) {
t.Run("Should be able to get moments metadata", func(t *testing.T) {
// Tests to ensure that all core metadataviews are resolvable
expectedMetadataName := "Lebron Dunk"
expectedMetadataDescription := "A series 0 Genesis moment with serial number 1"
expectedMetadataDescription := "lorem ipsum"
expectedMetadataThumbnail := "https://assets.nbatopshot.com/media/1?width=256"
expectedMetadataExternalURL := "https://nbatopshot.com/moment/1"
expectedStoragePath := "/storage/MomentCollection"
Expand All @@ -332,7 +341,7 @@ func TestMintNFTs(t *testing.T) {
expectedCollectionSquareImage := "https://nbatopshot.com/static/img/og/og.png"
expectedCollectionBannerImage := "https://nbatopshot.com/static/img/top-shot-logo-horizontal-white.svg"
expectedRoyaltyReceiversCount := 1
expectedTraitsCount := 5
expectedTraitsCount := 6
expectedVideoURL := "https://assets.nbatopshot.com/media/1/video"

resultNFT := executeScriptAndCheck(t, b, templates.GenerateGetNFTMetadataScript(env), [][]byte{jsoncdc.MustEncode(cadence.Address(topshotAddr)), jsoncdc.MustEncode(cadence.UInt64(1))})
Expand Down Expand Up @@ -608,6 +617,18 @@ func (b *topshotTestBlockchain) CreatePlay(t *testing.T, metadata []cadence.KeyV
)
}

func (b *topshotTestBlockchain) UpdateTagline(t *testing.T, plays []cadence.KeyValuePair) {
tx := createTxWithTemplateAndAuthorizer(b.Blockchain, templates.GenerateUpdateTaglineScript(b.env), b.topshotAdminAddr)
tags := cadence.NewDictionary(plays)
_ = tx.AddArgument(tags)

signAndSubmit(
t, b.Blockchain, tx,
[]flow.Address{b.ServiceKey().Address, b.topshotAdminAddr}, []crypto.Signer{b.serviceKeySigner, b.topshotAdminSigner},
false,
)
}

func (b *topshotTestBlockchain) CreateSet(t *testing.T, setName string) {
tx := createTxWithTemplateAndAuthorizer(b.Blockchain, templates.GenerateMintSetScript(b.env), b.topshotAdminAddr)

Expand Down
39 changes: 39 additions & 0 deletions transactions/admin/update_tagline.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import TopShot from 0xTOPSHOTADDRESS

// This transaction updates multiple existing plays' taglines
// and stores them in the Top Shot smart contract

// Parameters:
//
// plays: A dictionary of {playID: tagline} pairs

transaction(plays: {UInt32: String}) {

// Local variable for the topshot Admin object
let adminRef: &TopShot.Admin
let firstKey: UInt32
let lastKey: UInt32

prepare(acct: AuthAccount) {

// borrow a reference to the admin resource
self.adminRef = acct.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)
?? panic("No admin resource in storage")
self.firstKey = plays.keys[0]
self.lastKey = plays.keys[plays.keys.length - 1]
}

execute {
// update multiple plays with the specified metadata
for key in plays.keys {
self.adminRef.updatePlayTagline(playID: key, tagline: plays[key] ?? panic("No tagline for play"))
}
}

post {
TopShot.getPlayMetaDataByField(playID: self.firstKey, field: "Tagline") != nil:
"first play's tagline doesnt exist"
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
TopShot.getPlayMetaDataByField(playID: self.lastKey, field: "Tagline") != nil:
"last play's tagline doesnt exist"
zihehuang marked this conversation as resolved.
Show resolved Hide resolved
}
}