tx · FCk9jqnhrbfpy4RAE5C7kahBzZGftQw7rKWw6f78ZH27 3Mt6NEJ6ZK6TFZs14tvnR5iAgemnomy2m1V: -0.01700000 Waves 2022.09.13 12:06 [2227124] smart account 3Mt6NEJ6ZK6TFZs14tvnR5iAgemnomy2m1V > SELF 0.00000000 Waves
{ "type": 13, "id": "FCk9jqnhrbfpy4RAE5C7kahBzZGftQw7rKWw6f78ZH27", "fee": 1700000, "feeAssetId": null, "timestamp": 1663059992532, "version": 2, "chainId": 84, "sender": "3Mt6NEJ6ZK6TFZs14tvnR5iAgemnomy2m1V", "senderPublicKey": "5f9EawB8nxaLg7KKhyqV2RDBMwRBG6o69D8zc9myAzrY", "proofs": [ "2Q3bNvzYfk3NWwtA44CtWCxjHke1wa6MmtkcpGKygQu5ihkriWjfQEwtZjtLLLQVFUAzUfjFBtto77QqEFXLph1c" ], "script": "base64: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AAAAAAMJAARMAAAAAgAAAAAAAAAABgkABEwAAAACAAAAAAAAAAAJCQAETAAAAAIAAAAAAAAAAAwJAARMAAAAAgAAAAAAAAAADwkABEwAAAACAAAAAAAAAAASCQAETAAAAAIAAAAAAAAAABUJAARMAAAAAgAAAAAAAAAAGAkABEwAAAACAAAAAAAAAAAbCQAETAAAAAIAAAAAAAAAAB4JAARMAAAAAgAAAAAAAAAAIQkABEwAAAACAAAAAAAAAAAkBQAAAANuaWwAAAAAE2dhbWVNYXN0ZXJQdWJsaWNLZXkJAAJZAAAAAQIAAAAYPGdhbWUtbWFzdGVyLXB1YmxpYy1rZXk+AAAAABBnYW1lQ291bnRlckxhYmVsAgAAAA5HX0dBTUVTQ09VTlRFUgAAAAASZ2FtZVRvdGFsQmV0c0xhYmVsAgAAAAtHX1RPVEFMQkVUUwAAAAAWZ2FtZVByb2Nlc3NlZEJldHNMYWJlbAIAAAAPR19QUk9DRVNTRURCRVRTAAAAAA9nYW1lTWluQmV0TGFiZWwCAAAACEdfTUlOQkVUAAAAAA9nYW1lTWF4QmV0TGFiZWwCAAAACEdfTUFYQkVUAAAAABJnYW1lUlNBUHVibGljTGFiZWwCAAAAC0dfUlNBUFVCTElDAAAAABhnYW1lTWFzdGVyUHVibGljS2V5TGFiZWwCAAAAF0dfR0FNRU1BU1RFUl9QVUJMSUNfS0VZAAAAAA5nYW1lVG9rZW5MYWJlbAIAAAAJR19UT0tFTklEAQAAAA9nYW1lc0dldENvdW50ZXIAAAAACQAEGgAAAAIFAAAABHRoaXMFAAAAEGdhbWVDb3VudGVyTGFiZWwBAAAADmdhbWVzVG90YWxCZXRzAAAAAAkABBoAAAACBQAAAAR0aGlzBQAAABJnYW1lVG90YWxCZXRzTGFiZWwBAAAAEmdhbWVzUHJvY2Vzc2VkQmV0cwAAAAAJAAQaAAAAAgUAAAAEdGhpcwUAAAAWZ2FtZVByb2Nlc3NlZEJldHNMYWJlbAEAAAALZ2FtZXNNaW5CZXQAAAAACQEAAAARQGV4dHJOYXRpdmUoMTA1MCkAAAACBQAAAAR0aGlzBQAAAA9nYW1lTWluQmV0TGFiZWwBAAAAC2dhbWVzTWF4QmV0AAAAAAkBAAAAEUBleHRyTmF0aXZlKDEwNTApAAAA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dGNoMQkBAAAADGdhbWVHZXRTdGF0ZQAAAAEFAAAADEdBTUVTQ09VTlRFUgMJAAABAAAAAgUAAAAHJG1hdGNoMQIAAAADSW50BAAAAAlnYW1lU3RhdGUFAAAAByRtYXRjaDEDCQAAAAAAAAIFAAAACWdhbWVTdGF0ZQAAAAAAAAAAAAkAAAIAAAABAgAAAB9MYXN0IGdhbWUgaGFzIG5vdCBmaW5pc2hlZCB5ZXQuCQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACBQAAABBnYW1lQ291bnRlckxhYmVsBQAAAA5uZXdHYW1lQ291bnRlcgkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgkBAAAAFGdhbWVTdGFydEhlaWdodExhYmVsAAAAAQUAAAAObmV3R2FtZUNvdW50ZXIIBQAAAAlsYXN0QmxvY2sAAAAGaGVpZ2h0CQAETAAAAAIJAQAAAAxJbnRlZ2VyRW50cnkAAAACCQEAAAASZ2FtZUVuZEhlaWdodExhYmVsAAAAAQUAAAAObmV3R2FtZUNvdW50ZXIJAABkAAAAAgUAAAAGaGVpZ2h0BQAAAAxibG9ja0luUm91bmQJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAAA5nYW1lU3RhdGVMYWJlbAAAAAEFAAAADm5ld0dhbWVDb3VudGVyAAAAAAAAAAAACQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIJAQAAAA9nYW1lU3VtU2hhTGFiZWwAAAABBQAAAA5uZXdHYW1lQ291bnRlcgkAAlgAAAABCQAB9wAAAAEJAAJbAAAAAQUAAAAKcmFuZG9tSGFzaAUAAAADbmlsCQAAAgAAAAECAAAAFUdhbWUgc3RhdGUgbm90IGZvdW5kLgkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAAQZ2FtZUNvdW50ZXJMYWJlbAUAAAAObmV3R2FtZUNvdW50ZXIJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIJAQAAABRnYW1lU3RhcnRIZWlnaHRMYWJlbAAAAAEFAAAADm5ld0dhbWVDb3VudGVyCAUAAAAJbGFzdEJsb2NrAAAABmhlaWdodAkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAA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IG5vdCBleGlzdCBvciBoYXMgYWxyZWFkeSBlbmRlZAAAAAFpAQAAAAxzZXRQYXJhbWV0ZXIAAAACAAAADXBhcmFtZXRlck5hbWUAAAAOcGFyYW1ldGVyVmFsdWUDCQEAAAAPY2hlY2tQZXJtaXNzaW9uAAAAAQgFAAAAAWkAAAAPY2FsbGVyUHVibGljS2V5CQAAAgAAAAECAAAAFnNldFBhcmFtZXRlcl9GT1JCQklERU4DCQAAAAAAAAIFAAAADXBhcmFtZXRlck5hbWUCAAAACWNoYW5nZU1heAQAAAAMbWF4aW11bVZhbHVlCQEAAAANcGFyc2VJbnRWYWx1ZQAAAAEFAAAADnBhcmFtZXRlclZhbHVlBAAAAAxtaW5pbXVtVmFsdWUJAQAAAAtnYW1lc01pbkJldAAAAAADCQAAZgAAAAIAAAAAAAAAAAAFAAAADG1heGltdW1WYWx1ZQkAAAIAAAABAgAAABx2YWx1ZSBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwAwkAAGcAAAACBQAAAAxtaW5pbXVtVmFsdWUFAAAADG1heGltdW1WYWx1ZQkAAAIAAAABCQABLAAAAAIJAAEsAAAAAgIAAAAtdmFsdWUgY2Fubm90IGJlIGxvd2VyIG9yIGVxdWFsIHRoYW4gbWluIGJldCAoCQABpAAAAAEFAAAADG1pbmltdW1WYWx1ZQIAAAABKQkABEwAAAACCQEAAAAMSW50ZWdlckVudHJ5AAAAAgUAAAAPZ2FtZU1heEJldExhYmVsBQAAAAxtYXhpbXVtVmFsdWUFAAAAA25pbAMJAAAAAAAAAgUAAAANcGFyYW1ldGVyTmFtZQIAAAAJY2hhbmdlTWluBAAAAAxtYXhpbXVtVmFsdWUJAQAAAAtnYW1lc01heEJldAAAAAAEAAAADG1pbmltdW1WYWx1ZQkBAAAADXBhcnNlSW50VmFsdWUAAAABBQAAAA5wYXJhbWV0ZXJWYWx1ZQMJAABmAAAAAgAAAAAAAAAAAAUAAAAMbWluaW11bVZhbHVlCQAAAgAAAAECAAAAHHZhbHVlIGNhbm5vdCBiZSBsb3dlciB0aGFuIDADCQAAZwAAAAIFAAAADG1pbmltdW1WYWx1ZQUAAAAMbWF4aW11bVZhbHVlCQAAAgAAAAEJAAEsAAAAAgkAASwAAAACAgAAAC92YWx1ZSBjYW5ub3QgYmUgZ3JlYXRlciBvciBlcXVhbCB0aGFuIG1pbiBiZXQgKAkAAaQAAAABBQAAAAxtYXhpbXVtVmFsdWUCAAAAASkJAARMAAAAAgkBAAAADEludGVnZXJFbnRyeQAAAAIFAAAAD2dhbWVNaW5CZXRMYWJlbAUAAAAMbWluaW11bVZhbHVlBQAAAANuaWwDCQAAAAAAAAIFAAAADXBhcmFtZXRlck5hbWUCAAAACWNoYW5nZVJTQQQAAAAHJG1hdGNoMAkBAAAAD2dhbWVzR2V0Q291bnRlcgAAAAADCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAA0ludAQAAAAMR0FNRVNDT1VOVEVSBQAAAAckbWF0Y2gwBAAAAAckbWF0Y2gxCQEAAAAMZ2FtZUdldFN0YXRlAAAAAQUAAAAMR0FNRVNDT1VOVEVSAwkAAAEAAAACBQAAAAckbWF0Y2gxAgAAAANJbnQEAAAABXN0YXRlBQAAAAckbWF0Y2gxAwkBAAAAAiE9AAAAAgUAAAAFc3RhdGUAAAAAAAAAAAEJAAACAAAAAQIAAAAYR2FtZSBoYXMgdG8gYmUgZmluaXNoZWQuCQAETAAAAAIJAQAAAAtTdHJpbmdFbnRyeQAAAAIFAAAAEmdhbWVSU0FQdWJsaWNMYWJlbAUAAAAOcGFyYW1ldGVyVmFsdWUFAAAAA25pbAkABEwAAAACCQEAAAALU3RyaW5nRW50cnkAAAACBQAAABJnYW1lUlNBUHVibGljTGFiZWwFAAAADnBhcmFtZXRlclZhbHVlBQAAAANuaWwJAAACAAAAAQIAAAAdR2FtZSBuZWVkcyB0byBiZSBpbml0aWFsaXplZC4JAAACAAAAAQIAAABJU2V0dGluZyBub3QgZm91bmQuIEFsbG93ZWQ6IGNoYW5nZU1heCwgY2hhbmdlTWluLCBjaGFuZ2VSU0EsIGNoYW5nZVB1YktleQAAAAEAAAACdHgBAAAABnZlcmlmeQAAAAAJAAH0AAAAAwgFAAAAAnR4AAAACWJvZHlCeXRlcwkAAZEAAAACCAUAAAACdHgAAAAGcHJvb2ZzAAAAAAAAAAAACAUAAAACdHgAAAAPc2VuZGVyUHVibGljS2V5Dx/p9Q==", "height": 2227124, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: 59pAUTAXhHGoTvkSJCWrRWBHbWcfBUyoUbn84kR1cMPG Next: GYW4ctUkrBUmdcoyoM6uiZaZBx36b6r48sjM95DTuRZF Diff:
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 5 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let | |
4 | + | let wheel = [5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26, 0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10] | |
5 | 5 | ||
6 | - | let | |
6 | + | let black = [2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35] | |
7 | 7 | ||
8 | - | let | |
8 | + | let red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36] | |
9 | 9 | ||
10 | - | let | |
10 | + | let col1 = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34] | |
11 | 11 | ||
12 | - | let gamePrizeShareAmountLabel = "G_PRIZESHAREAMOUNT" | |
12 | + | let col2 = [2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35] | |
13 | + | ||
14 | + | let col3 = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36] | |
15 | + | ||
16 | + | let gameMasterPublicKey = fromBase58String("<game-master-public-key>") | |
13 | 17 | ||
14 | 18 | let gameCounterLabel = "G_GAMESCOUNTER" | |
15 | 19 | ||
17 | 21 | ||
18 | 22 | let gameProcessedBetsLabel = "G_PROCESSEDBETS" | |
19 | 23 | ||
20 | - | let | |
24 | + | let gameMinBetLabel = "G_MINBET" | |
21 | 25 | ||
22 | - | let | |
26 | + | let gameMaxBetLabel = "G_MAXBET" | |
23 | 27 | ||
24 | - | ||
28 | + | let gameRSAPublicLabel = "G_RSAPUBLIC" | |
25 | 29 | ||
30 | + | let gameMasterPublicKeyLabel = "G_GAMEMASTER_PUBLIC_KEY" | |
26 | 31 | ||
27 | - | func betDetailsLabel (betNumber) = (("B_" + toString(betNumber)) + "_BETDETAILS") | |
28 | - | ||
29 | - | ||
30 | - | func winnerDetailsLabel (winnerNumber) = (("W_" + toString(winnerNumber)) + "_WINNERDETAILS") | |
31 | - | ||
32 | - | ||
33 | - | func gameBalance () = getInteger(this, gameBalanceLabel) | |
34 | - | ||
35 | - | ||
36 | - | func gameCommunityPot () = getInteger(this, gameCommunityPotLabel) | |
37 | - | ||
38 | - | ||
39 | - | func gamePrizePot () = getInteger(this, gamePrizePotLabel) | |
40 | - | ||
41 | - | ||
42 | - | func gamePrizeShareAmount () = getInteger(this, gamePrizeShareAmountLabel) | |
43 | - | ||
32 | + | let gameTokenLabel = "G_TOKENID" | |
44 | 33 | ||
45 | 34 | func gamesGetCounter () = getInteger(this, gameCounterLabel) | |
46 | 35 | ||
47 | 36 | ||
48 | - | func | |
37 | + | func gamesTotalBets () = getInteger(this, gameTotalBetsLabel) | |
49 | 38 | ||
50 | 39 | ||
51 | - | func | |
40 | + | func gamesProcessedBets () = getInteger(this, gameProcessedBetsLabel) | |
52 | 41 | ||
53 | 42 | ||
54 | - | func | |
43 | + | func gamesMinBet () = getIntegerValue(this, gameMinBetLabel) | |
55 | 44 | ||
56 | 45 | ||
57 | - | func | |
46 | + | func gamesMaxBet () = getIntegerValue(this, gameMaxBetLabel) | |
58 | 47 | ||
59 | 48 | ||
60 | - | func | |
49 | + | func gameRSAPublicString () = getStringValue(this, gameRSAPublicLabel) | |
61 | 50 | ||
62 | 51 | ||
63 | - | func betDetails (betNumber) = getStringValue(this, betDetailsLabel(betNumber)) | |
64 | - | ||
65 | - | ||
66 | - | func winnerDetails (winnerNumber) = getStringValue(this, winnerDetailsLabel(winnerNumber)) | |
52 | + | func gameRSAPublic () = fromBase64String(gameRSAPublicString()) | |
67 | 53 | ||
68 | 54 | ||
69 | 55 | func gameToken () = match getString(this, gameTokenLabel) { | |
76 | 62 | } | |
77 | 63 | ||
78 | 64 | ||
65 | + | func betDetailsLabel (betNumber) = (("B_" + toString(betNumber)) + "_BETDETAILS") | |
66 | + | ||
67 | + | ||
68 | + | func betWinStateLabel (betNumber) = (("B_" + toString(betNumber)) + "_WINSTATE") | |
69 | + | ||
70 | + | ||
71 | + | func betWinAmountLabel (betNumber) = (("B_" + toString(betNumber)) + "_WINAMOUNT") | |
72 | + | ||
73 | + | ||
74 | + | func betGameLabel (betNumber) = (("B_" + toString(betNumber)) + "_GAME") | |
75 | + | ||
76 | + | ||
77 | + | func betPayoutLabel (betNumber) = (("B_" + toString(betNumber)) + "_PAYOUT") | |
78 | + | ||
79 | + | ||
80 | + | func betDetailsResult (betNumber) = getStringValue(this, betDetailsLabel(betNumber)) | |
81 | + | ||
82 | + | ||
83 | + | func betGameResult (betNumber) = getIntegerValue(this, betGameLabel(betNumber)) | |
84 | + | ||
85 | + | ||
86 | + | func gameStateLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_STATE") | |
87 | + | ||
88 | + | ||
89 | + | func gameStartHeightLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_STARTHEIGHT") | |
90 | + | ||
91 | + | ||
92 | + | func gameEndHeightLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_ENDHEIGHT") | |
93 | + | ||
94 | + | ||
95 | + | func gameSumShaLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_SUMSHA") | |
96 | + | ||
97 | + | ||
98 | + | func gameResultLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_RESULT") | |
99 | + | ||
100 | + | ||
101 | + | func gameRandomHashLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_RANDOMHASH") | |
102 | + | ||
103 | + | ||
104 | + | func gameGetState (gameNumber) = getInteger(this, gameStateLabel(gameNumber)) | |
105 | + | ||
106 | + | ||
107 | + | func gameGetEndHeight (gameNumber) = getInteger(this, gameEndHeightLabel(gameNumber)) | |
108 | + | ||
109 | + | ||
110 | + | func gameGetSumSha (gameNumber) = getString(this, gameSumShaLabel(gameNumber)) | |
111 | + | ||
112 | + | ||
113 | + | func gameGetResult (gameNumber) = getIntegerValue(this, gameResultLabel(gameNumber)) | |
114 | + | ||
115 | + | ||
116 | + | func checkPermission (callerPublicKey) = (callerPublicKey != gameMasterPublicKey) | |
117 | + | ||
118 | + | ||
119 | + | func gameInBaseToken (token) = (token == unit) | |
120 | + | ||
121 | + | ||
122 | + | func getMultiple (numberPlaced) = if ((numberPlaced == 1)) | |
123 | + | then 36 | |
124 | + | else if ((numberPlaced == 2)) | |
125 | + | then 18 | |
126 | + | else if ((numberPlaced == 3)) | |
127 | + | then 12 | |
128 | + | else if ((numberPlaced == 4)) | |
129 | + | then 9 | |
130 | + | else if ((numberPlaced == 5)) | |
131 | + | then 7 | |
132 | + | else if ((numberPlaced == 6)) | |
133 | + | then 6 | |
134 | + | else 0 | |
135 | + | ||
136 | + | ||
137 | + | func getWinnings (bet,result) = { | |
138 | + | let betStringList = split(bet, "-") | |
139 | + | let amountBet = value(parseInt(betStringList[1])) | |
140 | + | let betType = betStringList[2] | |
141 | + | if ((betType == "numbers")) | |
142 | + | then { | |
143 | + | let allNumberBets = removeByIndex(removeByIndex(removeByIndex(betStringList, 0), 0), 0) | |
144 | + | if (containsElement(allNumberBets, toString(result))) | |
145 | + | then (amountBet * getMultiple(size(allNumberBets))) | |
146 | + | else 0 | |
147 | + | } | |
148 | + | else if ((betType == "color")) | |
149 | + | then if ((betStringList[3] == "red")) | |
150 | + | then if (containsElement(red, result)) | |
151 | + | then (amountBet * 2) | |
152 | + | else 0 | |
153 | + | else if (containsElement(black, result)) | |
154 | + | then (amountBet * 2) | |
155 | + | else 0 | |
156 | + | else if ((betType == "column")) | |
157 | + | then if ((betStringList[3] == "1")) | |
158 | + | then if (containsElement(col1, result)) | |
159 | + | then (amountBet * 3) | |
160 | + | else 0 | |
161 | + | else if ((betStringList[3] == "2")) | |
162 | + | then if (containsElement(col2, result)) | |
163 | + | then (amountBet * 3) | |
164 | + | else 0 | |
165 | + | else if ((betStringList[3] == "3")) | |
166 | + | then if (containsElement(col3, result)) | |
167 | + | then (amountBet * 3) | |
168 | + | else 0 | |
169 | + | else 0 | |
170 | + | else if ((betType == "dozen")) | |
171 | + | then if ((betStringList[3] == "1")) | |
172 | + | then if (if ((result >= 1)) | |
173 | + | then (12 >= result) | |
174 | + | else false) | |
175 | + | then (amountBet * 3) | |
176 | + | else 0 | |
177 | + | else if ((betStringList[3] == "2")) | |
178 | + | then if (if ((result >= 13)) | |
179 | + | then (24 >= result) | |
180 | + | else false) | |
181 | + | then (amountBet * 3) | |
182 | + | else 0 | |
183 | + | else if ((betStringList[3] == "3")) | |
184 | + | then if (if ((result >= 25)) | |
185 | + | then (36 >= result) | |
186 | + | else false) | |
187 | + | then (amountBet * 3) | |
188 | + | else 0 | |
189 | + | else 0 | |
190 | + | else if ((betType == "oddeven")) | |
191 | + | then if ((result == 0)) | |
192 | + | then 0 | |
193 | + | else if ((betStringList[3] == "even")) | |
194 | + | then if (if (((result % 2) == 0)) | |
195 | + | then (result != 0) | |
196 | + | else false) | |
197 | + | then (amountBet * 2) | |
198 | + | else 0 | |
199 | + | else if (((result % 2) == 1)) | |
200 | + | then (amountBet * 2) | |
201 | + | else 0 | |
202 | + | else if ((betType == "lowhigh")) | |
203 | + | then if ((result == 0)) | |
204 | + | then 0 | |
205 | + | else if ((betStringList[3] == "low")) | |
206 | + | then if (if ((result >= 1)) | |
207 | + | then (18 >= result) | |
208 | + | else false) | |
209 | + | then (amountBet * 2) | |
210 | + | else 0 | |
211 | + | else if (if ((result >= 19)) | |
212 | + | then (36 >= result) | |
213 | + | else false) | |
214 | + | then (amountBet * 2) | |
215 | + | else 0 | |
216 | + | else 0 | |
217 | + | } | |
218 | + | ||
219 | + | ||
79 | 220 | @Callable(i) | |
80 | - | func initGame () = [IntegerEntry(gameBalanceLabel, 0), IntegerEntry(gameCommunityPotLabel, 0), IntegerEntry(gamePrizePotLabel, 10000000), IntegerEntry(gamePrizeShareAmountLabel, 0), IntegerEntry(gameCounterLabel, 0), IntegerEntry(gameTotalBetsLabel, 0), IntegerEntry(gameProcessedBetsLabel, 0), IntegerEntry(gameWinnersCounterLabel, 0), IntegerEntry(gameProcessedWinnersLabel, 0)] | |
221 | + | func processNextBet () = if (checkPermission(i.callerPublicKey)) | |
222 | + | then throw("processNextBet_FORBBIDEN") | |
223 | + | else match gamesTotalBets() { | |
224 | + | case totalBets: Int => | |
225 | + | match gamesProcessedBets() { | |
226 | + | case lastProcessedBet: Int => | |
227 | + | let nextBet = (lastProcessedBet + 1) | |
228 | + | if ((nextBet > totalBets)) | |
229 | + | then throw("Next bet has not been placed.") | |
230 | + | else { | |
231 | + | let gameNumber = betGameResult(nextBet) | |
232 | + | match gameGetState(gameNumber) { | |
233 | + | case state: Int => | |
234 | + | if ((state == 1)) | |
235 | + | then { | |
236 | + | let result = gameGetResult(gameNumber) | |
237 | + | let betDetails = betDetailsResult(nextBet) | |
238 | + | let betPlacer = split(betDetails, "-")[0] | |
239 | + | let address = value(addressFromString(betPlacer)) | |
240 | + | let winnings = getWinnings(betDetails, result) | |
241 | + | [ScriptTransfer(address, winnings, gameToken()), IntegerEntry(gameProcessedBetsLabel, (lastProcessedBet + 1)), IntegerEntry(betPayoutLabel(nextBet), winnings)] | |
242 | + | } | |
243 | + | else throw("Game has not finished.") | |
244 | + | case _ => | |
245 | + | throw("Game does not exist or has already ended.") | |
246 | + | } | |
247 | + | } | |
248 | + | case _ => | |
249 | + | throw("Game has not been initialized, next bets not found") | |
250 | + | } | |
251 | + | case _ => | |
252 | + | throw("Game has not been initialized, total bets not found") | |
253 | + | } | |
81 | 254 | ||
82 | 255 | ||
83 | 256 | ||
84 | 257 | @Callable(i) | |
85 | - | func startGame () = match gamesGetCounter() { | |
86 | - | case currentGame: Int => | |
87 | - | let newGame = (currentGame + 1) | |
88 | - | [IntegerEntry(gameCounterLabel, newGame)] | |
89 | - | case _ => | |
90 | - | throw("Game has not been initialized, total games not found") | |
91 | - | } | |
258 | + | func initGame () = if (checkPermission(i.callerPublicKey)) | |
259 | + | then throw("initGame_FORBBIDEN") | |
260 | + | else match gamesGetCounter() { | |
261 | + | case GAMESCOUNTER: Int => | |
262 | + | throw("INIT_ALREADY_DONE") | |
263 | + | case _ => | |
264 | + | [IntegerEntry(gameCounterLabel, 0), IntegerEntry(gameTotalBetsLabel, 0), IntegerEntry(gameProcessedBetsLabel, 0), IntegerEntry(gameMinBetLabel, 100000000), IntegerEntry(gameMaxBetLabel, 2500000000), StringEntry(gameRSAPublicLabel, "base64:<gameRSAPublicLabel>"), StringEntry(gameTokenLabel, "")] | |
265 | + | } | |
92 | 266 | ||
93 | 267 | ||
94 | 268 | ||
95 | 269 | @Callable(i) | |
96 | - | func placeBet () = match gamesGetCounter() { | |
97 | - | case currentGame: Int => | |
98 | - | match gameTotalBets() { | |
99 | - | case totalBets: Int => | |
100 | - | let newBet = (totalBets + 1) | |
101 | - | let bet = "1,2,3" | |
102 | - | [IntegerEntry(gameTotalBetsLabel, newBet), StringEntry(betDetailsLabel(newBet), ((("G" + toString(currentGame)) + "-") + bet))] | |
103 | - | case _ => | |
104 | - | throw("Game has not been initialized, total bets not found") | |
105 | - | } | |
106 | - | case _ => | |
107 | - | throw("Game has not been initialized, total games not found") | |
108 | - | } | |
270 | + | func startGame (randomHash,blockInRound) = if (checkPermission(i.callerPublicKey)) | |
271 | + | then throw("startGame_FORBBIDEN") | |
272 | + | else match gamesGetCounter() { | |
273 | + | case GAMESCOUNTER: Int => | |
274 | + | let newGameCounter = (GAMESCOUNTER + 1) | |
275 | + | if ((GAMESCOUNTER > 0)) | |
276 | + | then match gameGetState(GAMESCOUNTER) { | |
277 | + | case gameState: Int => | |
278 | + | if ((gameState == 0)) | |
279 | + | then throw("Last game has not finished yet.") | |
280 | + | else [IntegerEntry(gameCounterLabel, newGameCounter), IntegerEntry(gameStartHeightLabel(newGameCounter), lastBlock.height), IntegerEntry(gameEndHeightLabel(newGameCounter), (height + blockInRound)), IntegerEntry(gameStateLabel(newGameCounter), 0), StringEntry(gameSumShaLabel(newGameCounter), toBase58String(sha256(fromBase64String(randomHash))))] | |
281 | + | case _ => | |
282 | + | throw("Game state not found.") | |
283 | + | } | |
284 | + | else [IntegerEntry(gameCounterLabel, newGameCounter), IntegerEntry(gameStartHeightLabel(newGameCounter), lastBlock.height), IntegerEntry(gameEndHeightLabel(newGameCounter), (height + blockInRound)), IntegerEntry(gameStateLabel(newGameCounter), 0), StringEntry(gameSumShaLabel(newGameCounter), toBase58String(sha256(fromBase64String(randomHash))))] | |
285 | + | case _ => | |
286 | + | throw("INIT_NEEDED") | |
287 | + | } | |
109 | 288 | ||
110 | 289 | ||
111 | 290 | ||
112 | 291 | @Callable(i) | |
113 | - | func endGame () = match gamesGetCounter() { | |
114 | - | case currentGame: Int => | |
115 | - | [StringEntry(gameResultLabel(currentGame), ((("G" + toString(currentGame)) + "-") + "1,2,3"))] | |
116 | - | case _ => | |
117 | - | throw("Game has not been initialized, total games not found") | |
118 | - | } | |
292 | + | func placeBet (bet) = { | |
293 | + | let assetId = gameToken() | |
294 | + | if ((i.feeAssetId != assetId)) | |
295 | + | then throw("Fee is not in correct assetId") | |
296 | + | else { | |
297 | + | let betStringList = split(bet, "-") | |
298 | + | let betType = betStringList[0] | |
299 | + | if (if (if (if (if (if ((betType != "numbers")) | |
300 | + | then (betType != "column") | |
301 | + | else false) | |
302 | + | then (betType != "color") | |
303 | + | else false) | |
304 | + | then (betType != "dozen") | |
305 | + | else false) | |
306 | + | then (betType != "oddeven") | |
307 | + | else false) | |
308 | + | then (betType != "lowhigh") | |
309 | + | else false) | |
310 | + | then throw("Bet type is not valid.") | |
311 | + | else match gamesGetCounter() { | |
312 | + | case GAMESCOUNTER: Int => | |
313 | + | match gameGetState(GAMESCOUNTER) { | |
314 | + | case state: Int => | |
315 | + | if ((state == 1)) | |
316 | + | then throw("Game is finished and next game has not started.") | |
317 | + | else if ((size(i.payments) != 1)) | |
318 | + | then throw("Payment not attached or there is more then one payment") | |
319 | + | else { | |
320 | + | let p = i.payments[0] | |
321 | + | let amount = p.amount | |
322 | + | if (if (if ((value(gamesMaxBet()) >= amount)) | |
323 | + | then (amount >= value(gamesMinBet())) | |
324 | + | else false) | |
325 | + | then (i.payments[0].assetId == assetId) | |
326 | + | else false) | |
327 | + | then match gameGetEndHeight(GAMESCOUNTER) { | |
328 | + | case endHeight: Int => | |
329 | + | if ((endHeight > height)) | |
330 | + | then match gamesTotalBets() { | |
331 | + | case totalBets: Int => | |
332 | + | let thisBetNumber = (totalBets + 1) | |
333 | + | if ((assetId == unit)) | |
334 | + | then [IntegerEntry(gameTotalBetsLabel, (totalBets + 1)), StringEntry(betDetailsLabel(thisBetNumber), ((((toBase58String(i.caller.bytes) + "-") + toString(amount)) + "-") + bet)), IntegerEntry(betGameLabel(thisBetNumber), GAMESCOUNTER)] | |
335 | + | else [IntegerEntry(gameTotalBetsLabel, (totalBets + 1)), StringEntry(betDetailsLabel(thisBetNumber), ((((toBase58String(i.caller.bytes) + "-") + toString(amount)) + "-") + bet)), IntegerEntry(betGameLabel(thisBetNumber), GAMESCOUNTER)] | |
336 | + | case _ => | |
337 | + | throw("Game has not been initalized.") | |
338 | + | } | |
339 | + | else throw("Game has already exceeded betting round") | |
340 | + | case _ => | |
341 | + | throw("End height has not been defined.") | |
342 | + | } | |
343 | + | else throw((((((("Bet (" + toString(amount)) + ") is greater than maximum bet (") + toString(gamesMaxBet())) + "), lower than minimum (") + toString(gamesMinBet())) + ") or payment is not in correct assetId.")) | |
344 | + | } | |
345 | + | case _ => | |
346 | + | throw("Game does not exist or has already ended.") | |
347 | + | } | |
348 | + | case _ => | |
349 | + | throw("Game needs to be initialized.") | |
350 | + | } | |
351 | + | } | |
352 | + | } | |
119 | 353 | ||
120 | 354 | ||
121 | 355 | ||
122 | 356 | @Callable(i) | |
123 | - | func processNextBet () = match gameTotalBets() { | |
124 | - | case totalBets: Int => | |
125 | - | match gameProcessedBets() { | |
126 | - | case lastProcessedBet: Int => | |
127 | - | let nextBet = (lastProcessedBet + 1) | |
128 | - | if ((nextBet > totalBets)) | |
129 | - | then throw("Next bet has not been placed.") | |
130 | - | else match gamesGetCounter() { | |
131 | - | case gameNumber: Int => | |
132 | - | match gameWinnersCounter() { | |
133 | - | case winnersCount: Int => | |
134 | - | let result = gameGetResult(gameNumber) | |
135 | - | let betDetail = betDetails(nextBet) | |
136 | - | if ((betDetail == result)) | |
137 | - | then { | |
138 | - | let newWinnersCount = (winnersCount + 1) | |
139 | - | [IntegerEntry(gameProcessedBetsLabel, nextBet), IntegerEntry(gameWinnersCounterLabel, (winnersCount + 1)), StringEntry(winnerDetailsLabel(newWinnersCount), toBase58String(i.caller.bytes))] | |
140 | - | } | |
141 | - | else [IntegerEntry(gameProcessedBetsLabel, nextBet)] | |
357 | + | func endGame (rsaSign,gameNumber) = if (checkPermission(i.callerPublicKey)) | |
358 | + | then throw("endGame_FORBBIDEN") | |
359 | + | else match gameGetState(gameNumber) { | |
360 | + | case state: Int => | |
361 | + | if ((state == 1)) | |
362 | + | then throw("Game is finished") | |
363 | + | else match gameGetEndHeight(gameNumber) { | |
364 | + | case gameHeight: Int => | |
365 | + | if ((gameHeight > height)) | |
366 | + | then throw(("Not enough blocks have past since game has started, game ends at block " + toString(gameHeight))) | |
367 | + | else match gameGetSumSha(gameNumber) { | |
368 | + | case SUMSHA: String => | |
369 | + | let sig = fromBase64String(rsaSign) | |
370 | + | let rsaSigValid = rsaVerify(SHA256, fromBase58String(SUMSHA), sig, gameRSAPublic()) | |
371 | + | match blockInfoByHeight(gameHeight) { | |
372 | + | case previousBlock: BlockInfo => | |
373 | + | let randomHash = sha256((take(sig, 32) + take(previousBlock.generationSignature, 32))) | |
374 | + | if (!(rsaSigValid)) | |
375 | + | then throw("INVALID_RSA_SIGN") | |
376 | + | else { | |
377 | + | let index = wheel[((toInt(randomHash) + value(gameNumber)) % 37)] | |
378 | + | [IntegerEntry(gameStateLabel(gameNumber), 1), IntegerEntry(gameResultLabel(gameNumber), index), StringEntry(gameRandomHashLabel(gameNumber), toBase64String(randomHash))] | |
379 | + | } | |
380 | + | case _ => | |
381 | + | throw("Last block does not exist.") | |
382 | + | } | |
142 | 383 | case _ => | |
143 | - | throw(" | |
384 | + | throw("SHA does not exist") | |
144 | 385 | } | |
145 | - | case _ => | |
146 | - | throw("Game has not been initialized, total games not found") | |
147 | - | } | |
148 | - | case _ => | |
149 | - | throw("Game has not been initialized, total processed bets not found") | |
150 | - | } | |
151 | - | case _ => | |
152 | - | throw("Game has not been initialized, total bets not found") | |
153 | - | } | |
386 | + | case _ => | |
387 | + | throw("Cannot get game height.") | |
388 | + | } | |
389 | + | case _ => | |
390 | + | throw("Game does not exist or has already ended") | |
391 | + | } | |
154 | 392 | ||
155 | 393 | ||
156 | 394 | ||
157 | 395 | @Callable(i) | |
158 | - | func | |
159 | - | | |
160 | - | | |
161 | - | ||
162 | - | | |
163 | - | | |
164 | - | | |
165 | - | | |
166 | - | ||
167 | - | | |
168 | - | | |
396 | + | func setParameter (parameterName,parameterValue) = if (checkPermission(i.callerPublicKey)) | |
397 | + | then throw("setParameter_FORBBIDEN") | |
398 | + | else if ((parameterName == "changeMax")) | |
399 | + | then { | |
400 | + | let maximumValue = parseIntValue(parameterValue) | |
401 | + | let minimumValue = gamesMinBet() | |
402 | + | if ((0 > maximumValue)) | |
403 | + | then throw("value cannot be lower than 0") | |
404 | + | else if ((minimumValue >= maximumValue)) | |
405 | + | then throw((("value cannot be lower or equal than min bet (" + toString(minimumValue)) + ")")) | |
406 | + | else [IntegerEntry(gameMaxBetLabel, maximumValue)] | |
169 | 407 | } | |
170 | - | case _ => | |
171 | - | throw("Game has not been initialized, total winners not found") | |
172 | - | } | |
408 | + | else if ((parameterName == "changeMin")) | |
409 | + | then { | |
410 | + | let maximumValue = gamesMaxBet() | |
411 | + | let minimumValue = parseIntValue(parameterValue) | |
412 | + | if ((0 > minimumValue)) | |
413 | + | then throw("value cannot be lower than 0") | |
414 | + | else if ((minimumValue >= maximumValue)) | |
415 | + | then throw((("value cannot be greater or equal than min bet (" + toString(maximumValue)) + ")")) | |
416 | + | else [IntegerEntry(gameMinBetLabel, minimumValue)] | |
417 | + | } | |
418 | + | else if ((parameterName == "changeRSA")) | |
419 | + | then match gamesGetCounter() { | |
420 | + | case GAMESCOUNTER: Int => | |
421 | + | match gameGetState(GAMESCOUNTER) { | |
422 | + | case state: Int => | |
423 | + | if ((state != 1)) | |
424 | + | then throw("Game has to be finished.") | |
425 | + | else [StringEntry(gameRSAPublicLabel, parameterValue)] | |
426 | + | case _ => | |
427 | + | [StringEntry(gameRSAPublicLabel, parameterValue)] | |
428 | + | } | |
429 | + | case _ => | |
430 | + | throw("Game needs to be initialized.") | |
431 | + | } | |
432 | + | else throw("Setting not found. Allowed: changeMax, changeMin, changeRSA, changePubKey") | |
173 | 433 | ||
174 | 434 | ||
175 | - | ||
176 | - | @Callable(i) | |
177 | - | func processNextWinner () = match gameWinnersCounter() { | |
178 | - | case totalWinners: Int => | |
179 | - | if ((totalWinners == 0)) | |
180 | - | then throw("There are no winners this round.") | |
181 | - | else match gameProcessedWinners() { | |
182 | - | case lastProcessedWinner: Int => | |
183 | - | let nextWinner = (lastProcessedWinner + 1) | |
184 | - | if ((nextWinner > totalWinners)) | |
185 | - | then throw("No more winners to be processed.") | |
186 | - | else match gamePrizeShareAmount() { | |
187 | - | case prizeShareAmount: Int => | |
188 | - | let betPlacer = winnerDetails(nextWinner) | |
189 | - | let address = value(addressFromString(betPlacer)) | |
190 | - | let winnings = gamePrizeShareAmount() | |
191 | - | [ScriptTransfer(address, prizeShareAmount, gameToken())] | |
192 | - | case _ => | |
193 | - | throw("Game has not been initialized, prize share amount not found") | |
194 | - | } | |
195 | - | case _ => | |
196 | - | throw("Game has not been initialized, total processed winners not found") | |
197 | - | } | |
198 | - | case _ => | |
199 | - | throw("Game has not been initialized, total winners not found") | |
200 | - | } | |
201 | - | ||
435 | + | @Verifier(tx) | |
436 | + | func verify () = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) | |
202 | 437 |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 5 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | - | let | |
4 | + | let wheel = [5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26, 0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10] | |
5 | 5 | ||
6 | - | let | |
6 | + | let black = [2, 4, 6, 8, 10, 11, 13, 15, 17, 20, 22, 24, 26, 28, 29, 31, 33, 35] | |
7 | 7 | ||
8 | - | let | |
8 | + | let red = [1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36] | |
9 | 9 | ||
10 | - | let | |
10 | + | let col1 = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34] | |
11 | 11 | ||
12 | - | let gamePrizeShareAmountLabel = "G_PRIZESHAREAMOUNT" | |
12 | + | let col2 = [2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35] | |
13 | + | ||
14 | + | let col3 = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36] | |
15 | + | ||
16 | + | let gameMasterPublicKey = fromBase58String("<game-master-public-key>") | |
13 | 17 | ||
14 | 18 | let gameCounterLabel = "G_GAMESCOUNTER" | |
15 | 19 | ||
16 | 20 | let gameTotalBetsLabel = "G_TOTALBETS" | |
17 | 21 | ||
18 | 22 | let gameProcessedBetsLabel = "G_PROCESSEDBETS" | |
19 | 23 | ||
20 | - | let | |
24 | + | let gameMinBetLabel = "G_MINBET" | |
21 | 25 | ||
22 | - | let | |
26 | + | let gameMaxBetLabel = "G_MAXBET" | |
23 | 27 | ||
24 | - | ||
28 | + | let gameRSAPublicLabel = "G_RSAPUBLIC" | |
25 | 29 | ||
30 | + | let gameMasterPublicKeyLabel = "G_GAMEMASTER_PUBLIC_KEY" | |
26 | 31 | ||
27 | - | func betDetailsLabel (betNumber) = (("B_" + toString(betNumber)) + "_BETDETAILS") | |
28 | - | ||
29 | - | ||
30 | - | func winnerDetailsLabel (winnerNumber) = (("W_" + toString(winnerNumber)) + "_WINNERDETAILS") | |
31 | - | ||
32 | - | ||
33 | - | func gameBalance () = getInteger(this, gameBalanceLabel) | |
34 | - | ||
35 | - | ||
36 | - | func gameCommunityPot () = getInteger(this, gameCommunityPotLabel) | |
37 | - | ||
38 | - | ||
39 | - | func gamePrizePot () = getInteger(this, gamePrizePotLabel) | |
40 | - | ||
41 | - | ||
42 | - | func gamePrizeShareAmount () = getInteger(this, gamePrizeShareAmountLabel) | |
43 | - | ||
32 | + | let gameTokenLabel = "G_TOKENID" | |
44 | 33 | ||
45 | 34 | func gamesGetCounter () = getInteger(this, gameCounterLabel) | |
46 | 35 | ||
47 | 36 | ||
48 | - | func | |
37 | + | func gamesTotalBets () = getInteger(this, gameTotalBetsLabel) | |
49 | 38 | ||
50 | 39 | ||
51 | - | func | |
40 | + | func gamesProcessedBets () = getInteger(this, gameProcessedBetsLabel) | |
52 | 41 | ||
53 | 42 | ||
54 | - | func | |
43 | + | func gamesMinBet () = getIntegerValue(this, gameMinBetLabel) | |
55 | 44 | ||
56 | 45 | ||
57 | - | func | |
46 | + | func gamesMaxBet () = getIntegerValue(this, gameMaxBetLabel) | |
58 | 47 | ||
59 | 48 | ||
60 | - | func | |
49 | + | func gameRSAPublicString () = getStringValue(this, gameRSAPublicLabel) | |
61 | 50 | ||
62 | 51 | ||
63 | - | func betDetails (betNumber) = getStringValue(this, betDetailsLabel(betNumber)) | |
64 | - | ||
65 | - | ||
66 | - | func winnerDetails (winnerNumber) = getStringValue(this, winnerDetailsLabel(winnerNumber)) | |
52 | + | func gameRSAPublic () = fromBase64String(gameRSAPublicString()) | |
67 | 53 | ||
68 | 54 | ||
69 | 55 | func gameToken () = match getString(this, gameTokenLabel) { | |
70 | 56 | case tokenId: String => | |
71 | 57 | if ((tokenId == "")) | |
72 | 58 | then unit | |
73 | 59 | else fromBase58String(tokenId) | |
74 | 60 | case _ => | |
75 | 61 | unit | |
76 | 62 | } | |
77 | 63 | ||
78 | 64 | ||
65 | + | func betDetailsLabel (betNumber) = (("B_" + toString(betNumber)) + "_BETDETAILS") | |
66 | + | ||
67 | + | ||
68 | + | func betWinStateLabel (betNumber) = (("B_" + toString(betNumber)) + "_WINSTATE") | |
69 | + | ||
70 | + | ||
71 | + | func betWinAmountLabel (betNumber) = (("B_" + toString(betNumber)) + "_WINAMOUNT") | |
72 | + | ||
73 | + | ||
74 | + | func betGameLabel (betNumber) = (("B_" + toString(betNumber)) + "_GAME") | |
75 | + | ||
76 | + | ||
77 | + | func betPayoutLabel (betNumber) = (("B_" + toString(betNumber)) + "_PAYOUT") | |
78 | + | ||
79 | + | ||
80 | + | func betDetailsResult (betNumber) = getStringValue(this, betDetailsLabel(betNumber)) | |
81 | + | ||
82 | + | ||
83 | + | func betGameResult (betNumber) = getIntegerValue(this, betGameLabel(betNumber)) | |
84 | + | ||
85 | + | ||
86 | + | func gameStateLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_STATE") | |
87 | + | ||
88 | + | ||
89 | + | func gameStartHeightLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_STARTHEIGHT") | |
90 | + | ||
91 | + | ||
92 | + | func gameEndHeightLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_ENDHEIGHT") | |
93 | + | ||
94 | + | ||
95 | + | func gameSumShaLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_SUMSHA") | |
96 | + | ||
97 | + | ||
98 | + | func gameResultLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_RESULT") | |
99 | + | ||
100 | + | ||
101 | + | func gameRandomHashLabel (gameNumber) = (("G_" + toString(gameNumber)) + "_RANDOMHASH") | |
102 | + | ||
103 | + | ||
104 | + | func gameGetState (gameNumber) = getInteger(this, gameStateLabel(gameNumber)) | |
105 | + | ||
106 | + | ||
107 | + | func gameGetEndHeight (gameNumber) = getInteger(this, gameEndHeightLabel(gameNumber)) | |
108 | + | ||
109 | + | ||
110 | + | func gameGetSumSha (gameNumber) = getString(this, gameSumShaLabel(gameNumber)) | |
111 | + | ||
112 | + | ||
113 | + | func gameGetResult (gameNumber) = getIntegerValue(this, gameResultLabel(gameNumber)) | |
114 | + | ||
115 | + | ||
116 | + | func checkPermission (callerPublicKey) = (callerPublicKey != gameMasterPublicKey) | |
117 | + | ||
118 | + | ||
119 | + | func gameInBaseToken (token) = (token == unit) | |
120 | + | ||
121 | + | ||
122 | + | func getMultiple (numberPlaced) = if ((numberPlaced == 1)) | |
123 | + | then 36 | |
124 | + | else if ((numberPlaced == 2)) | |
125 | + | then 18 | |
126 | + | else if ((numberPlaced == 3)) | |
127 | + | then 12 | |
128 | + | else if ((numberPlaced == 4)) | |
129 | + | then 9 | |
130 | + | else if ((numberPlaced == 5)) | |
131 | + | then 7 | |
132 | + | else if ((numberPlaced == 6)) | |
133 | + | then 6 | |
134 | + | else 0 | |
135 | + | ||
136 | + | ||
137 | + | func getWinnings (bet,result) = { | |
138 | + | let betStringList = split(bet, "-") | |
139 | + | let amountBet = value(parseInt(betStringList[1])) | |
140 | + | let betType = betStringList[2] | |
141 | + | if ((betType == "numbers")) | |
142 | + | then { | |
143 | + | let allNumberBets = removeByIndex(removeByIndex(removeByIndex(betStringList, 0), 0), 0) | |
144 | + | if (containsElement(allNumberBets, toString(result))) | |
145 | + | then (amountBet * getMultiple(size(allNumberBets))) | |
146 | + | else 0 | |
147 | + | } | |
148 | + | else if ((betType == "color")) | |
149 | + | then if ((betStringList[3] == "red")) | |
150 | + | then if (containsElement(red, result)) | |
151 | + | then (amountBet * 2) | |
152 | + | else 0 | |
153 | + | else if (containsElement(black, result)) | |
154 | + | then (amountBet * 2) | |
155 | + | else 0 | |
156 | + | else if ((betType == "column")) | |
157 | + | then if ((betStringList[3] == "1")) | |
158 | + | then if (containsElement(col1, result)) | |
159 | + | then (amountBet * 3) | |
160 | + | else 0 | |
161 | + | else if ((betStringList[3] == "2")) | |
162 | + | then if (containsElement(col2, result)) | |
163 | + | then (amountBet * 3) | |
164 | + | else 0 | |
165 | + | else if ((betStringList[3] == "3")) | |
166 | + | then if (containsElement(col3, result)) | |
167 | + | then (amountBet * 3) | |
168 | + | else 0 | |
169 | + | else 0 | |
170 | + | else if ((betType == "dozen")) | |
171 | + | then if ((betStringList[3] == "1")) | |
172 | + | then if (if ((result >= 1)) | |
173 | + | then (12 >= result) | |
174 | + | else false) | |
175 | + | then (amountBet * 3) | |
176 | + | else 0 | |
177 | + | else if ((betStringList[3] == "2")) | |
178 | + | then if (if ((result >= 13)) | |
179 | + | then (24 >= result) | |
180 | + | else false) | |
181 | + | then (amountBet * 3) | |
182 | + | else 0 | |
183 | + | else if ((betStringList[3] == "3")) | |
184 | + | then if (if ((result >= 25)) | |
185 | + | then (36 >= result) | |
186 | + | else false) | |
187 | + | then (amountBet * 3) | |
188 | + | else 0 | |
189 | + | else 0 | |
190 | + | else if ((betType == "oddeven")) | |
191 | + | then if ((result == 0)) | |
192 | + | then 0 | |
193 | + | else if ((betStringList[3] == "even")) | |
194 | + | then if (if (((result % 2) == 0)) | |
195 | + | then (result != 0) | |
196 | + | else false) | |
197 | + | then (amountBet * 2) | |
198 | + | else 0 | |
199 | + | else if (((result % 2) == 1)) | |
200 | + | then (amountBet * 2) | |
201 | + | else 0 | |
202 | + | else if ((betType == "lowhigh")) | |
203 | + | then if ((result == 0)) | |
204 | + | then 0 | |
205 | + | else if ((betStringList[3] == "low")) | |
206 | + | then if (if ((result >= 1)) | |
207 | + | then (18 >= result) | |
208 | + | else false) | |
209 | + | then (amountBet * 2) | |
210 | + | else 0 | |
211 | + | else if (if ((result >= 19)) | |
212 | + | then (36 >= result) | |
213 | + | else false) | |
214 | + | then (amountBet * 2) | |
215 | + | else 0 | |
216 | + | else 0 | |
217 | + | } | |
218 | + | ||
219 | + | ||
79 | 220 | @Callable(i) | |
80 | - | func initGame () = [IntegerEntry(gameBalanceLabel, 0), IntegerEntry(gameCommunityPotLabel, 0), IntegerEntry(gamePrizePotLabel, 10000000), IntegerEntry(gamePrizeShareAmountLabel, 0), IntegerEntry(gameCounterLabel, 0), IntegerEntry(gameTotalBetsLabel, 0), IntegerEntry(gameProcessedBetsLabel, 0), IntegerEntry(gameWinnersCounterLabel, 0), IntegerEntry(gameProcessedWinnersLabel, 0)] | |
221 | + | func processNextBet () = if (checkPermission(i.callerPublicKey)) | |
222 | + | then throw("processNextBet_FORBBIDEN") | |
223 | + | else match gamesTotalBets() { | |
224 | + | case totalBets: Int => | |
225 | + | match gamesProcessedBets() { | |
226 | + | case lastProcessedBet: Int => | |
227 | + | let nextBet = (lastProcessedBet + 1) | |
228 | + | if ((nextBet > totalBets)) | |
229 | + | then throw("Next bet has not been placed.") | |
230 | + | else { | |
231 | + | let gameNumber = betGameResult(nextBet) | |
232 | + | match gameGetState(gameNumber) { | |
233 | + | case state: Int => | |
234 | + | if ((state == 1)) | |
235 | + | then { | |
236 | + | let result = gameGetResult(gameNumber) | |
237 | + | let betDetails = betDetailsResult(nextBet) | |
238 | + | let betPlacer = split(betDetails, "-")[0] | |
239 | + | let address = value(addressFromString(betPlacer)) | |
240 | + | let winnings = getWinnings(betDetails, result) | |
241 | + | [ScriptTransfer(address, winnings, gameToken()), IntegerEntry(gameProcessedBetsLabel, (lastProcessedBet + 1)), IntegerEntry(betPayoutLabel(nextBet), winnings)] | |
242 | + | } | |
243 | + | else throw("Game has not finished.") | |
244 | + | case _ => | |
245 | + | throw("Game does not exist or has already ended.") | |
246 | + | } | |
247 | + | } | |
248 | + | case _ => | |
249 | + | throw("Game has not been initialized, next bets not found") | |
250 | + | } | |
251 | + | case _ => | |
252 | + | throw("Game has not been initialized, total bets not found") | |
253 | + | } | |
81 | 254 | ||
82 | 255 | ||
83 | 256 | ||
84 | 257 | @Callable(i) | |
85 | - | func startGame () = match gamesGetCounter() { | |
86 | - | case currentGame: Int => | |
87 | - | let newGame = (currentGame + 1) | |
88 | - | [IntegerEntry(gameCounterLabel, newGame)] | |
89 | - | case _ => | |
90 | - | throw("Game has not been initialized, total games not found") | |
91 | - | } | |
258 | + | func initGame () = if (checkPermission(i.callerPublicKey)) | |
259 | + | then throw("initGame_FORBBIDEN") | |
260 | + | else match gamesGetCounter() { | |
261 | + | case GAMESCOUNTER: Int => | |
262 | + | throw("INIT_ALREADY_DONE") | |
263 | + | case _ => | |
264 | + | [IntegerEntry(gameCounterLabel, 0), IntegerEntry(gameTotalBetsLabel, 0), IntegerEntry(gameProcessedBetsLabel, 0), IntegerEntry(gameMinBetLabel, 100000000), IntegerEntry(gameMaxBetLabel, 2500000000), StringEntry(gameRSAPublicLabel, "base64:<gameRSAPublicLabel>"), StringEntry(gameTokenLabel, "")] | |
265 | + | } | |
92 | 266 | ||
93 | 267 | ||
94 | 268 | ||
95 | 269 | @Callable(i) | |
96 | - | func placeBet () = match gamesGetCounter() { | |
97 | - | case currentGame: Int => | |
98 | - | match gameTotalBets() { | |
99 | - | case totalBets: Int => | |
100 | - | let newBet = (totalBets + 1) | |
101 | - | let bet = "1,2,3" | |
102 | - | [IntegerEntry(gameTotalBetsLabel, newBet), StringEntry(betDetailsLabel(newBet), ((("G" + toString(currentGame)) + "-") + bet))] | |
103 | - | case _ => | |
104 | - | throw("Game has not been initialized, total bets not found") | |
105 | - | } | |
106 | - | case _ => | |
107 | - | throw("Game has not been initialized, total games not found") | |
108 | - | } | |
270 | + | func startGame (randomHash,blockInRound) = if (checkPermission(i.callerPublicKey)) | |
271 | + | then throw("startGame_FORBBIDEN") | |
272 | + | else match gamesGetCounter() { | |
273 | + | case GAMESCOUNTER: Int => | |
274 | + | let newGameCounter = (GAMESCOUNTER + 1) | |
275 | + | if ((GAMESCOUNTER > 0)) | |
276 | + | then match gameGetState(GAMESCOUNTER) { | |
277 | + | case gameState: Int => | |
278 | + | if ((gameState == 0)) | |
279 | + | then throw("Last game has not finished yet.") | |
280 | + | else [IntegerEntry(gameCounterLabel, newGameCounter), IntegerEntry(gameStartHeightLabel(newGameCounter), lastBlock.height), IntegerEntry(gameEndHeightLabel(newGameCounter), (height + blockInRound)), IntegerEntry(gameStateLabel(newGameCounter), 0), StringEntry(gameSumShaLabel(newGameCounter), toBase58String(sha256(fromBase64String(randomHash))))] | |
281 | + | case _ => | |
282 | + | throw("Game state not found.") | |
283 | + | } | |
284 | + | else [IntegerEntry(gameCounterLabel, newGameCounter), IntegerEntry(gameStartHeightLabel(newGameCounter), lastBlock.height), IntegerEntry(gameEndHeightLabel(newGameCounter), (height + blockInRound)), IntegerEntry(gameStateLabel(newGameCounter), 0), StringEntry(gameSumShaLabel(newGameCounter), toBase58String(sha256(fromBase64String(randomHash))))] | |
285 | + | case _ => | |
286 | + | throw("INIT_NEEDED") | |
287 | + | } | |
109 | 288 | ||
110 | 289 | ||
111 | 290 | ||
112 | 291 | @Callable(i) | |
113 | - | func endGame () = match gamesGetCounter() { | |
114 | - | case currentGame: Int => | |
115 | - | [StringEntry(gameResultLabel(currentGame), ((("G" + toString(currentGame)) + "-") + "1,2,3"))] | |
116 | - | case _ => | |
117 | - | throw("Game has not been initialized, total games not found") | |
118 | - | } | |
292 | + | func placeBet (bet) = { | |
293 | + | let assetId = gameToken() | |
294 | + | if ((i.feeAssetId != assetId)) | |
295 | + | then throw("Fee is not in correct assetId") | |
296 | + | else { | |
297 | + | let betStringList = split(bet, "-") | |
298 | + | let betType = betStringList[0] | |
299 | + | if (if (if (if (if (if ((betType != "numbers")) | |
300 | + | then (betType != "column") | |
301 | + | else false) | |
302 | + | then (betType != "color") | |
303 | + | else false) | |
304 | + | then (betType != "dozen") | |
305 | + | else false) | |
306 | + | then (betType != "oddeven") | |
307 | + | else false) | |
308 | + | then (betType != "lowhigh") | |
309 | + | else false) | |
310 | + | then throw("Bet type is not valid.") | |
311 | + | else match gamesGetCounter() { | |
312 | + | case GAMESCOUNTER: Int => | |
313 | + | match gameGetState(GAMESCOUNTER) { | |
314 | + | case state: Int => | |
315 | + | if ((state == 1)) | |
316 | + | then throw("Game is finished and next game has not started.") | |
317 | + | else if ((size(i.payments) != 1)) | |
318 | + | then throw("Payment not attached or there is more then one payment") | |
319 | + | else { | |
320 | + | let p = i.payments[0] | |
321 | + | let amount = p.amount | |
322 | + | if (if (if ((value(gamesMaxBet()) >= amount)) | |
323 | + | then (amount >= value(gamesMinBet())) | |
324 | + | else false) | |
325 | + | then (i.payments[0].assetId == assetId) | |
326 | + | else false) | |
327 | + | then match gameGetEndHeight(GAMESCOUNTER) { | |
328 | + | case endHeight: Int => | |
329 | + | if ((endHeight > height)) | |
330 | + | then match gamesTotalBets() { | |
331 | + | case totalBets: Int => | |
332 | + | let thisBetNumber = (totalBets + 1) | |
333 | + | if ((assetId == unit)) | |
334 | + | then [IntegerEntry(gameTotalBetsLabel, (totalBets + 1)), StringEntry(betDetailsLabel(thisBetNumber), ((((toBase58String(i.caller.bytes) + "-") + toString(amount)) + "-") + bet)), IntegerEntry(betGameLabel(thisBetNumber), GAMESCOUNTER)] | |
335 | + | else [IntegerEntry(gameTotalBetsLabel, (totalBets + 1)), StringEntry(betDetailsLabel(thisBetNumber), ((((toBase58String(i.caller.bytes) + "-") + toString(amount)) + "-") + bet)), IntegerEntry(betGameLabel(thisBetNumber), GAMESCOUNTER)] | |
336 | + | case _ => | |
337 | + | throw("Game has not been initalized.") | |
338 | + | } | |
339 | + | else throw("Game has already exceeded betting round") | |
340 | + | case _ => | |
341 | + | throw("End height has not been defined.") | |
342 | + | } | |
343 | + | else throw((((((("Bet (" + toString(amount)) + ") is greater than maximum bet (") + toString(gamesMaxBet())) + "), lower than minimum (") + toString(gamesMinBet())) + ") or payment is not in correct assetId.")) | |
344 | + | } | |
345 | + | case _ => | |
346 | + | throw("Game does not exist or has already ended.") | |
347 | + | } | |
348 | + | case _ => | |
349 | + | throw("Game needs to be initialized.") | |
350 | + | } | |
351 | + | } | |
352 | + | } | |
119 | 353 | ||
120 | 354 | ||
121 | 355 | ||
122 | 356 | @Callable(i) | |
123 | - | func processNextBet () = match gameTotalBets() { | |
124 | - | case totalBets: Int => | |
125 | - | match gameProcessedBets() { | |
126 | - | case lastProcessedBet: Int => | |
127 | - | let nextBet = (lastProcessedBet + 1) | |
128 | - | if ((nextBet > totalBets)) | |
129 | - | then throw("Next bet has not been placed.") | |
130 | - | else match gamesGetCounter() { | |
131 | - | case gameNumber: Int => | |
132 | - | match gameWinnersCounter() { | |
133 | - | case winnersCount: Int => | |
134 | - | let result = gameGetResult(gameNumber) | |
135 | - | let betDetail = betDetails(nextBet) | |
136 | - | if ((betDetail == result)) | |
137 | - | then { | |
138 | - | let newWinnersCount = (winnersCount + 1) | |
139 | - | [IntegerEntry(gameProcessedBetsLabel, nextBet), IntegerEntry(gameWinnersCounterLabel, (winnersCount + 1)), StringEntry(winnerDetailsLabel(newWinnersCount), toBase58String(i.caller.bytes))] | |
140 | - | } | |
141 | - | else [IntegerEntry(gameProcessedBetsLabel, nextBet)] | |
357 | + | func endGame (rsaSign,gameNumber) = if (checkPermission(i.callerPublicKey)) | |
358 | + | then throw("endGame_FORBBIDEN") | |
359 | + | else match gameGetState(gameNumber) { | |
360 | + | case state: Int => | |
361 | + | if ((state == 1)) | |
362 | + | then throw("Game is finished") | |
363 | + | else match gameGetEndHeight(gameNumber) { | |
364 | + | case gameHeight: Int => | |
365 | + | if ((gameHeight > height)) | |
366 | + | then throw(("Not enough blocks have past since game has started, game ends at block " + toString(gameHeight))) | |
367 | + | else match gameGetSumSha(gameNumber) { | |
368 | + | case SUMSHA: String => | |
369 | + | let sig = fromBase64String(rsaSign) | |
370 | + | let rsaSigValid = rsaVerify(SHA256, fromBase58String(SUMSHA), sig, gameRSAPublic()) | |
371 | + | match blockInfoByHeight(gameHeight) { | |
372 | + | case previousBlock: BlockInfo => | |
373 | + | let randomHash = sha256((take(sig, 32) + take(previousBlock.generationSignature, 32))) | |
374 | + | if (!(rsaSigValid)) | |
375 | + | then throw("INVALID_RSA_SIGN") | |
376 | + | else { | |
377 | + | let index = wheel[((toInt(randomHash) + value(gameNumber)) % 37)] | |
378 | + | [IntegerEntry(gameStateLabel(gameNumber), 1), IntegerEntry(gameResultLabel(gameNumber), index), StringEntry(gameRandomHashLabel(gameNumber), toBase64String(randomHash))] | |
379 | + | } | |
380 | + | case _ => | |
381 | + | throw("Last block does not exist.") | |
382 | + | } | |
142 | 383 | case _ => | |
143 | - | throw(" | |
384 | + | throw("SHA does not exist") | |
144 | 385 | } | |
145 | - | case _ => | |
146 | - | throw("Game has not been initialized, total games not found") | |
147 | - | } | |
148 | - | case _ => | |
149 | - | throw("Game has not been initialized, total processed bets not found") | |
150 | - | } | |
151 | - | case _ => | |
152 | - | throw("Game has not been initialized, total bets not found") | |
153 | - | } | |
386 | + | case _ => | |
387 | + | throw("Cannot get game height.") | |
388 | + | } | |
389 | + | case _ => | |
390 | + | throw("Game does not exist or has already ended") | |
391 | + | } | |
154 | 392 | ||
155 | 393 | ||
156 | 394 | ||
157 | 395 | @Callable(i) | |
158 | - | func | |
159 | - | | |
160 | - | | |
161 | - | ||
162 | - | | |
163 | - | | |
164 | - | | |
165 | - | | |
166 | - | ||
167 | - | | |
168 | - | | |
396 | + | func setParameter (parameterName,parameterValue) = if (checkPermission(i.callerPublicKey)) | |
397 | + | then throw("setParameter_FORBBIDEN") | |
398 | + | else if ((parameterName == "changeMax")) | |
399 | + | then { | |
400 | + | let maximumValue = parseIntValue(parameterValue) | |
401 | + | let minimumValue = gamesMinBet() | |
402 | + | if ((0 > maximumValue)) | |
403 | + | then throw("value cannot be lower than 0") | |
404 | + | else if ((minimumValue >= maximumValue)) | |
405 | + | then throw((("value cannot be lower or equal than min bet (" + toString(minimumValue)) + ")")) | |
406 | + | else [IntegerEntry(gameMaxBetLabel, maximumValue)] | |
169 | 407 | } | |
170 | - | case _ => | |
171 | - | throw("Game has not been initialized, total winners not found") | |
172 | - | } | |
408 | + | else if ((parameterName == "changeMin")) | |
409 | + | then { | |
410 | + | let maximumValue = gamesMaxBet() | |
411 | + | let minimumValue = parseIntValue(parameterValue) | |
412 | + | if ((0 > minimumValue)) | |
413 | + | then throw("value cannot be lower than 0") | |
414 | + | else if ((minimumValue >= maximumValue)) | |
415 | + | then throw((("value cannot be greater or equal than min bet (" + toString(maximumValue)) + ")")) | |
416 | + | else [IntegerEntry(gameMinBetLabel, minimumValue)] | |
417 | + | } | |
418 | + | else if ((parameterName == "changeRSA")) | |
419 | + | then match gamesGetCounter() { | |
420 | + | case GAMESCOUNTER: Int => | |
421 | + | match gameGetState(GAMESCOUNTER) { | |
422 | + | case state: Int => | |
423 | + | if ((state != 1)) | |
424 | + | then throw("Game has to be finished.") | |
425 | + | else [StringEntry(gameRSAPublicLabel, parameterValue)] | |
426 | + | case _ => | |
427 | + | [StringEntry(gameRSAPublicLabel, parameterValue)] | |
428 | + | } | |
429 | + | case _ => | |
430 | + | throw("Game needs to be initialized.") | |
431 | + | } | |
432 | + | else throw("Setting not found. Allowed: changeMax, changeMin, changeRSA, changePubKey") | |
173 | 433 | ||
174 | 434 | ||
175 | - | ||
176 | - | @Callable(i) | |
177 | - | func processNextWinner () = match gameWinnersCounter() { | |
178 | - | case totalWinners: Int => | |
179 | - | if ((totalWinners == 0)) | |
180 | - | then throw("There are no winners this round.") | |
181 | - | else match gameProcessedWinners() { | |
182 | - | case lastProcessedWinner: Int => | |
183 | - | let nextWinner = (lastProcessedWinner + 1) | |
184 | - | if ((nextWinner > totalWinners)) | |
185 | - | then throw("No more winners to be processed.") | |
186 | - | else match gamePrizeShareAmount() { | |
187 | - | case prizeShareAmount: Int => | |
188 | - | let betPlacer = winnerDetails(nextWinner) | |
189 | - | let address = value(addressFromString(betPlacer)) | |
190 | - | let winnings = gamePrizeShareAmount() | |
191 | - | [ScriptTransfer(address, prizeShareAmount, gameToken())] | |
192 | - | case _ => | |
193 | - | throw("Game has not been initialized, prize share amount not found") | |
194 | - | } | |
195 | - | case _ => | |
196 | - | throw("Game has not been initialized, total processed winners not found") | |
197 | - | } | |
198 | - | case _ => | |
199 | - | throw("Game has not been initialized, total winners not found") | |
200 | - | } | |
201 | - | ||
435 | + | @Verifier(tx) | |
436 | + | func verify () = sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) | |
202 | 437 |
github/deemru/w8io/169f3d6 56.26 ms ◑