Smart Contract

FixesAvatar

A.d2abb5dbf5e08666.FixesAvatar

Valid From

86,128,631

Deployed

2d ago
Feb 24, 2026, 11:54:28 PM UTC

Dependents

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