MarketplaceSEALED

◇╲╱░○◇▓!▒^▓$●$&░?#▫?●░□○▒░$░▒▫$■▪~▒█▫▫%●■□*○╳~╱█╲▫◇^~*#%◆!%▫▪&○^

Transaction ID

Timestamp

Feb 12, 2026, 04:20:53 PM UTC
2w ago

Block Height

141,992,564

Computation

0

Execution Fee

0.00314 FLOW

Transaction Summary

Marketplace

Called FungibleToken, DapperUtilityCoin, TokenForwarding +7 more

Script Arguments

0nftContractNameString
TuneGONFT
1saleItemIdUInt64
472018
2saleItemPriceUFix64
15.00000000
3salePaymentTypeString
DUC
4feesReceivers[Address]
5feesPercentages[UFix64]
[
  "3.50000000"
]
6royaltiesReceivers[Address]
7royaltiesPercentages[UFix64]
[
  "5.00000000"
]
8signatureExpirationUInt64
1770913541797
9signatureString
53216d59dec984f23943f7a347563c8ec01054896a31e9c21a19bd00c4081d511248c68e93d4736b91eb6f5ed7a58705facd3e75f620f24a0fc9f64c1207be00
10expiryUInt64
1928698001

Cadence Script

1import FungibleToken from 0xf233dcee88fe0abe
2import DapperUtilityCoin from 0xead892083b3e2c6c
3import TokenForwarding from 0xe544175ee0461c4b
4import NonFungibleToken from 0x1d7e57aa55817448
5import TuneGONFT from 0xc6945445cdbefec9
6import TuneGO from 0x0d9bc5af3fc0c2e3
7import TicalUniverse from 0xfef48806337aabf1
8import NFTStorefrontV2 from 0x4eb8a10cb9f87357
9import MetadataViews from 0x1d7e57aa55817448
10import PackNFT from 0xc6945445cdbefec9
11
12// Dapper App: TuneGO
13
14access(all) fun getOrCreateNFTProviderCapability(account: auth(IssueStorageCapabilityController) &Account, contractName: String): Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}> {
15    let pathMapping: {String: StoragePath} = {
16        "TuneGONFT": TuneGONFT.CollectionStoragePath,
17        "PackNFT": PackNFT.CollectionStoragePath,
18        "TuneGO": TuneGO.CollectionStoragePath,
19        "TicalUniverse": TicalUniverse.CollectionStoragePath
20    }
21    let storagePath = pathMapping[contractName] ?? panic("Contract not supported")
22    let provider = account.capabilities.storage.issue<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>(storagePath)
23    assert(provider.check(), message: "Missing or mis-typed ".concat(contractName).concat(" provider"))
24    return provider
25}
26access(all) fun setupStorefrontV2(acc: auth(IssueStorageCapabilityController, PublishCapability, UnpublishCapability, Storage) &Account): auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront {
27  let type = acc.storage.type(at: NFTStorefrontV2.StorefrontStoragePath)
28  let targetType = Type<@NFTStorefrontV2.Storefront>()
29  // Backup invalid storefront
30  if type != nil && type != targetType {
31    let objectAddress = type!.identifier.slice(from: 2, upTo: 18)
32    let backupPath = StoragePath(identifier: "backupStorefrontV2_".concat(objectAddress))!
33    acc.storage.save<@AnyResource>(<-acc.storage.load<@AnyResource>(from: NFTStorefrontV2.StorefrontStoragePath)!, to: backupPath);
34  }
35  // Save and link correct storefront if needed
36  if type != targetType {
37    let storefront <- NFTStorefrontV2.createStorefront() as! @NFTStorefrontV2.Storefront
38    acc.storage.save(<-storefront, to: NFTStorefrontV2.StorefrontStoragePath)
39  }
40  let capability = acc.capabilities.get<&{NFTStorefrontV2.StorefrontPublic}>(NFTStorefrontV2.StorefrontPublicPath)
41  if capability.check() != true {
42      acc.capabilities.unpublish(NFTStorefrontV2.StorefrontPublicPath)
43      acc.capabilities.publish(
44          acc.capabilities.storage.issue<&{NFTStorefrontV2.StorefrontPublic}>(NFTStorefrontV2.StorefrontStoragePath),
45          at: NFTStorefrontV2.StorefrontPublicPath
46      )
47  }
48  return acc.storage.borrow<auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront>(from: NFTStorefrontV2.StorefrontStoragePath)
49              ?? panic("Missing or mis-typed NFTStorefrontV2 Storefront")
50}
51
52access(all) fun getNftType(_ contractName: String): Type {
53    let pathMapping: {String: Type} = {
54        "TuneGONFT": Type<@TuneGONFT.NFT>(),
55        "PackNFT": Type<@PackNFT.NFT>(),
56        "TuneGO": Type<@TuneGO.NFT>(),
57        "TicalUniverse": Type<@TicalUniverse.NFT>()
58    }
59    return pathMapping[contractName] ?? panic("Contract not supported")
60}
61
62access(all) fun getPaymentVaultType(paymentType: String): Type {
63    switch paymentType {
64        case "DUC":
65            return Type<@DapperUtilityCoin.Vault>()
66    }
67    panic("Payment type not supported")
68}
69
70access(all) fun getPaymentReceiverPublicPath(paymentType: String): PublicPath {
71    switch paymentType {
72        case "DUC":
73            return /public/dapperUtilityCoinReceiver
74    }
75    panic("Payment type not supported")
76}
77
78
79access(all) fun roundAmount(amount: UFix64): UFix64 {
80  return UFix64(Int((amount + 0.005) * 100.0)) / 100.0
81}
82
83transaction(
84    nftContractName: String,
85    saleItemId: UInt64,
86    saleItemPrice: UFix64,
87    salePaymentType: String,
88    feesReceivers: [Address],
89    feesPercentages: [UFix64],
90    royaltiesReceivers: [Address],
91    royaltiesPercentages: [UFix64],
92    signatureExpiration: UInt64,
93    signature: String,
94    expiry: UInt64
95) {
96    let paymentReceiver: Capability<&{FungibleToken.Receiver}>
97    let nftProviderCapability: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>
98    let storefront: auth(NFTStorefrontV2.CreateListing) &NFTStorefrontV2.Storefront
99    let saleCuts: [NFTStorefrontV2.SaleCut]
100
101    prepare(seller: auth(
102                        IssueStorageCapabilityController, BorrowValue, SaveValue, Storage,
103                        PublishCapability, UnpublishCapability,
104                        NFTStorefrontV2.CreateListing, NonFungibleToken.Withdraw
105                     ) &Account) {
106
107        assert(["DUC"].contains(salePaymentType), message: "Payment type not supported")
108        let shouldRound = salePaymentType == "DUC"
109
110        assert(!shouldRound || saleItemPrice == roundAmount(amount: saleItemPrice), message: "Invalid sale item price")
111        let paymentReceiverPublicPath = getPaymentReceiverPublicPath(paymentType: salePaymentType)
112
113        self.paymentReceiver = seller.capabilities.get<&{FungibleToken.Receiver}>(paymentReceiverPublicPath)!
114        assert(self.paymentReceiver.borrow() != nil, message: "Missing or mis-typed payment receiver")
115
116        self.nftProviderCapability = getOrCreateNFTProviderCapability(account: seller, contractName: nftContractName)
117        assert(self.nftProviderCapability.borrow() != nil, message: "Missing nft provider")
118
119        self.saleCuts = []
120
121        var feesSignedParam = ""
122        var totalFeesCutsAmount: UFix64 = 0.0
123        for index, receiver in feesReceivers {
124            let account = getAccount(receiver)
125            let percentage = feesPercentages[index]
126            let exactAmount = saleItemPrice * percentage / 100.0;
127            let amount = shouldRound ? roundAmount(amount: exactAmount) : exactAmount
128            let feeReceiver = account.capabilities.get<&{FungibleToken.Receiver}>(paymentReceiverPublicPath)!
129            assert(feeReceiver.borrow() != nil, message: "Missing or mis-typed fee receiver")
130
131            self.saleCuts.append(NFTStorefrontV2.SaleCut(
132                receiver: feeReceiver,
133                amount: amount
134            ))
135            totalFeesCutsAmount = totalFeesCutsAmount + amount;
136            feesSignedParam = feesSignedParam.concat(":").concat(receiver.toString()).concat(":").concat(percentage.toString())
137        }
138        var royaltiesSignedParam = ""
139        var totalRoyaltiesCutsAmount: UFix64 = 0.0
140        for index, receiver in royaltiesReceivers {
141            let account = getAccount(receiver)
142            let percentage = royaltiesPercentages[index]
143            let exactAmount = saleItemPrice * percentage / 100.0;
144            let amount = shouldRound ? roundAmount(amount: exactAmount) : exactAmount
145            let royaltyReceiver = account.capabilities.get<&{FungibleToken.Receiver}>(paymentReceiverPublicPath)!
146            assert(royaltyReceiver.borrow() != nil, message: "Missing or mis-typed royalty receiver")
147
148            self.saleCuts.append(NFTStorefrontV2.SaleCut(
149                receiver: royaltyReceiver,
150                amount: amount
151            ))
152            totalRoyaltiesCutsAmount = totalRoyaltiesCutsAmount + amount;
153            royaltiesSignedParam = royaltiesSignedParam.concat(":").concat(receiver.toString()).concat(":").concat(percentage.toString())
154        }
155        let totalExtraCutsAmount = totalRoyaltiesCutsAmount + totalFeesCutsAmount
156        assert(totalExtraCutsAmount < saleItemPrice, message: "Total royalties and fees cuts amount must be less than sale price")
157
158        self.saleCuts.append(NFTStorefrontV2.SaleCut(
159            receiver: self.paymentReceiver,
160            amount: saleItemPrice - totalExtraCutsAmount
161        ))
162        var totalCutsAmount: UFix64 = 0.0
163        for cut in self.saleCuts {
164            totalCutsAmount = totalCutsAmount + cut.amount
165        }
166        assert(totalCutsAmount == saleItemPrice, message: "Total cuts amount must equal sale price")
167
168        self.storefront = setupStorefrontV2(acc: seller)
169
170        let publicKey = PublicKey(
171            publicKey: "abb32fcb741284ba71cd6b795ac6e59be678481227df882d25aab9ef6ece0041fbced2323381646c14e031bc9d6744e9f3364e4ee05d8b900b0851117d2b95a9".decodeHex(),
172            signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
173        )
174
175        let signedData = nftContractName
176            .concat(":")
177            .concat(saleItemId.toString())
178            .concat(":")
179            .concat(saleItemPrice.toString())
180            .concat(":")
181            .concat(salePaymentType)
182            .concat(feesSignedParam)
183            .concat(royaltiesSignedParam)
184            .concat(":")
185            .concat(signatureExpiration.toString())
186
187        let isValidSignature = publicKey.verify(
188            signature: signature.decodeHex(),
189            signedData: signedData.utf8,
190            domainSeparationTag: "",
191            hashAlgorithm: HashAlgorithm.SHA3_256
192        )
193
194        assert(isValidSignature, message: "Invalid signature for message: ".concat(signedData))
195        assert(UInt64(getCurrentBlock().timestamp) <= signatureExpiration, message: "Signature expired")
196    }
197
198    execute {
199        self.storefront.createListing(
200            nftProviderCapability: self.nftProviderCapability,
201            nftType: getNftType(nftContractName),
202            nftID: saleItemId,
203            salePaymentVaultType: getPaymentVaultType(paymentType: salePaymentType),
204            saleCuts: self.saleCuts,
205            marketplacesCapability: nil,
206            customID: nil,
207            commissionAmount: 0.0,
208            expiry: expiry
209        )
210    }
211}