@flowtyio/flow-contracts 0.1.0-beta.1 → 0.1.0-beta.3

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,422 @@
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
+ providerPath: /private/exampleNFTCollection,
114
+ publicCollection: Type<&ExampleNFT.Collection>(),
115
+ publicLinkedType: Type<&ExampleNFT.Collection>(),
116
+ providerLinkedType: Type<auth(NonFungibleToken.Withdrawable) &ExampleNFT.Collection>(),
117
+ createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
118
+ return <-ExampleNFT.createEmptyCollection()
119
+ })
120
+ )
121
+ case Type<MetadataViews.NFTCollectionDisplay>():
122
+ let media = MetadataViews.Media(
123
+ file: MetadataViews.HTTPFile(
124
+ url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
125
+ ),
126
+ mediaType: "image/svg+xml"
127
+ )
128
+ return MetadataViews.NFTCollectionDisplay(
129
+ name: "The Example Collection",
130
+ description: "This collection is used as an example to help you develop your next Flow NFT.",
131
+ externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
132
+ squareImage: media,
133
+ bannerImage: media,
134
+ socials: {
135
+ "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
136
+ }
137
+ )
138
+ case Type<MetadataViews.Traits>():
139
+ let dict: {String: AnyStruct} = {
140
+ "name": self.name,
141
+ "even": self.id % 2 == 0,
142
+ "id": self.id
143
+ }
144
+ return MetadataViews.dictToTraits(dict: dict, excludedNames: [])
145
+ }
146
+ return nil
147
+ }
148
+ }
149
+
150
+ access(all) resource interface ExampleNFTCollectionPublic: NonFungibleToken.Collection {
151
+ access(all) fun deposit(token: @{NonFungibleToken.NFT})
152
+ access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
153
+ post {
154
+ (result == nil) || (result?.id == id):
155
+ "Cannot borrow ExampleNFT reference: the ID of the returned reference is incorrect"
156
+ }
157
+ }
158
+ }
159
+
160
+ access(all) resource Collection: ExampleNFTCollectionPublic {
161
+ access(all) event ResourceDestroyed(id: UInt64 = self.uuid)
162
+
163
+ // dictionary of NFT conforming tokens
164
+ // NFT is a resource type with an `UInt64` ID field
165
+ access(self) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
166
+
167
+ init () {
168
+ self.ownedNFTs <- {}
169
+ emit CollectionCreated(id: self.uuid)
170
+ }
171
+
172
+ access(all) view fun getDefaultStoragePath(): StoragePath? {
173
+ return ExampleNFT.CollectionStoragePath
174
+ }
175
+
176
+ access(all) view fun getDefaultPublicPath(): PublicPath? {
177
+ return ExampleNFT.CollectionPublicPath
178
+ }
179
+
180
+ access(all) view fun getLength(): Int {
181
+ return self.ownedNFTs.length
182
+ }
183
+
184
+ access(all) view fun getSupportedNFTTypes(): {Type: Bool} {
185
+ return {
186
+ Type<@ExampleNFT.NFT>(): true
187
+ }
188
+ }
189
+
190
+ access(all) view fun isSupportedNFTType(type: Type): Bool {
191
+ return type == Type<@ExampleNFT.NFT>()
192
+ }
193
+
194
+ access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
195
+ return <- ExampleNFT.createEmptyCollection()
196
+ }
197
+
198
+ // withdraw removes an NFT from the collection and moves it to the caller
199
+ access(NonFungibleToken.Withdrawable) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
200
+ let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT")
201
+
202
+ emit Withdraw(id: token.getID(), from: self.owner?.address)
203
+
204
+ return <-token
205
+ }
206
+
207
+ // deposit takes a NFT and adds it to the collections dictionary
208
+ // and adds the ID to the id array
209
+ access(all) fun deposit(token: @{NonFungibleToken.NFT}) {
210
+ let token <- token as! @ExampleNFT.NFT
211
+
212
+ let id: UInt64 = token.id
213
+
214
+ // add the new token to the dictionary which removes the old one
215
+ let oldToken <- self.ownedNFTs[id] <- token
216
+
217
+ emit Deposit(id: id, to: self.owner?.address)
218
+
219
+ destroy oldToken
220
+ }
221
+
222
+ // getIDs returns an array of the IDs that are in the collection
223
+ access(all) view fun getIDs(): [UInt64] {
224
+ return self.ownedNFTs.keys
225
+ }
226
+
227
+ // borrowNFT gets a reference to an NFT in the collection
228
+ // so that the caller can read its metadata and call its methods
229
+ access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
230
+ return &self.ownedNFTs[id]
231
+ }
232
+
233
+ access(all) fun borrowExampleNFT(id: UInt64): &ExampleNFT.NFT? {
234
+ if self.ownedNFTs[id] != nil {
235
+ // Create an authorized reference to allow downcasting
236
+ let ref = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)!
237
+ return ref as! &ExampleNFT.NFT
238
+ }
239
+
240
+ return nil
241
+ }
242
+ }
243
+
244
+ // public function that anyone can call to create a new empty collection
245
+ access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} {
246
+ return <- create Collection()
247
+ }
248
+
249
+ // Resource that an admin or something similar would own to be
250
+ // able to mint new NFTs
251
+ //
252
+ access(all) resource NFTMinter {
253
+ // mintNFT mints a new NFT with a new ID
254
+ // and deposit it in the recipients collection using their collection reference
255
+ access(all) fun mintNFT(
256
+ recipient: &{NonFungibleToken.Collection},
257
+ name: String,
258
+ description: String,
259
+ thumbnail: String,
260
+ royaltyReceipient: Address,
261
+ ) {
262
+ ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1
263
+ self.mintNFTWithId(recipient: recipient, name: name, description: description, thumbnail: thumbnail, royaltyReceipient: royaltyReceipient, id: ExampleNFT.totalSupply)
264
+ }
265
+
266
+ access(all) fun mint(
267
+ name: String,
268
+ description: String,
269
+ thumbnail: String,
270
+ ): @NFT {
271
+ ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1
272
+ let newNFT <- create NFT(
273
+ id: ExampleNFT.totalSupply,
274
+ name: name,
275
+ description: description,
276
+ thumbnail: thumbnail,
277
+ royalties: []
278
+ )
279
+
280
+ return <- newNFT
281
+ }
282
+
283
+ access(all) fun mintNFTWithId(
284
+ recipient: &{NonFungibleToken.Collection},
285
+ name: String,
286
+ description: String,
287
+ thumbnail: String,
288
+ royaltyReceipient: Address,
289
+ id: UInt64
290
+ ) {
291
+ let royaltyRecipient = getAccount(royaltyReceipient).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
292
+ let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: 0.05, description: "")
293
+ // create a new NFT
294
+ var newNFT <- create NFT(
295
+ id: id,
296
+ name: name,
297
+ description: description,
298
+ thumbnail: thumbnail,
299
+ royalties: [cutInfo]
300
+ )
301
+
302
+ // deposit it in the recipient's account using their reference
303
+ recipient.deposit(token: <-newNFT)
304
+ }
305
+
306
+ // mintNFT mints a new NFT with a new ID
307
+ // and deposit it in the recipients collection using their collection reference
308
+ access(all) fun mintNFTWithRoyaltyCuts(
309
+ recipient: &{NonFungibleToken.Collection},
310
+ name: String,
311
+ description: String,
312
+ thumbnail: String,
313
+ royaltyReceipients: [Address],
314
+ royaltyCuts: [UFix64]
315
+ ) {
316
+ assert(royaltyReceipients.length == royaltyCuts.length, message: "mismatched royalty recipients and cuts")
317
+ let royalties: [MetadataViews.Royalty] = []
318
+
319
+ var index = 0
320
+ while index < royaltyReceipients.length {
321
+ let royaltyRecipient = getAccount(royaltyReceipients[index]).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver)!
322
+ let cutInfo = MetadataViews.Royalty(receiver: royaltyRecipient, cut: royaltyCuts[index], description: "")
323
+ royalties.append(cutInfo)
324
+ index = index + 1
325
+ }
326
+
327
+ ExampleNFT.totalSupply = ExampleNFT.totalSupply + 1
328
+
329
+ // create a new NFT
330
+ var newNFT <- create NFT(
331
+ id: ExampleNFT.totalSupply,
332
+ name: name,
333
+ description: description,
334
+ thumbnail: thumbnail,
335
+ royalties: royalties
336
+ )
337
+
338
+ // deposit it in the recipient's account using their reference
339
+ recipient.deposit(token: <-newNFT)
340
+ }
341
+ }
342
+
343
+ /// Function that resolves a metadata view for this contract.
344
+ ///
345
+ /// @param view: The Type of the desired view.
346
+ /// @return A structure representing the requested view.
347
+ ///
348
+ access(all) view fun resolveView(_ view: Type): AnyStruct? {
349
+ switch view {
350
+ case Type<MetadataViews.NFTCollectionData>():
351
+ return MetadataViews.NFTCollectionData(
352
+ storagePath: ExampleNFT.CollectionStoragePath,
353
+ publicPath: ExampleNFT.CollectionPublicPath,
354
+ providerPath: /private/exampleNFTCollection,
355
+ publicCollection: Type<&ExampleNFT.Collection>(),
356
+ publicLinkedType: Type<&ExampleNFT.Collection>(),
357
+ providerLinkedType: Type<auth(NonFungibleToken.Withdrawable) &ExampleNFT.Collection>(),
358
+ createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} {
359
+ return <-ExampleNFT.createEmptyCollection()
360
+ })
361
+ )
362
+ case Type<MetadataViews.NFTCollectionDisplay>():
363
+ let media = MetadataViews.Media(
364
+ file: MetadataViews.HTTPFile(
365
+ url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
366
+ ),
367
+ mediaType: "image/svg+xml"
368
+ )
369
+ return MetadataViews.NFTCollectionDisplay(
370
+ name: "The Example Collection",
371
+ description: "This collection is used as an example to help you develop your next Flow NFT.",
372
+ externalURL: MetadataViews.ExternalURL("https://example-nft.onflow.org"),
373
+ squareImage: media,
374
+ bannerImage: media,
375
+ socials: {
376
+ "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
377
+ }
378
+ )
379
+ }
380
+ return nil
381
+ }
382
+
383
+ /// Function that returns all the Metadata Views implemented by a Non Fungible Token
384
+ ///
385
+ /// @return An array of Types defining the implemented views. This value will be used by
386
+ /// developers to know which parameter to pass to the resolveView() method.
387
+ ///
388
+ access(all) view fun getViews(): [Type] {
389
+ return [
390
+ Type<MetadataViews.NFTCollectionData>(),
391
+ Type<MetadataViews.NFTCollectionDisplay>()
392
+ ]
393
+ }
394
+
395
+ init() {
396
+ // Initialize the total supply
397
+ self.totalSupply = 0
398
+
399
+ // Set the named paths
400
+ self.CollectionStoragePath = /storage/exampleNFTCollection
401
+ self.CollectionPublicPath = /public/exampleNFTCollection
402
+ self.MinterStoragePath = /storage/exampleNFTMinter
403
+ self.MinterPublicPath = /public/exampleNFTMinter
404
+
405
+ // Create a Collection resource and save it to storage
406
+ let collection <- create Collection()
407
+ self.account.storage.save(<-collection, to: self.CollectionStoragePath)
408
+
409
+ // create a public capability for the collection
410
+ let cap = self.account.capabilities.storage.issue<&ExampleNFT.Collection>(self.CollectionStoragePath)
411
+ self.account.capabilities.publish(cap, at: self.CollectionPublicPath)
412
+
413
+ // Create a Minter resource and save it to storage
414
+ let minter <- create NFTMinter()
415
+ self.account.storage.save(<-minter, to: self.MinterStoragePath)
416
+ let minterCap = self.account.capabilities.storage.issue<&ExampleNFT.NFTMinter>(self.MinterStoragePath)
417
+ self.account.capabilities.publish(minterCap, at: self.MinterPublicPath)
418
+
419
+ emit ContractInitialized()
420
+ }
421
+ }
422
+
@@ -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
+ }