@flowtyio/flow-contracts 0.0.10 → 0.0.14

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.
package/README.md CHANGED
@@ -40,17 +40,23 @@ Currently, the list includes:
40
40
  | NonFungibleToken | 0x1d7e57aa55817448 | 0x631e88ae7f1d7c20 |
41
41
  | MetadataViews | 0x1d7e57aa55817448 | 0x631e88ae7f1d7c20 |
42
42
  | ViewResolver | 0x1d7e57aa55817448 | 0x631e88ae7f1d7c20 |
43
- | HybridCusody | 0x294e44e1ec6993c6 | N/A |
44
- | CapabilityFactory | 0x294e44e1ec6993c6 | N/A |
45
- | CapabilityFilter | 0x294e44e1ec6993c6 | N/A |
46
- | CapabilityDelegator | 0x294e44e1ec6993c6 | N/A |
47
- | FTAllFactory | 0x294e44e1ec6993c6 | N/A |
48
- | FTBalanceFactory | 0x294e44e1ec6993c6 | N/A |
49
- | FTProviderFactory | 0x294e44e1ec6993c6 | N/A |
50
- | FTReceiverFactory | 0x294e44e1ec6993c6 | N/A |
51
- | NFTCollectionPublicFactory | 0x294e44e1ec6993c6 | N/A |
52
- | NFTProviderAndCollectionPublicFactory | 0x294e44e1ec6993c6 | N/A |
53
- | NFTProviderFactory | 0x294e44e1ec6993c6 | N/A |
43
+ | HybridCusody | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
44
+ | CapabilityFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
45
+ | CapabilityFilter | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
46
+ | CapabilityDelegator | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
47
+ | FTAllFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
48
+ | FTBalanceFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
49
+ | FTProviderFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
50
+ | FTReceiverFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
51
+ | NFTCollectionPublicFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
52
+ | NFTProviderAndCollectionPublicFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
53
+ | NFTProviderFactory | 0x294e44e1ec6993c6 | 0xd8a7e05a7ac670c0 |
54
+ | NFTCatalog | 0x49a7cda3a1eecc29 | 0x324c34e1c517e4db |
55
+ | NFTCatalogAdmin | 0x49a7cda3a1eecc29 | 0x324c34e1c517e4db |
56
+ | FlowToken | 0x7e60df042a9c0868 | 0x1654653399040a61 |
57
+ | TokenForwarding | 0x51ea0e37c27a1f1a | 0xe544175ee0461c4b |
58
+ | DapperUtilityCoin | 0x82ec283f88a62e65 | 0xead892083b3e2c6c |
59
+ | FlowUtilityToken | 0x82ec283f88a62e65 | 0xead892083b3e2c6c |
54
60
 
55
61
 
56
62
  ## Using a contract
package/add.js CHANGED
@@ -3,18 +3,24 @@ const {getImports} = require("./dependency-tree");
3
3
 
