From 12225e5dd0a1f9fea4e7ca2f8f591f9f0cf7d4b1 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Thu, 24 Jul 2025 12:50:28 -0500 Subject: [PATCH 1/4] add setup to sell item transactions --- scripts/has_listing_become_ghosted.cdc | 2 +- scripts/read_all_unique_ghost_listings.cdc | 2 +- scripts/read_allowed_commission_receivers.cdc | 2 +- scripts/read_duplicate_listing_ids.cdc | 9 +++++---- scripts/read_listing_details.cdc | 2 +- scripts/read_storefront_ids.cdc | 2 +- tests/NFTStorefrontV2_test.cdc | 5 ++++- transactions/sell_item.cdc | 17 +++++++++++++++++ .../sell_item_and_replace_current_listing.cdc | 16 ++++++++++++++++ transactions/sell_item_with_marketplace_cut.cdc | 16 ++++++++++++++++ 10 files changed, 63 insertions(+), 10 deletions(-) diff --git a/scripts/has_listing_become_ghosted.cdc b/scripts/has_listing_become_ghosted.cdc index eadffc3..a7bc829 100644 --- a/scripts/has_listing_become_ghosted.cdc +++ b/scripts/has_listing_become_ghosted.cdc @@ -1,4 +1,4 @@ -import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc" +import "NFTStorefrontV2" /// This script tells whether the provided `listingID` under the provided `storefront` address /// has a ghost listing. diff --git a/scripts/read_all_unique_ghost_listings.cdc b/scripts/read_all_unique_ghost_listings.cdc index 778058d..bf1b5ce 100644 --- a/scripts/read_all_unique_ghost_listings.cdc +++ b/scripts/read_all_unique_ghost_listings.cdc @@ -1,4 +1,4 @@ -import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc" +import "NFTStorefrontV2" /// This script provides the array of listing resource Id which got ghosted It automatically skips the duplicate listing /// as duplicate listings would get automatically delete once the primary one. diff --git a/scripts/read_allowed_commission_receivers.cdc b/scripts/read_allowed_commission_receivers.cdc index 6e4cb8a..7734af5 100644 --- a/scripts/read_allowed_commission_receivers.cdc +++ b/scripts/read_allowed_commission_receivers.cdc @@ -1,4 +1,4 @@ -import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc" +import "NFTStorefrontV2" import "FungibleToken" /// This script returns the list of allowed commission receivers supported by the given listing Id. diff --git a/scripts/read_duplicate_listing_ids.cdc b/scripts/read_duplicate_listing_ids.cdc index 1001534..849f481 100644 --- a/scripts/read_duplicate_listing_ids.cdc +++ b/scripts/read_duplicate_listing_ids.cdc @@ -1,11 +1,12 @@ -import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc" -import ExampleNFT from "../contracts/utility/ExampleNFT.cdc" +import "NFTStorefrontV2" /// This script returns an array of all the duplicate listingIDs for a given nftID. /// -access(all) fun main(account: Address, nftID: UInt64, listingID: UInt64): [UInt64] { +access(all) fun main(account: Address, nftID: UInt64, listingID: UInt64, nftTypeIdentifier: String): [UInt64] { + let nftType = CompositeType(nftTypeIdentifier) ?? panic("Could not construct type from identifier ".concat(nftTypeIdentifier)) + return getAccount(account).capabilities.borrow<&{NFTStorefrontV2.StorefrontPublic}>( NFTStorefrontV2.StorefrontPublicPath - )?.getDuplicateListingIDs(nftType: Type<@ExampleNFT.NFT>(), nftID: nftID, listingID: listingID) + )?.getDuplicateListingIDs(nftType: nftType, nftID: nftID, listingID: listingID) ?? panic("Could not borrow public storefront from address") } diff --git a/scripts/read_listing_details.cdc b/scripts/read_listing_details.cdc index 0d8595b..009c808 100644 --- a/scripts/read_listing_details.cdc +++ b/scripts/read_listing_details.cdc @@ -1,4 +1,4 @@ -import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc" +import "NFTStorefrontV2" /// This script returns the details for a listing within a storefront /// diff --git a/scripts/read_storefront_ids.cdc b/scripts/read_storefront_ids.cdc index 5d91631..9648061 100644 --- a/scripts/read_storefront_ids.cdc +++ b/scripts/read_storefront_ids.cdc @@ -1,4 +1,4 @@ -import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc" +import "NFTStorefrontV2" // This script returns an array of all the nft uuids for sale through a Storefront diff --git a/tests/NFTStorefrontV2_test.cdc b/tests/NFTStorefrontV2_test.cdc index 1b30451..d18fad5 100644 --- a/tests/NFTStorefrontV2_test.cdc +++ b/tests/NFTStorefrontV2_test.cdc @@ -186,7 +186,10 @@ fun testBuyItem() { let allowedCommissionReceivers = scriptExecutor("read_allowed_commission_receivers.cdc", [seller.address, listingID]) let listingDetails = scriptExecutor("read_listing_details.cdc", [seller.address, listingID]) Test.assert(listingDetails != nil, message: "Received invalid result from reading listing details") - let duplicateListingIDs = scriptExecutor("read_duplicate_listing_ids.cdc", [seller.address, listedNFTID, listingID]) + let duplicateListingIDs = scriptExecutor( + "read_duplicate_listing_ids.cdc", + [seller.address, listedNFTID, listingID, "A.0000000000000008.ExampleNFT.NFT"] + ) Test.assertEqual((duplicateListingIDs as! [UInt64]?)!.length, 0) let code = loadCode("buy_item.cdc", "transactions") diff --git a/transactions/sell_item.cdc b/transactions/sell_item.cdc index 4feb4cc..fc59641 100644 --- a/transactions/sell_item.cdc +++ b/transactions/sell_item.cdc @@ -33,6 +33,23 @@ transaction( var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) { + + // If the account doesn't already have a Storefront + if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil { + + // Create a new empty Storefront + let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront + + // save it to the account + acct.storage.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath) + + // create a public capability for the Storefront + let storefrontPublicCap = acct.capabilities.storage.issue<&{NFTStorefrontV2.StorefrontPublic}>( + NFTStorefrontV2.StorefrontStoragePath + ) + acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath) + } + self.saleCuts = [] self.marketplacesCapability = [] diff --git a/transactions/sell_item_and_replace_current_listing.cdc b/transactions/sell_item_and_replace_current_listing.cdc index 931a0c5..279ca06 100644 --- a/transactions/sell_item_and_replace_current_listing.cdc +++ b/transactions/sell_item_and_replace_current_listing.cdc @@ -33,6 +33,22 @@ transaction( var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) { + // If the account doesn't already have a Storefront + if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil { + + // Create a new empty Storefront + let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront + + // save it to the account + acct.storage.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath) + + // create a public capability for the Storefront + let storefrontPublicCap = acct.capabilities.storage.issue<&{NFTStorefrontV2.StorefrontPublic}>( + NFTStorefrontV2.StorefrontStoragePath + ) + acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath) + } + self.saleCuts = [] self.marketplacesCapability = [] diff --git a/transactions/sell_item_with_marketplace_cut.cdc b/transactions/sell_item_with_marketplace_cut.cdc index 809bb41..a805474 100644 --- a/transactions/sell_item_with_marketplace_cut.cdc +++ b/transactions/sell_item_with_marketplace_cut.cdc @@ -33,6 +33,22 @@ transaction( prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) { + // If the account doesn't already have a Storefront + if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil { + + // Create a new empty Storefront + let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront + + // save it to the account + acct.storage.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath) + + // create a public capability for the Storefront + let storefrontPublicCap = acct.capabilities.storage.issue<&{NFTStorefrontV2.StorefrontPublic}>( + NFTStorefrontV2.StorefrontStoragePath + ) + acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath) + } + self.saleCuts = [] self.marketplacesCapability = [] From 6488b135969a0cfa37b1741e7df4d120f6765519 Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Mon, 28 Jul 2025 11:05:16 -0500 Subject: [PATCH 2/4] update buy_item to use generic types --- flow.json | 16 +++---- transactions/buy_item.cdc | 44 ++++++++++++------- transactions/sell_item.cdc | 6 +-- .../sell_item_and_replace_current_listing.cdc | 8 ++-- .../sell_item_with_marketplace_cut.cdc | 8 ++-- 5 files changed, 47 insertions(+), 35 deletions(-) diff --git a/flow.json b/flow.json index 4df1cfa..4d36ec0 100644 --- a/flow.json +++ b/flow.json @@ -45,7 +45,7 @@ }, "dependencies": { "Burner": { - "source": "mainnet://f233dcee88fe0abe.Burner", + "source": "testnet://9a0766d93b6608b7.Burner", "hash": "71af18e227984cd434a3ad00bb2f3618b76482842bae920ee55662c37c8bf331", "aliases": { "emulator": "f8d6e0586b0a20c7", @@ -134,8 +134,8 @@ } }, "FungibleToken": { - "source": "mainnet://f233dcee88fe0abe.FungibleToken", - "hash": "23c1159cf99b2b039b6b868d782d57ae39b8d784045d81597f100a4782f0285b", + "source": "testnet://9a0766d93b6608b7.FungibleToken", + "hash": "d84e41a86bd27806367b89c75a2a9570f7713480ab113cc214c4576466888958", "aliases": { "emulator": "ee82856bf20e2aa6", "mainnet": "f233dcee88fe0abe", @@ -170,8 +170,8 @@ } }, "MetadataViews": { - "source": "mainnet://1d7e57aa55817448.MetadataViews", - "hash": "10a239cc26e825077de6c8b424409ae173e78e8391df62750b6ba19ffd048f51", + "source": "testnet://631e88ae7f1d7c20.MetadataViews", + "hash": "0b746cabba668c39de9dc47b94fc458f4119cbabb6883616e4a8750518310ccb", "aliases": { "emulator": "f8d6e0586b0a20c7", "mainnet": "1d7e57aa55817448", @@ -222,8 +222,8 @@ } }, "NonFungibleToken": { - "source": "mainnet://1d7e57aa55817448.NonFungibleToken", - "hash": "b63f10e00d1a814492822652dac7c0574428a200e4c26cb3c832c4829e2778f0", + "source": "testnet://631e88ae7f1d7c20.NonFungibleToken", + "hash": "ac40c5a3ec05884ae48cb52ebf680deebb21e8a0143cd7d9b1dc88b0f107e088", "aliases": { "emulator": "f8d6e0586b0a20c7", "mainnet": "1d7e57aa55817448", @@ -231,7 +231,7 @@ } }, "ViewResolver": { - "source": "mainnet://1d7e57aa55817448.ViewResolver", + "source": "testnet://631e88ae7f1d7c20.ViewResolver", "hash": "374a1994046bac9f6228b4843cb32393ef40554df9bd9907a702d098a2987bde", "aliases": { "emulator": "f8d6e0586b0a20c7", diff --git a/transactions/buy_item.cdc b/transactions/buy_item.cdc index e7eb5ba..5da52ee 100644 --- a/transactions/buy_item.cdc +++ b/transactions/buy_item.cdc @@ -1,27 +1,43 @@ -import "ExampleToken" import "FungibleToken" +import "FungibleTokenMetadataViews" import "NonFungibleToken" -import "ExampleNFT" import "NFTStorefrontV2" import "MetadataViews" -/// Transaction facilitates the purcahse of listed NFT. It takes the storefront address, listing resource that need to be +/// Transaction facilitates the purchase of listed NFT. +/// It takes the storefront address, listing resource that need to be /// purchased & a address that will takeaway the commission. /// -/// Buyer of the listing (,i.e. underling NFT) would authorize and sign the transaction and if purchase happens then +/// Buyer of the listing (,i.e. underling NFT) would authorize +/// and sign the transaction and if purchase happens then /// transacted NFT would store in buyer's collection. /// transaction(listingResourceID: UInt64, storefrontAddress: Address, - commissionRecipient: Address?) { + commissionRecipient: Address?, + nftTypeIdentifier: String, + ftTypeIdentifier: String) { let paymentVault: @{FungibleToken.Vault} - let exampleNFTReceiver: &{NonFungibleToken.Receiver} + let NFTReceiver: &{NonFungibleToken.Receiver} let storefront: &{NFTStorefrontV2.StorefrontPublic} let listing: &{NFTStorefrontV2.ListingPublic} var commissionRecipientCap: Capability<&{FungibleToken.Receiver}>? prepare(acct: auth(BorrowValue) &Account) { + + let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: nftTypeIdentifier, + viewType: Type() + ) as? MetadataViews.NFTCollectionData + ?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)") + + let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: ftTypeIdentifier, + viewType: Type() + ) as? FungibleTokenMetadataViews.FTVaultData + ?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)") + self.commissionRecipientCap = nil // Access the storefront public resource of the seller to purchase the listing. self.storefront = getAccount(storefrontAddress).capabilities.borrow<&{NFTStorefrontV2.StorefrontPublic}>( @@ -34,16 +50,12 @@ transaction(listingResourceID: UInt64, let price = self.listing.getDetails().salePrice // Access the vault of the buyer to pay the sale price of the listing. - let mainVault = acct.storage.borrow(from: /storage/exampleTokenVault) - ?? panic("The signer does not store an ExampleToken.Vault object at the path /storage/exampleTokenVault " + let mainVault = acct.storage.borrow(from: vaultData.storagePath) + ?? panic("The signer does not store an Vault object at the path \(vaultData.storagePath)" .concat(". The signer must initialize their account with this vault first!")) self.paymentVault <- mainVault.withdraw(amount: price) - // Access the buyer's NFT collection to store the purchased NFT. - let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type()) as! MetadataViews.NFTCollectionData? - ?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction") - - self.exampleNFTReceiver = acct.capabilities.borrow<&{NonFungibleToken.Receiver}>(collectionData.publicPath) + self.NFTReceiver = acct.capabilities.borrow<&{NonFungibleToken.Receiver}>(collectionData.publicPath) ?? panic("Cannot borrow an NFT collection receiver from the signer's account at path \(collectionData.publicPath).") // Fetch the commission amt. @@ -52,9 +64,9 @@ transaction(listingResourceID: UInt64, if commissionRecipient != nil && commissionAmount != 0.0 { // Access the capability to receive the commission. let _commissionRecipientCap = getAccount(commissionRecipient!).capabilities.get<&{FungibleToken.Receiver}>( - /public/exampleTokenReceiver + vaultData.receiverPath ) - assert(_commissionRecipientCap.check(), message: "Commission Recipient doesn't have ExampleToken receiving capability") + assert(_commissionRecipientCap.check(), message: "Commission Recipient doesn't have a receiving capability at \(vaultData.receiverPath)") self.commissionRecipientCap = _commissionRecipientCap } else if commissionAmount == 0.0 { self.commissionRecipientCap = nil @@ -70,6 +82,6 @@ transaction(listingResourceID: UInt64, commissionRecipient: self.commissionRecipientCap ) // Deposit the NFT in the buyer's collection. - self.exampleNFTReceiver.deposit(token: <-item) + self.NFTReceiver.deposit(token: <-item) } } diff --git a/transactions/sell_item.cdc b/transactions/sell_item.cdc index fc59641..b1e9b5e 100644 --- a/transactions/sell_item.cdc +++ b/transactions/sell_item.cdc @@ -27,7 +27,7 @@ transaction( ) { let tokenReceiver: Capability<&{FungibleToken.Receiver}> - let exampleNFTProvider: Capability + let NFTProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] @@ -79,7 +79,7 @@ transaction( } assert(nftProviderCap?.check() ?? false, message: "Could not assign Provider Capability") - self.exampleNFTProvider = nftProviderCap! + self.NFTProvider = nftProviderCap! let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath @@ -128,7 +128,7 @@ transaction( execute { // Create listing self.storefront.createListing( - nftProviderCapability: self.exampleNFTProvider, + nftProviderCapability: self.NFTProvider, nftType: Type<@ExampleNFT.NFT>(), nftID: saleItemID, salePaymentVaultType: Type<@ExampleToken.Vault>(), diff --git a/transactions/sell_item_and_replace_current_listing.cdc b/transactions/sell_item_and_replace_current_listing.cdc index 279ca06..4697a63 100644 --- a/transactions/sell_item_and_replace_current_listing.cdc +++ b/transactions/sell_item_and_replace_current_listing.cdc @@ -27,7 +27,7 @@ transaction( ) { let tokenReceiver: Capability<&{FungibleToken.Receiver}> - let exampleNFTProvider: Capability + let NFTProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing, NFTStorefrontV2.RemoveListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] @@ -82,7 +82,7 @@ transaction( } assert(nftProviderCap?.check() ?? false, message: "Could not assign Provider Capability") - self.exampleNFTProvider = nftProviderCap! + self.NFTProvider = nftProviderCap! let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath @@ -115,7 +115,7 @@ transaction( amount: effectiveSaleItemPrice - totalRoyaltyCut ) ) - assert(self.exampleNFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider") + assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider") self.storefront = acct.storage.borrow( from: NFTStorefrontV2.StorefrontStoragePath @@ -143,7 +143,7 @@ transaction( } // Create listing self.storefront.createListing( - nftProviderCapability: self.exampleNFTProvider, + nftProviderCapability: self.NFTProvider, nftType: Type<@ExampleNFT.NFT>(), nftID: saleItemID, salePaymentVaultType: Type<@ExampleToken.Vault>(), diff --git a/transactions/sell_item_with_marketplace_cut.cdc b/transactions/sell_item_with_marketplace_cut.cdc index a805474..6be9dab 100644 --- a/transactions/sell_item_with_marketplace_cut.cdc +++ b/transactions/sell_item_with_marketplace_cut.cdc @@ -26,7 +26,7 @@ transaction( marketPlaceSaleCutPercentage: UFix64 ) { let flowReceiver: Capability<&{FungibleToken.Receiver}> - let exampleNFTProvider: Capability + let NFTProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] @@ -81,7 +81,7 @@ transaction( } assert(nftProviderCap?.check() ?? false, message: "Could not assign Provider Capability") - self.exampleNFTProvider = nftProviderCap! + self.NFTProvider = nftProviderCap! let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath @@ -111,7 +111,7 @@ transaction( amount: saleItemPrice - totalRoyaltyCut - saleItemPrice * marketPlaceSaleCutPercentage ) ) - assert(self.exampleNFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider") + assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider") self.storefront = acct.storage.borrow( from: NFTStorefrontV2.StorefrontStoragePath @@ -134,7 +134,7 @@ transaction( execute { // Create listing self.storefront.createListing( - nftProviderCapability: self.exampleNFTProvider, + nftProviderCapability: self.NFTProvider, nftType: Type<@ExampleNFT.NFT>(), nftID: saleItemID, salePaymentVaultType: Type<@FlowToken.Vault>(), From 5e2fb6bdabe43739c050f816359e84edf7352fed Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Mon, 28 Jul 2025 14:16:30 -0500 Subject: [PATCH 3/4] use generic types in V2 transactions --- tests/NFTStorefrontV2_test.cdc | 53 +++++++++++++++---- tests/transactions/MVbytes | 1 + tests/transactions/update_contract.cdc | 5 ++ transactions/buy_item.cdc | 3 +- transactions/sell_item.cdc | 42 ++++++++++----- .../sell_item_and_replace_current_listing.cdc | 48 +++++++++++------ .../sell_item_with_marketplace_cut.cdc | 50 +++++++++++------ 7 files changed, 145 insertions(+), 57 deletions(-) create mode 100644 tests/transactions/MVbytes create mode 100644 tests/transactions/update_contract.cdc diff --git a/tests/NFTStorefrontV2_test.cdc b/tests/NFTStorefrontV2_test.cdc index d18fad5..264cb59 100644 --- a/tests/NFTStorefrontV2_test.cdc +++ b/tests/NFTStorefrontV2_test.cdc @@ -13,6 +13,11 @@ access(all) let marketplace = Test.createAccount() access(all) let storefrontAccount = Test.getAccount(0x0000000000000007) access(all) let exampleNFTAccount = Test.getAccount(0x0000000000000008) access(all) let exampleTokenAccount = Test.getAccount(0x0000000000000009) + +access(all) let nftTypeIdentifier = "A.0000000000000008.ExampleNFT.NFT" +access(all) let ftTypeIdentifier = "A.0000000000000009.ExampleToken.Vault" +access(all) let flowTokenTypeIdentifier = "A.0000000000000003.FlowToken.Vault" + access(all) var nftCount = 1 access(all) @@ -34,6 +39,20 @@ access(all) fun setup() { let serviceAccount = Test.serviceAccount() + // TODO: Remove this section once MetadataViews is updated on the emulator + // with the resolveViewfromIdentifier function. + let metadataViewsCode = loadCode("MVbytes", "tests/transactions") + var tx = Test.Transaction( + code: loadCode("update_contract.cdc", "tests/transactions"), + authorizers: [serviceAccount.address], + signers: [serviceAccount], + arguments: ["MetadataViews", metadataViewsCode], + ) + + var txResult = Test.executeTransaction(tx) + Test.expect(txResult, Test.beSucceeded()) + // TODO: End of section to remove + var err = Test.deployContract( name: "NFTStorefrontV2", path: "../contracts/NFTStorefrontV2.cdc", @@ -57,13 +76,13 @@ fun setup() { // Setup example token var code = loadCode("setup_account.cdc", "transactions/example-token") - var tx = Test.Transaction( + tx = Test.Transaction( code: code, authorizers: [buyer.address], signers: [buyer], arguments: [], ) - var txResult = Test.executeTransaction(tx) + txResult = Test.executeTransaction(tx) Test.expect(txResult, Test.beSucceeded()) tx = Test.Transaction( @@ -160,7 +179,9 @@ fun testSellItem() { "Custom", // custom id 0.1, // commission amount UInt64(2025908543), // 10 years in the future - [] // Marketplaces address + [], // Marketplaces address + nftTypeIdentifier, // nft type + ftTypeIdentifier // ft type ], ) var txResult = Test.executeTransaction(tx) @@ -188,7 +209,7 @@ fun testBuyItem() { Test.assert(listingDetails != nil, message: "Received invalid result from reading listing details") let duplicateListingIDs = scriptExecutor( "read_duplicate_listing_ids.cdc", - [seller.address, listedNFTID, listingID, "A.0000000000000008.ExampleNFT.NFT"] + [seller.address, listedNFTID, listingID, nftTypeIdentifier] ) Test.assertEqual((duplicateListingIDs as! [UInt64]?)!.length, 0) @@ -200,7 +221,9 @@ fun testBuyItem() { arguments: [ listingID, // listing resource id seller.address, // storefront address - seller.address // commision recipient + seller.address, // commision recipient + nftTypeIdentifier, // nft type + ftTypeIdentifier // ft type ], ) let txResult = Test.executeTransaction(tx) @@ -261,7 +284,9 @@ fun testCleanupGhostListings() { "Custom", // custom id 0.1, // commission amount UInt64(2025908543), // 10 years in the future - [] // Marketplaces address + [], // Marketplaces address + nftTypeIdentifier, // nft type + ftTypeIdentifier // ft type ], ) var txResult = Test.executeTransaction(tx) @@ -337,7 +362,9 @@ fun testSellItemWithMarketplaceCut() { "Custom1", // custom id UInt64(2025908543), // 10 years in the future seller.address, // set the buyer as the marketplace sale cut receiver - 0.1 // Marketplaces address + 0.1, // Marketplaces address + nftTypeIdentifier, // nft type + flowTokenTypeIdentifier // ft type ], ) let txResult = Test.executeTransaction(tx) @@ -378,7 +405,9 @@ fun testSellItemAndReplaceCurrentListing() { "Custom1", // custom id 0.1, // commission amount timestamp, // way in the past (testing expired listing next) - [seller.address] // set the buyer as the marketplace sale cut receiver + [seller.address], // set the buyer as the marketplace sale cut receiver + nftTypeIdentifier, // nft type + ftTypeIdentifier // ft type ], ) let txResult = Test.executeTransaction(tx) @@ -458,7 +487,9 @@ fun testRemoveItem() { "Custom1", // custom id UInt64(2025908543), // 10 years in the future seller.address, // set the buyer as the marketplace sale cut receiver - 0.1 // Marketplaces address + 0.1, // Marketplaces address + nftTypeIdentifier, // nft type + flowTokenTypeIdentifier // ft type ], ) var txResult = Test.executeTransaction(tx) @@ -531,7 +562,9 @@ fun testSellMaliciousListing() { arguments: [ listingID, // listing resource id exampleNFTAccount.address, // storefront address - exampleNFTAccount.address // commision recipient + exampleNFTAccount.address, // commision recipient + nftTypeIdentifier, // nft type + ftTypeIdentifier // ft type ], ) txResult = Test.executeTransaction(tx) diff --git a/tests/transactions/MVbytes b/tests/transactions/MVbytes new file mode 100644 index 0000000..67d1115 --- /dev/null +++ b/tests/transactions/MVbytes @@ -0,0 +1 @@ +696d706f72742046756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030320a696d706f7274204e6f6e46756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030310a696d706f727420566965775265736f6c7665722066726f6d203078303030303030303030303030303030310a0a2f2f2f205468697320636f6e747261637420696d706c656d656e747320746865206d65746164617461207374616e646172642070726f706f7365640a2f2f2f20696e20464c49502d303633362e0a2f2f2f0a2f2f2f205265663a2068747470733a2f2f6769746875622e636f6d2f6f6e666c6f772f666c6970732f626c6f622f6d61696e2f6170706c69636174696f6e2f32303231303931362d6e66742d6d657461646174612e6d640a2f2f2f0a2f2f2f205374727563747320616e64207265736f75726365732063616e20696d706c656d656e74206f6e65206f72206d6f72650a2f2f2f206d657461646174612074797065732c2063616c6c65642076696577732e20456163682076696577207479706520726570726573656e74730a2f2f2f206120646966666572656e74206b696e64206f66206d657461646174612c207375636820617320612063726561746f722062696f6772617068790a2f2f2f206f722061204a50454720696d6167652066696c652e0a2f2f2f0a61636365737328616c6c2920636f6e7472616374204d657461646174615669657773207b0a0a202020202f2f2f2046756e6374696f6e20746f207265736f6c7665206120636f6e74726163742076696577206261736564206f6e20612074797065206964656e74696669657220537472696e670a202020202f2f2f20616e64207669657720747970652e20426f72726f77732074686520636f6e747261637420617320267b566965775265736f6c7665727d20616e64200a202020202f2f2f207468656e2063616c6c73207265736f6c7665436f6e74726163745669657720666f722074686520737065636966696564207479706520616e6420766965772e0a202020202f2f2f0a202020202f2f2f2040706172616d207265736f75726365547970654964656e7469666965723a205468652074797065206964656e746966696572206f6620746865207265736f757263650a202020202f2f2f2040706172616d2076696577547970653a205468652054797065206f6620746865206465736972656420766965772e0a202020202f2f2f204072657475726e20412073747275637475726520726570726573656e74696e67207468652072657175657374656420766965772e20496620616e797468696e67206661696c65642c2072657475726e73206e696c0a202020202f2f2f0a2020202061636365737328616c6c292066756e207265736f6c7665436f6e74726163745669657746726f6d547970654964656e746966696572280a20202020202020207265736f75726365547970654964656e7469666965723a20537472696e672c0a202020202020202076696577547970653a20547970650a20202020293a20416e795374727563743f207b0a20202020202020206966206c6574207265736f7572636554797065203d20436f6d706f7369746554797065287265736f75726365547970654964656e74696669657229207b0a2020202020202020202020206966206c657420766965775265736f6c766572526566203d206765744163636f756e74287265736f75726365547970652e6164647265737321292e636f6e7472616374732e626f72726f773c267b566965775265736f6c7665727d3e280a202020202020202020202020202020206e616d653a207265736f75726365547970652e636f6e74726163744e616d65210a20202020202020202020202029207b0a2020202020202020202020202020202072657475726e20766965775265736f6c7665725265662e7265736f6c7665436f6e747261637456696577287265736f75726365547970653a207265736f75726365547970652c2076696577547970653a207669657754797065290a2020202020202020202020207d20656c7365207b0a2020202020202020202020202020202072657475726e206e696c0a2020202020202020202020207d0a20202020202020207d20656c7365207b0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a202020207d0a0a0a202020202f2f2f20446973706c617920697320612062617369632076696577207468617420696e636c7564657320746865206e616d652c206465736372697074696f6e20616e640a202020202f2f2f207468756d626e61696c20666f7220616e206f626a6563742e204d6f7374206f626a656374732073686f756c6420696d706c656d656e74207468697320766965772e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420446973706c6179207b0a0a20202020202020202f2f2f20546865206e616d65206f6620746865206f626a6563742e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c642077696c6c20626520646973706c6179656420696e206c6973747320616e64207468657265666f72652073686f756c640a20202020202020202f2f2f2062652073686f727420616e20636f6e636973652e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f2f2041207772697474656e206465736372697074696f6e206f6620746865206f626a6563742e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c642077696c6c20626520646973706c6179656420696e20612064657461696c65642076696577206f6620746865206f626a6563742c0a20202020202020202f2f2f20736f2063616e206265206d6f726520766572626f73652028652e672e20612070617261677261706820696e7374656164206f6620612073696e676c65206c696e65292e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e670a0a20202020202020202f2f2f204120736d616c6c207468756d626e61696c20726570726573656e746174696f6e206f6620746865206f626a6563742e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c642073686f756c642062652061207765622d667269656e646c792066696c652028692e65204a5045472c20504e47290a20202020202020202f2f2f20746861742063616e20626520646973706c6179656420696e206c697374732c206c696e6b2070726576696577732c206574632e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574207468756d626e61696c3a207b46696c657d0a0a20202020202020207669657720696e6974280a2020202020202020202020206e616d653a20537472696e672c0a2020202020202020202020206465736372697074696f6e3a20537472696e672c0a2020202020202020202020207468756d626e61696c3a207b46696c657d0a202020202020202029207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020202020202073656c662e7468756d626e61696c203d207468756d626e61696c0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420446973706c617920696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c20446973706c6179207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574446973706c6179285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a20446973706c61793f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c446973706c61793e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20446973706c6179207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2047656e6572696320696e74657266616365207468617420726570726573656e747320612066696c652073746f726564206f6e206f72206f666620636861696e2e2046696c65730a202020202f2f2f2063616e206265207573656420746f207265666572656e63657320696d616765732c20766964656f7320616e64206f74686572206d656469612e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420696e746572666163652046696c65207b0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e670a202020207d0a0a202020202f2f2f205669657720746f206578706f736520612066696c6520746861742069732061636365737369626c6520617420616e204854545020286f72204854545053292055524c2e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204854545046696c653a2046696c65207b0a202020202020202061636365737328616c6c29206c65742075726c3a20537472696e670a0a20202020202020207669657720696e69742875726c3a20537472696e6729207b0a20202020202020202020202073656c662e75726c203d2075726c0a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e67207b0a20202020202020202020202072657475726e2073656c662e75726c0a20202020202020207d0a202020207d0a0a202020202f2f2f205669657720746f206578706f736520612066696c652073746f726564206f6e20495046532e0a202020202f2f2f204950465320696d6167657320617265207265666572656e63656420627920746865697220636f6e74656e74206964656e7469666965722028434944290a202020202f2f2f20726174686572207468616e206120646972656374205552492e204120636c69656e74206170706c69636174696f6e2063616e207573652074686973204349440a202020202f2f2f20746f2066696e6420616e64206c6f61642074686520696d6167652076696120616e204950465320676174657761792e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204950465346696c653a2046696c65207b0a0a20202020202020202f2f2f204349442069732074686520636f6e74656e74206964656e74696669657220666f72207468697320495046532066696c652e0a20202020202020202f2f2f0a20202020202020202f2f2f205265663a2068747470733a2f2f646f63732e697066732e696f2f636f6e63657074732f636f6e74656e742d61646472657373696e672f0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206369643a20537472696e670a0a20202020202020202f2f2f205061746820697320616e206f7074696f6e616c207061746820746f207468652066696c65207265736f7572636520696e20616e2049504653206469726563746f72792e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c64206973206f6e6c79206e6565646564206966207468652066696c6520697320696e736964652061206469726563746f72792e0a20202020202020202f2f2f0a20202020202020202f2f2f205265663a2068747470733a2f2f646f63732e697066732e696f2f636f6e63657074732f66696c652d73797374656d732f0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c657420706174683a20537472696e673f0a0a20202020202020207669657720696e6974286369643a20537472696e672c20706174683a20537472696e673f29207b0a20202020202020202020202073656c662e636964203d206369640a20202020202020202020202073656c662e70617468203d20706174680a20202020202020207d0a0a20202020202020202f2f2f20546869732066756e6374696f6e2072657475726e73207468652049504653206e61746976652055524c20666f7220746869732066696c652e0a20202020202020202f2f2f205265663a2068747470733a2f2f646f63732e697066732e696f2f686f772d746f2f616464726573732d697066732d6f6e2d7765622f236e61746976652d75726c730a20202020202020202f2f2f0a20202020202020202f2f2f204072657475726e2054686520737472696e6720636f6e7461696e696e67207468652066696c65207572690a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e67207b0a2020202020202020202020206966206c65742070617468203d2073656c662e70617468207b0a2020202020202020202020202020202072657475726e2022697066733a2f2f5c2873656c662e636964292f5c287061746829220a2020202020202020202020207d0a0a20202020202020202020202072657475726e2022697066733a2f2f5c2873656c662e63696429220a20202020202020207d0a202020207d0a0a202020202f2f2f20412073747275637420746f20726570726573656e7420612067656e65726963205552492e204d6179206265207573656420746f20726570726573656e742074686520555249206f660a202020202f2f2f20746865204e4654207768657265207468652074797065206f6620555249206973206e6f742061626c6520746f2062652064657465726d696e65642028692e652e20485454502c0a202020202f2f2f20495046532c206574632e290a202020202f2f2f0a2020202061636365737328616c6c2920737472756374205552493a2046696c65207b0a20202020202020202f2f2f20546865206261736520555249207072656669782c20696620616e792e204e6f74206e656564656420666f7220616c6c20555249732c206275742068656c7066756c0a20202020202020202f2f2f20666f7220736f6d652075736520636173657320466f72206578616d706c652c207570646174696e6720612077686f6c65204e465420636f6c6c656374696f6e27730a20202020202020202f2f2f20696d61676520686f737420656173696c790a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c657420626173655552493a20537472696e673f0a20202020202020202f2f2f205468652055524920737472696e672076616c75650a20202020202020202f2f2f204e4f54453a207468697320697320736574206f6e20696e6974206173206120636f6e636174656e6174696f6e206f6620746865206261736555524920616e64207468650a20202020202020202f2f2f2076616c7565206966206261736555524920213d206e696c0a20202020202020202f2f2f0a20202020202020206163636573732873656c6629206c65742076616c75653a20537472696e670a0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e67207b0a20202020202020202020202072657475726e2073656c662e76616c75650a20202020202020207d0a0a2020202020202020696e697428626173655552493a20537472696e673f2c2076616c75653a20537472696e6729207b0a20202020202020202020202073656c662e62617365555249203d20626173655552490a20202020202020202020202073656c662e76616c7565203d206261736555524920213d206e696c203f2062617365555249212e636f6e6361742876616c756529203a2076616c75650a20202020202020207d0a202020207d0a0a2020202061636365737328616c6c2920737472756374204d65646961207b0a0a20202020202020202f2f2f2046696c6520666f7220746865206d656469610a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c65742066696c653a207b46696c657d0a0a20202020202020202f2f2f206d656469612d7479706520636f6d6573206f6e2074686520666f726d206f6620747970652f73756274797065206173206465736372696265642068657265200a20202020202020202f2f2f2068747470733a2f2f646576656c6f7065722e6d6f7a696c6c612e6f72672f656e2d55532f646f63732f5765622f485454502f4261736963735f6f665f485454502f4d494d455f74797065730a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206d65646961547970653a20537472696e670a0a20202020202020207669657720696e69742866696c653a207b46696c657d2c206d65646961547970653a20537472696e6729207b0a2020202020202020202073656c662e66696c653d66696c650a2020202020202020202073656c662e6d65646961547970653d6d65646961547970650a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720666f72206d756c7469706c65206d656469612076696577730a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204d6564696173207b0a0a20202020202020202f2f2f20416e206172626974726172792d73697a6564206c69737420666f7220616e79206e756d626572206f66204d65646961206974656d730a202020202020202061636365737328616c6c29206c6574206974656d733a205b4d656469615d0a0a20202020202020207669657720696e6974285f206974656d733a205b4d656469615d29207b0a20202020202020202020202073656c662e6974656d73203d206974656d730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204d656469617320696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204d6564696173207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744d6564696173285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204d65646961733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4d65646961733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204d6564696173207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f20726570726573656e742061206c6963656e7365206163636f7264696e6720746f2068747470733a2f2f737064782e6f72672f6c6963656e7365732f0a202020202f2f2f205468697320766965772063616e20626520757365642069662074686520636f6e74656e74206f6620616e204e4654206973206c6963656e7365642e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204c6963656e7365207b0a202020202020202061636365737328616c6c29206c657420737064784964656e7469666965723a20537472696e670a0a20202020202020207669657720696e6974285f206964656e7469666965723a20537472696e6729207b0a20202020202020202020202073656c662e737064784964656e746966696572203d206964656e7469666965720a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204c6963656e736520696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c204c6963656e7365207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744c6963656e7365285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204c6963656e73653f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4c6963656e73653e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204c6963656e7365207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f206578706f736520612055524c20746f2074686973206974656d206f6e20616e2065787465726e616c20736974652e0a202020202f2f2f20546869732063616e2062652075736564206279206170706c69636174696f6e73206c696b65202e66696e6420616e6420426c6f63746f20746f20646972656374207573657273200a202020202f2f2f20746f20746865206f726967696e616c206c696e6b20666f7220616e204e4654206f7220612070726f6a656374207061676520746861742064657363726962657320746865204e465420636f6c6c656374696f6e2e0a202020202f2f2f2065672068747470733a2f2f7777772e6d792d6e66742d70726f6a6563742e636f6d2f6f766572766965772d6f662d6e66742d636f6c6c656374696f6e0a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045787465726e616c55524c207b0a202020202020202061636365737328616c6c29206c65742075726c3a20537472696e670a0a20202020202020207669657720696e6974285f2075726c3a20537472696e6729207b0a20202020202020202020202073656c662e75726c3d75726c0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f206765742045787465726e616c55524c20696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c2045787465726e616c55524c207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e2067657445787465726e616c55524c285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2045787465726e616c55524c3f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c45787465726e616c55524c3e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2045787465726e616c55524c207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2056696577207468617420646566696e65732074686520636f6d706f7361626c6520726f79616c7479207374616e646172642074686174206769766573206d61726b6574706c616365732061200a202020202f2f2f20756e696669656420696e7465726661636520746f20737570706f7274204e465420726f79616c746965732e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420526f79616c7479207b0a0a20202020202020202f2f2f2047656e657269632046756e6769626c65546f6b656e20526563656976657220666f72207468652062656e6566696369617279206f662074686520726f79616c74790a20202020202020202f2f2f2043616e206765742074686520636f6e63726574652074797065206f662074686520726563656976657220776974682072656365697665722e6765745479706528290a20202020202020202f2f2f205265636f6d6d656e646174696f6e202d2055736572732073686f756c64206372656174652061206e6577206c696e6b20666f72206120466c6f77546f6b656e200a20202020202020202f2f2f20726563656976657220666f722074686973207573696e672060676574526f79616c747952656365697665725075626c6963506174682829602c20616e64206e6f74200a20202020202020202f2f2f20757365207468652064656661756c7420466c6f77546f6b656e2072656365697665722e20546869732077696c6c20616c6c6f7720757365727320746f20757064617465200a20202020202020202f2f2f20746865206361706162696c69747920696e207468652066757475726520746f207573652061206d6f72652067656e65726963206361706162696c6974790a202020202020202061636365737328616c6c29206c65742072656365697665723a204361706162696c6974793c267b46756e6769626c65546f6b656e2e52656365697665727d3e0a0a20202020202020202f2f2f204d756c7469706c696572207573656420746f2063616c63756c6174652074686520616d6f756e74206f662073616c652076616c7565207472616e7366657272656420746f200a20202020202020202f2f2f20726f79616c74792072656365697665722e204e6f7465202d2049742073686f756c64206265206265747765656e20302e3020616e6420312e30200a20202020202020202f2f2f204578202d204966207468652073616c652076616c7565206973207820616e64206d756c7469706c69657220697320302e3536207468656e2074686520726f79616c7479200a20202020202020202f2f2f2076616c756520776f756c6420626520302e3536202a20782e0a20202020202020202f2f2f2047656e6572616c6c792070657263656e746167652067657420726570726573656e74656420696e207465726d73206f6620626173697320706f696e74730a20202020202020202f2f2f20696e20736f6c696469747920626173656420736d61727420636f6e747261637473207768696c6520636164656e6365206f6666657273206055466978363460200a20202020202020202f2f2f207468617420616c726561647920737570706f7274732074686520626173697320706f696e7473207573652063617365206265636175736520697473200a20202020202020202f2f2f206f7065726174696f6e732061726520656e746972656c792064657465726d696e697374696320696e7465676572206f7065726174696f6e7320616e6420737570706f7274200a20202020202020202f2f2f20757020746f203820706f696e7473206f6620707265636973696f6e2e0a202020202020202061636365737328616c6c29206c6574206375743a205546697836340a0a20202020202020202f2f2f204f7074696f6e616c206465736372697074696f6e3a20546869732063616e20626520746865206361757365206f6620706179696e672074686520726f79616c74792c0a20202020202020202f2f2f207468652072656c6174696f6e73686970206265747765656e20746865206077616c6c65746020616e6420746865204e46542c206f7220616e797468696e6720656c73650a20202020202020202f2f2f207468617420746865206f776e6572206d696768742077616e7420746f20737065636966792e0a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e670a0a20202020202020207669657720696e69742872656365697665723a204361706162696c6974793c267b46756e6769626c65546f6b656e2e52656365697665727d3e2c206375743a205546697836342c206465736372697074696f6e3a20537472696e6729207b0a202020202020202020202020707265207b0a20202020202020202020202020202020637574203e3d20302e3020262620637574203c3d20312e30203a0a2020202020202020202020202020202020202020224d6574616461746156696577732e526f79616c74792e696e69743a2043616e6e6f7420696e697469616c697a652074686520526f79616c7479204d6574616461746120566965772120220a20202020202020202020202020202020202020202e636f6e63617428225468652070726f766964656420726f79616c7479206375742076616c7565206f66205c286375742920697320696e76616c69642e2022290a20202020202020202020202020202020202020202e636f6e636174282249742073686f756c642062652077697468696e207468652076616c69642072616e6765206265747765656e203020616e6420312e20692e65205b302c315d22290a2020202020202020202020207d0a20202020202020202020202073656c662e7265636569766572203d2072656365697665720a20202020202020202020202073656c662e637574203d206375740a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720666f72206d756c7469706c6520526f79616c74792076696577732e0a202020202f2f2f204d61726b6574706c616365732063616e20717565727920746869732060526f79616c7469657360207374727563742066726f6d204e465473200a202020202f2f2f20616e642061726520657870656374656420746f2070617920726f79616c74696573206261736564206f6e2074686573652073706563696669636174696f6e732e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420526f79616c74696573207b0a0a20202020202020202f2f2f204172726179207468617420747261636b732074686520696e646976696475616c20726f79616c746965730a20202020202020206163636573732873656c6629206c657420637574496e666f733a205b526f79616c74795d0a0a202020202020202061636365737328616c6c29207669657720696e6974285f20637574496e666f733a205b526f79616c74795d29207b0a2020202020202020202020202f2f2056616c696461746520746861742073756d206f6620616c6c20637574206d756c7469706c696572732073686f756c64206e6f742062652067726561746572207468616e20312e300a20202020202020202020202076617220746f74616c437574203d20302e300a202020202020202020202020666f7220726f79616c747920696e20637574496e666f73207b0a20202020202020202020202020202020746f74616c437574203d20746f74616c437574202b20726f79616c74792e6375740a2020202020202020202020207d0a202020202020202020202020617373657274280a20202020202020202020202020202020746f74616c437574203c3d20312e302c0a202020202020202020202020202020206d6573736167653a0a2020202020202020202020202020202020202020224d6574616461746156696577732e526f79616c746965732e696e69743a2043616e6e6f7420696e697469616c697a6520526f79616c74696573204d6574616461746120566965772120220a20202020202020202020202020202020202020202e636f6e6361742822205468652073756d206f6620637574496e666f73206d756c7469706c69657273206973205c28746f74616c43757429206275742069742073686f756c64206e6f742062652067726561746572207468616e20312e3022290a202020202020202020202020290a2020202020202020202020202f2f2041737369676e2074686520637574496e666f730a20202020202020202020202073656c662e637574496e666f73203d20637574496e666f730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e2074686520637574496e666f73206c6973740a20202020202020202f2f2f0a20202020202020202f2f2f204072657475726e20416e20617272617920636f6e7461696e696e6720616c6c2074686520726f79616c7469657320737472756374730a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e20676574526f79616c7469657328293a205b526f79616c74795d207b0a20202020202020202020202072657475726e2073656c662e637574496e666f730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420526f79616c7469657320696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c20526f79616c74696573207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574526f79616c74696573285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a20526f79616c746965733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c526f79616c746965733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20526f79616c74696573207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2047657420746865207061746820746861742073686f756c64206265207573656420666f7220726563656976696e6720726f79616c746965730a202020202f2f2f20546869732069732061207061746820746861742077696c6c206576656e7475616c6c79206265207573656420666f7220612067656e6572696320737769746368626f6172642072656365697665722c0a202020202f2f2f2068656e636520746865206e616d65206275742077696c6c206f6e6c79206265207573656420666f7220726f79616c7469657320666f72206e6f772e0a202020202f2f2f0a202020202f2f2f204072657475726e20546865205075626c69635061746820666f72207468652067656e657269632046542072656365697665720a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e20676574526f79616c747952656365697665725075626c69635061746828293a205075626c696350617468207b0a202020202020202072657475726e202f7075626c69632f47656e65726963465452656365697665720a202020207d0a0a202020202f2f2f205669657720746f20726570726573656e7420612073696e676c65206669656c64206f66206d65746164617461206f6e20616e204e46542e0a202020202f2f2f2054686973206973207573656420746f2067657420747261697473206f6620696e646976696475616c206b65792f76616c756520706169727320616c6f6e67207769746820736f6d650a202020202f2f2f20636f6e7465787475616c697a656420646174612061626f7574207468652074726169740a202020202f2f2f0a2020202061636365737328616c6c2920737472756374205472616974207b0a20202020202020202f2f20546865206e616d65206f66207468652074726169742e204c696b65204261636b67726f756e642c20457965732c20486169722c206574632e0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f2054686520756e6465726c79696e672076616c7565206f66207468652074726169742c207468652072657374206f6620746865206669656c6473206f6620612074726169742070726f7669646520636f6e7465787420746f207468652076616c75652e0a202020202020202061636365737328616c6c29206c65742076616c75653a20416e795374727563740a0a20202020202020202f2f20646973706c617954797065206973207573656420746f2073686f7720736f6d6520636f6e746578742061626f757420776861742074686973206e616d6520616e642076616c756520726570726573656e740a20202020202020202f2f20666f7220696e7374616e63652c20796f7520636f756c64207365742076616c756520746f206120756e69782074696d657374616d702c20616e64207370656369667920646973706c6179547970652061732022446174652220746f2074656c6c0a20202020202020202f2f20706c6174666f726d7320746f20636f6e73756d6520746869732074726169742061732061206461746520616e64206e6f742061206e756d6265720a202020202020202061636365737328616c6c29206c657420646973706c6179547970653a20537472696e673f0a0a20202020202020202f2f205261726974792063616e20616c736f2062652075736564206469726563746c79206f6e20616e206174747269627574652e0a20202020202020202f2f0a20202020202020202f2f2054686973206973206f7074696f6e616c2062656361757365206e6f7420616c6c2061747472696275746573206e65656420746f20636f6e7472696275746520746f20746865204e46542773207261726974792e0a202020202020202061636365737328616c6c29206c6574207261726974793a205261726974793f0a0a20202020202020207669657720696e6974286e616d653a20537472696e672c2076616c75653a20416e795374727563742c20646973706c6179547970653a20537472696e673f2c207261726974793a205261726974793f29207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e76616c7565203d2076616c75650a20202020202020202020202073656c662e646973706c617954797065203d20646973706c6179547970650a20202020202020202020202073656c662e726172697479203d207261726974790a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720746f2072657475726e20616c6c2074686520747261697473206f6e20616e204e46542e0a202020202f2f2f2054686973206973207573656420746f2072657475726e2074726169747320617320696e646976696475616c206b65792f76616c756520706169727320616c6f6e6720776974680a202020202f2f2f20736f6d6520636f6e7465787475616c697a656420646174612061626f757420656163682074726169742e0a2020202061636365737328616c6c292073747275637420547261697473207b0a202020202020202061636365737328616c6c29206c6574207472616974733a205b54726169745d0a0a20202020202020207669657720696e6974285f207472616974733a205b54726169745d29207b0a20202020202020202020202073656c662e747261697473203d207472616974730a20202020202020207d0a2020202020202020202020200a20202020202020202f2f2f204164647320612073696e676c6520547261697420746f207468652054726169747320766965770a20202020202020202f2f2f200a20202020202020202f2f2f2040706172616d2054726169743a205468652074726169742073747275637420746f2062652061646465640a20202020202020202f2f2f0a202020202020202061636365737328616c6c292066756e206164645472616974285f20743a20547261697429207b0a20202020202020202020202073656c662e7472616974732e617070656e642874290a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420547261697473207669657720696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c20547261697473207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574547261697473285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a205472616974733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c5472616974733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20547261697473207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2048656c7065722066756e6374696f6e20746f20656173696c7920636f6e7665727420612064696374696f6e61727920746f207472616974732e20466f72204e4654200a202020202f2f2f20636f6c6c656374696f6e73207468617420646f206e6f74206e65656420656974686572206f6620746865206f7074696f6e616c2076616c756573206f6620612054726169742c200a202020202f2f2f2074686973206d6574686f642073686f756c64207375666669636520746f2067697665207468656d20616e206172726179206f662076616c6964207472616974732e0a202020202f2f2f0a202020202f2f2f2040706172616d20646963743a205468652064696374696f6e61727920746f20626520636f6e76657274656420746f205472616974730a202020202f2f2f2040706172616d206578636c756465644e616d65733a20416e206f7074696f6e616c20537472696e672061727261792073706563696679696e6720746865206064696374600a202020202f2f2f2020202020202020206b657973207468617420617265206e6f742077616e74656420746f206265636f6d652060547261697473600a202020202f2f2f204072657475726e205468652067656e6572617465642054726169747320766965770a202020202f2f2f0a2020202061636365737328616c6c292066756e2064696374546f54726169747328646963743a207b537472696e673a20416e795374727563747d2c206578636c756465644e616d65733a205b537472696e675d3f293a20547261697473207b0a20202020202020202f2f20436f6c6c656374696f6e206f776e657273206d69676874206e6f742077616e7420616c6c20746865206669656c647320696e207468656972206d6574616461746120696e636c756465642e0a20202020202020202f2f2054686579206d696768742077616e7420746f2068616e646c6520736f6d65207370656369616c6c792c206f722074686579206d69676874206a757374206e6f742077616e74207468656d20696e636c7564656420617420616c6c2e0a20202020202020206966206578636c756465644e616d657320213d206e696c207b0a202020202020202020202020666f72206b20696e206578636c756465644e616d657321207b0a20202020202020202020202020202020646963742e72656d6f7665286b65793a206b290a2020202020202020202020207d0a20202020202020207d0a0a20202020202020206c6574207472616974733a205b54726169745d203d205b5d0a2020202020202020666f72206b20696e20646963742e6b657973207b0a2020202020202020202020206c6574207472616974203d205472616974286e616d653a206b2c2076616c75653a20646963745b6b5d212c20646973706c6179547970653a206e696c2c207261726974793a206e696c290a2020202020202020202020207472616974732e617070656e64287472616974290a20202020202020207d0a0a202020202020202072657475726e2054726169747328747261697473290a202020207d0a0a202020202f2f2f204f7074696f6e616c207669657720666f7220636f6c6c656374696f6e732074686174206973737565206d756c7469706c65206f626a656374730a202020202f2f2f2077697468207468652073616d65206f722073696d696c6172206d657461646174612c20666f72206578616d706c6520616e2058206f6620313030207365742e20546869730a202020202f2f2f20696e666f726d6174696f6e2069732075736566756c20666f722077616c6c65747320616e64206d61726b6574706c616365732e0a202020202f2f2f20416e204e4654206d696768742062652070617274206f66206d756c7469706c652065646974696f6e732c20776869636820697320776879207468652065646974696f6e0a202020202f2f2f20696e666f726d6174696f6e2069732072657475726e656420617320616e206172626974726172792073697a65642061727261790a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045646974696f6e207b0a0a20202020202020202f2f2f20546865206e616d65206f66207468652065646974696f6e0a20202020202020202f2f2f20466f72206578616d706c652c207468697320636f756c64206265205365742c20506c61792c205365726965732c0a20202020202020202f2f2f206f7220616e79206f746865722077617920612070726f6a65637420636f756c6420636c617373696679206974732065646974696f6e730a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e673f0a0a20202020202020202f2f2f205468652065646974696f6e206e756d626572206f6620746865206f626a6563742e0a20202020202020202f2f2f20466f7220616e20223234206f662031303020282332342f3130302922206974656d2c20746865206e756d6265722069732032342e0a202020202020202061636365737328616c6c29206c6574206e756d6265723a2055496e7436340a0a20202020202020202f2f2f20546865206d61782065646974696f6e206e756d626572206f6620746869732074797065206f66206f626a656374732e0a20202020202020202f2f2f2054686973206669656c642073686f756c64206f6e6c792062652070726f766964656420666f72206c696d697465642d65646974696f6e6564206f626a656374732e0a20202020202020202f2f2f20466f7220616e20223234206f662031303020282332342f3130302922206974656d2c206d6178206973203130302e0a20202020202020202f2f2f20466f7220616e206974656d207769746820756e6c696d697465642065646974696f6e2c206d61782073686f756c642062652073657420746f206e696c2e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206d61783a2055496e7436343f0a0a20202020202020207669657720696e6974286e616d653a20537472696e673f2c206e756d6265723a2055496e7436342c206d61783a2055496e7436343f29207b0a2020202020202020202020206966206d617820213d206e696c207b0a20202020202020202020202020202020617373657274280a20202020202020202020202020202020202020206e756d626572203c3d206d6178212c0a20202020202020202020202020202020202020206d6573736167653a0a202020202020202020202020202020202020202020202020224d6574616461746156696577732e45646974696f6e2e696e69743a2043616e6e6f7420696e7469616c697a65207468652045646974696f6e204d6574616461746120566965772120220a2020202020202020202020202020202020202020202020202e636f6e63617428225468652070726f76696465642065646974696f6e206e756d626572206f66205c286e756d626572292063616e6e6f742062652067726561746572207468616e20746865206d61782065646974696f6e206e756d626572206f66205c286d617821292e22290a20202020202020202020202020202020290a2020202020202020202020207d0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e6e756d626572203d206e756d6265720a20202020202020202020202073656c662e6d6178203d206d61780a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720666f72206d756c7469706c652045646974696f6e2076696577730a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045646974696f6e73207b0a0a20202020202020202f2f2f20416e206172626974726172792d73697a6564206c69737420666f7220616e79206e756d626572206f662065646974696f6e730a20202020202020202f2f2f207468617420746865204e4654206d6967687420626520612070617274206f660a202020202020202061636365737328616c6c29206c657420696e666f4c6973743a205b45646974696f6e5d0a0a20202020202020207669657720696e6974285f20696e666f4c6973743a205b45646974696f6e5d29207b0a20202020202020202020202073656c662e696e666f4c697374203d20696e666f4c6973740a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f206765742045646974696f6e7320696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c2045646974696f6e73207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e2067657445646974696f6e73285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2045646974696f6e733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c45646974696f6e733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2045646974696f6e73207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720726570726573656e74696e6720612070726f6a6563742d646566696e65642073657269616c206e756d62657220666f722061207370656369666963204e46540a202020202f2f2f2050726f6a65637473206861766520646966666572656e7420646566696e6974696f6e7320666f72207768617420612073657269616c206e756d6265722073686f756c642062650a202020202f2f2f20536f6d65206d61792075736520746865204e46547320726567756c617220494420616e6420736f6d65206d617920757365206120646966666572656e740a202020202f2f2f20636c617373696669636174696f6e2073797374656d2e205468652073657269616c206e756d62657220697320657870656374656420746f20626520756e6971756520616d6f6e670a202020202f2f2f206f74686572204e4654732077697468696e20746861742070726f6a6563740a202020202f2f2f0a2020202061636365737328616c6c29207374727563742053657269616c207b0a202020202020202061636365737328616c6c29206c6574206e756d6265723a2055496e7436340a0a20202020202020207669657720696e6974285f206e756d6265723a2055496e74363429207b0a20202020202020202020202073656c662e6e756d626572203d206e756d6265720a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f206765742053657269616c20696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c2053657269616c207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e2067657453657269616c285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2053657269616c3f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c53657269616c3e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2053657269616c207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f206578706f73652072617269747920696e666f726d6174696f6e20666f7220612073696e676c65207261726974790a202020202f2f2f204e6f74652074686174206120726172697479206e6565647320746f2068617665206569746865722073636f7265206f72206465736372697074696f6e206275742069742063616e200a202020202f2f2f206861766520626f74680a202020202f2f2f0a2020202061636365737328616c6c292073747275637420526172697479207b0a20202020202020202f2f2f205468652073636f7265206f6620746865207261726974792061732061206e756d6265720a202020202020202061636365737328616c6c29206c65742073636f72653a205546697836343f0a0a20202020202020202f2f2f20546865206d6178696d756d2076616c7565206f662073636f72650a202020202020202061636365737328616c6c29206c6574206d61783a205546697836343f0a0a20202020202020202f2f2f20546865206465736372697074696f6e206f662074686520726172697479206173206120737472696e672e0a20202020202020202f2f2f0a20202020202020202f2f2f205468697320636f756c64206265204c6567656e646172792c20457069632c20526172652c20556e636f6d6d6f6e2c20436f6d6d6f6e206f7220616e79206f7468657220737472696e672076616c75650a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e673f0a0a20202020202020207669657720696e69742873636f72653a205546697836343f2c206d61783a205546697836343f2c206465736372697074696f6e3a20537472696e673f29207b0a20202020202020202020202069662073636f7265203d3d206e696c202626206465736372697074696f6e203d3d206e696c207b0a2020202020202020202020202020202070616e696328224d6574616461746156696577732e5261726974792e696e69743a2043616e6e6f7420696e697469616c697a652074686520526172697479204d6574616461746120566965772120220a202020202020202020202020202020202020202020202e636f6e63617428225468652070726f76696465642073636f726520616e64206465736372697074696f6e2061726520626f746820606e696c602e204120526172697479206e6565647320746f207365742073636f72652c206465736372697074696f6e2c206f7220626f74682229290a2020202020202020202020207d0a0a20202020202020202020202073656c662e73636f7265203d2073636f72650a20202020202020202020202073656c662e6d6178203d206d61780a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420526172697479207669657720696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c20526172697479207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574526172697479285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a205261726974793f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c5261726974793e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20526172697479207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f204e46545669657720777261707320616c6c20436f726520766965777320616c6f6e67206069646020616e6420607575696460206669656c64732c20616e642069732075736564200a202020202f2f2f20746f2067697665206120636f6d706c6574652070696374757265206f6620616e204e46542e204d6f7374204e4654732073686f756c6420696d706c656d656e742074686973200a202020202f2f2f20766965772e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204e465456696577207b0a202020202020202061636365737328616c6c29206c65742069643a2055496e7436340a202020202020202061636365737328616c6c29206c657420757569643a2055496e7436340a202020202020202061636365737328616c6c29206c657420646973706c61793a204d6574616461746156696577732e446973706c61793f0a202020202020202061636365737328616c6c29206c65742065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c3f0a202020202020202061636365737328616c6c29206c657420636f6c6c656374696f6e446174613a204e4654436f6c6c656374696f6e446174613f0a202020202020202061636365737328616c6c29206c657420636f6c6c656374696f6e446973706c61793a204e4654436f6c6c656374696f6e446973706c61793f0a202020202020202061636365737328616c6c29206c657420726f79616c746965733a20526f79616c746965733f0a202020202020202061636365737328616c6c29206c6574207472616974733a205472616974733f0a0a20202020202020207669657720696e6974280a2020202020202020202020206964203a2055496e7436342c0a20202020202020202020202075756964203a2055496e7436342c0a202020202020202020202020646973706c6179203a204d6574616461746156696577732e446973706c61793f2c0a20202020202020202020202065787465726e616c55524c203a204d6574616461746156696577732e45787465726e616c55524c3f2c0a202020202020202020202020636f6c6c656374696f6e44617461203a204e4654436f6c6c656374696f6e446174613f2c0a202020202020202020202020636f6c6c656374696f6e446973706c6179203a204e4654436f6c6c656374696f6e446973706c61793f2c0a202020202020202020202020726f79616c74696573203a20526f79616c746965733f2c0a2020202020202020202020207472616974733a205472616974733f0a202020202020202029207b0a20202020202020202020202073656c662e6964203d2069640a20202020202020202020202073656c662e75756964203d20757569640a20202020202020202020202073656c662e646973706c6179203d20646973706c61790a20202020202020202020202073656c662e65787465726e616c55524c203d2065787465726e616c55524c0a20202020202020202020202073656c662e636f6c6c656374696f6e44617461203d20636f6c6c656374696f6e446174610a20202020202020202020202073656c662e636f6c6c656374696f6e446973706c6179203d20636f6c6c656374696f6e446973706c61790a20202020202020202020202073656c662e726f79616c74696573203d20726f79616c746965730a20202020202020202020202073656c662e747261697473203d207472616974730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420616e204e46542076696577200a202020202f2f2f0a202020202f2f2f2040706172616d2069643a20546865204e46542069640a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041204e465456696577207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744e4654566965772869643a2055496e7436342c20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204e465456696577207b0a20202020202020206c6574206e667456696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4e4654566965773e2829290a20202020202020206966206e66745669657720213d206e696c207b0a20202020202020202020202072657475726e206e6674566965772120617321204e4654566965770a20202020202020207d0a0a202020202020202072657475726e204e465456696577280a2020202020202020202020206964203a2069642c0a202020202020202020202020757569643a20766965775265736f6c7665722e757569642c0a202020202020202020202020646973706c61793a204d6574616461746156696577732e676574446973706c617928766965775265736f6c766572292c0a20202020202020202020202065787465726e616c55524c203a204d6574616461746156696577732e67657445787465726e616c55524c28766965775265736f6c766572292c0a202020202020202020202020636f6c6c656374696f6e44617461203a2073656c662e6765744e4654436f6c6c656374696f6e4461746128766965775265736f6c766572292c0a202020202020202020202020636f6c6c656374696f6e446973706c6179203a2073656c662e6765744e4654436f6c6c656374696f6e446973706c617928766965775265736f6c766572292c0a202020202020202020202020726f79616c74696573203a2073656c662e676574526f79616c7469657328766965775265736f6c766572292c0a202020202020202020202020747261697473203a2073656c662e67657454726169747328766965775265736f6c766572290a2020202020202020290a202020207d0a0a202020202f2f2f205669657720746f206578706f73652074686520696e666f726d6174696f6e206e65656465642073746f726520616e6420726574726965766520616e204e46542e0a202020202f2f2f20546869732063616e2062652075736564206279206170706c69636174696f6e7320746f2073657475702061204e465420636f6c6c656374696f6e20776974682070726f706572200a202020202f2f2f2073746f7261676520616e64207075626c6963206361706162696c69746965732e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204e4654436f6c6c656374696f6e44617461207b0a20202020202020202f2f2f205061746820696e2073746f726167652077686572652074686973204e4654206973207265636f6d6d656e64656420746f2062652073746f7265642e0a202020202020202061636365737328616c6c29206c65742073746f72616765506174683a2053746f72616765506174680a0a20202020202020202f2f2f205075626c69632070617468207768696368206d757374206265206c696e6b656420746f206578706f7365207075626c6963206361706162696c6974696573206f662074686973204e46540a20202020202020202f2f2f20696e636c7564696e67207374616e64617264204e465420696e746572666163657320616e64206d65746164617461766965777320696e74657266616365730a202020202020202061636365737328616c6c29206c6574207075626c6963506174683a205075626c6963506174680a0a20202020202020202f2f2f2054686520636f6e63726574652074797065206f662074686520636f6c6c656374696f6e2074686174206973206578706f73656420746f20746865207075626c69630a20202020202020202f2f2f206e6f77207468617420656e7469746c656d656e74732065786973742c206974206e6f206c6f6e676572206e6565647320746f206265207265737472696374656420746f206120737065636966696320696e746572666163650a202020202020202061636365737328616c6c29206c6574207075626c6963436f6c6c656374696f6e3a20547970650a0a20202020202020202f2f2f205479706520746861742073686f756c64206265206c696e6b6564206174207468652061666f72656d656e74696f6e6564207075626c696320706174680a202020202020202061636365737328616c6c29206c6574207075626c69634c696e6b6564547970653a20547970650a0a20202020202020202f2f2f2046756e6374696f6e207468617420616c6c6f7773206372656174696f6e206f6620616e20656d707479204e465420636f6c6c656374696f6e207468617420697320696e74656e64656420746f2073746f72650a20202020202020202f2f2f2074686973204e46542e0a202020202020202061636365737328616c6c29206c657420637265617465456d707479436f6c6c656374696f6e3a2066756e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d0a0a20202020202020207669657720696e6974280a20202020202020202020202073746f72616765506174683a2053746f72616765506174682c0a2020202020202020202020207075626c6963506174683a205075626c6963506174682c0a2020202020202020202020207075626c6963436f6c6c656374696f6e3a20547970652c0a2020202020202020202020207075626c69634c696e6b6564547970653a20547970652c0a202020202020202020202020637265617465456d707479436f6c6c656374696f6e46756e6374696f6e3a2066756e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d0a202020202020202029207b0a202020202020202020202020707265207b0a202020202020202020202020202020207075626c69634c696e6b6564547970652e697353756274797065286f663a20547970653c267b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d3e2829293a0a2020202020202020202020202020202020202020224d6574616461746156696577732e4e4654436f6c6c656374696f6e446174612e696e69743a2043616e6e6f7420696e697469616c697a6520746865204e4654436f6c6c656374696f6e44617461204d6574616461746120566965772120220a20202020202020202020202020202020202020202e636f6e6361742822546865205075626c6963206c696e6b65642074797065203c5c287075626c69634c696e6b6564547970652e6964656e746966696572293e20697320696e636f72726563742e204974206d75737420626520612073756274797065206f6620746865204e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e20696e746572666163652e22290a2020202020202020202020207d0a20202020202020202020202073656c662e73746f72616765506174683d73746f72616765506174680a20202020202020202020202073656c662e7075626c6963506174683d7075626c6963506174680a20202020202020202020202073656c662e7075626c6963436f6c6c656374696f6e3d7075626c6963436f6c6c656374696f6e0a20202020202020202020202073656c662e7075626c69634c696e6b6564547970653d7075626c69634c696e6b6564547970650a20202020202020202020202073656c662e637265617465456d707479436f6c6c656374696f6e3d637265617465456d707479436f6c6c656374696f6e46756e6374696f6e0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204e4654436f6c6c656374696f6e4461746120696e20612077617920746861742077696c6c2072657475726e20616e207479706564204f7074696f6e616c0a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204e4654436f6c6c656374696f6e44617461207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744e4654436f6c6c656374696f6e44617461285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204e4654436f6c6c656374696f6e446174613f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4e4654436f6c6c656374696f6e446174613e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204e4654436f6c6c656374696f6e44617461207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f206578706f73652074686520696e666f726d6174696f6e206e656564656420746f2073686f77636173652074686973204e465427730a202020202f2f2f20636f6c6c656374696f6e2e20546869732063616e2062652075736564206279206170706c69636174696f6e7320746f206769766520616e206f7665727669657720616e64200a202020202f2f2f206772617068696373206f6620746865204e465420636f6c6c656374696f6e2074686973204e46542062656c6f6e677320746f2e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204e4654436f6c6c656374696f6e446973706c6179207b0a20202020202020202f2f204e616d6520746861742073686f756c642062652075736564207768656e20646973706c6179696e672074686973204e465420636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f204465736372697074696f6e20746861742073686f756c64206265207573656420746f206769766520616e206f76657276696577206f66207468697320636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e670a0a20202020202020202f2f2045787465726e616c206c696e6b20746f20612055524c20746f2076696577206d6f726520696e666f726d6174696f6e2061626f7574207468697320636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c65742065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c0a0a20202020202020202f2f205371756172652d73697a656420696d61676520746f20726570726573656e74207468697320636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c657420737175617265496d6167653a204d6574616461746156696577732e4d656469610a0a20202020202020202f2f2042616e6e65722d73697a656420696d61676520666f72207468697320636f6c6c656374696f6e2c207265636f6d6d656e64656420746f206861766520612073697a65206e6561722031343030783335302e0a202020202020202061636365737328616c6c29206c65742062616e6e6572496d6167653a204d6574616461746156696577732e4d656469610a0a20202020202020202f2f20536f6369616c206c696e6b7320746f207265616368207468697320636f6c6c656374696f6e277320736f6369616c20686f6d6570616765732e0a20202020202020202f2f20506f737369626c65206b657973206d61792062652022696e7374616772616d222c202274776974746572222c2022646973636f7264222c206574632e0a202020202020202061636365737328616c6c29206c657420736f6369616c733a207b537472696e673a204d6574616461746156696577732e45787465726e616c55524c7d0a0a20202020202020207669657720696e6974280a2020202020202020202020206e616d653a20537472696e672c0a2020202020202020202020206465736372697074696f6e3a20537472696e672c0a20202020202020202020202065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c2c0a202020202020202020202020737175617265496d6167653a204d6574616461746156696577732e4d656469612c0a20202020202020202020202062616e6e6572496d6167653a204d6574616461746156696577732e4d656469612c0a202020202020202020202020736f6369616c733a207b537472696e673a204d6574616461746156696577732e45787465726e616c55524c7d0a202020202020202029207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020202020202073656c662e65787465726e616c55524c203d2065787465726e616c55524c0a20202020202020202020202073656c662e737175617265496d616765203d20737175617265496d6167650a20202020202020202020202073656c662e62616e6e6572496d616765203d2062616e6e6572496d6167650a20202020202020202020202073656c662e736f6369616c73203d20736f6369616c730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204e4654436f6c6c656374696f6e446973706c617920696e20612077617920746861742077696c6c2072657475726e2061207479706564200a202020202f2f2f204f7074696f6e616c0a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204e4654436f6c6c656374696f6e207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744e4654436f6c6c656374696f6e446973706c6179285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204e4654436f6c6c656374696f6e446973706c61793f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4e4654436f6c6c656374696f6e446973706c61793e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204e4654436f6c6c656374696f6e446973706c6179207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a202020202f2f2f20546869732076696577206d6179206265207573656420627920436164656e63652d6e61746976652070726f6a6563747320746f20646566696e652074686569720a202020202f2f2f20636f6e74726163742d20616e6420746f6b656e2d6c6576656c206d65746164617461206163636f7264696e6720746f2045564d2d636f6d70617469626c6520666f726d6174732e0a202020202f2f2f205365766572616c20455243207374616e64617264732028652e672e2045524332302c204552433732312c206574632e29206578706f7365206e616d6520616e642073796d626f6c0a202020202f2f2f2076616c75657320746f20646566696e65206173736574732061732077656c6c20617320636f6e74726163742d202620746f6b656e2d6c6576656c206d6574616461746120766965770a202020202f2f2f2060746f6b656e5552492875696e74323536296020616e642060636f6e7472616374555249282960206d6574686f64732e2054686973207669657720656e61626c65730a202020202f2f2f20436164656e63652070726f6a6563747320746f20646566696e6520696e207468656972206f776e20636f6e74726163747320686f77207468657920776f756c64206c696b650a202020202f2f2f207468656972206d6574616461746120746f20626520646566696e6564207768656e206272696467656420746f2045564d2e0a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045564d427269646765644d65746164617461207b0a0a20202020202020202f2f2f20546865206e616d65206f66207468652061737365740a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f2f205468652073796d626f6c206f66207468652061737365740a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c65742073796d626f6c3a20537472696e670a0a20202020202020202f2f2f2054686520555249206f6620746865206173736574202d20746869732063616e2065697468657220626520636f6e74726163742d6c6576656c206f720a20202020202020202f2f2f20746f6b656e2d6c6576656c2055524920646570656e64696e67206f6e20776865726520746865206d65746164617461206973207265736f6c7665642e2049740a20202020202020202f2f2f206973207265636f6d6d656e64656420746f207265666572656e63652045564d206d65746164617461207374616e646172647320666f7220686f7720746f20626573740a20202020202020202f2f2f207072657061726520796f75722076696577277320666f726d61747465642076616c75652e0a20202020202020202f2f2f0a20202020202020202f2f2f20466f72206578616d706c652c207768696c6520796f75206d61792063686f6f736520746f2074616b6520616476616e74616765206f66206f6e636861696e0a20202020202020202f2f2f206d657461646174612c20617320697320746865206361736520666f72206d6f737420436164656e6365204e4654732c20796f75206d617920616c736f2063686f6f73650a20202020202020202f2f2f20746f20726570726573656e7420796f75722061737365742773206d6574616461746120696e204950465320616e642061737369676e20746869732076616c75652061730a20202020202020202f2f2f20616e204950465346696c652073747275637420706f696e74696e6720746f207468617420495046532066696c652e20416c7465726e61746976656c792c20796f750a20202020202020202f2f2f206d61792073657269616c697a6520796f7572204e46542773206d6574616461746120616e642061737369676e2069742061732061204a534f4e20737472696e670a20202020202020202f2f2f20646174612055524c20726570726573656e746174696e6720746865204e46542773206f6e636861696e206d65746164617461206174207468652074696d6520746869730a20202020202020202f2f2f2076696577206973207265736f6c7665642e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574207572693a207b46696c657d0a0a2020202020202020696e6974286e616d653a20537472696e672c2073796d626f6c3a20537472696e672c207572693a207b46696c657d29207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e73796d626f6c203d2073796d626f6c0a20202020202020202020202073656c662e757269203d207572690a20202020202020207d0a202020207d0a0a2020202061636365737328616c6c292066756e2067657445564d427269646765644d65746164617461285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2045564d427269646765644d657461646174613f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c45564d427269646765644d657461646174613e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2045564d427269646765644d65746164617461207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a7d0a \ No newline at end of file diff --git a/tests/transactions/update_contract.cdc b/tests/transactions/update_contract.cdc new file mode 100644 index 0000000..22d9bdc --- /dev/null +++ b/tests/transactions/update_contract.cdc @@ -0,0 +1,5 @@ +transaction(name: String, code: String) { + prepare(signer: auth(Contracts) &Account) { + signer.contracts.update(name: name, code: code.decodeHex()) + } +} \ No newline at end of file diff --git a/transactions/buy_item.cdc b/transactions/buy_item.cdc index 5da52ee..936a3bb 100644 --- a/transactions/buy_item.cdc +++ b/transactions/buy_item.cdc @@ -25,7 +25,8 @@ transaction(listingResourceID: UInt64, var commissionRecipientCap: Capability<&{FungibleToken.Receiver}>? prepare(acct: auth(BorrowValue) &Account) { - + + // Get the metadata views for the NFT and FT types that are used in this transaction let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier( resourceTypeIdentifier: nftTypeIdentifier, viewType: Type() diff --git a/transactions/sell_item.cdc b/transactions/sell_item.cdc index b1e9b5e..c2a109b 100644 --- a/transactions/sell_item.cdc +++ b/transactions/sell_item.cdc @@ -1,7 +1,6 @@ -import "ExampleToken" import "FungibleToken" +import "FungibleTokenMetadataViews" import "NonFungibleToken" -import "ExampleNFT" import "MetadataViews" import "NFTStorefrontV2" @@ -23,7 +22,9 @@ transaction( customID: String?, commissionAmount: UFix64, expiry: UInt64, - marketplacesAddress: [Address] + marketplacesAddress: [Address], + nftTypeIdentifier: String, + ftTypeIdentifier: String ) { let tokenReceiver: Capability<&{FungibleToken.Receiver}> @@ -35,6 +36,7 @@ transaction( prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) { // If the account doesn't already have a Storefront + // Create a new empty Storefront if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil { // Create a new empty Storefront @@ -50,15 +52,25 @@ transaction( acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath) } + // Get the metadata views for the NFT and FT types that are used in this transaction + let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: nftTypeIdentifier, + viewType: Type() + ) as? MetadataViews.NFTCollectionData + ?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)") + + let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: ftTypeIdentifier, + viewType: Type() + ) as? FungibleTokenMetadataViews.FTVaultData + ?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)") + self.saleCuts = [] self.marketplacesCapability = [] - let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type()) as! MetadataViews.NFTCollectionData? - ?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction") - // Receiver for the sale cut. - self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver) - assert(self.tokenReceiver.borrow() != nil, message: "Missing or mis-typed ExampleToken receiver") + self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) + assert(self.tokenReceiver.borrow() != nil, message: "Missing or mis-typed Fungible Token receiver") var nftProviderCap: Capability? = nil // check if there is an existing capability/capability controller for the storage path @@ -90,7 +102,8 @@ transaction( let nft = collection.borrowNFT(saleItemID)! // Check whether the NFT implements the MetadataResolver or not. if nft.getViews().contains(Type()) { - let royaltiesRef = nft.resolveView(Type())?? panic("Unable to retrieve the royalties") + let royaltiesRef = nft.resolveView(Type()) + ?? panic("Unable to retrieve the Royalties metadata from the NFT for sale with ID \(nft.id).") let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties() for royalty in royalties { // TODO - Verify the type of the vault and it should exists @@ -118,20 +131,23 @@ transaction( for marketplace in marketplacesAddress { // Here we are making a fair assumption that all given addresses would have - // the capability to receive the `ExampleToken` + // the capability to receive the fungible token self.marketplacesCapability.append( - getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver) + getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) ) } } execute { + let nftType = CompositeType(nftTypeIdentifier)! + let ftType = CompositeType(ftTypeIdentifier)! + // Create listing self.storefront.createListing( nftProviderCapability: self.NFTProvider, - nftType: Type<@ExampleNFT.NFT>(), + nftType: nftType, nftID: saleItemID, - salePaymentVaultType: Type<@ExampleToken.Vault>(), + salePaymentVaultType: ftType, saleCuts: self.saleCuts, marketplacesCapability: self.marketplacesCapability.length == 0 ? nil : self.marketplacesCapability, customID: customID, diff --git a/transactions/sell_item_and_replace_current_listing.cdc b/transactions/sell_item_and_replace_current_listing.cdc index 4697a63..ba85756 100644 --- a/transactions/sell_item_and_replace_current_listing.cdc +++ b/transactions/sell_item_and_replace_current_listing.cdc @@ -1,7 +1,6 @@ -import "ExampleToken" import "FungibleToken" +import "FungibleTokenMetadataViews" import "NonFungibleToken" -import "ExampleNFT" import "MetadataViews" import "NFTStorefrontV2" @@ -23,7 +22,9 @@ transaction( customID: String?, commissionAmount: UFix64, expiry: UInt64, - marketplacesAddress: [Address] + marketplacesAddress: [Address], + nftTypeIdentifier: String, + ftTypeIdentifier: String ) { let tokenReceiver: Capability<&{FungibleToken.Receiver}> @@ -33,7 +34,9 @@ transaction( var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) { + // If the account doesn't already have a Storefront + // Create a new empty Storefront if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil { // Create a new empty Storefront @@ -48,18 +51,28 @@ transaction( ) acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath) } + + // Get the metadata views for the NFT and FT types that are used in this transaction + let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: nftTypeIdentifier, + viewType: Type() + ) as? MetadataViews.NFTCollectionData + ?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)") + + let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: ftTypeIdentifier, + viewType: Type() + ) as? FungibleTokenMetadataViews.FTVaultData + ?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)") self.saleCuts = [] self.marketplacesCapability = [] - let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type()) as! MetadataViews.NFTCollectionData? - ?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction") - // Receiver for the sale cut. - self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver) + self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) assert( self.tokenReceiver.check(), - message: "The signer does not store an ExampleToken.Receiver object at the path /public/exampleTokenReceiver" + message: "The signer does not store an Fungible Token Receiver object at the path \(vaultData.receiverPath)" .concat(". The signer must initialize their account with this Receiver first!") ) @@ -86,7 +99,7 @@ transaction( let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath - ) ?? panic("The signer does not store an ExampleNFT Collection object at the path \(collectionData.storagePath)." + ) ?? panic("The signer does not store an NFT Collection object at the path \(collectionData.storagePath)." .concat("The signer must initialize their account with this collection first!")) var totalRoyaltyCut = 0.0 @@ -95,7 +108,7 @@ transaction( // Check whether the NFT implements the MetadataResolver or not. if nft.getViews().contains(Type()) { let royaltiesRef = nft.resolveView(Type()) - ?? panic("Unable to get royalty info from the NFT for sale with ID \(nft.id).") + ?? panic("Unable to retrieve the Royalties metadata from the NFT for sale with ID \(nft.id).") let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties() for royalty in royalties { // TODO - Verify the type of the vault and it should exists @@ -115,7 +128,7 @@ transaction( amount: effectiveSaleItemPrice - totalRoyaltyCut ) ) - assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider") + assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider") self.storefront = acct.storage.borrow( from: NFTStorefrontV2.StorefrontStoragePath @@ -124,17 +137,20 @@ transaction( for marketplace in marketplacesAddress { // Here we are making a fair assumption that all given addresses would have - // the capability to receive the `ExampleToken` + // the capability to receive the fungible token self.marketplacesCapability.append( - getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver) + getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) ) } } execute { + let nftType = CompositeType(nftTypeIdentifier)! + let ftType = CompositeType(ftTypeIdentifier)! + // check for existing listings of the NFT var existingListingIDs = self.storefront.getExistingListingIDs( - nftType: Type<@ExampleNFT.NFT>(), + nftType: nftType, nftID: saleItemID ) // remove existing listings @@ -144,9 +160,9 @@ transaction( // Create listing self.storefront.createListing( nftProviderCapability: self.NFTProvider, - nftType: Type<@ExampleNFT.NFT>(), + nftType: nftType, nftID: saleItemID, - salePaymentVaultType: Type<@ExampleToken.Vault>(), + salePaymentVaultType: ftType, saleCuts: self.saleCuts, marketplacesCapability: self.marketplacesCapability.length == 0 ? nil : self.marketplacesCapability, customID: customID, diff --git a/transactions/sell_item_with_marketplace_cut.cdc b/transactions/sell_item_with_marketplace_cut.cdc index 6be9dab..8f3c6bf 100644 --- a/transactions/sell_item_with_marketplace_cut.cdc +++ b/transactions/sell_item_with_marketplace_cut.cdc @@ -1,7 +1,6 @@ -import "FlowToken" import "FungibleToken" +import "FungibleTokenMetadataViews" import "NonFungibleToken" -import "ExampleNFT" import "MetadataViews" import "NFTStorefrontV2" @@ -23,9 +22,11 @@ transaction( customID: String?, expiry: UInt64, marketPlaceSaleCutReceiver: Address, - marketPlaceSaleCutPercentage: UFix64 + marketPlaceSaleCutPercentage: UFix64, + nftTypeIdentifier: String, + ftTypeIdentifier: String ) { - let flowReceiver: Capability<&{FungibleToken.Receiver}> + let ftReceiver: Capability<&{FungibleToken.Receiver}> let NFTProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] @@ -34,6 +35,7 @@ transaction( prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) { // If the account doesn't already have a Storefront + // Create a new empty Storefront if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil { // Create a new empty Storefront @@ -49,17 +51,27 @@ transaction( acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath) } + // Get the metadata views for the NFT and FT types that are used in this transaction + let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: nftTypeIdentifier, + viewType: Type() + ) as? MetadataViews.NFTCollectionData + ?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)") + + let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: ftTypeIdentifier, + viewType: Type() + ) as? FungibleTokenMetadataViews.FTVaultData + ?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)") + self.saleCuts = [] self.marketplacesCapability = [] - let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type()) as! MetadataViews.NFTCollectionData? - ?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction") - // Receiver for the sale cut. - self.flowReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) + self.ftReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) assert( - self.flowReceiver.borrow() != nil, - message: "Missing or mis-typed FlowToken receiver" + self.ftReceiver.borrow() != nil, + message: "Missing or mis-typed Fungible Token receiver" ) var nftProviderCap: Capability? = nil @@ -91,7 +103,8 @@ transaction( let nft = collection.borrowNFT(saleItemID)! // Check whether the NFT implements the MetadataResolver or not. if nft.getViews().contains(Type()) { - let royaltiesRef = nft.resolveView(Type())?? panic("Unable to retrieve the royalties") + let royaltiesRef = nft.resolveView(Type()) + ?? panic("Unable to retrieve the Royalties metadata from the NFT for sale with ID \(nft.id).") let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties() for royalty in royalties { // TODO - Verify the type of the vault and it should exists @@ -107,11 +120,11 @@ transaction( // Append the cut for the seller. self.saleCuts.append( NFTStorefrontV2.SaleCut( - receiver: self.flowReceiver, + receiver: self.ftReceiver, amount: saleItemPrice - totalRoyaltyCut - saleItemPrice * marketPlaceSaleCutPercentage ) ) - assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider") + assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider") self.storefront = acct.storage.borrow( from: NFTStorefrontV2.StorefrontStoragePath @@ -119,8 +132,8 @@ transaction( .concat("Make sure the signer has initialized their account with a NFTStorefrontV2 storefront!")) // Here we are making a fair assumption that all given addresses would have - // the capability to receive the `FlowToken` - let marketPlaceCapability = getAccount(marketPlaceSaleCutReceiver).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) + // the capability to receive the fungible token + let marketPlaceCapability = getAccount(marketPlaceSaleCutReceiver).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) // Append the cut for the marketplace. self.saleCuts.append( @@ -132,12 +145,15 @@ transaction( } execute { + let nftType = CompositeType(nftTypeIdentifier)! + let ftType = CompositeType(ftTypeIdentifier)! + // Create listing self.storefront.createListing( nftProviderCapability: self.NFTProvider, - nftType: Type<@ExampleNFT.NFT>(), + nftType: nftType, nftID: saleItemID, - salePaymentVaultType: Type<@FlowToken.Vault>(), + salePaymentVaultType: ftType, saleCuts: self.saleCuts, marketplacesCapability: nil, customID: customID, From e927c707e9fc95a7b0f5ea05368b2d807e815d0a Mon Sep 17 00:00:00 2001 From: Josh Hannan Date: Mon, 4 Aug 2025 10:45:47 -0400 Subject: [PATCH 4/4] fix camel case and other PR feedback --- tests/NFTStorefrontV2_test.cdc | 6 ++--- transactions/buy_item.cdc | 25 ++++++++++--------- transactions/sell_item.cdc | 8 +++--- .../sell_item_and_replace_current_listing.cdc | 8 +++--- .../sell_item_with_marketplace_cut.cdc | 10 ++++---- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/tests/NFTStorefrontV2_test.cdc b/tests/NFTStorefrontV2_test.cdc index 264cb59..f4544ad 100644 --- a/tests/NFTStorefrontV2_test.cdc +++ b/tests/NFTStorefrontV2_test.cdc @@ -222,8 +222,7 @@ fun testBuyItem() { listingID, // listing resource id seller.address, // storefront address seller.address, // commision recipient - nftTypeIdentifier, // nft type - ftTypeIdentifier // ft type + nftTypeIdentifier // nft type ], ) let txResult = Test.executeTransaction(tx) @@ -563,8 +562,7 @@ fun testSellMaliciousListing() { listingID, // listing resource id exampleNFTAccount.address, // storefront address exampleNFTAccount.address, // commision recipient - nftTypeIdentifier, // nft type - ftTypeIdentifier // ft type + nftTypeIdentifier // nft type ], ) txResult = Test.executeTransaction(tx) diff --git a/transactions/buy_item.cdc b/transactions/buy_item.cdc index 936a3bb..e1be627 100644 --- a/transactions/buy_item.cdc +++ b/transactions/buy_item.cdc @@ -15,17 +15,16 @@ import "MetadataViews" transaction(listingResourceID: UInt64, storefrontAddress: Address, commissionRecipient: Address?, - nftTypeIdentifier: String, - ftTypeIdentifier: String) { + nftTypeIdentifier: String) { let paymentVault: @{FungibleToken.Vault} - let NFTReceiver: &{NonFungibleToken.Receiver} + let nftReceiver: &{NonFungibleToken.Receiver} let storefront: &{NFTStorefrontV2.StorefrontPublic} let listing: &{NFTStorefrontV2.ListingPublic} var commissionRecipientCap: Capability<&{FungibleToken.Receiver}>? prepare(acct: auth(BorrowValue) &Account) { - + // Get the metadata views for the NFT and FT types that are used in this transaction let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier( resourceTypeIdentifier: nftTypeIdentifier, @@ -33,12 +32,6 @@ transaction(listingResourceID: UInt64, ) as? MetadataViews.NFTCollectionData ?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)") - let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier( - resourceTypeIdentifier: ftTypeIdentifier, - viewType: Type() - ) as? FungibleTokenMetadataViews.FTVaultData - ?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)") - self.commissionRecipientCap = nil // Access the storefront public resource of the seller to purchase the listing. self.storefront = getAccount(storefrontAddress).capabilities.borrow<&{NFTStorefrontV2.StorefrontPublic}>( @@ -50,13 +43,21 @@ transaction(listingResourceID: UInt64, ?? panic("Could not get a listing with ID \(listingResourceID) from the storefront in account \(storefrontAddress)") let price = self.listing.getDetails().salePrice + let vaultType = self.listing.getDetails().salePaymentVaultType + + let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier( + resourceTypeIdentifier: vaultType.identifier, + viewType: Type() + ) as? FungibleTokenMetadataViews.FTVaultData + ?? panic("Could not construct valid FT type and view from identifier \(vaultType.identifier)") + // Access the vault of the buyer to pay the sale price of the listing. let mainVault = acct.storage.borrow(from: vaultData.storagePath) ?? panic("The signer does not store an Vault object at the path \(vaultData.storagePath)" .concat(". The signer must initialize their account with this vault first!")) self.paymentVault <- mainVault.withdraw(amount: price) - self.NFTReceiver = acct.capabilities.borrow<&{NonFungibleToken.Receiver}>(collectionData.publicPath) + self.nftReceiver = acct.capabilities.borrow<&{NonFungibleToken.Receiver}>(collectionData.publicPath) ?? panic("Cannot borrow an NFT collection receiver from the signer's account at path \(collectionData.publicPath).") // Fetch the commission amt. @@ -83,6 +84,6 @@ transaction(listingResourceID: UInt64, commissionRecipient: self.commissionRecipientCap ) // Deposit the NFT in the buyer's collection. - self.NFTReceiver.deposit(token: <-item) + self.nftReceiver.deposit(token: <-item) } } diff --git a/transactions/sell_item.cdc b/transactions/sell_item.cdc index c2a109b..745ea05 100644 --- a/transactions/sell_item.cdc +++ b/transactions/sell_item.cdc @@ -28,7 +28,7 @@ transaction( ) { let tokenReceiver: Capability<&{FungibleToken.Receiver}> - let NFTProvider: Capability + let nftProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] @@ -70,7 +70,7 @@ transaction( // Receiver for the sale cut. self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) - assert(self.tokenReceiver.borrow() != nil, message: "Missing or mis-typed Fungible Token receiver") + assert(self.tokenReceiver.borrow() != nil, message: "Missing or mis-typed Fungible Token receiver for token \(ftTypeIdentifier) at path \(vaultData.receiverPath)") var nftProviderCap: Capability? = nil // check if there is an existing capability/capability controller for the storage path @@ -91,7 +91,7 @@ transaction( } assert(nftProviderCap?.check() ?? false, message: "Could not assign Provider Capability") - self.NFTProvider = nftProviderCap! + self.nftProvider = nftProviderCap! let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath @@ -144,7 +144,7 @@ transaction( // Create listing self.storefront.createListing( - nftProviderCapability: self.NFTProvider, + nftProviderCapability: self.nftProvider, nftType: nftType, nftID: saleItemID, salePaymentVaultType: ftType, diff --git a/transactions/sell_item_and_replace_current_listing.cdc b/transactions/sell_item_and_replace_current_listing.cdc index ba85756..d755734 100644 --- a/transactions/sell_item_and_replace_current_listing.cdc +++ b/transactions/sell_item_and_replace_current_listing.cdc @@ -28,7 +28,7 @@ transaction( ) { let tokenReceiver: Capability<&{FungibleToken.Receiver}> - let NFTProvider: Capability + let nftProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing, NFTStorefrontV2.RemoveListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] @@ -95,7 +95,7 @@ transaction( } assert(nftProviderCap?.check() ?? false, message: "Could not assign Provider Capability") - self.NFTProvider = nftProviderCap! + self.nftProvider = nftProviderCap! let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath @@ -128,7 +128,7 @@ transaction( amount: effectiveSaleItemPrice - totalRoyaltyCut ) ) - assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider") + assert(self.nftProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider") self.storefront = acct.storage.borrow( from: NFTStorefrontV2.StorefrontStoragePath @@ -159,7 +159,7 @@ transaction( } // Create listing self.storefront.createListing( - nftProviderCapability: self.NFTProvider, + nftProviderCapability: self.nftProvider, nftType: nftType, nftID: saleItemID, salePaymentVaultType: ftType, diff --git a/transactions/sell_item_with_marketplace_cut.cdc b/transactions/sell_item_with_marketplace_cut.cdc index 8f3c6bf..e8b1a0b 100644 --- a/transactions/sell_item_with_marketplace_cut.cdc +++ b/transactions/sell_item_with_marketplace_cut.cdc @@ -27,7 +27,7 @@ transaction( ftTypeIdentifier: String ) { let ftReceiver: Capability<&{FungibleToken.Receiver}> - let NFTProvider: Capability + let nftProvider: Capability let storefront: auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront var saleCuts: [NFTStorefrontV2.SaleCut] var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>] @@ -71,7 +71,7 @@ transaction( self.ftReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath) assert( self.ftReceiver.borrow() != nil, - message: "Missing or mis-typed Fungible Token receiver" + message: "Missing or mis-typed Fungible Token receiver for token \(ftTypeIdentifier) at path \(vaultData.receiverPath)" ) var nftProviderCap: Capability? = nil @@ -93,7 +93,7 @@ transaction( } assert(nftProviderCap?.check() ?? false, message: "Could not assign Provider Capability") - self.NFTProvider = nftProviderCap! + self.nftProvider = nftProviderCap! let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>( collectionData.publicPath @@ -124,7 +124,7 @@ transaction( amount: saleItemPrice - totalRoyaltyCut - saleItemPrice * marketPlaceSaleCutPercentage ) ) - assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider") + assert(self.nftProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider") self.storefront = acct.storage.borrow( from: NFTStorefrontV2.StorefrontStoragePath @@ -150,7 +150,7 @@ transaction( // Create listing self.storefront.createListing( - nftProviderCapability: self.NFTProvider, + nftProviderCapability: self.nftProvider, nftType: nftType, nftID: saleItemID, salePaymentVaultType: ftType,