Skip to content

Commit

Permalink
add universal collection and other small changes
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Nov 20, 2023
1 parent 2b6dfd6 commit 825022e
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 23 deletions.
5 changes: 5 additions & 0 deletions contracts/BasicNFT-v2.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import NonFungibleToken from "NonFungibleToken"
import MetadataViews from "MetadataViews"
import ViewResolver from "ViewResolver"
import UniversalCollection from "UniversalCollection"

access(all) contract BasicNFT {

Expand Down Expand Up @@ -67,6 +68,10 @@ access(all) contract BasicNFT {
}
}

access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
return <- UniversalCollection.createEmptyCollection(identifier: "flowBasicNFTCollection", type: Type<@BasicNFT.NFT>())
}

init() {
let minter <- create NFTMinter()
self.account.save(<-minter, to: /storage/flowBasicNFTMinterPath)
Expand Down
3 changes: 0 additions & 3 deletions contracts/NonFungibleToken-v2.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,6 @@ access(all) contract NonFungibleToken {
}
}

/// withdraw removes an NFT from the collection and moves it to the caller
access(Withdrawable) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT}

/// deposit takes a NFT and adds it to the collections dictionary
/// and adds the ID to the id array
access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
Expand Down
128 changes: 128 additions & 0 deletions contracts/UniversalCollection.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
*
* This is an example collection that can store any one type of NFT
* The Collection is restricted to one NFT type.
* This allows developers to write NFT contracts without having
* to also write all of the Collection boilerplate code,
* saving many lines of code.
*
*/

import "NonFungibleToken"
import "MetadataViews"
import "ViewResolver"

access(all) contract UniversalCollection {

/// The typical Collection resource, but one that anyone can use
///
access(all) resource Collection: NonFungibleToken.Collection {

/// every Universal collection supports a single type
/// All deposits and withdrawals must be of this type
access(all) let supportedType : Type

/// The path identifier
access(all) let identifier: String

/// Dictionary mapping NFT IDs to the stored NFTs
access(contract) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}

access(self) var storagePath: StoragePath
access(self) var publicPath: PublicPath

/// Return the default storage path for the collection
access(all) view fun getDefaultStoragePath(): StoragePath? {
return self.storagePath
}

/// Return the default public path for the collection
access(all) view fun getDefaultPublicPath(): PublicPath? {
return self.publicPath
}

init (identifier: String, type:Type) {
self.ownedNFTs <- {}
self.identifier = identifier
self.supportedType = type
self.storagePath = StoragePath(identifier: identifier)!
self.publicPath = PublicPath(identifier: identifier)!
}

/// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
let supportedTypes: {Type: Bool} = {}
supportedTypes[self.supportedType] = true
return supportedTypes
}

/// Returns whether or not the given type is accepted by the collection
access(all) view fun isSupportedNFTType(type: Type): Bool {
if type == self.supportedType {
return true
} else {
return false
}
}

/// withdraw removes an NFT from the collection and moves it to the caller
access(NonFungibleToken.Withdrawable) fun withdraw(_ withdrawID: UInt64): @{NonFungibleToken.NFT} {
let token <- self.ownedNFTs.remove(key: withdrawID)
?? panic("Could not withdraw an NFT with the ID: ".concat(withdrawID.toString()).concat(" from the collection"))

return <-token
}

/// deposit takes a NFT and adds it to the collections dictionary
/// and adds the ID to the id array
access(all) fun deposit(_ token: @{NonFungibleToken.NFT}) {
if self.supportedType != token.getType() {
panic("Cannot deposit an NFT of the given type")
}

// add the new token to the dictionary which removes the old one
let oldToken <- self.ownedNFTs[token.getID()] <- token
destroy oldToken
}

/// getIDs returns an array of the IDs that are in the collection
access(all) view fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
}

/// getLength retusnt the number of items in the collection
access(all) view fun getLength(): Int {
return self.ownedNFTs.length
}

