Smart Contract

ExampleToken

A.93c18b0282a6b82c.ExampleToken

Valid From

85,991,805

Deployed

3d ago
Feb 24, 2026, 11:58:31 PM UTC

Dependents

1 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import MetadataViews from 0x1d7e57aa55817448
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5access(all) contract ExampleToken: FungibleToken {
6
7    /// The event that is emitted when new tokens are minted
8    access(all) event TokensMinted(amount: UFix64, type: String)
9
10    /// Total supply of ExampleTokens in existence
11    access(all) var totalSupply: UFix64
12
13    /// Storage and Public Paths
14    access(all) let VaultStoragePath: StoragePath
15    access(all) let VaultPublicPath: PublicPath
16    access(all) let ReceiverPublicPath: PublicPath
17    access(all) let AdminStoragePath: StoragePath
18
19    access(all) view fun getContractViews(resourceType: Type?): [Type] {
20        return [
21            Type<FungibleTokenMetadataViews.FTView>(),
22            Type<FungibleTokenMetadataViews.FTDisplay>(),
23            Type<FungibleTokenMetadataViews.FTVaultData>(),
24            Type<FungibleTokenMetadataViews.TotalSupply>()
25        ]
26    }
27
28    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
29        switch viewType {
30            case Type<FungibleTokenMetadataViews.FTView>():
31                return FungibleTokenMetadataViews.FTView(
32                    ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
33                    ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
34                )
35            case Type<FungibleTokenMetadataViews.FTDisplay>():
36                let media = MetadataViews.Media(
37                        file: MetadataViews.HTTPFile(
38                        url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
39                    ),
40                    mediaType: "image/svg+xml"
41                )
42                let medias = MetadataViews.Medias([media])
43                return FungibleTokenMetadataViews.FTDisplay(
44                    name: "Example Fungible Token",
45                    symbol: "EFT",
46                    description: "This fungible token is used as an example to help you develop your next FT #onFlow.",
47                    externalURL: MetadataViews.ExternalURL("https://example-ft.onflow.org"),
48                    logos: medias,
49                    socials: {
50                        "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
51                    }
52                )
53            case Type<FungibleTokenMetadataViews.FTVaultData>():
54                return FungibleTokenMetadataViews.FTVaultData(
55                    storagePath: /storage/exampleTokenVault,
56                    receiverPath: /public/exampleTokenReceiver,
57                    metadataPath: /public/exampleTokenVault,
58                    receiverLinkedType: Type<&ExampleToken.Vault>(),
59                    metadataLinkedType: Type<&ExampleToken.Vault>(),
60                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
61                        return <-ExampleToken.createEmptyVault(vaultType: Type<@ExampleToken.Vault>())
62                    })
63                )
64            case Type<FungibleTokenMetadataViews.TotalSupply>():
65                return FungibleTokenMetadataViews.TotalSupply(
66                    totalSupply: ExampleToken.totalSupply
67                )
68        }
69        return nil
70    }
71
72    /// Vault
73    ///
74    /// Each user stores an instance of only the Vault in their storage
75    /// The functions in the Vault and governed by the pre and post conditions
76    /// in FungibleToken when they are called.
77    /// The checks happen at runtime whenever a function is called.
78    ///
79    /// Resources can only be created in the context of the contract that they
80    /// are defined in, so there is no way for a malicious user to create Vaults
81    /// out of thin air. A special Minter resource needs to be defined to mint
82    /// new tokens.
83    ///
84    access(all) resource Vault: FungibleToken.Vault {
85
86        /// The total balance of this vault
87        access(all) var balance: UFix64
88
89        // initialize the balance at resource creation time
90        init(balance: UFix64) {
91            self.balance = balance
92        }
93
94        /// Called when a fungible token is burned via the `Burner.burn()` method
95        access(contract) fun burnCallback() {
96            if self.balance > 0.0 {
97                ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance
98            }
99            self.balance = 0.0
100        }
101
102        access(all) view fun getViews(): [Type] {
103            return ExampleToken.getContractViews(resourceType: nil)
104        }
105
106        access(all) fun resolveView(_ view: Type): AnyStruct? {
107            return ExampleToken.resolveContractView(resourceType: nil, viewType: view)
108        }
109
110        /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
111        access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
112            let supportedTypes: {Type: Bool} = {}
113            supportedTypes[self.getType()] = true
114            return supportedTypes
115        }
116
117        access(all) view fun isSupportedVaultType(type: Type): Bool {
118            return self.getSupportedVaultTypes()[type] ?? false
119        }
120
121        /// Asks if the amount can be withdrawn from this vault
122        access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
123            return amount <= self.balance
124        }
125
126        /// withdraw
127        ///
128        /// Function that takes an amount as an argument
129        /// and withdraws that amount from the Vault.
130        ///
131        /// It creates a new temporary Vault that is used to hold
132        /// the tokens that are being transferred. It returns the newly
133        /// created Vault to the context that called so it can be deposited
134        /// elsewhere.
135        ///
136        access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @ExampleToken.Vault {
137            self.balance = self.balance - amount
138            return <-create Vault(balance: amount)
139        }
140
141        /// deposit
142        ///
143        /// Function that takes a Vault object as an argument and adds
144        /// its balance to the balance of the owners Vault.
145        ///
146        /// It is allowed to destroy the sent Vault because the Vault
147        /// was a temporary holder of the tokens. The Vault's balance has
148        /// been consumed and therefore can be destroyed.
149        ///
150        access(all) fun deposit(from: @{FungibleToken.Vault}) {
151            let vault <- from as! @ExampleToken.Vault
152            self.balance = self.balance + vault.balance
153            vault.balance = 0.0
154            destroy vault
155        }
156
157        /// createEmptyVault
158        ///
159        /// Function that creates a new Vault with a balance of zero
160        /// and returns it to the calling context. A user must call this function
161        /// and store the returned Vault in their storage in order to allow their
162        /// account to be able to receive deposits of this token type.
163        ///
164        access(all) fun createEmptyVault(): @ExampleToken.Vault {
165            return <-create Vault(balance: 0.0)
166        }
167    }
168
169    /// Minter
170    ///
171    /// Resource object that token admin accounts can hold to mint new tokens.
172    ///
173    access(all) resource Minter {
174        /// mintTokens
175        ///
176        /// Function that mints new tokens, adds them to the total supply,
177        /// and returns them to the calling context.
178        ///
179        access(all) fun mintTokens(amount: UFix64): @ExampleToken.Vault {
180            ExampleToken.totalSupply = ExampleToken.totalSupply + amount
181            emit TokensMinted(amount: amount, type: self.getType().identifier)
182            return <-create Vault(balance: amount)
183        }
184    }
185
186    /// createEmptyVault
187    ///
188    /// Function that creates a new Vault with a balance of zero
189    /// and returns it to the calling context. A user must call this function
190    /// and store the returned Vault in their storage in order to allow their
191    /// account to be able to receive deposits of this token type.
192    ///
193    access(all) fun createEmptyVault(vaultType: Type): @ExampleToken.Vault {
194        return <- create Vault(balance: 0.0)
195    }
196
197    init() {
198        self.totalSupply = 1000.0
199
200        self.VaultStoragePath = /storage/exampleTokenVault
201        self.VaultPublicPath = /public/exampleTokenVault
202        self.ReceiverPublicPath = /public/exampleTokenReceiver
203        self.AdminStoragePath = /storage/exampleTokenAdmin 
204
205        // Create the Vault with the total supply of tokens and save it in storage
206        //
207        let vault <- create Vault(balance: self.totalSupply)
208
209        // Create a public capability to the stored Vault that exposes
210        // the `deposit` method and getAcceptedTypes method through the `Receiver` interface
211        // and the `balance` method through the `Balance` interface
212        //
213        let exampleTokenCap = self.account.capabilities.storage.issue<&ExampleToken.Vault>(self.VaultStoragePath)
214        self.account.capabilities.publish(exampleTokenCap, at: self.VaultPublicPath)
215        let receiverCap = self.account.capabilities.storage.issue<&ExampleToken.Vault>(self.VaultStoragePath)
216        self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
217
218        self.account.storage.save(<-vault, to: /storage/exampleTokenVault)
219
220        let admin <- create Minter()
221        self.account.storage.save(<-admin, to: self.AdminStoragePath)
222    }
223}