Smart Contract
FlovatarComponentUpgrader
A.921ea449dffec68a.FlovatarComponentUpgrader
1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import FlowToken from 0x1654653399040a61
4import FlovatarComponentTemplate from 0x921ea449dffec68a
5import FlovatarComponent from 0x921ea449dffec68a
6import FlovatarPack from 0x921ea449dffec68a
7import FlovatarDustToken from 0x921ea449dffec68a
8import FlovatarInbox from 0x921ea449dffec68a
9import Flovatar from 0x921ea449dffec68a
10
11/*
12
13 This contract provides the ability for users to upgrade their Flobits
14
15 */
16
17access(all)
18contract FlovatarComponentUpgrader{
19
20 access(all) entitlement Withdraw
21
22 // The withdrawEnabled will allow to put all withdraws on hold while the distribution of new airdrops is happening
23 // So that everyone will be then be able to access his rewards at the same time
24 access(account)
25 var upgradeEnabled: Bool
26
27 access(all)
28 let CollectionStoragePath: StoragePath
29
30 access(all)
31 let CollectionPublicPath: PublicPath
32
33 // Event to notify about the Inbox creation
34 access(all)
35 event ContractInitialized()
36
37 // Events to notify when Dust or Components are deposited or withdrawn
38 access(all)
39 event FlovatarComponentUpgraded(
40 newId: UInt64,
41 rarity: String,
42 category: String,
43 burnedIds: [
44 UInt64
45 ]
46 )
47
48 //Randomize code gently provided by @bluesign
49 access(all)
50 struct RandomInt{
51 access(self)
52 var value: UInt64?
53
54 access(self)
55 let maxValue: UInt64
56
57 access(self)
58 let minValue: UInt64
59
60 access(self)
61 let field: String
62
63 access(self)
64 let uuid: UInt64
65
66 access(all)
67 init(uuid: UInt64, field: String, minValue: UInt64, maxValue: UInt64){
68 self.uuid = uuid
69 self.field = field
70 self.minValue = minValue
71 self.maxValue = maxValue
72 self.value = nil
73 }
74
75 access(all)
76 fun getValue(): UInt64{
77 if let value = self.value{
78 return value
79 }
80 let h: [UInt8] = HashAlgorithm.SHA3_256.hash(self.uuid.toBigEndianBytes())
81 let f: [UInt8] = HashAlgorithm.SHA3_256.hash(self.field.utf8)
82 var id = (getBlock(at: getCurrentBlock().height)!).id
83 var random: UInt64 = 0
84 var i = 0
85 while i < 8{
86 random = random + (UInt64(id[i]) ^ UInt64(h[i]) ^ UInt64(f[i]))
87 random = random << 8
88 i = i + 1
89 }
90 self.value = self.minValue + random % (self.maxValue - self.minValue)
91 return self.minValue + random % (self.maxValue - self.minValue)
92 }
93 }
94
95 access(all)
96 resource interface CollectionPublic{
97 access(all)
98 fun depositComponent(component: @FlovatarComponent.NFT)
99 }
100
101 // The main Collection that manages the Containers
102 access(all)
103 resource Collection: CollectionPublic{
104 access(contract)
105 let flovatarComponents: @{UInt64: FlovatarComponent.NFT}
106
107 access(contract)
108 let rarityLookup:{ UInt32:{ String:{ String:{ UInt64: UInt64}}}}
109
110 init(){
111 self.flovatarComponents <-{}
112 self.rarityLookup ={}
113 }
114
115 access(all)
116 fun depositComponent(component: @FlovatarComponent.NFT){
117 if !self.rarityLookup.containsKey(component.getSeries()){
118 self.rarityLookup.insert(key: component.getSeries(),{} as{ String:{ String:{ UInt64: UInt64}}})
119 }
120 if !(self.rarityLookup[component.getSeries()]!).containsKey(component.getRarity()){
121 (self.rarityLookup[component.getSeries()]!).insert(key: component.getRarity(),{} as{ String:{ UInt64: UInt64}})
122 ((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!).insert(key: "all",{} as{ UInt64: UInt64})
123 }
124 if !((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!).containsKey(component.getCategory()){
125 ((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!).insert(key: component.getCategory(),{} as{ UInt64: UInt64})
126 }
127 (((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!)["all"]!).insert(key: component.id, component.id)
128 (((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!)[component.getCategory()]!).insert(key: component.id, component.id)
129 let oldComponent <- self.flovatarComponents[component.id] <- component
130 destroy oldComponent
131 }
132
133 access(Withdraw)
134 fun withdrawComponent(id: UInt64): @FlovatarComponent.NFT{
135 let component <- self.flovatarComponents.remove(key: id) ?? panic("missing NFT")
136 (((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!)["all"]!).remove(key: component.id)
137 (((self.rarityLookup[component.getSeries()]!)[component.getRarity()]!)[component.getCategory()]!).remove(key: component.id)
138 return <-component
139 }
140
141 access(Withdraw)
142 fun withdrawRandomComponent(series: UInt32, rarity: String, category: String?): @FlovatarComponent.NFT{
143 //FILTER BY SERIES AND RARITY AND THEN RANDOMIZE AND PICK ONE
144 var components: [UInt64] = []
145 if self.rarityLookup[series] == nil{
146 panic("No Components found for the provided Series")
147 }
148 if (self.rarityLookup[series]!)[rarity] == nil{
149 panic("No Components found for the provided Rarity")
150 }
151 if category != nil{
152 if ((self.rarityLookup[series]!)[rarity]!)[category!] == nil{
153 //panic("No Components found for the provided Category")
154 components = (((self.rarityLookup[series]!)[rarity]!)["all"]!).keys
155 } else{
156 components = (((self.rarityLookup[series]!)[rarity]!)[category!]!).keys
157 }
158 } else{
159 components = (((self.rarityLookup[series]!)[rarity]!)["all"]!).keys
160 }
161 if components.length < 1{
162 panic("No Components found!")
163 }
164 let randInt: UInt64 = revertibleRandom<UInt64>()
165 var fieldString: String = series.toString().concat(rarity)
166 if category != nil{
167 fieldString = fieldString.concat(category!)
168 }
169 if components.length == Int(1){
170 let component <- self.withdrawComponent(id: components[UInt64(0)])
171 return <-component
172 }
173 let randomPos: RandomInt = RandomInt(uuid: revertibleRandom<UInt64>(), field: fieldString, minValue: UInt64(0), maxValue: UInt64(components.length - Int(1)))
174 let component <- self.withdrawComponent(id: components[randomPos.getValue()])
175 return <-component
176 }
177
178 access(all)
179 fun getComponentIDs(): [UInt64]{
180 return self.flovatarComponents.keys
181 }
182 }
183
184 // This function can only be called by the account owner to create an empty Collection
185 access(account)
186 fun createEmptyCollection(): @FlovatarComponentUpgrader.Collection{
187 return <-create Collection()
188 }
189
190 // This function withdraws all the Components assigned to a Flovatar and sends them to the Owner's address
191 access(all)
192 fun upgradeFlovatarComponent(
193 components: @[
194 FlovatarComponent.NFT
195 ],
196 vault: @{FungibleToken.Vault},
197 address: Address
198 ){
199 pre{
200 self.upgradeEnabled:
201 "Upgrade is not enabled!"
202 vault.balance == 20.0:
203 "The amount of $DUST is not correct"
204 vault.isInstance(Type<@FlovatarDustToken.Vault>()):
205 "Vault not of the right Token Type"
206 components.length == 10:
207 "You need to provide exactly 10 Flobits for the upgrade"
208 }
209 if let upgraderCollection =
210 self.account.storage.borrow<auth(Withdraw) &FlovatarComponentUpgrader.Collection>(
211 from: self.CollectionStoragePath
212 ){
213 var componentSeries: UInt32 = 0
214 var checkCategory: Bool = true
215 var componentCategory: String? = nil
216 var componentRarity: String = ""
217 var outputRarity: String = ""
218 var i: UInt32 = 0
219 while i < UInt32(components.length){
220 let template = FlovatarComponentTemplate.getComponentTemplate(id: components[i].templateId)!
221 if i == UInt32(0){
222 componentSeries = template.series
223 componentCategory = template.category
224 componentRarity = template.rarity
225 }
226 if componentSeries != template.series{
227 panic("All the Flovatar Components need to be belong to the same Series")
228 }
229 if componentRarity != template.rarity{
230 panic("All the Flovatar Components need to be belong to the same Rarity Level")
231 }
232 if componentCategory != template.category{
233 checkCategory = false
234 }
235 i = i + UInt32(1)
236 }
237 if componentRarity == "common"{
238 outputRarity = "rare"
239 } else if componentRarity == "rare"{
240 outputRarity = "epic"
241 } else if componentRarity == "epic"{
242 outputRarity = "legendary"
243 } else{
244 panic("Rarity needs to be Common, Rare or Epic")
245 }
246 if !checkCategory{
247 componentCategory = nil
248 }
249
250 let component <-
251 upgraderCollection.withdrawRandomComponent(
252 series: componentSeries,
253 rarity: outputRarity,
254 category: componentCategory
255 )
256 destroy components
257 destroy vault
258
259 if let inboxCollection =
260 self.account.storage.borrow<auth(NonFungibleToken.Withdraw) &FlovatarInbox.Collection>(
261 from: FlovatarInbox.CollectionStoragePath
262 ){
263 inboxCollection.depositComponentToWallet(address: address, component: <-component)
264 } else{
265 panic("Couldn't borrow Flovatar Inbox Collection")
266 }
267
268 } else{
269 panic("Can't borrow the Upgrader Collection")
270 }
271 }
272
273 // Admin function to temporarly enable or disable the airdrop and reward withdraw so that
274 // we can distribute them to everyone at the same time
275 access(account)
276 fun setUpgradeEnable(enabled: Bool){
277 self.upgradeEnabled = enabled
278 }
279
280 init(){
281 self.upgradeEnabled = true
282 self.CollectionPublicPath = /public/FlovatarComponentUpgraderCollection
283 self.CollectionStoragePath = /storage/FlovatarComponentUpgraderCollection
284 self.account.storage.save<@FlovatarComponentUpgrader.Collection>(
285 <-FlovatarComponentUpgrader.createEmptyCollection(),
286 to: FlovatarComponentUpgrader.CollectionStoragePath
287 )
288 var capability_1 =
289 self.account.capabilities.storage.issue<&{FlovatarComponentUpgrader.CollectionPublic}>(
290 FlovatarComponentUpgrader.CollectionStoragePath
291 )
292 self.account.capabilities.publish(
293 capability_1,
294 at: FlovatarComponentUpgrader.CollectionPublicPath
295 )
296 emit ContractInitialized()
297 }
298}
299