Skip to content

Commit

Permalink
remove transfer, withdraw methods, and default implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Nov 14, 2023
1 parent 683df24 commit 2b6dfd6
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 227 deletions.
7 changes: 0 additions & 7 deletions contracts/BasicNFT-v2.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,6 @@ access(all) contract BasicNFT {
}
}

/// Return the NFT types that the contract defines
access(all) view fun getNFTTypes(): [Type] {
return [
Type<@BasicNFT.NFT>()
]
}

access(all) resource NFTMinter {
access(all) fun mintNFT(metadata: {String: AnyStruct}): @BasicNFT.NFT {
return <- create NFT(metadata: metadata)
Expand Down
101 changes: 10 additions & 91 deletions contracts/ExampleNFT-v2.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import MultipleNFT from "MultipleNFT"
import ViewResolver from "ViewResolver"
import MetadataViews from "MetadataViews"

access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
access(all) contract ExampleNFT: ViewResolver {

/// Path where the minter should be stored
/// The standard paths for the collection are stored in the collection resource type
Expand All @@ -25,10 +25,9 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
/// because the interface does not require it to have a specific name any more
access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {

/// The ID of the NFT
/// Could be a project specific ID, or the UUID
/// Here we choose the UUID
access(all) let id: UInt64
access(all) view fun getID(): UInt64 {
return self.uuid
}

/// From the Display metadata view
access(all) let name: String
Expand All @@ -48,7 +47,6 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
royalties: [MetadataViews.Royalty],
metadata: {String: AnyStruct},
) {
self.id = self.uuid
self.name = name
self.description = description
self.thumbnail = thumbnail
Expand Down Expand Up @@ -82,21 +80,21 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
case Type<MetadataViews.Editions>():
// There is no max number of NFTs that can be minted from this contract
// so the max edition field value is set to nil
let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.id, max: nil)
let editionInfo = MetadataViews.Edition(name: "Example NFT Edition", number: self.getID(), max: nil)
let editionList: [MetadataViews.Edition] = [editionInfo]
return MetadataViews.Editions(
editionList
)
case Type<MetadataViews.Serial>():
return MetadataViews.Serial(
self.id
self.getID()
)
case Type<MetadataViews.Royalties>():
return MetadataViews.Royalties(
self.royalties
)
case Type<MetadataViews.ExternalURL>():
return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.getID().toString()))
case Type<MetadataViews.NFTCollectionData>():
return ExampleNFT.getCollectionData(nftType: Type<@ExampleNFT.NFT>())
case Type<MetadataViews.NFTCollectionDisplay>():
Expand Down Expand Up @@ -164,11 +162,6 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
}
}

/// Indicates that the collection is using UUID to key the NFT dictionary
access(all) view fun usesUUID(): Bool {
return true
}

/// 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)
Expand All @@ -177,54 +170,17 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
return <-token
}

/// withdrawWithUUID removes an NFT from the collection, using its UUID, and moves it to the caller
access(NonFungibleToken.Withdrawable) fun withdrawWithUUID(_ uuid: UInt64): @{NonFungibleToken.NFT} {
return <-self.withdraw(withdrawID: uuid)
}

/// withdrawWithType removes an NFT from the collection, using its Type and ID and moves it to the caller
/// This would be used by a collection that can store multiple NFT types
access(NonFungibleToken.Withdrawable) fun withdrawWithType(type: Type, withdrawID: UInt64): @{NonFungibleToken.NFT} {
return <-self.withdraw(withdrawID: withdrawID)
}

/// withdrawWithTypeAndUUID removes an NFT from the collection using its type and uuid and moves it to the caller
/// This would be used by a collection that can store multiple NFT types
access(NonFungibleToken.Withdrawable) fun withdrawWithTypeAndUUID(type: Type, uuid: UInt64): @{NonFungibleToken.NFT} {
return <-self.withdraw(withdrawID: uuid)
}

/// 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}) {
let token <- token as! @ExampleNFT.NFT

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

destroy oldToken
}

/// Function for a direct transfer instead of having to do a deposit and withdrawal
///
access(NonFungibleToken.Withdrawable) fun transfer(id: UInt64, receiver: Capability<&{NonFungibleToken.Receiver}>): Bool {
let token <- self.withdraw(withdrawID: id)

let displayView = token.resolveView(Type<MetadataViews.Display>())! as! MetadataViews.Display

// If we can't borrow a receiver reference, don't panic, just return the NFT
// and return true for an error
if let receiverRef = receiver.borrow() {

receiverRef.deposit(token: <-token)

return false
} else {
self.deposit(token: <-token)
return true
}
}

