Smart Contract

FlowToken

A.7e60df042a9c0868.FlowToken

Deployed

3h ago
Feb 26, 2026, 04:02:35 PM UTC

Dependents

0 imports
1import FungibleToken from 0x9a0766d93b6608b7
2import MetadataViews from 0x631e88ae7f1d7c20
3import FungibleTokenMetadataViews from 0x9a0766d93b6608b7
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