tx · 5G26X7xFnuNSCgHXBMCV13U336oF6EHSUsRoC3zB8esf

3Mso4SkQZqP8xKHMq7AJJjexAU6L2McSfbx:  -0.01000000 Waves

2022.07.31 02:50 [2163239] smart account 3Mso4SkQZqP8xKHMq7AJJjexAU6L2McSfbx > SELF 0.00000000 Waves

{ "type": 13, "id": "5G26X7xFnuNSCgHXBMCV13U336oF6EHSUsRoC3zB8esf", "fee": 1000000, "feeAssetId": null, "timestamp": 1659225020800, "version": 2, "chainId": 84, "sender": "3Mso4SkQZqP8xKHMq7AJJjexAU6L2McSfbx", "senderPublicKey": "APom8FtYuLDXUGnjz1mwLh3uzsFwxRTQGDwi3LowpCBY", "proofs": [ "45JLAd6yKnSgLdX5gqSFkckspSuuRq2KGjGSTf3FtpEA7MVXEdM8QY7LjhwEGFtmRhdUfQjq5W6wJpz8YX6ybRvr" ], "script": "base64:BgIjCAISAwoBCBIAEgUKAwgIARIECgIIARIAEgUKAwgICBIAEgAPAAV3YXZlcwCAwtcvAA50aWNrZXRQcmljZUludADAhD0AC3RpY2tldFByaWNlBQ50aWNrZXRQcmljZUludAAGaW52RmVlAKDCHgASd2l0aGRyYXdGZWVQZXJjZW50AAUAFmZlZUNvbGxlY3RvckFkZHJlc3NLZXkCE2ZlZUNvbGxlY3RvckFkZHJlc3MBDXRyeUdldEludGVnZXIBA2tleQQHJG1hdGNoMAkAmggCBQR0aGlzBQNrZXkDCQABAgUHJG1hdGNoMAIDSW50BAFiBQckbWF0Y2gwBQFiAAABEmdldFRpY2tldHNDb3VudEtleQENYWRkcmVzc1N0cmluZwkArAICBQ1hZGRyZXNzU3RyaW5nAg1fdGlja2V0c0NvdW50AQ1nYW1lV2lubmVyS2V5AQZnYW1lSWQJAKwCAgUGZ2FtZUlkAgdfd2lubmVyARhhZGRyZXNzQXZhaWxhYmxlRnVuZHNLZXkBB2FkZHJlc3MJAKwCAgUHYWRkcmVzcwIPX2F2YWlsYWJsZUZ1bmRzARphZGRyZXNzVG90YWxMb2NrZWRGdW5kc0tleQEHYWRkcmVzcwkArAICBQdhZGRyZXNzAhFfdG90YWxMb2NrZWRGdW5kcwEZYWRkcmVzc0xvY2tlZEZ1bmRzRm9yR2FtZQIHYWRkcmVzcwZnYW1lSWQJAKwCAgkArAICBQdhZGRyZXNzAg1fbG9ja2VkRnVuZHNfBQZnYW1lSWQBGmFkZHJlc3NBdmFpbGFibGVSZXdhcmRzS2V5AQdhZGRyZXNzCQCsAgIFB2FkZHJlc3MCEV9hdmFpbGFibGVSZXdhcmRzAQ9nZXRUaWNrZXRzQ291bnQBDWFkZHJlc3NTdHJpbmcJAQ10cnlHZXRJbnRlZ2VyAQkBEmdldFRpY2tldHNDb3VudEtleQEFDWFkZHJlc3NTdHJpbmcBDWNvbnN1bWVUaWNrZXQBCnBsYXllckFkZHIEEmN1cnJlbnRUaWNrZXRDb3VudAkBD2dldFRpY2tldHNDb3VudAEFCnBsYXllckFkZHIEFW5ld0N1cnJlbnRUaWNrZXRDb3VudAkAZQIFEmN1cnJlbnRUaWNrZXRDb3VudAABAwkAZgIAAAUVbmV3Q3VycmVudFRpY2tldENvdW50AAAFFW5ld0N1cnJlbnRUaWNrZXRDb3VudAgBaQEWc2V0RmVlQ29sbGVjdG9yQWRkcmVzcwENY29sbGVjdG9yQWRkcgMJAQIhPQIIBQFpBmNhbGxlcgUEdGhpcwkAAgECCkFkbWluIE9ubHkJAMwIAgkBC1N0cmluZ0VudHJ5AgUWZmVlQ29sbGVjdG9yQWRkcmVzc0tleQUNY29sbGVjdG9yQWRkcgUDbmlsAWkBDGRlcG9zaXRGdW5kcwAEA3BtdAMJAAACCQCQAwEIBQFpCHBheW1lbnRzAAEJAJEDAggFAWkIcGF5bWVudHMAAAkAAgECIVBsZWFzZSBhdHRhY2ggZXhhY3RseSBvbmUgcGF5bWVudAMJAQlpc0RlZmluZWQBCAUDcG10B2Fzc2V0SWQJAAIBAhlZb3UgY2FuIG9ubHkgZGVwb3NpdCBVU0ROBA1jYWxsZXJBZGRyU3RyCQDYBAEICAUBaQZjYWxsZXIFYnl0ZXMJAJQKAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBGGFkZHJlc3NBdmFpbGFibGVGdW5kc0tleQEFDWNhbGxlckFkZHJTdHIIBQNwbXQGYW1vdW50BQNuaWwAAQFpAQ5wbGF5ZXJFeGl0R2FtZQMGZ2FtZUlkCnBsYXllckFkZHIPZXhpdFN0YWNrQW1vdW50AwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQIKQWRtaW4gT25seQQXY3VycmVudFRvdGFsTG9ja2VkRnVuZHMJAQ10cnlHZXRJbnRlZ2VyAQkBGmFkZHJlc3NUb3RhbExvY2tlZEZ1bmRzS2V5AQUKcGxheWVyQWRkcgQVY3VycmVudEF2YWlsYWJsZUZ1bmRzCQENdHJ5R2V0SW50ZWdlcgEJARhhZGRyZXNzQXZhaWxhYmxlRnVuZHNLZXkBBQpwbGF5ZXJBZGRyBCBjdXJyZW50R2FtZUxvY2tlZEFtb3VudEZvclBsYXllcgkBDXRyeUdldEludGVnZXIBCQEZYWRkcmVzc0xvY2tlZEZ1bmRzRm9yR2FtZQIFCnBsYXllckFkZHIFBmdhbWVJZAkAlAoCCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEYYWRkcmVzc0F2YWlsYWJsZUZ1bmRzS2V5AQUKcGxheWVyQWRkcgkAZAIFFWN1cnJlbnRBdmFpbGFibGVGdW5kcwUPZXhpdFN0YWNrQW1vdW50CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEaYWRkcmVzc1RvdGFsTG9ja2VkRnVuZHNLZXkBBQpwbGF5ZXJBZGRyCQBlAgUXY3VycmVudFRvdGFsTG9ja2VkRnVuZHMFIGN1cnJlbnRHYW1lTG9ja2VkQW1vdW50Rm9yUGxheWVyBQNuaWwAAQFpARBsb2NrRnVuZHNGb3JHYW1lAgZnYW1lSWQMYW1vdW50VG9Mb2NrBA1jYWxsZXJBZGRyU3RyCQDYBAEICAUBaQZjYWxsZXIFYnl0ZXMEFWN1cnJlbnRBdmFpbGFibGVGdW5kcwkBDXRyeUdldEludGVnZXIBCQEYYWRkcmVzc0F2YWlsYWJsZUZ1bmRzS2V5AQUNY2FsbGVyQWRkclN0cgQXY3VycmVudFRvdGFsTG9ja2VkRnVuZHMJAQ10cnlHZXRJbnRlZ2VyAQkBGmFkZHJlc3NUb3RhbExvY2tlZEZ1bmRzS2V5AQUNY2FsbGVyQWRkclN0cgMJAGYCBQxhbW91bnRUb0xvY2sFFWN1cnJlbnRBdmFpbGFibGVGdW5kcwkAAgECIk5vdCBlbm91Z2ggYXZhaWxhYmxlIGZ1bmRzIHRvIGxvY2sJAJQKAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBGGFkZHJlc3NBdmFpbGFibGVGdW5kc0tleQEFDWNhbGxlckFkZHJTdHIJAGUCBRVjdXJyZW50QXZhaWxhYmxlRnVuZHMFDGFtb3VudFRvTG9jawkAzAgCCQEMSW50ZWdlckVudHJ5AgkBGmFkZHJlc3NUb3RhbExvY2tlZEZ1bmRzS2V5AQUNY2FsbGVyQWRkclN0cgkAZAIFF2N1cnJlbnRUb3RhbExvY2tlZEZ1bmRzBQxhbW91bnRUb0xvY2sJAMwIAgkBDEludGVnZXJFbnRyeQIJARlhZGRyZXNzTG9ja2VkRnVuZHNGb3JHYW1lAgUNY2FsbGVyQWRkclN0cgUGZ2FtZUlkBQxhbW91bnRUb0xvY2sFA25pbAABAWkBDXdpdGhkcmF3RnVuZHMABA1jYWxsZXJBZGRyU3RyCQDYBAEICAUBaQZjYWxsZXIFYnl0ZXMEEGZlZUNvbGxlY3RvckFkZHIJARFAZXh0ck5hdGl2ZSgxMDYyKQEJARFAZXh0ck5hdGl2ZSgxMDU4KQEFFmZlZUNvbGxlY3RvckFkZHJlc3NLZXkEFWN1cnJlbnRBdmFpbGFibGVGdW5kcwkBDXRyeUdldEludGVnZXIBCQEYYWRkcmVzc0F2YWlsYWJsZUZ1bmRzS2V5AQUNY2FsbGVyQWRkclN0cgQLd2l0aGRyYXdGZWUJAGsDBRVjdXJyZW50QXZhaWxhYmxlRnVuZHMFEndpdGhkcmF3RmVlUGVyY2VudABkBBRhY3R1YWxBdmFpbGFibGVGdW5kcwkAZQIFFWN1cnJlbnRBdmFpbGFibGVGdW5kcwULd2l0aGRyYXdGZWUDCQBnAgAABRRhY3R1YWxBdmFpbGFibGVGdW5kcwkAAgECHk5vIGF2YWlsYWJsZSBmdW5kcyB0byB3aXRoZHJhdwkAzAgCCQEMSW50ZWdlckVudHJ5AgkBGGFkZHJlc3NBdmFpbGFibGVGdW5kc0tleQEFDWNhbGxlckFkZHJTdHIAAAkAzAgCCQEOU2NyaXB0VHJhbnNmZXIDCAUBaQZjYWxsZXIFFGFjdHVhbEF2YWlsYWJsZUZ1bmRzBQR1bml0CQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMFEGZlZUNvbGxlY3RvckFkZHIFC3dpdGhkcmF3RmVlBQR1bml0BQNuaWwBaQEQc2V0V2lubmVySGVhZHNVcAMGZ2FtZUlkDXdpbm5lckFkZHJTdHIPb3RoZXJQbGF5ZXJBZGRyAwkBAiE9AggFAWkGY2FsbGVyBQR0aGlzCQACAQIKQWRtaW4gT25seQQTYXZhaWxhYmxlUmV3YXJkc0tleQkBGmFkZHJlc3NBdmFpbGFibGVSZXdhcmRzS2V5AQUNd2lubmVyQWRkclN0cgQXY3VycmVudEF2YWlsYWJsZVJld2FyZHMJAQ10cnlHZXRJbnRlZ2VyAQUTYXZhaWxhYmxlUmV3YXJkc0tleQQUbmV3VGlja2V0Q291bnRXaW5uZXIJAQ1jb25zdW1lVGlja2V0AQUNd2lubmVyQWRkclN0cgQZbmV3VGlja2V0Q291bnRPdGhlclBsYXllcgkBDWNvbnN1bWVUaWNrZXQBBQ9vdGhlclBsYXllckFkZHIJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDWdhbWVXaW5uZXJLZXkBBQZnYW1lSWQFDXdpbm5lckFkZHJTdHIJAMwIAgkBDEludGVnZXJFbnRyeQIFE2F2YWlsYWJsZVJld2FyZHNLZXkJAGQCBRdjdXJyZW50QXZhaWxhYmxlUmV3YXJkcwkAaAIFC3RpY2tldFByaWNlAAIJAMwIAgkBDEludGVnZXJFbnRyeQIJARJnZXRUaWNrZXRzQ291bnRLZXkBBQ13aW5uZXJBZGRyU3RyBRRuZXdUaWNrZXRDb3VudFdpbm5lcgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmdldFRpY2tldHNDb3VudEtleQEFD290aGVyUGxheWVyQWRkcgUZbmV3VGlja2V0Q291bnRPdGhlclBsYXllcgUDbmlsAWkBD3dpdGhkcmF3UmV3YXJkcwAEDWNhbGxlckFkZHJTdHIJANgEAQgIBQFpBmNhbGxlcgVieXRlcwQQZmVlQ29sbGVjdG9yQWRkcgkBEUBleHRyTmF0aXZlKDEwNjIpAQkBEUBleHRyTmF0aXZlKDEwNTgpAQUWZmVlQ29sbGVjdG9yQWRkcmVzc0tleQQXY3VycmVudEF2YWlsYWJsZVJld2FyZHMJAQ10cnlHZXRJbnRlZ2VyAQkBGmFkZHJlc3NBdmFpbGFibGVSZXdhcmRzS2V5AQUNY2FsbGVyQWRkclN0cgQLd2l0aGRyYXdGZWUJAGsDBRdjdXJyZW50QXZhaWxhYmxlUmV3YXJkcwUSd2l0aGRyYXdGZWVQZXJjZW50AGQEFmFjdHVhbEF2YWlsYWJsZVJld2FyZHMJAGUCCQBlAgUXY3VycmVudEF2YWlsYWJsZVJld2FyZHMFBmludkZlZQULd2l0aGRyYXdGZWUDCQBnAgAABRdjdXJyZW50QXZhaWxhYmxlUmV3YXJkcwkAAgECIE5vIGF2YWlsYWJsZSByZXdhcmRzIHRvIHdpdGhkcmF3CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEaYWRkcmVzc0F2YWlsYWJsZVJld2FyZHNLZXkBBQ1jYWxsZXJBZGRyU3RyAAAJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwgFAWkGY2FsbGVyBRZhY3R1YWxBdmFpbGFibGVSZXdhcmRzBQR1bml0CQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMFEGZlZUNvbGxlY3RvckFkZHIFC3dpdGhkcmF3RmVlBQR1bml0BQNuaWwBaQEKYnV5VGlja2V0cwAEA3BtdAMJAAACCQCQAwEIBQFpCHBheW1lbnRzAAEJAJEDAggFAWkIcGF5bWVudHMAAAkAAgECIVBsZWFzZSBhdHRhY2ggZXhhY3RseSBvbmUgcGF5bWVudAMJAQlpc0RlZmluZWQBCAUDcG10B2Fzc2V0SWQJAAIBAidUaWNrZXRzIGNhbiBvbmx5IGliZSBwdXJjaGFzZWQgaW4gV2F2ZXMDCQECIT0CCAUDcG10BmFtb3VudAULdGlja2V0UHJpY2UJAAIBCQCsAgIJAKwCAgIfVGhlIHBheW1lbnQgbXVzdCBiZSBvZiBleGFjdGx5IAkApAMBBQ50aWNrZXRQcmljZUludAIGIHdhdmVzBA1jYWxsZXJBZGRyU3RyCQDYBAEICAUBaQZjYWxsZXIFYnl0ZXMEE2N1cnJlbnRUaWNrZXRzQ291bnQJAQ10cnlHZXRJbnRlZ2VyAQkBEmdldFRpY2tldHNDb3VudEtleQEFDWNhbGxlckFkZHJTdHIJAJQKAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmdldFRpY2tldHNDb3VudEtleQEFDWNhbGxlckFkZHJTdHIJAGQCBRNjdXJyZW50VGlja2V0c0NvdW50AAEFA25pbAABAQJ0eAEGdmVyaWZ5AAkA9AMDCAUCdHgJYm9keUJ5dGVzCQCRAwIIBQJ0eAZwcm9vZnMAAAgFAnR4D3NlbmRlclB1YmxpY0tleZjeCqA=", "height": 2163239, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: none Next: none Full:
OldNewDifferences
1-# no script
1+{-# STDLIB_VERSION 6 #-}
2+{-# SCRIPT_TYPE ACCOUNT #-}
3+{-# CONTENT_TYPE DAPP #-}
4+let waves = 100000000
5+
6+let ticketPriceInt = 1000000
7+
8+let ticketPrice = ticketPriceInt
9+
10+let invFee = 500000
11+
12+let withdrawFeePercent = 5
13+
14+let feeCollectorAddressKey = "feeCollectorAddress"
15+
16+func tryGetInteger (key) = match getInteger(this, key) {
17+ case b: Int =>
18+ b
19+ case _ =>
20+ 0
21+}
22+
23+
24+func getTicketsCountKey (addressString) = (addressString + "_ticketsCount")
25+
26+
27+func gameWinnerKey (gameId) = (gameId + "_winner")
28+
29+
30+func addressAvailableFundsKey (address) = (address + "_availableFunds")
31+
32+
33+func addressTotalLockedFundsKey (address) = (address + "_totalLockedFunds")
34+
35+
36+func addressLockedFundsForGame (address,gameId) = ((address + "_lockedFunds_") + gameId)
37+
38+
39+func addressAvailableRewardsKey (address) = (address + "_availableRewards")
40+
41+
42+func getTicketsCount (addressString) = tryGetInteger(getTicketsCountKey(addressString))
43+
44+
45+func consumeTicket (playerAddr) = {
46+ let currentTicketCount = getTicketsCount(playerAddr)
47+ let newCurrentTicketCount = (currentTicketCount - 1)
48+ if ((0 > newCurrentTicketCount))
49+ then 0
50+ else newCurrentTicketCount
51+ }
52+
53+
54+@Callable(i)
55+func setFeeCollectorAddress (collectorAddr) = if ((i.caller != this))
56+ then throw("Admin Only")
57+ else [StringEntry(feeCollectorAddressKey, collectorAddr)]
58+
59+
60+
61+@Callable(i)
62+func depositFunds () = {
63+ let pmt = if ((size(i.payments) == 1))
64+ then i.payments[0]
65+ else throw("Please attach exactly one payment")
66+ if (isDefined(pmt.assetId))
67+ then throw("You can only deposit USDN")
68+ else {
69+ let callerAddrStr = toBase58String(i.caller.bytes)
70+ $Tuple2([IntegerEntry(addressAvailableFundsKey(callerAddrStr), pmt.amount)], 1)
71+ }
72+ }
73+
74+
75+
76+@Callable(i)
77+func playerExitGame (gameId,playerAddr,exitStackAmount) = if ((i.caller != this))
78+ then throw("Admin Only")
79+ else {
80+ let currentTotalLockedFunds = tryGetInteger(addressTotalLockedFundsKey(playerAddr))
81+ let currentAvailableFunds = tryGetInteger(addressAvailableFundsKey(playerAddr))
82+ let currentGameLockedAmountForPlayer = tryGetInteger(addressLockedFundsForGame(playerAddr, gameId))
83+ $Tuple2([IntegerEntry(addressAvailableFundsKey(playerAddr), (currentAvailableFunds + exitStackAmount)), IntegerEntry(addressTotalLockedFundsKey(playerAddr), (currentTotalLockedFunds - currentGameLockedAmountForPlayer))], 1)
84+ }
85+
86+
87+
88+@Callable(i)
89+func lockFundsForGame (gameId,amountToLock) = {
90+ let callerAddrStr = toBase58String(i.caller.bytes)
91+ let currentAvailableFunds = tryGetInteger(addressAvailableFundsKey(callerAddrStr))
92+ let currentTotalLockedFunds = tryGetInteger(addressTotalLockedFundsKey(callerAddrStr))
93+ if ((amountToLock > currentAvailableFunds))
94+ then throw("Not enough available funds to lock")
95+ else $Tuple2([IntegerEntry(addressAvailableFundsKey(callerAddrStr), (currentAvailableFunds - amountToLock)), IntegerEntry(addressTotalLockedFundsKey(callerAddrStr), (currentTotalLockedFunds + amountToLock)), IntegerEntry(addressLockedFundsForGame(callerAddrStr, gameId), amountToLock)], 1)
96+ }
97+
98+
99+
100+@Callable(i)
101+func withdrawFunds () = {
102+ let callerAddrStr = toBase58String(i.caller.bytes)
103+ let feeCollectorAddr = addressFromStringValue(getStringValue(feeCollectorAddressKey))
104+ let currentAvailableFunds = tryGetInteger(addressAvailableFundsKey(callerAddrStr))
105+ let withdrawFee = fraction(currentAvailableFunds, withdrawFeePercent, 100)
106+ let actualAvailableFunds = (currentAvailableFunds - withdrawFee)
107+ if ((0 >= actualAvailableFunds))
108+ then throw("No available funds to withdraw")
109+ else [IntegerEntry(addressAvailableFundsKey(callerAddrStr), 0), ScriptTransfer(i.caller, actualAvailableFunds, unit), ScriptTransfer(feeCollectorAddr, withdrawFee, unit)]
110+ }
111+
112+
113+
114+@Callable(i)
115+func setWinnerHeadsUp (gameId,winnerAddrStr,otherPlayerAddr) = if ((i.caller != this))
116+ then throw("Admin Only")
117+ else {
118+ let availableRewardsKey = addressAvailableRewardsKey(winnerAddrStr)
119+ let currentAvailableRewards = tryGetInteger(availableRewardsKey)
120+ let newTicketCountWinner = consumeTicket(winnerAddrStr)
121+ let newTicketCountOtherPlayer = consumeTicket(otherPlayerAddr)
122+[StringEntry(gameWinnerKey(gameId), winnerAddrStr), IntegerEntry(availableRewardsKey, (currentAvailableRewards + (ticketPrice * 2))), IntegerEntry(getTicketsCountKey(winnerAddrStr), newTicketCountWinner), IntegerEntry(getTicketsCountKey(otherPlayerAddr), newTicketCountOtherPlayer)]
123+ }
124+
125+
126+
127+@Callable(i)
128+func withdrawRewards () = {
129+ let callerAddrStr = toBase58String(i.caller.bytes)
130+ let feeCollectorAddr = addressFromStringValue(getStringValue(feeCollectorAddressKey))
131+ let currentAvailableRewards = tryGetInteger(addressAvailableRewardsKey(callerAddrStr))
132+ let withdrawFee = fraction(currentAvailableRewards, withdrawFeePercent, 100)
133+ let actualAvailableRewards = ((currentAvailableRewards - invFee) - withdrawFee)
134+ if ((0 >= currentAvailableRewards))
135+ then throw("No available rewards to withdraw")
136+ else [IntegerEntry(addressAvailableRewardsKey(callerAddrStr), 0), ScriptTransfer(i.caller, actualAvailableRewards, unit), ScriptTransfer(feeCollectorAddr, withdrawFee, unit)]
137+ }
138+
139+
140+
141+@Callable(i)
142+func buyTickets () = {
143+ let pmt = if ((size(i.payments) == 1))
144+ then i.payments[0]
145+ else throw("Please attach exactly one payment")
146+ if (isDefined(pmt.assetId))
147+ then throw("Tickets can only ibe purchased in Waves")
148+ else if ((pmt.amount != ticketPrice))
149+ then throw((("The payment must be of exactly " + toString(ticketPriceInt)) + " waves"))
150+ else {
151+ let callerAddrStr = toBase58String(i.caller.bytes)
152+ let currentTicketsCount = tryGetInteger(getTicketsCountKey(callerAddrStr))
153+ $Tuple2([IntegerEntry(getTicketsCountKey(callerAddrStr), (currentTicketsCount + 1))], 1)
154+ }
155+ }
156+
157+
158+@Verifier(tx)
159+func verify () = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
160+

github/deemru/w8io/873ac7e 
26.56 ms