Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
},
"dependencies": {
"Burner": {
"source": "mainnet://f233dcee88fe0abe.Burner",
"source": "testnet://9a0766d93b6608b7.Burner",
"hash": "71af18e227984cd434a3ad00bb2f3618b76482842bae920ee55662c37c8bf331",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
Expand Down Expand Up @@ -134,8 +134,8 @@
}
},
"FungibleToken": {
"source": "mainnet://f233dcee88fe0abe.FungibleToken",
"hash": "23c1159cf99b2b039b6b868d782d57ae39b8d784045d81597f100a4782f0285b",
"source": "testnet://9a0766d93b6608b7.FungibleToken",
"hash": "d84e41a86bd27806367b89c75a2a9570f7713480ab113cc214c4576466888958",
"aliases": {
"emulator": "ee82856bf20e2aa6",
"mainnet": "f233dcee88fe0abe",
Expand Down Expand Up @@ -170,8 +170,8 @@
}
},
"MetadataViews": {
"source": "mainnet://1d7e57aa55817448.MetadataViews",
"hash": "10a239cc26e825077de6c8b424409ae173e78e8391df62750b6ba19ffd048f51",
"source": "testnet://631e88ae7f1d7c20.MetadataViews",
"hash": "0b746cabba668c39de9dc47b94fc458f4119cbabb6883616e4a8750518310ccb",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
"mainnet": "1d7e57aa55817448",
Expand Down Expand Up @@ -222,16 +222,16 @@
}
},
"NonFungibleToken": {
"source": "mainnet://1d7e57aa55817448.NonFungibleToken",
"hash": "b63f10e00d1a814492822652dac7c0574428a200e4c26cb3c832c4829e2778f0",
"source": "testnet://631e88ae7f1d7c20.NonFungibleToken",
"hash": "ac40c5a3ec05884ae48cb52ebf680deebb21e8a0143cd7d9b1dc88b0f107e088",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
"mainnet": "1d7e57aa55817448",
"testnet": "631e88ae7f1d7c20"
}
},
"ViewResolver": {
"source": "mainnet://1d7e57aa55817448.ViewResolver",
"source": "testnet://631e88ae7f1d7c20.ViewResolver",
"hash": "374a1994046bac9f6228b4843cb32393ef40554df9bd9907a702d098a2987bde",
"aliases": {
"emulator": "f8d6e0586b0a20c7",
Expand Down
2 changes: 1 addition & 1 deletion scripts/has_listing_become_ghosted.cdc
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion scripts/read_all_unique_ghost_listings.cdc
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion scripts/read_allowed_commission_receivers.cdc
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
9 changes: 5 additions & 4 deletions scripts/read_duplicate_listing_ids.cdc
Original file line number Diff line number Diff line change
@@ -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")
}
2 changes: 1 addition & 1 deletion scripts/read_listing_details.cdc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import NFTStorefrontV2 from "../contracts/NFTStorefrontV2.cdc"
import "NFTStorefrontV2"

/// This script returns the details for a listing within a storefront
///
Expand Down
2 changes: 1 addition & 1 deletion scripts/read_storefront_ids.cdc
Original file line number Diff line number Diff line change
@@ -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

Expand Down
54 changes: 44 additions & 10 deletions tests/NFTStorefrontV2_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand 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",
Expand All @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -186,7 +207,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, nftTypeIdentifier]
)
Test.assertEqual((duplicateListingIDs as! [UInt64]?)!.length, 0)

let code = loadCode("buy_item.cdc", "transactions")
Expand All @@ -197,7 +221,8 @@ fun testBuyItem() {
arguments: [
listingID, // listing resource id
seller.address, // storefront address
seller.address // commision recipient
seller.address, // commision recipient
nftTypeIdentifier // nft type
],
)
let txResult = Test.executeTransaction(tx)
Expand Down Expand Up @@ -258,7 +283,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)
Expand Down Expand Up @@ -334,7 +361,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)
Expand Down Expand Up @@ -375,7 +404,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)
Expand Down Expand Up @@ -455,7 +486,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)
Expand Down Expand Up @@ -528,7 +561,8 @@ fun testSellMaliciousListing() {
arguments: [
listingID, // listing resource id
exampleNFTAccount.address, // storefront address
exampleNFTAccount.address // commision recipient
exampleNFTAccount.address, // commision recipient
nftTypeIdentifier // nft type
],
)
txResult = Test.executeTransaction(tx)
Expand Down
1 change: 1 addition & 0 deletions tests/transactions/MVbytes

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions tests/transactions/update_contract.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
transaction(name: String, code: String) {
prepare(signer: auth(Contracts) &Account) {
signer.contracts.update(name: name, code: code.decodeHex())
}
}
46 changes: 30 additions & 16 deletions transactions/buy_item.cdc
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
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) {

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) {

// Get the metadata views for the NFT and FT types that are used in this transaction
let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier(
resourceTypeIdentifier: nftTypeIdentifier,
viewType: Type<MetadataViews.NFTCollectionData>()
) as? MetadataViews.NFTCollectionData
?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)")

self.commissionRecipientCap = nil
// Access the storefront public resource of the seller to purchase the listing.
self.storefront = getAccount(storefrontAddress).capabilities.borrow<&{NFTStorefrontV2.StorefrontPublic}>(
Expand All @@ -33,17 +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<FungibleTokenMetadataViews.FTVaultData>()
) 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<auth(FungibleToken.Withdraw) &ExampleToken.Vault>(from: /storage/exampleTokenVault)
?? panic("The signer does not store an ExampleToken.Vault object at the path /storage/exampleTokenVault "
let mainVault = acct.storage.borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(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<MetadataViews.NFTCollectionData>()) 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.
Expand All @@ -52,9 +66,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
Expand All @@ -70,6 +84,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)
}
}
Loading
Loading