github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/main/cadence/nba/TopShotLocking.cdc (about) 1 import NonFungibleToken from 0xf8d6e0586b0a20c7 2 3 pub contract TopShotLocking { 4 5 // ----------------------------------------------------------------------- 6 // TopShotLocking contract Events 7 // ----------------------------------------------------------------------- 8 9 // Emitted when a Moment is locked 10 pub event MomentLocked(id: UInt64, duration: UFix64, expiryTimestamp: UFix64) 11 12 // Emitted when a Moment is unlocked 13 pub event MomentUnlocked(id: UInt64) 14 15 // Dictionary of locked NFTs 16 // TopShot nft resource id is the key 17 // locked until timestamp is the value 18 access(self) var lockedNFTs: {UInt64: UFix64} 19 20 // Dictionary of NFTs overridden to be unlocked 21 access(self) var unlockableNFTs: {UInt64: Bool} // nft resource id is the key 22 23 // isLocked Returns a boolean indicating if an nft exists in the lockedNFTs dictionary 24 // 25 // Parameters: nftRef: A reference to the NFT resource 26 // 27 // Returns: true if NFT is locked 28 pub fun isLocked(nftRef: &NonFungibleToken.NFT): Bool { 29 return self.lockedNFTs.containsKey(nftRef.id) 30 } 31 32 // getLockExpiry Returns the unix timestamp when an nft is unlockable 33 // 34 // Parameters: nftRef: A reference to the NFT resource 35 // 36 // Returns: unix timestamp 37 pub fun getLockExpiry(nftRef: &NonFungibleToken.NFT): UFix64 { 38 if !self.lockedNFTs.containsKey(nftRef.id) { 39 panic("NFT is not locked") 40 } 41 return self.lockedNFTs[nftRef.id]! 42 } 43 44 // lockNFT Takes an NFT resource and adds its unique identifier to the lockedNFTs dictionary 45 // 46 // Parameters: nft: NFT resource 47 // duration: number of seconds the NFT will be locked for 48 // 49 // Returns: the NFT resource 50 pub fun lockNFT(nft: @NonFungibleToken.NFT, duration: UFix64): @NonFungibleToken.NFT { 51 let TopShotNFTType: Type = CompositeType("A.TOPSHOTADDRESS.TopShot.NFT")! 52 if !nft.isInstance(TopShotNFTType) { 53 panic("NFT is not a TopShot NFT") 54 } 55 56 if self.lockedNFTs.containsKey(nft.id) { 57 // already locked - short circuit and return the nft 58 return <- nft 59 } 60 61 let expiryTimestamp = getCurrentBlock().timestamp + duration 62 63 self.lockedNFTs[nft.id] = expiryTimestamp 64 65 emit MomentLocked(id: nft.id, duration: duration, expiryTimestamp: expiryTimestamp) 66 67 return <- nft 68 } 69 70 // unlockNFT Takes an NFT resource and removes it from the lockedNFTs dictionary 71 // 72 // Parameters: nft: NFT resource 73 // 74 // Returns: the NFT resource 75 // 76 // NFT must be eligible for unlocking by an admin 77 pub fun unlockNFT(nft: @NonFungibleToken.NFT): @NonFungibleToken.NFT { 78 if !self.lockedNFTs.containsKey(nft.id) { 79 // nft is not locked, short circuit and return the nft 80 return <- nft 81 } 82 83 let lockExpiryTimestamp: UFix64 = self.lockedNFTs[nft.id]! 84 let isPastExpiry: Bool = getCurrentBlock().timestamp >= lockExpiryTimestamp 85 86 let isUnlockableOverridden: Bool = self.unlockableNFTs.containsKey(nft.id) 87 88 if !(isPastExpiry || isUnlockableOverridden) { 89 panic("NFT is not eligible to be unlocked, expires at ".concat(lockExpiryTimestamp.toString())) 90 } 91 92 self.unlockableNFTs.remove(key: nft.id) 93 self.lockedNFTs.remove(key: nft.id) 94 95 emit MomentUnlocked(id: nft.id) 96 97 return <- nft 98 } 99 100 // getIDs Returns the ids of all locked Top Shot NFT tokens 101 // 102 // Returns: array of ids 103 // 104 pub fun getIDs(): [UInt64] { 105 return self.lockedNFTs.keys 106 } 107 108 // getExpiry Returns the timestamp when a locked token is eligible for unlock 109 // 110 // Parameters: tokenID: the nft id of the locked token 111 // 112 // Returns: a unix timestamp in seconds 113 // 114 pub fun getExpiry(tokenID: UInt64): UFix64? { 115 return self.lockedNFTs[tokenID] 116 } 117 118 // getLockedNFTsLength Returns the count of locked tokens 119 // 120 // Returns: an integer containing the number of locked tokens 121 // 122 pub fun getLockedNFTsLength(): Int { 123 return self.lockedNFTs.length 124 } 125 126 // Admin is a special authorization resource that 127 // allows the owner to override the lock on a moment 128 // 129 pub resource Admin { 130 // createNewAdmin creates a new Admin resource 131 // 132 pub fun createNewAdmin(): @Admin { 133 return <-create Admin() 134 } 135 136 // markNFTUnlockable marks a given nft as being 137 // unlockable, overridding the expiry timestamp 138 // the nft owner will still need to send an unlock transaction to unlock 139 // 140 pub fun markNFTUnlockable(nftRef: &NonFungibleToken.NFT) { 141 TopShotLocking.unlockableNFTs[nftRef.id] = true 142 } 143 144 // unlocks all NFTs 145 pub fun unlockAll() { 146 TopShotLocking.lockedNFTs = {} 147 TopShotLocking.unlockableNFTs = {} 148 } 149 } 150 151 // ----------------------------------------------------------------------- 152 // TopShotLocking initialization function 153 // ----------------------------------------------------------------------- 154 // 155 init() { 156 self.lockedNFTs = {} 157 self.unlockableNFTs = {} 158 159 // Create a single admin resource 160 let admin <- create Admin() 161 162 // Store it in private account storage in `init` so only the admin can use it 163 self.account.save(<-admin, to: /storage/TopShotLockingAdmin) 164 } 165 }