/// getIDs returns an array of the IDs that are in the collection
access(all) view fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
Expand All @@ -235,12 +191,6 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
return self.ownedNFTs.keys.length
}

access(all) view fun getIDsWithTypes(): {Type: [UInt64]} {
let typeIDs: {Type: [UInt64]} = {}
typeIDs[Type<@ExampleNFT.NFT>()] = self.getIDs()
return typeIDs
}

/// borrowNFT gets a reference to an NFT in the collection
/// so that the caller can read its metadata and call its methods
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT} {
Expand Down Expand Up @@ -275,13 +225,8 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
/// public function that anyone can call to create a new empty collection
/// Since multiple collection types can be defined in a contract,
/// The caller needs to specify which one they want to create
access(all) fun createEmptyCollection(collectionType: Type): @{NonFungibleToken.Collection} {
switch collectionType {
case Type<@ExampleNFT.Collection>():
return <- create Collection()
default:
return <- create Collection()
}
access(all) fun createEmptyCollection(): @ExampleNFT.Collection {
return <- create Collection()
}

/// Function that returns all the Metadata Views implemented by a Non Fungible Token
Expand Down Expand Up @@ -311,32 +256,6 @@ access(all) contract ExampleNFT: MultipleNFT, ViewResolver {
return nil
}

/// Return the NFT types that the contract defines
access(all) view fun getNFTTypes(): [Type] {
return [
Type<@ExampleNFT.NFT>()
]
}

/// get a list of all the NFT collection types that the contract defines
/// could include a post-condition that verifies that each Type is an NFT collection type
access(all) view fun getCollectionTypes(): [Type] {
return [
Type<@ExampleNFT.Collection>()
]
}

/// tells what collection type should be used for the specified NFT type
/// return `nil` if no collection type exists for the specified NFT type
access(all) view fun getCollectionTypeForNftType(nftType: Type): Type? {
switch nftType {
case Type<@ExampleNFT.NFT>():
return Type<@ExampleNFT.Collection>()
default:
return nil
}
}

/// resolve a type to its CollectionData so you know where to store it
/// Returns `nil` if no collection type exists for the specified NFT type
access(all) view fun getCollectionData(nftType: Type): MetadataViews.NFTCollectionData? {
Expand Down
125 changes: 9 additions & 116 deletions contracts/NonFungibleToken-v2.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -109,16 +109,7 @@ access(all) contract NonFungibleToken {
///
access(all) resource interface NFT: ViewResolver.Resolver {
/// The unique ID that each NFT has
access(all) view fun getID(): UInt64 {
return self.uuid
}

// access(all) view fun getViews(): [Type] {
// return []
// }
// access(all) fun resolveView(_ view: Type): AnyStruct? {
// return nil
// }
access(all) view fun getID(): UInt64

destroy() {
pre {
Expand All @@ -130,10 +121,6 @@ access(all) contract NonFungibleToken {
/// Interface to mediate withdraws from the Collection
///
access(all) resource interface Provider {
/// Function for projects to indicate if they are using UUID or not
access(all) view fun usesUUID(): Bool {
return false
}

// We emit withdraw events from the provider interface because conficting withdraw
// events aren't as confusing to event listeners as conflicting deposit events
Expand All @@ -146,50 +133,6 @@ access(all) contract NonFungibleToken {
NonFungibleToken.emitNFTWithdraw(id: result.getID(), uuid: result.uuid, from: self.owner?.address, type: result.getType().identifier)
}
}

/// Alternate withdraw methods
/// The next three withdraw methods allow projects to have more flexibility
/// to indicate how their NFTs are meant to be used
/// With the v2 upgrade, some projects will be using UUID and others
/// will be using custom IDs, so projects can pick and choose which
/// of these withdraw methods applies to them
/// TODO: These will eventually have optional return types, but don't right now
/// because of a bug in Cadence
/// withdrawWithUUID removes an NFT from the collection, using its UUID, and moves it to the caller
access(Withdrawable) fun withdrawWithUUID(_ uuid: UInt64): @{NFT} {
post {
result == nil || result!.uuid == uuid: "The ID of the withdrawn token must be the same as the requested ID"
NonFungibleToken.emitNFTWithdraw(id: result.getID(), uuid: result.uuid, from: self.owner?.address, type: result.getType().identifier)
}
}

/// withdrawWithType removes an NFT from the collection, using its Type and ID and moves it to the caller
/// This would be used by a collection that can store multiple NFT types
access(Withdrawable) fun withdrawWithType(type: Type, withdrawID: UInt64): @{NFT} {
post {
result == nil || result.getID() == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
NonFungibleToken.emitNFTWithdraw(id: result.getID(), uuid: result.uuid, from: self.owner?.address, type: result.getType().identifier)
}
}

/// withdrawWithTypeAndUUID removes an NFT from the collection using its type and uuid and moves it to the caller
/// This would be used by a collection that can store multiple NFT types
access(Withdrawable) fun withdrawWithTypeAndUUID(type: Type, uuid: UInt64): @{NFT} {
post {
result == nil || result!.uuid == uuid: "The ID of the withdrawn token must be the same as the requested ID"
NonFungibleToken.emitNFTWithdraw(id: result.getID(), uuid: result.uuid, from: self.owner?.address, type: result.getType().identifier)
}
}
}

/// Interface to mediate transfers between Collections
///
access(all) resource interface Transferor {
/// transfer removes an NFT from the callers collection
/// and moves it to the collection specified by `receiver`
access(Withdrawable) fun transfer(id: UInt64, receiver: Capability<&{Receiver}>): Bool
}

/// Interface to mediate deposits to the Collection
Expand All @@ -200,49 +143,29 @@ access(all) contract NonFungibleToken {
///
access(all) fun deposit(token: @{NFT})

// /// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
// access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
// return {}
// }
/// getSupportedNFTTypes returns a list of NFT types that this receiver accepts
access(all) view fun getSupportedNFTTypes(): {Type: Bool}

// /// Returns whether or not the given type is accepted by the collection
// /// A collection that can accept any type should just return true by default
// access(all) view fun isSupportedNFTType(type: Type): Bool {
// return false
// }
/// Returns whether or not the given type is accepted by the collection
/// A collection that can accept any type should just return true by default
access(all) view fun isSupportedNFTType(type: Type): Bool
}

/// Requirement for the concrete resource type
/// to be declared in the implementing contract
///
access(all) resource interface Collection: Provider, Receiver, Transferor, ViewResolver.ResolverCollection {
access(all) resource interface Collection: Provider, Receiver, ViewResolver.ResolverCollection {

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

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

/// Normally we would require that the collection specify
/// a specific dictionary to store the NFTs, but this isn't necessary any more
/// as long as all the other functions are there
/// Returns the NFT types that this collection can store
/// If the collection can accept any NFT type, it should return
/// a one element dictionary with the key type as `@{NonFungibleToken.NFT}`
access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
pre { true: "dummy" }
}

/// Returns whether or not the given type is accepted by the collection
access(all) view fun isSupportedNFTType(type: Type): Bool {
pre { true: "dummy" }
}

/// createEmptyCollection creates an empty Collection
/// and returns it to the caller so that they can own NFTs
access(all) fun createEmptyCollection(): @{Collection} {
Expand All @@ -251,10 +174,6 @@ access(all) contract NonFungibleToken {
}
}

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

Expand All @@ -270,38 +189,12 @@ access(all) contract NonFungibleToken {
}
}

/// Function for a direct transfer instead of having to do a deposit and withdrawal
/// This can and should return false if the transfer doesn't succeed and true if it does succeed
///
access(Withdrawable) fun transfer(id: UInt64, receiver: Capability<&{NonFungibleToken.Receiver}>): Bool {
pre {
self.getIDs().contains(id): "The collection does not contain the specified ID"
NonFungibleToken.emitNFTTransfer(id: id, uuid: self.borrowNFTSafe(id: id)?.uuid, from: self.owner?.address, to: receiver.borrow()?.owner?.address, type: self.borrowNFT(id).getType().identifier)
}
}

/// getIDs returns an array of the IDs that are in the collection
access(all) view fun getIDs(): [UInt64]

/// Gets the amount of NFTs stored in the collection
access(all) view fun getLength(): Int

/// getIDsWithTypes returns a list of IDs that are in the collection, keyed by type
/// Should only be used by collections that can store multiple NFT types
access(all) view fun getIDsWithTypes(): {Type: [UInt64]}

/// Returns a borrowed reference to an NFT in the collection
/// so that the caller can read data and call methods from it
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT} {
pre { true: "dummy" }
}

/// From the ViewResolver Contract
/// borrows a reference to get metadata views for the NFTs that the contract contains
access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? {
pre { true: "dummy" }
}

access(all) view fun borrowNFTSafe(id: UInt64): &{NonFungibleToken.NFT}? {
post {
(result == nil) || (result?.getID() == id):
Expand Down
Loading

0 comments on commit 2b6dfd6

Please sign in to comment.