Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#104 - WIP update tx to avoid DUC leak #105

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
131 changes: 61 additions & 70 deletions cadence/contracts/TransactionTemplates.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -498,20 +498,20 @@ pub fun DapperCreateListingTemplate(nftSchema: TransactionGenerationUtils.NFTSch
let lines: [[String]] = [
["// This transaction was auto-generated with the NFT Catalog (https://github.com/dapperlabs/nft-catalog)"],
["//"],
["// This transaction purchases an NFT from a dapp directly (i.e. **not** on a peer-to-peer marketplace)."],
["// This transaction facilitates the listing of an NFT with the StorefrontV2 contract"],
["// "],
["// Collection Identifier: ", nftSchema!.identifier, ""],
["// Vault Identifier: ", ftSchema!.identifier, ""],
["//"],
["// Version: 0.1.1"],
[""],
["transaction(saleItemID: UInt64, saleItemPrice: UFix64, commissionAmount: UFix64, marketplacesAddress: [Address], expiry: UInt64, customID: String?) {"],
[" /// `saleItemID` - ID of the NFT that is put on sale by the seller."],
[" /// `saleItemPrice` - Amount of tokens (FT) buyer needs to pay for the purchase of listed NFT."],
[" /// `commissionAmount` - Commission amount that will be taken away by the purchase facilitator."],
[" /// `marketplacesAddress` - List of addresses that are allowed to get the commission."],
[" /// `expiry` - Unix timestamp at which created listing become expired."],
[" /// `customID` - Optional string to represent identifier of the dapp."],
["transaction(saleItemID: UInt64, saleItemPrice: UFix64, commissionAmount: UFix64, marketplaceAddresses: [Address], expiry: UInt64, customID: String?) {"],
[" // 'saleItemID' - ID of the NFT that is put on sale by the seller."],
[" // 'saleItemPrice' - Amount of tokens (FT) buyer needs to pay for the purchase of listed NFT."],
[" // 'commissionAmount' - Commission amount that will be taken away by the purchase facilitator i.e marketplaceAddresses."],
[" // 'marketplaceAddresses' - List of addresses that are allowed to get the commission."],
[" // 'expiry' - Unix timestamp at which created listing become expired."],
[" // 'customID' - Optional string to represent identifier of the dapp."],
[" let sellerPaymentReceiver: Capability<&{FungibleToken.Receiver}>"],
[" let nftProvider: Capability<&", nftSchema!.contractName, ".Collection{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>"],
[" let storefront: &NFTStorefrontV2.Storefront"],
Expand All @@ -523,101 +523,91 @@ let lines: [[String]] = [
[" prepare(seller: AuthAccount) {"],
[" self.saleCuts = []"],
[" self.marketplacesCapability = []"],
[""],
[" // If the account doesn't already have a storefront, create one and add it to the account"],
[" if seller.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil {"],
[" // Create a new empty Storefront"],
[" let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront"],
[" // save it to the account"],
[" seller.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath)"],
[" // create a public capability for the Storefront"],
[" seller.link<&NFTStorefrontV2.Storefront{NFTStorefrontV2.StorefrontPublic}>(NFTStorefrontV2.StorefrontPublicPath, target: NFTStorefrontV2.StorefrontStoragePath)"],
[" }"],
[""],
[" // FT Setup if the user's account is not initialized with FT receiver"],
[" if seller.borrow<&{FungibleToken.Receiver}>(from: ", ftSchema!.receiverStoragePath ?? "", ") == nil {"],
[""],
[" let dapper = getAccount(", TransactionGenerationUtils.getAddressFromType(ftSchema!.type), ")"],
[" let dapperFTReceiver = dapper.getCapability<&{FungibleToken.Receiver}>(", ftSchema!.publicPath, ")!"],
[""],
[" // Create a new Forwarder resource for FUT and store it in the new account's storage"],
[" let ftForwarder <- TokenForwarding.createNewForwarder(recipient: dapperFTReceiver)"],
[" seller.save(<-ftForwarder, to: ", ftSchema!.receiverStoragePath ?? "", ")"],
[""],
[" // Publish a Receiver capability for the new account, which is linked to the FUT Forwarder"],
[" seller.link<&", ftSchema!.contractName, ".Vault{FungibleToken.Receiver}>("],
[" ", ftSchema!.publicPath, ","],
[" target: ", ftSchema!.receiverStoragePath ?? "", ""],
[" let nftPrivateCollectionPath = ", nftSchema!.privatePath, ""],
[""],
[" // ************************* Handling of DUC Recevier *************************** //"],
[""],
[" // Fetch the capability of the universal DUC receiver"],
[" // Note - Below address only works for the Testnet transaction, For mainnet it needs to change."],
[" let recipient = getAccount(", TransactionGenerationUtils.getAddressFromType(ftSchema!.type), ").getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)"],
[" assert(recipient.borrow() != nil, message: \"Missing or mis-typed Fungible Token receiver for the DUC recipient\")"],
[""],
[" // Check whether the receiver has the capability to receive the DUC"],
[" self.sellerPaymentReceiver = seller.getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)"],
[" if self.sellerPaymentReceiver.borrow() == nil || !self.sellerPaymentReceiver.borrow()!.isInstance(Type<@TokenForwarding.Forwarder>()) {"],
[" seller.unlink(/public/dapperUtilityCoinReceiver)"],
[" // Create the forwarder and save it to the account that is doing the forwarding"],
[" let vault <- TokenForwarding.createNewForwarder(recipient: recipient)"],
[" seller.save(<-vault, to: /storage/ducTokenForwarder)"],
[" // Link the new forwarding receiver capability"],
[" seller.link<&{FungibleToken.Receiver}>("],
[" /public/dapperUtilityCoinReceiver,"],
[" target: /storage/ducTokenForwarder"],
[" )"],
[" self.sellerPaymentReceiver = seller.getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)"],
[" }"],
[""],
[" // Get a reference to the receiver that will receive the fungible tokens if the sale executes."],
[" // Note that the sales receiver aka MerchantAddress should be an account owned by Dapper or an end-user Dapper Wallet account address."],
[" self.sellerPaymentReceiver = getAccount(seller.address).getCapability<&{FungibleToken.Receiver}>(", ftSchema!.publicPath, ")"],
[" assert(self.sellerPaymentReceiver.borrow() != nil, message: \"Missing or mis-typed DapperUtilityCoin receiver\")"],
[""],
[" // If the user does not have their collection linked to their account, link it."],
[" if seller.borrow<&", nftSchema!.contractName, ".Collection>(from: ", nftSchema!.storagePath, ") == nil {"],
[" let collection <- ", nftSchema!.contractName, ".createEmptyCollection()"],
[" seller.save(<-collection, to: ", nftSchema!.storagePath, ")"],
[" }"],
[" if (seller.getCapability<&", nftPublicLink, ">(", nftSchema!.publicPath, ").borrow() == nil) {"],
[" seller.unlink(", nftSchema!.publicPath, ")"],
[" seller.link<&", nftPublicLink, ">(", nftSchema!.publicPath, ", target: ", nftSchema!.storagePath, ")"],
[" // Validate the marketplaces capability before submiting to 'createListing'."],
[" for mp in marketplaceAddresses {"],
[" let marketplaceReceiver = getAccount(mp).getCapability<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)"],
[" assert(marketplaceReceiver.borrow() != nil && marketplaceReceiver.borrow()!.isInstance(Type<@TokenForwarding.Forwarder>()), message: \"Marketplaces does not posses the valid receiver type for DUC\")"],
[" self.marketplacesCapability.append(marketplaceReceiver)"],
[" }"],
[""],
[" if (seller.getCapability<&", nftPrivateLink, ">(", nftSchema!.privatePath, ").borrow() == nil) {"],
[" seller.unlink(", nftSchema!.privatePath, ")"],
[" seller.link<&", nftPrivateLink, ">(", nftSchema!.privatePath, ", target: ", nftSchema!.storagePath, ")"],
[" }"],
[" // *************************** Seller account interactions *************************** //"],
[""],
[" self.nftProvider = seller.getCapability<&", nftSchema!.contractName, ".Collection{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(", nftSchema!.privatePath, ")!"],
[" assert(self.nftProvider.borrow() != nil, message: \"Missing or mis-typed collection provider\")"],
[" // This checks for the public capability"],
[" if !seller.getCapability<&", nftPublicLink, ">(", nftSchema!.contractName, ".CollectionPublicPath)!.check() {"],
[" seller.unlink(", nftSchema!.contractName, ".CollectionPublicPath)"],
[" seller.link<&{", nftPublicLink, ">(", nftSchema!.contractName, ".CollectionPublicPath, target: ", nftSchema!.contractName, ".CollectionStoragePath)"],
[" }"],
[""],
[" if seller.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil {"],
[" // Create a new empty Storefront"],
[" let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront"],
[" // save it to the account"],
[" seller.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath)"],
[" // create a public capability for the Storefront"],
[" seller.link<&NFTStorefrontV2.Storefront{NFTStorefrontV2.StorefrontPublic}>(NFTStorefrontV2.StorefrontPublicPath, target: NFTStorefrontV2.StorefrontStoragePath)"],
[" // Check if the Provider capability exists or not if 'no' then create a new link for the same."],
[" if !seller.getCapability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(nftPrivateCollectionPath)!.check() {"],
[" seller.unlink(nftPrivateCollectionPath)"],
[" seller.link<&{", nftPrivateLink, ">(nftPrivateCollectionPath, target: ", nftSchema!.contractName, ".CollectionStoragePath)"],
[" }"],
[" self.storefront = seller.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath)"],
[" ?? panic(\"Missing or mis-typed NFTStorefront Storefront\")"],
[""],
[" "],
[" self.nftProvider = seller.getCapability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>(nftPrivateCollectionPath)!"],
[" let collectionRef = seller"],
[" .getCapability(", nftSchema!.publicPath, ")"],
[" .borrow<&", nftPublicLink, ">()"],
[" ?? panic(\"Could not borrow a reference to the collection\")"],
[" var totalRoyaltyCut = 0.0"],
[" let effectiveSaleItemPrice = saleItemPrice - commissionAmount"],
[""],
[" let nft = collectionRef.borrowViewResolver(id: saleItemID)! "],
[" if (nft.getViews().contains(Type<MetadataViews.Royalties>())) {"],
[" // Check whether the NFT implements the MetadataResolver or not."],
[" if nft.getViews().contains(Type<MetadataViews.Royalties>()) {"],
[" let royaltiesRef = nft.resolveView(Type<MetadataViews.Royalties>()) ?? panic(\"Unable to retrieve the royalties\")"],
[" let royalties = (royaltiesRef as! MetadataViews.Royalties).getRoyalties()"],
[" for royalty in royalties {"],
[" // TODO - Verify the type of the vault and it should exists"],
[" let royaltyReceiver = royalty.receiver"],
[" assert(royaltyReceiver.borrow() != nil && royaltyReceiver.borrow()!.isInstance(Type<@TokenForwarding.Forwarder>()), message: \"Royalty receiver does not has valid receiver type\")"],
[" self.saleCuts.append(NFTStorefrontV2.SaleCut(receiver: royalty.receiver, amount: royalty.cut * effectiveSaleItemPrice))"],
[" totalRoyaltyCut = totalRoyaltyCut + royalty.cut * effectiveSaleItemPrice"],
[" }"],
[" }"],
[""],
[" // Append the cut for the seller."],
[" self.saleCuts.append(NFTStorefrontV2.SaleCut("],
[" receiver: self.sellerPaymentReceiver,"],
[" amount: effectiveSaleItemPrice - totalRoyaltyCut"],
[" ))"],
[" assert(self.nftProvider.borrow() != nil, message: \"Missing or mis-typed ", nftSchema!.contractName, ".Collection provider\")"],
[""],
[" for marketplace in marketplacesAddress {"],
[" self.marketplacesCapability.append(getAccount(marketplace).getCapability<&{FungibleToken.Receiver}>(", ftSchema!.publicPath, "))"],
[" if seller.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath) == nil {"],
[" // Create a new empty Storefront"],
[" let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront"],
[" // save it to the account"],
[" seller.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath)"],
[" // create a public capability for the Storefront"],
[" seller.link<&NFTStorefrontV2.Storefront{NFTStorefrontV2.StorefrontPublic}>(NFTStorefrontV2.StorefrontPublicPath, target: NFTStorefrontV2.StorefrontStoragePath)"],
[" }"],
[" self.storefront = seller.borrow<&NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath)!"],
[" }"],
[""],
[" execute {"],
[""],
[" self.storefront.createListing("],
[" // Create listing"],
[" self.storefront.createListing("],
[" nftProviderCapability: self.nftProvider,"],
[" nftType: Type<@", TransactionGenerationUtils.createStaticTypeFromType(nftSchema!.type), ">(),"],
[" nftID: saleItemID,"],
Expand All @@ -630,6 +620,7 @@ let lines: [[String]] = [
[" )"],
[" }"],
["}"],
[""],
[""]]
var combinedLines: [String] = []
for line in lines {
Expand Down
2 changes: 1 addition & 1 deletion lib/CadenceToJson.json

Large diffs are not rendered by default.

Loading