Smart Contract
Flowns
A.233eb012d34b0070.Flowns
1
2import FungibleToken from 0xf233dcee88fe0abe
3import NonFungibleToken from 0x1d7e57aa55817448
4import Domains from 0x233eb012d34b0070
5import FNSConfig from 0x233eb012d34b0070
6
7// Flowns is the core contract of FNS, Flowns define Root domain and admin resource
8access(all) contract Flowns {
9 // paths
10 access(all) let FlownsAdminPrivatePath: PrivatePath
11 access(all) let FlownsAdminStoragePath: StoragePath
12 access(all) let CollectionStoragePath: StoragePath
13 access(all) let CollectionPublicPath: PublicPath
14 // access(all) let CollectionPrivatePath: PrivatePath
15
16 // variables
17 access(all) var totalRootDomains: UInt64
18 // status that set register pause or not
19 access(self) var isPause: Bool
20 // for domain name on-chain validator
21 access(self) var forbidChars: String
22 // events
23
24
25 access(all) event RootDomainDestroyed(id: UInt64)
26
27 access(all) event RootDomainCreated(name: String, nameHash: String, id: UInt64)
28
29 access(all) event RenewDomain(name: String, nameHash: String, duration: UFix64, price: UFix64)
30
31 access(all) event RootDomainPriceChanged(name: String, key: Int, price: UFix64)
32
33 access(all) event RootDomainVaultWithdrawn(name: String, amount: UFix64)
34
35 access(all) event RootDomainServerAdded()
36
37 access(all) event FlownsAdminCreated()
38
39 access(all) event RootDomainVaultChanged()
40
41 access(all) event FlownsPaused()
42
43 access(all) event FlownsActivated()
44
45 access(all) event FlownsForbidCharsUpdated(before: String, after: String)
46
47 access(all) event RootDomainMaxLengthUpdated(domainId: UInt64, before: Int, after: Int)
48
49 access(all) event RootDomainCommissionRateUpdated(domainId: UInt64, before: UFix64, after: UFix64)
50
51 access(all) event RootDomainMintDurationUpdated(domainId: UInt64, before: UFix64, after: UFix64)
52
53 access(all) event DomainRegisterCommissionAllocated(domainId: UInt64, nameHash: String, amount: UFix64, commissionAmount: UFix64, refer: Address, receiveId: UInt64)
54
55
56 // structs
57 access(all) struct RootDomainInfo {
58 access(all) let id: UInt64
59 access(all) let name: String
60 access(all) let nameHash: String
61 access(all) let domainCount: UInt64
62 access(all) let minRentDuration: UFix64
63 access(all) let maxDomainLength: Int
64 access(all) let prices: {Int: UFix64}
65 access(all) let commissionRate: UFix64
66
67
68 init(
69 id: UInt64,
70 name: String,
71 nameHash: String,
72 domainCount: UInt64,
73 minRentDuration: UFix64,
74 maxDomainLength: Int,
75 prices: {Int: UFix64},
76 commissionRate: UFix64
77 ) {
78 self.id = id
79 self.name = name
80 self.nameHash = nameHash
81 self.domainCount = domainCount
82 self.minRentDuration = minRentDuration
83 self.maxDomainLength = maxDomainLength
84 self.prices = prices
85 self.commissionRate = commissionRate
86 }
87 }
88
89 // resources
90 // Rootdomain is the root of domain name
91 // ex. domain 'fns.flow' 'flow' is the root domain name, and save as a resource by RootDomain
92 access(all) resource RootDomain {
93 access(all) let id: UInt64
94
95 access(all) let name: String
96
97 // namehash is calc by eth-ens-namehash
98 access(all) let nameHash: String
99
100 access(all) var domainCount: UInt64
101
102 // Here is the vault to receive domain rent fee, every root domain has his own vault
103 // you can call Flowns.getRootVaultBalance to get balance
104 access(self) var domainVault: @{FungibleToken.Vault}
105
106 // Here is the prices store for domain rent fee
107 // When user register or renew a domain ,the rent price is get from here, and price store by {domains length: flow per second}
108 // If cannot get price, then register will not open
109 access(self) var prices: {Int: UFix64}
110
111 access(self) var minRentDuration: UFix64
112
113 access(self) var maxDomainLength: Int
114
115 access(self) var commissionRate: UFix64
116
117 // Server store the collection private resource to manage the domains
118 // Server need to init before open register
119 access(self) var server: Capability<&Domains.Collection>?
120
121 init(id: UInt64, name: String, nameHash: String, vault: @{FungibleToken.Vault}){
122 self.id = id
123 self.name = name
124 self.nameHash = nameHash
125 self.domainCount = 0
126 self.domainVault <- vault
127 self.prices = {}
128 self.server = nil
129 self.minRentDuration = 31536000.00
130 self.maxDomainLength = 30
131 self.commissionRate = 0.0
132 }
133
134 // Set CollectionPrivate to RootDomain resource
135 access(all) fun addCapability(_ cap: Capability<&Domains.Collection>) {
136 pre {
137 cap.check() : "Invalid server capablity"
138 self.server == nil : "Server already set"
139 }
140 self.server = cap
141
142 emit RootDomainServerAdded()
143
144 }
145
146 // Query root domain info
147 access(all) fun getRootDomainInfo() : RootDomainInfo {
148 return RootDomainInfo(
149 id: self.id,
150 name: self.name,
151 nameHash: self.nameHash,
152 domainCount: self.domainCount,
153 minRentDuration: self.minRentDuration,
154 maxDomainLength: self.maxDomainLength,
155 prices: self.prices,
156 commissionRate: self.commissionRate
157
158 )
159 }
160
161 // Query root domain vault balance
162 access(all) fun getVaultBalance() : UFix64 {
163 pre {
164 self.domainVault != nil : "Vault not init yet..."
165 }
166 return self.domainVault.balance
167 }
168
169 // Deposit fee to domain Vault
170 access(all) fun depositVault(fee: @{FungibleToken.Vault}) {
171 pre {
172 self.domainVault != nil : "Vault not init yet..."
173 }
174 self.domainVault.deposit(from: <- fee)
175 }
176
177 access(all) fun getPrices(): {Int: UFix64} {
178 return self.prices
179 }
180
181 // Mint domain
182 access(account) fun mintDomain(name: String, duration: UFix64, receiver: Capability<&{NonFungibleToken.Receiver}>){
183 pre {
184 self.server != nil : "Domains collection has not been linked to the server"
185 }
186 let nameHash = Flowns.getDomainNameHash(name: name, parentNameHash: self.nameHash)
187
188 let expiredTime = getCurrentBlock().timestamp + duration
189
190 self.server!.borrow()!.mintDomain(name: name, nameHash: nameHash, parentName: self.name, expiredAt: expiredTime, receiver: receiver)
191 self.domainCount = self.domainCount + (1 as UInt64)
192
193 }
194 // Set domain rent fee
195 access(all) fun setPrices(key: Int, price: UFix64) {
196 self.prices[key]= price
197
198 emit RootDomainPriceChanged(name: self.name, key: key, price: price)
199 }
200
201 access(account) fun setMinRentDuration(_ duration: UFix64) {
202 let oldDuration = self.minRentDuration
203 self.minRentDuration = duration
204
205 emit RootDomainMintDurationUpdated(domainId: self.id, before: oldDuration, after: duration)
206 }
207
208 access(account) fun setMaxDomainLength(_ length: Int) {
209 let oldLength = self.maxDomainLength
210 self.maxDomainLength = length
211
212 emit RootDomainMaxLengthUpdated(domainId: self.id, before: oldLength, after: length)
213 }
214
215 access(account) fun setCommissionRate(_ rate: UFix64) {
216 let oldRate = self.commissionRate
217 self.commissionRate = rate
218
219 emit RootDomainCommissionRateUpdated(domainId: self.id, before: oldRate, after: rate)
220 }
221
222 access(contract) fun _distributeCommission(feeTokens: @{FungibleToken.Vault}, refer: Address, from: Address?): @{FungibleToken.Vault} {
223 let rentFee = feeTokens.balance
224 let commissionFee = rentFee * self.commissionRate
225
226 let referAcc = getAccount(refer!)
227
228 let collection = referAcc.capabilities.borrow<&Domains.Collection>(Domains.CollectionPublicPath)
229 // let collection = collectionCap.borrow()
230 if collection != nil {
231 let ids = collection!.getIDs()
232 var defaultDomain: &Domains.NFT? = nil
233 defaultDomain = collection!.borrowDomain(id: ids[0])!
234 if ids.length > 0 {
235 for id in ids {
236 let domain = collection!.borrowDomain(id: id)!
237 let isDefault = domain.getText(key: "isDefault")
238 if isDefault == "true" {
239 defaultDomain = domain
240 }
241 }
242 if defaultDomain!.receivable == true && !Domains.isExpired(defaultDomain!.nameHash) {
243 defaultDomain!.depositVault(from: <- feeTokens.withdraw(amount: commissionFee), senderRef: nil)
244 emit DomainRegisterCommissionAllocated(domainId: self.id, nameHash: defaultDomain!.nameHash, amount: rentFee, commissionAmount: commissionFee, refer: refer!, receiveId: defaultDomain!.id)
245 }
246 }
247 }
248 return <- feeTokens
249 }
250
251
252 // Renew domain
253 access(all) fun renewDomain(domain: &Domains.NFT, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?) {
254 pre {
255 !Domains.isDeprecated(nameHash: domain.nameHash, domainId: domain.id) : "Domain already deprecated ..."
256 }
257 // When domain name longer than 10, the price will set by 10 price
258 var len = domain.name.length
259 if len > 10 {
260 len = 10
261 }
262 let price = self.getPrices()[len]
263
264 if domain.parent != self.name {
265 panic("domain not root domain's sub domain")
266 }
267 if duration < self.minRentDuration {
268 panic("Duration must greater than min rent duration ".concat(self.minRentDuration.toString()))
269 }
270
271 if price == 0.0 || price == nil {
272 panic("Can not renew domain, rent price not set yet")
273 }
274
275 // Calc rent price
276 let rentPrice = price! * duration
277
278 let rentFee = feeTokens.balance
279
280 // check the rent fee
281 if rentFee < rentPrice {
282 panic("Not enough fee to renew your domain.")
283 }
284 var feeVault: @{FungibleToken.Vault}? <- nil
285 // distribution of commission
286 if self.commissionRate > 0.0 && refer != nil {
287 feeVault <-! self._distributeCommission(feeTokens: <- feeTokens, refer: refer!, from: Domains.getRecords(domain.nameHash))
288 } else {
289 feeVault <-! feeTokens
290 }
291
292 // Receive rent fee
293 self.domainVault.deposit(from: <- feeVault!)
294
295 let expiredAt = Domains.getExpiredTime(domain.nameHash)! + UFix64(duration)
296 // Update domain's expire time with Domains expired mapping
297 Domains.updateExpired(nameHash: domain.nameHash, time: expiredAt )
298
299 emit RenewDomain(name: domain.name, nameHash: domain.nameHash, duration: duration, price: rentFee )
300
301 }
302
303 // Register domain
304 access(all) fun registerDomain(name: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, receiver: Capability<&{NonFungibleToken.Receiver}>, refer: Address? ){
305 pre {
306 self.server != nil : "Your client has not been linked to the server"
307 name.length <= self.maxDomainLength : "Domain name can not exceed max length: ".concat(self.maxDomainLength.toString())
308 }
309
310 let nameHash = Flowns.getDomainNameHash(name: name, parentNameHash: self.nameHash)
311
312 if Flowns.available(nameHash: nameHash) == false {
313 panic("Domain not available")
314 }
315
316 // same as renew domain
317 var len = name.length
318 if len > 10 {
319 len = 10
320 }
321
322 let price = self.getPrices()[len]
323 // limit the register and renew time longer than one year
324
325 if duration < self.minRentDuration {
326 panic("Duration must geater than min rent duration, expect: ".concat(self.minRentDuration.toString()))
327 }
328 if price == 0.0 || price == nil {
329 panic("Can not register domain, rent price not set yet")
330 }
331
332 let rentPrice = price! * duration
333
334 let rentFee = feeTokens.balance
335
336 if rentFee < rentPrice {
337 panic("Not enough fee to rent your domain, expect: ".concat(rentPrice.toString()))
338 }
339
340 let expiredTime = getCurrentBlock().timestamp + UFix64(duration)
341
342 var feeVault: @{FungibleToken.Vault}? <- nil
343 // distribution of commission
344 if self.commissionRate > 0.0 && refer != nil {
345 feeVault <-! self._distributeCommission(feeTokens: <- feeTokens, refer: refer!, from: Domains.getRecords(nameHash))
346 } else {
347 feeVault <-! feeTokens
348 }
349
350 self.domainVault.deposit(from: <- feeVault!)
351
352 self.server!.borrow()!.mintDomain(name: name, nameHash: nameHash, parentName: self.name, expiredAt: expiredTime, receiver: receiver)
353
354 self.domainCount = self.domainCount + (1 as UInt64)
355
356 }
357
358 // Withdraw vault fee
359 access(account) fun withdrawVault(receiver: Capability<&{FungibleToken.Receiver}>, amount: UFix64) {
360 let vault = receiver.borrow()!
361 vault.deposit(from: <- self.domainVault.withdraw(amount: amount))
362
363 emit RootDomainVaultWithdrawn(name: self.name, amount: amount)
364 }
365
366 access(account) fun changeRootDomainVault(vault: @{FungibleToken.Vault}) {
367
368 let balance = self.getVaultBalance()
369
370 if balance > 0.0 {
371 panic("Please withdraw the balance of the previous vault first ")
372 }
373
374 let preVault <- self.domainVault <- vault
375
376 destroy preVault
377 // clean the price
378 self.prices = {}
379 emit RootDomainVaultChanged()
380 }
381
382 }
383
384
385 // Root domain public interface for fns user
386 access(all) resource interface RootDomainCollectionPublic {
387
388 access(all) fun getDomainInfo(domainId: UInt64) : RootDomainInfo
389
390 access(all) fun getAllDomains(): {UInt64: RootDomainInfo}
391
392 access(all) fun renewDomain(domainId: UInt64, domain: &Domains.NFT, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?)
393
394 access(all) fun renewDomainWithNameHash(nameHash: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?)
395
396 access(all) fun registerDomain(domainId: UInt64, name: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, receiver: Capability<&{NonFungibleToken.Receiver}>, refer: Address? )
397
398 access(all) fun getPrices(domainId: UInt64): {Int: UFix64}
399
400 access(all) fun getVaultBalance(domainId: UInt64): UFix64
401
402 }
403
404 // Manager resource
405 access(all) resource interface RootDomainCollectionAdmin {
406
407 access(account) fun createRootDomain(
408 name: String,
409 vault: @{FungibleToken.Vault}
410 )
411
412 access(account) fun withdrawVault(domainId: UInt64, receiver: Capability<&{FungibleToken.Receiver}>, amount: UFix64)
413
414 access(account) fun changeRootDomainVault(domainId: UInt64, vault: @{FungibleToken.Vault})
415
416 access(account) fun setPrices(domainId: UInt64, len: Int, price: UFix64)
417
418 access(account) fun setMinRentDuration(domainId: UInt64, duration: UFix64)
419
420 access(account) fun setMaxDomainLength(domainId: UInt64, length: Int)
421
422 access(account) fun setCommissionRate(domainId: UInt64, rate: UFix64)
423
424 access(account) fun mintDomain(domainId: UInt64, name: String, duration: UFix64, receiver: Capability<&{NonFungibleToken.Receiver}>)
425
426 access(account) fun renewDomainWithAdmin(nameHash: String, duration: UFix64)
427
428 }
429
430 // Root domain Collection
431 access(all) resource RootDomainCollection: RootDomainCollectionPublic, RootDomainCollectionAdmin {
432 // Root domains
433 access(account) var domains: @{UInt64: RootDomain}
434
435 init(
436 ) {
437 self.domains <- {}
438 }
439
440 // Create root domain
441 access(account) fun createRootDomain(
442 name: String,
443 vault: @{FungibleToken.Vault}
444 ) {
445
446 let nameHash = Flowns.hash(node:"", lable: name)
447 let prefix = "0x"
448
449 let rootDomain <- create RootDomain(
450 id: Flowns.totalRootDomains,
451 name: name,
452 nameHash: prefix.concat(nameHash),
453 vault: <- vault
454 )
455
456 Flowns.totalRootDomains = Flowns.totalRootDomains + 1 as UInt64
457 emit RootDomainCreated(name: name, nameHash: nameHash, id: rootDomain.id)
458
459 let oldDomain <- self.domains[rootDomain.id] <- rootDomain
460 destroy oldDomain
461 }
462
463 access(all) fun renewDomain(domainId: UInt64, domain: &Domains.NFT, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?) {
464 pre {
465 self.domains[domainId] != nil : "Root domain not exist..."
466 }
467 let root = self.getRootDomain(domainId)
468 root.renewDomain(domain: domain, duration: duration, feeTokens: <- feeTokens, refer: refer)
469 }
470
471 access(all) fun renewDomainWithNameHash(nameHash: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?) {
472 let domain = Flowns.getDomain(nameHash: nameHash) ?? panic("Can not find domain by nameHash")
473 // get all domains with access(all)
474 let rootDomains = Flowns.getAllRootDomains()!
475 let ids = rootDomains.keys
476 var rootDomain: RootDomainInfo? = nil
477 for id in ids {
478 let root = rootDomains[id]!
479 if root.name == domain.parent {
480 rootDomain = root
481 }
482 }
483 assert(rootDomain != nil, message: "Can not get root domain info")
484
485 var len = domain.name.length
486 if len > 10 {
487 len = 10
488 }
489 let price = rootDomain!.prices[len]
490
491 if duration < rootDomain!.minRentDuration {
492 panic("Duration must greater than min rent duration ".concat(rootDomain!.minRentDuration.toString()))
493 }
494 if price == 0.0 || price == nil {
495 panic("Can not renew domain, rent price not set yet")
496 }
497 // Calc rent price
498 let rentPrice = price! * duration
499
500 let rentFee = feeTokens.balance
501
502 // check the rent fee
503 if rentFee < rentPrice {
504 panic("Not enough fee to renew your domain.")
505 }
506
507 let rootDomainRef = self.getRootDomain(rootDomain!.id)!
508
509 var feeVault: @{FungibleToken.Vault}? <- nil
510 // distribution of commission
511 if rootDomain!.commissionRate > 0.0 && refer != nil {
512 feeVault <-! rootDomainRef._distributeCommission(feeTokens: <- feeTokens, refer: refer!, from: Domains.getRecords(nameHash) )
513 } else {
514 feeVault <-! feeTokens
515 }
516
517
518 rootDomainRef.depositVault(fee: <- feeVault!)
519
520 let expiredAt = Domains.getExpiredTime(nameHash)! + UFix64(duration)
521 Domains.updateExpired(nameHash: nameHash, time: expiredAt )
522 emit RenewDomain(name: domain.name, nameHash: domain.nameHash, duration: duration, price: rentFee )
523 }
524
525
526 access(all) fun registerDomain(domainId: UInt64, name: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, receiver: Capability<&{NonFungibleToken.Receiver}>, refer: Address?) {
527 pre {
528 self.domains[domainId] != nil : "Root domain not exist..."
529 }
530 let root = self.getRootDomain(domainId)
531 root.registerDomain(name: name, duration: duration, feeTokens: <-feeTokens, receiver: receiver, refer: refer )
532 }
533
534 access(all) fun getVaultBalance(domainId: UInt64): UFix64 {
535 pre {
536 self.domains[domainId] != nil : "Root domain not exist..."
537 }
538 let rootRef = &self.domains[domainId] as &RootDomain?
539
540 return rootRef!.getVaultBalance()
541 }
542
543 access(account) fun withdrawVault(domainId: UInt64, receiver: Capability<&{FungibleToken.Receiver}>, amount: UFix64) {
544 pre {
545 self.domains[domainId] != nil : "Root domain not exist..."
546 }
547 self.getRootDomain(domainId).withdrawVault(receiver: receiver, amount: amount)
548 }
549
550 access(account) fun changeRootDomainVault(domainId: UInt64, vault: @{FungibleToken.Vault}) {
551 pre {
552 self.domains[domainId] != nil : "Root domain not exist..."
553 }
554 self.getRootDomain(domainId).changeRootDomainVault(vault: <- vault)
555 }
556
557 access(account) fun mintDomain(domainId: UInt64, name: String, duration: UFix64, receiver: Capability<&{NonFungibleToken.Receiver}>) {
558 pre {
559 self.domains[domainId] != nil : "Root domain not exist..."
560 }
561 let root = self.getRootDomain(domainId)
562 root.mintDomain(name: name, duration: duration, receiver: receiver)
563 }
564
565 // Get all root domains
566 access(all) fun getAllDomains(): {UInt64: RootDomainInfo} {
567 var domainInfos: {UInt64: RootDomainInfo }= {}
568 for id in self.domains.keys {
569 let itemRef = &self.domains[id] as &RootDomain?
570 domainInfos[id] = itemRef!.getRootDomainInfo()
571 }
572 return domainInfos
573 }
574
575 access(account) fun setPrices(domainId: UInt64, len: Int, price: UFix64){
576 self.getRootDomain(domainId).setPrices(key: len, price: price)
577 }
578
579 access(account) fun setMinRentDuration(domainId: UInt64, duration: UFix64){
580 pre {
581 duration >= 604800.00 : "Duration must be greater than one week"
582 }
583 self.getRootDomain(domainId).setMinRentDuration(duration)
584 }
585
586 access(account) fun setMaxDomainLength(domainId: UInt64, length: Int){
587 pre {
588 length > 0 && length < 50 : "Domain length must greater than 0 and smaller than 50"
589 }
590 self.getRootDomain(domainId).setMaxDomainLength(length)
591 }
592
593 access(account) fun setCommissionRate(domainId: UInt64, rate: UFix64){
594 pre {
595 rate >= 0.0 && rate <= 1.0 : "Commission rate not valid"
596 }
597 self.getRootDomain(domainId).setCommissionRate(rate)
598 }
599
600 // get domain reference
601 access(contract) fun getRootDomain(_ domainId: UInt64) : &RootDomain {
602 pre {
603 self.domains[domainId] != nil: "domain doesn't exist"
604 }
605 return (&self.domains[domainId] as &RootDomain?)!
606 }
607 // get Root domain info
608 access(all) fun getDomainInfo(domainId: UInt64): RootDomainInfo {
609 return self.getRootDomain(domainId).getRootDomainInfo()
610 }
611
612 // Query root domain's rent price
613 access(all) fun getPrices(domainId: UInt64): {Int: UFix64} {
614 return self.getRootDomain(domainId).getPrices()
615 }
616
617 // renew domain by nameHash with admin auth
618 access(account) fun renewDomainWithAdmin(nameHash: String, duration: UFix64) {
619 // pre{
620 // Domains.getExpiredTime(nameHash) != nil : "Domain doesn't exist"
621 // }
622 assert(Domains.getExpiredTime(nameHash) != nil, message: "Can not find owner")
623 let expiredAt = Domains.getExpiredTime(nameHash)! + UFix64(duration)
624 // Update domain's expire time with Domains expired mapping
625 Domains.updateExpired(nameHash: nameHash, time: expiredAt )
626 let domain = Flowns.getDomain(nameHash: nameHash)!
627 emit RenewDomain(name: domain.name.concat(".").concat(domain.parent), nameHash: nameHash, duration: duration, price: 0.0 )
628 }
629
630 }
631
632 // Admin interface resource
633 access(all) resource interface AdminPrivate {
634
635 access(all) fun addCapability(_ cap: Capability<&Flowns.RootDomainCollection>)
636
637 access(all) fun addRootDomainCapability(domainId: UInt64, cap: Capability<&Domains.Collection>)
638
639 access(all) fun createRootDomain(name: String, vault: @{FungibleToken.Vault})
640
641 access(all) fun setRentPrice(domainId: UInt64, len: Int, price: UFix64)
642
643 access(all) fun withdrawVault(domainId: UInt64, receiver: Capability<&{FungibleToken.Receiver}>, amount: UFix64)
644
645 access(all) fun changeRootDomainVault(domainId: UInt64, vault: @{FungibleToken.Vault})
646
647 access(all) fun mintDomain(domainId: UInt64, name: String, duration: UFix64, receiver: Capability<&{NonFungibleToken.Receiver}>)
648
649 access(all) fun setMinRentDuration(domainId: UInt64, duration: UFix64)
650
651 access(all) fun setMaxDomainLength(domainId: UInt64, length: Int)
652
653 access(all) fun setCommissionRate(domainId: UInt64, rate: UFix64)
654
655 access(all) fun setDomainForbidChars(_ chars: String)
656
657 access(all) fun setPause(_ flag: Bool)
658
659 access(all) fun updateFTWhitelist(key: String, flag: Bool)
660
661 access(all) fun updateNFTWhitelist(key: String, flag: Bool)
662
663 access(all) fun setFTWhitelist(_ val: {String: Bool})
664
665 access(all) fun setNFTWhitelist(_ val: {String: Bool})
666
667 }
668
669
670 access(all) resource Admin: AdminPrivate {
671
672 access(self) var server: Capability<&Flowns.RootDomainCollection>?
673
674 init() {
675 // Server is the root collection for manager to create and store root domain
676 self.server = nil
677 }
678
679 // init RootDomainCollection for admin
680 access(all) fun addCapability(_ cap: Capability<&Flowns.RootDomainCollection>) {
681 pre {
682 cap.check() : "Invalid server capablity"
683 self.server == nil : "Server already set"
684 }
685 self.server = cap
686 }
687
688 // init Root domain's Domains collection to create collection for domain register
689 access(all) fun addRootDomainCapability(domainId: UInt64, cap: Capability<&Domains.Collection>) {
690 pre {
691 cap.check() : "Invalid server capablity"
692 }
693 self.server!.borrow()!.getRootDomain(domainId).addCapability(cap)
694 }
695
696 // Create root domain with admin
697 access(all) fun createRootDomain(name: String, vault: @{FungibleToken.Vault}) {
698 pre {
699 self.server != nil : "Your client has not been linked to the server"
700 }
701
702 self.server!.borrow()!.createRootDomain(name: name, vault: <- vault)
703 }
704
705 // Set rent price
706 access(all) fun setRentPrice(domainId: UInt64, len: Int, price: UFix64) {
707 pre {
708 self.server != nil : "Your client has not been linked to the server"
709 }
710
711 self.server!.borrow()!.setPrices(domainId: domainId, len: len, price: price)
712 }
713
714 access(all) fun setMinRentDuration(domainId: UInt64, duration: UFix64) {
715 pre {
716 self.server != nil : "Your client has not been linked to the server"
717 }
718
719 self.server!.borrow()!.setMinRentDuration(domainId: domainId, duration: duration)
720 }
721
722 access(all) fun setMaxDomainLength(domainId: UInt64, length: Int) {
723 pre {
724 self.server != nil : "Your client has not been linked to the server"
725 }
726
727 self.server!.borrow()!.setMaxDomainLength(domainId: domainId, length: length)
728 }
729
730
731 access(all) fun setCommissionRate(domainId: UInt64, rate: UFix64) {
732 pre {
733 self.server != nil : "Your client has not been linked to the server"
734 }
735
736 self.server!.borrow()!.setCommissionRate(domainId: domainId, rate: rate)
737 }
738
739
740
741 // Withdraw vault
742 access(all) fun withdrawVault(domainId: UInt64, receiver: Capability<&{FungibleToken.Receiver}>, amount: UFix64) {
743 pre {
744 self.server != nil : "Your client has not been linked to the server"
745 }
746
747 self.server!.borrow()!.withdrawVault(domainId: domainId, receiver: receiver, amount: amount)
748 }
749
750 // Withdraw vault
751 access(all) fun changeRootDomainVault(domainId: UInt64, vault: @{FungibleToken.Vault}) {
752 pre {
753 self.server != nil : "Your client has not been linked to the server"
754 }
755
756 self.server!.borrow()!.changeRootDomainVault(domainId: domainId, vault: <- vault)
757 }
758
759 // Mint domain with root domain
760 access(all) fun mintDomain(domainId: UInt64, name: String, duration: UFix64,receiver: Capability<&{NonFungibleToken.Receiver}>) {
761 pre {
762 self.server != nil : "Your client has not been linked to the server"
763 }
764
765 self.server!.borrow()!.mintDomain(domainId: domainId, name: name, duration: duration, receiver: receiver)
766 }
767
768 // Renew domain with admin auth
769 access(all) fun renewDomain(nameHash: String, duration: UFix64) {
770 pre {
771 self.server != nil : "Your client has not been linked to the server"
772 }
773 self.server!.borrow()!.renewDomainWithAdmin(nameHash: nameHash, duration: duration)
774 }
775
776 access(all) fun setPause(_ flag: Bool) {
777 pre {
778 Flowns.isPause != flag : "Already done!"
779 }
780 Flowns.isPause = flag
781 if flag == true {
782 emit FlownsPaused()
783 } else {
784 emit FlownsActivated()
785 }
786 }
787
788 access(all) fun setDomainForbidChars(_ chars: String) {
789 let oldChars = Flowns.forbidChars
790 Flowns.forbidChars = chars
791
792 emit FlownsForbidCharsUpdated(before: oldChars, after: chars)
793 }
794
795 access(all) fun updateFTWhitelist(key: String, flag: Bool) {
796 FNSConfig.updateFTWhitelist(key: key, flag: flag)
797 }
798
799 access(all) fun updateNFTWhitelist(key: String, flag: Bool) {
800 FNSConfig.updateNFTWhitelist(key: key, flag: flag)
801 }
802
803 access(all) fun setFTWhitelist(_ val: {String: Bool}) {
804 FNSConfig.setFTWhitelist(val)
805 }
806
807 access(all) fun setNFTWhitelist(_ val: {String: Bool}) {
808 FNSConfig.setNFTWhitelist(val)
809 }
810
811
812
813 }
814
815 // Create admin resource
816 access(self) fun createAdminClient(): @Admin {
817 emit FlownsAdminCreated()
818 return <- create Admin()
819 }
820
821 access(all) fun getDomainNameHash(name: String, parentNameHash: String): String {
822
823 let prefix = "0x"
824 let forbidenChars: [UInt8] = Flowns.forbidChars.utf8
825 let nameASCII = name.utf8
826
827 for char in forbidenChars {
828 if nameASCII.contains(char) {
829 panic("Domain name illegal ...")
830 }
831 }
832
833 let domainNameHash = Flowns.hash(node: parentNameHash.slice(from: 2, upTo: 66), lable:name )
834 return prefix.concat(domainNameHash)
835 }
836
837 // calc hash with node and lable
838 access(all) fun hash(node: String, lable: String): String {
839 var prefixNode = node
840 if node.length == 0 {
841 prefixNode = "0000000000000000000000000000000000000000000000000000000000000000"
842 }
843 let lableHash = String.encodeHex(HashAlgorithm.SHA3_256.hash(lable.utf8))
844 let hash = String.encodeHex(HashAlgorithm.SHA3_256.hash(prefixNode.concat(lableHash).utf8))
845 return hash
846 }
847
848 // query domain info by nameHash
849 access(all) fun getDomain(nameHash: String): &Domains.NFT? {
850 let address = Domains.getRecords(nameHash) ?? panic("Domain not exist")
851 let account = getAccount(address)
852 let collection = account.capabilities.borrow<&{Domains.CollectionPublic}>(Domains.CollectionPublicPath)!
853 // let collection = collectionCap.borrow()!
854 var domain: &Domains.NFT? = nil
855
856 let id = Domains.getDomainId(nameHash)
857 domain = collection.borrowDomain(id: id!)
858 return domain
859 }
860
861
862 // Query root domain
863 access(all) fun getRootDomainInfo(domainId: UInt64): RootDomainInfo? {
864 let account = Flowns.account
865 let collection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
866 if collection != nil {
867 return collection!.getDomainInfo(domainId: domainId)
868 }
869 return nil
870 }
871 // Query all root domain
872 access(all) fun getAllRootDomains(): {UInt64: RootDomainInfo}? {
873
874 let account = Flowns.account
875 let rootCollection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
876 if rootCollection != nil {
877 return rootCollection!.getAllDomains()
878 }
879 return nil
880 }
881
882 // Check domain available
883 access(all) fun available(nameHash: String): Bool {
884
885 if Domains.getRecords(nameHash) == nil {
886 return true
887 }
888 return Domains.isExpired(nameHash)
889 }
890
891 access(all) fun getRentPrices(domainId: UInt64): {Int: UFix64} {
892
893 let account = Flowns.account
894 let rootCollection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
895 if rootCollection != nil {
896 return rootCollection!.getPrices(domainId: domainId)
897 }
898 return {}
899 }
900
901 access(all) fun getRootVaultBalance(domainId: UInt64): UFix64 {
902
903 let account = Flowns.account
904 let rootCollection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
905 // let collection = rootCollectionCap.borrow()?? panic("Could not borrow collection ")
906 let balance = rootCollection!.getVaultBalance(domainId: domainId)
907 return balance
908 }
909
910 access(all) fun registerDomain(domainId: UInt64, name: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, receiver: Capability<&{NonFungibleToken.Receiver}>, refer: Address? ){
911 pre {
912 Flowns.isPause == false : "Register pause"
913 }
914 let account = Flowns.account
915 let rootCollection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
916 // let collection = rootCollectionCap.borrow() ?? panic("Could not borrow collection ")
917 rootCollection!.registerDomain(domainId: domainId, name: name, duration: duration, feeTokens: <-feeTokens, receiver: receiver, refer: refer)
918 }
919
920 access(all) fun renewDomain(domainId: UInt64, domain: &Domains.NFT, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?) {
921 pre {
922 Flowns.isPause == false : "Renewer pause"
923 }
924 let account = Flowns.account
925 let rootCollection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
926 // let collection = rootCollectionCap.borrow() ?? panic("Could not borrow collection ")
927 rootCollection!.renewDomain(domainId: domainId, domain: domain, duration: duration, feeTokens: <-feeTokens, refer: refer)
928 }
929
930 access(all) fun renewDomainWithNameHash(nameHash: String, duration: UFix64, feeTokens: @{FungibleToken.Vault}, refer: Address?) {
931 pre {
932 Flowns.isPause == false : "Renewer pause"
933 duration > 0.0 : "Duration must great than 0"
934 }
935
936 let account = Flowns.account
937 let rootCollection = account.capabilities.borrow<&{Flowns.RootDomainCollectionPublic}>(self.CollectionPublicPath)
938 // let collection = rootCollectionCap.borrow() ?? panic("Could not borrow collection ")
939 rootCollection!.renewDomainWithNameHash(nameHash: nameHash, duration: duration, feeTokens: <-feeTokens, refer: refer)
940 }
941
942 init() {
943
944 self.CollectionPublicPath = /public/flownsCollection
945 self.CollectionStoragePath = /storage/flownsCollection
946 self.FlownsAdminPrivatePath = /private/flownsAdmin
947 self.FlownsAdminStoragePath =/storage/flownsAdmin
948
949 let account = self.account
950 let admin <- Flowns.createAdminClient()
951
952 account.storage.save<@Flowns.Admin>(<-admin, to: self.FlownsAdminStoragePath)
953
954 self.totalRootDomains = 0
955 self.isPause = true
956 self.forbidChars = "!@#$%^&*()<>? ./ABCDEFGHIJKLMNOPQRSTUVWXYZ"
957
958 let collection: @Flowns.RootDomainCollection <- create RootDomainCollection()
959
960 account.storage.save(<-collection, to: Flowns.CollectionStoragePath)
961 }
962}
963