Smart Contract

SoulMadePack

A.9a57dfe5c8ce609c.SoulMadePack

Deployed

2h ago
Feb 28, 2026, 06:38:20 PM UTC

Dependents

0 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2import SoulMadeComponent from 0x9a57dfe5c8ce609c
3import SoulMadeMain from 0x9a57dfe5c8ce609c
4import MetadataViews from 0x1d7e57aa55817448
5import FlowToken from 0x1654653399040a61
6import FungibleToken from 0xf233dcee88fe0abe
7
8pub contract SoulMadePack: NonFungibleToken {
9
10    pub var totalSupply: UInt64
11
12    access(self) var freeClaim : {Address : String}
13
14    pub event ContractInitialized()
15    pub event Withdraw(id: UInt64, from: Address?)
16    pub event Deposit(id: UInt64, to: Address?)
17
18    pub event WithdrawnMain(id: UInt64, mainDetail: SoulMadeMain.MainDetail)
19    pub event WithdrawnComponent(id: UInt64, componentDetail: SoulMadeComponent.ComponentDetail)
20
21
22    pub event SoulMadePackOpened(id: UInt64, packDetail: PackDetail, to: Address?)
23    pub event SoulMadePackFreeClaim(id: UInt64, from: Address, series: String)
24
25    pub let CollectionStoragePath: StoragePath
26    pub let CollectionPublicPath: PublicPath
27    pub let CollectionPrivatePath: PrivatePath
28
29    pub let CollectionFreeClaimStoragePath: StoragePath
30    pub let CollectionFreeClaimPublicPath: PublicPath
31    pub let CollectionFreeClaimPrivatePath: PrivatePath
32
33    pub struct PackDetail{
34        pub let id: UInt64
35        pub let scarcity: String
36        pub let series: String
37        pub let ipfsHash: String
38
39        init(id: UInt64,
40                scarcity: String,
41                series: String,
42                ipfsHash: String){
43            self.id = id
44            self.scarcity = scarcity
45            self.series = series
46            self.ipfsHash = ipfsHash
47        }
48    }
49
50    pub struct MainComponentNftIds{
51      pub let mainNftIds: [UInt64]
52      pub let componentNftIds: [UInt64]
53
54      init(mainNftIds: [UInt64], componentNftIds: [UInt64]){
55        self.mainNftIds = mainNftIds
56        self.componentNftIds = componentNftIds
57      }
58
59    }
60
61    pub resource interface PackPublic {
62        pub let id: UInt64
63        pub let packDetail: PackDetail
64    }
65
66    pub resource NFT: NonFungibleToken.INFT, PackPublic, MetadataViews.Resolver {
67        pub let id: UInt64
68        pub let packDetail: PackDetail
69
70        pub var mainNft: @{UInt64: SoulMadeMain.NFT}
71        pub var componentNft: @{UInt64: SoulMadeComponent.NFT}
72
73        pub fun getMainComponentIds(): MainComponentNftIds {
74          return MainComponentNftIds(mainNftIds: self.mainNft.keys, componentNftIds: self.componentNft.keys)
75        }
76
77        pub fun depositMain(mainNft: @SoulMadeMain.NFT) {
78          var old <- self.mainNft[mainNft.id] <- mainNft
79          destroy old
80        }
81
82        pub fun depositComponent(componentNft: @SoulMadeComponent.NFT) {
83          var old <- self.componentNft[componentNft.id] <- componentNft
84          destroy old
85        }
86
87        pub fun withdrawMain(mainNftId: UInt64): @SoulMadeMain.NFT? {
88          let mainWithdraw <- self.mainNft.remove(key: mainNftId)
89          let mainWithdrawRef: &SoulMadeMain.NFT? = &mainWithdraw as &SoulMadeMain.NFT?
90          emit WithdrawnMain(id: mainWithdrawRef!.id, mainDetail: mainWithdrawRef!.mainDetail)
91          return <- mainWithdraw
92        }
93
94        pub fun withdrawComponent(componentNftId: UInt64): @SoulMadeComponent.NFT? {
95          let componentWithdraw <- self.componentNft.remove(key: componentNftId)
96          let componentWithdrawRef: &SoulMadeComponent.NFT? = &componentWithdraw as &SoulMadeComponent.NFT?
97          emit WithdrawnComponent(id: componentWithdrawRef!.id, componentDetail: componentWithdrawRef!.componentDetail)
98          return <- componentWithdraw
99        }
100
101        pub fun getViews(): [Type] {
102            return [
103                Type<MetadataViews.Display>(),
104                Type<MetadataViews.Royalties>(),
105                Type<MetadataViews.ExternalURL>(),
106                Type<MetadataViews.NFTCollectionData>(),
107                Type<MetadataViews.NFTCollectionDisplay>()
108            ]
109        }
110
111        pub fun resolveView(_ view: Type): AnyStruct? {
112            switch view {
113                case Type<MetadataViews.Display>():
114                    return MetadataViews.Display(
115                        name: "SoulMadepack",
116                        description: "SoulMadePack Contains Main and Component NFTs",
117                        thumbnail: MetadataViews.IPFSFile(
118                            cid: self.packDetail.ipfsHash,
119                            path: nil
120                        )
121                    )
122                case Type<MetadataViews.Royalties>():
123                    return MetadataViews.Royalties([
124						MetadataViews.Royalty(
125							recepient: getAccount(0x9a57dfe5c8ce609c).getCapability<&FlowToken.Vault{FungibleToken.Receiver}>(/public/flowTokenReceiver),
126							cut: 0.00, 
127							description: "SoulMade Pack Royalties"
128						)
129					])
130                case Type<MetadataViews.ExternalURL>():
131                    return MetadataViews.ExternalURL("www.soulmade.art")
132                case Type<MetadataViews.NFTCollectionData>():
133                    return MetadataViews.NFTCollectionData(
134                        storagePath: SoulMadePack.CollectionStoragePath,
135                        publicPath: SoulMadePack.CollectionPublicPath,
136                        providerPath: /private/SoulMadePackCollection,
137                        publicCollection: Type<&Collection{CollectionPublic}>(),
138                        publicLinkedType: Type<&Collection{CollectionPublic, NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, MetadataViews.ResolverCollection}>(),
139                        providerLinkedType: Type<&Collection{CollectionPublic, NonFungibleToken.CollectionPublic, NonFungibleToken.Provider, MetadataViews.ResolverCollection}>(),
140                        createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection {
141                            return <- SoulMadePack.createEmptyCollection()
142                        })
143                    )
144                case Type<MetadataViews.NFTCollectionDisplay>():
145                    let squareMedia = MetadataViews.Media(
146                        file: MetadataViews.HTTPFile(
147                           url: "https://i.imgur.com/XgqDY3s.jpg"
148                        ),
149                        mediaType: "image"
150                    )
151                    let bannerMedia = MetadataViews.Media(
152                        file: MetadataViews.HTTPFile(
153                            url: "https://i.imgur.com/yBw3ktk.png"
154                        ),
155                        mediaType: "image"
156                    )
157                    return MetadataViews.NFTCollectionDisplay(
158                        name: "SoulMadeMain",
159                        description: "SoulMade Main Collection",
160                        externalURL: MetadataViews.ExternalURL("www.soulmade.art"),
161                        squareImage: squareMedia,
162                        bannerImage: bannerMedia,
163                        socials: {
164                            "twitter": MetadataViews.ExternalURL("https://twitter.com/soulmade_nft"),
165                            "discord": MetadataViews.ExternalURL("https://discord.com/invite/xtqqXCKW9B")
166                        }
167                    )
168            }
169
170            return nil
171        }
172
173        init(initID: UInt64, packDetail: PackDetail) {
174            self.id = initID
175            self.packDetail = packDetail
176            self.mainNft <- {}
177            self.componentNft <- {}
178        }
179
180        destroy() {
181            destroy self.mainNft
182            destroy self.componentNft
183        }        
184    }
185
186    pub resource interface CollectionPublic {
187        pub fun deposit(token: @NonFungibleToken.NFT)
188        pub fun getIDs(): [UInt64]
189        pub fun borrowViewResolver(id: UInt64): &{MetadataViews.Resolver}
190        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
191        pub fun borrowPack(id: UInt64): &{SoulMadePack.PackPublic}
192    }
193
194    pub resource interface CollectionFreeClaim {
195        pub fun deposit(token: @NonFungibleToken.NFT)
196        pub fun borrowViewResolver(id: UInt64): &{MetadataViews.Resolver}
197        pub fun freeClaim(mainNftCollectionRef: &{SoulMadeMain.CollectionPublic}?, componentNftCollectionRef: &{SoulMadeComponent.CollectionPublic}?)
198    }
199
200    pub resource Collection: CollectionPublic, CollectionFreeClaim, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic,MetadataViews.ResolverCollection {
201        pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
202        init () {
203            self.ownedNFTs <- {}
204        }
205
206        pub fun openPackNft(pack: @SoulMadePack.NFT, mainNftCollectionRef: &{SoulMadeMain.CollectionPublic}?, componentNftCollectionRef: &{SoulMadeComponent.CollectionPublic}?) {
207          
208          emit SoulMadePackOpened(id: pack.id, packDetail: pack.packDetail , to: self.owner?.address)
209
210          let mainComponentIds = pack.getMainComponentIds()
211
212          let mainNftIds = mainComponentIds.mainNftIds
213          let componentNftIds = mainComponentIds.componentNftIds
214
215          if(mainNftIds.length > 0 && mainNftCollectionRef != nil){
216            for mainNftId in mainNftIds{
217              var nft <- pack.withdrawMain(mainNftId: mainNftId)! as @NonFungibleToken.NFT
218              mainNftCollectionRef!.deposit(token: <- nft)
219            }
220          } else if mainNftIds.length > 0 && mainNftCollectionRef == nil {
221            panic("reference is null")
222          }
223
224          if(componentNftIds.length > 0 && componentNftIds != nil){
225            for componentNftId in componentNftIds{
226              var nft <- pack.withdrawComponent(componentNftId: componentNftId)! as @NonFungibleToken.NFT
227              componentNftCollectionRef!.deposit(token: <- nft)
228            }
229          } else if componentNftIds.length > 0 && componentNftCollectionRef == nil {
230            panic("reference is null")
231          }
232  
233          destroy pack
234        }
235
236      pub fun borrowViewResolver(id: UInt64): &{MetadataViews.Resolver} {
237            let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
238            let packNFT = nft as! &SoulMadePack.NFT
239            return packNFT as &{MetadataViews.Resolver}
240        }    
241
242        pub fun openPackFromCollection(id: UInt64, mainNftCollectionRef: &{SoulMadeMain.CollectionPublic}?, componentNftCollectionRef: &{SoulMadeComponent.CollectionPublic}?) {     
243          let pack <- self.withdraw(withdrawID: id) as! @SoulMadePack.NFT
244          self.openPackNft(pack: <- pack, mainNftCollectionRef: mainNftCollectionRef, componentNftCollectionRef: componentNftCollectionRef)
245        }
246
247        pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
248          let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing Pack NFT")
249
250          emit Withdraw(id: token.id, from: self.owner?.address)
251
252          return <- token
253        }
254
255        pub fun deposit(token: @NonFungibleToken.NFT) {
256            let token <- token as! @SoulMadePack.NFT
257            let id: UInt64 = token.id
258            let oldToken <- self.ownedNFTs[id] <- token
259
260            emit Deposit(id: id, to: self.owner?.address)
261
262            destroy oldToken
263        }
264
265        pub fun getIDs(): [UInt64] {
266            return self.ownedNFTs.keys
267        }
268
269        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
270            return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
271        }
272
273        pub fun borrowPack(id: UInt64): &{SoulMadePack.PackPublic} {    
274            pre {
275                self.ownedNFTs[id] != nil: "Main NFT doesn't exist"
276            }
277            let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
278            return ref as! &SoulMadePack.NFT
279        }
280
281        pub fun freeClaim(mainNftCollectionRef: &{SoulMadeMain.CollectionPublic}?, componentNftCollectionRef: &{SoulMadeComponent.CollectionPublic}?) {
282          pre {
283            mainNftCollectionRef!.owner!.address == componentNftCollectionRef!.owner!.address: "Main and Component NFTs must be owned by the same user"
284            SoulMadePack.checkClaimed(address: mainNftCollectionRef!.owner!.address) == false: "This address has already claimed"
285            self.ownedNFTs.length > 0: "All have been claimed"
286          }
287
288          let token <- self.ownedNFTs.remove(key: self.ownedNFTs.keys[0]) ?? panic("Giveaway Pack is not enough")
289          
290          let pack <- token as! @SoulMadePack.NFT
291
292          var series = pack.packDetail.series
293
294          SoulMadePack.updateClaimDictionary(address: mainNftCollectionRef!.owner!.address, series: series)
295          emit SoulMadePackFreeClaim(id: pack.id, from: componentNftCollectionRef!.owner!.address, series: series)
296
297          self.openPackNft(pack: <- pack, mainNftCollectionRef: mainNftCollectionRef, componentNftCollectionRef: componentNftCollectionRef)
298        }
299
300        destroy() {
301            destroy self.ownedNFTs
302        }
303    }
304
305
306    pub fun createEmptyCollection(): @NonFungibleToken.Collection {
307        return <- create Collection()
308    }
309    
310    access(account) fun mintPack(scarcity: String, series: String, ipfsHash: String, mainNfts: @[SoulMadeMain.NFT], componentNfts: @[SoulMadeComponent.NFT]) : @NFT {
311
312      let packDetail = PackDetail(
313          id: SoulMadePack.totalSupply,
314          scarcity: scarcity,
315          series: series,
316          ipfsHash: ipfsHash        
317      )
318
319      var pack <- create NFT(initID: SoulMadePack.totalSupply, packDetail: packDetail)
320      SoulMadePack.totalSupply = SoulMadePack.totalSupply + UInt64(1)
321      while mainNfts.length > 0{
322        pack.depositMain(mainNft: <- mainNfts.removeFirst())
323      }
324
325      while componentNfts.length > 0{
326        pack.depositComponent(componentNft: <- componentNfts.removeFirst())
327      }
328
329      destroy mainNfts
330      destroy componentNfts
331      return <- pack
332    }
333
334    pub fun checkClaimed(address: Address?): Bool {
335      if self.freeClaim[address!] != nil {
336        return true
337      }
338      return false
339    }
340
341    pub fun getFreeClaimDictionary() : {Address : String} {
342      return self.freeClaim
343    }
344
345    pub fun getAllFreeClaimAddress() : [Address] {
346      return self.freeClaim.keys
347    }
348
349    access(account) fun updateClaimDictionary(address:Address, series:String){
350      self.freeClaim[address] = series
351    }
352
353    access(account) fun renewClaimDictionary(){
354      self.freeClaim = {}
355    }
356
357    init() {
358        self.totalSupply = 0
359        self.freeClaim = {}
360
361        self.CollectionPublicPath = /public/SoulMadePackCollection
362        self.CollectionStoragePath = /storage/SoulMadePackCollection
363        self.CollectionPrivatePath = /private/SoulMadePackCollection
364                
365        self.CollectionFreeClaimPublicPath = /public/SoulMadePackCollectionFreeClaim
366        self.CollectionFreeClaimStoragePath = /storage/SoulMadePackCollectionFreeClaim
367        self.CollectionFreeClaimPrivatePath = /private/SoulMadePackCollectionFreeClaim
368
369        emit ContractInitialized()
370    }
371}
372