Cadence Cookbook

Contribute

Purchase NFT on Marketplace

Purchase NFT on Marketplace

01 Apr 2022

Contributed by Flow Blockchain

Intermediate

Buy an NFT from a marketplace.

Smart Contract Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 //Rest of NFTStorefront contract above pub fun purchase(payment: @FungibleToken.Vault): @NonFungibleToken.NFT { pre { self.details.purchased == false: "listing has already been purchased" payment.isInstance(self.details.salePaymentVaultType): "payment vault is not requested fungible token" payment.balance == self.details.salePrice: "payment vault does not contain requested price" } // Make sure the listing cannot be purchased again. self.details.setToPurchased() // Fetch the token to return to the purchaser. let nft <-self.nftProviderCapability.borrow()!.withdraw(withdrawID: self.details.nftID) // Neither receivers nor providers are trustworthy, they must implement the correct // interface but beyond complying with its pre/post conditions they are not gauranteed // to implement the functionality behind the interface in any given way. // Therefore we cannot trust the Collection resource behind the interface, // and we must check the NFT resource it gives us to make sure that it is the correct one. assert(nft.isInstance(self.details.nftType), message: "withdrawn NFT is not of specified type") assert(nft.id == self.details.nftID, message: "withdrawn NFT does not have specified ID") // Rather than aborting the transaction if any receiver is absent when we try to pay it, // we send the cut to the first valid receiver. // The first receiver should therefore either be the seller, or an agreed recipient for // any unpaid cuts. var residualReceiver: &{FungibleToken.Receiver}? = nil // Pay each beneficiary their amount of the payment. for cut in self.details.saleCuts { if let receiver = cut.receiver.borrow() { let paymentCut <- payment.withdraw(amount: cut.amount) receiver.deposit(from: <-paymentCut) if (residualReceiver == nil) { residualReceiver = receiver } } } assert(residualReceiver != nil, message: "No valid payment receivers") // At this point, if all recievers were active and availabile, then the payment Vault will have // zero tokens left, and this will functionally be a no-op that consumes the empty vault residualReceiver!.deposit(from: <-payment) // If the listing is purchased, we regard it as completed here. // Otherwise we regard it as completed in the destructor. emit ListingCompleted( listingResourceID: self.uuid, storefrontResourceID: self.details.storefrontID, purchased: self.details.purchased ) return <-nft } //Rest of NFTStorefront contract below

When purchasing an NFT, all that is needed from the purchaser is their payment that comes in the form of their vault resource.

Once that happens, the smart contract checks to make sure the listing hasn't already been purchased, the fungible token is the right type for this transaction, and that the payment is the correct amount.

If this is the case then the purchase can be approved. We change the details of the listing to purchased. We will then withdraw the NFT from the account that listed it.

We check to see if the NFT is of the right type and the right ID to make sure that the NFT being purchased is in fact the correct one.

After that, we take all of the sales cuts and start depositing the amounts into the accounts vault receivers we have capabilities for.

Then we move the remaining payment vault into the residual receiver local variable. The price of the remaining payment vault should be 0 so we are just moving our resource into there.

Lastly we emit that the transaction has completed and we return the NFT which can then be deposited into the purchasers collection.

Transaction Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import FungibleToken from 0x01 import NonFungibleToken from 0x02 import ExampleNFT from 0x04 import NFTStorefront from 0x03 transaction { let paymentVault: @FungibleToken.Vault let exampleNFTCollection: &ExampleNFT.Collection{NonFungibleToken.Receiver} let storefront: &NFTStorefront.Storefront{NFTStorefront.StorefrontPublic} let listing: &NFTStorefront.Listing{NFTStorefront.ListingPublic} prepare(acct: AuthAccount) { self.storefront = getAccount(0x04) .getCapability<&NFTStorefront.Storefront{NFTStorefront.StorefrontPublic}>( NFTStorefront.StorefrontPublicPath )! .borrow() ?? panic("Could not borrow Storefront from provided address") self.listing = self.storefront.borrowListing(listingResourceID: 10) ?? panic("No Offer with that ID in Storefront") let price = self.listing.getDetails().salePrice let mainFlowVault = acct.borrow<&FungibleToken.Vault>(from: /storage/MainVault) ?? panic("Cannot borrow FlowToken vault from acct storage") self.paymentVault <- mainFlowVault.withdraw(amount: price) self.exampleNFTCollection = acct.borrow<&ExampleNFT.Collection{NonFungibleToken.Receiver}>( from: ExampleNFT.CollectionStoragePath) ?? panic("Cannot borrow NFT collection receiver from account") } execute { let item <- self.listing.purchase( payment: <-self.paymentVault ) self.exampleNFTCollection.deposit(token: <-item) /* //- error: Execution failed: computation limited exceeded: 100 */ // Be kind and recycle self.storefront.cleanup(listingResourceID: 10) log("transaction done") } //- Post to check item is in collection? }

Here we are defining initially the types that we are expected to return for each of the listed variables.

First we need to get the storefront of the account that the listing is under. You will have linked a public capability to this store front so that others can access it and purchase your listing.

Once we borrow that storefront, we then want to borrow the listing that we are interested in buying. We assign that to the listing variable.

We'll then fetch the price of that listing so that in the next step when we take our Vault resource and withdraw tokens from it, we pass in the price of the NFT because that is how much we would like to withdraw.

We'll also get our Receiver capability so that we can deposit the NFT into our collection.

When we're ready to execute, we call the purchase function on our listing and pass in the payment vault we'll be paying for this with. This will return and NFT resource for us that we temporarily store in our item variable.

We'll then deposit that NFT into our collection, and then we'll cleanup the storefront by deleting our listing, and with that you have created a purchase on an NFT marketplace.


ProgressNFT Storefront Essentials

100%


Related Recipes

14 Oct 2022
Create a Marketplace
Intermediate
14 Oct 2022
Create an NFT Listing
Intermediate