Smart Contract

PuffPalz

A.c4b1f4387748f389.PuffPalz

Deployed

1d ago
Feb 25, 2026, 04:49:20 AM UTC

Dependents

3 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import Toucans from 0x577a3c409c5dcb5e
5import ToucansTokens from 0x577a3c409c5dcb5e
6import FlowToken from 0x1654653399040a61
7import ViewResolver from 0x1d7e57aa55817448
8 
9access(all) contract PuffPalz: FungibleToken {
10
11    // The amount of tokens in existance
12    access(all) var totalSupply: UFix64
13    // nil if there is none
14    access(all) let maxSupply: UFix64?
15
16    // Paths
17    access(all) let VaultStoragePath: StoragePath
18    access(all) let ReceiverPublicPath: PublicPath
19    access(all) let VaultPublicPath: PublicPath
20    access(all) let MinterStoragePath: StoragePath
21    access(all) let AdministratorStoragePath: StoragePath
22
23    // Events
24    access(all) event TokensTransferred(amount: UFix64, from: Address, to: Address)
25    access(all) event TokensMinted(amount: UFix64)
26    access(all) event TokensBurned(amount: UFix64)
27
28    access(all) resource Vault: FungibleToken.Vault {
29        access(all) var balance: UFix64
30
31        /// Called when a fungible token is burned via the `Burner.burn()` method
32        access(contract) fun burnCallback() {
33            if self.balance > 0.0 {
34                emit TokensBurned(amount: self.balance)
35                PuffPalz.totalSupply = PuffPalz.totalSupply - self.balance
36            }
37            self.balance = 0.0
38        }
39
40        access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
41            self.balance = self.balance - amount
42
43            if let owner: Address = self.owner?.address {
44                PuffPalz.setBalance(address: owner, balance: self.balance)
45            }
46            return <- create Vault(balance: amount)
47        }
48
49        access(all) fun deposit(from: @{FungibleToken.Vault}) {
50            let vault: @Vault <- from as! @Vault
51            self.balance = self.balance + vault.balance
52            destroy vault
53
54            if let owner: Address = self.owner?.address {
55                PuffPalz.setBalance(address: owner, balance: self.balance)
56            }
57        }
58
59        access(all) view fun getViews(): [Type] {
60            return PuffPalz.getContractViews(resourceType: nil)
61        }
62
63        access(all) fun resolveView(_ view: Type): AnyStruct? {
64            return PuffPalz.resolveContractView(resourceType: nil, viewType: view)
65        }
66
67        access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
68            return amount <= self.balance
69        }
70
71        access(all) fun createEmptyVault(): @Vault {
72            return <- create Vault(balance: 0.0)
73        }
74  
75        init(balance: UFix64) {
76            self.balance = balance
77        }
78    }
79
80    access(all) fun createEmptyVault(vaultType: Type): @Vault {
81        return <- create Vault(balance: 0.0)
82    }
83
84    access(all) resource Minter: Toucans.Minter {
85        access(all) fun mint(amount: UFix64): @Vault {
86            post {
87                PuffPalz.maxSupply == nil || PuffPalz.totalSupply <= PuffPalz.maxSupply!: 
88                    "Exceeded the max supply of tokens allowd."
89            }
90            PuffPalz.totalSupply = PuffPalz.totalSupply + amount
91            emit TokensMinted(amount: amount)
92            return <- create Vault(balance: amount)
93        }
94    }
95
96    // We follow this pattern of storage
97    // so the (potentially) huge dictionary 
98    // isn't loaded when the contract is imported
99    access(all) resource Administrator {
100        // This is an experimental index and should
101        // not be used for anything official
102        // or monetary related
103        access(self) let balances: {Address: UFix64}
104
105        access(contract) fun setBalance(address: Address, balance: UFix64) {
106            self.balances[address] = balance
107        }
108
109        access(all) view fun getBalance(address: Address): UFix64 {
110            return self.balances[address] ?? 0.0
111        }
112
113        access(all) view fun getBalances(): {Address: UFix64} {
114            return self.balances
115        }
116
117        init() {
118            self.balances = {}
119        }
120    }
121
122    access(contract) fun setBalance(address: Address, balance: UFix64) {
123        let admin: &Administrator = self.account.storage.borrow<&Administrator>(from: self.AdministratorStoragePath)!
124        admin.setBalance(address: address, balance: balance)
125    }
126
127    access(all) view fun getBalance(address: Address): UFix64 {
128        let admin: &Administrator = self.account.storage.borrow<&Administrator>(from: self.AdministratorStoragePath)!
129        return admin.getBalance(address: address)
130    }
131
132    access(all) view fun getBalances(): {Address: UFix64} {
133        let admin: &Administrator = self.account.storage.borrow<&Administrator>(from: self.AdministratorStoragePath)!
134        return admin.getBalances()
135    }
136
137    access(all) view fun getContractViews(resourceType: Type?): [Type] {
138        return [
139            Type<FungibleTokenMetadataViews.FTView>(),
140            Type<FungibleTokenMetadataViews.FTDisplay>(),
141            Type<FungibleTokenMetadataViews.FTVaultData>(),
142            Type<FungibleTokenMetadataViews.TotalSupply>()
143        ]
144    }
145
146    access(all) view fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
147        switch viewType {
148            case Type<FungibleTokenMetadataViews.FTView>():
149                return FungibleTokenMetadataViews.FTView(
150                    ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
151                    ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
152                )
153            case Type<FungibleTokenMetadataViews.FTDisplay>():
154                let media = MetadataViews.Media(
155                        file: MetadataViews.HTTPFile(
156                        url: "https://nftstorage.link/ipfs/bafybeic24hbznfwsmknsci7kqmt6n2xzu4veui7lzke2bueidthbs5vmb4"
157                    ),
158                    mediaType: "image"
159                )
160                let bannerMedia = MetadataViews.Media(
161                        file: MetadataViews.HTTPFile(
162                        url: "https://nftstorage.link/ipfs/bafybeiedcpq2wblkqbfx3xhmk4uyp5zumvobnbhzfnl5rzjakedmeloe24"
163                    ),
164                    mediaType: "image"
165                )
166                let medias = MetadataViews.Medias([media, bannerMedia])
167                return FungibleTokenMetadataViews.FTDisplay(
168                    name: "Puff Palz",
169                    symbol: "MOKE",
170                    description: "Puff Palz DAO is for the holders/supporters of the Puff Palz NFT collection/brand.",
171                    externalURL: MetadataViews.ExternalURL("puffpalz.io"),
172                    logos: medias,
173                    socials: {
174                        "twitter": MetadataViews.ExternalURL("https://x.com/PuffPalz"),
175                        "discord": MetadataViews.ExternalURL("https://discord.gg/gB4fvMVDMW")
176                    }
177                )
178            case Type<FungibleTokenMetadataViews.FTVaultData>():
179                return FungibleTokenMetadataViews.FTVaultData(
180                    storagePath: PuffPalz.VaultStoragePath,
181                    receiverPath: PuffPalz.ReceiverPublicPath,
182                    metadataPath: PuffPalz.VaultPublicPath,
183                    receiverLinkedType: Type<&Vault>(),
184                    metadataLinkedType: Type<&Vault>(),
185                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
186                        return <-PuffPalz.createEmptyVault(vaultType: Type<@Vault>())
187                    })
188                )
189            case Type<FungibleTokenMetadataViews.TotalSupply>():
190                return FungibleTokenMetadataViews.TotalSupply(
191                    totalSupply: PuffPalz.totalSupply
192                )
193        }
194        return nil
195    }
196
197    init(
198      _paymentTokenInfo: ToucansTokens.TokenInfo,
199      _editDelay: UFix64,
200      _minting: Bool,
201      _initialTreasurySupply: UFix64,
202      _maxSupply: UFix64?,
203      _extra: {String: AnyStruct}
204    ) {
205
206      // Contract Variables
207      self.totalSupply = 0.0
208      self.maxSupply = _maxSupply
209
210      // Paths
211      self.VaultStoragePath = /storage/PuffPalzVault
212      self.ReceiverPublicPath = /public/PuffPalzReceiver
213      self.VaultPublicPath = /public/PuffPalzMetadata
214      self.MinterStoragePath = /storage/PuffPalzMinter
215      self.AdministratorStoragePath = /storage/PuffPalzAdmin
216 
217      // Admin Setup
218      let vault <- create Vault(balance: self.totalSupply)
219      self.account.storage.save(<- vault, to: self.VaultStoragePath)
220
221      let publicCap = self.account.capabilities.storage.issue<&Vault>(self.VaultStoragePath)
222      self.account.capabilities.publish(publicCap, at: self.VaultPublicPath)
223
224      let receiverCap = self.account.capabilities.storage.issue<&Vault>(self.VaultStoragePath)
225      self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
226
227      if self.account.storage.borrow<&Toucans.Collection>(from: Toucans.CollectionStoragePath) == nil {
228        self.account.storage.save(<- Toucans.createCollection(), to: Toucans.CollectionStoragePath)
229        let cap = self.account.capabilities.storage.issue<&Toucans.Collection>(Toucans.CollectionStoragePath)
230        self.account.capabilities.publish(cap, at: Toucans.CollectionPublicPath)
231      }
232
233      let toucansProjectCollection = self.account.storage.borrow<auth(Toucans.CollectionOwner) &Toucans.Collection>(from: Toucans.CollectionStoragePath)!
234      toucansProjectCollection.createProject(
235        projectTokenInfo: ToucansTokens.TokenInfo("PuffPalz", self.account.address, "MOKE", self.ReceiverPublicPath, self.VaultPublicPath, self.VaultStoragePath), 
236        paymentTokenInfo: _paymentTokenInfo, 
237        minter: <- create Minter(), 
238        editDelay: _editDelay,
239        minting: _minting,
240        initialTreasurySupply: _initialTreasurySupply,
241        extra: _extra
242      )
243
244      self.account.storage.save(<- create Administrator(), to: self.AdministratorStoragePath)
245    }
246}
247