Smart Contract
NWayUtilityCoin
A.011b6f1425389550.NWayUtilityCoin
1import FungibleToken from 0xf233dcee88fe0abe
2import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4
5access(all)
6contract NWayUtilityCoin: FungibleToken {
7
8 // Total supply of NWayUtilityCoins in existence
9 access(all)
10 var totalSupply: UFix64
11
12
13 // Event that is emitted when tokens are withdrawn from a Vault
14 access(all)
15 event TokensWithdrawn(amount: UFix64, from: Address?)
16
17 // Event that is emitted when tokens are deposited to a Vault
18 access(all)
19 event TokensDeposited(amount: UFix64, to: Address?)
20
21 // Event that is emitted when new tokens are minted
22 access(all)
23 event TokensMinted(amount: UFix64)
24
25 // Event that is emitted when tokens are destroyed
26 access(all)
27 event TokensBurned(amount: UFix64)
28
29 // Event that is emitted when a new minter resource is created
30 access(all)
31 event MinterCreated(allowedAmount: UFix64)
32
33 // Event that is emitted when a new burner resource is created
34 access(all)
35 event BurnerCreated()
36
37 // Path where the `token Vault` is stored
38 access(all)
39 let VaultStoragePath: StoragePath
40
41 // Path where the `token Admin` is stored
42 access(all)
43 let AdminStoragePath: StoragePath
44
45 // Path where the public capability for the `token balance` is
46 access(all)
47 let TokenBalancePublicPath: PublicPath
48
49 // Path where the public capability for the `token receiver` is
50 access(all)
51 let TokenReceiverPublicPath: PublicPath
52
53 // Vault
54 //
55 // Each user stores an instance of only the Vault in their storage
56 // The functions in the Vault and governed by the pre and post conditions
57 // in FungibleToken when they are called.
58 // The checks happen at runtime whenever a function is called.
59 //
60 // Resources can only be created in the context of the contract that they
61 // are defined in, so there is no way for a malicious user to create Vaults
62 // out of thin air. A special Minter resource needs to be defined to mint
63 // new tokens.
64 //
65 access(all)
66 resource Vault: FungibleToken.Vault {
67 access(all) event ResourceDestroyed(id: UInt64 = self.uuid, balance: UFix64 = self.balance)
68
69 // holds the balance of a users tokens
70 access(all)
71 var balance: UFix64
72
73 // initialize the balance at resource creation time
74 init(balance: UFix64){
75 self.balance = balance
76 }
77
78 /// Called when a fungible token is burned via the `Burner.burn()` method
79 access(contract) fun burnCallback() {
80 if self.balance > 0.0 {
81 NWayUtilityCoin.totalSupply = NWayUtilityCoin.totalSupply - self.balance
82 }
83 self.balance = 0.0
84 }
85
86 /// Asks if the amount can be withdrawn from this vault
87 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
88 return amount <= self.balance
89 }
90
91 access(all) view fun getViews(): [Type] {
92 return NWayUtilityCoin.getContractViews(resourceType: nil)
93 }
94
95 access(all) fun resolveView(_ view: Type): AnyStruct? {
96 return NWayUtilityCoin.resolveContractView(resourceType: nil, viewType: view)
97 }
98
99 /// Get the balance of the vault
100 access(all) view fun getBalance(): UFix64 {
101 return self.balance
102 }
103
104 // withdraw
105 //
106 // Function that takes an integer amount as an argument
107 // and withdraws that amount from the Vault.
108 // It creates a new temporary Vault that is used to hold
109 // the money that is being transferred. It returns the newly
110 // created Vault to the context that called so it can be deposited
111 // elsewhere.
112 //
113 access(FungibleToken.Withdraw)
114 fun withdraw(amount: UFix64): @NWayUtilityCoin.Vault{
115 self.balance = self.balance - amount
116 emit TokensWithdrawn(amount: amount, from: self.owner?.address)
117 return <-create Vault(balance: amount)
118 }
119
120 // deposit
121 //
122 // Function that takes a Vault object as an argument and adds
123 // its balance to the balance of the owners Vault.
124 // It is allowed to destroy the sent Vault because the Vault
125 // was a temporary holder of the tokens. The Vault's balance has
126 // been consumed and therefore can be destroyed.
127 access(all)
128 fun deposit(from: @{FungibleToken.Vault}): Void{
129 let vault <- from as! @NWayUtilityCoin.Vault
130 self.balance = self.balance + vault.balance
131 emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
132 vault.balance = 0.0
133 destroy vault
134 }
135 /// Returns the storage path where the vault should typically be stored
136 access(all) view fun getDefaultStoragePath(): StoragePath? {
137 return /storage/NWayUtilityCoinVault
138 }
139
140 /// Returns the public path where this vault should have a public capability
141 access(all) view fun getDefaultPublicPath(): PublicPath? {
142 return /public/dapperUtilityCoinVault
143 }
144
145 /// Returns the public path where this vault's Receiver should have a public capability
146 access(all) view fun getDefaultReceiverPath(): PublicPath? {
147 return nil
148 }
149
150 access(all) fun createEmptyVault(): @NWayUtilityCoin.Vault {
151 return <-create Vault(balance: 0.0)
152 }
153 }
154
155 // createEmptyVault
156 //
157 // Function that creates a new Vault with a balance of zero
158 // and returns it to the calling context. A user must call this function
159 // and store the returned Vault in their storage in order to allow their
160 // account to be able to receive deposits of this token type.
161 //
162 access(all)
163 fun createEmptyVault(vaultType: Type): @NWayUtilityCoin.Vault{
164 return <-create Vault(balance: 0.0)
165 }
166
167 access(all)
168 resource Administrator{
169 // createNewMinter
170 //
171 // Function that creates and returns a new minter resource
172 //
173 access(all)
174 fun createNewMinter(allowedAmount: UFix64): @Minter{
175 emit MinterCreated(allowedAmount: allowedAmount)
176 return <-create Minter(allowedAmount: allowedAmount)
177 }
178
179 // createNewBurner
180 //
181 // Function that creates and returns a new burner resource
182 //
183 access(all)
184 fun createNewBurner(): @Burner{
185 emit BurnerCreated()
186 return <-create Burner()
187 }
188 }
189
190 // Minter
191 //
192 // Resource object that token admin accounts can hold to mint new tokens.
193 //
194 access(all)
195 resource Minter{
196
197 // the amount of tokens that the minter is allowed to mint
198 access(all)
199 var allowedAmount: UFix64
200
201 // mintTokens
202 //
203 // Function that mints new tokens, adds them to the total supply,
204 // and returns them to the calling context.
205 //
206 access(all)
207 fun mintTokens(amount: UFix64): @NWayUtilityCoin.Vault{
208 pre{
209 amount > UFix64(0):
210 "Amount minted must be greater than zero"
211 amount <= self.allowedAmount:
212 "Amount minted must be less than the allowed amount"
213 }
214 NWayUtilityCoin.totalSupply = NWayUtilityCoin.totalSupply + amount
215 self.allowedAmount = self.allowedAmount - amount
216 emit TokensMinted(amount: amount)
217 return <-create Vault(balance: amount)
218 }
219
220 init(allowedAmount: UFix64){
221 self.allowedAmount = allowedAmount
222 }
223 }
224
225 // Burner
226 //
227 // Resource object that token admin accounts can hold to burn tokens.
228 //
229 access(all)
230 resource Burner{
231
232 // burnTokens
233 //
234 // Function that destroys a Vault instance, effectively burning the tokens.
235 //
236 // Note: the burned tokens are automatically subtracted from the
237 // total supply in the Vault destructor.
238 //
239 access(all)
240 fun burnTokens(from: @NWayUtilityCoin.Vault){
241 let vault <- from as! @NWayUtilityCoin.Vault
242 let amount = vault.balance
243 vault.burnCallback()
244 destroy vault
245 emit TokensBurned(amount: amount)
246 }
247 }
248
249 access(all) view fun getContractViews(resourceType: Type?): [Type] {
250 return [
251 Type<FungibleTokenMetadataViews.FTVaultData>(),
252 Type<FungibleTokenMetadataViews.TotalSupply>()
253 ]
254 }
255
256 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
257 switch viewType {
258 case Type<FungibleTokenMetadataViews.FTVaultData>():
259 return FungibleTokenMetadataViews.FTVaultData(
260 storagePath: self.VaultStoragePath,
261 receiverPath: self.TokenReceiverPublicPath,
262 metadataPath: self.TokenBalancePublicPath,
263 receiverLinkedType: Type<&NWayUtilityCoin.Vault>(),
264 metadataLinkedType: Type<&NWayUtilityCoin.Vault>(),
265 createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
266 return <-NWayUtilityCoin.createEmptyVault(vaultType: Type<@NWayUtilityCoin.Vault>())
267 })
268 )
269 case Type<FungibleTokenMetadataViews.TotalSupply>():
270 return FungibleTokenMetadataViews.TotalSupply(
271 totalSupply: NWayUtilityCoin.totalSupply
272 )
273 }
274 return nil
275 }
276
277
278 init(){
279 self.VaultStoragePath = /storage/NWayUtilityCoinVault
280 self.AdminStoragePath = /storage/NWayUtilityCoinAdmin
281 self.TokenBalancePublicPath = /public/NWayUtilityCoinBalance
282 self.TokenReceiverPublicPath = /public/NWayUtilityCoinReceiver
283
284 // we're using a high value as the balance here to make it look like we've got a ton of money,
285 // just in case some contract manually checks that our balance is sufficient to pay for stuff
286 self.totalSupply = 0.0
287 let initialSupply = 999999999.0
288 let admin <- create Administrator()
289 let minter <- admin.createNewMinter(allowedAmount: initialSupply)
290 self.account.storage.save(<-admin, to: self.AdminStoragePath)
291
292 // mint tokens
293 let tokenVault <- minter.mintTokens(amount: initialSupply)
294 self.account.storage.save(<-tokenVault, to: self.VaultStoragePath)
295 destroy minter
296
297 let vaultCap = self.account.capabilities.storage.issue<&NWayUtilityCoin.Vault>(self.VaultStoragePath)
298 self.account.capabilities.publish(vaultCap, at: self.TokenBalancePublicPath)
299
300 let receiverCap = self.account.capabilities.storage.issue<&NWayUtilityCoin.Vault>(self.VaultStoragePath)
301 self.account.capabilities.publish(receiverCap, at: self.TokenReceiverPublicPath)
302 }
303}