Smart Contract

MagnetiqLocking

A.5257f1455ed366fe.MagnetiqLocking

Deployed

1d ago
Feb 26, 2026, 09:44:01 PM UTC

Dependents

0 imports
1import NonFungibleToken from 0x1d7e57aa55817448
2
3access(all)
4contract MagnetiqLocking {
5
6    // -----------------------------------------------------------------------
7    // MagnetiqLocking contract Events
8    // -----------------------------------------------------------------------
9
10    // Emitted when a Token is locked
11    access(all)
12    event TokenLocked(id: UInt64, duration: UFix64, expiryTimestamp: UFix64)
13
14    // Emitted when a Token is unlocked
15    access(all)
16    event TokenUnlocked(id: UInt64)
17
18    // Dictionary of locked NFTs
19    // Magnetiq nft resource id is the key
20    // locked until timestamp is the value
21    access(self) var lockedNFTs: {UInt64: UFix64}
22
23    // Dictionary of NFTs overridden to be unlocked
24    access(self) var unlockableNFTs: {UInt64: Bool} // nft resource id is the key
25
26    // isLocked Returns a boolean indicating if an nft exists in the lockedNFTs dictionary
27    //
28    // Parameters: nftRef: A reference to the NFT resource
29    //
30    // Returns: true if NFT is locked
31    access(all)
32    fun isLocked(nftRef: &{NonFungibleToken.NFT}): Bool {
33        return self.lockedNFTs.containsKey(nftRef.id)
34    }
35
36    // getLockExpiry Returns the unix timestamp when an nft is unlockable
37    //
38    // Parameters: nftRef: A reference to the NFT resource
39    //
40    // Returns: unix timestamp
41    access(all)
42    fun getLockExpiry(nftRef: &{NonFungibleToken.NFT}): UFix64 {
43        if !self.lockedNFTs.containsKey(nftRef.id) {
44            panic("NFT is not locked")
45        }
46        return self.lockedNFTs[nftRef.id]!
47    }
48
49    // lockNFT Takes an NFT resource and adds its unique identifier to the lockedNFTs dictionary
50    //
51    // Parameters: nft: NFT resource
52    //             duration: number of seconds the NFT will be locked for
53    //
54    // Returns: the NFT resource
55    access(all)
56    fun lockNFT(nft: @{NonFungibleToken.NFT}, duration: UFix64): @{NonFungibleToken.NFT} {
57        let MagnetiqNFTType: Type = CompositeType("A.e55718549e2805ca.Magnetiq.NFT")!
58        if !nft.isInstance(MagnetiqNFTType) {
59            panic("NFT is not a Magnetiq NFT")
60        }
61
62        if self.lockedNFTs.containsKey(nft.id) {
63            // already locked - short circuit and return the nft
64            return <- nft
65        }
66
67        let expiryTimestamp = getCurrentBlock().timestamp + duration
68
69        self.lockedNFTs[nft.id] = expiryTimestamp
70
71        emit TokenLocked(id: nft.id, duration: duration, expiryTimestamp: expiryTimestamp)
72
73        return <- nft
74    }
75
76    // unlockNFT Takes an NFT resource and removes it from the lockedNFTs dictionary
77    //
78    // Parameters: nft: NFT resource
79    //
80    // Returns: the NFT resource
81    //
82    // NFT must be eligible for unlocking by an admin
83    access(all)
84    fun unlockNFT(nft: @{NonFungibleToken.NFT}): @{NonFungibleToken.NFT} {
85        if !self.lockedNFTs.containsKey(nft.id) {
86            // nft is not locked, short circuit and return the nft
87            return <- nft
88        }
89
90        let lockExpiryTimestamp: UFix64 = self.lockedNFTs[nft.id]!
91        let isPastExpiry: Bool = getCurrentBlock().timestamp >= lockExpiryTimestamp
92
93        let isUnlockableOverridden: Bool = self.unlockableNFTs.containsKey(nft.id)
94
95        if !(isPastExpiry || isUnlockableOverridden) {
96            panic("NFT is not eligible to be unlocked, expires at ".concat(lockExpiryTimestamp.toString()))
97        }
98
99        self.unlockableNFTs.remove(key: nft.id)
100        self.lockedNFTs.remove(key: nft.id)
101
102        emit TokenUnlocked(id: nft.id)
103
104        return <- nft
105    }
106
107    // getIDs Returns the ids of all locked Top Shot NFT tokens
108    //
109    // Returns: array of ids
110    //
111    access(all)
112    fun getIDs(): [UInt64] {
113        return self.lockedNFTs.keys
114    }
115
116    // getExpiry Returns the timestamp when a locked token is eligible for unlock
117    //
118    // Parameters: tokenID: the nft id of the locked token
119    //
120    // Returns: a unix timestamp in seconds
121    //
122    access(all)
123    fun getExpiry(tokenID: UInt64): UFix64? {
124        return self.lockedNFTs[tokenID]
125    }
126
127    // getLockedNFTsLength Returns the count of locked tokens
128    //
129    // Returns: an integer containing the number of locked tokens
130    //
131    access(all)
132    fun getLockedNFTsLength(): Int {
133        return self.lockedNFTs.length
134    }
135    
136    // The path to the MagnetiqLocking Admin resource belonging to the Account
137    // which the contract is deployed on
138    access(all) view fun AdminStoragePath() : StoragePath { return /storage/MagnetiqLockingAdmin}
139
140    // Admin is a special authorization resource that 
141    // allows the owner to override the lock on a moment
142    access(all)
143    resource Admin {
144        // createNewAdmin creates a new Admin resource
145        access(all)
146        fun createNewAdmin(): @Admin {
147            return <-create Admin()
148        }
149
150        // markNFTUnlockable marks a given nft as being
151        // unlockable, overridding the expiry timestamp
152        // the nft owner will still need to send an unlock transaction to unlock
153        access(all)
154        fun markNFTUnlockable(nftRef: &{NonFungibleToken.NFT}) {
155            MagnetiqLocking.unlockableNFTs[nftRef.id] = true
156        }
157
158        access(all) fun unlockByID(id: UInt64) {
159            if !MagnetiqLocking.lockedNFTs.containsKey(id) {
160                // nft is not locked, do nothing
161                return
162            }
163            MagnetiqLocking.lockedNFTs.remove(key: id)
164            emit TokenUnlocked(id: id)
165        }
166
167        // admin may alter the expiry of a lock on an NFT
168        access(all) fun setLockExpiryByID(id: UInt64, expiryTimestamp: UFix64) {
169            if expiryTimestamp < getCurrentBlock().timestamp {
170                panic("cannot set expiry in the past")
171            }
172
173            let duration = expiryTimestamp - getCurrentBlock().timestamp
174
175            MagnetiqLocking.lockedNFTs[id] = expiryTimestamp
176
177            emit TokenLocked(id: id, duration: duration, expiryTimestamp: expiryTimestamp)
178        }
179
180        // unlocks all NFTs
181        access(all)
182        fun unlockAll() {
183            MagnetiqLocking.lockedNFTs = {}
184            MagnetiqLocking.unlockableNFTs = {}
185        }
186    }
187
188    // -----------------------------------------------------------------------
189    // MagnetiqLocking initialization function
190    // -----------------------------------------------------------------------
191    //
192    init() {
193        self.lockedNFTs = {}
194        self.unlockableNFTs = {}
195
196        // Create a single admin resource
197        let admin <- create Admin()
198
199        // Store it in private account storage in `init` so only the admin can use it
200        self.account.storage.save(<-admin, to: MagnetiqLocking.AdminStoragePath())
201        }
202}
203