Smart Contract

DriverzInsuranceStorefront

A.f887ece39166906e.DriverzInsuranceStorefront

Deployed

1d ago
Feb 26, 2026, 09:44:38 PM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import DriverzInsurance from 0xf887ece39166906e
4import DriverzAirdrop from 0xf887ece39166906e
5
6pub contract DriverzInsuranceStorefront {
7    
8    pub event DriverzInsuranceStorefrontInitialized()
9    pub event StorefrontInitialized(storefrontResourceID: UInt64)
10    pub event StorefrontDestroyed(storefrontResourceID: UInt64)
11    
12    pub event ListingAvailable(
13        storefrontAddress: Address,
14        listingResourceID: UInt64,
15        nftType: Type,
16        nftID: UInt64,
17        ftVaultType: Type,
18        price: UFix64
19    )
20
21    pub event ListingCompleted(
22        listingResourceID: UInt64, 
23        storefrontResourceID: UInt64, 
24        purchased: Bool,
25        nftType: Type,
26        nftID: UInt64
27    )
28
29    pub let StorefrontStoragePath: StoragePath
30    pub let StorefrontPublicPath: PublicPath
31
32    pub struct ListingDetails {
33
34        pub var storefrontID: UInt64
35        // Whether this listing has been purchased or not.
36        pub var purchased: Bool
37        // The Type of the NonFungibleToken.NFT that is being listed.
38        pub let nftType: Type
39        // The ID of the NFT within that type.
40        pub let nftID: UInt64
41        // The Type of the FungibleToken that payments must be made in.
42        pub let salePaymentVaultType: Type
43        // The amount that must be paid in the specified FungibleToken.
44        pub let salePrice: UFix64
45
46        pub let receiver: Capability<&{FungibleToken.Receiver}>
47
48        pub let discount: UFix64?
49
50        // setToPurchased
51        // Irreversibly set this listing as purchased.
52        //
53        access(contract) fun setToPurchased() {
54            self.purchased = true
55        }
56
57        // initializer
58        //
59        init (
60            nftType: Type,
61            nftID: UInt64,
62            salePaymentVaultType: Type,
63            storefrontID: UInt64,
64            salePrice: UFix64,
65            receiver: Capability<&{FungibleToken.Receiver}>,
66            discount: UFix64?
67        ) {
68            self.storefrontID = storefrontID
69            self.purchased = false
70            self.nftType = nftType
71            self.nftID = nftID
72            self.salePaymentVaultType = salePaymentVaultType
73            self.salePrice = salePrice
74            self.receiver = receiver
75            //Store the discounts
76            self.discount = discount
77        }
78    }
79
80
81    // ListingPublic
82    // An interface providing a useful public interface to a Listing.
83    //
84    pub resource interface ListingPublic {
85        // borrowNFT
86        // This will assert in the same way as the NFT standard borrowNFT()
87        // if the NFT is absent, for example if it has been sold via another listing.
88        //
89        pub fun borrowNFT(): &NonFungibleToken.NFT
90
91        // purchase
92        // Purchase the listing, buying the token.
93        // This pays the beneficiaries and returns the token to the buyer.
94        //
95        pub fun purchase(payment: @FungibleToken.Vault, collection: &NonFungibleToken.Collection{NonFungibleToken.Receiver})
96
97        // getDetails
98        //
99        pub fun getDetails(): ListingDetails
100
101    }
102
103
104    // Listing
105    // A resource that allows an NFT to be sold for an amount of a given FungibleToken,
106    // and for the proceeds of that sale to be split between several recipients.
107    pub resource Listing: ListingPublic {
108        // The simple (non-Capability, non-complex) details of the sale
109        access(self) let details: ListingDetails
110
111        access(contract) let nftProviderCapability: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
112
113        // borrowNFT
114        // This will assert in the same way as the NFT standard borrowNFT()
115        // if the NFT is absent, for example if it has been sold via another listing.
116        pub fun borrowNFT(): &NonFungibleToken.NFT {
117            let ref = self.nftProviderCapability.borrow()!.borrowNFT(id: self.getDetails().nftID)
118            //- CANNOT DO THIS IN PRECONDITION: "member of restricted type is not accessible: isInstance"
119            //  result.isInstance(self.getDetails().nftType): "token has wrong type"
120            assert(ref.isInstance(self.getDetails().nftType), message: "token has wrong type")
121            assert(ref.id == self.getDetails().nftID, message: "token has wrong ID")
122            return (ref as &NonFungibleToken.NFT?)!
123        }
124
125        // getDetails
126        // Get the details of the current state of the Listing as a struct.
127        pub fun getDetails(): ListingDetails {
128            return self.details
129        }
130        
131        // Purchase the listing, buying the token.
132        //The purchase function receives payment and capability from the collection to which the NFT is to be sent. 
133        //This checks the user's address and determines whether they are entitled to a discount
134        pub fun purchase(payment: @FungibleToken.Vault, collection: &NonFungibleToken.Collection{NonFungibleToken.Receiver}) {
135            pre {
136                self.details.purchased == false: "listing has already been purchased"
137                payment.isInstance(self.details.salePaymentVaultType): "payment vault is not requested fungible token"
138            }
139            
140            let buyerAddress: Address = collection.owner!.address
141            var finalSalePrice: UFix64 = 0.0
142
143            //Check if the account has a DriverzAirdrop collection. 
144            //If so, check if there are Driverz in it. 
145            //If account do not have driverz, the discount will not be applied.
146            if(getAccount(buyerAddress).getCapability<&{DriverzAirdrop.CollectionPublic}>(DriverzAirdrop.CollectionPublicPath).borrow() != nil){
147                let driverzCollection:&{DriverzAirdrop.CollectionPublic} = getAccount(buyerAddress).getCapability<&{DriverzAirdrop.CollectionPublic}>(DriverzAirdrop.CollectionPublicPath).borrow()!
148                
149                if(driverzCollection.getIDs().length > 0){
150                    let discountAmount: UFix64 = self.details.discount != nil ? self.details.discount! : 0.0
151                    finalSalePrice = self.details.salePrice - discountAmount
152                } else {
153                    finalSalePrice = self.details.salePrice
154                }
155            } else {
156                finalSalePrice = self.details.salePrice
157            }
158
159            //Check if the payment is the same amount as the listed NFT amount price for this address
160            if(payment.balance != finalSalePrice){
161                panic("Payment vault does not contain requested price, Driverz owners = $80 and non-owners = $100")
162            }
163
164            // Fetch the token to return to the purchaser.
165            let nft <-self.nftProviderCapability.borrow()!.withdraw(withdrawID: self.details.nftID)
166            // Therefore we cannot trust the Collection resource behind the interface,
167            // and we must check the NFT resource it gives us to make sure that it is the correct one.
168            assert(nft.isInstance(self.details.nftType), message: "withdrawn NFT is not of specified type")
169            assert(nft.id == self.details.nftID, message: "withdrawn NFT does not have specified ID")
170
171            self.details.receiver.borrow()!.deposit(from: <-payment)
172
173            self.details.setToPurchased()
174            // If the listing is purchased, we regard it as completed here.
175            // Otherwise we regard it as completed in the destructor.        
176            emit ListingCompleted(
177                listingResourceID: self.uuid,
178                storefrontResourceID: self.details.storefrontID,
179                purchased: self.details.purchased,
180                nftType: self.details.nftType,
181                nftID: self.details.nftID
182            )
183
184            collection.deposit(token: <-nft)
185        }
186
187        // destructor
188        destroy () {
189            if !self.details.purchased {
190
191                emit ListingCompleted(
192                    listingResourceID: self.uuid,
193                    storefrontResourceID: self.details.storefrontID,
194                    purchased: self.details.purchased,
195                    nftType: self.details.nftType,
196                    nftID: self.details.nftID
197                )
198            }
199        }
200
201        // initializer
202        init (
203            nftProviderCapability: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>,
204            nftType: Type,
205            nftID: UInt64,
206            salePaymentVaultType: Type,
207            salePrice: UFix64,
208            storefrontID: UInt64,
209            receiver: Capability<&{FungibleToken.Receiver}>,
210            discount: UFix64?
211        ) {
212            // Store the sale information
213            self.details = ListingDetails(
214                nftType: nftType,
215                nftID: nftID,
216                salePaymentVaultType: salePaymentVaultType,
217                storefrontID: storefrontID,
218                salePrice: salePrice,
219                receiver: receiver,
220                discount: discount
221            )
222
223            // Store the NFT provider
224            self.nftProviderCapability = nftProviderCapability
225
226            // Check that the provider contains the NFT.
227            // We will check it again when the token is sold.
228            // We cannot move this into a function because initializers cannot call member functions.
229            let provider = self.nftProviderCapability.borrow()
230            assert(provider != nil, message: "cannot borrow nftProviderCapability")
231
232            // This will precondition assert if the token is not available.
233            let nft = provider!.borrowNFT(id: self.details.nftID)
234            assert(nft.isInstance(self.details.nftType), message: "token is not of specified type")
235            assert(nft.id == self.details.nftID, message: "token does not have specified ID")
236        }
237    }
238
239    // StorefrontManager
240    // An interface for adding and removing Listings within a Storefront,
241    // intended for use by the Storefront's own
242    //
243    pub resource interface StorefrontManager {
244        // createListing
245        // Allows the Storefront owner to create and insert Listings.
246        //
247        pub fun createListing(
248            nftProviderCapability: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>,
249            nftType: Type,
250            nftID: UInt64,
251            receiver: Capability<&{FungibleToken.Receiver}>,
252            salePaymentVaultType: Type,
253            salePrice: UFix64,
254            discount: UFix64?
255        ): UInt64
256        // removeListing
257        // Allows the Storefront owner to remove any sale listing, acepted or not.
258        //
259        pub fun removeListing(listingResourceID: UInt64)
260    }
261
262    // StorefrontPublic
263    // An interface to allow listing and borrowing Listings, and purchasing items via Listings
264    // in a Storefront.
265    //
266    pub resource interface StorefrontPublic {
267        pub fun getListingIDs(): [UInt64]
268        pub fun borrowListing(listingResourceID: UInt64): &Listing{ListingPublic}?
269        pub fun cleanup(listingResourceID: UInt64)
270   }
271
272    // Storefront
273    // A resource that allows its owner to manage a list of Listings, and anyone to interact with them
274    // in order to query their details and purchase the NFTs that they represent.
275    //
276    pub resource Storefront : StorefrontManager, StorefrontPublic {
277        // The dictionary of Listing uuids to Listing resources.
278        access(self) var listings: @{UInt64: Listing}
279
280        // insert
281        // Create and publish a Listing for an NFT.
282        //
283         pub fun createListing(
284            nftProviderCapability: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>,
285            nftType: Type,
286            nftID: UInt64,
287            receiver: Capability<&{FungibleToken.Receiver}>,
288            salePaymentVaultType: Type,
289            salePrice: UFix64,
290            discount: UFix64?
291         ): UInt64 {
292            let listing <- create Listing(
293                nftProviderCapability: nftProviderCapability,
294                nftType: nftType,
295                nftID: nftID,
296                salePaymentVaultType: salePaymentVaultType,
297                salePrice: salePrice,
298                storefrontID: self.uuid,
299                receiver: receiver,
300                discount: discount
301            )
302
303            let listingResourceID = listing.uuid
304            let listingPrice = listing.getDetails().salePrice
305
306            // Add the new listing to the dictionary.
307            let oldListing <- self.listings[listingResourceID] <- listing
308            // Note that oldListing will always be nil, but we have to handle it.
309
310            destroy oldListing
311
312            emit ListingAvailable(
313                storefrontAddress: self.owner?.address!,
314                listingResourceID: listingResourceID,
315                nftType: nftType,
316                nftID: nftID,
317                ftVaultType: salePaymentVaultType,
318                price: listingPrice
319            )
320
321            return listingResourceID
322        }
323        
324
325        // removeListing
326        // Remove a Listing that has not yet been purchased from the collection and destroy it.
327        //
328        pub fun removeListing(listingResourceID: UInt64) {
329            let listing <- self.listings.remove(key: listingResourceID)
330                ?? panic("missing Listing")
331    
332            // This will emit a ListingCompleted event.
333            destroy listing
334        }
335
336        // getListingIDs
337        // Returns an array of the Listing resource IDs that are in the collection
338        //
339        pub fun getListingIDs(): [UInt64] {
340            return self.listings.keys
341        }
342
343        // borrowSaleItem
344        // Returns a read-only view of the SaleItem for the given listingID if it is contained by this collection.
345        //
346        pub fun borrowListing(listingResourceID: UInt64): &Listing{ListingPublic}? {
347            if self.listings[listingResourceID] != nil {
348                return (&self.listings[listingResourceID] as &Listing{ListingPublic}?)
349            } else {
350                return nil
351            }
352        }
353
354        // cleanup
355        // Remove an listing *if* it has been purchased.
356        // Anyone can call, but at present it only benefits the account owner to do so.
357        // Kind purchasers can however call it if they like.
358        //
359        pub fun cleanup(listingResourceID: UInt64) {
360            pre {
361                self.listings[listingResourceID] != nil: "could not find listing with given id"
362            }
363
364            let listing <- self.listings.remove(key: listingResourceID)!
365            assert(listing.getDetails().purchased == true, message: "listing is not purchased, only admin can remove")
366            destroy listing
367        }
368
369        // destructor
370        //
371        destroy () {
372            destroy self.listings
373
374            // Let event consumers know that this storefront will no longer exist
375            emit StorefrontDestroyed(storefrontResourceID: self.uuid)
376        }
377
378        // constructor
379        //
380        init () {
381            self.listings <- {}
382
383            // Let event consumers know that this storefront exists
384            emit StorefrontInitialized(storefrontResourceID: self.uuid)
385        }
386    }
387
388    // createStorefront
389    // Make creating a Storefront publicly accessible.
390    //
391    pub fun createStorefront(): @Storefront {
392        return <-create Storefront()
393    }
394
395    init () {
396        self.StorefrontStoragePath = /storage/DriverzInsuranceStorefront
397        self.StorefrontPublicPath = /public/DriverzInsuranceStorefront
398
399        emit DriverzInsuranceStorefrontInitialized()
400    }
401}
402