Smart Contract
MyToken
A.348fe2042c8a70d8.MyToken
1import FungibleToken from 0xf233dcee88fe0abe
2
3pub contract MyToken: FungibleToken {
4
5 // Event that is emitted when the contract is created
6 pub event TokensInitialized(initialSupply: UFix64)
7
8 // Event that is emitted when tokens are withdrawn from a Vault
9 pub event TokensWithdrawn(amount: UFix64, from: Address?)
10
11 // Event that is emitted when tokens are deposited to a Vault
12 pub event TokensDeposited(amount: UFix64, to: Address?)
13
14 // Event that is emitted when new tokens are minted
15 pub event TokensMinted(amount: UFix64)
16 // The storage path for the admin resource
17 pub let AdminStoragePath: StoragePath
18
19 // The storage Path for minters' MinterProxy
20 pub let MinterProxyStoragePath: StoragePath
21
22 // The public path for minters' MinterProxy capability
23 pub let MinterProxyPublicPath: PublicPath
24
25 // Event that is emitted when a new minter resource is created
26 pub event MinterCreated()
27
28 // Total supply of My token in existence
29 pub var totalSupply: UFix64
30
31 // Vault
32 //
33 // Each user stores an instance of only the Vault in their storage
34 // The functions in the Vault are governed by the pre and post conditions
35 // in FungibleToken when they are called.
36 // The checks happen at runtime whenever a function is called.
37 //
38 // Resources can only be created in the context of the contract that they
39 // are defined in, so there is no way for a malicious user to create Vaults
40 // out of thin air. A special Minter resource needs to be defined to mint
41 // new tokens.
42 //
43 pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance {
44
45 // holds the balance of a users tokens
46 pub var balance: UFix64
47
48 // initialize the balance at resource creation time
49 init(balance: UFix64) {
50 self.balance = balance
51 }
52
53 // withdraw
54 //
55 // Function that takes an integer amount as an argument
56 // and withdraws that amount from the Vault.
57 // It creates a new temporary Vault that is used to hold
58 // the money that is being transferred. It returns the newly
59 // created Vault to the context that called so it can be deposited
60 // elsewhere.
61 //
62 pub fun withdraw(amount: UFix64): @FungibleToken.Vault {
63
64 self.balance = self.balance - amount
65 emit TokensWithdrawn(amount: amount, from: self.owner?.address)
66 return <-create Vault(balance: amount)
67 }
68
69 // deposit
70 //
71 // Function that takes a Vault object as an argument and adds
72 // its balance to the balance of the owners Vault.
73 // It is allowed to destroy the sent Vault because the Vault
74 // was a temporary holder of the tokens. The Vault's balance has
75 // been consumed and therefore can be destroyed.
76 pub fun deposit(from: @FungibleToken.Vault) {
77 let vault <- from as! @MyToken.Vault
78 self.balance = self.balance + vault.balance
79 emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
80 vault.balance = 0.0
81 destroy vault
82 }
83
84 destroy() {
85 MyToken.totalSupply = MyToken.totalSupply - self.balance
86 }
87 }
88
89 // createEmptyVault
90 //
91 // Function that creates a new Vault with a balance of zero
92 // and returns it to the calling context. A user must call this function
93 // and store the returned Vault in their storage in order to allow their
94 // account to be able to receive deposits of this token type.
95 //
96 pub fun createEmptyVault(): @MyToken.Vault {
97 return <-create Vault(balance: 0.0)
98 }
99
100 // Minter
101 //
102 // Resource object that can mint new tokens.
103 // The admin stores this and passes it to the minter account as a capability wrapper resource.
104 //
105 pub resource Minter {
106
107 // mintTokens
108 //
109 // Function that mints new tokens, adds them to the total supply,
110 // and returns them to the calling context.
111 //
112 pub fun mintTokens(amount: UFix64): @MyToken.Vault {
113 pre {
114 amount > 0.0: "Amount minted must be greater than zero"
115 }
116 MyToken.totalSupply = MyToken.totalSupply + amount
117 emit TokensMinted(amount: amount)
118 return <-create Vault(balance: amount)
119 }
120
121 }
122
123 pub resource interface MinterProxyPublic {
124 pub fun setMinterCapability(cap: Capability<&Minter>)
125 }
126
127 // MinterProxy
128 //
129 // Resource object holding a capability that can be used to mint new tokens.
130 // The resource that this capability represents can be deleted by the admin
131 // in order to unilaterally revoke minting capability if needed.
132
133 pub resource MinterProxy: MinterProxyPublic {
134
135 // access(self) so nobody else can copy the capability and use it.
136 access(self) var minterCapability: Capability<&Minter>?
137
138 // Anyone can call this, but only the admin can create Minter capabilities,
139 // so the type system constrains this to being called by the admin.
140 pub fun setMinterCapability(cap: Capability<&Minter>) {
141 self.minterCapability = cap
142 }
143
144 pub fun mintTokens(amount: UFix64): @MyToken.Vault {
145 return <- self.minterCapability!
146 .borrow()!
147 .mintTokens(amount:amount)
148 }
149
150 init() {
151 self.minterCapability = nil
152 }
153
154 }
155
156 // createMinterProxy
157 //
158 // Function that creates a MinterProxy.
159 // Anyone can call this, but the MinterProxy cannot mint without a Minter capability,
160 // and only the admin can provide that.
161 //
162 pub fun createMinterProxy(): @MinterProxy {
163 return <- create MinterProxy()
164 }
165
166 // Administrator
167 //
168 // A resource that allows new minters to be created
169 //
170 // We will only want one minter for now, but might need to add or replace them in future.
171 // The Minter/Minter Proxy structure enables this.
172 // Ideally we would create this structure in a single function, generate the paths from the address
173 // and cache all of this information to enable easy revocation but String/Path comversion isn't yet supported.
174 //
175 pub resource Administrator {
176
177 // createNewMinter
178 //
179 // Function that creates a Minter resource.
180 // This should be stored at a unique path in storage then a capability to it wrapped
181 // in a MinterProxy to be stored in a minter account's storage.
182 // This is done by the minter account running:
183 pub fun createNewMinter(): @Minter {
184 emit MinterCreated()
185 return <- create Minter()
186 }
187 }
188
189 init() {
190 self.AdminStoragePath = /storage/MyTokenAdmin
191 self.MinterProxyPublicPath = /public/MyTokenMinterProxy
192 self.MinterProxyStoragePath = /storage/MyTokenMinterProxy
193
194 self.totalSupply = 0.0
195
196 let admin <- create Administrator()
197 self.account.save(<-admin, to: self.AdminStoragePath)
198
199 // Emit an event that shows that the contract was initialized
200 emit TokensInitialized(initialSupply: 0.0)
201 }
202}
203