Smart Contract
HWGaragePM
A.d0bcefdf1e67ea85.HWGaragePM
1/*
2* A contract that manages the creation and sale of packs and tokens
3*
4* A manager resource exists allow modifying the parameters of the public
5* sale and have the capability to mint editions themselves
6*/
7
8import NonFungibleToken from 0x1d7e57aa55817448
9import FungibleToken from 0xf233dcee88fe0abe
10import FlowToken from 0x1654653399040a61
11import HWGarageCard from 0xd0bcefdf1e67ea85
12import HWGaragePack from 0xd0bcefdf1e67ea85
13
14
15access(all) contract HWGaragePM {
16 /*
17 * Events
18 *
19 * emitted when the contract is deployed
20 */
21 access(all) event ContractInitialized()
22
23 /*
24 * HWGarageCard
25 *
26 * emmited when an admin has initiated a mint of a HWGarageCard
27 */
28 access(all) event AdminMintHWGarageCard(id: UInt64)
29 // emmited when the metadata for an HWGarageCard collection is updated
30 access(all) event UpdateHWGarageCardCollectionMetadata()
31 // emmited when an edition within HWGarageCard has had it's metdata updated
32 access(all) event UpdateTokenEditionMetadata(id: UInt64, metadata: {String: String}, packHash: String, address: Address)
33
34 /*
35 * HWGaragePack
36 *
37 * emitted when an admin has initiated a mint of a HWGarageCard
38 */
39 access(all) event AdminMintPack(id: UInt64, packHash: String)
40 // emitted when someone redeems a HWGaragePack for Cards
41 access(all) event RedeemPack(id: UInt64, packID: UInt64, packEditionID: UInt64, address: Address, packHash: String)
42 // emitted when Pack has metadata updated
43 access(all) event UpdatePackCollectionMetadata()
44 // emitted when PackEdition updates metadata
45 access(all) event UpdatePackEditionMetadata(id: UInt64, metadata: {String: String})
46 // emitted when any information about redemption has been modified
47 access(all) event UpdateHWGaragePackRedeemInfo(redeemStartTime: UFix64)
48 // emitted when a user submits a packHash to claim a pack
49 access(all) event PackClaimBegin(address: Address, packHash: String)
50 // emitted when a user succesfully claims a pack
51 access(all) event PackClaimSuccess(address: Address, packHash: String, packID: UInt64)
52
53 /*
54 * HWGarageAirdrop
55 *
56 * emitted when an admin has initiates an airdrop to a wallet
57 */
58 // V2 is depracated
59 access(all) event AirdropRedeemableV2(id:UInt64, address: Address)
60
61 access(all) event AirdropRedeemable(
62 WalletAddress: Address
63 , TokenID:UInt64
64 , TokenMintID: UInt64
65 , OriginalCardSerial: String
66 , TokenSerial: String
67 , SeriesName: String
68 , Name: String
69 , TokenImageHash: String
70 , TokenReleaseDate: String
71 , TokenExpireDate: String
72 , CardID: UInt64
73 , TemplateID: String
74 )
75
76 // emitted when a user initiates a burn on redeemable airdrop
77 access(all) event AirdropBurn(
78 WalletAddress: Address
79 , TokenSerial: String
80 , AirdropEditionId: UInt64
81 )
82
83 /*
84 * Named Paths
85 */
86 access(all) let ManagerStoragePath: StoragePath
87
88 /*
89 * HWGaragePM fields
90 */
91 /*
92 * HWGarageCard
93 */
94 access(self) let HWGarageCardsMintedEditions: { UInt64: Bool}
95 access(self) var HWGarageCardsSequentialMintMin: UInt64
96 access(all) var HWGarageCardsTotalSupply: UInt64
97 /*
98 * HWGaragePack
99 */
100 access(self) let packsMintedEditions: {UInt64: Bool}
101 access(self) let packsByPackIdMintedEditions: {UInt64: {UInt64:Bool}}
102 access(self) var packsSequentialMintMin: UInt64
103 access(self) var packsByPackIdSequentialMintMin: {UInt64: UInt64}
104 access(all) var packTotalSupply: UInt64
105 access(all) var packRedeemStartTime: UFix64
106
107 access(all) entitlement UpdateMetadata
108 access(all) entitlement MintResource
109 access(all) entitlement UpdateStartTime
110
111 /*
112 * Manager resource for all NFTs
113 */
114 access(all) resource Manager { // FlowToken follows similar pattern
115 /*
116 * HWGarageCard
117 */
118 access(all) fun updateHWGarageCardEditionMetadata(editionNumber: UInt64, metadata: {String: String}, packHash: String, address: Address) {
119 HWGarageCard.setEditionMetadata(editionNumber: editionNumber, metadata: metadata)
120 emit UpdateTokenEditionMetadata(id: editionNumber, metadata: metadata, packHash: packHash, address: address)
121 }
122
123 access(all) fun updateHWGarageCardCollectionMetadata(metadata: {String: String}) {
124 HWGarageCard.setCollectionMetadata(metadata: metadata)
125 emit UpdateHWGarageCardCollectionMetadata()
126 }
127
128 access(all) fun mintHWGarageCardAtEdition(edition: UInt64, packID: UInt64): @{NonFungibleToken.NFT} {
129 emit AdminMintHWGarageCard(id: edition)
130 return <-HWGaragePM.mintHWGarageCard(edition: edition, packID: packID)
131 }
132
133 access(all) fun mintSequentialHWGarageCard(packID: UInt64): @{NonFungibleToken.NFT} {
134 let HWGarageCard <- HWGaragePM.mintSequentialHWGarageCard(packID: packID)
135 emit AdminMintHWGarageCard(id: HWGarageCard.id)
136 return <- HWGarageCard
137 }
138
139 /*
140 * HWGaragePack
141 */
142 access(all) fun updatePackEditionMetadata(editionNumber: UInt64, metadata: {String: String}) {
143 HWGaragePack.setEditionMetadata(editionNumber: editionNumber, metadata: metadata)
144 emit UpdatePackEditionMetadata(id: editionNumber, metadata: metadata)
145 }
146
147 access(all) fun updatePackCollectionMetadata(metadata: {String: String}) {
148 HWGaragePack.setCollectionMetadata(metadata: metadata)
149 emit UpdatePackCollectionMetadata()
150 }
151
152 access(all) fun mintPackAtEdition(edition: UInt64, packID: UInt64, packEditionID: UInt64, address: Address, packHash: String): @{NonFungibleToken.NFT} {
153 emit AdminMintHWGarageCard(id: edition)
154 emit PackClaimSuccess(address: address, packHash: packHash, packID: packID)
155 return <-HWGaragePM.mintPackAtEdition(edition: edition, packID: packID, packEditionID: packEditionID, packHash: packHash)
156 }
157
158 access(all) fun mintSequentialHWGaragePack(packID: UInt64, address: Address, packHash: String): @{NonFungibleToken.NFT} {
159 let HWGarageCard <- HWGaragePM.mintSequentialPack(packID: packID, packHash: packHash)
160 emit AdminMintPack(id: HWGarageCard.id, packHash: packHash)
161 emit PackClaimSuccess(address: address, packHash: packHash, packID: HWGarageCard.id)
162 return <-HWGarageCard
163 }
164
165 access(all) fun updateHWGaragePackRedeemStartTime(_ redeemStartTime: UFix64) {
166 HWGaragePM.packRedeemStartTime = redeemStartTime
167 emit UpdateHWGaragePackRedeemInfo(redeemStartTime: HWGaragePM.packRedeemStartTime)
168 }
169
170 /*
171 * HWGarageAirdrop
172 */
173 access(all) fun airdropRedeemable(
174 airdropSeriesID: UInt64
175 , address: Address
176 , tokenMintID: UInt64
177 , originalCardSerial: String
178 , tokenSerial: String
179 , seriesName: String
180 , carName: String
181 , tokenImageHash: String
182 , tokenReleaseDate: String
183 , tokenExpireDate: String
184 , cardID: UInt64
185 , templateID: String
186 ): @{NonFungibleToken.NFT} {
187 let HWGarageAirdrop <- HWGaragePM.mintSequentialAirdrop(
188 airdropID: airdropSeriesID
189 )
190 emit AirdropRedeemable(
191 WalletAddress: address
192 , TokenID:HWGarageAirdrop.id // tokenEditionID
193 , TokenMintID: tokenMintID
194 , OriginalCardSerial: originalCardSerial
195 , TokenSerial: tokenSerial
196 , SeriesName: seriesName
197 , Name: carName
198 , TokenImageHash: tokenImageHash
199 , TokenReleaseDate: tokenReleaseDate
200 , TokenExpireDate: tokenExpireDate
201 , CardID: cardID
202 , TemplateID: templateID
203 )
204 return <-HWGarageAirdrop
205 }
206 }
207
208 /*
209 * HWGarageCard
210 *
211 * Mint a HWGarageCard
212 */
213 access(self) fun mintHWGarageCard(edition: UInt64, packID: UInt64): @{NonFungibleToken.NFT} {
214 pre {
215 edition >= 1: "Requested edition is outisde the realm of space and time, you just lost the game."
216 self.HWGarageCardsMintedEditions[edition] == nil : "Requested edition has already been minted"
217 }
218 self.HWGarageCardsMintedEditions[edition] = true
219
220 let hwGarageCard: @HWGarageCard.NFT <- HWGarageCard.mint(nftID: edition, packID: packID)
221 return <-hwGarageCard
222 }
223
224 // look for the next Card in the sequence, and mint there
225 access(self) fun mintSequentialHWGarageCard(packID: UInt64): @{NonFungibleToken.NFT} {
226 var currentEditionNumber = HWGarageCard.getTotalSupply()
227 currentEditionNumber = currentEditionNumber + 1
228
229 self.HWGarageCardsSequentialMintMin = currentEditionNumber
230 let hwGarageCard: @{NonFungibleToken.NFT} <- self.mintHWGarageCard(edition: currentEditionNumber, packID: packID)
231 return <- hwGarageCard
232 }
233
234 /*
235 * HWGaragePack
236 *
237 * Mint a HWGaragePack
238 */
239 access(self) fun mintPackAtEdition(edition: UInt64, packID: UInt64, packEditionID: UInt64, packHash: String): @{NonFungibleToken.NFT} {
240 pre {
241 edition >= 1: "Requested edition is outside the realm of space and time, you just lost the game."
242 }
243
244 let pack: @HWGaragePack.NFT <- HWGaragePack.mint(nftID: edition, packID: packID, packEditionID: packEditionID)
245 return <-pack
246 }
247
248 // Look for the next available pack, and mint there
249 access(self) fun mintSequentialPack(packID: UInt64, packHash: String): @{NonFungibleToken.NFT} {
250
251 var currentPackEditionNumber = HWGaragePack.getTotalSupply() + 1
252 let newToken: @{NonFungibleToken.NFT} <- self.mintPackAtEdition(edition: UInt64(currentPackEditionNumber), packID: packID, packEditionID: UInt64(currentPackEditionNumber), packHash: packHash)
253 return <-newToken
254 }
255
256 /*
257 * HWGarageAirdrop
258 *
259 * Mint a redeemable HWGarageAirdrop token
260 */
261 access(self) fun mintSequentialAirdrop(airdropID: UInt64): @{NonFungibleToken.NFT} {
262 var currentAirdrop = HWGaragePack.getTotalSupply() + 1
263 let newAirdropToken <- HWGaragePack.mint(
264 nftID: currentAirdrop
265 , packID: airdropID
266 , packEditionID: currentAirdrop
267 )
268 return <- newAirdropToken
269 }
270
271
272 /*
273 * Public Functions
274 *
275 * HWGaragePack
276 */
277 access(all) fun claimPack(address: Address, packHash: String) {
278 // this event is picked up by a web hook to verify packHash
279 // if packHash is valid, the backend will mint the pack and
280 // deposit to the recipient address
281 emit PackClaimBegin(address: address, packHash: packHash)
282 }
283
284 access(all) fun publicRedeemPack(pack: @{NonFungibleToken.NFT}, address: Address, packHash: String) {
285 pre {
286 getCurrentBlock().timestamp >= self.packRedeemStartTime: "Redemption has not yet started"
287 pack.isInstance(Type<@HWGaragePack.NFT>())
288 }
289 let packInstance <- pack as! @HWGaragePack.NFT
290
291 // emit event that our backend will read and mint pack contents to the associated address
292 emit RedeemPack(id: packInstance.id, packID: packInstance.packID, packEditionID: packInstance.packEditionID, address: address, packHash: packHash)
293 // burn pack since it was redeemed for HWGarageCard(s)
294 destroy packInstance
295 }
296
297
298 //
299 access(all) fun burnAirdrop(
300 walletAddress: Address
301 , tokenSerial: String
302 , airdropToken: @{NonFungibleToken.NFT}
303
304 ) {
305 pre{
306 // check airdropIdEdition is the Type
307 airdropToken.isInstance(Type<@HWGaragePack.NFT>())
308 }
309 let airdropInstance <- airdropToken as! @HWGaragePack.NFT
310 // emit event signaling Airdrop is burned
311 emit AirdropBurn(
312 WalletAddress: walletAddress
313 , TokenSerial: tokenSerial
314 , AirdropEditionId: airdropInstance.id
315 )
316 destroy airdropInstance
317 }
318
319 init(){
320 /*
321 * Non-human modifiable state variables
322 *
323 * HWGarageCard
324 */
325 self.HWGarageCardsTotalSupply = 0
326 self.HWGarageCardsSequentialMintMin = 1
327 // Start with no existing editions minted
328 self.HWGarageCardsMintedEditions = {}
329
330 /*
331 * HWGaragePack
332 */
333 self.packTotalSupply = 0
334 self.packRedeemStartTime = 1658361290.0
335 self.packsSequentialMintMin = 1
336 // start with no existing editions minted
337 self.packsMintedEditions = {}
338 // setup with initial PackID
339 self.packsByPackIdMintedEditions = { 1: {}}
340 self.packsByPackIdSequentialMintMin = {1 : 1}
341
342
343 // manager resource is only saved to the deploying account's storage
344 self.ManagerStoragePath = /storage/HWGaragePM
345 self.account.storage.save(<- create Manager(), to: self.ManagerStoragePath)
346
347 emit ContractInitialized()
348 }
349}
350