Skip to content

Commit 5e2fb6b

Browse files
committed
use generic types in V2 transactions
1 parent 6488b13 commit 5e2fb6b

File tree

7 files changed

+145
-57
lines changed

7 files changed

+145
-57
lines changed

tests/NFTStorefrontV2_test.cdc

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ access(all) let marketplace = Test.createAccount()
1313
access(all) let storefrontAccount = Test.getAccount(0x0000000000000007)
1414
access(all) let exampleNFTAccount = Test.getAccount(0x0000000000000008)
1515
access(all) let exampleTokenAccount = Test.getAccount(0x0000000000000009)
16+
17+
access(all) let nftTypeIdentifier = "A.0000000000000008.ExampleNFT.NFT"
18+
access(all) let ftTypeIdentifier = "A.0000000000000009.ExampleToken.Vault"
19+
access(all) let flowTokenTypeIdentifier = "A.0000000000000003.FlowToken.Vault"
20+
1621
access(all) var nftCount = 1
1722

1823
access(all)
@@ -34,6 +39,20 @@ access(all)
3439
fun setup() {
3540
let serviceAccount = Test.serviceAccount()
3641

42+
// TODO: Remove this section once MetadataViews is updated on the emulator
43+
// with the resolveViewfromIdentifier function.
44+
let metadataViewsCode = loadCode("MVbytes", "tests/transactions")
45+
var tx = Test.Transaction(
46+
code: loadCode("update_contract.cdc", "tests/transactions"),
47+
authorizers: [serviceAccount.address],
48+
signers: [serviceAccount],
49+
arguments: ["MetadataViews", metadataViewsCode],
50+
)
51+
52+
var txResult = Test.executeTransaction(tx)
53+
Test.expect(txResult, Test.beSucceeded())
54+
// TODO: End of section to remove
55+
3756
var err = Test.deployContract(
3857
name: "NFTStorefrontV2",
3958
path: "../contracts/NFTStorefrontV2.cdc",
@@ -57,13 +76,13 @@ fun setup() {
5776

5877
// Setup example token
5978
var code = loadCode("setup_account.cdc", "transactions/example-token")
60-
var tx = Test.Transaction(
79+
tx = Test.Transaction(
6180
code: code,
6281
authorizers: [buyer.address],
6382
signers: [buyer],
6483
arguments: [],
6584
)
66-
var txResult = Test.executeTransaction(tx)
85+
txResult = Test.executeTransaction(tx)
6786
Test.expect(txResult, Test.beSucceeded())
6887

6988
tx = Test.Transaction(
@@ -160,7 +179,9 @@ fun testSellItem() {
160179
"Custom", // custom id
161180
0.1, // commission amount
162181
UInt64(2025908543), // 10 years in the future
163-
[] // Marketplaces address
182+
[], // Marketplaces address
183+
nftTypeIdentifier, // nft type
184+
ftTypeIdentifier // ft type
164185
],
165186
)
166187
var txResult = Test.executeTransaction(tx)
@@ -188,7 +209,7 @@ fun testBuyItem() {
188209
Test.assert(listingDetails != nil, message: "Received invalid result from reading listing details")
189210
let duplicateListingIDs = scriptExecutor(
190211
"read_duplicate_listing_ids.cdc",
191-
[seller.address, listedNFTID, listingID, "A.0000000000000008.ExampleNFT.NFT"]
212+
[seller.address, listedNFTID, listingID, nftTypeIdentifier]
192213
)
193214
Test.assertEqual((duplicateListingIDs as! [UInt64]?)!.length, 0)
194215

@@ -200,7 +221,9 @@ fun testBuyItem() {
200221
arguments: [
201222
listingID, // listing resource id
202223
seller.address, // storefront address
203-
seller.address // commision recipient
224+
seller.address, // commision recipient
225+
nftTypeIdentifier, // nft type
226+
ftTypeIdentifier // ft type
204227
],
205228
)
206229
let txResult = Test.executeTransaction(tx)
@@ -261,7 +284,9 @@ fun testCleanupGhostListings() {
261284
"Custom", // custom id
262285
0.1, // commission amount
263286
UInt64(2025908543), // 10 years in the future
264-
[] // Marketplaces address
287+
[], // Marketplaces address
288+
nftTypeIdentifier, // nft type
289+
ftTypeIdentifier // ft type
265290
],
266291
)
267292
var txResult = Test.executeTransaction(tx)
@@ -337,7 +362,9 @@ fun testSellItemWithMarketplaceCut() {
337362
"Custom1", // custom id
338363
UInt64(2025908543), // 10 years in the future
339364
seller.address, // set the buyer as the marketplace sale cut receiver
340-
0.1 // Marketplaces address
365+
0.1, // Marketplaces address
366+
nftTypeIdentifier, // nft type
367+
flowTokenTypeIdentifier // ft type
341368
],
342369
)
343370
let txResult = Test.executeTransaction(tx)
@@ -378,7 +405,9 @@ fun testSellItemAndReplaceCurrentListing() {
378405
"Custom1", // custom id
379406
0.1, // commission amount
380407
timestamp, // way in the past (testing expired listing next)
381-
[seller.address] // set the buyer as the marketplace sale cut receiver
408+
[seller.address], // set the buyer as the marketplace sale cut receiver
409+
nftTypeIdentifier, // nft type
410+
ftTypeIdentifier // ft type
382411
],
383412
)
384413
let txResult = Test.executeTransaction(tx)
@@ -458,7 +487,9 @@ fun testRemoveItem() {
458487
"Custom1", // custom id
459488
UInt64(2025908543), // 10 years in the future
460489
seller.address, // set the buyer as the marketplace sale cut receiver
461-
0.1 // Marketplaces address
490+
0.1, // Marketplaces address
491+
nftTypeIdentifier, // nft type
492+
flowTokenTypeIdentifier // ft type
462493
],
463494
)
464495
var txResult = Test.executeTransaction(tx)
@@ -531,7 +562,9 @@ fun testSellMaliciousListing() {
531562
arguments: [
532563
listingID, // listing resource id
533564
exampleNFTAccount.address, // storefront address
534-
exampleNFTAccount.address // commision recipient
565+
exampleNFTAccount.address, // commision recipient
566+
nftTypeIdentifier, // nft type
567+
ftTypeIdentifier // ft type
535568
],
536569
)
537570
txResult = Test.executeTransaction(tx)

tests/transactions/MVbytes

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
transaction(name: String, code: String) {
2+
prepare(signer: auth(Contracts) &Account) {
3+
signer.contracts.update(name: name, code: code.decodeHex())
4+
}
5+
}

transactions/buy_item.cdc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ transaction(listingResourceID: UInt64,
2525
var commissionRecipientCap: Capability<&{FungibleToken.Receiver}>?
2626

2727
prepare(acct: auth(BorrowValue) &Account) {
28-
28+
29+
// Get the metadata views for the NFT and FT types that are used in this transaction
2930
let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier(
3031
resourceTypeIdentifier: nftTypeIdentifier,
3132
viewType: Type<MetadataViews.NFTCollectionData>()

transactions/sell_item.cdc

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import "ExampleToken"
21
import "FungibleToken"
2+
import "FungibleTokenMetadataViews"
33
import "NonFungibleToken"
4-
import "ExampleNFT"
54
import "MetadataViews"
65
import "NFTStorefrontV2"
76

@@ -23,7 +22,9 @@ transaction(
2322
customID: String?,
2423
commissionAmount: UFix64,
2524
expiry: UInt64,
26-
marketplacesAddress: [Address]
25+
marketplacesAddress: [Address],
26+
nftTypeIdentifier: String,
27+
ftTypeIdentifier: String
2728
) {
2829

2930
let tokenReceiver: Capability<&{FungibleToken.Receiver}>
@@ -35,6 +36,7 @@ transaction(
3536
prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) {
3637

3738
// If the account doesn't already have a Storefront
39+
// Create a new empty Storefront
3840
if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil {
3941

4042
// Create a new empty Storefront
@@ -50,15 +52,25 @@ transaction(
5052
acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath)
5153
}
5254

55+
// Get the metadata views for the NFT and FT types that are used in this transaction
56+
let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier(
57+
resourceTypeIdentifier: nftTypeIdentifier,
58+
viewType: Type<MetadataViews.NFTCollectionData>()
59+
) as? MetadataViews.NFTCollectionData
60+
?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)")
61+
62+
let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier(
63+
resourceTypeIdentifier: ftTypeIdentifier,
64+
viewType: Type<FungibleTokenMetadataViews.FTVaultData>()
65+
) as? FungibleTokenMetadataViews.FTVaultData
66+
?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)")
67+
5368
self.saleCuts = []
5469
self.marketplacesCapability = []
5570

56-
let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?
57-
?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction")
58-
5971
// Receiver for the sale cut.
60-
self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver)
61-
assert(self.tokenReceiver.borrow() != nil, message: "Missing or mis-typed ExampleToken receiver")
72+
self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath)
73+
assert(self.tokenReceiver.borrow() != nil, message: "Missing or mis-typed Fungible Token receiver")
6274

6375
var nftProviderCap: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>? = nil
6476
// check if there is an existing capability/capability controller for the storage path
@@ -90,7 +102,8 @@ transaction(
90102
let nft = collection.borrowNFT(saleItemID)!
91103
// Check whether the NFT implements the MetadataResolver or not.
92104
if nft.getViews().contains(Type<MetadataViews.Royalties>()) {
93-
let royaltiesRef = nft.resolveView(Type<MetadataViews.Royalties>())?? panic("Unable to retrieve the royalties")
105+
let royaltiesRef = nft.resolveView(Type<MetadataViews.Royalties>())
106+
?? panic("Unable to retrieve the Royalties metadata from the NFT for sale with ID \(nft.id).")
94107
let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties()
95108
for royalty in royalties {
96109
// TODO - Verify the type of the vault and it should exists
@@ -118,20 +131,23 @@ transaction(
118131

119132
for marketplace in marketplacesAddress {
120133
// Here we are making a fair assumption that all given addresses would have
121-
// the capability to receive the `ExampleToken`
134+
// the capability to receive the fungible token
122135
self.marketplacesCapability.append(
123-
getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver)
136+
getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath)
124137
)
125138
}
126139
}
127140

128141
execute {
142+
let nftType = CompositeType(nftTypeIdentifier)!
143+
let ftType = CompositeType(ftTypeIdentifier)!
144+
129145
// Create listing
130146
self.storefront.createListing(
131147
nftProviderCapability: self.NFTProvider,
132-
nftType: Type<@ExampleNFT.NFT>(),
148+
nftType: nftType,
133149
nftID: saleItemID,
134-
salePaymentVaultType: Type<@ExampleToken.Vault>(),
150+
salePaymentVaultType: ftType,
135151
saleCuts: self.saleCuts,
136152
marketplacesCapability: self.marketplacesCapability.length == 0 ? nil : self.marketplacesCapability,
137153
customID: customID,

transactions/sell_item_and_replace_current_listing.cdc

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import "ExampleToken"
21
import "FungibleToken"
2+
import "FungibleTokenMetadataViews"
33
import "NonFungibleToken"
4-
import "ExampleNFT"
54
import "MetadataViews"
65
import "NFTStorefrontV2"
76

@@ -23,7 +22,9 @@ transaction(
2322
customID: String?,
2423
commissionAmount: UFix64,
2524
expiry: UInt64,
26-
marketplacesAddress: [Address]
25+
marketplacesAddress: [Address],
26+
nftTypeIdentifier: String,
27+
ftTypeIdentifier: String
2728
) {
2829

2930
let tokenReceiver: Capability<&{FungibleToken.Receiver}>
@@ -33,7 +34,9 @@ transaction(
3334
var marketplacesCapability: [Capability<&{FungibleToken.Receiver}>]
3435

3536
prepare(acct: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, StorageCapabilities) &Account) {
37+
3638
// If the account doesn't already have a Storefront
39+
// Create a new empty Storefront
3740
if acct.storage.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil {
3841

3942
// Create a new empty Storefront
@@ -48,18 +51,28 @@ transaction(
4851
)
4952
acct.capabilities.publish(storefrontPublicCap, at: NFTStorefrontV2.StorefrontPublicPath)
5053
}
54+
55+
// Get the metadata views for the NFT and FT types that are used in this transaction
56+
let collectionData = MetadataViews.resolveContractViewFromTypeIdentifier(
57+
resourceTypeIdentifier: nftTypeIdentifier,
58+
viewType: Type<MetadataViews.NFTCollectionData>()
59+
) as? MetadataViews.NFTCollectionData
60+
?? panic("Could not construct valid NFT type and view from identifier \(nftTypeIdentifier)")
61+
62+
let vaultData = MetadataViews.resolveContractViewFromTypeIdentifier(
63+
resourceTypeIdentifier: ftTypeIdentifier,
64+
viewType: Type<FungibleTokenMetadataViews.FTVaultData>()
65+
) as? FungibleTokenMetadataViews.FTVaultData
66+
?? panic("Could not construct valid FT type and view from identifier \(ftTypeIdentifier)")
5167

5268
self.saleCuts = []
5369
self.marketplacesCapability = []
5470

55-
let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?
56-
?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction")
57-
5871
// Receiver for the sale cut.
59-
self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver)
72+
self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath)
6073
assert(
6174
self.tokenReceiver.check(),
62-
message: "The signer does not store an ExampleToken.Receiver object at the path /public/exampleTokenReceiver"
75+
message: "The signer does not store an Fungible Token Receiver object at the path \(vaultData.receiverPath)"
6376
.concat(". The signer must initialize their account with this Receiver first!")
6477
)
6578

@@ -86,7 +99,7 @@ transaction(
8699

87100
let collection = acct.capabilities.borrow<&{NonFungibleToken.Collection}>(
88101
collectionData.publicPath
89-
) ?? panic("The signer does not store an ExampleNFT Collection object at the path \(collectionData.storagePath)."
102+
) ?? panic("The signer does not store an NFT Collection object at the path \(collectionData.storagePath)."
90103
.concat("The signer must initialize their account with this collection first!"))
91104

92105
var totalRoyaltyCut = 0.0
@@ -95,7 +108,7 @@ transaction(
95108
// Check whether the NFT implements the MetadataResolver or not.
96109
if nft.getViews().contains(Type<MetadataViews.Royalties>()) {
97110
let royaltiesRef = nft.resolveView(Type<MetadataViews.Royalties>())
98-
?? panic("Unable to get royalty info from the NFT for sale with ID \(nft.id).")
111+
?? panic("Unable to retrieve the Royalties metadata from the NFT for sale with ID \(nft.id).")
99112
let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties()
100113
for royalty in royalties {
101114
// TODO - Verify the type of the vault and it should exists
@@ -115,7 +128,7 @@ transaction(
115128
amount: effectiveSaleItemPrice - totalRoyaltyCut
116129
)
117130
)
118-
assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed ExampleNFT.Collection provider")
131+
assert(self.NFTProvider.borrow() != nil, message: "Missing or mis-typed NFT Collection provider")
119132

120133
self.storefront = acct.storage.borrow<auth(NFTStorefrontV2.CreateListing, NFTStorefrontV2.RemoveListing) &NFTStorefrontV2.Storefront>(
121134
from: NFTStorefrontV2.StorefrontStoragePath
@@ -124,17 +137,20 @@ transaction(
124137

125138
for marketplace in marketplacesAddress {
126139
// Here we are making a fair assumption that all given addresses would have
127-
// the capability to receive the `ExampleToken`
140+
// the capability to receive the fungible token
128141
self.marketplacesCapability.append(
129-
getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(/public/exampleTokenReceiver)
142+
getAccount(marketplace).capabilities.get<&{FungibleToken.Receiver}>(vaultData.receiverPath)
130143
)
131144
}
132145
}
133146

134147
execute {
148+
let nftType = CompositeType(nftTypeIdentifier)!
149+
let ftType = CompositeType(ftTypeIdentifier)!
150+
135151
// check for existing listings of the NFT
136152
var existingListingIDs = self.storefront.getExistingListingIDs(
137-
nftType: Type<@ExampleNFT.NFT>(),
153+
nftType: nftType,
138154
nftID: saleItemID
139155
)
140156
// remove existing listings
@@ -144,9 +160,9 @@ transaction(
144160
// Create listing
145161
self.storefront.createListing(
146162
nftProviderCapability: self.NFTProvider,
147-
nftType: Type<@ExampleNFT.NFT>(),
163+
nftType: nftType,
148164
nftID: saleItemID,
149-
salePaymentVaultType: Type<@ExampleToken.Vault>(),
165+
salePaymentVaultType: ftType,
150166
saleCuts: self.saleCuts,
151167
marketplacesCapability: self.marketplacesCapability.length == 0 ? nil : self.marketplacesCapability,
152168
customID: customID,

0 commit comments

Comments
 (0)