Smart Contract

FIND

A.097bafa4e0b48eef.FIND

Valid From

117,549,737

Deployed

3d ago
Feb 24, 2026, 06:34:26 AM UTC

Dependents

43 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FlowToken from 0x1654653399040a61
3import DapperUtilityCoin from 0xead892083b3e2c6c
4import Profile from 0x097bafa4e0b48eef
5import Debug from 0x097bafa4e0b48eef
6import Clock from 0x097bafa4e0b48eef
7import Sender from 0x097bafa4e0b48eef
8import ProfileCache from 0x097bafa4e0b48eef
9import FindUtils from 0x097bafa4e0b48eef
10import PublicPriceOracle from 0xec67451f8a58216a
11import BandOracle from 0x6801a6222ebf784a
12
13/*
14///FIND
15
16///Flow Integrated Name Directory - A naming service on flow,
17
18/// Lease a name in the network for as little as 5 USD a year, (4 characters cost 100, 3 cost 500)
19
20Taxonomy:
21
22- name: a textual description minimum 3 chars long that can be leased in FIND
23- profile: A Versus profile that represents a person, a name registed in FIND points to a profile
24- lease: a resource representing registering a name for a period of 1 year
25- leaseCollection: Collection of the leases an account holds
26- leaseStatus: FREE|TAKEN|LOCKED, a LOCKED lease can be reopend by the owner. A lease will be locked for 90 days before it is freed
27*/
28access(all) contract FIND {
29    //event when FT is sent
30    access(all) event FungibleTokenSent(from:Address, fromName:String?, name:String, toAddress:Address, message:String, tag:String, amount: UFix64, ftType:String)
31
32    /// An event to singla that there is a name in the network
33    access(all) event Name(name: String)
34
35    access(all) event AddonActivated(name: String, addon:String)
36
37    ///  Emitted when a name is registred in FIND
38    access(all) event Register(name: String, owner: Address, validUntil: UFix64, lockedUntil: UFix64)
39
40    /// Emitted when a name is moved to a new owner
41    access(all) event Moved(name: String, previousOwner: Address, newOwner: Address, validUntil: UFix64, lockedUntil: UFix64)
42
43    //store the network itself
44    access(all) let NetworkStoragePath: StoragePath
45
46    //store the leases you own
47    access(all) let LeaseStoragePath: StoragePath
48    access(all) let LeasePublicPath: PublicPath
49
50    access(all) fun getLeases() : &[NetworkLease] {
51        if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
52            return network.profiles.values
53        }
54        panic("Network is not set up")
55    }
56
57    //////////////////////////////////////////
58    // ORACLE
59    //////////////////////////////////////////
60    // Get the latest FLOW/USD price
61    //This uses the FLOW/USD increment.fi oracle
62    access(all) fun getLatestPrice(): UFix64 {
63        let lastResult = PublicPriceOracle.getLatestPrice(oracleAddr: self.getFlowUSDOracleAddress())
64        let lastBlockNum = PublicPriceOracle.getLatestBlockHeight(oracleAddr: self.getFlowUSDOracleAddress())
65
66        // Make sure the price is not expired
67        if getCurrentBlock().height - lastBlockNum > 2000 {
68            return FIND.getLatestPriceBand()
69        }
70
71        return lastResult
72    }
73
74    //this uses band oracle, we use this as backup
75    access(all) fun getLatestPriceBand(): UFix64 {
76
77        let acct = FIND.account
78        let vaultRef = acct.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Cannot borrow reference to signer's FLOW vault")
79        let payment <- vaultRef.withdraw(amount: BandOracle.getFee())
80        let baseSymbol="FLOW"
81        let quoteSymbol="USDC"
82        let quote =BandOracle.getReferenceData (baseSymbol: baseSymbol, quoteSymbol: quoteSymbol, payment: <- payment)
83
84        return quote.fixedPointRate
85    }
86
87
88
89    access(all) fun convertFLOWToUSD(_ amount: UFix64): UFix64 {
90        return amount * self.getLatestPrice()
91    }
92
93    access(all) fun convertUSDToFLOW(_ amount: UFix64): UFix64 {
94        return amount / self.getLatestPrice()
95    }
96
97    //////////////////////////////////////////
98    // HELPER FUNCTIONS
99    //////////////////////////////////////////
100
101    //These methods are basically just here for convenience
102
103    access(all) fun calculateCostInFlow(_ name:String) : UFix64 {
104        if !FIND.validateFindName(name) {
105            panic("A FIND name has to be lower-cased alphanumeric or dashes and between 3 and 16 characters")
106        }
107
108        if let network = FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
109            return self.convertUSDToFLOW(network.calculateCost(name))
110        } 
111        panic("Network is not set up")
112    }
113
114    access(all) fun calculateAddonCostInFlow(_ addon: String) : UFix64 {
115        let network=FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath)!
116
117        if !network.publicEnabled {
118            panic("Public registration is not enabled yet")
119        }
120
121        if network.addonPrices[addon] == nil {
122            panic("This addon is not available. addon : ".concat(addon))
123        }
124        var addonPrice = network.addonPrices[addon]!
125        let cost= FIND.convertUSDToFLOW(addonPrice)
126        return cost
127    }
128
129    /// Calculate the cost of an name
130    /// @param _ the name to calculate the cost for
131    access(all) fun calculateCost(_ name:String) : UFix64 {
132        if !FIND.validateFindName(name) {
133            panic("A FIND name has to be lower-cased alphanumeric or dashes and between 3 and 16 characters")
134        }
135
136        if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
137            return network.calculateCost(name)
138        }
139        panic("Network is not set up")
140    }
141
142    access(all) fun resolve(_ input:String) : Address? {
143
144        let trimmedInput = FIND.trimFindSuffix(input)
145
146        if FIND.validateFindName(trimmedInput) {
147            if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
148                return network.lookup(trimmedInput)?.owner?.address
149            }
150
151            return nil
152        }
153
154        var address=trimmedInput
155        if trimmedInput.utf8[1] == 120 {
156            address = trimmedInput.slice(from: 2, upTo: trimmedInput.length)
157        }
158        var r:UInt64 = 0
159        var bytes = address.decodeHex()
160
161        while bytes.length>0{
162            r = r  + (UInt64(bytes.removeFirst()) << UInt64(bytes.length * 8 ))
163        }
164
165        return Address(r)
166    }
167
168    /// Lookup the address registered for a name
169    access(all) fun lookupAddress(_ name:String): Address? {
170
171        let trimmedName = FIND.trimFindSuffix(name)
172
173        if !FIND.validateFindName(trimmedName) {
174            panic("A FIND name has to be lower-cased alphanumeric or dashes and between 3 and 16 characters")
175        }
176
177        if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
178            return network.lookup(trimmedName)?.owner?.address
179        }
180        return nil
181    }
182
183    /// Lookup the profile registered for a name
184    access(all) fun lookup(_ input:String): &{Profile.Public}? {
185        if let address = FIND.resolve(input) {
186            let account = getAccount(address)
187            return account.capabilities.borrow<&{Profile.Public}>(Profile.publicPath) 
188        }
189        return nil
190    }
191
192    access(all) fun reverseLookupFN() : fun(Address) : String? {
193        return fun(address:Address): String? {
194            return FIND.reverseLookup(address)
195        }
196    }
197
198    /// lookup if an address has a .find name, if it does pick either the default one or the first registered
199    access(all) fun reverseLookup(_ address:Address): String? {
200
201        let leaseNameCache = ProfileCache.getAddressLeaseName(address)
202
203        if leaseNameCache == nil {
204            let leaseOptCol = getAccount(address).capabilities.borrow<&{FIND.LeaseCollectionPublic}>(FIND.LeasePublicPath)
205
206            if leaseOptCol == nil {
207                return nil
208            }
209
210            let profileFindName= Profile.find(address).getFindName()
211
212            let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) ?? panic("Network is not set up")
213
214            if profileFindName != "" {
215                let status = network.readStatus(profileFindName)
216                if status.owner != nil && status.owner! == address {
217                    if status.status == FIND.LeaseStatus.TAKEN {
218                        ProfileCache.setAddressLeaseNameCache(address: address, leaseName: profileFindName, validUntil: network.getLeaseExpireTime(profileFindName))
219                        return profileFindName
220                    }
221                }
222            }
223
224            let leaseCol = leaseOptCol!
225            let nameLeases = leaseCol.getNames()
226            for nameLease in nameLeases {
227
228                //filter out all leases that are FREE or LOCKED since they are not actice
229                let status = network.readStatus(nameLease)
230                if status.owner != nil && status.owner! == address {
231                    if status.status == FIND.LeaseStatus.TAKEN {
232                        ProfileCache.setAddressLeaseNameCache(address: address, leaseName: nameLease, validUntil: network.getLeaseExpireTime(nameLease))
233                        return nameLease
234                    }
235                }
236            }
237            ProfileCache.setAddressLeaseNameCache(address: address, leaseName: nil, validUntil: UFix64.max)
238            return nil
239        } else if leaseNameCache! == "" {
240            // If empty string, return no find Name
241            return nil
242        }
243        return leaseNameCache!
244    }
245
246    /// Deposit FT to name
247    /// @param to: The name to send money too
248    /// @param message: The message to send
249    /// @param tag: The tag to add to the event
250    /// @param vault: The vault to send too
251    /// @param from: The sender that sent the funds
252    access(all) fun depositWithTagAndMessage(to:String, message:String, tag: String, vault: @{FungibleToken.Vault}, from: &Sender.Token){
253
254        let fromAddress= from.owner!.address
255        let maybeAddress = FIND.resolve(to)
256        if maybeAddress  == nil{
257            panic("Not a valid .find name or address")
258        }
259        let address=maybeAddress!
260
261        let account = getAccount(address)
262        if let profile = account.capabilities.borrow<&{Profile.Public}>(Profile.publicPath) {
263            emit FungibleTokenSent(from: fromAddress, fromName: FIND.reverseLookup(fromAddress), name: to, toAddress: profile.getAddress(), message:message, tag:tag, amount:vault.balance, ftType:vault.getType().identifier)
264            profile.deposit(from: <- vault)
265            return
266        }
267
268        var path = ""
269        if vault.getType() == Type<@FlowToken.Vault>() {
270            path ="flowTokenReceiver"
271        } else {
272            panic("Could not find a valid receiver for this vault type")
273        }
274        if path != "" {
275            emit FungibleTokenSent(from: fromAddress, fromName: FIND.reverseLookup(fromAddress), name: "", toAddress: address, message:message, tag:tag, amount:vault.balance, ftType:vault.getType().identifier)
276            account.capabilities.borrow<&{FungibleToken.Receiver}>(PublicPath(identifier: path)!)!.deposit(from: <- vault)
277            return
278        }
279        panic("Could not find a valid receiver for this vault type")
280
281    }
282
283
284    /// Deposit FT to name
285    /// @param to: The name to send money too
286    /// @param from: The vault to send too
287    access(all) fun deposit(to:String, from: @{FungibleToken.Vault}) {
288        if !FIND.validateFindName(to) {
289            panic("A FIND name has to be lower-cased alphanumeric or dashes and between 3 and 16 characters")
290        }
291
292        if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
293            let profile=network.lookup(to) ?? panic("could not find name")
294            profile.deposit(from: <- from)
295            return
296        }
297        panic("Network is not set up")
298    }
299
300    /// Return the status for a given name
301    /// @return The Name status of a name
302    access(all) fun status(_ name: String): NameStatus {
303        if !FIND.validateFindName(name) {
304            panic("A FIND name has to be lower-cased alphanumeric or dashes and between 3 and 16 characters")
305        }
306
307        if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
308            return network.readStatus(name)
309        }
310        panic("Network is not set up")
311    }
312
313
314    /// Struct holding information about a lease. Contains both the internal status the owner of the lease and if the state is persisted or not.
315    access(all) struct NameStatus{
316        access(all) let status: LeaseStatus
317        access(all) let owner: Address?
318
319        init(status:LeaseStatus, owner:Address?) {
320            self.status=status
321            self.owner=owner
322        }
323    }
324
325
326
327    /*
328    =============================================================
329    Lease is a collection/resource for storing the token leases
330    Also have a seperate Auction for tracking auctioning of leases
331    =============================================================
332    */
333
334    access(all) entitlement LeaseOwner
335
336    /*
337
338    Lease is a resource you get back when you register a lease.
339    You can use methods on it to renew the lease or to move to another profile
340    */
341    access(all) resource Lease {
342        access(contract) let name: String
343        access(contract) let networkCap: Capability<&Network>
344        access(contract) var addons: {String: Bool}
345
346        //These fields are here, but they are not in use anymore
347        access(contract) var salePrice: UFix64?
348        access(contract) var auctionStartPrice: UFix64?
349        access(contract) var auctionReservePrice: UFix64?
350        access(contract) var auctionDuration: UFix64
351        access(contract) var auctionMinBidIncrement: UFix64
352        access(contract) var auctionExtensionOnLateBid: UFix64
353        access(contract) var offerCallback: Capability<&BidCollection>?
354
355        init(name:String, networkCap: Capability<&Network>) {
356            self.name=name
357            self.networkCap= networkCap
358            self.salePrice=nil
359            self.auctionStartPrice=nil
360            self.auctionReservePrice=nil
361            self.auctionDuration=86400.0
362            self.auctionExtensionOnLateBid=300.0
363            self.auctionMinBidIncrement=10.0
364            self.offerCallback=nil
365            self.addons={}
366        }
367
368        access(all) fun getName() : String {
369            return self.name
370        }
371
372        access(all) fun getAddon() : [String] {
373            return self.addons.keys
374        }
375
376        access(all) fun checkAddon(addon: String) : Bool {
377            if !self.addons.containsKey(addon) {
378                return false
379            }
380            return self.addons[addon]!
381        }
382
383        access(contract) fun addAddon(_ addon:String) {
384            self.addons[addon]=true
385        }
386
387        access(LeaseOwner) fun extendLease(_ vault: @FlowToken.Vault) {
388            let network= self.networkCap.borrow() ?? panic("The network is not up")
389            network.renew(name: self.name, vault:<-  vault)
390        }
391
392        access(LeaseOwner) fun extendLeaseDapper(merchAccount: Address, vault: @DapperUtilityCoin.Vault) {
393            let network= self.networkCap.borrow() ?? panic("The network is not up")
394            network.renewDapper(merchAccount: merchAccount, name: self.name, vault:<-  vault)
395        }
396
397        access(contract) fun move(profile: Capability<&{Profile.Public}>) {
398            let network= self.networkCap.borrow() ?? panic("The network is not up")
399            let senderAddress= network.profiles[self.name]!.profile.address
400            network.move(name: self.name, profile: profile)
401
402
403            // set FindNames
404            // receiver
405            let receiver = profile.borrow() ?? panic("The profile capability is invalid")
406            if receiver.getFindName() == "" {
407                receiver.setFindName(self.name)
408            }
409
410            // sender
411            let sender = Profile.find(senderAddress)
412            if sender.getFindName() == self.name {
413                let network = FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) ?? panic("Network is not set up")
414                let leaseCol = getAccount(senderAddress).capabilities.borrow<&{FIND.LeaseCollectionPublic}>(FIND.LeasePublicPath) ?? panic("Could not borrow lease collection")
415
416                let nameLeases = leaseCol.getNames()
417                for nameLease in nameLeases {
418
419                    //filter out all leases that are FREE or LOCKED since they are not actice
420                    let status = network.readStatus(nameLease)
421                    if status.owner != nil && status.owner! == senderAddress {
422                        if status.status == FIND.LeaseStatus.TAKEN {
423                            sender.setFindName(nameLease)
424                            return
425                        }
426                    }
427
428                }
429                sender.setFindName("")
430            }
431        }
432
433        access(all) fun getLeaseExpireTime() : UFix64 {
434            let network = self.networkCap.borrow() ?? panic("The network is not up")
435            return network.getLeaseExpireTime(self.name)
436        }
437
438        access(all) fun getLeaseLockedUntil() : UFix64 {
439            let network = self.networkCap.borrow() ?? panic("The network is not up")
440            return network.getLeaseLockedUntil(self.name)
441        }
442
443        access(all) fun getProfile():&{Profile.Public}? {
444            let network = self.networkCap.borrow() ?? panic("The network is not up")
445            return network.profile(self.name)
446        }
447
448        access(all) fun getLeaseStatus() : LeaseStatus {
449            return FIND.status(self.name).status
450        }
451
452        access(all) fun validate() : Bool {
453            // if network is not there anymore, it is not valid
454            if !self.networkCap.check() {
455                return false
456            }
457            let network = self.networkCap.borrow()!
458            let lease = network.getLease(self.name)
459            // if the network lease is nil, it is definitely not validated
460            if lease == nil {
461                Debug.log("no lease")
462                return false
463            }
464
465            // regardless of the status (FREE / LOCKED / TAKEN)
466            //TODO: these comments here are wrong... 
467            // (because other functions checks that) 
468            // if this lease is not the current / latest owner, this lease is not valid anymore
469            let registeredOwner = lease!.profile.address
470            if registeredOwner == self.owner?.address {
471                if lease!.status() == LeaseStatus.FREE {
472                    return false
473                }
474                return true
475            }
476
477            return false
478        }
479    }
480
481
482
483    //struct to expose information about leases
484    access(all) struct LeaseInformation {
485        access(all) let name: String
486        access(all) let address: Address
487        access(all) let cost: UFix64
488        access(all) let status: String
489        access(all) let validUntil: UFix64
490        access(all) let lockedUntil: UFix64
491        access(all) let latestBid: UFix64?
492        access(all) let auctionEnds: UFix64?
493        access(all) let salePrice: UFix64?
494        access(all) let latestBidBy: Address?
495        access(all) let currentTime: UFix64
496        access(all) let auctionStartPrice: UFix64?
497        access(all) let auctionReservePrice: UFix64?
498        access(all) let extensionOnLateBid: UFix64?
499        access(all) let addons: [String]
500
501        init(name: String, status:LeaseStatus, validUntil: UFix64, lockedUntil:UFix64, latestBid: UFix64?, auctionEnds: UFix64?, salePrice: UFix64?, latestBidBy: Address?, auctionStartPrice: UFix64?, auctionReservePrice: UFix64?, extensionOnLateBid:UFix64?, address:Address, addons: [String]){
502
503            self.name=name
504            var s="TAKEN"
505            if status == LeaseStatus.FREE {
506                s="FREE"
507            } else if status == LeaseStatus.LOCKED {
508                s="LOCKED"
509            }
510            self.status=s
511            self.validUntil=validUntil
512            self.lockedUntil=lockedUntil
513            self.latestBid=latestBid
514            self.latestBidBy=latestBidBy
515            self.auctionEnds=auctionEnds
516            self.salePrice=salePrice
517            self.currentTime=Clock.time()
518            self.auctionStartPrice=auctionStartPrice
519            self.auctionReservePrice=auctionReservePrice
520            self.extensionOnLateBid=extensionOnLateBid
521            self.address=address
522            self.cost=FIND.calculateCost(name)
523            self.addons=addons
524
525        }
526        access(all) fun getAddons() : [String] {
527            return self.addons
528        }
529
530    }
531    /*
532    Since a single account can own more then one name there is a collecition of them
533    This collection has build in support for direct sale of a FIND leaseToken. The network owner till take 2.5% cut
534    */
535    access(all) resource interface LeaseCollectionPublic {
536        //fetch all the tokens in the collection
537        access(all) fun getLeases(): [String]
538        access(all) fun getInvalidatedLeases(): [String]
539        //fetch all names that are for sale
540        access(all) fun getLeaseInformation() : [LeaseInformation]
541        access(all) fun getLease(_ name: String) :LeaseInformation?
542
543        //add a new lease token to the collection, can only be called in this contract
544        access(contract) fun deposit(token: @FIND.Lease)
545
546        access(all) fun buyAddon(name:String, addon: String, vault: @FlowToken.Vault)
547        access(all) fun buyAddonDapper(merchAccount: Address, name:String, addon:String, vault: @DapperUtilityCoin.Vault)
548        access(account) fun adminAddAddon(name:String, addon: String)
549        access(all) fun getAddon(name:String) : [String]
550        access(all) fun checkAddon(name:String, addon: String) : Bool
551        access(account) fun getNames() : [String]
552        access(account) fun containsName(_ name: String) : Bool
553        access(LeaseOwner) fun move(name: String, profile: Capability<&{Profile.Public}>, to: Capability<&LeaseCollection>)
554        access(all) fun getLeaseUUID(_ name: String) : UInt64
555    }
556
557    access(all) resource LeaseCollection: LeaseCollectionPublic {
558        // dictionary of NFT conforming tokens
559        // NFT is a resource type with an `UInt64` ID field
560        access(contract) var leases: @{String: FIND.Lease}
561        access(contract) var auctions: @{String: Auction}
562
563        //the cut the network will take, default 2.5%
564        access(contract) let networkCut: UFix64
565
566        //the wallet of the network to transfer royalty to
567        access(contract) let networkWallet: Capability<&{FungibleToken.Receiver}>
568
569        init (networkCut: UFix64, networkWallet: Capability<&{FungibleToken.Receiver}>) {
570            self.leases <- {}
571            self.auctions <- {}
572            self.networkCut=networkCut
573            self.networkWallet=networkWallet
574        }
575
576        access(all) fun buyAddon(name:String, addon:String, vault: @FlowToken.Vault)  {
577            if !self.leases.containsKey(name) {
578                panic("Invalid name=".concat(name))
579            }
580            let cost=FIND.calculateAddonCostInFlow(addon)
581
582            let lease = self.borrowAuth(name)
583
584            if !lease.validate() {
585                panic("This is not a valid lease. Lease already expires and some other user registered it. Lease : ".concat(name))
586            }
587
588            if lease.addons.containsKey(addon) {
589                panic("You already have this addon : ".concat(addon))
590            }
591
592            if vault.balance != cost {
593                panic("Expect ".concat(cost.toString()).concat(" FLOW for ").concat(addon).concat(" addon"))
594            }
595
596            lease.addAddon(addon)
597
598            //put something in your storage
599            emit AddonActivated(name: name, addon: addon)
600            let networkWallet = self.networkWallet.borrow() ?? panic("The network is not up")
601            networkWallet.deposit(from: <- vault)
602        }
603
604        access(all) fun buyAddonDapper(merchAccount: Address, name:String, addon:String, vault: @DapperUtilityCoin.Vault)  {
605            FIND.checkMerchantAddress(merchAccount)
606
607            if !self.leases.containsKey(name) {
608                panic("Invalid name=".concat(name))
609            }
610
611            let network=FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath)!
612
613            if !network.publicEnabled {
614                panic("Public registration is not enabled yet")
615            }
616
617            if network.addonPrices[addon] == nil {
618                panic("This addon is not available. addon : ".concat(addon))
619            }
620            let addonPrice = network.addonPrices[addon]!
621
622            let lease = self.borrowAuth(name)
623
624            if !lease.validate() {
625                panic("This is not a valid lease. Lease already expires and some other user registered it. Lease : ".concat(name))
626            }
627
628            if lease.addons.containsKey(addon) {
629                panic("You already have this addon : ".concat(addon))
630            }
631
632            if vault.balance != addonPrice {
633                panic("Expect ".concat(addonPrice.toString()).concat(" Dapper Credit for ").concat(addon).concat(" addon"))
634            }
635
636            lease.addAddon(addon)
637
638            //put something in your storage
639            emit AddonActivated(name: name, addon: addon)
640
641            // This is here just to check if the network is up
642            let networkWallet = self.networkWallet.borrow() ?? panic("The network is not up")
643
644            let wallet = getAccount(merchAccount).capabilities.borrow<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver) ?? panic("Cannot borrow reference to Dapper Merch Account receiver. Address : ".concat(merchAccount.toString()))
645            wallet.deposit(from: <- vault)
646        }
647
648        access(account) fun adminAddAddon(name:String, addon:String)  {
649            if !self.leases.containsKey(name) {
650                panic("Invalid name=".concat(name))
651            }
652
653            let network=FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath)!
654
655            if !network.publicEnabled {
656                panic("Public registration is not enabled yet")
657            }
658
659            if network.addonPrices[addon] == nil {
660                panic("This addon is not available. addon : ".concat(addon))
661            }
662            let addonPrice = network.addonPrices[addon]!
663
664            let lease = self.borrowAuth(name)
665
666            if !lease.validate() {
667                panic("This is not a valid lease. Lease already expires and some other user registered it. Lease : ".concat(name))
668            }
669
670            if lease.addons.containsKey(addon) {
671                panic("You already have this addon : ".concat(addon))
672            }
673
674            lease.addAddon(addon)
675
676            //put something in your storage
677            emit AddonActivated(name: name, addon: addon)
678        }
679
680        access(all) fun getAddon(name: String) : [String] {
681            let lease = self.borrowAuth(name)
682            if !lease.validate() {
683                return []
684            }
685            return lease.getAddon()
686        }
687
688        access(all) fun checkAddon(name:String, addon: String) : Bool {
689            let lease = self.borrowAuth(name)
690            if !lease.validate() {
691                return false
692            }
693            return lease.checkAddon(addon: addon)
694        }
695
696        access(all) fun getLeaseUUID(_ name: String) : UInt64 {
697            return self.borrowAuth(name).uuid
698        }
699
700        access(all) fun getLease(_ name: String) : LeaseInformation? {
701            if !self.leases.containsKey(name) {
702                return nil
703            }
704            let token=self.borrowAuth(name)
705
706            if !token.validate() {
707                return nil
708            }
709
710
711            var latestBid: UFix64? = nil
712            var auctionEnds: UFix64?= nil
713            var latestBidBy: Address?=nil
714
715            return LeaseInformation(name:  name, status: token.getLeaseStatus(), validUntil: token.getLeaseExpireTime(), lockedUntil: token.getLeaseLockedUntil(), latestBid: latestBid, auctionEnds: auctionEnds, salePrice: token.salePrice, latestBidBy: latestBidBy, auctionStartPrice: token.auctionStartPrice, auctionReservePrice: token.auctionReservePrice, extensionOnLateBid: token.auctionExtensionOnLateBid, address: token.owner!.address, addons: token.getAddon())
716        }
717
718        access(account) fun getNames() : [String] {
719            return self.leases.keys
720        }
721
722        access(account) fun containsName(_ name: String) : Bool {
723            return self.leases.containsKey(name)
724        }
725
726        access(all) fun getLeaseInformation() : [LeaseInformation]  {
727            var info: [LeaseInformation]=[]
728            for name in self.leases.keys {
729                // if !FIND.validateFindName(name) {
730                // 	continue
731                // }
732                let lease=self.getLease(name)
733                if lease != nil && lease!.status != "FREE" {
734                    info.append(lease!)
735                }
736            }
737            return info
738        }
739
740
741        access(LeaseOwner) fun move(name: String, profile: Capability<&{Profile.Public}>, to: Capability<&LeaseCollection>) {
742
743            let lease = self.borrowAuth(name)
744            if !lease.validate() {
745                panic("This is not a valid lease. Lease already expires and some other user registered it. Lease : ".concat(name))
746            }
747
748            let token <- self.leases.remove(key:  name) ?? panic("missing NFT")
749            emit Moved(name: name, previousOwner:self.owner!.address, newOwner: profile.address, validUntil: token.getLeaseExpireTime(), lockedUntil: token.getLeaseLockedUntil())
750            token.move(profile: profile)
751            let walletRef = to.borrow() ?? panic("The receiver capability is not valid. wallet address : ".concat(to.address.toString()))
752            walletRef.deposit(token: <- token)
753
754        }
755
756        //depoit a lease token into the lease collection, not available from the outside
757        access(contract) fun deposit(token: @FIND.Lease) {
758            // add the new token to the dictionary which removes the old one
759            let oldToken <- self.leases[token.name] <- token
760
761            destroy oldToken
762        }
763
764        // getIDs returns an array of the IDs that are in the collection
765        access(all) fun getLeases(): [String] {
766            var list : [String] = []
767            for key in  self.leases.keys {
768                let lease = self.borrow(key)
769                if !lease.validate() {
770                    continue
771                }
772                list.append(key)
773            }
774            return list
775        }
776
777        access(all) fun getInvalidatedLeases(): [String] {
778            var list : [String] = []
779            for key in  self.leases.keys {
780                let lease = self.borrow(key)
781                if lease.validate() {
782                    continue
783                }
784                list.append(key)
785            }
786            return list
787        }
788
789        // borrowNFT gets a reference to an NFT in the collection
790        // so that the caller can read its metadata and call its methods
791        access(all) fun borrow(_ name: String): &FIND.Lease {
792            return (&self.leases[name])!
793        }
794
795        access(LeaseOwner) fun borrowAuth(_ name: String): auth(LeaseOwner) &FIND.Lease {
796            return (&self.leases[name])!
797        }
798
799        //borrow the auction
800        access(all) fun borrowAuction(_ name: String): &FIND.Auction {
801            return (&self.auctions[name])!
802        }
803
804
805        //This has to be here since you can only get this from a auth account and thus we ensure that you cannot use wrong paths
806        access(LeaseOwner) fun register(name: String, vault: @FlowToken.Vault){
807            let profileCap = self.owner!.capabilities.get<&{Profile.Public}>(Profile.publicPath)
808            let leases= self.owner!.capabilities.get<&{FIND.LeaseCollectionPublic}>(FIND.LeasePublicPath)
809
810            let network=FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath)!
811
812            if !network.publicEnabled {
813                panic("Public registration is not enabled yet")
814            }
815
816            network.register(name:name, vault: <- vault, profile: profileCap, leases: leases)
817        }
818
819        //This has to be here since you can only get this from a auth account and thus we ensure that you cannot use wrong paths
820        access(LeaseOwner) fun registerDapper(merchAccount: Address, name: String, vault: @DapperUtilityCoin.Vault){
821            let profileCap = self.owner!.capabilities.get<&{Profile.Public}>(Profile.publicPath)
822            let leases= self.owner!.capabilities.get<&{FIND.LeaseCollectionPublic}>(FIND.LeasePublicPath)
823
824            let network=FIND.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath)!
825
826            if !network.publicEnabled {
827                panic("Public registration is not enabled yet")
828            }
829
830            network.registerDapper(merchAccount: merchAccount, name:name, vault: <- vault, profile: profileCap, leases: leases)
831        }
832
833        access(LeaseOwner) fun cleanUpInvalidatedLease(_ name: String) {
834            let lease = self.borrowAuth(name)
835            if lease.validate() {
836                panic("This is a valid lease. You cannot clean this up. Lease : ".concat(name))
837            }
838            destroy <- self.leases.remove(key: name)!
839        }
840    }
841
842    //Create an empty lease collection that store your leases to a name
843    access(all) fun createEmptyLeaseCollection(): @FIND.LeaseCollection {
844        if let network = self.account.storage.borrow<&Network>(from: FIND.NetworkStoragePath) {
845            return <- create LeaseCollection(networkCut:network.secondaryCut, networkWallet: network.wallet)
846        }
847        panic("Network is not set up")
848    }
849
850
851
852    /*
853    Core network things
854    //===================================================================================================================
855    */
856    //a struct that represents a lease of a name in the network.
857    access(all) struct NetworkLease {
858        access(all) let registeredTime: UFix64
859        access(all) var validUntil: UFix64
860        access(all) var lockedUntil: UFix64
861        access(all) var profile: Capability<&{Profile.Public}>
862        // This address is wrong for some account and can never be refered
863        access(all) var address: Address
864        access(all) var name: String
865
866        init( validUntil:UFix64, lockedUntil:UFix64, profile: Capability<&{Profile.Public}>, name: String) {
867            self.validUntil=validUntil
868            self.lockedUntil=lockedUntil
869            self.registeredTime=Clock.time()
870            self.profile=profile
871            self.address= profile.address
872            self.name=name
873        }
874
875        access(all) fun setValidUntil(_ unit: UFix64) {
876            self.validUntil=unit
877        }
878
879        access(all) fun setLockedUntil(_ unit: UFix64) {
880            self.lockedUntil=unit
881        }
882
883        access(all) fun status() : LeaseStatus {
884            let time=Clock.time()
885
886            if time >= self.lockedUntil {
887                return LeaseStatus.FREE
888            }
889
890            if time >= self.validUntil {
891                return LeaseStatus.LOCKED
892            }
893            return 	LeaseStatus.TAKEN
894        }
895
896        access(all) fun setProfile (_ profile: Capability<&{Profile.Public}>) {
897            self.profile=profile
898        }
899    }
900
901
902    /*
903    FREE, does not exist in profiles dictionary
904    TAKEN, registered with a time that is currentTime + leasePeriod
905    LOCKED, after TAKEN.time you will get a new  status and the new time will be
906
907    */
908
909    access(all) enum LeaseStatus: UInt8 {
910        access(all) case FREE
911        access(all) case TAKEN
912        access(all) case LOCKED
913    }
914
915    /*
916    The main network resource that holds the state of the names in the network
917    */
918    access(all) resource Network {
919        access(contract) var wallet: Capability<&{FungibleToken.Receiver}>
920        access(contract) let leasePeriod: UFix64
921        access(contract) let lockPeriod: UFix64
922        access(contract) var defaultPrice: UFix64
923        access(contract) let secondaryCut: UFix64
924        access(contract) var pricesChangedAt: UFix64
925        access(contract) var lengthPrices: {Int: UFix64}
926        access(contract) var addonPrices: {String: UFix64}
927
928        access(contract) var publicEnabled: Bool
929
930        //map from name to lease for that name
931        access(contract) let profiles: { String: NetworkLease}
932
933        init(leasePeriod: UFix64, lockPeriod: UFix64, secondaryCut: UFix64, defaultPrice: UFix64, lengthPrices: {Int:UFix64}, wallet:Capability<&{FungibleToken.Receiver}>, publicEnabled:Bool) {
934            self.leasePeriod=leasePeriod
935            self.addonPrices = {
936                "forge" : 50.0 ,    // will have to run transactions on this when update on mainnet.
937                "premiumForge" : 1000.0
938            }
939            self.lockPeriod=lockPeriod
940            self.secondaryCut=secondaryCut
941            self.defaultPrice=defaultPrice
942            self.lengthPrices=lengthPrices
943            self.profiles={}
944            self.wallet=wallet
945            self.pricesChangedAt= Clock.time()
946            self.publicEnabled=publicEnabled
947        }
948
949        access(all) fun getLease(_ name: String) : NetworkLease? {
950            return self.profiles[name]
951        }
952
953        access(account) fun setAddonPrice(name:String, price:UFix64) {
954            self.addonPrices[name]=price
955        }
956
957        access(account) fun setPrice(defaultPrice: UFix64, additionalPrices: {Int: UFix64}) {
958            self.defaultPrice=defaultPrice
959            self.lengthPrices=additionalPrices
960        }
961
962        access(contract) fun renew(name: String, vault: @FlowToken.Vault) {
963            if let lease= self.profiles[name] {
964                let cost= FIND.calculateCostInFlow(name) 
965                if vault.balance != cost {
966                    panic("Vault did not contain ".concat(cost.toString()).concat(" amount of Flow"))
967                }
968                let walletRef = self.wallet.borrow() ?? panic("The receiver capability is invalid. Wallet address : ".concat(self.wallet.address.toString()))
969                walletRef.deposit(from: <- vault)
970                self.internal_renew(name: name)
971                return
972            }
973            panic("Could not find profile with name=".concat(name))
974        }
975
976        access(contract) fun renewDapper(merchAccount: Address, name: String, vault: @DapperUtilityCoin.Vault) {
977
978            FIND.checkMerchantAddress(merchAccount)
979
980            if let lease= self.profiles[name] {
981                let cost= self.calculateCost(name)
982                if vault.balance != cost {
983                    panic("Vault did not contain ".concat(cost.toString()).concat(" amount of Dapper Credit"))
984                }
985                let walletRef = getAccount(merchAccount).capabilities.borrow<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver) ?? panic("Cannot borrow reference to Dapper Merch Account receiver. Address : ".concat(merchAccount.toString()))
986                walletRef.deposit(from: <- vault)
987                self.internal_renew(name: name)
988                return
989            }
990            panic("Could not find profile with name=".concat(name))
991        }
992
993        access(account) fun internal_renew(name: String) {
994            if let lease= self.profiles[name] {
995
996                var newTime=0.0
997                if lease.status() == LeaseStatus.TAKEN {
998                    //the name is taken but not expired so we extend the total period of the lease
999                    lease.setValidUntil(lease.validUntil + self.leasePeriod)
1000                } else {
1001                    lease.setValidUntil(Clock.time() + self.leasePeriod)
1002                }
1003                lease.setLockedUntil(lease.validUntil+ self.lockPeriod)
1004
1005
1006                emit Register(name: name, owner:lease.profile.address, validUntil: lease.validUntil, lockedUntil: lease.lockedUntil)
1007                self.profiles[name] =  lease
1008                return
1009            }
1010            panic("Could not find profile with name=".concat(name))
1011        }
1012
1013        access(account) fun getLeaseExpireTime(_ name: String) : UFix64{
1014            if let lease= self.profiles[name] {
1015                return lease.validUntil
1016            }
1017            panic("Could not find profile with name=".concat(name))
1018        }
1019
1020        access(account) fun getLeaseLockedUntil(_ name: String) : UFix64{
1021            if let lease= self.profiles[name] {
1022                return lease.lockedUntil
1023            }
1024            panic("Could not find profile with name=".concat(name))
1025        }
1026
1027        //moving leases are done from the lease collection
1028        access(contract) fun move(name: String, profile: Capability<&{Profile.Public}>) {
1029            if let lease= self.profiles[name] {
1030                lease.setProfile(profile)
1031                self.profiles[name] = lease
1032                return
1033            }
1034            panic("Could not find profile with name=".concat(name))
1035        }
1036
1037        //everybody can call register, normally done through the convenience method in the contract
1038        access(all) fun register(name: String, vault: @FlowToken.Vault, profile: Capability<&{Profile.Public}>,  leases: Capability<&{LeaseCollectionPublic}>) {
1039
1040            if name.length < 3 {
1041                panic( "A FIND name has to be minimum 3 letters long")
1042            }
1043
1044            let nameStatus=self.readStatus(name)
1045            if nameStatus.status == LeaseStatus.TAKEN {
1046                panic("Name already registered")
1047            }
1048
1049            //if we have a locked profile that is not owned by the same identity then panic
1050            if nameStatus.status == LeaseStatus.LOCKED {
1051                panic("Name is locked")
1052            }
1053
1054            let cost= FIND.calculateCostInFlow(name)
1055            if vault.balance != cost {
1056                panic("Vault did not contain ".concat(cost.toString()).concat(" amount of Flow"))
1057            }
1058            self.wallet.borrow()!.deposit(from: <- vault)
1059
1060            self.internal_register(name: name, profile: profile, leases: leases)
1061        }
1062
1063        //everybody can call register, normally done through the convenience method in the contract
1064        access(all) fun registerDapper(merchAccount: Address, name: String, vault: @DapperUtilityCoin.Vault, profile: Capability<&{Profile.Public}>, leases: Capability<&{LeaseCollectionPublic}>) {
1065            FIND.checkMerchantAddress(merchAccount)
1066
1067            if name.length < 3 {
1068                panic( "A FIND name has to be minimum 3 letters long")
1069            }
1070
1071            let nameStatus=self.readStatus(name)
1072            if nameStatus.status == LeaseStatus.TAKEN {
1073                panic("Name already registered")
1074            }
1075
1076            //if we have a locked profile that is not owned by the same identity then panic
1077            if nameStatus.status == LeaseStatus.LOCKED {
1078                panic("Name is locked")
1079            }
1080
1081            let cost= self.calculateCost(name)
1082            if vault.balance != cost {
1083                panic("Vault did not contain ".concat(cost.toString()).concat(" amount of Dapper Credit"))
1084            }
1085
1086            let walletRef = getAccount(merchAccount).capabilities.borrow<&{FungibleToken.Receiver}>(/public/dapperUtilityCoinReceiver) ?? panic("Cannot borrow reference to Dapper Merch Account receiver. Address : ".concat(merchAccount.toString()))
1087            walletRef.deposit(from: <- vault)
1088            self.internal_register(name: name, profile: profile, leases: leases)
1089        }
1090
1091        access(account) fun internal_register(name: String, profile: Capability<&{Profile.Public}>,  leases: Capability<&{LeaseCollectionPublic}>) {
1092
1093            if name.length < 3 {
1094                panic("A FIND name has to be minimum 3 letters long")
1095            }
1096            if !leases.check() {
1097                panic("The lease collection capability is invalid.")
1098            }
1099            if !profile.check() {
1100                panic("The profile capability is invalid")
1101            }
1102
1103            let nameStatus=self.readStatus(name)
1104            if nameStatus.status == LeaseStatus.TAKEN {
1105                panic("Name already registered")
1106            }
1107
1108            //if we have a locked profile that is not owned by the same identity then panic
1109            if nameStatus.status == LeaseStatus.LOCKED {
1110                panic("Name is locked")
1111            }
1112
1113            let lease= NetworkLease(
1114                validUntil:Clock.time() + self.leasePeriod,
1115                lockedUntil: Clock.time() + self.leasePeriod+ self.lockPeriod,
1116                profile: profile,
1117                name: name
1118            )
1119
1120            emit Register(name: name, owner:profile.address, validUntil: lease.validUntil, lockedUntil: lease.lockedUntil)
1121            emit Name(name: name)
1122
1123            let profileRef = profile.borrow()!
1124
1125            if profileRef.getFindName() == "" {
1126                profileRef.setFindName(name)
1127            }
1128
1129            self.profiles[name] =  lease
1130
1131            leases.borrow()!.deposit(token: <- create Lease(name: name, networkCap: FIND.account.capabilities.storage.issue<&Network>(FIND.NetworkStoragePath)))
1132
1133        }
1134
1135        access(all) fun readStatus(_ name: String): NameStatus {
1136            let currentTime=Clock.time()
1137            if let lease= self.profiles[name] {
1138                if !lease.profile.check() {
1139                    return NameStatus(status: LeaseStatus.TAKEN, owner: nil)
1140                }
1141                let owner=lease.profile.borrow()!.owner!.address
1142                return NameStatus(status: lease.status(), owner: owner)
1143            }
1144            return NameStatus(status:LeaseStatus.FREE, owner: nil)
1145        }
1146
1147        access(account) fun profile(_ name: String) : &{Profile.Public}? {
1148            let nameStatus=self.readStatus(name)
1149            if nameStatus.status == LeaseStatus.FREE {
1150                return nil
1151            }
1152
1153            if let lease=self.profiles[name] {
1154                return lease.profile.borrow()
1155            }
1156            return nil
1157        }
1158
1159        //lookup a name that is not locked
1160        access(all) fun lookup(_ name: String) : &{Profile.Public}? {
1161            let nameStatus=self.readStatus(name)
1162            if nameStatus.status != LeaseStatus.TAKEN {
1163                return nil
1164            }
1165
1166            if let lease=self.profiles[name] {
1167                return lease.profile.borrow()
1168            }
1169            return nil
1170        }
1171
1172        access(all) fun calculateCost(_ name: String) : UFix64 {
1173            if self.lengthPrices[name.length] != nil {
1174                return self.lengthPrices[name.length]!
1175            } else {
1176                return self.defaultPrice
1177            }
1178        }
1179
1180        access(account) fun setWallet(_ wallet: Capability<&{FungibleToken.Receiver}>) {
1181            self.wallet=wallet
1182        }
1183
1184        access(account) fun setPublicEnabled(_ enabled: Bool) {
1185            self.publicEnabled=enabled
1186        }
1187
1188        access(all) fun getSecondaryCut() : UFix64 {
1189            return self.secondaryCut
1190        }
1191
1192        access(all) fun getWallet() : Capability<&{FungibleToken.Receiver}> {
1193            return self.wallet
1194        }
1195    }
1196
1197    access(all) fun getFindNetworkAddress() : Address {
1198        return self.account.address
1199    }
1200
1201
1202
1203    access(all) fun validateFindName(_ value: String) : Bool {
1204        if value.length < 3 || value.length > 16 {
1205            return false
1206        }
1207        if !FIND.validateAlphanumericLowerDash(value) {
1208            return false
1209        }
1210
1211        if value.length==16 && FIND.validateHex(value) {
1212            return false
1213        }
1214
1215        return true
1216    }
1217
1218    access(all) fun validateAlphanumericLowerDash(_ value:String) : Bool {
1219        let lowerA: UInt8=97
1220        let lowerZ: UInt8=122
1221
1222        let dash:UInt8=45
1223        let number0:UInt8=48
1224        let number9:UInt8=57
1225
1226        let bytes=value.utf8
1227        for byte in bytes {
1228            if byte >= lowerA && byte <= lowerZ {
1229                continue
1230            }
1231            if byte >= number0 && byte <= number9  {
1232                continue
1233            }
1234
1235            if byte == dash {
1236                continue
1237            }
1238            return false
1239
1240        }
1241        return true
1242
1243    }
1244
1245    access(all) fun validateHex(_ value:String) : Bool {
1246        let lowerA: UInt8=97
1247        let lowerF: UInt8=102
1248
1249        let number0:UInt8=48
1250        let number9:UInt8=57
1251
1252        let bytes=value.utf8
1253        for byte in bytes {
1254            if byte >= lowerA && byte <= lowerF {
1255                continue
1256            }
1257            if byte >= number0 && byte <= number9  {
1258                continue
1259            }
1260            return false
1261
1262        }
1263        return true
1264
1265    }
1266
1267    access(all) fun trimFindSuffix(_ name: String) : String {
1268        return FindUtils.trimSuffix(name, suffix: ".find")
1269    }
1270
1271    access(contract) fun checkMerchantAddress(_ merchAccount: Address) {
1272        // If only find can sign the trxns and call this function, then we do not have to check the address passed in.
1273        // Otherwise, would it be wiser if we hard code the address here?
1274
1275        if FIND.account.address == 0x097bafa4e0b48eef {
1276            // This is for mainnet
1277            if merchAccount != 0x55459409d30274ee {
1278                panic("Merch Account address does not match with expected")
1279            }
1280        } else if FIND.account.address == 0x35717efbbce11c74 {
1281            // This is for testnet
1282            if merchAccount != 0x4748780c8bf65e19{
1283                panic("Merch Account address does not match with expected")
1284            }
1285        } else {
1286            // otherwise falls into emulator and user dapper
1287            if merchAccount !=  0x179b6b1cb6755e31 {
1288                panic("Merch Account address does not match with expected ".concat(merchAccount.toString()))
1289            }
1290        }
1291    }
1292
1293
1294    access(account) fun getFlowUSDOracleAddress() : Address {
1295        // If only find can sign the trxns and call this function, then we do not have to check the address passed in.
1296        // Otherwise, would it be wiser if we hard code the address here?
1297        if FIND.account.address == 0x097bafa4e0b48eef {
1298            // This is for mainnet
1299            return 0xe385412159992e11
1300        } else if FIND.account.address == 0x35717efbbce11c74 {
1301            // This is for testnet
1302            return 0xcbdb5a7b89c3c844
1303        } else {
1304            //otherwise on emulator we use same account as FIND
1305            return self.account.address
1306        }
1307    }
1308
1309    access(account) fun getMerchantAddress() : Address {
1310        // If only find can sign the trxns and call this function, then we do not have to check the address passed in.
1311        // Otherwise, would it be wiser if we hard code the address here?
1312
1313        if FIND.account.address == 0x097bafa4e0b48eef {
1314            // This is for mainnet
1315            return 0x55459409d30274ee
1316        } else if FIND.account.address == 0x35717efbbce11c74 {
1317            // This is for testnet
1318            return 0x4748780c8bf65e19
1319        } else {
1320            // otherwise falls into emulator and user dapper
1321            return 0x179b6b1cb6755e31
1322        }
1323    }
1324
1325    init() {
1326        self.NetworkStoragePath= /storage/FIND
1327
1328        self.LeasePublicPath=/public/findLeases
1329        self.LeaseStoragePath=/storage/findLeases
1330
1331        // Get
1332        let wallet=self.account.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
1333
1334        // these values are hardcoded here for a reason. Then plan is to throw away the key and not have setters for them so that people can trust the contract to be the same
1335        let network <-  create Network(
1336            leasePeriod: 31536000.0, //365 days
1337            lockPeriod: 7776000.0, //90 days
1338            secondaryCut: 0.05,
1339            defaultPrice: 5.0,
1340            lengthPrices: {3: 500.0, 4:100.0},
1341            wallet: wallet,
1342            publicEnabled: false
1343        )
1344        self.account.storage.save(<-network, to: FIND.NetworkStoragePath)
1345    }
1346
1347    //These are deprecated resources that we cannot have remove 
1348    access(all) struct BidInfo{}
1349    access(all) resource Auction { }
1350    access(all) resource Bid { }
1351    access(all) resource interface BidCollectionPublic { }
1352    access(all) resource BidCollection: BidCollectionPublic { }
1353
1354}
1355