Smart Contract

MatchContract

A.a10dde51240c1ec7.MatchContract

Valid From

85,498,115

Deployed

3d ago
Feb 25, 2026, 03:48:49 PM UTC

Dependents

0 imports
1import Crypto
2
3pub contract MatchContract {
4
5    pub let AdminStoragePath: StoragePath
6
7    pub let waitingIndices: [UInt32]
8    pub let matchedIndices: [UInt32]
9    pub let finalizeIndices: [UInt32]
10
11    priv var registerActive: Bool
12    priv var matchActive: Bool
13
14    // latest not used yet index
15    priv var nextIndex: UInt32
16
17    // if using pub let other can still modify by such transaction
18    // execute {
19    //   MatchContract.indexAddressMap[0]?.remove(key: MatchContract.MatchRole.host)
20    // }
21    access(account) let addressGroupMap: { String: { MatchStatus: [UInt32] } }
22    access(account) let indexAddressMap: { UInt32: { MatchRole: Address } }
23
24    // Events
25    pub event registered(
26        host: Address,
27        index: UInt32
28    )
29    
30    pub event matched(
31        host: Address,
32        challenger: Address,
33        index: UInt32
34    )
35
36    pub resource Admin {
37        pub fun setActivateRegistration(_ active: Bool) {
38            MatchContract.registerActive = active
39        }
40
41        pub fun setActivateMatching(_ active: Bool) {
42            MatchContract.matchActive = active
43        }
44    }
45
46    pub init() {
47        self.registerActive = false
48        self.matchActive = false
49        self.nextIndex = 0
50        self.addressGroupMap = {}
51        self.indexAddressMap = {}
52        self.waitingIndices = []
53        self.matchedIndices = []
54        self.finalizeIndices = []
55        self.AdminStoragePath = /storage/matchAdminV1
56        let admin <- create Admin()
57        self.account.save(<- admin, to: self.AdminStoragePath)
58    }
59
60    // Script
61
62    // Return oldest waiting index as well as first index of waiting group of specific address.
63    pub fun getFirstWaitingIndex(hostAddress: Address): UInt32? {
64        let key = hostAddress.toString().toLower()
65        let matchGroups = self.addressGroupMap[key] ?? {}
66        let waitingGroup = matchGroups[MatchStatus.waiting] ?? []
67        if waitingGroup.length > 0 {
68            return waitingGroup[0]
69        } else {
70            return nil
71        }
72    }
73
74    // Return oldest waiting index as well as first index of waitingIndices.
75    pub fun getRandomWaitingIndex(): UInt32? {
76        if self.waitingIndices.length > 0 {
77            var iterationIndex = 0
78            for waitingIndex in self.waitingIndices {
79                assert(self.indexAddressMap.keys.contains(waitingIndex), message: "IndexAddressMap should contain index ".concat(waitingIndex.toString()))
80                if let addressGroups = self.indexAddressMap[waitingIndex] {
81                    if addressGroups[MatchRole.challenger] == nil {
82                        return waitingIndex
83                    } else {
84                        continue
85                    }
86                }
87            }
88            return nil
89        } else {
90            return nil
91        }
92    }
93
94    pub fun getNextIndex(): UInt32 {
95        return self.nextIndex
96    }
97
98    pub fun getWaiting(by address: Address): [UInt32] {
99        let key = address.toString().toLower()
100        let addressGroup = self.addressGroupMap[key] ?? {}
101        return addressGroup[MatchStatus.waiting] ?? []
102    }
103
104    pub fun getMatched(by address: Address): [UInt32] {
105        let key = address.toString().toLower()
106        let addressGroup = self.addressGroupMap[key] ?? {}
107        return addressGroup[MatchStatus.matched] ?? []
108    }
109
110    pub fun getFinished(by address: Address): [UInt32] {
111        let key = address.toString().toLower()
112        let addressGroup = self.addressGroupMap[key] ?? {}
113        return addressGroup[MatchStatus.finished] ?? []
114    }
115
116    pub fun getHostAddress(by index: UInt32): Address? {
117        let roleAddressMap = self.indexAddressMap[index] ?? {}
118        return roleAddressMap[MatchRole.host]
119    }
120
121    pub fun getChallengerAddress(by index: UInt32): Address? {
122        let roleAddressMap = self.indexAddressMap[index] ?? {}
123        return roleAddressMap[MatchRole.challenger]
124    }
125
126    // Transaction
127
128    // Register a waiting match.
129    pub fun register(host: Address): UInt32 {
130        pre {
131            self.registerActive: "Registration is not active."
132        }
133        var currentIndex = self.nextIndex
134
135        let key = host.toString().toLower()
136
137        let matchGroups = self.addressGroupMap[key] ?? {}
138        let waitingGroup: [UInt32] = matchGroups[MatchStatus.waiting] ?? []
139
140        waitingGroup.append(currentIndex)
141        matchGroups[MatchStatus.waiting] = waitingGroup
142        self.addressGroupMap[key] = matchGroups
143
144        self.indexAddressMap[currentIndex] = { MatchRole.host: host }
145        self.waitingIndices.append(currentIndex)
146
147        if currentIndex == UInt32.max {
148            // Indices is using out.
149            self.registerActive = false
150        } else {
151            self.nextIndex = currentIndex + 1
152        }
153
154        emit registered(
155            host: host,
156            index: currentIndex
157        )
158
159        return currentIndex
160    }
161
162    // Must match with specific index in case host register slightly before.
163    access(account) fun match(
164        index: UInt32,
165        challengerAddress: Address
166    ): Address? {
167        pre {
168            self.matchActive: "Matching is not active."
169            self.indexAddressMap.keys.contains(index): "Index not found in indexAddressMap"
170        }
171        let roleAddressMap = self.indexAddressMap[index] ?? {}
172        assert(roleAddressMap[MatchRole.host] != nil, message: "Host not found for this index.")
173        assert(roleAddressMap[MatchRole.challenger] == nil, message: "Challenger already exist.")
174        roleAddressMap[MatchRole.challenger] = challengerAddress
175        self.indexAddressMap[index] = roleAddressMap
176
177        let hostAddress = roleAddressMap[MatchRole.host]!
178        let hostKey = hostAddress.toString().toLower()
179        let addressGroup = self.addressGroupMap[hostKey] ?? {}
180        let waitingGroup: [UInt32] = addressGroup[MatchStatus.waiting] ?? []
181        assert(waitingGroup.length > 0, message: hostAddress.toString().concat("'s waiting group length should over 0"))
182
183        if let firstIndex: Int = waitingGroup.firstIndex(of: index) {
184            let matchIndex = waitingGroup.remove(at: firstIndex)
185            addressGroup[MatchStatus.waiting] = waitingGroup
186            assert(matchIndex == index, message: "Match index not equal.")
187            let matchedGroup: [UInt32] = addressGroup[MatchStatus.matched] ?? []
188            matchedGroup.append(matchIndex)
189            addressGroup[MatchStatus.matched] = matchedGroup
190            self.addressGroupMap[hostKey] = addressGroup
191
192            let challengerAddress = challengerAddress
193            let challengerKey = challengerAddress.toString().toLower()
194            let challengerAddressGroup = self.addressGroupMap[challengerKey] ?? {}
195            let indices = challengerAddressGroup[MatchStatus.matched] ?? []
196            indices.append(index)
197            challengerAddressGroup[MatchStatus.matched] = indices
198            self.addressGroupMap[challengerKey] = challengerAddressGroup
199
200            assert(self.waitingIndices.contains(matchIndex), message: "WaitingIndices should include ".concat(matchIndex.toString()).concat(" before matched."))
201            assert(self.matchedIndices.contains(matchIndex) == false, message: "MatchedIndices should not include ".concat(matchIndex.toString()).concat(" before matched."))
202            if let waitingIndex = self.waitingIndices.firstIndex(of: matchIndex) {
203                let matchedIndex = self.waitingIndices.remove(at: waitingIndex)
204                self.matchedIndices.append(matchedIndex)
205                assert(self.waitingIndices.contains(matchIndex) == false, message: "WaitingIndices should not include ".concat(matchIndex.toString()).concat(" after matched."))
206                assert(self.matchedIndices.contains(matchIndex), message: "MatchedIndices should include ".concat(matchIndex.toString()).concat(" after matched."))
207
208                emit matched(
209                    host: hostAddress,
210                    challenger: challengerAddress,
211                    index: index
212                )
213        
214                return hostAddress
215            } else {
216                panic("MatchIndex ".concat(matchIndex.toString()).concat(" should be found in waitingIndices"))
217            }
218        } else {
219            panic(hostAddress.toString().concat(" not contain index: ").concat(index.toString()))
220        }
221        return nil
222    }
223
224    access(account) fun finish(index: UInt32) {
225        pre {
226            self.finalizeIndices.contains(index) == false: "Index already finished."
227            self.matchedIndices.firstIndex(of: index) != nil: "Index not exist in matchedIndices."
228        }
229        post {
230            self.finalizeIndices.contains(index): "Finish failed."
231        }
232
233        let indexOfMatched = self.matchedIndices.firstIndex(of: index)!
234        let finishedIndex = self.matchedIndices.remove(at: indexOfMatched)
235        assert(finishedIndex == index, message: "Finish failed.")
236        self.finalizeIndices.append(index)
237
238        let roleAddressMap = self.indexAddressMap[index] ?? {}
239        assert(roleAddressMap[MatchRole.host] != nil, message: "Host not found for this index.")
240        assert(roleAddressMap[MatchRole.challenger] != nil, message: "Challenger already exist.")
241        let hostAddress = roleAddressMap[MatchRole.host]!
242        let challengerAddress = roleAddressMap[MatchRole.challenger]!
243
244        let hostKey = hostAddress.toString().toLower()
245        let challengerKey = challengerAddress.toString().toLower()
246        self.moveMatchedToFinished(for: index, key: hostKey)
247        self.moveMatchedToFinished(for: index, key: challengerKey)
248    }
249
250    priv fun moveMatchedToFinished(for index: UInt32, key: String) {
251        assert(self.addressGroupMap.keys.contains(key), message: "Address key not found for this index.")
252
253        let addressGroup = self.addressGroupMap[key] ?? {}
254        let matchedGroup: [UInt32] = addressGroup[MatchStatus.matched] ?? []
255        assert(matchedGroup.contains(index), message: "Index not found in matchedGroup.")
256        let indexOfMatchedGroup = matchedGroup.firstIndex(of: index)!
257        let removedIndex = matchedGroup.remove(at: indexOfMatchedGroup)
258        assert(removedIndex == index, message: "Finish failed.")
259        addressGroup[MatchStatus.matched] = matchedGroup
260
261        let finishedGroup: [UInt32] = addressGroup[MatchStatus.finished] ?? []
262        finishedGroup.append(index)
263        addressGroup[MatchStatus.finished] = finishedGroup
264
265        self.addressGroupMap[key] = addressGroup
266    }
267
268    pub enum MatchStatus: UInt8 {
269        pub case waiting
270        pub case matched
271        pub case finished
272    }
273
274    pub enum MatchRole: UInt8 {
275        pub case host
276        pub case challenger
277    }
278
279}