Smart Contract
FixesAvatar
A.d2abb5dbf5e08666.FixesAvatar
1/**
2> Author: Fixes Lab <https://github.com/fixes-world/>
3
4# FixesAvatar
5
6It is responsible for managing the user's profile, properties, and NFTs.
7
8*/
9
10// Thirdparty Imports
11import NonFungibleToken from 0x1d7e57aa55817448
12import FungibleToken from 0xf233dcee88fe0abe
13import MetadataViews from 0x1d7e57aa55817448
14import ViewResolver from 0x1d7e57aa55817448
15import Burner from 0xf233dcee88fe0abe
16// Fixes Imports
17import FixesTraits from 0xd2abb5dbf5e08666
18import FRC20FTShared from 0xd2abb5dbf5e08666
19
20/// The `FixesAvatar` contract
21///
22access(all) contract FixesAvatar {
23
24 access(all) entitlement Manage
25
26 /* --- Events --- */
27
28 /// Event emitted when the contract is initialized
29 access(all) event ContractInitialized()
30
31 /// Event emitted when a new trait entry is added
32 access(all) event TraitEntryAdd(owner: Address?, traitId: UInt64, series: String, value: UInt8, rarity: UInt8, offset: Int8)
33
34 /* --- Variable, Enums and Structs --- */
35
36 access(all)
37 let AvatarStoragePath: StoragePath
38 access(all)
39 let AvatarPublicPath: PublicPath
40
41 /* --- Interfaces & Resources --- */
42
43 access(all) resource interface ProfilePublic {
44 access(all)
45 view fun getProperty(_ name: String): String?
46
47 access(all)
48 view fun getOwnedTraitIDs(): [UInt64]
49
50 access(all)
51 view fun getOwnedTrait(_ id: UInt64): FixesTraits.TraitWithOffset?
52
53 access(all)
54 fun getOwnedTraitView(_ id: UInt64): MetadataViews.Trait?
55
56 access(all)
57 fun getEnabledTraits(): [MetadataViews.Trait]
58 }
59
60 /// The resource that stores the metadata for this contract
61 ///
62 access(all) resource Profile: ProfilePublic, FRC20FTShared.TransactionHook, ViewResolver.Resolver {
63 // Holds the properties for this profile, key is the property name, value is the property value
64 access(self)
65 let properties: {String: String}
66 // Holds NFTs for this profile, key is the NFT type, value is a map of NFT ID to NFT
67 access(self)
68 let nfts: @{Type: {UInt64: {NonFungibleToken.NFT}}}
69 // Holds the owned entities for this profile
70 access(self)
71 let ownedEntities: @{UInt64: FixesTraits.Entry}
72 // Holds the enabled entities for this profile
73 access(self)
74 let enabledEntities: [UInt64]
75
76 init() {
77 self.properties = {}
78 self.nfts <- {}
79 self.ownedEntities <- {}
80 self.enabledEntities = []
81 }
82
83 // ---- implement TransactionHook ----
84
85 /// The method that is invoked when the transaction is executed
86 ///
87 access(account)
88 fun onDeal(
89 seller: Address,
90 buyer: Address,
91 tick: String,
92 dealAmount: UFix64,
93 dealPrice: UFix64,
94 storefront: Address,
95 listingId: UInt64?,
96 ) {
97 // For frc20, we only support the following tick
98 // "flows", the default tick
99 // "fixes", the tick for the FIXeS platform
100 let availableTicks = [
101 FRC20FTShared.getPlatformStakingTickerName(),
102 FRC20FTShared.getPlatformUtilityTickerName()
103 ]
104 var isValidTrx = availableTicks.contains(tick)
105
106 // For Coins, all transactions from tradable pool are valid
107 if tick.length > 0 && tick[0] == "$" {
108 isValidTrx = seller == storefront || buyer == storefront
109 }
110
111 // Try to generate a random entry
112 if isValidTrx {
113 if let entry <- FixesTraits.attemptToGenerateRandomEntryForSeason0() {
114 log("Generated a random entry for season 0".concat(entry.uuid.toString()))
115 self.addTraitEntry(<- entry)
116 }
117 }
118 }
119
120 // ---- implement Resolver ----
121
122 /// Function that returns all the Metadata Views available for this profile
123 ///
124 access(all)
125 view fun getViews(): [Type] {
126 return [
127 Type<MetadataViews.Traits>()
128 ]
129 }
130
131 /// Function that resolves a metadata view for this profile
132 ///
133 access(all)
134 fun resolveView(_ view: Type): AnyStruct? {
135 switch view {
136 case Type<MetadataViews.Traits>():
137 return MetadataViews.Traits(self.getEnabledTraits())
138 }
139 return nil
140 }
141
142 // ---- Public methods ----
143
144 /// Get the property with the given name
145 ///
146 access(all)
147 view fun getProperty(_ name: String): String? {
148 return self.properties[name]
149 }
150
151 access(all)
152 view fun getOwnedTraitIDs(): [UInt64] {
153 return self.ownedEntities.keys
154 }
155
156 access(all)
157 view fun getOwnedTrait(_ id: UInt64): FixesTraits.TraitWithOffset? {
158 if let entry = self.borrowEntry(id) {
159 return entry.getTrait()
160 }
161 return nil
162 }
163
164 access(all)
165 fun getOwnedTraitView(_ id: UInt64): MetadataViews.Trait? {
166 if let entry = self.borrowEntry(id) {
167 return entry.resolveView(Type<MetadataViews.Trait>()) as! MetadataViews.Trait?
168 }
169 return nil
170 }
171
172 access(all)
173 fun getEnabledTraits(): [MetadataViews.Trait] {
174 let traits: [MetadataViews.Trait] = []
175 for traitId in self.enabledEntities {
176 if let entry = self.borrowEntry(traitId) {
177 if let view = entry.resolveView(Type<MetadataViews.Trait>()) {
178 traits.append(view as! MetadataViews.Trait)
179 }
180 }
181 }
182 return traits
183 }
184
185 // ---- Account Access methods ----
186
187 access(account)
188 fun addTraitEntry(_ entry: @FixesTraits.Entry) {
189 let uuid = entry.uuid
190 if self.ownedEntities[uuid] != nil {
191 Burner.burn(<- entry)
192 return
193 }
194
195 let trait = entry.getTrait()
196
197 // Add the entry to the owned entities
198 self.ownedEntities[uuid] <-! entry
199
200 // emit the event
201 emit TraitEntryAdd(
202 owner: self.owner?.address,
203 traitId: uuid,
204 series: trait.series.identifier,
205 value: trait.value,
206 rarity: trait.rarity,
207 offset: trait.offset
208 )
209 }
210
211 // ---- Private methods ----
212
213 /// Set the property with the given name to the given value
214 ///
215 access(Manage)
216 fun setProperty(_ name: String, _ value: String) {
217 self.properties[name] = value
218 }
219
220 // ---- Internal methods ----
221
222 access(self)
223 view fun borrowEntry(_ id: UInt64): auth(FixesTraits.Write) &FixesTraits.Entry? {
224 return &self.ownedEntities[id]
225 }
226 }
227
228 /* --- Public Functions --- */
229
230 /// Creates a new `Profile` resource
231 ///
232 access(all)
233 fun createProfile(): @Profile {
234 return <-create Profile()
235 }
236
237 /// Returns the `Profile` public capability for the given address
238 ///
239 access(all)
240 view fun getProfileCap(_ addr: Address): Capability<&Profile> {
241 return getAccount(addr).capabilities.get<&Profile>(self.AvatarPublicPath)
242 }
243
244 /// Borrow the `Profile` reference
245 ///
246 access(all)
247 view fun borrowProfile(_ addr: Address): &Profile? {
248 return self.getProfileCap(addr).borrow()
249 }
250
251 init() {
252 let identifier = "FixesAvatar_".concat(self.account.address.toString())
253 self.AvatarStoragePath = StoragePath(identifier: identifier)!
254 self.AvatarPublicPath = PublicPath(identifier: identifier)!
255
256 // Register the hooks
257 FRC20FTShared.registerHookType(Type<@FixesAvatar.Profile>())
258
259 emit ContractInitialized()
260 }
261}
262