Smart Contract
Beaver
A.687e1a7aef17b78b.Beaver
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