Smart Contract
SoulMadePack
A.9a57dfe5c8ce609c.SoulMadePack
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