Smart Contract

PlayToken

A.123cb47fe122f6e3.PlayToken

Deployed

3d ago
Feb 25, 2026, 01:32:34 AM UTC

Dependents

1 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import MoxyData from 0x123cb47fe122f6e3
3 
4
5pub contract PlayToken: FungibleToken {
6
7    /// Total supply of PlayTokens in existence
8    pub var totalSupply: UFix64
9    access(contract) var totalSupplies: @MoxyData.OrderedDictionary
10
11    /// TokensInitialized
12    ///
13    /// The event that is emitted when the contract is created
14    pub event TokensInitialized(initialSupply: UFix64)
15
16    /// TokensWithdrawn
17    ///
18    /// The event that is emitted when tokens are withdrawn from a Vault
19    pub event TokensWithdrawn(amount: UFix64, from: Address?)
20
21    /// TokensDeposited
22    ///
23    /// The event that is emitted when tokens are deposited to a Vault
24    pub event TokensDeposited(amount: UFix64, to: Address?)
25
26    /// TokensMinted
27    ///
28    /// The event that is emitted when new tokens are minted
29    pub event TokensMinted(amount: UFix64)
30
31    /// TokensBurned
32    ///
33    /// The event that is emitted when tokens are destroyed
34    pub event TokensBurned(amount: UFix64)
35
36    /// MinterCreated
37    ///
38    /// The event that is emitted when a new minter resource is created
39    pub event MinterCreated(allowedAmount: UFix64)
40
41    /// BurnerCreated
42    ///
43    /// The event that is emitted when a new burner resource is created
44    pub event BurnerCreated()
45
46    /// Vault
47    ///
48    /// Each user stores an instance of only the Vault in their storage
49    /// The functions in the Vault and governed by the pre and post conditions
50    /// in FungibleToken when they are called.
51    /// The checks happen at runtime whenever a function is called.
52    ///
53    /// Resources can only be created in the context of the contract that they
54    /// are defined in, so there is no way for a malicious user to create Vaults
55    /// out of thin air. A special Minter resource needs to be defined to mint
56    /// new tokens.
57    ///
58    pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance, DailyBalancesInterface, ReceiverInterface {
59
60        /// The total balance of this vault
61        pub var balance: UFix64
62        access(contract) var dailyBalances: @MoxyData.OrderedDictionary
63
64        // initialize the balance at resource creation time
65        init(balance: UFix64) {
66            self.balance = balance
67            self.dailyBalances <- MoxyData.createNewOrderedDictionary()
68        }
69
70        pub fun getDailyBalanceFor(timestamp: UFix64): UFix64? {
71            // Returns the balance for the requested day or zero
72            // if no records at that day.
73            return self.dailyBalances.getValueOrMostRecentFor(timestamp: timestamp)
74        }
75
76        pub fun getBalanceFor(timestamp: UFix64): UFix64? {
77            return self.dailyBalances.getValueFor(timestamp: timestamp)
78        }
79
80        /// withdraw
81        ///
82        /// Function that takes an amount as an argument
83        /// and withdraws that amount from the Vault.
84        ///
85        /// It creates a new temporary Vault that is used to hold
86        /// the money that is being transferred. It returns the newly
87        /// created Vault to the context that called so it can be deposited
88        /// elsewhere.
89        ///
90        pub fun withdraw(amount: UFix64): @FungibleToken.Vault {
91            self.balance = self.balance - amount
92            emit TokensWithdrawn(amount: amount, from: self.owner?.address)
93            return <-create Vault(balance: amount)
94        }
95
96        /// deposit
97        ///
98        /// Function that takes a Vault object as an argument and adds
99        /// its balance to the balance of the owners Vault.
100        ///
101        /// It is allowed to destroy the sent Vault because the Vault
102        /// was a temporary holder of the tokens. The Vault's balance has
103        /// been consumed and therefore can be destroyed.
104        ///
105        pub fun deposit(from: @FungibleToken.Vault) {
106           // PLAY Tokens can't be transferred
107            panic("PLAY can't be deposited")
108        }
109        
110        pub fun convertedFromMOXY(from: @FungibleToken.Vault) {
111            let vault <- from as! @PlayToken.Vault
112            
113            self.dailyBalances.setAmountFor(timestamp: getCurrentBlock().timestamp, amount: vault.balance)
114
115            self.balance = self.balance + vault.balance
116
117            emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
118            vault.balance = 0.0
119            destroy vault
120        }
121
122        destroy() {
123            // Updating total supply registered daily
124            PlayToken.destroyTotalSupply(orderedDictionary: <-self.dailyBalances)
125            PlayToken.totalSupply = PlayToken.totalSupply - self.balance
126        }
127    }
128
129    /// createEmptyVault
130    ///
131    /// Function that creates a new Vault with a balance of zero
132    /// and returns it to the calling context. A user must call this function
133    /// and store the returned Vault in their storage in order to allow their
134    /// account to be able to receive deposits of this token type.
135    ///
136    pub fun createEmptyVault(): @Vault {
137        return <-create Vault(balance: 0.0)
138    }
139
140    pub resource Administrator {
141
142        /// createNewMinter
143        ///
144        /// Function that creates and returns a new minter resource
145        ///
146        pub fun createNewMinter(allowedAmount: UFix64): @Minter {
147            emit MinterCreated(allowedAmount: allowedAmount)
148            return <-create Minter(allowedAmount: allowedAmount)
149        }
150
151        /// createNewBurner
152        ///
153        /// Function that creates and returns a new burner resource
154        ///
155        pub fun createNewBurner(): @Burner {
156            emit BurnerCreated()
157            return <-create Burner()
158        }
159    }
160
161    /// Minter
162    ///
163    /// Resource object that token admin accounts can hold to mint new tokens.
164    ///
165    pub resource Minter {
166
167        /// The amount of tokens that the minter is allowed to mint
168        pub var allowedAmount: UFix64
169
170        /// mintTokens
171        ///
172        /// Function that mints new tokens, adds them to the total supply,
173        /// and returns them to the calling context.
174        ///
175        pub fun mintTokens(amount: UFix64): @PlayToken.Vault {
176            pre {
177                amount > 0.0: "Amount minted must be greater than zero"
178                amount <= self.allowedAmount: "Amount minted must be less than the allowed amount"
179            }
180
181            PlayToken.totalSupplies.setAmountFor(timestamp: getCurrentBlock().timestamp, amount: amount)
182
183            PlayToken.totalSupply = PlayToken.totalSupply + amount
184
185            self.allowedAmount = self.allowedAmount - amount
186
187            emit TokensMinted(amount: amount)
188            return <-create Vault(balance: amount)
189        }
190
191        init(allowedAmount: UFix64) {
192            self.allowedAmount = allowedAmount
193        }
194    }
195
196    /// Burner
197    ///
198    /// Resource object that token admin accounts can hold to burn tokens.
199    ///
200    pub resource Burner {
201
202        /// burnTokens
203        ///
204        /// Function that destroys a Vault instance, effectively burning the tokens.
205        ///
206        /// Note: the burned tokens are automatically subtracted from the
207        /// total supply in the Vault destructor.
208        ///
209        pub fun burnTokens(from: @FungibleToken.Vault) {
210            let vault <- from as! @PlayToken.Vault
211            let amount = vault.balance
212            destroy vault
213            emit TokensBurned(amount: amount)
214        }
215    }
216
217    pub fun getTotalSupplyFor(timestamp: UFix64): UFix64 {
218        return self.totalSupplies.getValueOrMostRecentFor(timestamp: timestamp)
219    }
220
221    access(contract) fun destroyTotalSupply(orderedDictionary: @MoxyData.OrderedDictionary) {
222        self.totalSupplies.destroyWith(orderedDictionary: <-orderedDictionary)
223    }
224
225    pub resource interface DailyBalancesInterface {
226        pub fun getDailyBalanceFor(timestamp: UFix64): UFix64? 
227        pub fun getBalanceFor(timestamp: UFix64): UFix64?
228    }
229
230    pub resource interface ReceiverInterface {
231        pub fun convertedFromMOXY(from: @FungibleToken.Vault)
232    }
233
234    pub let playTokenVaultStorage: StoragePath
235    pub let playTokenAdminStorage: StoragePath
236    pub let playTokenReceiverPath: PublicPath
237    pub let playTokenReceiverInterfacePath: PublicPath
238    pub let playTokenBalancePath: PublicPath
239    pub let playTokenDailyBalancePath: PublicPath
240
241    init() {
242        // Initial total supply defined for PLAY token to starting strength
243        // of Proof of Play
244        self.totalSupply = 350000000.0
245        self.totalSupplies <- MoxyData.createNewOrderedDictionary()
246        self.totalSupplies.setAmountFor(timestamp: getCurrentBlock().timestamp, amount: self.totalSupply)
247
248        self.playTokenVaultStorage = /storage/playTokenVault
249        self.playTokenAdminStorage = /storage/playTokenAdmin
250        self.playTokenReceiverPath = /public/playTokenReceiver
251        self.playTokenReceiverInterfacePath = /public/playTokenReceiverInterface
252        self.playTokenBalancePath = /public/playTokenBalance
253        self.playTokenDailyBalancePath = /public/playTokenDailyBalancePath
254
255        // Create the Vault with the total supply of tokens and save it in storage
256        //
257        let vault <- create Vault(balance: self.totalSupply)
258        self.account.save(<-vault, to: self.playTokenVaultStorage)
259
260        // Create a public capability to the stored Vault that only exposes
261        // the `deposit` method through the `Receiver` interface
262        //
263        self.account.link<&{FungibleToken.Receiver}>(
264            self.playTokenReceiverPath ,
265            target: self.playTokenVaultStorage
266        )
267        self.account.link<&{PlayToken.ReceiverInterface}>(
268            self.playTokenReceiverInterfacePath ,
269            target: self.playTokenVaultStorage
270        )
271
272        // Create a public capability to the stored Vault that only exposes
273        // the `balance` field through the `Balance` interface
274        //
275        self.account.link<&PlayToken.Vault{FungibleToken.Balance}>(
276            self.playTokenBalancePath,
277            target: self.playTokenVaultStorage
278        )
279
280        self.account.link<&PlayToken.Vault{FungibleToken.Balance}>(
281            self.playTokenDailyBalancePath,
282            target: self.playTokenVaultStorage
283        )
284
285        let admin <- create Administrator()
286        self.account.save(<-admin, to: self.playTokenAdminStorage)
287
288        // Emit an event that shows that the contract was initialized
289        //
290        emit TokensInitialized(initialSupply: self.totalSupply)
291    }
292}
293 
294