Smart Contract
EmptyPotionBottle
A.de7a5daf9df48c65.EmptyPotionBottle
1import FungibleToken from 0xf233dcee88fe0abe
2
3// Token contract of EmptyPotionBottle (EPB)
4access(all) contract EmptyPotionBottle: FungibleToken {
5
6 // -----------------------------------------------------------------------
7 // Entitlements
8 // -----------------------------------------------------------------------
9 access(all) entitlement AdminOwner
10 access(all) entitlement MinterProxyOwner
11
12 // -----------------------------------------------------------------------
13 // EmptyPotionBottle contract Events
14 // -----------------------------------------------------------------------
15
16 // Event that is emitted when the contract is created
17 access(all) event TokensInitialized(initialSupply: UFix64)
18
19 // Event that is emitted when tokens are withdrawn from a Vault
20 access(all) event TokensWithdrawn(amount: UFix64, from: Address?)
21
22 // Event that is emitted when tokens are deposited to a Vault
23 access(all) event TokensDeposited(amount: UFix64, to: Address?)
24
25 // Event that is emitted when new tokens are minted
26 access(all) event TokensMinted(amount: UFix64)
27
28 // Event that is emitted when tokens are destroyed
29 access(all) event TokensBurned(amount: UFix64)
30
31 // Event that is emitted when a new minter resource is created
32 access(all) event MinterCreated()
33
34 // Event that is emitted when a new burner resource is created
35 access(all) event BurnerCreated()
36
37 // Event that is emitted when a new MinterProxy resource is created
38 access(all) event MinterProxyCreated()
39
40 // -----------------------------------------------------------------------
41 // EmptyPotionBottle contract Named Paths
42 // -----------------------------------------------------------------------
43 // Defines EmptyPotionBottle vault storage path
44 access(all) let VaultStoragePath: StoragePath
45
46 // Defines EmptyPotionBottle vault public balance path
47 access(all) let BalancePublicPath: PublicPath
48
49 // Defines EmptyPotionBottle vault public receiver path
50 access(all) let ReceiverPublicPath: PublicPath
51
52 // Defines EmptyPotionBottle admin storage path
53 access(all) let AdminStoragePath: StoragePath
54
55 // Defines EmptyPotionBottle minter storage path
56 access(all) let MinterStoragePath: StoragePath
57
58 // Defines EmptyPotionBottle minters' MinterProxy storage path
59 access(all) let MinterProxyStoragePath: StoragePath
60
61 // Defines EmptyPotionBottle minters' MinterProxy capability public path
62 access(all) let MinterProxyPublicPath: PublicPath
63
64 // -----------------------------------------------------------------------
65 // EmptyPotionBottle contract fields
66 // These contain actual values that are stored in the smart contract
67 // -----------------------------------------------------------------------
68 // Total supply of EmptyPotionBottle in existence
69 access(all) var totalSupply: UFix64
70
71 // Vault
72 //
73 // Each user stores an instance of only the Vault in their storage
74 // The functions in the Vault are governed by the pre and post conditions
75 // in FungibleToken when they are called.
76 // The checks happen at runtime whenever a function is called.
77 //
78 // Resources can only be created in the context of the contract that they
79 // are defined in, so there is no way for a malicious user to create Vaults
80 // out of thin air. A special Minter resource needs to be defined to mint
81 // new tokens.
82 //
83 access(all) resource Vault: FungibleToken.Vault, FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance {
84
85 // holds the balance of a users tokens
86 access(all) var balance: UFix64
87
88 // initialize the balance at resource creation time
89 init(balance: UFix64) {
90 self.balance = balance
91 }
92
93 // withdraw
94 //
95 // Function that takes an integer amount as an argument
96 // and withdraws that amount from the Vault.
97 // It creates a new temporary Vault that is used to hold
98 // the money that is being transferred. It returns the newly
99 // created Vault to the context that called so it can be deposited
100 // elsewhere.
101 //
102 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
103 self.balance = self.balance - amount
104 emit TokensWithdrawn(amount: amount, from: self.owner?.address)
105 return <-create Vault(balance: amount)
106 }
107
108 // deposit
109 //
110 // Function that takes a Vault object as an argument and adds
111 // its balance to the balance of the owners Vault.
112 // It is allowed to destroy the sent Vault because the Vault
113 // was a temporary holder of the tokens. The Vault's balance has
114 // been consumed and therefore can be destroyed.
115 access(all) fun deposit(from: @{FungibleToken.Vault}): Void {
116 let vault <- from as! @EmptyPotionBottle.Vault
117 self.balance = self.balance + vault.balance
118 emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
119 vault.balance = 0.0
120 destroy vault
121 }
122
123 access(all) fun createEmptyVault(): @{FungibleToken.Vault} {
124 return <-create Vault(balance: 0.0)
125 }
126
127 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
128 return self.balance >= amount
129 }
130
131 access(all) view fun getViews(): [Type] {
132 return EmptyPotionBottle.getContractViews(resourceType: nil)
133 }
134
135 access(all) fun resolveView(_ view: Type): AnyStruct? {
136 return EmptyPotionBottle.resolveContractView(resourceType: nil, viewType: view)
137 }
138 }
139
140 // createEmptyVault
141 //
142 // Function that creates a new Vault with a balance of zero
143 // and returns it to the calling context. A user must call this function
144 // and store the returned Vault in their storage in order to allow their
145 // account to be able to receive deposits of this token type.
146 //
147 access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault} {
148 return <-create Vault(balance: 0.0)
149 }
150
151 // Administrator
152 //
153 // Resource object that token admin accounts can hold to create new minters and burners.
154 //
155 access(all) resource Administrator {
156 // createNewMinter
157 //
158 // Function that creates and returns a new minter resource
159 //
160 access(AdminOwner) fun createNewMinter(): @Minter {
161 emit MinterCreated()
162 return <-create Minter()
163 }
164
165 // createNewBurner
166 //
167 // Function that creates and returns a new burner resource
168 //
169 access(AdminOwner) fun createNewBurner(): @Burner {
170 emit BurnerCreated()
171 return <-create Burner()
172 }
173 }
174
175 // Minter
176 //
177 // Resource object that token admin accounts can hold to mint new tokens.
178 //
179 access(all) resource Minter {
180
181 // mintTokens
182 //
183 // Function that mints new tokens, adds them to the total supply,
184 // and returns them to the calling context.
185 //
186 access(all) fun mintTokens(amount: UFix64): @EmptyPotionBottle.Vault {
187 pre{
188 amount > 0.0:
189 "Amount minted must be greater than zero"
190 }
191 EmptyPotionBottle.totalSupply = EmptyPotionBottle.totalSupply + amount
192 emit TokensMinted(amount: amount)
193 return <-create Vault(balance: amount)
194 }
195 }
196
197 // Burner
198 //
199 // Resource object that token admin accounts can hold to burn tokens.
200 //
201 access(all) resource Burner {
202
203 // burnTokens
204 //
205 // Function that destroys a Vault instance, effectively burning the tokens.
206 //
207 // Note: the burned tokens are automatically subtracted from the
208 // total supply in the Vault destructor.
209 //
210 access(all) fun burnTokens(from: @{FungibleToken.Vault}) {
211 let vault <- from as! @EmptyPotionBottle.Vault
212 let amount = vault.balance
213 destroy vault
214 emit TokensBurned(amount: amount)
215 }
216 }
217
218 access(all) resource interface MinterProxyPublic {
219 access(all) fun setMinterCapability(cap: Capability<&EmptyPotionBottle.Minter>): Void
220 }
221
222 // MinterProxy
223 //
224 // Resource object holding a capability that can be used to mint new tokens.
225 // The resource that this capability represents can be deleted by the admin
226 // in order to unilaterally revoke minting capability if needed.
227 access(all) resource MinterProxy: MinterProxyPublic {
228
229 // access(self) so nobody else can copy the capability and use it.
230 access(self) var minterCapability: Capability<&Minter>?
231
232 // Anyone can call this, but only the admin can create Minter capabilities,
233 // so the type system constrains this to being called by the admin.
234 access(all) fun setMinterCapability(cap: Capability<&Minter>) {
235 self.minterCapability = cap
236 }
237
238 access(MinterProxyOwner) fun mintTokens(amount: UFix64): @EmptyPotionBottle.Vault {
239 return <-((self.minterCapability!).borrow()!).mintTokens(amount: amount)
240 }
241
242 init(){
243 self.minterCapability = nil
244 }
245 }
246
247 // createMinterProxy
248 //
249 // Function that creates a MinterProxy.
250 // Anyone can call this, but the MinterProxy cannot mint without a Minter capability,
251 // and only the admin can provide that.
252 //
253 access(all) fun createMinterProxy(): @MinterProxy {
254 emit MinterProxyCreated()
255 return <-create MinterProxy()
256 }
257
258 // -----------------------------------------------------------------------
259 // MetadataViews - Purposely Empty
260 // -----------------------------------------------------------------------
261 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
262 return nil
263 }
264
265 access(all) view fun getContractViews(resourceType: Type?): [Type] {
266 return []
267 }
268
269 init() {
270 self.VaultStoragePath = /storage/emptyPotionBottleVault
271 self.ReceiverPublicPath = /public/emptyPotionBottleReceiver
272 self.BalancePublicPath = /public/emptyPotionBottleBalance
273 self.AdminStoragePath = /storage/emptyPotionBottleAdmin
274 self.MinterStoragePath = /storage/emptyPotionBottleMinter
275 self.MinterProxyPublicPath = /public/emptyPotionBottleMinterProxy
276 self.MinterProxyStoragePath = /storage/emptyPotionBottleMinterProxy
277 self.totalSupply = 0.0
278
279 // Create the Vault with the total supply of tokens and save it in storage
280 let vault <- create Vault(balance: self.totalSupply)
281 self.account.storage.save(<-vault, to: self.VaultStoragePath)
282
283 // Create a public capability to the stored Vault that only exposes
284 // the `deposit` method through the `Receiver` interface
285 var capability_1 = self.account.capabilities.storage.issue<&EmptyPotionBottle.Vault>(self.VaultStoragePath)
286 self.account.capabilities.publish(capability_1, at: self.ReceiverPublicPath)
287
288 // Create a public capability to the stored Vault that only exposes
289 // the `balance` field through the `Balance` interface
290 var capability_2 = self.account.capabilities.storage.issue<&EmptyPotionBottle.Vault>(self.VaultStoragePath)
291 self.account.capabilities.publish(capability_2, at: self.BalancePublicPath)
292 let admin <- create Administrator()
293 self.account.storage.save(<-admin, to: self.AdminStoragePath)
294
295 // Emit an event that shows that the contract was initialized
296 emit TokensInitialized(initialSupply: self.totalSupply)
297 }
298}
299