/// Borrows a reference to an NFT in the collection if it is there
/// otherwise, returns `nil`
access(all) view fun borrowNFTSafe(id: UInt64): &{NonFungibleToken.NFT}? {
return (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)
}

/// Borrow the view resolver for the specified NFT ID
access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
return (&self.ownedNFTs[id] as &{ViewResolver.Resolver}?)!
}

/// public function that anyone can call to create a new empty collection
/// of the same type as the called collection
access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
UniversalCollection.createEmptyCollection(identifier: self.identifier, type: self.supportedType)
}

destroy() {
destroy self.ownedNFTs
}
}

/// Public function that anyone can call to create
/// a new empty collection with the specified type restriction
/// NFT contracts can include a call to this method in
/// their own createEmptyCollection method
access(all) fun createEmptyCollection(identifier: String, type: Type): @{NonFungibleToken.Collection} {
return <- create Collection(identifier: identifier, type:type)
}

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

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions lib/go/templates/internal/assets/assets.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion scripts/borrow_nft.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ access(all) fun main(address: Address, id: UInt64) {
) ?? panic("Could not borrow capability from public collection")

// Borrow a reference to a specific NFT in the collection
let _ = collectionRef.borrowNFTSafe(id)!
let _ = collectionRef.borrowNFTSafe(id: id)!
}
1 change: 1 addition & 0 deletions tests/example_nft_tests.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ access(all) fun setup() {
deploy("NonFungibleToken", admin, "../contracts/NonFungibleToken-v2.cdc")
deploy("MetadataViews", admin, "../contracts/MetadataViews.cdc")
deploy("MultipleNFT", admin, "../contracts/MultipleNFT.cdc")
deploy("UniversalCollection", admin, "../contracts/UniversalCollection.cdc")
deploy("ExampleNFT", admin, "../contracts/ExampleNFT-v2.cdc")
}

Expand Down
2 changes: 1 addition & 1 deletion transactions/setup_account.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ transaction {
}

// Create a new empty collection
let collection <- ExampleNFT.createEmptyCollection(collectionType: Type<@ExampleNFT.Collection>())
let collection <- ExampleNFT.createEmptyCollection()

// save it to the account
signer.storage.save(<-collection, to: collectionData.storagePath)
Expand Down
14 changes: 8 additions & 6 deletions transactions/transfer_nft.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ transaction(contractAddress: Address, contractName: String, recipient: Address,
let withdrawRef: auth(NonFungibleToken.Withdrawable) &{NonFungibleToken.Collection}

/// Reference of the collection to deposit the NFT to
let receiverCap: Capability<&{NonFungibleToken.Receiver}>
let receiverRef: &{NonFungibleToken.Receiver}

prepare(signer: auth(BorrowValue) &Account) {

Expand All @@ -31,16 +31,18 @@ transaction(contractAddress: Address, contractName: String, recipient: Address,
let recipient = getAccount(recipient)

// borrow a public reference to the receivers collection
self.receiverCap = recipient.capabilities.get<&{NonFungibleToken.Receiver}>(collectionData.publicPath)
?? panic("Could not get the recipient's the Receiver Capability")
let receiverCap = recipient.capabilities.get<&{NonFungibleToken.Receiver}>(collectionData.publicPath)
?? panic("Could not get the recipient's Receiver Capability")

self.receiverRef = receiverCap.borrow()
?? panic("Could not borrow reference to the recipient's receiver")

}

execute {

// Transfer the NFT between the accounts - returns true if error, false if successful
let error = self.withdrawRef.transfer(id: withdrawID, receiver: self.receiverCap)
assert(error == false, message: "Problem executing transfer")
let nft <- self.withdrawRef.withdraw(withdrawID: withdrawID)
self.receiverRef.deposit(token: <-nft)

}

Expand Down

0 comments on commit 825022e

Please sign in to comment.