Smart Contract

FlovatarComponentUpgrader

A.921ea449dffec68a.FlovatarComponentUpgrader

Deployed

1w ago
Feb 16, 2026, 11:20:41 PM UTC

Dependents

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