@flowtyio/flow-contracts 0.1.0-beta.2 → 0.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,420 @@
1
+ /*
2
+ *
3
+ * This is an example implementation of a Flow Non-Fungible Token
4
+ * It is not part of the official standard but it assumed to be
5
+ * similar to how many NFTs would implement the core functionality.
6
+ *
7
+ * This contract does not implement any sophisticated classification
8
+ * system for its NFTs. It defines a simple NFT with minimal metadata.
9
+ *
10
+ */
11
+
12
+ import "NonFungibleToken"
13
+ import "MetadataViews"
14
+ import "ViewResolver"
15
+ import "FungibleToken"
16
+
17
+ // THIS CONTRACT IS FOR TESTING PURPOSES ONLY!
18
+ access(all) contract ExampleNFT: ViewResolver {
19
+
20
+ access(all) var totalSupply: UInt64
21
+
22
+ access(all) event ContractInitialized()
23
+ access(all) event Withdraw(id: UInt64, from: Address?)
24
+ access(all) event Deposit(id: UInt64, to: Address?)
25
+ access(all) event Mint(id: UInt64)
26
+
27
+ access(all) event CollectionCreated(id: UInt64)
28
+ access(all) event CollectionDestroyed(id: UInt64)
29
+
30
+ access(all) let CollectionStoragePath: StoragePath
31
+ access(all) let CollectionPublicPath: PublicPath
32
+ access(all) let MinterStoragePath: StoragePath
33
+ access(all) let MinterPublicPath: PublicPath
34
+
35
+ access(all) resource NFT: NonFungibleToken.NFT, ViewResolver.Resolver {
36
+ access(all) let id: UInt64
37
+
38
+ access(all) let name: String
39
+ access(all) let description: String
40
+ access(all) let thumbnail: String
41
+ access(self) var royalties: [MetadataViews.Royalty]
42
+
43
+ access(all) view fun getID(): UInt64 {
44
+ return self.id
45
+ }
46
+
47
+ init(
48
+ id: UInt64,
49
+ name: String,
50
+ description: String,
51
+ thumbnail: String,
52
+ royalties: [MetadataViews.Royalty]
53
+ ) {
54
+ self.id = id
55
+ self.name = name
56
+ self.description = description
57
+ self.thumbnail = thumbnail
58
+ self.royalties = royalties
59
+
60
+ emit Mint(id: self.id)
61
+ }
62
+
63
+ access(Mutate) fun setRoyalties(_ royalties: [MetadataViews.Royalty]) {
64
+ self.royalties = royalties
65
+ }
66
+
67
+ access(all) view fun getViews(): [Type] {
68
+ return [
69
+ Type<MetadataViews.Display>(),
70
+ Type<MetadataViews.Royalties>(),
71
+ Type<MetadataViews.Editions>(),
72
+ Type<MetadataViews.ExternalURL>(),
73
+ Type<MetadataViews.NFTCollectionData>(),
74
+ Type<MetadataViews.NFTCollectionDisplay>(),
75
+ Type<MetadataViews.Serial>(),
76
+ Type<MetadataViews.Traits>()
77
+ ]
78
+ }
79
+
80
+ access(all) fun resolveView(_ view: Type): AnyStruct? {
81
+ switch view {
82
+ case Type<MetadataViews.Display>():
83
+ return MetadataViews.Display(
84
+ name: self.name,
85
+ description: self.description,
86
+ thumbnail: MetadataViews.HTTPFile(
87
+ url: self.thumbnail
88
+ )
89
+ )
90
+ case Type<MetadataViews.Editions>():
91
+ // There is no max number of NFTs that can be minted from this contract
92
+ // so the max edition field value is set to nil
93
+ let editionName = self.id % 2 == 0 ? "Example NFT Edition (even)" : "Example NFT Edition (odd)"
94
+ let editionInfo = MetadataViews.Edition(name: editionName, number: self.id, max: nil)
95
+ let editionList: [MetadataViews.Edition] = [editionInfo]
96
+ return MetadataViews.Editions(
97
+ editionList
98
+ )
99
+ case Type<MetadataViews.Serial>():
100
+ return MetadataViews.Serial(
101
+ self.id
102
+ )
103
+ case Type<MetadataViews.Royalties>():
104
+ return MetadataViews.Royalties(
105
+ self.royalties
106
+ )
107
+ case Type<MetadataViews.ExternalURL>():
108
+ return MetadataViews.ExternalURL("https://example-nft.onflow.org/".concat(self.id.toString()))
109
+ case Type<MetadataViews.NFTCollectionData>():
110
+ return MetadataViews.NFTCollectionData(
111
+ storagePath: ExampleNFT.CollectionStoragePath,
112
+ publicPath: ExampleNFT.CollectionPublicPath,
113
+ publicCollection: Type<&ExampleNFT.Collection>(),
114
+ publicLinkedType: Type<&ExampleNFT.Collection>(),
115
+ createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
116
+ return <-ExampleNFT.createEmptyCollection()
117
+ })
118
+ )
119
+ case Type<MetadataViews.NFTCollectionDisplay>():
120
+ let media = MetadataViews.Media(
121
+ file: MetadataViews.HTTPFile(
122
+ url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
123
+ ),
124
+ mediaType: "image/svg+xml"
125
+ )
126
+ return MetadataViews.NFTCollectionDisplay(
127
+ name: "The Example Collection",
128
+ description: "This collection is used as an example to help you develop your next Flow NFT.",
129
+ externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
130
+ squareImage: media,
131
+ bannerImage: media,
132
+ socials: {
133
+ "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
134
+ }
135
+ )
136
+ case Type<MetadataViews.Traits>():
137
+ let dict: {String: AnyStruct} = {
138
+ "name": self.name,
139
+ "even": self.id % 2 == 0,
140
+ "id": self.id
141
+ }
142
+ return MetadataViews.dictToTraits(dict: dict, excludedNames: [])
143
+ }
144
+ return nil
145
+ }
146
+ }
147
+
148
+ access(all) resource interface ExampleNFTCollectionPublic: NonFungibleToken.Collection {
149
+ access(all) fun deposit(token: @{NonFungibleToken.NFT})
150
+ access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
151
+ post {
152
+ (result == nil) || (result?.id == id):
153
+ "Cannot borrow ExampleNFT reference: the ID of the returned reference is incorrect"
154
+ }
155
+ }
156
+ }
157
+
158
+ access(all) resource Collection: ExampleNFTCollectionPublic {
159
+ access(all) event ResourceDestroyed(id: UInt64 = self.uuid)
160
+
161
+ // dictionary of NFT conforming tokens
162
+ // NFT is a resource type with an `UInt64` ID field
163
+ access(self) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
164
+
165
+ init () {
166
+ self.ownedNFTs <- {}
167
+ emit CollectionCreated(id: self.uuid)
168
+ }
169
+
170
+ access(all) view fun getDefaultStoragePath(): StoragePath? {
171
+ return ExampleNFT.CollectionStoragePath
172
+ }
173
+
174
+ access(all) view fun getDefaultPublicPath(): PublicPath? {
175
+ return ExampleNFT.CollectionPublicPath
176
+ }
177
+
178
+ access(all) view fun getLength(): Int {
179
+ return self.ownedNFTs.length
180
+ }
181
+
182
+ access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
183
+ return {
184
+ Type<@ExampleNFT.NFT>(): true
185
+ }
186
+ }
187
+
188
+ access(all) view fun isSupportedNFTType(type: Type): Bool {
189
+ return type == Type<@ExampleNFT.NFT>()
190
+ }
191
+
192
+ access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
193
+ return <- ExampleNFT.createEmptyCollection()
194
+ }
195
+
196
+ // withdraw removes an NFT from the collection and moves it to the caller
197
+ access(NonFungibleToken.Withdrawable) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
198
+ let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
199
+
200
+ emit Withdraw(id: token.getID(), from: self.owner?.address)
201
+
202
+ return <-token
203
+ }
204
+
205
+ // deposit takes a NFT and adds it to the collections dictionary
206
+ // and adds the ID to the id array
207
+ access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
208
+ let token <- token as! @ExampleNFT.NFT
209
+
210
+ let id: UInt64 = token.id
211
+
212
+ // add the new token to the dictionary which removes the old one
213
+ let oldToken <- self.ownedNFTs[id] <- token
214
+
215
+ emit Deposit(id: id, to: self.owner?.address)
216
+
217
+ destroy oldToken
218
+ }
219
+
220
+ // getIDs returns an array of the IDs that are in the collection
221
+ access(all) view fun getIDs(): [UInt64] {
222
+ return self.ownedNFTs.keys
223
+ }
224
+
225
+ // borrowNFT gets a reference to an NFT in the collection
226
+ // so that the caller can read its metadata and call its methods
227
+ access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
228
+ return &self.ownedNFTs[id]
229
+ }
230
+
231
+ access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
232
+ if self.ownedNFTs[id] != nil {
233
+ // Create an authorized reference to allow downcasting
234
+ let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
235
+ return ref as! &ExampleNFT.NFT
236
+ }
237
+
238
+ return nil
239
+ }
240
+ }
241
+
242
+ // public function that anyone can call to create a new empty collection
243
+ access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
244
+ return <- create Collection()
245
+ }
246
+
247
+ // Resource that an admin or something similar would own to be
248
+ // able to mint new NFTs
249
+ //
250
+ access(all) resource NFTMinter {
251
+ // mintNFT mints a new NFT with a new ID
252
+ // and deposit it in the recipients collection using their collection reference
253
+ access(all) fun mintNFT(
254
+ recipient: &{NonFungibleToken.Collection},
255
+ name: String,
256
+ description: String,
257
+ thumbnail: String,
258
+ royaltyReceipient: Address,
259
+ ) {
260
+ ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1
261
+ self.mintNFTWithId(recipient: recipient, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: royaltyReceipient, id: ExampleNFT.totalSupply)
262
+ }
263
+
264
+ access(all) fun mint(
265
+ name: String,
266
+ description: String,
267
+ thumbnail: String,
268
+ ): @NFT {
269
+ ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1
270
+ let newNFT <- create NFT(
271
+ id: ExampleNFT.totalSupply,
272
+ name: name,
273
+ description: description,
274
+ thumbnail: thumbnail,
275
+ royalties: []
276
+ )
277
+
278
+ return <- newNFT
279
+ }
280
+
281
+ access(all) fun mintNFTWithId(
282
+ recipient: &{NonFungibleToken.Collection},
283
+ name: String,
284
+ description: String,
285
+ thumbnail: String,
286
+ royaltyReceipient: Address,
287
+ id: UInt64
288
+ ) {
289
+ let royaltyRecipient = getAccount(royaltyReceipient).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
290
+ let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: 0.05, description: "")
291
+ // create a new NFT
292
+ var newNFT <- create NFT(
293
+ id: id,
294
+ name: name,
295
+ description: description,
296
+ thumbnail: thumbnail,
297
+ royalties: [cutInfo]
298
+ )
299
+
300
+ // deposit it in the recipient's account using their reference
301
+ recipient.deposit(token: <-newNFT)
302
+ }
303
+
304
+ // mintNFT mints a new NFT with a new ID
305
+ // and deposit it in the recipients collection using their collection reference
306
+ access(all) fun mintNFTWithRoyaltyCuts(
307
+ recipient: &{NonFungibleToken.Collection},
308
+ name: String,
309
+ description: String,
310
+ thumbnail: String,
311
+ royaltyReceipients: [Address],
312
+ royaltyCuts: [UFix64]
313
+ ) {
314
+ assert(royaltyReceipients.length == royaltyCuts.length, message: "mismatched royalty recipients and cuts")
315
+ let royalties: [MetadataViews.Royalty] = []
316
+
317
+ var index = 0
318
+ while index < royaltyReceipients.length {
319
+ let royaltyRecipient = getAccount(royaltyReceipients[index]).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
320
+ let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: royaltyCuts[index], description: "")
321
+ royalties.append(cutInfo)
322
+ index = index + 1
323
+ }
324
+
325
+ ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1
326
+
327
+ // create a new NFT
328
+ var newNFT <- create NFT(
329
+ id: ExampleNFT.totalSupply,
330
+ name: name,
331
+ description: description,
332
+ thumbnail: thumbnail,
333
+ royalties: royalties
334
+ )
335
+
336
+ // deposit it in the recipient's account using their reference
337
+ recipient.deposit(token: <-newNFT)
338
+ }
339
+ }
340
+
341
+ /// Function that resolves a metadata view for this contract.
342
+ ///
343
+ /// @param view: The Type of the desired view.
344
+ /// @return A structure representing the requested view.
345
+ ///
346
+ access(all) view fun resolveView(_ view: Type): AnyStruct? {
347
+ switch view {
348
+ case Type<MetadataViews.NFTCollectionData>():
349
+ return MetadataViews.NFTCollectionData(
350
+ storagePath: ExampleNFT.CollectionStoragePath,
351
+ publicPath: ExampleNFT.CollectionPublicPath,
352
+ providerPath: /private/exampleNFTCollection,
353
+ publicCollection: Type<&ExampleNFT.Collection>(),
354
+ publicLinkedType: Type<&ExampleNFT.Collection>(),
355
+ providerLinkedType: Type<auth(NonFungibleToken.Withdrawable) &ExampleNFT.Collection>(),
356
+ createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
357
+ return <-ExampleNFT.createEmptyCollection()
358
+ })
359
+ )
360
+ case Type<MetadataViews.NFTCollectionDisplay>():
361
+ let media = MetadataViews.Media(
362
+ file: MetadataViews.HTTPFile(
363
+ url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
364
+ ),
365
+ mediaType: "image/svg+xml"
366
+ )
367
+ return MetadataViews.NFTCollectionDisplay(
368
+ name: "The Example Collection",
369
+ description: "This collection is used as an example to help you develop your next Flow NFT.",
370
+ externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
371
+ squareImage: media,
372
+ bannerImage: media,
373
+ socials: {
374
+ "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
375
+ }
376
+ )
377
+ }
378
+ return nil
379
+ }
380
+
381
+ /// Function that returns all the Metadata Views implemented by a Non Fungible Token
382
+ ///
383
+ /// @return An array of Types defining the implemented views. This value will be used by
384
+ /// developers to know which parameter to pass to the resolveView() method.
385
+ ///
386
+ access(all) view fun getViews(): [Type] {
387
+ return [
388
+ Type<MetadataViews.NFTCollectionData>(),
389
+ Type<MetadataViews.NFTCollectionDisplay>()
390
+ ]
391
+ }
392
+
393
+ init() {
394
+ // Initialize the total supply
395
+ self.totalSupply = 0
396
+
397
+ // Set the named paths
398
+ self.CollectionStoragePath = /storage/exampleNFTCollection
399
+ self.CollectionPublicPath = /public/exampleNFTCollection
400
+ self.MinterStoragePath = /storage/exampleNFTMinter
401
+ self.MinterPublicPath = /public/exampleNFTMinter
402
+
403
+ // Create a Collection resource and save it to storage
404
+ let collection <- create Collection()
405
+ self.account.storage.save(<-collection, to: self.CollectionStoragePath)
406
+
407
+ // create a public capability for the collection
408
+ let cap = self.account.capabilities.storage.issue<&ExampleNFT.Collection>(self.CollectionStoragePath)
409
+ self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
410
+
411
+ // Create a Minter resource and save it to storage
412
+ let minter <- create NFTMinter()
413
+ self.account.storage.save(<-minter, to: self.MinterStoragePath)
414
+ let minterCap = self.account.capabilities.storage.issue<&ExampleNFT.NFTMinter>(self.MinterStoragePath)
415
+ self.account.capabilities.publish(minterCap, at: self.MinterPublicPath)
416
+
417
+ emit ContractInitialized()
418
+ }
419
+ }
420
+
@@ -0,0 +1,235 @@
1
+ import "FungibleToken"
2
+
3
+
4
+ // THIS CONTRACT IS FOR TESTING PURPOSES ONLY!
5
+ access(all) contract ExampleToken {
6
+
7
+ /// Total supply of ExampleTokens in existence
8
+ access(all) var totalSupply: UFix64
9
+
10
+ /// TokensInitialized
11
+ ///
12
+ /// The event that is emitted when the contract is created
13
+ access(all) event TokensInitialized(initialSupply: UFix64)
14
+
15
+ /// TokensWithdrawn
16
+ ///
17
+ /// The event that is emitted when tokens are withdrawn from a Vault
18
+ access(all) event TokensWithdrawn(amount: UFix64, from: Address?)
19
+
20
+ /// TokensDeposited
21
+ ///
22
+ /// The event that is emitted when tokens are deposited to a Vault
23
+ access(all) event TokensDeposited(amount: UFix64, to: Address?)
24
+
25
+ /// TokensMinted
26
+ ///
27
+ /// The event that is emitted when new tokens are minted
28
+ access(all) event TokensMinted(amount: UFix64)
29
+
30
+ /// TokensBurned
31
+ ///
32
+ /// The event that is emitted when tokens are destroyed
33
+ access(all) event TokensBurned(amount: UFix64)
34
+
35
+ /// MinterCreated
36
+ ///
37
+ /// The event that is emitted when a new minter resource is created
38
+ access(all) event MinterCreated(allowedAmount: UFix64)
39
+
40
+ /// BurnerCreated
41
+ ///
42
+ /// The event that is emitted when a new burner resource is created
43
+ access(all) event BurnerCreated()
44
+
45
+ /// Vault
46
+ ///
47
+ /// Each user stores an instance of only the Vault in their storage
48
+ /// The functions in the Vault and governed by the pre and post conditions
49
+ /// in FungibleToken when they are called.
50
+ /// The checks happen at runtime whenever a function is called.
51
+ ///
52
+ /// Resources can only be created in the context of the contract that they
53
+ /// are defined in, so there is no way for a malicious user to create Vaults
54
+ /// out of thin air. A special Minter resource needs to be defined to mint
55
+ /// new tokens.
56
+ ///
57
+ access(all) resource Vault: FungibleToken.Vault {
58
+
59
+ /// The total balance of this vault
60
+ access(all) var balance: UFix64
61
+
62
+ // initialize the balance at resource creation time
63
+ init(balance: UFix64) {
64
+ self.balance = balance
65
+ }
66
+
67
+ access(all) view fun getBalance(): UFix64 {
68
+ return self.balance
69
+ }
70
+
71
+ access(all) view fun getDefaultStoragePath(): StoragePath? {
72
+ return /storage/exampleTokenVault
73
+ }
74
+
75
+ access(all) view fun getDefaultPublicPath(): PublicPath? {
76
+ return /public/exampleTokenPublic
77
+ }
78
+
79
+ access(all) view fun getDefaultReceiverPath(): PublicPath? {
80
+ return /public/exampleTokenPublic
81
+ }
82
+
83
+ /// withdraw
84
+ ///
85
+ /// Function that takes an amount as an argument
86
+ /// and withdraws that amount from the Vault.
87
+ ///
88
+ /// It creates a new temporary Vault that is used to hold
89
+ /// the money that is being transferred. It returns the newly
90
+ /// created Vault to the context that called so it can be deposited
91
+ /// elsewhere.
92
+ ///
93
+ access(FungibleToken.Withdrawable) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
94
+ self.balance = self.balance - amount
95
+ emit TokensWithdrawn(amount: amount, from: self.owner?.address)
96
+ return <-create Vault(balance: amount)
97
+ }
98
+
99
+ access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
100
+ return {
101
+ Type<@ExampleToken.Vault>(): true
102
+ }
103
+ }
104
+
105
+ access(all) view fun isSupportedVaultType(type: Type): Bool {
106
+ return type == Type<@ExampleToken.Vault>()
107
+ }
108
+
109
+ /// deposit
110
+ ///
111
+ /// Function that takes a Vault object as an argument and adds
112
+ /// its balance to the balance of the owners Vault.
113
+ ///
114
+ /// It is allowed to destroy the sent Vault because the Vault
115
+ /// was a temporary holder of the tokens. The Vault's balance has
116
+ /// been consumed and therefore can be destroyed.
117
+ ///
118
+ access(all) fun deposit(from: @{FungibleToken.Vault}) {
119
+ let vault <- from as! @ExampleToken.Vault
120
+ self.balance = self.balance + vault.balance
121
+ emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
122
+ vault.balance = 0.0
123
+ destroy vault
124
+ }
125
+
126
+ access(all) fun createEmptyVault(): @Vault {
127
+ return <- ExampleToken.createEmptyVault()
128
+ }
129
+ }
130
+
131
+ /// createEmptyVault
132
+ ///
133
+ /// Function that creates a new Vault with a balance of zero
134
+ /// and returns it to the calling context. A user must call this function
135
+ /// and store the returned Vault in their storage in order to allow their
136
+ /// account to be able to receive deposits of this token type.
137
+ ///
138
+ access(all) fun createEmptyVault(): @Vault {
139
+ return <-create Vault(balance: 0.0)
140
+ }
141
+
142
+ access(all) resource Administrator {
143
+
144
+ /// createNewMinter
145
+ ///
146
+ /// Function that creates and returns a new minter resource
147
+ ///
148
+ access(all) fun createNewMinter(allowedAmount: UFix64): @Minter {
149
+ emit MinterCreated(allowedAmount: allowedAmount)
150
+ return <-create Minter(allowedAmount: allowedAmount)
151
+ }
152
+
153
+ /// createNewBurner
154
+ ///
155
+ /// Function that creates and returns a new burner resource
156
+ ///
157
+ access(all) fun createNewBurner(): @Burner {
158
+ emit BurnerCreated()
159
+ return <-create Burner()
160
+ }
161
+ }
162
+
163
+ /// Minter
164
+ ///
165
+ /// Resource object that token admin accounts can hold to mint new tokens.
166
+ ///
167
+ access(all) resource Minter {
168
+
169
+ /// The amount of tokens that the minter is allowed to mint
170
+ access(all) var allowedAmount: UFix64
171
+
172
+ /// mintTokens
173
+ ///
174
+ /// Function that mints new tokens, adds them to the total supply,
175
+ /// and returns them to the calling context.
176
+ ///
177
+ access(all) fun mintTokens(amount: UFix64): @ExampleToken.Vault {
178
+ pre {
179
+ amount > 0.0: "Amount minted must be greater than zero"
180
+ amount <= self.allowedAmount: "Amount minted must be less than the allowed amount"
181
+ }
182
+ ExampleToken.totalSupply = ExampleToken.totalSupply + amount
183
+ self.allowedAmount = self.allowedAmount - amount
184
+ emit TokensMinted(amount: amount)
185
+ return <-create Vault(balance: amount)
186
+ }
187
+
188
+ init(allowedAmount: UFix64) {
189
+ self.allowedAmount = allowedAmount
190
+ }
191
+ }
192
+
193
+ /// Burner
194
+ ///
195
+ /// Resource object that token admin accounts can hold to burn tokens.
196
+ ///
197
+ access(all) resource Burner {
198
+
199
+ /// burnTokens
200
+ ///
201
+ /// Function that destroys a Vault instance, effectively burning the tokens.
202
+ ///
203
+ /// Note: the burned tokens are automatically subtracted from the
204
+ /// total supply in the Vault destructor.
205
+ ///
206
+ access(all) fun burnTokens(from: @{FungibleToken.Vault}) {
207
+ let vault <- from as! @ExampleToken.Vault
208
+ let amount = vault.balance
209
+ destroy vault
210
+ emit TokensBurned(amount: amount)
211
+ }
212
+ }
213
+
214
+ init() {
215
+ self.totalSupply = 1000.0
216
+
217
+ // Create the Vault with the total supply of tokens and save it in storage
218
+ //
219
+ let vault <- create Vault(balance: self.totalSupply)
220
+ self.account.storage.save(<-vault, to: /storage/exampleTokenVault)
221
+
222
+ // Create a public capability to the stored Vault that only exposes
223
+ // the `deposit` method through the `Receiver` interface
224
+ //
225
+ let publicCap = self.account.capabilities.storage.issue<&ExampleToken.Vault>(/storage/exampleTokenVault)
226
+ self.account.capabilities.publish(publicCap, at: /public/exampleTokenPublic)
227
+
228
+ let admin <- create Administrator()
229
+ self.account.storage.save(<-admin, to: /storage/exampleTokenAdmin)
230
+
231
+ // Emit an event that shows that the contract was initialized
232
+ //
233
+ emit TokensInitialized(initialSupply: self.totalSupply)
234
+ }
235
+ }