Smart Contract

ProjectMetadata

A.0b80e42aaab305f0.ProjectMetadata

Deployed

14h ago
Feb 28, 2026, 02:29:56 AM UTC

Dependents

0 imports
1access(all)contract ProjectMetadata {
2    // ProjectMetadataRandomNFT
3    access(all)struct RatioRandomNFTItem {
4        access(all)let id: UInt64
5        access(contract) let image: String
6        access(contract) let nftMetadata: {String:String}
7        access(contract) let nftIDs: [UInt64]
8        access(contract) let maxSupply: UInt64
9        access(contract) let ratio: UFix64
10
11        init(id: UInt64, image: String, maxSupply: UInt64, nftMetadata: {String:String}, ratio: UFix64) {
12            pre {
13                ratio <= 1.0: "ratio sould be <= 100%"
14            }
15            self.image = image
16            self.nftIDs = []
17            self.maxSupply = maxSupply
18            self.ratio = ratio
19            self.nftMetadata = nftMetadata
20            self.id = id
21        }
22
23        access(all)fun addNFT(_ nftID: UInt64) {
24            self.nftIDs.append(nftID)
25        }
26
27        access(all)fun getImage(): String {
28            return self.image
29        }
30
31        access(all)fun getRatio(): UFix64 {
32            return self.ratio
33        }
34
35        access(all)fun getNftMetadata(): {String:String} {
36            return self.nftMetadata
37        }
38
39        access(all)fun getNftIDs(): [UInt64] {
40            return self.nftIDs
41        }
42
43        access(all)fun getMaxSupply(): UInt64 {
44            return self.maxSupply
45        }
46
47        access(all)fun getTotalSupply(): UInt64 {
48            return UInt64(self.nftIDs.length)
49        }
50    }
51    access(all)struct RatioRandomNFT {
52        access(all)let items: [RatioRandomNFTItem]
53
54        init(images: [String], maxSupplies: [UInt64], nftMetadatas: [{String:String}], ratios: [UFix64]) {
55            pre {
56                images.length == maxSupplies.length: "maxSupplies length is invalid"
57                images.length == nftMetadatas.length: "nftMetadatas length is invalid"
58                images.length == ratios.length: "ratios length is invalid"
59            }
60            self.items = []
61            var i = 0
62            var totalRatio = 0.0
63            while i < images.length {
64                let randomItem = RatioRandomNFTItem(id: UInt64(i+1), image: images[i], maxSupply: maxSupplies[i], nftMetadata: nftMetadatas[i], ratio: ratios[i])
65                self.items.append(randomItem)
66                totalRatio = totalRatio + randomItem.getRatio()
67                i = i + 1
68            }
69
70            if totalRatio != 1.0 {
71                panic("Total raito must be equal 100%")
72            }
73        }
74
75        access(all)fun getImages(): [String] {
76            let res: [String] = []
77            for item in self.items {
78                res.append(item.getImage())
79            }
80            return res
81        }
82
83        access(all)fun getNftMetadatas(): [{String:String}] {
84            let res: [{String:String}] = []
85            for item in self.items {
86                res.append(item.getNftMetadata())
87            }
88            return res
89        }
90
91        access(all)fun updateItem(itemId: UInt64, item: RatioRandomNFTItem) {
92            var i = 0
93            while i < self.items.length {
94                if self.items[i].id == itemId {
95                    self.items[i] = item
96                    return
97                }
98                i = i + 1
99            }
100        }
101
102        access(contract) fun getRandomByRatio(_ items: [RatioRandomNFTItem]): RatioRandomNFTItem {
103            let defaultItem = items[0]
104            let itemLen = items.length
105            var i = 0
106            var cumSum = 0.0
107            let randomNum: UFix64 = UFix64(revertibleRandom<UInt64>() % 1000000) / 1000000.0 // get random number from 0.0 to 0.99999
108            while(i < itemLen) {
109                cumSum = cumSum + items[i].getRatio()
110                if randomNum <= cumSum {
111                    return items[i]
112                }
113                i = i + 1
114            }
115            return defaultItem
116        }
117
118        access(all)fun getRaitoRandomItemForMint(_ quantity: UInt64): [RatioRandomNFTItem] {
119            let randomItems: [RatioRandomNFTItem] = []
120
121            while UInt64(randomItems.length) < quantity {
122                // get images valid
123                let itemValid: [RatioRandomNFTItem] = []
124                for item in self.items {
125                    if item.getMaxSupply() > item.getTotalSupply() {
126                        itemValid.append(item)
127                    }
128                }
129
130                if itemValid.length == 0 {
131                    panic("RANDOM_NFT_IS_SOLD_OUT")
132                }
133
134                // prepare random image
135                let itemRandom = self.getRandomByRatio(itemValid)
136                randomItems.append(itemRandom)
137            }
138            return randomItems
139        }
140
141        access(all)fun addNFT(itemId: UInt64, nftID: UInt64) {
142            var i = 0
143            while i < self.items.length {
144                let item: RatioRandomNFTItem = self.items[i]
145                if item.id == itemId {
146                    item.addNFT(nftID)
147                    self.items[i] = item
148                }
149                i = i + 1
150            }
151        }
152    }
153
154    // LotteryRandom
155    access(all)event LotteryRandom(nftID: UInt64, nftType: String, nftOwner: Address, lotteryNumber: UInt64, winner: Bool)
156    access(all)struct LotteryItem {
157        access(all)let nftID: UInt64
158        access(all)let nftOwner: Address
159        access(all)let timestamp: UFix64
160        access(all)let lotteryNumber: UInt64
161        access(all)let nftType: String
162
163        init(
164            nftID: UInt64,
165            nftOwner: Address,
166            nftType: String,
167            lotteryNumber: UInt64,
168        ) {
169            self.nftID = nftID
170            self.nftOwner = nftOwner
171            self.lotteryNumber = lotteryNumber
172            self.nftType = nftType
173            self.timestamp = getCurrentBlock().timestamp
174        }
175    }
176    access(all)struct LotteryRandomNFT {
177        access(all)let lotteryItems: [LotteryItem]
178        access(all)let luckyNumbers: [UInt64]
179        access(all)let lotteryNumberNotUse: [UInt64]
180        access(all)let nftMetadata: {String:String}
181        access(self) var winner: LotteryItem?
182
183        init(nftMetadata: {String:String}, luckyNumbers: [UInt64], totalNumber: UInt64) {
184            // validate lucky numbers
185            for luckyNumber in luckyNumbers {
186                if luckyNumber < 1 || luckyNumber > totalNumber {
187                    panic("Lucky number is invalid: ".concat(luckyNumber.toString()))
188                }
189            }
190
191            self.winner = nil
192            self.lotteryItems = []
193            self.nftMetadata = nftMetadata
194            self.luckyNumbers = luckyNumbers
195
196            self.lotteryNumberNotUse = []
197            var i = UInt64(1)
198            while i <= totalNumber {
199                self.lotteryNumberNotUse.append(i)
200                i = i + 1
201            }
202        }
203
204        access(all)fun isNftUsed(nftID: UInt64, nftType: String): Bool {
205            for item in self.lotteryItems {
206                if item.nftType == nftType && item.nftID == nftID {
207                    return true
208                }
209            }
210            return false
211        }
212
213        access(self) fun getLotteryNumberIndex(): Int {
214            let randomNumber = revertibleRandom<UInt64>()
215            let randomNum = UFix64(revertibleRandom<UInt64>() % 1000000) / 1000000.0 // get random number from 0.0 to 0.99999
216            let randomIndex = Int(randomNum * UFix64(self.lotteryNumberNotUse.length)) // get random number from 0 to lotteryNumberNotUse.length - 1
217            return randomIndex
218        }
219
220        // create a lottery number and update winner
221        access(all)fun tokenGate(nftID: UInt64, nftType: String, nftOwner: Address): Void {
222            if self.isNftUsed(nftID: nftID, nftType: nftType) {
223                return
224            }
225            let lotteryNumberIndex = self.getLotteryNumberIndex();
226            let lotteryNumber = self.lotteryNumberNotUse[lotteryNumberIndex]
227            self.lotteryNumberNotUse.remove(at: lotteryNumberIndex)
228            let item = LotteryItem(nftID: nftID, nftOwner: nftOwner, nftType: nftType,lotteryNumber: lotteryNumber)
229            self.lotteryItems.append(item)
230            if self.luckyNumbers.contains(lotteryNumber) {
231                self.winner = item
232                emit LotteryRandom(nftID: nftID, nftType: nftType, nftOwner: nftOwner, lotteryNumber: lotteryNumber, winner: true)
233            } else {
234                emit LotteryRandom(nftID: nftID, nftType: nftType, nftOwner: nftOwner, lotteryNumber: lotteryNumber, winner: false)
235            }
236        }
237
238        access(all)fun getWinner(): LotteryItem? {
239            return self.winner
240        }
241
242        access(all)fun resetGate() {
243            while self.lotteryItems.length != 0 {
244                self.lotteryItems.remove(at: 0)
245            }
246        }
247    }
248}