Smart Contract

MyToken

A.348fe2042c8a70d8.MyToken

Deployed

1w ago
Feb 20, 2026, 03:50:40 AM UTC

Dependents

4 imports
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