Smart Contract

Duckcoin

A.48ff88b4ccb47359.Duckcoin

Deployed

3d ago
Feb 25, 2026, 01:29:16 AM UTC

Dependents

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