Smart Contract
GoatedGoatsTrait
A.2068315349bdfce5.GoatedGoatsTrait
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