Smart Contract

Beaver

A.687e1a7aef17b78b.Beaver

Deployed

1d ago
Feb 25, 2026, 12:06:31 AM UTC

Dependents

135 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 Beaver: 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                Beaver.totalSupply = Beaver.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                Beaver.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                Beaver.setBalance(address: owner, balance: self.balance)
56            }
57        }
58
59        access(all) view fun getViews(): [Type] {
60            return Beaver.getContractViews(resourceType: nil)
61        }
62
63        access(all) fun resolveView(_ view: Type): AnyStruct? {
64            return Beaver.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                Beaver.maxSupply == nil || Beaver.totalSupply <= Beaver.maxSupply!: 
88                    "Exceeded the max supply of tokens allowd."
89            }
90            Beaver.totalSupply = Beaver.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://i.imgur.com/pIwQW2b.png"
157                    ),
158                    mediaType: "image"
159                )
160                let bannerMedia = MetadataViews.Media(
161                        file: MetadataViews.HTTPFile(
162                        url: "https://nftstorage.link/ipfs/bafkreihl2ltp45od7ngyafd6qx6at2452ccee3vdtduvw5vlgxpxsnmlpu"
163                    ),
164                    mediaType: "image"
165                )
166                let medias = MetadataViews.Medias([media, bannerMedia])
167                return FungibleTokenMetadataViews.FTDisplay(
168                    name: "Beaver",
169                    symbol: "PUSS",
170                    description: "Hey, nice beaverTelegram: @beavercoinflow",
171                    externalURL: MetadataViews.ExternalURL("beavercoin.io"),
172                    logos: medias,
173                    socials: {
174                        "twitter": MetadataViews.ExternalURL("beavercoinflow"),
175                        "discord": MetadataViews.ExternalURL("")
176                    }
177                )
178            case Type<FungibleTokenMetadataViews.FTVaultData>():
179                return FungibleTokenMetadataViews.FTVaultData(
180                    storagePath: Beaver.VaultStoragePath,
181                    receiverPath: Beaver.ReceiverPublicPath,
182                    metadataPath: Beaver.VaultPublicPath,
183                    receiverLinkedType: Type<&Vault>(),
184                    metadataLinkedType: Type<&Vault>(),
185                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
186                        return <-Beaver.createEmptyVault(vaultType: Type<@Vault>())
187                    })
188                )
189            case Type<FungibleTokenMetadataViews.TotalSupply>():
190                return FungibleTokenMetadataViews.TotalSupply(
191                    totalSupply: Beaver.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/BeaverVault
212      self.ReceiverPublicPath = /public/BeaverReceiver
213      self.VaultPublicPath = /public/BeaverMetadata
214      self.MinterStoragePath = /storage/BeaverMinter
215      self.AdministratorStoragePath = /storage/BeaverAdmin
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("Beaver", self.account.address, "PUSS", 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