TransactionSEALED

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

Transaction ID

Timestamp

Dec 01, 2025, 08:47:45 AM UTC
2mo ago

Block Height

134,562,473

Computation

0

Execution Fee

0.00001774 FLOW

Transaction Summary

Contract Call

Called OffersV2, DapperOffersV2, TopShot +4 more

Script Arguments

0amountUFix64
8.00000000
1royalties{Address
{
  "0xfaf0cc52c6e3acaf": "0.40000000"
}
2offerParamsString{String
{
  "resolver": "1",
  "setId": "164",
  "playId": "5736",
  "setUuid": "22a4cd21-ea46-4861-8139-848a17c21047",
  "playUuid": "70bad2a0-24b1-4a2b-84d2-ab339c89d92d"
}

Cadence Script

1import OffersV2 from 0xb8ea91944fd51c43
2import DapperOffersV2 from 0xb8ea91944fd51c43
3import TopShot from 0x0b2a3299cc857e29
4import NonFungibleToken from 0x1d7e57aa55817448
5import FungibleToken from 0xf233dcee88fe0abe
6import DapperUtilityCoin from 0xead892083b3e2c6c
7import Resolver from 0xb8ea91944fd51c43
8transaction(amount: UFix64, royalties: {Address:UFix64}, offerParamsString: {String:String}) {
9    var nftReceiver: Capability<&{NonFungibleToken.CollectionPublic}>?
10    let dapperOffer: auth(DapperOffersV2.Manager) &DapperOffersV2.DapperOffer
11    var ducVaultRef: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>
12    let resolverCapability: Capability<&{Resolver.ResolverPublic}>
13    var proxyManagerCap: Capability<auth(DapperOffersV2.ProxyManager) &DapperOffersV2.DapperOffer>
14    var ROYALITY_ADDRESS: Address
15    var ROYALITY_PERCENT: UFix64
16    prepare(signer: auth(Storage, Capabilities) &Account, dapper: auth(Storage, Capabilities) &Account) {
17        // Link the NFT collection
18        if signer.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) == nil {
19            let collection <- TopShot.createEmptyCollection(nftType: Type<@TopShot.NFT>())
20            signer.storage.save(<-collection, to: /storage/MomentCollection)
21            signer.capabilities.unpublish(/public/MomentCollection)
22            signer.capabilities.publish(
23                signer.capabilities.storage.issue<&TopShot.Collection>(/storage/MomentCollection),
24                at: /public/MomentCollection
25            )
26        }
27        self.nftReceiver = signer.capabilities.get<&{NonFungibleToken.CollectionPublic}>(/public/MomentCollection)
28        if self.nftReceiver == nil || !self.nftReceiver!.check() {
29            signer.capabilities.unpublish(/public/MomentCollection)
30            signer.capabilities.publish(
31                signer.capabilities.storage.issue<&{NonFungibleToken.CollectionPublic}>(/storage/MomentCollection),
32                at: /public/MomentCollection
33            )
34            self.nftReceiver = signer.capabilities.get<&{NonFungibleToken.CollectionPublic}>(/public/MomentCollection)
35        }
36        // Link the DapperOffer resource
37        if signer.storage.borrow<&DapperOffersV2.DapperOffer>(from: DapperOffersV2.DapperOffersStoragePath) == nil {
38            let dapperOffer <- DapperOffersV2.createDapperOffer() as! @DapperOffersV2.DapperOffer
39            signer.storage.save(<-dapperOffer, to: DapperOffersV2.DapperOffersStoragePath)
40            signer.capabilities.publish(
41                signer.capabilities.storage.issue<&DapperOffersV2.DapperOffer>(DapperOffersV2.DapperOffersStoragePath),
42                at: DapperOffersV2.DapperOffersPublicPath
43            )
44        }
45        // DapperOfferProxyManager Setup
46        if dapper.storage.borrow<&DapperOffersV2.DapperOffer>(from: DapperOffersV2.DapperOffersStoragePath) == nil {
47            let dapperOffer <- DapperOffersV2.createDapperOffer() as! @DapperOffersV2.DapperOffer
48            dapper.storage.save(<-dapperOffer, to: DapperOffersV2.DapperOffersStoragePath)
49            dapper.capabilities.publish(
50                dapper.capabilities.storage.issue<&DapperOffersV2.DapperOffer>(DapperOffersV2.DapperOffersStoragePath),
51                at: DapperOffersV2.DapperOffersPublicPath
52            )
53        }
54        // Setup Proxy Cancel for Dapper
55        let capabilityReceiver = dapper.capabilities.borrow
56            <&DapperOffersV2.DapperOffer>
57            (/public/DapperOffersV2) ?? panic("Could not borrow capability receiver reference")
58       
59
60        let dapperOfferProxyManagerCapPath = /storage/DapperOfferProxyManagerCapability
61        var dapperOfferProxyManagerCap = signer.storage.copy<Capability<auth(DapperOffersV2.ProxyManager) &DapperOffersV2.DapperOffer>>(from: dapperOfferProxyManagerCapPath)
62        if dapperOfferProxyManagerCap == nil || !dapperOfferProxyManagerCap!.check() {
63            self.proxyManagerCap = signer.capabilities.storage.issue<auth(DapperOffersV2.ProxyManager) &DapperOffersV2.DapperOffer>(DapperOffersV2.DapperOffersStoragePath)
64            // save capability to storage
65            signer.capabilities.storage.getController(byCapabilityID: self.proxyManagerCap.id)!.setTag("DapperOfferProxyManagerCap")
66            signer.storage.save(
67                self.proxyManagerCap,
68                to: dapperOfferProxyManagerCapPath
69            )
70        } else {
71            self.proxyManagerCap = dapperOfferProxyManagerCap!
72        }
73
74
75        capabilityReceiver.addProxyCapability(account: signer.address, cap: self.proxyManagerCap)
76        // Setup Proxy Cancel for Collection Minter
77        let minter = getAccount(0xe1f2a091f7bb5245)
78        let capabilityReceiverCollectionMinter = minter.capabilities.borrow
79            <&DapperOffersV2.DapperOffer>
80            (/public/DapperOffersV2) ?? panic("Could not borrow capability receiver reference")
81        capabilityReceiverCollectionMinter.addProxyCapability(account: signer.address, cap: self.proxyManagerCap)
82        // Get the capability to the offer creators NFT collection
83        self.nftReceiver = signer.capabilities.get<&{NonFungibleToken.CollectionPublic}>(/public/MomentCollection)
84        self.dapperOffer = signer.storage.borrow<auth(DapperOffersV2.Manager) &DapperOffersV2.DapperOffer>(from: DapperOffersV2.DapperOffersStoragePath)
85            ?? panic("Missing or mis-typed DapperOffersV2.DapperOffer")
86
87        let ducWithdrawCapPath = /storage/DUCWithdrawCapabilityForOffers
88        var ducWithdrawCap = dapper.storage.copy<Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>>(from: ducWithdrawCapPath)
89        if ducWithdrawCap == nil || !ducWithdrawCap!.check() {
90            self.ducVaultRef = dapper.capabilities.storage.issue<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(/storage/dapperUtilityCoinVault)
91            // save capability to storage
92            dapper.capabilities.storage.getController(byCapabilityID: self.ducVaultRef.id)!.setTag("OffersDUCWithdrawCap")
93            dapper.storage.save(
94                self.ducVaultRef,
95                to: ducWithdrawCapPath
96            )
97        } else{
98            self.ducVaultRef = ducWithdrawCap!
99        }
100
101        // Validate marketplace royalties
102        self.ROYALITY_ADDRESS = 0xfaf0cc52c6e3acaf
103        self.ROYALITY_PERCENT = 0.05000000
104        assert(royalties[self.ROYALITY_ADDRESS] == self.ROYALITY_PERCENT * amount, message: "Missing or mis-typed royalty information for marketplace")
105        assert(royalties.keys.length == 1, message: "please provide only one royalty address")
106        // Validate offerParamsString and resolver for type TopShotEdition
107        assert(offerParamsString.containsKey("playId"), message: "playId missing from offerParamsString")
108        assert(offerParamsString.containsKey("setId"), message: "setId missing from offerParamsString")
109        assert(offerParamsString.containsKey("resolver"), message: "resolver missing from offerParamsString")
110        assert(offerParamsString["resolver"] == Resolver.ResolverType.TopShotEdition.rawValue.toString(), message: "Invalid resolver")
111        // Setup and link offer Resolver
112        if signer.storage.borrow<&Resolver.OfferResolver>(from: /storage/OfferResolver) == nil {
113            let resolver <- Resolver.createResolver()
114            signer.storage.save(<-resolver, to: /storage/OfferResolver)
115            signer.capabilities.publish(
116                signer.capabilities.storage.issue<&Resolver.OfferResolver>(/storage/OfferResolver),
117                at: /public/OfferResolver
118            )
119        }
120        self.resolverCapability = signer.capabilities.get<&{Resolver.ResolverPublic}>(/public/OfferResolver)!
121    }
122    execute {
123        var royaltysList: [OffersV2.Royalty] = []
124        let keys = royalties.keys
125        for key in keys {
126            royaltysList.append(OffersV2.Royalty(
127                receiver: getAccount(key).capabilities.get<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver)!,
128                amount: royalties[key]!
129            ))
130        }
131        offerParamsString.insert(key: "typeId", "Type<@TopShot.NFT>()")
132
133        // Set offer type based on whether subeditionId is present
134        // Backend relies on this to identify and process subedition offers
135        if offerParamsString.containsKey("subeditionId") {
136            offerParamsString.insert(key: "_type", "TopShotSubedition")
137        } else {
138            offerParamsString.insert(key: "_type", "TopShotEdition")
139        }
140
141        self.dapperOffer.createOffer(
142            vaultRefCapability: self.ducVaultRef,
143            nftReceiverCapability: self.nftReceiver!,
144            nftType: Type<@TopShot.NFT>(),
145            amount: amount,
146            royalties: royaltysList,
147            offerParamsString: offerParamsString,
148            offerParamsUFix64: {},
149            offerParamsUInt64: {},
150            resolverCapability: self.resolverCapability
151        )
152    }
153}