Smart Contract
FlowToken
A.1654653399040a61.FlowToken
1import FungibleToken from 0xf233dcee88fe0abe
2import MetadataViews from 0x1d7e57aa55817448
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5access(all) contract FlowToken: FungibleToken {
6
7 // Total supply of Flow tokens in existence
8 access(all) var totalSupply: UFix64
9
10 // Event that is emitted when tokens are withdrawn from a Vault
11 access(all) event TokensWithdrawn(amount: UFix64, from: Address?)
12
13 // Event that is emitted when tokens are deposited to a Vault
14 access(all) event TokensDeposited(amount: UFix64, to: Address?)
15
16 // Event that is emitted when new tokens are minted
17 access(all) event TokensMinted(amount: UFix64)
18
19 // Event that is emitted when a new minter resource is created
20 access(all) event MinterCreated(allowedAmount: UFix64)
21
22 // Event that is emitted when a new burner resource is created
23 access(all) event BurnerCreated()
24
25 // Vault
26 //
27 // Each user stores an instance of only the Vault in their storage
28 // The functions in the Vault and governed by the pre and post conditions
29 // in FungibleToken when they are called.
30 // The checks happen at runtime whenever a function is called.
31 //
32 // Resources can only be created in the context of the contract that they
33 // are defined in, so there is no way for a malicious user to create Vaults
34 // out of thin air. A special Minter resource needs to be defined to mint
35 // new tokens.
36 //
37 access(all) resource Vault: FungibleToken.Vault {
38
39 // holds the balance of a users tokens
40 access(all) var balance: UFix64
41
42 // initialize the balance at resource creation time
43 init(balance: UFix64) {
44 self.balance = balance
45 }
46
47 /// Called when a fungible token is burned via the `Burner.burn()` method
48 access(contract) fun burnCallback() {
49 if self.balance > 0.0 {
50 FlowToken.totalSupply = FlowToken.totalSupply - self.balance
51 }
52 self.balance = 0.0
53 }
54
55 /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
56 access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
57 return {self.getType(): true}
58 }
59
60 access(all) view fun isSupportedVaultType(type: Type): Bool {
61 if (type == self.getType()) { return true } else { return false }
62 }
63
64 /// Asks if the amount can be withdrawn from this vault
65 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
66 return amount <= self.balance
67 }
68
69 // withdraw
70 //
71 // Function that takes an integer amount as an argument
72 // and withdraws that amount from the Vault.
73 // It creates a new temporary Vault that is used to hold
74 // the money that is being transferred. It returns the newly
75 // created Vault to the context that called so it can be deposited
76 // elsewhere.
77 //
78 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
79 self.balance = self.balance - amount
80
81 // If the owner is the staking account, do not emit the contract defined events
82 // this is to help with the performance of the epoch transition operations
83 // Either way, event listeners should be paying attention to the
84 // FungibleToken.Withdrawn events anyway because those contain
85 // much more comprehensive metadata
86 // Additionally, these events will eventually be removed from this contract completely
87 // in favor of the FungibleToken events
88 if let address = self.owner?.address {
89 if address != 0xf8d6e0586b0a20c7 &&
90 address != 0xf4527793ee68aede &&
91 address != 0x9eca2b38b18b5dfe &&
92 address != 0x8624b52f9ddcd04a
93 {
94 emit TokensWithdrawn(amount: amount, from: address)
95 }
96 } else {
97 emit TokensWithdrawn(amount: amount, from: nil)
98 }
99 return <-create Vault(balance: amount)
100 }
101
102 // deposit
103 //
104 // Function that takes a Vault object as an argument and adds
105 // its balance to the balance of the owners Vault.
106 // It is allowed to destroy the sent Vault because the Vault
107 // was a temporary holder of the tokens. The Vault's balance has
108 // been consumed and therefore can be destroyed.
109 access(all) fun deposit(from: @{FungibleToken.Vault}) {
110 let vault <- from as! @FlowToken.Vault
111 self.balance = self.balance + vault.balance
112
113 // If the owner is the staking account, do not emit the contract defined events
114 // this is to help with the performance of the epoch transition operations
115 // Either way, event listeners should be paying attention to the
116 // FungibleToken.Deposited events anyway because those contain
117 // much more comprehensive metadata
118 // Additionally, these events will eventually be removed from this contract completely
119 // in favor of the FungibleToken events
120 if let address = self.owner?.address {
121 if address != 0xf8d6e0586b0a20c7 &&
122 address != 0xf4527793ee68aede &&
123 address != 0x9eca2b38b18b5dfe &&
124 address != 0x8624b52f9ddcd04a
125 {
126 emit TokensDeposited(amount: vault.balance, to: address)
127 }
128 } else {
129 emit TokensDeposited(amount: vault.balance, to: nil)
130 }
131 vault.balance = 0.0
132 destroy vault
133 }
134
135 /// Get all the Metadata Views implemented by FlowToken
136 ///
137 /// @return An array of Types defining the implemented views. This value will be used by
138 /// developers to know which parameter to pass to the resolveView() method.
139 ///
140 access(all) view fun getViews(): [Type]{
141 return FlowToken.getContractViews(resourceType: nil)
142 }
143
144 /// Get a Metadata View from FlowToken
145 ///
146 /// @param view: The Type of the desired view.
147 /// @return A structure representing the requested view.
148 ///
149 access(all) fun resolveView(_ view: Type): AnyStruct? {
150 return FlowToken.resolveContractView(resourceType: nil, viewType: view)
151 }
152
153 access(all) fun createEmptyVault(): @{FungibleToken.Vault} {
154 return <-create Vault(balance: 0.0)
155 }
156 }
157
158 // createEmptyVault
159 //
160 // Function that creates a new Vault with a balance of zero
161 // and returns it to the calling context. A user must call this function
162 // and store the returned Vault in their storage in order to allow their
163 // account to be able to receive deposits of this token type.
164 //
165 access(all) fun createEmptyVault(vaultType: Type): @FlowToken.Vault {
166 return <-create Vault(balance: 0.0)
167 }
168
169 /// Gets a list of the metadata views that this contract supports
170 access(all) view fun getContractViews(resourceType: Type?): [Type] {
171 return [Type<FungibleTokenMetadataViews.FTView>(),
172 Type<FungibleTokenMetadataViews.FTDisplay>(),
173 Type<FungibleTokenMetadataViews.FTVaultData>(),
174 Type<FungibleTokenMetadataViews.TotalSupply>()]
175 }
176
177 /// Get a Metadata View from FlowToken
178 ///
179 /// @param view: The Type of the desired view.
180 /// @return A structure representing the requested view.
181 ///
182 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
183 switch viewType {
184 case Type<FungibleTokenMetadataViews.FTView>():
185 return FungibleTokenMetadataViews.FTView(
186 ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
187 ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
188 )
189 case Type<FungibleTokenMetadataViews.FTDisplay>():
190 let media = MetadataViews.Media(
191 file: MetadataViews.HTTPFile(
192 url: FlowToken.getLogoURI()
193 ),
194 mediaType: "image/svg+xml"
195 )
196 let medias = MetadataViews.Medias([media])
197 return FungibleTokenMetadataViews.FTDisplay(
198 name: "FLOW Network Token",
199 symbol: "FLOW",
200 description: "FLOW is the native token for the Flow blockchain. It is required for securing the network, transaction fees, storage fees, staking, FLIP voting and may be used by applications built on the Flow Blockchain",
201 externalURL: MetadataViews.ExternalURL("https://flow.com"),
202 logos: medias,
203 socials: {
204 "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
205 }
206 )
207 case Type<FungibleTokenMetadataViews.FTVaultData>():
208 let vaultRef = FlowToken.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
209 ?? panic("Could not borrow reference to the contract's Vault!")
210 return FungibleTokenMetadataViews.FTVaultData(
211 storagePath: /storage/flowTokenVault,
212 receiverPath: /public/flowTokenReceiver,
213 metadataPath: /public/flowTokenBalance,
214 receiverLinkedType: Type<&{FungibleToken.Receiver, FungibleToken.Vault}>(),
215 metadataLinkedType: Type<&{FungibleToken.Balance, FungibleToken.Vault}>(),
216 createEmptyVaultFunction: (fun (): @{FungibleToken.Vault} {
217 return <-vaultRef.createEmptyVault()
218 })
219 )
220 case Type<FungibleTokenMetadataViews.TotalSupply>():
221 return FungibleTokenMetadataViews.TotalSupply(totalSupply: FlowToken.totalSupply)
222 }
223 return nil
224 }
225
226 access(all) resource Administrator {
227 // createNewMinter
228 //
229 // Function that creates and returns a new minter resource
230 //
231 access(all) fun createNewMinter(allowedAmount: UFix64): @Minter {
232 emit MinterCreated(allowedAmount: allowedAmount)
233 return <-create Minter(allowedAmount: allowedAmount)
234 }
235 }
236
237 // Minter
238 //
239 // Resource object that token admin accounts can hold to mint new tokens.
240 //
241 access(all) resource Minter {
242
243 // the amount of tokens that the minter is allowed to mint
244 access(all) var allowedAmount: UFix64
245
246 // mintTokens
247 //
248 // Function that mints new tokens, adds them to the total supply,
249 // and returns them to the calling context.
250 //
251 access(all) fun mintTokens(amount: UFix64): @FlowToken.Vault {
252 pre {
253 amount > UFix64(0): "Amount minted must be greater than zero"
254 amount <= self.allowedAmount: "Amount minted must be less than the allowed amount"
255 }
256 FlowToken.totalSupply = FlowToken.totalSupply + amount
257 self.allowedAmount = self.allowedAmount - amount
258 emit TokensMinted(amount: amount)
259 return <-create Vault(balance: amount)
260 }
261
262 init(allowedAmount: UFix64) {
263 self.allowedAmount = allowedAmount
264 }
265 }
266
267 /// Gets the Flow Logo XML URI from storage
268 access(all) view fun getLogoURI(): String {
269 return FlowToken.account.storage.copy<String>(from: /storage/flowTokenLogoURI) ?? ""
270 }
271
272 init(adminAccount: auth(Storage, Capabilities) &Account) {
273 self.totalSupply = 0.0
274
275 // Create the Vault with the total supply of tokens and save it in storage
276 //
277 let vault <- create Vault(balance: self.totalSupply)
278
279 adminAccount.storage.save(<-vault, to: /storage/flowTokenVault)
280
281 // Create a public capability to the stored Vault that only exposes
282 // the `deposit` method through the `Receiver` interface
283 //
284 let receiverCapability = adminAccount.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault)
285 adminAccount.capabilities.publish(receiverCapability, at: /public/flowTokenReceiver)
286
287 // Create a public capability to the stored Vault that only exposes
288 // the `balance` field through the `Balance` interface
289 //
290 let balanceCapability = adminAccount.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault)
291 adminAccount.capabilities.publish(balanceCapability, at: /public/flowTokenBalance)
292
293 let admin <- create Administrator()
294 adminAccount.storage.save(<-admin, to: /storage/flowTokenAdmin)
295
296 }
297}
298