Smart Contract
FRC20NFTWrapper
A.d2abb5dbf5e08666.FRC20NFTWrapper
1/**
2> Author: Fixes Lab <https://github.com/fixes-world/>
3
4# FRC20NFTWrapper
5
6The FRC20NFTWrapper contract is a contract that allows users to wrap their NFTs with FRC20 tokens.
7The contract is designed to be used with the Fixes ecosystem, and it allows users to wrap their NFTs with FRC20 tokens
8that are minted by the Fixes FRC20 contract.
9
10*/
11import MetadataViews from 0x1d7e57aa55817448
12import ViewResolver from 0x1d7e57aa55817448
13import NonFungibleToken from 0x1d7e57aa55817448
14import FlowToken from 0x1654653399040a61
15import StringUtils from 0xa340dc0a4ec828ab
16import NFTCatalog from 0x49a7cda3a1eecc29
17// Fixes Imports
18import Fixes from 0xd2abb5dbf5e08666
19import FixesInscriptionFactory from 0xd2abb5dbf5e08666
20import FixesWrappedNFT from 0xd2abb5dbf5e08666
21import FRC20Indexer from 0xd2abb5dbf5e08666
22
23access(all) contract FRC20NFTWrapper {
24
25 access(all) entitlement Manage
26 access(all) entitlement Admin
27
28 /// The event that is emitted when the contract is created
29 access(all) event ContractInitialized()
30
31 /// The event that is emitted when the internal flow vault is donated to
32 access(all) event InternalFlowVaultDonated(amount: UFix64)
33
34 /// The event that is emitted when a new Wrapper is created
35 access(all) event WrapperCreated()
36 /// The event that is emitted when the wrapper options is updated
37 access(all) event WrapperOptionsUpdated(wrapper: Address, key: String)
38
39 /// The event that is emitted when the whitelist is updated
40 access(all) event AuthorizedWhitelistUpdated(
41 addr: Address,
42 isAuthorized: Bool,
43 )
44
45 /// The event that is emitted when an NFT is unwrapped
46 access(all) event FRC20StrategyRegistered(
47 wrapper: Address,
48 deployer: Address,
49 nftType: Type,
50 tick: String,
51 alloc: UFix64,
52 copies: UInt64,
53 cond: String?
54 )
55 /// The event that is emitted when an NFT is wrapped
56 access(all) event NFTWrappedWithFRC20Allocated(
57 wrapper: Address,
58 nftType: Type,
59 srcNftId: UInt64,
60 wrappedNftId: UInt64,
61 tick: String,
62 alloc: UFix64,
63 address: Address,
64 )
65
66 // Indexer
67 /// The event that is emitted when a new wrapper is added to the indexer
68 access(all) event WrapperAddedToIndexer(wrapper: Address)
69 /// The event that is emitted when the extra NFT collection display is updated
70 access(all) event WrapperIndexerUpdatedNFTCollectionDisplay(nftType: Type, name: String, description: String)
71
72 /* --- Variable, Enums and Structs --- */
73 access(all)
74 let FRC20NFTWrapperStoragePath: StoragePath
75 access(all)
76 let FRC20NFTWrapperPublicPath: PublicPath
77 access(all)
78 let FRC20NFTWrapperIndexerStoragePath: StoragePath
79 access(all)
80 let FRC20NFTWrapperIndexerPublicPath: PublicPath
81
82
83 /* --- Interfaces & Resources --- */
84
85 access(all) struct FRC20Strategy {
86 access(all) let tick: String
87 access(all) let nftType: Type
88 access(all) let alloc: UFix64
89 access(all) let copies: UInt64
90 access(all) let cond: String?
91 access(all) let createdAt: UFix64
92 access(all) var usedAmt: UInt64
93
94 view init(
95 tick: String,
96 nftType: Type,
97 alloc: UFix64,
98 copies: UInt64,
99 cond: String?
100 ) {
101 self.tick = tick
102 self.nftType = nftType
103 self.alloc = alloc
104 self.copies = copies
105 self.cond = cond
106 self.usedAmt = 0
107 self.createdAt = getCurrentBlock().timestamp
108 }
109
110 access(all)
111 view fun isUsedUp(): Bool {
112 return self.usedAmt >= self.copies
113 }
114
115 access(contract)
116 fun use() {
117 pre {
118 self.usedAmt < self.copies: "The strategy is used up"
119 }
120 self.usedAmt = self.usedAmt + 1
121 }
122 }
123
124
125 access(all) resource interface WrapperPublic {
126 // public methods ----
127
128 /// Get the internal flow vault balance
129 access(all)
130 view fun getInternalFlowBalance(): UFix64
131
132 access(all)
133 view fun hasFRC20Strategy(_ collectionType: Type): Bool
134
135 access(all)
136 view fun getStrategiesAmount(all: Bool): UInt64
137
138 access(all)
139 view fun getStrategies(all: Bool): [FRC20Strategy]
140
141 access(all)
142 view fun isAuthorizedToRegister(addr: Address): Bool
143
144 access(all)
145 view fun getWhitelistedAddresses(): [Address]
146
147 access(all)
148 view fun getOption(key: String): AnyStruct?
149
150 access(all)
151 view fun getOptions(): {String: AnyStruct}
152
153 access(all)
154 fun isFRC20NFTWrappered(nft: &{NonFungibleToken.NFT}): Bool
155
156 // write methods ----
157
158 /// Donate to the internal flow vault
159 access(all)
160 fun donate(value: @FlowToken.Vault): Void
161
162 /// Register a new FRC20 strategy
163 access(all)
164 fun registerFRC20Strategy(
165 type: Type,
166 alloc: UFix64,
167 copies: UInt64,
168 cond: String?,
169 ins: auth(Fixes.Extractable) &Fixes.Inscription,
170 )
171
172 /// Xerox an NFT and wrap it to the FixesWrappedNFT collection
173 ///
174 access(all)
175 fun wrap(
176 recipient: &FixesWrappedNFT.Collection,
177 nftToWrap: @{NonFungibleToken.NFT}
178 ): UInt64
179 }
180
181 /// The resource for the Wrapper contract
182 ///
183 access(all) resource Wrapper: WrapperPublic {
184 access(self)
185 let strategies: {Type: FRC20Strategy}
186 access(self)
187 let histories: {Type: {UInt64: Bool}}
188 access(self)
189 let internalFlowVault: @FlowToken.Vault
190 access(self)
191 let whitelist: {Address: Bool}
192 access(self)
193 let options: {String: AnyStruct}
194
195 init() {
196 self.histories = {}
197 self.strategies = {}
198 self.whitelist = {}
199 self.options = {}
200 self.internalFlowVault <- FlowToken.createEmptyVault(vaultType: Type<@FlowToken.Vault>())
201
202 emit WrapperCreated()
203 }
204
205 // public methods
206
207 access(all)
208 view fun getInternalFlowBalance(): UFix64 {
209 return self.internalFlowVault.balance
210 }
211
212 access(all)
213 fun isFRC20NFTWrappered(nft: &{NonFungibleToken.NFT}): Bool {
214 let collectionType = FRC20NFTWrapper.asCollectionType(nft.getType().identifier)
215 if let nftHistories = &self.histories[collectionType] as &{UInt64: Bool}? {
216 return nftHistories[nft.id] ?? false
217 }
218 return false
219 }
220
221 access(all)
222 view fun hasFRC20Strategy(_ collectionType: Type): Bool {
223 return self.strategies[collectionType] != nil
224 }
225
226 access(all)
227 view fun getStrategiesAmount(all: Bool): UInt64 {
228 if all {
229 return UInt64(self.strategies.length)
230 }
231 return UInt64(self.strategies.values.filter(view fun (s: FRC20Strategy): Bool {
232 return s.isUsedUp() == false
233 }).length)
234 }
235
236 access(all)
237 view fun getStrategies(all: Bool): [FRC20Strategy] {
238 if all {
239 return self.strategies.values
240 }
241 return self.strategies.values.filter(view fun (s: FRC20Strategy): Bool {
242 return s.isUsedUp() == false
243 })
244 }
245
246 access(all)
247 view fun isAuthorizedToRegister(addr: Address): Bool {
248 return addr == self.owner?.address || (self.whitelist[addr] ?? false)
249 }
250
251 access(all)
252 view fun getWhitelistedAddresses(): [Address] {
253 let refDict = &self.whitelist as &{Address: Bool}
254 return self.whitelist.keys.filter(view fun (addr: Address): Bool {
255 return refDict[addr]! == true
256 })
257 }
258
259 access(all)
260 view fun getOption(key: String): AnyStruct? {
261 return self.options[key]
262 }
263
264 access(all)
265 view fun getOptions(): {String: AnyStruct} {
266 return self.options
267 }
268
269 // write methods
270
271 access(all)
272 fun donate(value: @FlowToken.Vault): Void {
273 pre {
274 value.balance > 0.0: "Donation must be greater than 0"
275 }
276 let amt = value.balance
277 self.internalFlowVault.deposit(from: <- value)
278 emit InternalFlowVaultDonated(amount: amt)
279 }
280
281 /// Register a new FRC20 strategy
282 access(all)
283 fun registerFRC20Strategy(
284 type: Type,
285 alloc: UFix64,
286 copies: UInt64,
287 cond: String?,
288 ins: auth(Fixes.Extractable) &Fixes.Inscription,
289 ) {
290 pre {
291 ins.isExtractable(): "The inscription is not extractable"
292 }
293 let indexer = FRC20Indexer.getIndexer()
294 assert(
295 indexer.isValidFRC20Inscription(ins: ins),
296 message: "The inscription is not a valid FRC20 inscription"
297 )
298 let fromAddr = ins.owner?.address ?? panic("Inscription owner is nil")
299 let data = FixesInscriptionFactory.parseMetadata(ins.borrowData())
300 assert(
301 data["op"] == "transfer" && data["tick"] != nil && data["amt"] != nil && data["to"] != nil,
302 message: "The inscription is not a valid FRC20 inscription for transfer"
303 )
304 let tick: String = data["tick"]!.toLower()
305 let meta = indexer.getTokenMeta(tick: tick)
306 ?? panic("Could not get token meta for ".concat(tick))
307
308 /// check if the deployer is the owner of the inscription
309 assert(
310 meta.deployer == fromAddr,
311 message: "The frc20 deployer is not the owner of the inscription"
312 )
313
314 // check if the deployer is authorized to register a new strategy
315 assert(
316 self.isAuthorizedToRegister(addr: fromAddr),
317 message: "The deployer is not authorized to register a new strategy"
318 )
319
320 // ensure store as collection type
321 let collectionType = FRC20NFTWrapper.asCollectionType(type.identifier)
322 assert(
323 collectionType.identifier != Type<@FixesWrappedNFT.Collection>().identifier,
324 message: "You cannot wrap a FixesWrappedNFT"
325 )
326
327 // check if the strategy already exists
328 assert(
329 self.strategies[collectionType] == nil,
330 message: "The strategy already exists"
331 )
332
333 // ensure condition is valid
334 if let condStr = cond {
335 let conds = StringUtils.split(condStr, ",")
336 for one in conds {
337 let subConds = StringUtils.split(one, "~")
338 if subConds.length == 1 && UInt64.fromString(subConds[0]) != nil {
339 continue
340 } else if subConds.length == 2 && UInt64.fromString(subConds[0]) != nil && UInt64.fromString(subConds[1]) != nil {
341 continue
342 } else {
343 panic("Invalid condition")
344 }
345 }
346 }
347
348 // indexer address
349 let indexerAddr = FRC20Indexer.getAddress()
350
351 // check if the allocation is enough
352 let amt = UFix64.fromString(data["amt"]!) ?? panic("The amount is not a valid UFix64")
353 let to = Address.fromString(data["to"]!) ?? panic("The receiver is not a valid address")
354 let toAllocateAmt = alloc * UFix64(copies)
355 assert(
356 amt >= toAllocateAmt,
357 message: "The amount is not enough to allocate"
358 )
359 assert(
360 to == indexerAddr,
361 message: "The receiver is not the indexer"
362 )
363
364 // apply inscription for transfer
365 indexer.transfer(ins: ins)
366
367 // ensure frc20 is enough
368 let frc20BalanceForContract = indexer.getBalance(tick: tick, addr: indexerAddr)
369 assert(
370 frc20BalanceForContract >= toAllocateAmt,
371 message: "The FRC20 balance for the contract is not enough"
372 )
373
374 // setup strategy
375 self.strategies[collectionType] = FRC20Strategy(
376 tick: tick,
377 nftType: collectionType,
378 alloc: alloc,
379 copies: copies,
380 cond: cond,
381 )
382 // setup history
383 self.histories[collectionType] = {}
384
385 // emit event
386 emit FRC20StrategyRegistered(
387 wrapper: self.owner?.address ?? panic("Wrapper owner is nil"),
388 deployer: fromAddr,
389 nftType: collectionType,
390 tick: tick,
391 alloc: alloc,
392 copies: copies,
393 cond: cond,
394 )
395 }
396
397 /// Wrap an NFT and wrap it to the FixesWrappedNFT collection
398 ///
399 access(all)
400 fun wrap(
401 recipient: &FixesWrappedNFT.Collection,
402 nftToWrap: @{NonFungibleToken.NFT}
403 ): UInt64 {
404 // check if the NFT is owned by the signer
405 let recipientAddr = recipient.owner?.address ?? panic("Recipient owner is nil")
406 // get the NFT type
407 let nftTypeIdentifier = nftToWrap.getType().identifier
408 // generate the collection type
409 let nftType = FRC20NFTWrapper.asCollectionType(nftTypeIdentifier)
410 // get the NFT id
411 let srcNftId = nftToWrap.id
412 // check if the strategy exists, and borrow it
413 let strategy = self.borrowStrategy(nftType: nftType)
414 // check if the strategy is used up
415 assert(
416 strategy.usedAmt < strategy.copies,
417 message: "The strategy is used up"
418 )
419
420 // check strategy condition
421 if let condStr = strategy.cond {
422 var valid: Bool = false
423 let conds = StringUtils.split(condStr, ",")
424 for one in conds {
425 let subConds = StringUtils.split(one, "~")
426 if subConds.length == 1 {
427 // check if the NFT id is the same
428 valid = valid || UInt64.fromString(subConds[0]) == srcNftId
429 } else if subConds.length == 2 {
430 // check if the NFT id is in the range
431 let start = UInt64.fromString(subConds[0]) ?? panic("Invalid condition")
432 let end = UInt64.fromString(subConds[1]) ?? panic("Invalid condition")
433 // check if the range is valid, and the NFT id is in the range
434 // NOTE: the range is [start, end)
435 valid = valid || (start <= srcNftId && srcNftId < end)
436 } else {
437 panic("Invalid condition")
438 }
439 // break if valid
440 if valid {
441 break
442 }
443 }
444
445 assert(
446 valid,
447 message: "The NFT ID does not meet the condition:".concat(condStr)
448 )
449 }
450
451 // borrow the history
452 let history = self.borrowHistory(nftType: nftType)
453 // check if the NFT is already wrapped
454 assert(
455 history[nftToWrap.id] == nil,
456 message: "The NFT is already wrapped"
457 )
458
459 // basic attributes
460 let mimeType = "text/plain"
461 let metaProtocol = "frc20"
462 let dataStr = "op=alloc,tick=".concat(strategy.tick)
463 .concat(",amt=").concat(strategy.alloc.toString())
464 .concat(",to=").concat(recipientAddr.toString())
465 let metadata = dataStr.utf8
466
467 // estimate the required storage
468 let estimatedReqValue = Fixes.estimateValue(
469 index: Fixes.totalInscriptions,
470 mimeType: mimeType,
471 data: metadata,
472 protocol: metaProtocol,
473 encoding: nil
474 )
475
476 // Get a reference to the signer's stored vault
477 let flowToReserve <- self.internalFlowVault.withdraw(amount: estimatedReqValue)
478
479 // Create the Inscription first
480 let newIns <- Fixes.createInscription(
481 // Withdraw tokens from the signer's stored vault
482 value: <- (flowToReserve as! @FlowToken.Vault),
483 mimeType: mimeType,
484 metadata: metadata,
485 metaProtocol: metaProtocol,
486 encoding: nil,
487 parentId: nil
488 )
489 // mint the wrapped NFT
490 let newId = FixesWrappedNFT.wrap(recipient: recipient, nftToWrap: <- nftToWrap, inscription: <- newIns)
491
492 // borrow the inscription
493 let nft = recipient.borrowFixesWrappedNFT(newId) ?? panic("Could not borrow FixesWrappedNFT")
494 let insRef = nft.borrowInscription() ?? panic("Could not borrow inscription")
495
496 // get FRC20 indexer
497 let indexer = FRC20Indexer.getIndexer()
498 let used <- indexer.allocate(ins: insRef)
499
500 // deposit the unused flow back to the internal flow vault
501 self.internalFlowVault.deposit(from: <- used)
502
503 // update strategy used one time
504 strategy.use()
505
506 // update histories
507 history[srcNftId] = true
508
509 // emit event
510 emit NFTWrappedWithFRC20Allocated(
511 wrapper: self.owner?.address ?? panic("Wrapper owner is nil"),
512 nftType: nftType,
513 srcNftId: srcNftId,
514 wrappedNftId: newId,
515 tick: strategy.tick,
516 alloc: strategy.alloc,
517 address: recipientAddr,
518 )
519
520 return newId
521 }
522
523 // private methods
524
525 /// Update the wrapper options
526 access(Manage)
527 fun updateOptions(key: String, value: AnyStruct) {
528 self.options[key] = value
529
530 emit WrapperOptionsUpdated(
531 wrapper: self.owner?.address ?? panic("Wrapper owner is nil"),
532 key: key
533 )
534 }
535
536 /// Update the whitelist
537 ///
538 access(Manage)
539 fun updateWhitelist(addr: Address, isAuthorized: Bool): Void {
540 self.whitelist[addr] = isAuthorized
541
542 emit AuthorizedWhitelistUpdated(
543 addr: addr,
544 isAuthorized: isAuthorized,
545 )
546 }
547
548 // internal methods
549
550 /// Borrow the strategy for an NFT type
551 ///
552 access(self)
553 view fun borrowStrategy(nftType: Type): &FRC20Strategy {
554 return &self.strategies[nftType] as &FRC20Strategy?
555 ?? panic("Could not borrow strategy")
556 }
557
558 /// Borrow the history for an NFT type
559 ///
560 access(self)
561 view fun borrowHistory(nftType: Type): auth(Mutate) &{UInt64: Bool} {
562 return &self.histories[nftType] as auth(Mutate) &{UInt64: Bool}?
563 ?? panic("Could not borrow history")
564 }
565 }
566
567 /// The public resource interface for the Wrapper Indexer
568 ///
569 access(all) resource interface WrapperIndexerPublic {
570 // public methods ----
571
572 /// Check if the wrapper is registered
573 ///
574 access(all)
575 view fun hasRegisteredWrapper(addr: Address): Bool
576
577 /// Get all the wrappers
578 access(all)
579 view fun getAllWrappers(_ includeNoStrategy: Bool, _ includeFinished: Bool): [Address]
580
581 /// Get the public reference to the Wrapper resource
582 ///
583 access(all)
584 view fun borrowWrapperPublic(addr: Address): &{WrapperPublic}? {
585 return FRC20NFTWrapper.borrowWrapperPublic(addr: addr)
586 }
587
588 /// Get the NFT collection display
589 ///
590 access(all)
591 fun getNFTCollectionDisplay(
592 nftType: Type,
593 ): MetadataViews.NFTCollectionDisplay
594
595 // ---- write methods ----
596
597 /// Register a new Wrapper
598 access(all)
599 fun registerWrapper(wrapper: auth(Manage) &Wrapper)
600 }
601
602 /// The resource for the Wrapper indexer contract
603 ///
604 access(all) resource WrapperIndexer: WrapperIndexerPublic {
605 /// The event that is emitted when the contract is created
606 access(self)
607 let wrappers: {Address: Bool}
608 access(self)
609 let displayHelper: {Type: MetadataViews.NFTCollectionDisplay}
610
611 init() {
612 self.wrappers = {}
613 self.displayHelper = {}
614 }
615
616 // public methods ----
617
618 /// Check if the wrapper is registered
619 ///
620 access(all)
621 view fun hasRegisteredWrapper(addr: Address): Bool {
622 return self.wrappers[addr] != nil
623 }
624
625 /// Get all the wrappers
626 ///
627 access(all)
628 view fun getAllWrappers(
629 _ includeNoStrategy: Bool,
630 _ includeFinished: Bool,
631 ): [Address] {
632 return self.wrappers.keys.filter(view fun (addr: Address): Bool {
633 if let wrapper = FRC20NFTWrapper.borrowWrapperPublic(addr: addr) {
634 return includeNoStrategy ? true : wrapper.getStrategiesAmount(all: includeFinished) > 0
635 } else {
636 return false
637 }
638 })
639 }
640
641 /// Get the extra NFT collection display
642 ///
643 access(all)
644 fun getNFTCollectionDisplay(
645 nftType: Type,
646 ): MetadataViews.NFTCollectionDisplay {
647 let collectionType = FRC20NFTWrapper.asCollectionType(nftType.identifier)
648 let nftType = FRC20NFTWrapper.asNFTType(nftType.identifier)
649 // get from NFTCatalog first
650 if let entries: {String: Bool} = NFTCatalog.getCollectionsForType(nftTypeIdentifier: nftType.identifier) {
651 for colId in entries.keys {
652 if let catalogEntry = NFTCatalog.getCatalogEntry(collectionIdentifier: colId) {
653 return catalogEntry.collectionDisplay
654 }
655 }
656 }
657 // if no exists, then get from display helper
658 if let display = self.displayHelper[collectionType] {
659 return display
660 }
661 let defaultDisplay = FixesWrappedNFT.resolveContractView(resourceType: Type<@FixesWrappedNFT.NFT>(), viewType: Type<MetadataViews.NFTCollectionDisplay>()) as! MetadataViews.NFTCollectionDisplay?
662 ?? panic("Could not resolve default display")
663 let ids = StringUtils.split(nftType.identifier, ".")
664 return MetadataViews.NFTCollectionDisplay(
665 name: ids[2],
666 description: "NFT Collection built by address ".concat(ids[1]),
667 externalURL: defaultDisplay.externalURL,
668 squareImage: defaultDisplay.squareImage,
669 bannerImage: defaultDisplay.bannerImage,
670 socials: defaultDisplay.socials
671 )
672 }
673
674 // ---- write methods ----
675
676 /// Register a new Wrapper
677 access(all)
678 fun registerWrapper(wrapper: auth(Manage) &Wrapper) {
679 pre {
680 wrapper.owner != nil: "Wrapper owner is nil"
681 }
682 let ownerAddr = wrapper.owner!.address
683 self.wrappers[ownerAddr] = true
684
685 emit WrapperAddedToIndexer(wrapper: ownerAddr)
686 }
687
688 // ---- private write methods ----
689
690 access(Admin)
691 fun updateExtraNFTCollectionDisplay(
692 nftType: Type,
693 display: MetadataViews.NFTCollectionDisplay,
694 ): Void {
695 let collectionType = FRC20NFTWrapper.asCollectionType(nftType.identifier)
696 self.displayHelper[collectionType] = display
697
698 emit WrapperIndexerUpdatedNFTCollectionDisplay(
699 nftType: collectionType,
700 name: display.name,
701 description: display.description,
702 )
703 }
704 }
705
706 /// Donate to the internal flow vault
707 ///
708 access(all)
709 fun donate(
710 addr: Address,
711 _ value: @FlowToken.Vault
712 ): Void {
713 let ref = self.borrowWrapperPublic(addr: addr)
714 ?? panic("Could not borrow Xerox public reference")
715 ref.donate(value: <- value)
716 }
717
718 /// Create a new Wrapper resourceTON
719 ///
720 access(all)
721 fun createNewWrapper(): @Wrapper {
722 return <- create Wrapper()
723 }
724
725 /// Make a new NFT type
726 ///
727 access(all)
728 fun asNFTType(_ identifier: String): Type {
729 let ids = StringUtils.split(identifier, ".")
730 assert(ids.length == 4, message: "Invalid NFT type identifier!")
731 ids[3] = "NFT" // replace the last part with NFT
732 return CompositeType(StringUtils.join(ids, "."))!
733 }
734
735 /// Make a new NFT Collection type
736 ///
737 access(all)
738 fun asCollectionType(_ identifier: String): Type {
739 let ids = StringUtils.split(identifier, ".")
740 assert(ids.length == 4, message: "Invalid NFT Collection type identifier!")
741 ids[3] = "Collection" // replace the last part with Collection
742 return CompositeType(StringUtils.join(ids, "."))!
743 }
744
745 /// Borrow the public reference to the Wrapper resource
746 ///
747 access(all)
748 view fun borrowWrapperPublic(
749 addr: Address,
750 ): &{WrapperPublic}? {
751 return getAccount(addr)
752 .capabilities.get<&{WrapperPublic}>(self.FRC20NFTWrapperPublicPath)
753 .borrow()
754 }
755
756 /// Borrow the public interface to the Wrapper Indexer resource
757 ///
758 access(all)
759 view fun borrowWrapperIndexerPublic(): &{WrapperIndexerPublic} {
760 return getAccount(self.account.address)
761 .capabilities.get<&{WrapperIndexerPublic}>(self.FRC20NFTWrapperIndexerPublicPath)
762 .borrow()
763 ?? panic("Could not borrow WrapperIndexer public reference")
764 }
765
766 /// Get the NFT collection display
767 ///
768 access(all)
769 fun getNFTCollectionDisplay(
770 nftType: Type,
771 ): MetadataViews.NFTCollectionDisplay {
772 return self.borrowWrapperIndexerPublic().getNFTCollectionDisplay(nftType: nftType)
773 }
774
775 /// init
776 init() {
777 let identifier = "FixesFRC20NFTWrapper_".concat(self.account.address.toString())
778 self.FRC20NFTWrapperStoragePath = StoragePath(identifier: identifier)!
779 self.FRC20NFTWrapperPublicPath = PublicPath(identifier: identifier)!
780
781 self.FRC20NFTWrapperIndexerStoragePath = StoragePath(identifier: identifier.concat("_indexer"))!
782 self.FRC20NFTWrapperIndexerPublicPath = PublicPath(identifier: identifier.concat("_indexer"))!
783
784 // default wrapper
785 self.account.storage.save(<- self.createNewWrapper(), to: FRC20NFTWrapper.FRC20NFTWrapperStoragePath)
786 self.account.capabilities.publish(
787 self.account.capabilities.storage.issue<&FRC20NFTWrapper.Wrapper>(FRC20NFTWrapper.FRC20NFTWrapperStoragePath),
788 at: FRC20NFTWrapper.FRC20NFTWrapperPublicPath
789 )
790
791 // create indexer
792 let indexer <- create WrapperIndexer()
793 // save the indexer
794 self.account.storage.save(<- indexer, to: self.FRC20NFTWrapperIndexerStoragePath)
795 self.account.capabilities.publish(
796 self.account.capabilities.storage.issue<&FRC20NFTWrapper.WrapperIndexer>(self.FRC20NFTWrapperIndexerStoragePath),
797 at: self.FRC20NFTWrapperIndexerPublicPath
798 )
799
800 let indexerRef = self.account.storage.borrow<&FRC20NFTWrapper.WrapperIndexer>(from: self.FRC20NFTWrapperIndexerStoragePath)
801 ?? panic("Could not borrow indexer public reference")
802
803 // register the wrapper to the indexer
804 let wrapper = self.account.storage
805 .borrow<auth(Manage) &Wrapper>(from: FRC20NFTWrapper.FRC20NFTWrapperStoragePath)
806 ?? panic("Could not borrow wrapper public reference")
807 indexerRef.registerWrapper(wrapper: wrapper)
808
809 emit ContractInitialized()
810 }
811}
812