Smart Contract
POINT24
A.215f3ac76cb49656.POINT24
1import MetadataViews from 0x1d7e57aa55817448
2import FungibleToken from 0xf233dcee88fe0abe
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5access(all) contract POINT24: 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 POINT24 tokens in existence
30 access(all) var totalSupply: UFix64
31
32 /// Storage and Public Paths
33 access(all) let VaultStoragePath: StoragePath
34 access(all) let BalancePublicPath: PublicPath
35 access(all) let ReceiverPublicPath: PublicPath
36 access(all) let AdminStoragePath: StoragePath
37
38 access(all) view fun getContractViews(resourceType: Type?): [Type] {
39 return [
40 Type<FungibleTokenMetadataViews.FTView>(),
41 Type<FungibleTokenMetadataViews.FTDisplay>(),
42 Type<FungibleTokenMetadataViews.FTVaultData>(),
43 Type<FungibleTokenMetadataViews.TotalSupply>()
44 ]
45 }
46
47 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
48 switch viewType {
49 case Type<FungibleTokenMetadataViews.FTView>():
50 return FungibleTokenMetadataViews.FTView(
51 ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
52 ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
53 )
54 case Type<FungibleTokenMetadataViews.FTDisplay>():
55 let media = MetadataViews.Media(
56 file: MetadataViews.HTTPFile(
57 url: "https://24karat.io/tokens/POINT24/thumbnail.png"
58 ),
59 mediaType: "image/png"
60 )
61 return FungibleTokenMetadataViews.FTDisplay(
62 name: "POINT24 Token",
63 symbol: "POINT24",
64 description: "POINT24 Token - 24KARAT loyalty points token",
65 externalURL: MetadataViews.ExternalURL("https://24karat.io"),
66 logos: MetadataViews.Medias([media]),
67 socials: {}
68 )
69 case Type<FungibleTokenMetadataViews.FTVaultData>():
70 return FungibleTokenMetadataViews.FTVaultData(
71 storagePath: self.VaultStoragePath,
72 receiverPath: self.ReceiverPublicPath,
73 metadataPath: self.BalancePublicPath,
74 receiverLinkedType: Type<&POINT24.Vault>(),
75 metadataLinkedType: Type<&POINT24.Vault>(),
76 createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
77 return <-POINT24.createEmptyVault(vaultType: Type<@POINT24.Vault>())
78 })
79 )
80 case Type<FungibleTokenMetadataViews.TotalSupply>():
81 return FungibleTokenMetadataViews.TotalSupply(
82 totalSupply: POINT24.totalSupply
83 )
84 }
85 return nil
86 }
87
88 /// Vault
89 ///
90 /// Each user stores an instance of only the Vault in their storage
91 /// The functions in the Vault and governed by the pre and post conditions
92 /// in FungibleToken when they are called.
93 /// The checks happen at runtime whenever a function is called.
94 ///
95 /// Resources can only be created in the context of the contract that they
96 /// are defined in, so there is no way for a malicious user to create Vaults
97 /// out of thin air. A special Minter resource needs to be defined to mint
98 /// new tokens.
99 ///
100 access(all) resource Vault: FungibleToken.Vault {
101
102 /// The total balance of this vault
103 access(all) var balance: UFix64
104
105 // initialize the balance at resource creation time
106 init(balance: UFix64) {
107 self.balance = balance
108 }
109
110 /// Called when a fungible token is burned via the `Burner.burn()` method
111 access(contract) fun burnCallback() {
112 if self.balance > 0.0 {
113 POINT24.totalSupply = POINT24.totalSupply - self.balance
114 }
115 self.balance = 0.0
116 }
117
118 access(all) view fun getViews(): [Type] {
119 return POINT24.getContractViews(resourceType: nil)
120 }
121
122 access(all) fun resolveView(_ view: Type): AnyStruct? {
123 return POINT24.resolveContractView(resourceType: nil, viewType: view)
124 }
125
126 /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
127 access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
128 let supportedTypes: {Type: Bool} = {}
129 supportedTypes[self.getType()] = true
130 return supportedTypes
131 }
132
133 access(all) view fun isSupportedVaultType(type: Type): Bool {
134 return self.getSupportedVaultTypes()[type] ?? false
135 }
136
137 /// Asks if the amount can be withdrawn from this vault
138 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
139 return amount <= self.balance
140 }
141
142 /// withdraw
143 ///
144 /// Function that takes an amount as an argument
145 /// and withdraws that amount from the Vault.
146 ///
147 /// It creates a new temporary Vault that is used to hold
148 /// the tokens that are being transferred. It returns the newly
149 /// created Vault to the context that called so it can be deposited
150 /// elsewhere.
151 ///
152 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @POINT24.Vault {
153 self.balance = self.balance - amount
154 return <-create Vault(balance: amount)
155 }
156
157 /// deposit
158 ///
159 /// Function that takes a Vault object as an argument and adds
160 /// its balance to the balance of the owners Vault.
161 ///
162 /// It is allowed to destroy the sent Vault because the Vault
163 /// was a temporary holder of the tokens. The Vault's balance has
164 /// been consumed and therefore can be destroyed.
165 ///
166 access(all) fun deposit(from: @{FungibleToken.Vault}) {
167 let vault <- from as! @POINT24.Vault
168 self.balance = self.balance + vault.balance
169 vault.balance = 0.0
170 destroy vault
171 }
172
173 /// createEmptyVault
174 ///
175 /// Function that creates a new Vault with a balance of zero
176 /// and returns it to the calling context. A user must call this function
177 /// and store the returned Vault in their storage in order to allow their
178 /// account to be able to receive deposits of this token type.
179 ///
180 access(all) fun createEmptyVault(): @POINT24.Vault {
181 return <-create Vault(balance: 0.0)
182 }
183 }
184
185 /// createEmptyVault
186 ///
187 /// Function that creates a new Vault with a balance of zero
188 /// and returns it to the calling context. A user must call this function
189 /// and store the returned Vault in their storage in order to allow their
190 /// account to be able to receive deposits of this token type.
191 ///
192 access(all) fun createEmptyVault(vaultType: Type): @POINT24.Vault {
193 return <- create Vault(balance: 0.0)
194 }
195
196 access(all) resource Administrator {
197 // createNewMinter
198 //
199 // Function that creates and returns a new minter resource
200 //
201 access(all) fun createNewMinter(): @Minter {
202 return <-create Minter()
203 }
204 }
205
206 /// Minter
207 ///
208 /// Resource object that token admin accounts can hold to mint new tokens.
209 ///
210 access(all) resource Minter {
211 /// mintTokens
212 ///
213 /// Function that mints new tokens, adds them to the total supply,
214 /// and returns them to the calling context.
215 ///
216 access(all) fun mintTokens(amount: UFix64, metadata: {String: String}?): @POINT24.Vault {
217 POINT24.totalSupply = POINT24.totalSupply + amount
218
219 let eventData: {String: String} = {}
220 if metadata != nil {
221 for key in metadata!.keys {
222 eventData[key] = metadata![key]
223 }
224 }
225
226 emit TokensMinted(amount: amount, type: self.getType().identifier, metadata: eventData)
227 return <-create Vault(balance: amount)
228 }
229 }
230
231 init() {
232 // Set our named paths.
233 self.VaultStoragePath = /storage/point24Vault
234 self.ReceiverPublicPath = /public/point24Receiver
235 self.BalancePublicPath = /public/point24Balance
236 self.AdminStoragePath = /storage/point24Admin
237
238 // Initialize contract state.
239 self.totalSupply = 0.0
240
241 // Create the Vault with the total supply of tokens and save it in storage
242 //
243 let vault <- create Vault(balance: self.totalSupply)
244
245 // Create a public capability to the stored Vault that exposes
246 // the `deposit` method and getAcceptedTypes method through the `Receiver` interface
247 // and the `balance` method through the `Balance` interface
248 //
249 let tokenCap = self.account.capabilities.storage.issue<&POINT24.Vault>(self.VaultStoragePath)
250 self.account.capabilities.publish(tokenCap, at: self.BalancePublicPath)
251 let receiverCap = self.account.capabilities.storage.issue<&POINT24.Vault>(self.VaultStoragePath)
252 self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
253
254 self.account.storage.save(<-vault, to: self.VaultStoragePath)
255
256 let admin <- create Administrator()
257 self.account.storage.save(<-admin, to: self.AdminStoragePath)
258 }
259
260 access(all) fun emitBurnEvent(amount: UFix64, burner: Address, metadata: {String: String}?) {
261 emit TokensBurned(amount: amount, burner: burner, metadata: metadata ?? {})
262 }
263}
264