SC CODE: // DERO Dice -- permissionless dice table registry. RANDOM() seeds from
// Keccak256(SCID,BLID,TXID) -- nobody can steer a roll. modifier/total can
// go negative via sign+magnitude (Uint64 has no sign). requireApproval:
// opt-in guard (0=open). status: 0 pending, 1 approved, 2 denied, 3 banned.
Function InitializePrivate() Uint64
10 STORE("owner", address())
20 STORE("name", "DERO Dice")
30 STORE("tableCount", 0)
1000 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
// Shared, pure-read (not a listed entrypoint, confirmed via check-sc-code).
// IF ... THEN RETURN directly (no GOTO) is not valid DVM-BASIC syntax --
// confirmed live (a real regression caught by the scenario suite, not
// guessed) -- every branch here goes through GOTO 900, matching every
// other function in this file.
Function validTable(table_id Uint64) Uint64
10 IF table_id == 0 THEN GOTO 900
20 IF table_id > LOAD("tableCount") THEN GOTO 900
30 RETURN 1
900 RETURN 0
End Function
Function isTableOwner(table_id Uint64) Uint64
10 IF address() != LOAD("table_"+table_id+"_owner") THEN GOTO 900
20 RETURN 1
900 RETURN 0
End Function
Function CreateTable(name String, requireApproval Uint64) Uint64
10 DIM id as Uint64
20 LET id = LOAD("tableCount") + 1
30 STORE("table_"+id+"_name", name)
40 STORE("table_"+id+"_owner", address())
50 STORE("table_"+id+"_rollCount", 0)
55 IF requireApproval == 0 THEN GOTO 60
57 STORE("table_"+id+"_requireApproval", 1)
60 STORE("tableCount", id)
1000 RETURN 0
End Function
// One-shot signup. Two keys: O(1) status lookup, and an enumerable
// playerReq index for the owner's pending-request UI.
Function JoinTable(table_id Uint64) Uint64
10 DIM n as Uint64
30 IF validTable(table_id) == 0 THEN GOTO 900
50 IF EXISTS("table_"+table_id+"_closed") == 0 THEN GOTO 80
60 IF LOAD("table_"+table_id+"_closed") == 1 THEN GOTO 900
80 IF EXISTS("table_"+table_id+"_player_"+address()+"_status") == 0 THEN GOTO 100
90 GOTO 900
100 IF EXISTS("table_"+table_id+"_playerCount") == 0 THEN GOTO 106
103 LET n = LOAD("table_"+table_id+"_playerCount")
106 LET n = n + 1
110 STORE("table_"+table_id+"_player_"+address()+"_status", 0)
120 STORE("table_"+table_id+"_playerReq_"+n+"_addr", address())
130 STORE("table_"+table_id+"_playerCount", n)
140 RETURN 0
900 RETURN 1
End Function
// Shared by ApproveJoin/DenyJoin -- status STORE stays in each caller.
Function canDecideJoin(table_id Uint64, player String) Uint64
10 IF validTable(table_id) == 0 THEN GOTO 900
20 IF isTableOwner(table_id) == 0 THEN GOTO 900
30 IF EXISTS("table_"+table_id+"_player_"+player+"_status") == 0 THEN GOTO 900
40 IF LOAD("table_"+table_id+"_player_"+player+"_status") != 0 THEN GOTO 900
50 RETURN 1
900 RETURN 0
End Function
// Owner-only, pending (status 0) only.
Function ApproveJoin(table_id Uint64, player String) Uint64
10 IF canDecideJoin(table_id, player) == 0 THEN GOTO 900
20 STORE("table_"+table_id+"_player_"+player+"_status", 1)
30 RETURN 0
900 RETURN 1
End Function
Function DenyJoin(table_id Uint64, player String) Uint64
10 IF canDecideJoin(table_id, player) == 0 THEN GOTO 900
20 STORE("table_"+table_id+"_player_"+player+"_status", 2)
30 RETURN 0
900 RETURN 1
End Function
// Owner-only, unconditional STORE.
Function BanPlayer(table_id Uint64, player String) Uint64
10 IF validTable(table_id) == 0 THEN GOTO 900
20 IF isTableOwner(table_id) == 0 THEN GOTO 900
40 IF player == LOAD("table_"+table_id+"_owner") THEN GOTO 900
50 STORE("table_"+table_id+"_player_"+player+"_status", 3)
60 RETURN 0
900 RETURN 1
End Function
// Recursion (no FOR/WHILE). STOREs each die face under
// table_<t>_roll_<n>_die_<idx>, returns the running sum.
Function rollOne(t Uint64, n Uint64, idx Uint64, sides Uint64, remaining Uint64) Uint64
10 DIM face as Uint64
20 IF remaining == 0 THEN GOTO 30 ELSE GOTO 50
30 RETURN 0
50 LET face = RANDOM(sides) + 1
60 STORE("table_"+t+"_roll_"+n+"_die_"+idx, face)
70 RETURN face + rollOne(t, n, idx+1, sides, remaining-1)
End Function
// closed/requireApproval/player-status EXISTS-guarded (panics on a
// never-STOREd key). Banned always rejects; owner/open pass; else status
// must be 1. Up to 4 groups (sides_g, count_g), a param-count cap not a
// storage limit -- a group beyond `groups` is LET-zeroed so rollOne skips
// it and no key is STOREd; a declared group with a zero count/sides is
// rejected. Total dice capped at 100. Signed modifier: modifierNegative
// flips subtract vs add -- never a raw subtraction (would underflow if
// modifier exceeds sum), compares first so total/totalNegative are always
// safe. Both *Negative flags only STOREd when 1. Old rolls lack "groups"/
// *Negative keys -- render.js EXISTS-guards and falls back accordingly.
Function RollDice(table_id Uint64, groups Uint64, sides_1 Uint64, count_1 Uint64, sides_2 Uint64, count_2 Uint64, sides_3 Uint64, count_3 Uint64, sides_4 Uint64, count_4 Uint64, modifier Uint64, modifierNegative Uint64) Uint64
10 DIM n, sum, total_count, idx2, idx3, idx4, sum1, sum2, sum3, sum4, total, totalNegative as Uint64
20 IF validTable(table_id) == 0 THEN GOTO 900
32 IF EXISTS("table_"+table_id+"_closed") == 0 THEN GOTO 36
34 IF LOAD("table_"+table_id+"_closed") == 1 THEN GOTO 900
36 IF EXISTS("table_"+table_id+"_player_"+address()+"_status") == 0 THEN GOTO 42
38 IF LOAD("table_"+table_id+"_player_"+address()+"_status") == 3 THEN GOTO 900
42 IF isTableOwner(table_id) == 1 THEN GOTO 130
44 IF EXISTS("table_"+table_id+"_requireApproval") == 0 THEN GOTO 130
46 IF EXISTS("table_"+table_id+"_player_"+address()+"_status") == 0 THEN GOTO 900
48 IF LOAD("table_"+table_id+"_player_"+address()+"_status") != 1 THEN GOTO 900
130 IF groups == 0 THEN GOTO 900
132 IF groups > 4 THEN GOTO 900
134 IF modifierNegative > 1 THEN GOTO 900
140 IF groups >= 2 THEN GOTO 150
142 LET count_2 = 0
144 LET sides_2 = 0
150 IF groups >= 3 THEN GOTO 160
152 LET count_3 = 0
154 LET sides_3 = 0
160 IF groups >= 4 THEN GOTO 170
162 LET count_4 = 0
164 LET sides_4 = 0
170 IF count_1 == 0 THEN GOTO 900
171 IF sides_1 == 0 THEN GOTO 900
172 IF groups < 2 THEN GOTO 178
173 IF count_2 == 0 THEN GOTO 900
174 IF sides_2 == 0 THEN GOTO 900
178 IF groups < 3 THEN GOTO 184
179 IF count_3 == 0 THEN GOTO 900
180 IF sides_3 == 0 THEN GOTO 900
184 IF groups < 4 THEN GOTO 190
185 IF count_4 == 0 THEN GOTO 900
186 IF sides_4 == 0 THEN GOTO 900
190 LET total_count = count_1 + count_2 + count_3 + count_4
191 IF total_count > 100 THEN GOTO 900
200 LET n = LOAD("table_"+table_id+"_rollCount") + 1
210 LET sum1 = rollOne(table_id, n, 1, sides_1, count_1)
220 LET idx2 = count_1 + 1
230 LET sum2 = rollOne(table_id, n, idx2, sides_2, count_2)
240 LET idx3 = idx2 + count_2
250 LET sum3 = rollOne(table_id, n, idx3, sides_3, count_3)
260 LET idx4 = idx3 + count_3
270 LET sum4 = rollOne(table_id, n, idx4, sides_4, count_4)
280 LET sum = sum1 + sum2 + sum3 + sum4
281 IF modifierNegative == 0 THEN GOTO 288
282 IF modifier > sum THEN GOTO 285
283 LET total = sum - modifier
284 GOTO 300
285 LET total = modifier - sum
286 LET totalNegative = 1
287 GOTO 300
288 LET total = sum + modifier
300 STORE("table_"+table_id+"_roll_"+n+"_groups", groups)
310 STORE("table_"+table_id+"_roll_"+n+"_sides_1", sides_1)
312 STORE("table_"+table_id+"_roll_"+n+"_count_1", count_1)
320 IF groups < 2 THEN GOTO 330
322 STORE("table_"+table_id+"_roll_"+n+"_sides_2", sides_2)
324 STORE("table_"+table_id+"_roll_"+n+"_count_2", count_2)
330 IF groups < 3 THEN GOTO 340
332 STORE("table_"+table_id+"_roll_"+n+"_sides_3", sides_3)
334 STORE("table_"+table_id+"_roll_"+n+"_count_3", count_3)
340 IF groups < 4 THEN GOTO 350
342 STORE("table_"+table_id+"_roll_"+n+"_sides_4", sides_4)
344 STORE("table_"+table_id+"_roll_"+n+"_count_4", count_4)
350 STORE("table_"+table_id+"_roll_"+n+"_modifier", modifier)
352 IF modifierNegative == 0 THEN GOTO 360
354 STORE("table_"+table_id+"_roll_"+n+"_modifierNegative", 1)
360 STORE("table_"+table_id+"_roll_"+n+"_sum", sum)
370 STORE("table_"+table_id+"_roll_"+n+"_total", total)
372 IF totalNegative == 0 THEN GOTO 380
374 STORE("table_"+table_id+"_roll_"+n+"_totalNegative", 1)
380 STORE("table_"+table_id+"_roll_"+n+"_roller", address())
390 STORE("table_"+table_id+"_roll_"+n+"_height", BLOCK_HEIGHT())
400 STORE("table_"+table_id+"_rollCount", n)
410 RETURN 0
900 RETURN 1
End Function
// Owner-only, irreversible.
Function CloseTable(table_id Uint64) Uint64
10 IF validTable(table_id) == 0 THEN GOTO 900
20 IF isTableOwner(table_id) == 0 THEN GOTO 900
40 STORE("table_"+table_id+"_closed", 1)
50 RETURN 0
900 RETURN 1
End Function
// UPDATE_SC_CODE only replaces bytecode -- table_T_* keys survive. Push
// ceiling ~9953 bytes (charged TWICE: SCDATA args + internal STORE;
// GasEstimate.Status is always "OK" -- check GasStorage yourself against
// config.MAX_STORAGE_GAS_ATOMIC_UNITS). Fragment-assembly (LOAD costs
// len/10) raises this to ~18000 if ever needed -- see storage_probe.bas.
Function UpdateCode(code String) Uint64
10 IF address() == LOAD("owner") THEN GOTO 40
20 RETURN 1
40 UPDATE_SC_CODE(code)
50 RETURN 0
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function |
| SC Arguments: [Name:SC_ACTION Type:uint64 Value:'1' Name:SC_CODE Type:string Value:'// DERO Dice -- permissionless dice table registry. RANDOM() seeds from
// Keccak256(SCID,BLID,TXID) -- nobody can steer a roll. modifier/total can
// go negative via sign+magnitude (Uint64 has no sign). requireApproval:
// opt-in guard (0=open). status: 0 pending, 1 approved, 2 denied, 3 banned.
Function InitializePrivate() Uint64
10 STORE("owner", address())
20 STORE("name", "DERO Dice")
30 STORE("tableCount", 0)
1000 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
// Shared, pure-read (not a listed entrypoint, confirmed via check-sc-code).
// IF ... THEN RETURN directly (no GOTO) is not valid DVM-BASIC syntax --
// confirmed live (a real regression caught by the scenario suite, not
// guessed) -- every branch here goes through GOTO 900, matching every
// other function in this file.
Function validTable(table_id Uint64) Uint64
10 IF table_id == 0 THEN GOTO 900
20 IF table_id > LOAD("tableCount") THEN GOTO 900
30 RETURN 1
900 RETURN 0
End Function
Function isTableOwner(table_id Uint64) Uint64
10 IF address() != LOAD("table_"+table_id+"_owner") THEN GOTO 900
20 RETURN 1
900 RETURN 0
End Function
Function CreateTable(name String, requireApproval Uint64) Uint64
10 DIM id as Uint64
20 LET id = LOAD("tableCount") + 1
30 STORE("table_"+id+"_name", name)
40 STORE("table_"+id+"_owner", address())
50 STORE("table_"+id+"_rollCount", 0)
55 IF requireApproval == 0 THEN GOTO 60
57 STORE("table_"+id+"_requireApproval", 1)
60 STORE("tableCount", id)
1000 RETURN 0
End Function
// One-shot signup. Two keys: O(1) status lookup, and an enumerable
// playerReq index for the owner's pending-request UI.
Function JoinTable(table_id Uint64) Uint64
10 DIM n as Uint64
30 IF validTable(table_id) == 0 THEN GOTO 900
50 IF EXISTS("table_"+table_id+"_closed") == 0 THEN GOTO 80
60 IF LOAD("table_"+table_id+"_closed") == 1 THEN GOTO 900
80 IF EXISTS("table_"+table_id+"_player_"+address()+"_status") == 0 THEN GOTO 100
90 GOTO 900
100 IF EXISTS("table_"+table_id+"_playerCount") == 0 THEN GOTO 106
103 LET n = LOAD("table_"+table_id+"_playerCount")
106 LET n = n + 1
110 STORE("table_"+table_id+"_player_"+address()+"_status", 0)
120 STORE("table_"+table_id+"_playerReq_"+n+"_addr", address())
130 STORE("table_"+table_id+"_playerCount", n)
140 RETURN 0
900 RETURN 1
End Function
// Shared by ApproveJoin/DenyJoin -- status STORE stays in each caller.
Function canDecideJoin(table_id Uint64, player String) Uint64
10 IF validTable(table_id) == 0 THEN GOTO 900
20 IF isTableOwner(table_id) == 0 THEN GOTO 900
30 IF EXISTS("table_"+table_id+"_player_"+player+"_status") == 0 THEN GOTO 900
40 IF LOAD("table_"+table_id+"_player_"+player+"_status") != 0 THEN GOTO 900
50 RETURN 1
900 RETURN 0
End Function
// Owner-only, pending (status 0) only.
Function ApproveJoin(table_id Uint64, player String) Uint64
10 IF canDecideJoin(table_id, player) == 0 THEN GOTO 900
20 STORE("table_"+table_id+"_player_"+player+"_status", 1)
30 RETURN 0
900 RETURN 1
End Function
Function DenyJoin(table_id Uint64, player String) Uint64
10 IF canDecideJoin(table_id, player) == 0 THEN GOTO 900
20 STORE("table_"+table_id+"_player_"+player+"_status", 2)
30 RETURN 0
900 RETURN 1
End Function
// Owner-only, unconditional STORE.
Function BanPlayer(table_id Uint64, player String) Uint64
10 IF validTable(table_id) == 0 THEN GOTO 900
20 IF isTableOwner(table_id) == 0 THEN GOTO 900
40 IF player == LOAD("table_"+table_id+"_owner") THEN GOTO 900
50 STORE("table_"+table_id+"_player_"+player+"_status", 3)
60 RETURN 0
900 RETURN 1
End Function
// Recursion (no FOR/WHILE). STOREs each die face under
// table_<t>_roll_<n>_die_<idx>, returns the running sum.
Function rollOne(t Uint64, n Uint64, idx Uint64, sides Uint64, remaining Uint64) Uint64
10 DIM face as Uint64
20 IF remaining == 0 THEN GOTO 30 ELSE GOTO 50
30 RETURN 0
50 LET face = RANDOM(sides) + 1
60 STORE("table_"+t+"_roll_"+n+"_die_"+idx, face)
70 RETURN face + rollOne(t, n, idx+1, sides, remaining-1)
End Function
// closed/requireApproval/player-status EXISTS-guarded (panics on a
// never-STOREd key). Banned always rejects; owner/open pass; else status
// must be 1. Up to 4 groups (sides_g, count_g), a param-count cap not a
// storage limit -- a group beyond `groups` is LET-zeroed so rollOne skips
// it and no key is STOREd; a declared group with a zero count/sides is
// rejected. Total dice capped at 100. Signed modifier: modifierNegative
// flips subtract vs add -- never a raw subtraction (would underflow if
// modifier exceeds sum), compares first so total/totalNegative are always
// safe. Both *Negative flags only STOREd when 1. Old rolls lack "groups"/
// *Negative keys -- render.js EXISTS-guards and falls back accordingly.
Function RollDice(table_id Uint64, groups Uint64, sides_1 Uint64, count_1 Uint64, sides_2 Uint64, count_2 Uint64, sides_3 Uint64, count_3 Uint64, sides_4 Uint64, count_4 Uint64, modifier Uint64, modifierNegative Uint64) Uint64
10 DIM n, sum, total_count, idx2, idx3, idx4, sum1, sum2, sum3, sum4, total, totalNegative as Uint64
20 IF validTable(table_id) == 0 THEN GOTO 900
32 IF EXISTS("table_"+table_id+"_closed") == 0 THEN GOTO 36
34 IF LOAD("table_"+table_id+"_closed") == 1 THEN GOTO 900
36 IF EXISTS("table_"+table_id+"_player_"+address()+"_status") == 0 THEN GOTO 42
38 IF LOAD("table_"+table_id+"_player_"+address()+"_status") == 3 THEN GOTO 900
42 IF isTableOwner(table_id) == 1 THEN GOTO 130
44 IF EXISTS("table_"+table_id+"_requireApproval") == 0 THEN GOTO 130
46 IF EXISTS("table_"+table_id+"_player_"+address()+"_status") == 0 THEN GOTO 900
48 IF LOAD("table_"+table_id+"_player_"+address()+"_status") != 1 THEN GOTO 900
130 IF groups == 0 THEN GOTO 900
132 IF groups > 4 THEN GOTO 900
134 IF modifierNegative > 1 THEN GOTO 900
140 IF groups >= 2 THEN GOTO 150
142 LET count_2 = 0
144 LET sides_2 = 0
150 IF groups >= 3 THEN GOTO 160
152 LET count_3 = 0
154 LET sides_3 = 0
160 IF groups >= 4 THEN GOTO 170
162 LET count_4 = 0
164 LET sides_4 = 0
170 IF count_1 == 0 THEN GOTO 900
171 IF sides_1 == 0 THEN GOTO 900
172 IF groups < 2 THEN GOTO 178
173 IF count_2 == 0 THEN GOTO 900
174 IF sides_2 == 0 THEN GOTO 900
178 IF groups < 3 THEN GOTO 184
179 IF count_3 == 0 THEN GOTO 900
180 IF sides_3 == 0 THEN GOTO 900
184 IF groups < 4 THEN GOTO 190
185 IF count_4 == 0 THEN GOTO 900
186 IF sides_4 == 0 THEN GOTO 900
190 LET total_count = count_1 + count_2 + count_3 + count_4
191 IF total_count > 100 THEN GOTO 900
200 LET n = LOAD("table_"+table_id+"_rollCount") + 1
210 LET sum1 = rollOne(table_id, n, 1, sides_1, count_1)
220 LET idx2 = count_1 + 1
230 LET sum2 = rollOne(table_id, n, idx2, sides_2, count_2)
240 LET idx3 = idx2 + count_2
250 LET sum3 = rollOne(table_id, n, idx3, sides_3, count_3)
260 LET idx4 = idx3 + count_3
270 LET sum4 = rollOne(table_id, n, idx4, sides_4, count_4)
280 LET sum = sum1 + sum2 + sum3 + sum4
281 IF modifierNegative == 0 THEN GOTO 288
282 IF modifier > sum THEN GOTO 285
283 LET total = sum - modifier
284 GOTO 300
285 LET total = modifier - sum
286 LET totalNegative = 1
287 GOTO 300
288 LET total = sum + modifier
300 STORE("table_"+table_id+"_roll_"+n+"_groups", groups)
310 STORE("table_"+table_id+"_roll_"+n+"_sides_1", sides_1)
312 STORE("table_"+table_id+"_roll_"+n+"_count_1", count_1)
320 IF groups < 2 THEN GOTO 330
322 STORE("table_"+table_id+"_roll_"+n+"_sides_2", sides_2)
324 STORE("table_"+table_id+"_roll_"+n+"_count_2", count_2)
330 IF groups < 3 THEN GOTO 340
332 STORE("table_"+table_id+"_roll_"+n+"_sides_3", sides_3)
334 STORE("table_"+table_id+"_roll_"+n+"_count_3", count_3)
340 IF groups < 4 THEN GOTO 350
342 STORE("table_"+table_id+"_roll_"+n+"_sides_4", sides_4)
344 STORE("table_"+table_id+"_roll_"+n+"_count_4", count_4)
350 STORE("table_"+table_id+"_roll_"+n+"_modifier", modifier)
352 IF modifierNegative == 0 THEN GOTO 360
354 STORE("table_"+table_id+"_roll_"+n+"_modifierNegative", 1)
360 STORE("table_"+table_id+"_roll_"+n+"_sum", sum)
370 STORE("table_"+table_id+"_roll_"+n+"_total", total)
372 IF totalNegative == 0 THEN GOTO 380
374 STORE("table_"+table_id+"_roll_"+n+"_totalNegative", 1)
380 STORE("table_"+table_id+"_roll_"+n+"_roller", address())
390 STORE("table_"+table_id+"_roll_"+n+"_height", BLOCK_HEIGHT())
400 STORE("table_"+table_id+"_rollCount", n)
410 RETURN 0
900 RETURN 1
End Function
// Owner-only, irreversible.
Function CloseTable(table_id Uint64) Uint64
10 IF validTable(table_id) == 0 THEN GOTO 900
20 IF isTableOwner(table_id) == 0 THEN GOTO 900
40 STORE("table_"+table_id+"_closed", 1)
50 RETURN 0
900 RETURN 1
End Function
// UPDATE_SC_CODE only replaces bytecode -- table_T_* keys survive. Push
// ceiling ~9953 bytes (charged TWICE: SCDATA args + internal STORE;
// GasEstimate.Status is always "OK" -- check GasStorage yourself against
// config.MAX_STORAGE_GAS_ATOMIC_UNITS). Fragment-assembly (LOAD costs
// len/10) raises this to ~18000 if ever needed -- see storage_probe.bas.
Function UpdateCode(code String) Uint64
10 IF address() == LOAD("owner") THEN GOTO 40
20 RETURN 1
40 UPDATE_SC_CODE(code)
50 RETURN 0
End Function
// This function is used to change owner
// owner is an string form of address
Function TransferOwnership(newowner String) Uint64
10 IF LOAD("owner") == SIGNER() THEN GOTO 30
20 RETURN 1
30 STORE("tmpowner",ADDRESS_RAW(newowner))
40 RETURN 0
End Function'] |