Smart Contract
Duckcoin
A.48ff88b4ccb47359.Duckcoin
1import FungibleToken from 0xf233dcee88fe0abe
2import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import Toucans from 0x577a3c409c5dcb5e
5import ToucansTokens from 0x577a3c409c5dcb5e
6
7pub contract Duckcoin: FungibleToken {
8
9 // The amount of tokens in existance
10 pub var totalSupply: UFix64
11 // nil if there is none
12 pub let maxSupply: UFix64?
13
14 // Paths
15 pub let VaultStoragePath: StoragePath
16 pub let ReceiverPublicPath: PublicPath
17 pub let VaultPublicPath: PublicPath
18 pub let MinterStoragePath: StoragePath
19 pub let AdministratorStoragePath: StoragePath
20
21 // Events
22 pub event TokensInitialized(initialSupply: UFix64)
23 pub event TokensWithdrawn(amount: UFix64, from: Address?)
24 pub event TokensDeposited(amount: UFix64, to: Address?)
25 pub event TokensTransferred(amount: UFix64, from: Address, to: Address)
26 pub event TokensMinted(amount: UFix64)
27 pub event TokensBurned(amount: UFix64)
28
29 pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance, MetadataViews.Resolver {
30 pub var balance: UFix64
31
32 pub fun withdraw(amount: UFix64): @FungibleToken.Vault {
33 self.balance = self.balance - amount
34 emit TokensWithdrawn(amount: amount, from: self.owner?.address)
35
36 if let owner: Address = self.owner?.address {
37 Duckcoin.setBalance(address: owner, balance: self.balance)
38 }
39 return <- create Vault(balance: amount)
40 }
41
42 pub fun deposit(from: @FungibleToken.Vault) {
43 let vault: @Vault <- from as! @Vault
44 self.balance = self.balance + vault.balance
45 emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
46
47 // We set the balance to 0.0 here so that it doesn't
48 // decrease the totalSupply in the `destroy` function.
49 vault.balance = 0.0
50 destroy vault
51
52 if let owner: Address = self.owner?.address {
53 Duckcoin.setBalance(address: owner, balance: self.balance)
54 }
55 }
56
57 pub fun getViews(): [Type]{
58 return [Type<FungibleTokenMetadataViews.FTView>(),
59 Type<FungibleTokenMetadataViews.FTDisplay>(),
60 Type<FungibleTokenMetadataViews.FTVaultData>()]
61 }
62
63 pub fun resolveView(_ view: Type): AnyStruct? {
64 switch view {
65 case Type<FungibleTokenMetadataViews.FTView>():
66 return FungibleTokenMetadataViews.FTView(
67 ftDisplay: self.resolveView(Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
68 ftVaultData: self.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
69 )
70 case Type<FungibleTokenMetadataViews.FTDisplay>():
71 let media = MetadataViews.Media(
72 file: MetadataViews.HTTPFile(
73 url: "https://nftstorage.link/ipfs/bafkreicxpcbvwiwhlksi6gjwif6bivg67ti53lcjv3ozyo3h6iyul5otay"
74 ),
75 mediaType: "image"
76 )
77 let bannerMedia = MetadataViews.Media(
78 file: MetadataViews.HTTPFile(
79 url: "https://nftstorage.link/ipfs/bafkreig374egrfhgxvqabvgnu2gadm6nvgtzjsxda44zwis5zf4siyxw4u"
80 ),
81 mediaType: "image"
82 )
83 let medias = MetadataViews.Medias([media, bannerMedia])
84 return FungibleTokenMetadataViews.FTDisplay(
85 name: "Duck coin",
86 symbol: "DUCK",
87 description: "$Duck meme coin will unify people and start the next big Flow wave, from private company chain to people blockchain, one duck at a time.",
88 externalURL: MetadataViews.ExternalURL("www.duck-coin.vip/"),
89 logos: medias,
90 socials: {
91 "twitter": MetadataViews.ExternalURL("DuckCoin_flow"),
92 "discord": MetadataViews.ExternalURL("")
93 }
94 )
95 case Type<FungibleTokenMetadataViews.FTVaultData>():
96 return FungibleTokenMetadataViews.FTVaultData(
97 storagePath: Duckcoin.VaultStoragePath,
98 receiverPath: Duckcoin.ReceiverPublicPath,
99 metadataPath: Duckcoin.VaultPublicPath,
100 providerPath: /private/DuckcoinVault,
101 receiverLinkedType: Type<&Vault{FungibleToken.Receiver}>(),
102 metadataLinkedType: Type<&Vault{FungibleToken.Balance, MetadataViews.Resolver}>(),
103 providerLinkedType: Type<&Vault{FungibleToken.Provider}>(),
104 createEmptyVaultFunction: (fun (): @Vault {
105 return <- Duckcoin.createEmptyVault()
106 })
107 )
108 }
109 return nil
110 }
111
112 init(balance: UFix64) {
113 self.balance = balance
114 }
115
116 destroy() {
117 emit TokensBurned(amount: self.balance)
118 Duckcoin.totalSupply = Duckcoin.totalSupply - self.balance
119 }
120 }
121
122 pub fun createEmptyVault(): @Vault {
123 return <- create Vault(balance: 0.0)
124 }
125
126 pub resource Minter: Toucans.Minter {
127 pub fun mint(amount: UFix64): @Vault {
128 post {
129 Duckcoin.maxSupply == nil || Duckcoin.totalSupply <= Duckcoin.maxSupply!:
130 "Exceeded the max supply of tokens allowd."
131 }
132 Duckcoin.totalSupply = Duckcoin.totalSupply + amount
133 emit TokensMinted(amount: amount)
134 return <- create Vault(balance: amount)
135 }
136 }
137
138 // We follow this pattern of storage
139 // so the (potentially) huge dictionary
140 // isn't loaded when the contract is imported
141 pub resource Administrator {
142 // This is an experimental index and should
143 // not be used for anything official
144 // or monetary related
145 access(self) let balances: {Address: UFix64}
146
147 access(contract) fun setBalance(address: Address, balance: UFix64) {
148 self.balances[address] = balance
149 }
150
151 pub fun getBalance(address: Address): UFix64 {
152 return self.balances[address] ?? 0.0
153 }
154
155 pub fun getBalances(): {Address: UFix64} {
156 return self.balances
157 }
158
159 init() {
160 self.balances = {}
161 }
162 }
163
164 access(contract) fun setBalance(address: Address, balance: UFix64) {
165 let admin: &Administrator = self.account.borrow<&Administrator>(from: self.AdministratorStoragePath)!
166 admin.setBalance(address: address, balance: balance)
167 }
168
169 pub fun getBalance(address: Address): UFix64 {
170 let admin: &Administrator = self.account.borrow<&Administrator>(from: self.AdministratorStoragePath)!
171 return admin.getBalance(address: address)
172 }
173
174 pub fun getBalances(): {Address: UFix64} {
175 let admin: &Administrator = self.account.borrow<&Administrator>(from: self.AdministratorStoragePath)!
176 return admin.getBalances()
177 }
178
179 init(
180 _paymentTokenInfo: ToucansTokens.TokenInfo,
181 _editDelay: UFix64,
182 _minting: Bool,
183 _initialTreasurySupply: UFix64,
184 _maxSupply: UFix64?,
185 _extra: {String: AnyStruct}
186 ) {
187
188 // Contract Variables
189 self.totalSupply = 0.0
190 self.maxSupply = _maxSupply
191
192 // Paths
193 self.VaultStoragePath = /storage/DuckcoinVault
194 self.ReceiverPublicPath = /public/DuckcoinReceiver
195 self.VaultPublicPath = /public/DuckcoinMetadata
196 self.MinterStoragePath = /storage/DuckcoinMinter
197 self.AdministratorStoragePath = /storage/DuckcoinAdmin
198
199 // Admin Setup
200 let vault <- create Vault(balance: self.totalSupply)
201 self.account.save(<- vault, to: self.VaultStoragePath)
202
203 self.account.link<&Vault{FungibleToken.Receiver}>(
204 self.ReceiverPublicPath,
205 target: self.VaultStoragePath
206 )
207
208 self.account.link<&Vault{FungibleToken.Balance, MetadataViews.Resolver}>(
209 self.VaultPublicPath,
210 target: self.VaultStoragePath
211 )
212
213 if self.account.borrow<&Toucans.Collection>(from: Toucans.CollectionStoragePath) == nil {
214 self.account.save(<- Toucans.createCollection(), to: Toucans.CollectionStoragePath)
215 self.account.link<&Toucans.Collection{Toucans.CollectionPublic}>(Toucans.CollectionPublicPath, target: Toucans.CollectionStoragePath)
216 }
217
218 let toucansProjectCollection = self.account.borrow<&Toucans.Collection>(from: Toucans.CollectionStoragePath)!
219 toucansProjectCollection.createProject(
220 projectTokenInfo: ToucansTokens.TokenInfo("Duckcoin", self.account.address, "DUCK", self.ReceiverPublicPath, self.VaultPublicPath, self.VaultStoragePath),
221 paymentTokenInfo: _paymentTokenInfo,
222 minter: <- create Minter(),
223 editDelay: _editDelay,
224 minting: _minting,
225 initialTreasurySupply: _initialTreasurySupply,
226 extra: _extra
227 )
228
229 self.account.save(<- create Administrator(), to: self.AdministratorStoragePath)
230
231 // Events
232 emit TokensInitialized(initialSupply: self.totalSupply)
233 }
234}
235