Smart Contract

HWGaragePM

A.d0bcefdf1e67ea85.HWGaragePM

Deployed

1d ago
Feb 26, 2026, 09:43:55 PM UTC

Dependents

0 imports
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