Smart Contract

DapperUtilityCoin

A.ead892083b3e2c6c.DapperUtilityCoin

Deployed

2w ago
Feb 11, 2026, 05:25:39 PM UTC

Dependents

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