Smart Contract
KARATZ1XR82W
A.215f3ac76cb49656.KARATZ1XR82W
1import MetadataViews from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5access(all) contract KARATZ1XR82W: FungibleToken {
6 // TokensInitialized
7 //
8 // The event that is emitted when the contract is created
9 access(all) event TokensInitialized(initialSupply: UFix64)
10
11 // TokensWithdrawn
12 //
13 // The event that is emitted when tokens are withdrawn from a Vault
14 access(all) event TokensWithdrawn(amount: UFix64, from: Address?)
15
16 // TokensDeposited
17 //
18 // The event that is emitted when tokens are deposited to a Vault
19 access(all) event TokensDeposited(amount: UFix64, to: Address?)
20
21 // TokensBurned
22 //
23 // The event that is emitted when tokens are destroyed
24 access(all) event TokensBurned(amount: UFix64, burner: Address, metadata: {String: String}?)
25
26 /// The event that is emitted when new tokens are minted
27 access(all) event TokensMinted(amount: UFix64, type: String, metadata: {String: String}?)
28
29 /// Total supply of KARATZ1XR82W tokens in existence
30 access(all) var totalSupply: UFix64
31
32 /// Maximum supply cap that can be updated
33 access(all) var maxSupply: UFix64
34
35 /// Initial token supply (S0)
36 access(all) var initialSupply: UFix64
37
38 /// Initial price (P0)
39 access(all) var initialPrice: UFix64
40
41 /// Reserve ratio (R) used for Bancor pricing
42 /// Typical values: 0.1 (10%) - more volatile, 0.5 (50%) - balanced, 0.9 (90%) - stable
43 access(all) var reserveRatio: UFix64
44
45 /// Storage and Public Paths
46 access(all) let VaultStoragePath: StoragePath
47 access(all) let BalancePublicPath: PublicPath
48 access(all) let ReceiverPublicPath: PublicPath
49 access(all) let AdminStoragePath: StoragePath
50
51 access(all) view fun getContractViews(resourceType: Type?): [Type] {
52 return [
53 Type<FungibleTokenMetadataViews.FTView>(),
54 Type<FungibleTokenMetadataViews.FTDisplay>(),
55 Type<FungibleTokenMetadataViews.FTVaultData>(),
56 Type<FungibleTokenMetadataViews.TotalSupply>()
57 ]
58 }
59
60 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
61 switch viewType {
62 case Type<FungibleTokenMetadataViews.FTView>():
63 return FungibleTokenMetadataViews.FTView(
64 ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
65 ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
66 )
67 case Type<FungibleTokenMetadataViews.FTDisplay>():
68 let media = MetadataViews.Media(
69 file: MetadataViews.HTTPFile(
70 url: "https://apps.24karat.io/tokens/KARATZ1XR82W/thumbnail.png"
71 ),
72 mediaType: "image/png"
73 )
74 return FungibleTokenMetadataViews.FTDisplay(
75 name: "GKT",
76 symbol: "KARATZ1XR82W",
77 description: "GKT",
78 externalURL: MetadataViews.ExternalURL("https://24karat.io"),
79 logos: MetadataViews.Medias([media]),
80 socials: {}
81 )
82 case Type<FungibleTokenMetadataViews.FTVaultData>():
83 return FungibleTokenMetadataViews.FTVaultData(
84 storagePath: self.VaultStoragePath,
85 receiverPath: self.ReceiverPublicPath,
86 metadataPath: self.BalancePublicPath,
87 receiverLinkedType: Type<&KARATZ1XR82W.Vault>(),
88 metadataLinkedType: Type<&KARATZ1XR82W.Vault>(),
89 createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
90 return <-KARATZ1XR82W.createEmptyVault(vaultType: Type<@KARATZ1XR82W.Vault>())
91 })
92 )
93 case Type<FungibleTokenMetadataViews.TotalSupply>():
94 return FungibleTokenMetadataViews.TotalSupply(
95 totalSupply: KARATZ1XR82W.totalSupply
96 )
97 }
98 return nil
99 }
100
101 /// Vault
102 ///
103 /// Each user stores an instance of only the Vault in their storage
104 /// The functions in the Vault and governed by the pre and post conditions
105 /// in FungibleToken when they are called.
106 /// The checks happen at runtime whenever a function is called.
107 ///
108 /// Resources can only be created in the context of the contract that they
109 /// are defined in, so there is no way for a malicious user to create Vaults
110 /// out of thin air. A special Minter resource needs to be defined to mint
111 /// new tokens.
112 ///
113 access(all) resource Vault: FungibleToken.Vault {
114
115 /// The total balance of this vault
116 access(all) var balance: UFix64
117
118 // initialize the balance at resource creation time
119 init(balance: UFix64) {
120 self.balance = balance
121 }
122
123 /// Called when a fungible token is burned via the `Burner.burn()` method
124 access(contract) fun burnCallback() {
125 if self.balance > 0.0 {
126 KARATZ1XR82W.totalSupply = KARATZ1XR82W.totalSupply - self.balance
127 }
128 self.balance = 0.0
129 }
130
131 access(all) view fun getViews(): [Type] {
132 return KARATZ1XR82W.getContractViews(resourceType: nil)
133 }
134
135 access(all) fun resolveView(_ view: Type): AnyStruct? {
136 return KARATZ1XR82W.resolveContractView(resourceType: nil, viewType: view)
137 }
138
139 /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
140 access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
141 let supportedTypes: {Type: Bool} = {}
142 supportedTypes[self.getType()] = true
143 return supportedTypes
144 }
145
146 access(all) view fun isSupportedVaultType(type: Type): Bool {
147 return self.getSupportedVaultTypes()[type] ?? false
148 }
149
150 /// Asks if the amount can be withdrawn from this vault
151 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
152 return amount <= self.balance
153 }
154
155 /// withdraw
156 ///
157 /// Function that takes an amount as an argument
158 /// and withdraws that amount from the Vault.
159 ///
160 /// It creates a new temporary Vault that is used to hold
161 /// the tokens that are being transferred. It returns the newly
162 /// created Vault to the context that called so it can be deposited
163 /// elsewhere.
164 ///
165 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @KARATZ1XR82W.Vault {
166 self.balance = self.balance - amount
167 return <-create Vault(balance: amount)
168 }
169
170 /// deposit
171 ///
172 /// Function that takes a Vault object as an argument and adds
173 /// its balance to the balance of the owners Vault.
174 ///
175 /// It is allowed to destroy the sent Vault because the Vault
176 /// was a temporary holder of the tokens. The Vault's balance has
177 /// been consumed and therefore can be destroyed.
178 ///
179 access(all) fun deposit(from: @{FungibleToken.Vault}) {
180 let vault <- from as! @KARATZ1XR82W.Vault
181 self.balance = self.balance + vault.balance
182 vault.balance = 0.0
183 destroy vault
184 }
185
186 /// createEmptyVault
187 ///
188 /// Function that creates a new Vault with a balance of zero
189 /// and returns it to the calling context. A user must call this function
190 /// and store the returned Vault in their storage in order to allow their
191 /// account to be able to receive deposits of this token type.
192 ///
193 access(all) fun createEmptyVault(): @KARATZ1XR82W.Vault {
194 return <-create Vault(balance: 0.0)
195 }
196 }
197
198 /// createEmptyVault
199 ///
200 /// Function that creates a new Vault with a balance of zero
201 /// and returns it to the calling context. A user must call this function
202 /// and store the returned Vault in their storage in order to allow their
203 /// account to be able to receive deposits of this token type.
204 ///
205 access(all) fun createEmptyVault(vaultType: Type): @KARATZ1XR82W.Vault {
206 return <- create Vault(balance: 0.0)
207 }
208
209 access(all) resource Administrator {
210 // createNewMinter
211 //
212 // Function that creates and returns a new minter resource
213 //
214 access(all) fun createNewMinter(): @Minter {
215 return <-create Minter()
216 }
217
218 /// Setup pricing parameters (only admin can call)
219 access(all) fun setupPricing(
220 maxSupply: UFix64,
221 initialSupply: UFix64,
222 initialPrice: UFix64,
223 reserveRatio: UFix64
224 ) {
225 KARATZ1XR82W.maxSupply = maxSupply
226 KARATZ1XR82W.initialSupply = initialSupply
227 KARATZ1XR82W.initialPrice = initialPrice
228 KARATZ1XR82W.reserveRatio = reserveRatio
229 }
230
231 /// Update pricing parameters (only admin can call)
232 access(all) fun updatePricing(
233 maxSupply: UFix64?,
234 initialSupply: UFix64?,
235 initialPrice: UFix64?,
236 reserveRatio: UFix64?
237 ) {
238 if maxSupply != nil {
239 KARATZ1XR82W.maxSupply = maxSupply!
240 }
241 if initialSupply != nil {
242 KARATZ1XR82W.initialSupply = initialSupply!
243 }
244 if initialPrice != nil {
245 KARATZ1XR82W.initialPrice = initialPrice!
246 }
247 if reserveRatio != nil {
248 KARATZ1XR82W.reserveRatio = reserveRatio!
249 }
250 }
251 }
252
253 /// Minter
254 ///
255 /// Resource object that token admin accounts can hold to mint new tokens.
256 ///
257 access(all) resource Minter {
258 /// mintTokens
259 ///
260 /// Function that mints new tokens, adds them to the total supply,
261 /// and returns them to the calling context.
262 ///
263 access(all) fun mintTokens(amount: UFix64, metadata: {String: String}?): @KARATZ1XR82W.Vault {
264 pre {
265 KARATZ1XR82W.totalSupply + amount <= KARATZ1XR82W.maxSupply: "Max supply exceeded"
266 }
267
268 KARATZ1XR82W.totalSupply = KARATZ1XR82W.totalSupply + amount
269
270 let eventData: {String: String} = {}
271 if metadata != nil {
272 for key in metadata!.keys {
273 eventData[key] = metadata![key]
274 }
275 }
276
277 emit TokensMinted(amount: amount, type: self.getType().identifier, metadata: eventData)
278 return <-create Vault(balance: amount)
279 }
280 }
281
282 init() {
283 // Set our named paths.
284 self.VaultStoragePath = /storage/KARATZ1XR82WVault
285 self.ReceiverPublicPath = /public/KARATZ1XR82WReceiver
286 self.BalancePublicPath = /public/KARATZ1XR82WBalance
287 self.AdminStoragePath = /storage/KARATZ1XR82WAdmin
288
289 // Initialize contract state.
290 self.totalSupply = 0.0
291 // Set default pricing parameters
292 self.maxSupply = 2400000.0
293 self.initialSupply = 0.0
294 self.initialPrice = 1.0
295
296 self.reserveRatio = 0.0
297
298
299 // Create the Vault with the total supply of tokens and save it in storage
300 //
301 let vault <- create Vault(balance: self.totalSupply)
302
303 // Create a public capability to the stored Vault that exposes
304 // the `deposit` method and getAcceptedTypes method through the `Receiver` interface
305 // and the `balance` method through the `Balance` interface
306 //
307 let tokenCap = self.account.capabilities.storage.issue<&KARATZ1XR82W.Vault>(self.VaultStoragePath)
308 self.account.capabilities.publish(tokenCap, at: self.BalancePublicPath)
309 let receiverCap = self.account.capabilities.storage.issue<&KARATZ1XR82W.Vault>(self.VaultStoragePath)
310 self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
311
312 self.account.storage.save(<-vault, to: self.VaultStoragePath)
313
314 let admin <- create Administrator()
315 self.account.storage.save(<-admin, to: self.AdminStoragePath)
316 }
317
318 access(all) fun emitBurnEvent(amount: UFix64, burner: Address, metadata: {String: String}?) {
319 emit TokensBurned(amount: amount, burner: burner, metadata: metadata ?? {})
320 }
321
322 /// Get pricing information
323 access(all) fun getPricingInfo(): {String: UFix64} {
324 return {
325 "maxSupply": self.maxSupply,
326 "initialSupply": self.initialSupply,
327 "initialPrice": self.initialPrice,
328 "reserveRatio": self.reserveRatio,
329 "totalSupply": self.totalSupply
330 }
331 }
332}