Smart Contract

GoatedGoatsTrait

A.2068315349bdfce5.GoatedGoatsTrait

Deployed

4h ago
Mar 01, 2026, 10:17:51 PM UTC

Dependents

0 imports
1/*
2    A NFT contract for the Goated Goats individual traits.
3    
4    Key Callouts: 
5    * Unlimited supply of traits
6    * Created via GoatedGoatsTrait only by admin on back-end
7    * Store collection id from pack metadata
8    * Store pack id that created this trait (specified by Admin at Trait creation time)
9    * Main id for a trait is auto-increment
10    * Collection-level metadata
11    * Edition-level metadata (ipfs link, trait name, etc)
12*/
13
14import NonFungibleToken from 0x1d7e57aa55817448
15import FungibleToken from 0xf233dcee88fe0abe
16import MetadataViews from 0x1d7e57aa55817448
17
18pub contract GoatedGoatsTrait: NonFungibleToken {
19    // -----------------------------------------------------------------------
20    // NonFungibleToken Standard Events
21    // -----------------------------------------------------------------------
22
23    pub event ContractInitialized()
24    pub event Withdraw(id: UInt64, from: Address?)
25    pub event Deposit(id: UInt64, to: Address?)
26
27    // -----------------------------------------------------------------------
28    // GoatedGoatsTrait Events
29    // -----------------------------------------------------------------------
30
31    pub event Mint(id: UInt64)
32    pub event Burn(id: UInt64)
33
34    // -----------------------------------------------------------------------
35    // Named Paths
36    // -----------------------------------------------------------------------
37
38    pub let CollectionStoragePath: StoragePath
39    pub let CollectionPublicPath: PublicPath
40
41    // -----------------------------------------------------------------------
42    // NonFungibleToken Standard Fields
43    // -----------------------------------------------------------------------
44
45    pub var totalSupply: UInt64
46
47    // -----------------------------------------------------------------------
48    // GoatedGoatsTrait Fields
49    // -----------------------------------------------------------------------
50
51    pub var name: String
52
53    access(self) var collectionMetadata: { String: String }
54    access(self) let idToTraitMetadata: { UInt64: TraitMetadata }
55
56    pub struct TraitMetadata {
57        pub let metadata: { String: String }
58
59        init(metadata: { String: String }) {
60            self.metadata = metadata
61        }
62    }
63
64    pub resource NFT : NonFungibleToken.INFT, MetadataViews.Resolver {
65        pub let id: UInt64
66        pub let packID: UInt64
67
68        pub fun getViews(): [Type] {
69            return [
70                Type<MetadataViews.Display>(),
71                Type<MetadataViews.NFTCollectionData>(),
72                Type<MetadataViews.NFTCollectionDisplay>(),
73                Type<MetadataViews.Traits>(),
74                Type<MetadataViews.ExternalURL>(),
75                Type<MetadataViews.Royalties>()
76            ]
77        }
78
79        pub fun resolveView(_ view: Type): AnyStruct? {
80            let metadata: {String: String} = self.getMetadata()
81            switch view {
82                case Type<MetadataViews.Trait>():
83                    if let skinFileName: String = metadata["skinFilename"] {
84                        let skin: String = GoatedGoatsTrait.formatFileName(value: skinFileName, prefix: "skin")
85                        let skinRarity: String = metadata["skinRarity"]!
86                    }
87                    return MetadataViews.Trait(
88                        name: metadata["traitSlot"]!,
89                        value: GoatedGoatsTrait.formatFileName(value: metadata["fileName"]!, prefix: metadata["traitSlot"]!),
90                        displayType: "String",
91                        rarity:MetadataViews.Rarity(score: nil, max: nil, description: metadata["rarity"]!)
92                    )
93
94                case Type<MetadataViews.Traits>():
95                    return MetadataViews.Traits(
96                        [
97                            MetadataViews.Trait(
98                                name: metadata["traitSlot"]!,
99                                value: GoatedGoatsTrait.formatFileName(value: metadata["fileName"]!, prefix: metadata["traitSlot"]!),
100                                displayType: "String",
101                                rarity:MetadataViews.Rarity(score: nil, max: nil, description: metadata["rarity"]!)
102                            ) 
103                        ]
104                    )
105
106                
107                case Type<MetadataViews.NFTCollectionData>():
108                    return MetadataViews.NFTCollectionData(
109                        storagePath: GoatedGoatsTrait.CollectionStoragePath,
110                        publicPath: GoatedGoatsTrait.CollectionPublicPath,
111                        providerPath: /private/GoatTraitCollection,
112                        publicCollection: Type<&GoatedGoatsTrait.Collection{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, GoatedGoatsTrait.TraitCollectionPublic, MetadataViews.ResolverCollection}>(),
113                        publicLinkedType: Type<&GoatedGoatsTrait.Collection{NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, GoatedGoatsTrait.TraitCollectionPublic, MetadataViews.ResolverCollection}>(), 
114                        providerLinkedType: Type<&GoatedGoatsTrait.Collection{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic, NonFungibleToken.Receiver, GoatedGoatsTrait.TraitCollectionPublic, MetadataViews.ResolverCollection}>(),
115                        createEmptyCollectionFunction: fun(): @NonFungibleToken.Collection {return <- GoatedGoatsTrait.createEmptyCollection()}
116                    )
117
118                case Type<MetadataViews.NFTCollectionDisplay>():
119                    let externalURL: MetadataViews.ExternalURL = MetadataViews.ExternalURL("https://GoatedGoats.com")
120                    let squareImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://goatedgoats.com/_ipx/w_32,q_75/%2FLogo.png?url=%2FLogo.png&w=32&q=75"), mediaType: "image/png")
121
122                    let bannerImage = MetadataViews.Media(file: MetadataViews.HTTPFile(url: "https://goatedgoats.com/_ipx/w_32,q_75/%2FLogo.png?url=%2FLogo.png&w=32&q=75"), mediaType: "image/png")
123
124                    let socialMap : {String : MetadataViews.ExternalURL} = {
125                        "twitter" : MetadataViews.ExternalURL("https://twitter.com/goatedgoats")
126                    }
127
128                    return MetadataViews.NFTCollectionDisplay(
129                         name: "Goated Goats Traits",
130                         description: "This is the collection of Traits that can be equipped onto Goated Goats",
131                         externalURL: externalURL,
132                         squareImage: squareImage,
133                         bannerImage: bannerImage,
134                         socials: socialMap
135                    )
136                
137                case Type<MetadataViews.ExternalURL>():
138                    return MetadataViews.ExternalURL(
139                        "https://GoatedGoats.com"
140                    )
141                
142                case Type<MetadataViews.Royalties>():
143                    let royaltyReceiver = getAccount(0xd7081a5c66dc3e7f).getCapability<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)
144
145                    return MetadataViews.Royalties(
146                        [MetadataViews.Royalty(recepient: royaltyReceiver, cut: 0.05, description: "This is the royalty receiver for traits")]
147                    )
148
149                case Type<MetadataViews.Display>():
150                    var ipfsImage = MetadataViews.IPFSFile(cid: "No thumbnail cid set", path: "No thumbnail pat set")
151                    if (self.getMetadata().containsKey("thumbnailCID")) {
152                        ipfsImage = MetadataViews.IPFSFile(cid: self.getMetadata()["thumbnailCID"]!, path: self.getMetadata()["thumbnailPath"])
153                    }
154                    return MetadataViews.Display(
155                        name: self.getMetadata()["name"] ?? "Goated Goat Trait ".concat(self.id.toString()),
156                        description: self.getMetadata()["description"] ?? "No description set",
157                        thumbnail: ipfsImage
158                    )
159            }
160
161            return nil
162        }
163
164        pub fun getMetadata(): {String: String} {
165            if (GoatedGoatsTrait.idToTraitMetadata[self.id] != nil) {
166                return GoatedGoatsTrait.idToTraitMetadata[self.id]!.metadata
167            } else {
168                return {}
169            }
170        }
171
172        init(id: UInt64, packID: UInt64) {
173            self.id = id
174            self.packID = packID
175            emit Mint(id: self.id)
176        }
177
178        destroy() {
179            emit Burn(id: self.id)
180        }
181    }
182
183    pub resource interface TraitCollectionPublic {
184        pub fun deposit(token: @NonFungibleToken.NFT)
185        pub fun getIDs(): [UInt64]
186        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT
187        pub fun borrowTrait(id: UInt64): &GoatedGoatsTrait.NFT? {
188            post {
189                (result == nil) || (result?.id == id): 
190                    "Cannot borrow GoatedGoatsTrait reference: The ID of the returned reference is incorrect"
191            }
192        }
193    }
194
195    pub resource Collection: TraitCollectionPublic, NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection {
196        pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT}
197
198        init () {
199            self.ownedNFTs <- {}
200        }
201
202        pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
203            let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
204            emit Withdraw(id: token.id, from: self.owner?.address)
205            return <-token
206        }
207
208        pub fun deposit(token: @NonFungibleToken.NFT) {
209            let token <- token as! @GoatedGoatsTrait.NFT
210            let id: UInt64 = token.id
211            let oldToken <- self.ownedNFTs[id] <- token
212            emit Deposit(id: id, to: self.owner?.address)
213            destroy oldToken
214        }
215
216        pub fun getIDs(): [UInt64] {
217            return self.ownedNFTs.keys
218        }
219
220        pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT {
221            return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)!
222        }
223
224        pub fun borrowTrait(id: UInt64): &GoatedGoatsTrait.NFT? {
225            if self.ownedNFTs[id] != nil {
226                let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
227                return ref as! &GoatedGoatsTrait.NFT
228            } else {
229                return nil
230            }
231        }
232
233        pub fun borrowViewResolver(id: UInt64): &AnyResource{MetadataViews.Resolver} {
234            let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
235            let trait = nft as! &GoatedGoatsTrait.NFT
236            return trait as &AnyResource{MetadataViews.Resolver}
237        }
238
239        destroy() {
240            destroy self.ownedNFTs
241        }
242    }
243
244    // -----------------------------------------------------------------------
245    // Admin Functions
246    // -----------------------------------------------------------------------
247    access(account) fun setEditionMetadata(editionNumber: UInt64, metadata: {String: String}) {
248        self.idToTraitMetadata[editionNumber] = TraitMetadata(metadata: metadata)
249    }
250
251    access(account) fun setCollectionMetadata(metadata: {String: String}) {
252        self.collectionMetadata = metadata
253    }
254
255    access(account) fun mint(nftID: UInt64, packID: UInt64) : @NonFungibleToken.NFT {
256        self.totalSupply = self.totalSupply + 1
257        return <-create NFT(id: nftID, packID: packID)
258    }
259
260    // -----------------------------------------------------------------------
261    // Public Functions
262    // -----------------------------------------------------------------------
263    pub fun getTotalSupply(): UInt64 {
264        return self.totalSupply
265    }
266
267    pub fun getName(): String {
268        return self.name
269    }
270
271    pub fun getCollectionMetadata(): {String: String} {
272        return self.collectionMetadata
273    }
274
275    pub fun getEditionMetadata(_ edition: UInt64): {String: String} {
276        if (self.idToTraitMetadata[edition] != nil) {
277            return self.idToTraitMetadata[edition]!.metadata
278        } else {
279            return {}
280        }
281    }
282
283        access(contract) fun formatFileName(value:String, prefix:String):String {
284        let length= value.length
285        let start=prefix.length+1
286        let trimmed = value.slice(from:start, upTo: length-4)
287        return  trimmed
288    }
289
290    // -----------------------------------------------------------------------
291    // NonFungibleToken Standard Functions
292    // -----------------------------------------------------------------------
293    pub fun createEmptyCollection(): @NonFungibleToken.Collection {
294        return <- create Collection()
295    }
296
297    init() {
298        self.name = "Goated Goats Traits"
299        self.totalSupply = 0
300
301        self.collectionMetadata = {}
302        self.idToTraitMetadata = {}
303
304        self.CollectionStoragePath = /storage/GoatTraitCollection
305        self.CollectionPublicPath = /public/GoatTraitCollection
306
307        emit ContractInitialized()
308    }
309}
310 
311