Smart Contract
Staking
A.807c3d470888cc48.Staking
1
2
3// SPDX-License-Identifier: MIT
4import NonFungibleToken from 0x1d7e57aa55817448
5import FungibleToken from 0xf233dcee88fe0abe
6import MetadataViews from 0x1d7e57aa55817448
7import Flunks from 0x807c3d470888cc48
8import Backpack from 0x807c3d470888cc48
9import GUM from 0x807c3d470888cc48
10import HybridCustodyHelper from 0x807c3d470888cc48
11import GUMStakingTracker from 0x807c3d470888cc48
12
13access(all)
14contract Staking{
15 access(all)
16 let AdminStoragePath: StoragePath
17
18 access(self)
19 var Pools: @{String: Pool} // {poolName: Pool}
20
21
22 access(all)
23 event Stake(id: UInt64, pool: String)
24
25 access(all)
26 event Unstake(id: UInt64, pool: String)
27
28 access(all)
29 event ClaimReward(pool: String, amount: UFix64, tokenID: UInt64)
30
31 access(all)
32 resource Pool{
33 access(all)
34 let name: String
35
36 access(all)
37 var multiplier: UFix64
38
39 access(self)
40 var stakedInfo:{ UInt64: StakingInfo} // {tokenID: StakingInfo}
41
42
43 access(all)
44 fun getStakedInfoByTokenID(tokenID: UInt64): StakingInfo?{
45 return self.stakedInfo[tokenID]
46 }
47
48 access(all)
49 fun mutateStakingInfo(tokenID: UInt64, stakingInfo: StakingInfo?){
50 if stakingInfo == nil{
51 // If new staking info is nil, remove the existing info
52 self.stakedInfo.remove(key: tokenID)
53 return
54 } else{
55 // Otherwise update the existing info
56 self.stakedInfo[tokenID] = stakingInfo
57 }
58 }
59
60 // Admin function to claim GUMS on behalf of users, GUM will go to the staker's wallet if it's still the owner at the time called
61 access(contract)
62 fun adminClaimOne(tokenID: UInt64, staker: Address){
63 if !self.stakedInfo.keys.contains(tokenID){
64 // 1. Not staked, return directly
65 return
66 }
67 let stakingInfo = self.stakedInfo[tokenID]
68 if stakingInfo == nil{
69 // 2. Not staked, return directly
70 return
71 }
72 if (stakingInfo!).staker != staker{
73 // 3. Staker not owner of the token
74 return
75 }
76
77 // Verify the staker still ownes the token, return silently if not
78 if self.name == "Flunks"{
79 let ownedTokenIDs = HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(ownerAddress: staker)
80 if !ownedTokenIDs.contains(tokenID){
81 return
82 }
83 } else if self.name == "Backpack"{
84 let ownedTokenIDs = HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(ownerAddress: staker)
85 if !ownedTokenIDs.contains(tokenID){
86 return
87 }
88 }
89
90 // Consolidate Rewards
91 let validRewardPeriodInDays = (stakingInfo!).validRewardPeriodInDays()
92 if validRewardPeriodInDays <= 0.0{
93 // 4. No reward to claim, return directly
94 return
95 }
96 var stakingReward =
97 Staking.pendingRewards(pool: self.name, ownerAddress: staker, tokenID: tokenID)
98 let GUMMinter =
99 Staking.account.storage.borrow<&GUM.Minter>(from: GUM.AdminStoragePath)
100 ?? panic("Could not borrow reference to the GUM Minter resource")
101
102 // Reset staking timestamp to the last reward timestamp
103 let newStakingInfo =
104 StakingInfo(
105 _staker: (stakingInfo!).staker,
106 _tokenID: (stakingInfo!).tokenID,
107 _stakedAtInSeconds: UInt64(getCurrentBlock().timestamp) + 60,
108 _pool: (stakingInfo!).pool
109 )
110 self.stakedInfo[tokenID] = newStakingInfo
111 emit ClaimReward(
112 pool: newStakingInfo.pool,
113 amount: stakingReward,
114 tokenID: newStakingInfo.tokenID
115 )
116 Staking.updateTracker(pool: self.name, tokenID: tokenID, amount: stakingReward)
117
118 // Mint the specified amount of GUM tokens
119 let newVault <- GUMMinter.mintTokens(amount: stakingReward)
120 // Deposit the minted tokens into the recipient's Vault
121 let recipientVault =
122 getAccount(staker).capabilities.get<&{FungibleToken.Receiver}>(
123 GUM.VaultReceiverPublicPath
124 ).borrow()
125 ?? panic("Could not borrow reference to the recipient's Vault")
126 recipientVault.deposit(from: <-newVault)
127 }
128
129 access(contract)
130 fun adminClaimAll(tokenIDs: [UInt64], staker: Address){
131 for tokenID in tokenIDs{
132 self.adminClaimOne(tokenID: tokenID, staker: staker)
133 }
134 }
135
136 access(all)
137 fun claimOne(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, tokenID: UInt64){
138 if !self.stakedInfo.keys.contains(tokenID){
139 // 1. Not staked, return directly
140 return
141 }
142 let stakingInfo = self.stakedInfo[tokenID]
143 if stakingInfo == nil{
144 // 2. Not staked, return directly
145 return
146 }
147 if (stakingInfo!).staker != signerAuth.address{
148 // 3. staker is not owner, check if the signer is the new owner of the staked NFT
149 if self.name == "Flunks"{
150 let ownedTokenIDs = HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(ownerAddress: signerAuth.address)
151 if !ownedTokenIDs.contains(tokenID){
152 return
153 }
154 } else if self.name == "Backpack"{
155 let ownedTokenIDs = HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(ownerAddress: signerAuth.address)
156 if !ownedTokenIDs.contains(tokenID){
157 return
158 }
159 }
160
161 // if not, return directly
162 return
163 }
164
165 // Consolidate Rewards
166 let validRewardPeriodInDays = (stakingInfo!).validRewardPeriodInDays()
167 if validRewardPeriodInDays <= 0.0{
168 // 4. No reward to claim, return directly
169 return
170 }
171
172 // Setup $GUM Vault if user does not already have one
173 Staking.setupGUMVault(signer: signerAuth)
174 let recipient = getAccount(signerAuth.address)
175 let recipientVault =
176 signerAuth.capabilities.get<&{FungibleToken.Receiver}>(
177 GUM.VaultReceiverPublicPath
178 ).borrow() ?? panic("Could not borrow reference to the recipient's Vault")
179 var stakingReward =
180 Staking.pendingRewards(
181 pool: self.name,
182 ownerAddress: signerAuth.address,
183 tokenID: tokenID
184 )
185 let GUMMinter =
186 Staking.account.storage.borrow<&GUM.Minter>(from: GUM.AdminStoragePath)
187 ?? panic("Could not borrow reference to the GUM Minter resource")
188
189 // Reset staking timestamp to the last reward timestamp
190 let newStakingInfo =
191 StakingInfo(
192 _staker: (stakingInfo!).staker,
193 _tokenID: (stakingInfo!).tokenID,
194 _stakedAtInSeconds: UInt64(getCurrentBlock().timestamp) + 60,
195 _pool: (stakingInfo!).pool
196 )
197 self.stakedInfo[tokenID] = newStakingInfo
198 emit ClaimReward(
199 pool: newStakingInfo.pool,
200 amount: stakingReward,
201 tokenID: newStakingInfo.tokenID
202 )
203 Staking.updateTracker(pool: self.name, tokenID: tokenID, amount: stakingReward)
204
205 // Mint the specified amount of GUM tokens
206 let newVault <- GUMMinter.mintTokens(amount: stakingReward)
207 // Deposit the minted tokens into the recipient's Vault
208 recipientVault.deposit(from: <-newVault)
209 }
210
211 access(all)
212 fun claimAll(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, tokenIDs: [UInt64]){
213 // Force re-link collections
214 HybridCustodyHelper.forceRelinkCollections(signer: signerAuth)
215 for tokenID in tokenIDs{
216 self.claimOne(signerAuth: signerAuth, tokenID: tokenID)
217 }
218 }
219
220 access(all)
221 fun claimMany(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, tokenIDs: [UInt64]){
222 // Force re-link collections
223 HybridCustodyHelper.forceRelinkCollections(signer: signerAuth)
224 for tokenID in tokenIDs{
225 self.claimOne(signerAuth: signerAuth, tokenID: tokenID)
226 }
227 }
228
229 init(_name: String, _multiplier: UFix64){
230 self.name = _name
231 self.multiplier = _multiplier
232 self.stakedInfo ={}
233 }
234 }
235
236 access(all)
237 struct StakingInfo{
238 access(all)
239 let staker: Address
240
241 access(all)
242 let tokenID: UInt64
243
244 access(all)
245 let stakedAtInSeconds: UInt64
246
247 access(all)
248 let pool: String
249
250 access(all)
251 fun validRewardPeriodInDays(): UFix64{
252 if UInt64(getCurrentBlock().timestamp) <= self.stakedAtInSeconds{
253 return 0.0
254 }
255
256 // Calculate the time difference in seconds
257 let timeDiffInSeconds = UInt64(getCurrentBlock().timestamp) - self.stakedAtInSeconds
258
259 // Convert the time difference to UFix64 for decimal calculation
260 let timeDiffInSecondsDecimal = UFix64(timeDiffInSeconds)
261
262 // Number of seconds in a day (as UFix64 for decimal precision)
263 let secondsInADay = 86400.0
264
265 // Calculate the time difference in days as a decimal
266 let daysDecimal = timeDiffInSecondsDecimal / secondsInADay
267 return daysDecimal
268 }
269
270 init(_staker: Address, _tokenID: UInt64, _stakedAtInSeconds: UInt64, _pool: String){
271 self.staker = _staker
272 self.tokenID = _tokenID
273 self.stakedAtInSeconds = _stakedAtInSeconds
274 self.pool = _pool
275 }
276 }
277
278 // Dumb helper
279 access(all)
280 fun validateFlunkOwner(ownerAddress: Address, tokenID: UInt64): Bool{
281 let tokenIDs =
282 HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(ownerAddress: ownerAddress)
283 return tokenIDs.contains(tokenID)
284 }
285
286 // Dumb helper
287 access(all)
288 fun validateBackpackOwner(ownerAddress: Address, tokenID: UInt64): Bool{
289 let tokenIDs =
290 HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(ownerAddress: ownerAddress)
291 return tokenIDs.contains(tokenID)
292 }
293
294 // Dumb helper
295 access(all)
296 fun validateOwnership(pool: String, ownerAddress: Address, tokenID: UInt64): Bool{
297 if pool == "Flunks"{
298 return Staking.validateFlunkOwner(ownerAddress: ownerAddress, tokenID: tokenID)
299 } else if pool == "Backpack"{
300 return Staking.validateBackpackOwner(ownerAddress: ownerAddress, tokenID: tokenID)
301 }
302 return false
303 }
304
305 // Dumb helper
306 access(all)
307 fun getStakingInfo(
308 signerAddress: Address,
309 pool: String,
310 tokenID: UInt64
311 ): Staking.StakingInfo?{
312 let selfAdmin =
313 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
314 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
315 let pool = selfAdmin.borrowPool(pool: pool)
316 return pool.getStakedInfoByTokenID(tokenID: tokenID)
317 }
318
319 access(all)
320 fun stakeOne(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, pool: String, tokenID: UInt64){
321 let isValid = Staking.validateOwnership(pool: pool, ownerAddress: signerAuth.address, tokenID: tokenID)
322 if (!isValid){
323 panic("Not owner")
324 }
325
326 let existingStakingInfo =
327 Staking.getStakingInfo(signerAddress: signerAuth.address, pool: pool, tokenID: tokenID)
328 if existingStakingInfo != nil && (existingStakingInfo!).staker == signerAuth.address{
329 // Already staked by the user, just return silently
330 return
331 }
332
333 // Otherwise stake it fresh
334 let stakingInfo =
335 StakingInfo(
336 _staker: signerAuth.address,
337 _tokenID: tokenID,
338 _stakedAtInSeconds: UInt64(getCurrentBlock().timestamp) + 60,
339 _pool: pool
340 )
341 let selfAdmin =
342 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
343 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
344 let poolRef = selfAdmin.borrowPool(pool: pool)
345 poolRef.mutateStakingInfo(tokenID: tokenID, stakingInfo: stakingInfo)
346 emit Stake(id: tokenID, pool: pool)
347 }
348
349 access(all)
350 fun unstakeOne(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, pool: String, tokenID: UInt64){
351 let isValid = Staking.validateOwnership(pool: pool, ownerAddress: signerAuth.address, tokenID: tokenID)
352 if (!isValid){
353 panic("Not owner")
354 }
355
356 let existingStakingInfo =
357 Staking.getStakingInfo(signerAddress: signerAuth.address, pool: pool, tokenID: tokenID)
358 if existingStakingInfo == nil{
359 // Not staked, return silently
360 return
361 }
362
363 // Claim the rewards for the user before unstaking
364 Staking.claimOne(signerAuth: signerAuth, pool: pool, tokenID: tokenID)
365 let selfAdmin =
366 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
367 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
368 let poolRef = selfAdmin.borrowPool(pool: pool)
369 poolRef.mutateStakingInfo(tokenID: tokenID, stakingInfo: nil)
370 emit Unstake(id: tokenID, pool: pool)
371 }
372
373 access(all)
374 fun stakeAll(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account){
375 let flunksTokenIDs =
376 HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(
377 ownerAddress: signerAuth.address
378 )
379 let backpackTokenIDs =
380 HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(
381 ownerAddress: signerAuth.address
382 )
383 if flunksTokenIDs != nil{
384 for tokenID in flunksTokenIDs!{
385 Staking.stakeOne(signerAuth: signerAuth, pool: "Flunks", tokenID: tokenID)
386 }
387 }
388 if backpackTokenIDs != nil{
389 for tokenID in backpackTokenIDs!{
390 Staking.stakeOne(signerAuth: signerAuth, pool: "Backpack", tokenID: tokenID)
391 }
392 }
393 }
394
395 access(all)
396 fun stakeMany(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, pool: String, tokenIDs: [UInt64]){
397 for tokenID in tokenIDs{
398 Staking.stakeOne(signerAuth: signerAuth, pool: pool, tokenID: tokenID)
399 }
400 }
401
402 access(all)
403 fun claimOne(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, pool: String, tokenID: UInt64){
404 let selfAdmin =
405 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
406 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
407 let poolRef = selfAdmin.borrowPool(pool: pool)
408 if pool == "Flunks"{
409 let flunksTokenIDs = HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(ownerAddress: signerAuth.address)
410 if !flunksTokenIDs.contains(tokenID){
411 panic("tokenID not found in signer's collection")
412 }
413 } else if pool == "Backpack"{
414 let backpackTokenIDs = HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(ownerAddress: signerAuth.address)
415 if !backpackTokenIDs.contains(tokenID){
416 panic("tokenID not found in signer's collection")
417 }
418 }
419 poolRef.claimOne(signerAuth: signerAuth, tokenID: tokenID)
420 }
421
422 access(all)
423 fun claimPool(pool: String, signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account){
424 // Claim all GUMS accumulated from a pool
425 let selfAdmin =
426 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
427 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
428 let poolRef: &Staking.Pool = selfAdmin.borrowPool(pool: pool)
429 if pool == "Flunks"{
430 let flunksTokenIDs = HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(ownerAddress: signerAuth.address)
431 poolRef.claimAll(signerAuth: signerAuth, tokenIDs: flunksTokenIDs)
432 } else if pool == "Backpack"{
433 let backpackTokenIDs = HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(ownerAddress: signerAuth.address)
434 poolRef.claimAll(signerAuth: signerAuth, tokenIDs: backpackTokenIDs)
435 }
436 }
437
438 access(all)
439 fun claimMany(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account, pool: String, tokenIDs: [UInt64]){
440 // Claim GUMs from many NFTs accumulated from a pool
441 let selfAdmin =
442 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
443 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
444 let poolRef: &Staking.Pool = selfAdmin.borrowPool(pool: pool)
445 poolRef.claimMany(signerAuth: signerAuth, tokenIDs: tokenIDs)
446 }
447
448 access(all)
449 fun claimAll(signerAuth: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account){
450 Staking.claimPool(pool: "Flunks", signerAuth: signerAuth)
451 Staking.claimPool(pool: "Backpack", signerAuth: signerAuth)
452 }
453
454 access(all)
455 fun pendingRewards(pool: String, ownerAddress: Address, tokenID: UInt64): UFix64{
456 if !["Flunks", "Backpack"].contains(pool) {
457 panic("Invalid pool")
458 }
459
460 let selfAdmin =
461 self.account.storage.borrow<&Staking.Admin>(from: Staking.AdminStoragePath)
462 ?? panic("Could not borrow a reference to the NonFungibleGUM Admin")
463 let poolRef = selfAdmin.borrowPool(pool: pool)
464 let stakingInfo = poolRef.getStakedInfoByTokenID(tokenID: tokenID)
465 let stakingPeriodInDays = stakingInfo?.validRewardPeriodInDays() ?? 0.0
466 if pool == "Flunks"{
467 // Flunks staking reward = 5.0 * stakingPeriodInDays
468 return poolRef.multiplier * stakingPeriodInDays
469 } else if pool == "Backpack"{
470 // Backpack staking reward = (1.0 + 0.1 * slot bonus) * stakingPeriodInDays
471 let slots = HybridCustodyHelper.getBackpackSlots(ownerAddress: ownerAddress, tokenID: tokenID)
472
473 // Invalid slot bonus, throw error
474 if slots < 0 || slots > 20{
475 panic("Invalid slot bonus found!")
476 }
477
478 // Get slots from MetadataViews resolved traits
479 return poolRef.multiplier * stakingPeriodInDays + 0.1 * UFix64(slots) * stakingPeriodInDays
480 }
481 return poolRef.multiplier * stakingPeriodInDays
482 }
483
484 access(all)
485 fun pendingRewardsPerWallet(address: Address): UFix64{
486 let flunksTokenIDs =
487 HybridCustodyHelper.getFlunksTokenIDsFromAllLinkedAccounts(ownerAddress: address)
488 let backpackTokenIDs =
489 HybridCustodyHelper.getBackpackTokenIDsFromAllLinkedAccounts(ownerAddress: address)
490 var totalRewards = 0.0
491 if flunksTokenIDs != nil{
492 for tokenID in flunksTokenIDs{
493 let pendingRewards = Staking.pendingRewards(pool: "Flunks", ownerAddress: address, tokenID: tokenID)
494 totalRewards = totalRewards + pendingRewards
495 }
496 }
497 if backpackTokenIDs != nil{
498 for tokenID in backpackTokenIDs{
499 let pendingRewards = Staking.pendingRewards(pool: "Backpack", ownerAddress: address, tokenID: tokenID)
500 totalRewards = totalRewards + pendingRewards
501 }
502 }
503 return totalRewards
504 }
505
506 access(all)
507 fun setupGUMVault(signer: auth(SaveValue, Capabilities, Storage, BorrowValue) &Account){
508 // Check if the account already has a GUM Vault
509 if signer.storage.borrow<&GUM.Vault>(from: GUM.VaultStoragePath) == nil{
510 // If not, create a new empty Vault and save it in the account's storage
511 let newVault <- GUM.createEmptyVault(vaultType: Type<@GUM.Vault>())
512 signer.storage.save(<-newVault, to: GUM.VaultStoragePath)
513
514 let cap = signer.capabilities.storage.issue<&GUM.Vault>(GUM.VaultStoragePath)
515 signer.capabilities.publish(cap, at: GUM.VaultReceiverPublicPath)
516 signer.capabilities.publish(cap, at: GUM.VaultBalancePublicPath)
517 }
518
519 let balanceRef = getAccount(signer.address)
520 .capabilities.borrow<&GUM.Vault>(GUM.VaultBalancePublicPath)
521 if balanceRef == nil{
522 signer.capabilities.unpublish(GUM.VaultReceiverPublicPath)
523 signer.capabilities.unpublish(GUM.VaultBalancePublicPath)
524
525 let cap = signer.capabilities.storage.issue<&GUM.Vault>(GUM.VaultStoragePath)
526 signer.capabilities.publish(cap, at: GUM.VaultReceiverPublicPath)
527 signer.capabilities.publish(cap, at: GUM.VaultBalancePublicPath)
528 }
529 }
530
531 access(all)
532 resource Admin{
533 access(all)
534 fun borrowPool(pool: String): &Pool{
535 pre{
536 Staking.Pools[pool] != nil:
537 "Cannot borrow Pool: Pool with provided name does not exist"
538 }
539 return (&Staking.Pools[pool] as &Pool?)!
540 }
541
542 access(all)
543 fun adminClaimPool(pool: String, staker: Address){
544 let poolRef = self.borrowPool(pool: pool)
545 if pool == "Flunks"{
546 let tokenIDs = getAccount(staker).capabilities.get<&Flunks.Collection>(Flunks.CollectionPublicPath).borrow()?.getIDs()
547 poolRef.adminClaimAll(tokenIDs: tokenIDs!, staker: staker)
548 } else if pool == "Backpack"{
549 let tokenIDs = getAccount(staker).capabilities.get<&Backpack.Collection>(Backpack.CollectionPublicPath).borrow()?.getIDs()
550 poolRef.adminClaimAll(tokenIDs: tokenIDs!, staker: staker)
551 }
552 }
553
554 access(all)
555 fun adminClaimAll(staker: Address){
556 self.adminClaimPool(pool: "Flunks", staker: staker)
557 self.adminClaimPool(pool: "Backpack", staker: staker)
558 }
559
560 access(all)
561 fun adminStakeOne(
562 stakerAddress: Address,
563 pool: String,
564 tokenID: UInt64,
565 stakedAtInSeconds: UInt64
566 ){
567 // Validate stakedAtInSeconds has to be within 60 days
568 if UInt64(getCurrentBlock().timestamp) - stakedAtInSeconds > 5184000{
569 panic("StakedAtInSeconds cannot be more than 60 days in the past")
570 }
571 let poolRef = self.borrowPool(pool: pool)
572 let adminStakingInfo =
573 StakingInfo(
574 _staker: stakerAddress,
575 _tokenID: tokenID,
576 _stakedAtInSeconds: stakedAtInSeconds,
577 _pool: pool
578 )
579 poolRef.mutateStakingInfo(tokenID: tokenID, stakingInfo: adminStakingInfo)
580 }
581
582 access(all)
583 fun adminStakeMany(
584 stakerAddress: Address,
585 pool: String,
586 tokenIDs: [
587 UInt64
588 ],
589 stakedAtInSeconds: UInt64
590 ){
591 for tokenID in tokenIDs{
592 self.adminStakeOne(stakerAddress: stakerAddress, pool: pool, tokenID: tokenID, stakedAtInSeconds: stakedAtInSeconds)
593 }
594 }
595 }
596
597 access(contract)
598 fun updateTracker(pool: String, tokenID: UInt64, amount: UFix64){
599 let trackerAdmin =
600 Staking.account.storage.borrow<&GUMStakingTracker.Admin>(
601 from: GUMStakingTracker.AdminStoragePath
602 )
603 ?? panic("Could not borrow a reference to the GUMStakingTracker Admin")
604 trackerAdmin.updateClaimedGUM(pool: pool, tokenID: tokenID, amount: amount)
605 }
606
607 init(){
608 self.AdminStoragePath = /storage/StakingAdmin
609 self.Pools <-{
610 "Flunks": <-create Pool(_name: "Flunks", _multiplier: 5.0),
611 "Backpack": <-create Pool(_name: "Backpack", _multiplier: 1.0)
612 }
613 let admin <- create Admin()
614 self.account.storage.save(<-admin, to: self.AdminStoragePath)
615 }
616}
617