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

Add remix #163

Merged
merged 25 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 267 additions & 0 deletions contracts/TopShot.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ pub contract TopShot: NonFungibleToken {
// Emitted when a Moment is destroyed
pub event MomentDestroyed(id: UInt64)

pub event SubeditionCreated(id: UInt32, name: String, metadata: {String:String})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't clear to me what the ID represents, considering you use subeditionID in the next event. Does it represent the subedition ID? Shouldn't we also have the play and set IDs in the event?

Copy link
Contributor

@jrkhan jrkhan Oct 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with consistent/more specific name for id.
In the current iteration, the subedition is intended to be reused across many sets/plays. (This will make it much easier to search for all 'diamond' moments if they share an id) -> so subedition created event will not have a set/play as the subedition itself does not have a set/play.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub event SubeditionCreated(id: UInt32, name: String, metadata: {String:String})
pub event SubeditionCreated(subeditionID: UInt32, name: String, metadata: {String:String})


pub event SubeditionAddedToMoment(momentID: UInt64, subeditionID: UInt32)
jrkhan marked this conversation as resolved.
Show resolved Hide resolved

// -----------------------------------------------------------------------
// TopShot contract-level fields.
// These contain actual values that are stored in the smart contract.
Expand Down Expand Up @@ -390,6 +394,70 @@ pub contract TopShot: NonFungibleToken {
return <-newCollection
}

// mintMomentWithSubedition mints a new Moment with subedition and returns the newly minted Moment
//
// Parameters: playID: The ID of the Play that the Moment references
// subeditionID: The ID of the subedition within Edition that the Moment references
//
// Pre-Conditions:
// The Play must exist in the Set and be allowed to mint new Moments
//
// Returns: The NFT that was minted
//
pub fun mintMomentWithSubedition(playID: UInt32, subeditionID: UInt32): @NFT {
pre {
self.retired[playID] != nil: "Cannot mint the moment: This play doesn't exist."
!self.retired[playID]!: "Cannot mint the moment from this play: This play has been retired."
}

// Gets the number of Moments that have been minted for this subedition
// to use as this Moment's serial number
let subeditionRef = TopShot.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")

let numInSubedition = subeditionRef.getNumberMintedPerSubedition(setID: self.setID,
playID: playID,
subeditionID: subeditionID)

// Mint the new moment
let newMoment: @NFT <- create NFT(serialNumber: numInSubedition + UInt32(1),
playID: playID,
setID: self.setID)

jrkhan marked this conversation as resolved.
Show resolved Hide resolved
// Increment the count of Moments minted for this subedition
subeditionRef.addToNumberMintedPerSubedition(setID: self.setID,
playID: playID,
subeditionID: subeditionID)

subeditionRef.setMomentsSubedition(nftID: newMoment.id, subeditionID: subeditionID)

self.numberMintedPerPlay[playID] = self.numberMintedPerPlay[playID]! + UInt32(1)

return <-newMoment
}

// batchMintMomentWithSubedition mints an arbitrary quantity of Moments with subedition
// and returns them as a Collection
//
// Parameters: playID: the ID of the Play that the Moments are minted for
// quantity: The quantity of Moments to be minted
// subeditionID: The ID of the subedition within Edition that the Moments references
//
// Returns: Collection object that contains all the Moments that were minted
//
pub fun batchMintMomentWithSubedition(playID: UInt32, quantity: UInt64, subeditionID: UInt32): @Collection {
let newCollection <- create Collection()

var i: UInt64 = 0
while i < quantity {
newCollection.deposit(token: <-self.mintMomentWithSubedition(playID: playID,
subeditionID: subeditionID))
i = i + UInt64(1)
}

return <-newCollection
}

pub fun getPlays(): [UInt32] {
return self.plays
}
Expand Down Expand Up @@ -922,6 +990,26 @@ pub contract TopShot: NonFungibleToken {
return TopShot.currentSeries
}

// createSubeditionResource creates new SubeditionMap resource that
// will be used to mint Moments with Subeditions
pub fun createSubeditionAdminResource() {
TopShot.account.save<@SubeditionAdmin>(<- create SubeditionAdmin(), to: /storage/TopShotSubeditionAdmin)
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
}

pub fun setMomentsSubedition(nftID: UInt64, subeditionID: UInt32) {
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
let subeditionAdmin = TopShot.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")

subeditionAdmin.setMomentsSubedition(nftID: nftID, subeditionID: subeditionID)
}

pub fun createSubedition(name:String, metadata:{String:String}): UInt32 {
let subeditionAdmin = TopShot.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")

return subeditionAdmin.createSubedition(name:name, metadata:metadata)
}

// createNewAdmin creates a new Admin resource
//
pub fun createNewAdmin(): @Admin {
Expand Down Expand Up @@ -1324,6 +1412,185 @@ pub contract TopShot: NonFungibleToken {
}
}

// getMomentsSubedition returns the Subedition the Moment belongs to
//
// Parameters: nftID: The ID of the NFT
//
// returns: UInt32? Subedition's ID if exists
//
pub fun getMomentsSubedition(nftID: UInt64):UInt32? {
let subeditionAdmin = self.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")

return subeditionAdmin.getMomentsSubedition(nftID: nftID)
}

// getAllSubeditions returns all the subeditions in topshot subeditionAdmin resource
//
// Returns: An array of all the subeditions that have been created
pub fun getAllSubeditions():[TopShot.Subedition] {
let subeditionAdmin = self.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")
return subeditionAdmin.subeditionDatas.values
}

// getSubeditionByID returns the subedition struct entity
//
// Parameters: subeditionID: The id of the Subedition that is being searched
//
// Returns: The Subedition struct
pub fun getSubeditionByID(subeditionID: UInt32):TopShot.Subedition {
let subeditionAdmin = self.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")
return subeditionAdmin.subeditionDatas[subeditionID]!
}

// This script reads the public nextSubeditionID from the SubeditionAdmin resource and
// returns that number to the caller
//
// Returns: UInt32
// the next number in nextSubeditionID from the SubeditionAdmin resource
pub fun getNextSubeditionID():UInt32 {
let subeditionAdmin = self.account.borrow<&SubeditionAdmin>(from: /storage/TopShotSubeditionAdmin)
?? panic("No subedition admin resource in storage")
return subeditionAdmin.nextSubeditionID
}
// SubeditionAdmin is a resource that allows Set to mint Moments with Subeditions
//
pub struct Subedition {
pub let subeditionID: UInt32

pub let name: String

pub let metadata: {String: String}

init(subeditionID: UInt32, name: String, metadata: {String: String}) {
pre {
name.length != 0: "New Subedition name cannot be empty"
metadata.length != 0: "New Subedition metadata cannot be empty"
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
}
self.subeditionID = subeditionID
self.name = name
self.metadata = metadata
}
}

pub resource SubeditionAdmin {

// Map of number of already minted Moments using Subedition.
// When a new Moment with Subedition is minted, 1 is added to the
// number in this map by the key, formed by concatinating of
// SetID, PlayID and SubeditionID
access(contract) var numberMintedPerSubedition: {String:UInt32}

//Map of Subedition which the Moment belongs to.
//This map updates after each minting.
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
access(contract) var momentsSubedition: {UInt64:UInt32}
jrkhan marked this conversation as resolved.
Show resolved Hide resolved

// The ID that is used to create Subeditions.
// Every time a Subeditions is created, subeditionID is assigned
// to the new Subedition's ID and then is incremented by 1.
access(contract) var nextSubeditionID: UInt32

// Variable size dictionary of Subedition structs
access(contract) var subeditionDatas: {UInt32: Subedition}

// createSubedition creates a new Subedition struct
// and stores it in the Subeditions dictionary in the SubeditionAdmin resource
//
// Parameters: name: The name of the Subedition
// metadata: A dictionary mapping metadata titles to their data
//
// Returns: the ID of the new Subedition object
//
pub fun createSubedition(name:String, metadata:{String:String}): UInt32 {

let newID = self.nextSubeditionID

var newSubedition = Subedition(subeditionID: newID, name: name, metadata: metadata)

self.nextSubeditionID = self.nextSubeditionID + UInt32(1)

emit SubeditionCreated(id: newID, name: name, metadata: metadata)

self.subeditionDatas[newID] = newSubedition

emit SubeditionCreated(id: newID, name: name, metadata: metadata)
jrkhan marked this conversation as resolved.
Show resolved Hide resolved

return newID
}

// getMomentsSubedition function that return's wich Subedition the Moment belongs to
//
// Parameters: nftID: The ID of the NFT
//
// returns: UInt32? Subedition's ID if exists
//
pub fun getMomentsSubedition( nftID: UInt64):UInt32? {
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
return self.momentsSubedition[nftID]
}

// getNumberMintedPerSubedition function that return's
// the number of Moments that have been minted for this subedition
// to use as this Moment's serial number
//
// Parameters: setID: The ID of the Set Moment will be minted from
// playID: The ID of the Play Moment will be minted from
// subeditionID: The ID of the Subedition using which moment will be minted
//
// returns: UInt32 Number of Moments, already minted for this Subedition
//
pub fun getNumberMintedPerSubedition(setID: UInt32, playID: UInt32, subeditionID: UInt32): UInt32 {
let setPlaySubedition = setID.toString().concat(playID.toString()).concat(subeditionID.toString())
if !self.numberMintedPerSubedition.containsKey(setPlaySubedition) {
self.numberMintedPerSubedition.insert(key: setPlaySubedition,UInt32(0))
return UInt32(0)
}
return self.numberMintedPerSubedition[setPlaySubedition]!
}

// addToNumberMintedPerSubedition function that increments 1 to the
// number of Moments that have been minted for this subedition
//
// Parameters: setID: The ID of the Set Moment will be minted from
// playID: The ID of the Play Moment will be minted from
// subeditionID: The ID of the Subedition using which moment will be minted
//
//
pub fun addToNumberMintedPerSubedition(setID: UInt32, playID: UInt32, subeditionID: UInt32) {
let setPlaySubedition = setID.toString().concat(playID.toString()).concat(subeditionID.toString())

if self.numberMintedPerSubedition.containsKey(setPlaySubedition) {
self.numberMintedPerSubedition[setPlaySubedition]!= self.numberMintedPerSubedition[setPlaySubedition]! + UInt32(1)
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
} else {
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
panic("Could not find specified Subedition!")
}
}


// setMomentsSubedition function that saves which Subedition the Moment belongs to
//
// Parameters: nftID: The ID of the NFT
// subeditionID: The ID of the Subedition the Moment belongs to
//
pub fun setMomentsSubedition(nftID: UInt64, subeditionID: UInt32){
if self.momentsSubedition.containsKey(nftID) {
jrkhan marked this conversation as resolved.
Show resolved Hide resolved
panic("Subedition for this moment already exists!")
}
self.momentsSubedition.insert(key: nftID, subeditionID)

emit SubeditionAddedToMoment(momentID: nftID, subeditionID: subeditionID)
}

init() {
self.momentsSubedition = {}
self.numberMintedPerSubedition = {}
self.subeditionDatas = {}
self.nextSubeditionID = 1
}
}


// -----------------------------------------------------------------------
// TopShot initialization function
// -----------------------------------------------------------------------
Expand Down
20 changes: 10 additions & 10 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions lib/go/events/subedition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package events

import (
"fmt"

"github.com/onflow/cadence"
jsoncdc "github.com/onflow/cadence/encoding/json"
)

var (
EventSubeditionCreated = "TopShot.SubeditionCreated"
)

type SubeditionCreatedEvent interface {
Id() uint32
Name() string
MetaData() map[interface{}]interface{}
}

type subeditionCreatedEvent cadence.Event

func (evt subeditionCreatedEvent) Id() uint32 {
return evt.Fields[0].(cadence.UInt32).ToGoValue().(uint32)
}

func (evt subeditionCreatedEvent) Name() string {
return evt.Fields[1].(cadence.String).ToGoValue().(string)
}

func (evt subeditionCreatedEvent) MetaData() map[interface{}]interface{} {
return evt.Fields[2].(cadence.Dictionary).ToGoValue().(map[interface{}]interface{})
}

func (evt subeditionCreatedEvent) validate() error {
if evt.EventType.QualifiedIdentifier != EventSubeditionCreated {
return fmt.Errorf("error validating event: event is not a valid subedition created event, expected type %s, got %s",
EventSubeditionCreated, evt.EventType.QualifiedIdentifier)
}
return nil
}

func DecodeSubeditionCreatedEvent(b []byte) (SubeditionCreatedEvent, error) {
value, err := jsoncdc.Decode(nil, b)
if err != nil {
return nil, err
}
event := subeditionCreatedEvent(value.(cadence.Event))
if err := event.validate(); err != nil {
return nil, fmt.Errorf("error decoding event: %w", err)
}
return event, nil
}
47 changes: 47 additions & 0 deletions lib/go/events/subedition_added_to_moment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package events

import (
"fmt"

"github.com/onflow/cadence"
jsoncdc "github.com/onflow/cadence/encoding/json"
)

var (
EventSubeditionAddedToMoment = "TopShot.SubeditionAddedToMoment"
)

type SubeditionAddedToMomentEvent interface {
MomentID() uint64
SubeditionID() uint32
}

type subeditionAddedToMomentEvent cadence.Event

func (evt subeditionAddedToMomentEvent) MomentID() uint64 {
return evt.Fields[0].(cadence.UInt64).ToGoValue().(uint64)
}

func (evt subeditionAddedToMomentEvent) SubeditionID() uint32 {
return evt.Fields[1].(cadence.UInt32).ToGoValue().(uint32)
}

func (evt subeditionAddedToMomentEvent) validate() error {
if evt.EventType.QualifiedIdentifier != EventSubeditionAddedToMoment {
return fmt.Errorf("error validating event: event is not a valid subedition added to moment event, expected type %s, got %s",
EventSubeditionAddedToMoment, evt.EventType.QualifiedIdentifier)
}
return nil
}

func DecodeSubeditionAddedToMomentEvent(b []byte) (SubeditionAddedToMomentEvent, error) {
value, err := jsoncdc.Decode(nil, b)
if err != nil {
return nil, err
}
event := subeditionAddedToMomentEvent(value.(cadence.Event))
if err := event.validate(); err != nil {
return nil, fmt.Errorf("error decoding event: %w", err)
}
return event, nil
}
Loading