Smart Contract
ArtDropToken
A.0e964e9e2b53ed06.ArtDropToken
1import FungibleToken from 0xf233dcee88fe0abe
2import MetadataViews from 0x1d7e57aa55817448
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5access(all) contract ArtDropToken: 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 ArtDropTokens 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: self.VaultStoragePath,
56 receiverPath: self.ReceiverPublicPath,
57 metadataPath: self.VaultPublicPath,
58 receiverLinkedType: Type<&ArtDropToken.Vault>(),
59 metadataLinkedType: Type<&ArtDropToken.Vault>(),
60 createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
61 return <-ArtDropToken.createEmptyVault(vaultType: Type<@ArtDropToken.Vault>())
62 })
63 )
64 case Type<FungibleTokenMetadataViews.TotalSupply>():
65 return FungibleTokenMetadataViews.TotalSupply(
66 totalSupply: ArtDropToken.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 ArtDropToken.totalSupply = ArtDropToken.totalSupply - self.balance
98 }
99 self.balance = 0.0
100 }
101
102 /// In fungible tokens, there are no specific views for specific vaults,
103 /// So we can route calls to view functions to the contract views functions
104 access(all) view fun getViews(): [Type] {
105 return ArtDropToken.getContractViews(resourceType: nil)
106 }
107
108 access(all) fun resolveView(_ view: Type): AnyStruct? {
109 return ArtDropToken.resolveContractView(resourceType: nil, viewType: view)
110 }
111
112 /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
113 access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
114 let supportedTypes: {Type: Bool} = {}
115 supportedTypes[self.getType()] = true
116 return supportedTypes
117 }
118
119 access(all) view fun isSupportedVaultType(type: Type): Bool {
120 return self.getSupportedVaultTypes()[type] ?? false
121 }
122
123 /// Asks if the amount can be withdrawn from this vault
124 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
125 return amount <= self.balance
126 }
127
128 /// withdraw
129 ///
130 /// Function that takes an amount as an argument
131 /// and withdraws that amount from the Vault.
132 ///
133 /// It creates a new temporary Vault that is used to hold
134 /// the tokens that are being transferred. It returns the newly
135 /// created Vault to the context that called so it can be deposited
136 /// elsewhere.
137 ///
138 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @ArtDropToken.Vault {
139 self.balance = self.balance - amount
140 return <-create Vault(balance: amount)
141 }
142
143 /// deposit
144 ///
145 /// Function that takes a Vault object as an argument and adds
146 /// its balance to the balance of the owners Vault.
147 ///
148 /// It is allowed to destroy the sent Vault because the Vault
149 /// was a temporary holder of the tokens. The Vault's balance has
150 /// been consumed and therefore can be destroyed.
151 ///
152 access(all) fun deposit(from: @{FungibleToken.Vault}) {
153 let vault <- from as! @ArtDropToken.Vault
154 self.balance = self.balance + vault.balance
155 destroy vault
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(): @ArtDropToken.Vault {
166 return <-create Vault(balance: 0.0)
167 }
168 }
169
170 /// Minter
171 ///
172 /// Resource object that token admin accounts can hold to mint new tokens.
173 ///
174 access(all) resource Minter {
175 /// mintTokens
176 ///
177 /// Function that mints new tokens, adds them to the total supply,
178 /// and returns them to the calling context.
179 ///
180 access(all) fun mintTokens(amount: UFix64): @ArtDropToken.Vault {
181 ArtDropToken.totalSupply = ArtDropToken.totalSupply + amount
182 let vault <-create Vault(balance: amount)
183 emit TokensMinted(amount: amount, type: vault.getType().identifier)
184 return <-vault
185 }
186 }
187
188 /// createEmptyVault
189 ///
190 /// Function that creates a new Vault with a balance of zero
191 /// and returns it to the calling context. A user must call this function
192 /// and store the returned Vault in their storage in order to allow their
193 /// account to be able to receive deposits of this token type.
194 ///
195 access(all) fun createEmptyVault(vaultType: Type): @ArtDropToken.Vault {
196 return <- create Vault(balance: 0.0)
197 }
198
199 init() {
200 self.totalSupply = 0.0
201
202 self.VaultStoragePath = /storage/ArtDropTokenVault
203 self.VaultPublicPath = /public/ArtDropTokenVault
204 self.ReceiverPublicPath = /public/ArtDropTokenReceiver
205 self.AdminStoragePath = /storage/ArtDropTokenAdmin
206
207 let admin <- create Minter()
208
209 // Create the Vault with the total supply of tokens and save it in storage
210 //
211 let vault <- admin.mintTokens(amount: 1000.0)
212
213 self.account.storage.save(<-vault, to: self.VaultStoragePath)
214
215 self.account.storage.save(<-admin, to: self.AdminStoragePath)
216
217 // Create a public capability to the stored Vault that exposes
218 // the `deposit` method and getAcceptedTypes method through the `Receiver` interface
219 // and the `balance` method through the `Balance` interface
220 //
221 let ArtDropTokenCap = self.account.capabilities.storage.issue<&ArtDropToken.Vault>(self.VaultStoragePath)
222 self.account.capabilities.publish(ArtDropTokenCap, at: self.VaultPublicPath)
223 let receiverCap = self.account.capabilities.storage.issue<&ArtDropToken.Vault>(self.VaultStoragePath)
224 self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
225 }
226}