Claim sale does not distribute in the minted order
psiemens opened this issue · comments
When creating a ClaimSale
instance from a newly-minted collection, the items are not claimed in the order in which they are minted.
Some notes on the performance of the new Collection
implementation I'm testing out in #123:
Freshmint users need to be able to distribute NFTs in the same order in which they are minted, so I've been experimenting with a new implementation of NonFungibleToken.Collection
that preserves insertion order using an additional array field. I was worried about the performance so I did some benchmarking on testnet.
The results were surprising! I minted up to 10K NFTs and looked at the computation required to withdraw the first NFT returned by collection.getIDs()
. The ordered collection drastically outperforms the standard collection when you remove the first NFT in the ID list. I imagine this has to do with the underlying tree implementation for arrays and maps in Cadence.
When withdrawing the NFTs not in order, the ordered collection is expectedly slower than the standard collection, likely due to the additional overhead of having to remove the ID from the ids
list.
Freshmint users need to be able to distribute NFTs in the same order in which they are minted.
I'm not set on using a Collection
to satisfy this requirement, but I wanted to try, because I think it'd be really nice to give developers the flexibility to transfer individual NFTs from the same collection they use for their drop (e.g. for gifting or airdrops). The other option is to put all the minted NFTs in an array and just pop off the top.
Another added benefit of the ordered collection is that it allows an on-chain query to return NFTs to users in the order in which they acquired them, which IMO is more intuitive than relying on the ordering that Cadence chooses for dictionary keys (which isn't random, but definitely feels random to a user).
Simplified code showing the different implementations:
Standard Collection (the one we know and love)
pub resource StandardCollection: NonFungibleToken.Collection {
pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
init () {
self.ownedNFTs <- {}
}
pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
let token <- self.ownedNFTs.remove(key: withdrawID)!
emit Withdraw(id: token.id, from: self.owner?.address)
return <- token
}
pub fun deposit(token: @NonFungibleToken.NFT) {
let token <- token as! @Foo.NFT
let id: UInt64 = token.id
let oldToken <- self.ownedNFTs[id] <- token
destroy oldToken
emit Deposit(id: id, to: self.owner?.address)
}
pub fun getIDs(): [UInt64] {
return self.ownedNFTs.keys
}
}
Ordered Collection
pub resource OrderedCollection: NonFungibleToken.Collection {
/// An array of all NFT IDs in this collection in insertion order
///
pub let ids: [UInt64]
pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
init () {
self.ids = []
self.ownedNFTs <- {}
}
pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
let token <- self.ownedNFTs.remove(key: withdrawID)!
// Remove the NFT ID from the IDs list
let index = self.ids.firstIndex(of: token.id)!
self.ids.remove(at: index)
emit Withdraw(id: token.id, from: self.owner?.address)
return <- token
}
pub fun deposit(token: @NonFungibleToken.NFT) {
let token <- token as! @Foo3.NFT
let id: UInt64 = token.id
let oldToken <- self.ownedNFTs[id] <- token
destroy oldToken
// Add the NFT ID to the end of the IDs list
self.ids.append(id)
emit Deposit(id: id, to: self.owner?.address)
}
pub fun getIDs(): [UInt64] {
return self.ids
}
}
Implemented in #143