Skip to content

Commit

Permalink
Backfill play tagline for NFT Catalog (#180)
Browse files Browse the repository at this point in the history
* add contract updates first

* add transaction template

* add tests

* change file name

* fix update tagline transaction

* fix test

* use prebuilt description if tagline is empty string

* capitalize Tagline

* adding more comments to tagline backfill related functions

* addressing comments

* more concise description code
  • Loading branch information
zihehuang authored Nov 16, 2022
1 parent 049abb7 commit 8637e8b
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 7 deletions.
32 changes: 30 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 {
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,14 @@ 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
pub fun description(): String {
let playDesc: String = TopShot.getPlayMetaDataByField(playID: self.data.playID, field: "Tagline") ?? ""

return playDesc.length > 0 ? playDesc : 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 +961,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
pub fun updatePlayTagline(playID: UInt32, tagline: String): UInt32 {
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 does not exist"
TopShot.getPlayMetaDataByField(playID: self.lastKey, field: "Tagline") != nil:
"Last play's tagline does not exist"
}
}

0 comments on commit 8637e8b

Please sign in to comment.