Smart Contract
ExampleToken
A.93c18b0282a6b82c.ExampleToken
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}