4
4
  const specialContractsHandlers = {
5
5
  "FungibleToken": (contract, userConfig, account) => {
6
- console.log("FungibleToken requires some special setup. The account `emulator-ft` " +
6
+ return handleFungibleTokenAccountContract(contract, userConfig, account, "FungibleToken")
7
+ },
8
+ "FungibleTokenMetadataViews": (contract, userConfig, account) => {
9
+ return handleFungibleTokenAccountContract(contract, userConfig, account, "FungibleTokenMetadataViews")
10
+ },
11
+ "FlowToken": (contract, userConfig, account) => {
12
+ console.log("FlowToken requires some special setup. The account `emulator-flowtoken` " +
7
13
  "will be created and the contract will be deployed to it on the emulator. \nGoing forward, any deployments to the " +
8
14
  "flow emulator will require the --update flag to work correctly.")
9
15
 
10
- const name = "FungibleToken"
16
+ const name = "FlowToken"
11
17
 
12
18
  const serverPK = userConfig.accounts[account].key
13
19
  const ftAccount = {
14
- address: "ee82856bf20e2aa6", // this is the FungibleToken address on the flow emulator
20
+ address: "0ae53cb6e3f42a79", // this is the FungibleToken address on the flow emulator
15
21
  key: serverPK
16
22
  }
17
- const emulatorAcct = "emulator-ft"
23
+ const emulatorAcct = "emulator-flowtoken"
18
24
 
19
25
  // ensure emulator-ft is an account
20
26
  userConfig.accounts[emulatorAcct] = ftAccount
@@ -131,6 +137,40 @@ const addAll = (path, account) => {
131
137
  })
132
138
  }
133
139
 
140
+ const handleFungibleTokenAccountContract = (contract, userConfig, account, contractName) => {
141
+ console.log(`FungibleToken requires some special setup. The account "emulator-ft"\n
142
+ will be created and the contract will be deployed to it on the emulator. \nGoing forward, any deployments to the\n
143
+ flow emulator will require the --update flag to work correctly.`)
144
+
145
+ const serverPK = userConfig.accounts[account].key
146
+ const ftAccount = {
147
+ address: "ee82856bf20e2aa6", // this is the FungibleToken address on the flow emulator
148
+ key: serverPK
149
+ }
150
+ const emulatorAcct = "emulator-ft"
151
+
152
+ // ensure emulator-ft is an account
153
+ userConfig.accounts[emulatorAcct] = ftAccount
154
+ if (!userConfig.deployments) {
155
+ userConfig.deployments = {}
156
+ }
157
+
158
+ // ensure that emulator-ft is a deployment account
159
+ if (!userConfig.deployments.emulator) {
160
+ userConfig.deployments.emulator = {}
161
+ }
162
+
163
+ if (!userConfig.deployments.emulator[emulatorAcct]) {
164
+ userConfig.deployments.emulator[emulatorAcct] = []
165
+ }
166
+
167
+ userConfig.contracts[contractName] = contract
168
+
169
+ if (!userConfig.deployments.emulator[emulatorAcct].includes(contractName)) {
170
+ userConfig.deployments.emulator[emulatorAcct].push(contractName)
171
+ }
172
+ }
173
+
134
174
  module.exports = {
135
175
  add,
136
176
  addAll
@@ -0,0 +1,193 @@
1
+ /*
2
+ * The FlowStorageFees smart contract
3
+ *
4
+ * An account's storage capacity determines up to how much storage on chain it can use.
5
+ * A storage capacity is calculated by multiplying the amount of reserved flow with `StorageFee.storageMegaBytesPerReservedFLOW`
6
+ * The minimum amount of flow tokens reserved for storage capacity is `FlowStorageFees.minimumStorageReservation` this is paid during account creation, by the creator.
7
+ *
8
+ * At the end of all transactions, any account that had any value changed in their storage
9
+ * has their storage capacity checked against their storage used and their main flow token vault against the minimum reservation.
10
+ * If any account fails this check the transaction wil fail.
11
+ *
12
+ * An account moving/deleting its `FlowToken.Vault` resource will result
13
+ * in the transaction failing because the account will have no storage capacity.
14
+ *
15
+ */
16
+
17
+ import "FungibleToken"
18
+ import "FlowToken"
19
+
20
+ pub contract FlowStorageFees {
21
+
22
+ // Emitted when the amount of storage capacity an account has per reserved Flow token changes
23
+ pub event StorageMegaBytesPerReservedFLOWChanged(_ storageMegaBytesPerReservedFLOW: UFix64)
24
+
25
+ // Emitted when the minimum amount of Flow tokens that an account needs to have reserved for storage capacity changes.
26
+ pub event MinimumStorageReservationChanged(_ minimumStorageReservation: UFix64)
27
+
28
+ // Defines how much storage capacity every account has per reserved Flow token.
29
+ // definition is written per unit of flow instead of the inverse,
30
+ // so there is no loss of precision calculating storage from flow,
31
+ // but there is loss of precision when calculating flow per storage.
32
+ pub var storageMegaBytesPerReservedFLOW: UFix64
33
+
34
+ // Defines the minimum amount of Flow tokens that every account needs to have reserved for storage capacity.
35
+ // If an account has less then this amount reserved by the end of any transaction it participated in, the transaction will fail.
36
+ pub var minimumStorageReservation: UFix64
37
+
38
+ // An administrator resource that can change the parameters of the FlowStorageFees smart contract.
39
+ pub resource Administrator {
40
+
41
+ // Changes the amount of storage capacity an account has per accounts' reserved storage FLOW.
42
+ pub fun setStorageMegaBytesPerReservedFLOW(_ storageMegaBytesPerReservedFLOW: UFix64) {
43
+ if FlowStorageFees.storageMegaBytesPerReservedFLOW == storageMegaBytesPerReservedFLOW {
44
+ return
45
+ }
46
+ FlowStorageFees.storageMegaBytesPerReservedFLOW = storageMegaBytesPerReservedFLOW
47
+ emit StorageMegaBytesPerReservedFLOWChanged(storageMegaBytesPerReservedFLOW)
48
+ }
49
+
50
+ // Changes the minimum amount of FLOW an account has to have reserved.
51
+ pub fun setMinimumStorageReservation(_ minimumStorageReservation: UFix64) {
52
+ if FlowStorageFees.minimumStorageReservation == minimumStorageReservation {
53
+ return
54
+ }
55
+ FlowStorageFees.minimumStorageReservation = minimumStorageReservation
56
+ emit MinimumStorageReservationChanged(minimumStorageReservation)
57
+ }
58
+
59
+ access(contract) init(){}
60
+ }
61
+
62
+ /// calculateAccountCapacity returns the storage capacity of an account
63
+ ///
64
+ /// Returns megabytes
65
+ /// If the account has no default balance it is counted as a balance of 0.0 FLOW
66
+ pub fun calculateAccountCapacity(_ accountAddress: Address): UFix64 {
67
+ var balance = 0.0
68
+ if let balanceRef = getAccount(accountAddress)
69
+ .getCapability<&FlowToken.Vault{FungibleToken.Balance}>(/public/flowTokenBalance)!
70
+ .borrow() {
71
+ balance = balanceRef.balance
72
+ }
73
+
74
+ return self.accountBalanceToAccountStorageCapacity(balance)
75
+ }
76
+
77
+ /// calculateAccountsCapacity returns the storage capacity of a batch of accounts
78
+ pub fun calculateAccountsCapacity(_ accountAddresses: [Address]): [UFix64] {
79
+ let capacities: [UFix64] = []
80
+ for accountAddress in accountAddresses {
81
+ let capacity = self.calculateAccountCapacity(accountAddress)
82
+ capacities.append(capacity)
83
+ }
84
+ return capacities
85
+ }
86
+
87
+ // getAccountsCapacityForTransactionStorageCheck returns the storage capacity of a batch of accounts
88
+ // This is used to check if a transaction will fail because of any account being over the storage capacity
89
+ // The payer is an exception as its storage capacity is derived from its balance minus the maximum possible transaction fees
90
+ // (transaction fees if the execution effort is at the execution efort limit, a.k.a.: computation limit, a.k.a.: gas limit)
91
+ pub fun getAccountsCapacityForTransactionStorageCheck(accountAddresses: [Address], payer: Address, maxTxFees: UFix64): [UFix64] {
92
+ let capacities: [UFix64] = []
93
+ for accountAddress in accountAddresses {
94
+ var balance = 0.0
95
+ if let balanceRef = getAccount(accountAddress)
96
+ .getCapability<&FlowToken.Vault{FungibleToken.Balance}>(/public/flowTokenBalance)!
97
+ .borrow() {
98
+ if accountAddress == payer {
99
+ // if the account is the payer, deduct the maximum possible transaction fees from the balance
100
+ balance = balanceRef.balance.saturatingSubtract(maxTxFees)
101
+ } else {
102
+ balance = balanceRef.balance
103
+ }
104
+ }
105
+
106
+ capacities.append(self.accountBalanceToAccountStorageCapacity(balance))
107
+ }
108
+ return capacities
109
+ }
110
+
111
+ // accountBalanceToAccountStorageCapacity returns the storage capacity
112
+ // an account would have with given the flow balance of the account.
113
+ pub fun accountBalanceToAccountStorageCapacity(_ balance: UFix64): UFix64 {
114
+ // get address token balance
115
+ if balance < self.minimumStorageReservation {
116
+ // if < then minimum return 0
117
+ return 0.0
118
+ }
119
+
120
+ // return balance multiplied with megabytes per flow
121
+ return self.flowToStorageCapacity(balance)
122
+ }
123
+
124
+ // Amount in Flow tokens
125
+ // Returns megabytes
126
+ pub fun flowToStorageCapacity(_ amount: UFix64): UFix64 {
127
+ return amount.saturatingMultiply(FlowStorageFees.storageMegaBytesPerReservedFLOW)
128
+ }
129
+
130
+ // Amount in megabytes
131
+ // Returns Flow tokens
132
+ pub fun storageCapacityToFlow(_ amount: UFix64): UFix64 {
133
+ if FlowStorageFees.storageMegaBytesPerReservedFLOW == 0.0 as UFix64 {
134
+ return 0.0 as UFix64
135
+ }
136
+ // possible loss of precision
137
+ // putting the result back into `flowToStorageCapacity` might not yield the same result
138
+ return amount / FlowStorageFees.storageMegaBytesPerReservedFLOW
139
+ }
140
+
141
+ // converts storage used from UInt64 Bytes to UFix64 Megabytes.
142
+ pub fun convertUInt64StorageBytesToUFix64Megabytes(_ storage: UInt64): UFix64 {
143
+ // safe convert UInt64 to UFix64 (without overflow)
144
+ let f = UFix64(storage % 100000000 as UInt64) * 0.00000001 as UFix64 + UFix64(storage / 100000000 as UInt64)
145
+ // decimal point correction. Megabytes to bytes have a conversion of 10^-6 while UFix64 minimum value is 10^-8
146
+ let storageMb = f.saturatingMultiply(100.0)
147
+ return storageMb
148
+ }
149
+
150
+ /// Gets "available" balance of an account
151
+ ///
152
+ /// The available balance of an account is its default token balance minus what is reserved for storage.
153
+ /// If the account has no default balance it is counted as a balance of 0.0 FLOW
154
+ pub fun defaultTokenAvailableBalance(_ accountAddress: Address): UFix64 {
155
+ //get balance of account
156
+ let acct = getAccount(accountAddress)
157
+ var balance = 0.0
158
+ if let balanceRef = acct
159
+ .getCapability(/public/flowTokenBalance)
160
+ .borrow<&FlowToken.Vault{FungibleToken.Balance}>() {
161
+ balance = balanceRef.balance
162
+ }
163
+
164
+ // get how much should be reserved for storage
165
+ var reserved = self.defaultTokenReservedBalance(accountAddress)
166
+
167
+ return balance.saturatingSubtract(reserved)
168
+ }
169
+
170
+ /// Gets "reserved" balance of an account
171
+ ///
172
+ /// The reserved balance of an account is its storage used multiplied by the storage cost per flow token.
173
+ /// The reserved balance is at least the minimum storage reservation.
174
+ pub fun defaultTokenReservedBalance(_ accountAddress: Address): UFix64 {
175
+ let acct = getAccount(accountAddress)
176
+ var reserved = self.storageCapacityToFlow(self.convertUInt64StorageBytesToUFix64Megabytes(acct.storageUsed))
177
+ // at least self.minimumStorageReservation should be reserved
178
+ if reserved < self.minimumStorageReservation {
179
+ reserved = self.minimumStorageReservation
180
+ }
181
+
182
+ return reserved
183
+ }
184
+
185
+ init() {
186
+ self.storageMegaBytesPerReservedFLOW = 1.0 // 1 Mb per 1 Flow token
187
+ self.minimumStorageReservation = 0.0 // or 0 kb of minimum storage reservation
188
+
189
+ let admin <- create Administrator()
190
+ self.account.save(<-admin, to: /storage/storageFeesAdmin)
191
+ }
192
+ }
193
+
@@ -0,0 +1,274 @@
1
+ import "FungibleToken"
2
+ import "MetadataViews"
3
+ import "FungibleTokenMetadataViews"
4
+ import "ViewResolver"
5
+
6
+ pub contract FlowToken: FungibleToken, ViewResolver {
7
+
8
+ // Total supply of Flow tokens in existence
9
+ pub var totalSupply: UFix64
10
+
11
+ // Event that is emitted when the contract is created
12
+ pub event TokensInitialized(initialSupply: UFix64)
13
+
14
+ // Event that is emitted when tokens are withdrawn from a Vault
15
+ pub event TokensWithdrawn(amount: UFix64, from: Address?)
16
+
17
+ // Event that is emitted when tokens are deposited to a Vault
18
+ pub event TokensDeposited(amount: UFix64, to: Address?)
19
+
20
+ // Event that is emitted when new tokens are minted
21
+ pub event TokensMinted(amount: UFix64)
22
+
23
+ // Event that is emitted when tokens are destroyed
24
+ pub event TokensBurned(amount: UFix64)
25
+
26
+ // Event that is emitted when a new minter resource is created
27
+ pub event MinterCreated(allowedAmount: UFix64)
28
+
29
+ // Event that is emitted when a new burner resource is created
30
+ pub event BurnerCreated()
31
+
32
+ // Vault
33
+ //
34
+ // Each user stores an instance of only the Vault in their storage
35
+ // The functions in the Vault and governed by the pre and post conditions
36
+ // in FungibleToken when they are called.
37
+ // The checks happen at runtime whenever a function is called.
38
+ //
39
+ // Resources can only be created in the context of the contract that they
40
+ // are defined in, so there is no way for a malicious user to create Vaults
41
+ // out of thin air. A special Minter resource needs to be defined to mint
42
+ // new tokens.
43
+ //
44
+ pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance, MetadataViews.Resolver {
45
+
46
+ // holds the balance of a users tokens
47
+ pub var balance: UFix64
48
+
49
+ // initialize the balance at resource creation time
50
+ init(balance: UFix64) {
51
+ self.balance = balance
52
+ }
53
+
54
+ // withdraw
55
+ //
56
+ // Function that takes an integer amount as an argument
57
+ // and withdraws that amount from the Vault.
58
+ // It creates a new temporary Vault that is used to hold
59
+ // the money that is being transferred. It returns the newly
60
+ // created Vault to the context that called so it can be deposited
61
+ // elsewhere.
62
+ //
63
+ pub fun withdraw(amount: UFix64): @FungibleToken.Vault {
64
+ self.balance = self.balance - amount
65
+ emit TokensWithdrawn(amount: amount, from: self.owner?.address)
66
+ return <-create Vault(balance: amount)
67
+ }
68
+
69
+ // deposit
70
+ //
71
+ // Function that takes a Vault object as an argument and adds
72
+ // its balance to the balance of the owners Vault.
73
+ // It is allowed to destroy the sent Vault because the Vault
74
+ // was a temporary holder of the tokens. The Vault's balance has
75
+ // been consumed and therefore can be destroyed.
76
+ pub fun deposit(from: @FungibleToken.Vault) {
77
+ let vault <- from as! @FlowToken.Vault
78
+ self.balance = self.balance + vault.balance
79
+ emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
80
+ vault.balance = 0.0
81
+ destroy vault
82
+ }
83
+
84
+ destroy() {
85
+ if self.balance > 0.0 {
86
+ FlowToken.totalSupply = FlowToken.totalSupply - self.balance
87
+ }
88
+ }
89
+
90
+ /// Get all the Metadata Views implemented by FlowToken
91
+ ///
92
+ /// @return An array of Types defining the implemented views. This value will be used by
93
+ /// developers to know which parameter to pass to the resolveView() method.
94
+ ///
95
+ pub fun getViews(): [Type]{
96
+ return FlowToken.getViews()
97
+ }
98
+
99
+ /// Get a Metadata View from FlowToken
100
+ ///
101
+ /// @param view: The Type of the desired view.
102
+ /// @return A structure representing the requested view.
103
+ ///
104
+ pub fun resolveView(_ view: Type): AnyStruct? {
105
+ return FlowToken.resolveView(view)
106
+ }
107
+ }
108
+
109
+ // createEmptyVault
110
+ //
111
+ // Function that creates a new Vault with a balance of zero
112
+ // and returns it to the calling context. A user must call this function
113
+ // and store the returned Vault in their storage in order to allow their
114
+ // account to be able to receive deposits of this token type.
115
+ //
116
+ pub fun createEmptyVault(): @FungibleToken.Vault {
117
+ return <-create Vault(balance: 0.0)
118
+ }
119
+
120
+ pub fun getViews(): [Type] {
121
+ return [Type<FungibleTokenMetadataViews.FTView>(),
122
+ Type<FungibleTokenMetadataViews.FTDisplay>(),
123
+ Type<FungibleTokenMetadataViews.FTVaultData>()]
124
+ }
125
+
126
+ /// Get a Metadata View from FlowToken
127
+ ///
128
+ /// @param view: The Type of the desired view.
129
+ /// @return A structure representing the requested view.
130
+ ///
131
+ pub fun resolveView(_ view: Type): AnyStruct? {
132
+ switch view {
133
+ case Type<FungibleTokenMetadataViews.FTView>():
134
+ return FungibleTokenMetadataViews.FTView(
135
+ ftDisplay: self.resolveView(Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
136
+ ftVaultData: self.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
137
+ )
138
+ case Type<FungibleTokenMetadataViews.FTDisplay>():
139
+ let media = MetadataViews.Media(
140
+ file: MetadataViews.HTTPFile(
141
+ url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
142
+ ),
143
+ mediaType: "image/svg+xml"
144
+ )
145
+ let medias = MetadataViews.Medias([media])
146
+ return FungibleTokenMetadataViews.FTDisplay(
147
+ name: "FLOW Network Token",
148
+ symbol: "FLOW",
149
+ description: "FLOW is the protocol token that is required for transaction fees, storage fees, staking, and many applications built on the Flow Blockchain",
150
+ externalURL: MetadataViews.ExternalURL("https://flow.com"),
151
+ logos: medias,
152
+ socials: {
153
+ "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
154
+ }
155
+ )
156
+ case Type<FungibleTokenMetadataViews.FTVaultData>():
157
+ return FungibleTokenMetadataViews.FTVaultData(
158
+ storagePath: /storage/flowTokenVault,
159
+ receiverPath: /public/flowTokenReceiver,
160
+ metadataPath: /public/flowTokenBalance,
161
+ providerPath: /private/flowTokenVault,
162
+ receiverLinkedType: Type<&FlowToken.Vault{FungibleToken.Receiver, FungibleToken.Balance, MetadataViews.Resolver}>(),
163
+ metadataLinkedType: Type<&FlowToken.Vault{FungibleToken.Balance, MetadataViews.Resolver}>(),
164
+ providerLinkedType: Type<&FlowToken.Vault{FungibleToken.Provider}>(),
165
+ createEmptyVaultFunction: (fun (): @FungibleToken.Vault {
166
+ return <-FlowToken.createEmptyVault()
167
+ })
168
+ )
169
+ }
170
+ return nil
171
+ }
172
+
173
+ pub resource Administrator {
174
+ // createNewMinter
175
+ //
176
+ // Function that creates and returns a new minter resource
177
+ //
178
+ pub fun createNewMinter(allowedAmount: UFix64): @Minter {
179
+ emit MinterCreated(allowedAmount: allowedAmount)
180
+ return <-create Minter(allowedAmount: allowedAmount)
181
+ }
182
+
183
+ // createNewBurner
184
+ //
185
+ // Function that creates and returns a new burner resource
186
+ //
187
+ pub fun createNewBurner(): @Burner {
188
+ emit BurnerCreated()
189
+ return <-create Burner()
190
+ }
191
+ }
192
+
193
+ // Minter
194
+ //
195
+ // Resource object that token admin accounts can hold to mint new tokens.
196
+ //
197
+ pub resource Minter {
198
+
199
+ // the amount of tokens that the minter is allowed to mint
200
+ pub var allowedAmount: UFix64
201
+
202
+ // mintTokens
203
+ //
204
+ // Function that mints new tokens, adds them to the total supply,
205
+ // and returns them to the calling context.
206
+ //
207
+ pub fun mintTokens(amount: UFix64): @FlowToken.Vault {
208
+ pre {
209
+ amount > UFix64(0): "Amount minted must be greater than zero"
210
+ amount <= self.allowedAmount: "Amount minted must be less than the allowed amount"
211
+ }
212
+ FlowToken.totalSupply = FlowToken.totalSupply + amount
213
+ self.allowedAmount = self.allowedAmount - amount
214
+ emit TokensMinted(amount: amount)
215
+ return <-create Vault(balance: amount)
216
+ }
217
+
218
+ init(allowedAmount: UFix64) {
219
+ self.allowedAmount = allowedAmount
220
+ }
221
+ }
222
+
223
+ // Burner
224
+ //
225
+ // Resource object that token admin accounts can hold to burn tokens.
226
+ //
227
+ pub resource Burner {
228
+
229
+ // burnTokens
230
+ //
231
+ // Function that destroys a Vault instance, effectively burning the tokens.
232
+ //
233
+ // Note: the burned tokens are automatically subtracted from the
234
+ // total supply in the Vault destructor.
235
+ //
236
+ pub fun burnTokens(from: @FungibleToken.Vault) {
237
+ let vault <- from as! @FlowToken.Vault
238
+ let amount = vault.balance
239
+ destroy vault
240
+ emit TokensBurned(amount: amount)
241
+ }
242
+ }
243
+
244
+ init(adminAccount: AuthAccount) {
245
+ self.totalSupply = 0.0
246
+
247
+ // Create the Vault with the total supply of tokens and save it in storage
248
+ //
249
+ let vault <- create Vault(balance: self.totalSupply)
250
+ adminAccount.save(<-vault, to: /storage/flowTokenVault)
251
+
252
+ // Create a public capability to the stored Vault that only exposes
253
+ // the `deposit` method through the `Receiver` interface
254
+ //
255
+ adminAccount.link<&FlowToken.Vault{FungibleToken.Receiver, FungibleToken.Balance, MetadataViews.Resolver}>(
256
+ /public/flowTokenReceiver,
257
+ target: /storage/flowTokenVault
258
+ )
259
+
260
+ // Create a public capability to the stored Vault that only exposes
261
+ // the `balance` field through the `Balance` interface
262
+ //
263
+ adminAccount.link<&FlowToken.Vault{FungibleToken.Balance, MetadataViews.Resolver}>(
264
+ /public/flowTokenBalance,
265
+ target: /storage/flowTokenVault
266
+ )
267
+
268
+ let admin <- create Administrator()
269
+ adminAccount.save(<-admin, to: /storage/flowTokenAdmin)
270
+
271
+ // Emit an event that shows that the contract was initialized
272
+ emit TokensInitialized(initialSupply: self.totalSupply)
273
+ }
274
+ }