Skip to content

Commit 050d4b6

Browse files
committed
Added casino files
1 parent 820f8ce commit 050d4b6

File tree

3 files changed

+280
-0
lines changed

3 files changed

+280
-0
lines changed

casino/readme.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Casino
2+
3+
This is an implementation of the Casino with new RIDE features, so called ride 4 dapps
4+
5+
## Oracle
6+
7+
You need an oracle which will provide results for the round
8+
Oracle must provide the result in the following form
9+
roundKey -> result
10+
where roundKey is a string identifying the round, and result is a string in which:
11+
character 0..1 - represent winning number
12+
character 2 - represents winning red|black
13+
character 3 - represents winiing even|odd
14+
character 4 - represents winning half of the desk
15+
character 5 - represents winning third of the desk
16+
character 6 - represents winning row of the desk
17+
18+
## Making bet
19+
20+
To participate you need to invoke 'bet' function with
21+
string argument identifying the round which you want to participate
22+
int argument identifying type of the bet (0 - number, 1 - red|black, 2 - even|odd ...)
23+
int argument representing you guess according to the bet type
24+
And you need to put not less than 0.5 Waves to the call - it's your bet
25+
You can make multiple invokes of the 'bet' function for the same round
26+
27+
After the oracle put stop mark for the round (data transaction with key <Round_id>_stop)
28+
you can not make bets for this round anymore.
29+
30+
## Getting your prize if you win
31+
After the oracle put result you can invoke 'withdraw' function with
32+
string argument identifying the round
33+
The function will send all money you won

casino/ride/casino.ride

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{-# STDLIB_VERSION 3 #-}
2+
{-# CONTENT_TYPE DAPP #-}
3+
{-# SCRIPT_TYPE ACCOUNT #-}
4+
5+
let oracle = extract(addressFromString("$ORACLE_ADDRESS"))
6+
let minBet = 50000000
7+
let maxSumBet = 1000000000 # max sum bet for round
8+
9+
func calcWinAmount(this: Address|Alias, key: String, koeff: Int) = {
10+
11+
match getInteger(this, key) {
12+
case a: Int => a * koeff
13+
case _ => 0
14+
}
15+
}
16+
17+
@Callable(i)
18+
func bet(round: String, guessType: Int, guessValue: Int) = {
19+
20+
let pmt = extract(i.payment)
21+
22+
if (isDefined(pmt.assetId)) then
23+
throw("Bets only in Waves supported")
24+
else if (pmt.amount < minBet) then
25+
throw("Your Bet amount is less then minimal bet " + toString(minBet))
26+
else if (isDefined(getBoolean(oracle, round + "_stop")) || isDefined(getString(oracle, round))) then
27+
throw("This round is already played")
28+
else {
29+
let roundBetsKey = round + "_sumBets"
30+
let curSumBets = match getInteger(this, roundBetsKey) {
31+
case a:Int => a
32+
case _ => 0
33+
}
34+
let newSumBets = curSumBets + pmt.amount
35+
36+
if (newSumBets > maxSumBet) then
37+
throw("Maximum amount of bets for round " + toString(maxSumBet) + ". With your bet it's " + toString(newSumBets))
38+
else
39+
{
40+
let betKey = toBase58String(i.caller.bytes) + "_" + round
41+
+ "_" + toString(guessType) + "_" + toString(guessValue)
42+
let curBetAmount = match getInteger(this, betKey) {
43+
case a:Int => a
44+
case _ => 0
45+
}
46+
let newBetAmount = curBetAmount + pmt.amount
47+
48+
WriteSet([ DataEntry(betKey, newBetAmount),
49+
DataEntry(roundBetsKey, newSumBets)])
50+
}
51+
}
52+
}
53+
54+
@Callable(i)
55+
func withdraw(round: String) = {
56+
let betKeyPart = toBase58String(i.caller.bytes) + "_" + round
57+
let withdrawKey = betKeyPart + "_withdraw"
58+
59+
if (isDefined(getInteger(this, withdrawKey))) then {
60+
throw("You have already got your prize")
61+
#WriteSet([])
62+
}
63+
else
64+
{
65+
let valComplex = getStringValue(oracle, round)
66+
67+
let winNum = parseIntValue(drop(take(valComplex, 2), 0))
68+
let winRedBlack = parseIntValue(drop(take(valComplex, 3), 2))
69+
let winEvenOdd = parseIntValue(drop(take(valComplex, 4), 3))
70+
let winDeskHalf = parseIntValue(drop(take(valComplex, 5), 4))
71+
let winDeskThird = parseIntValue(drop(take(valComplex, 6), 5))
72+
let winRow = parseIntValue(drop(take(valComplex, 7), 6))
73+
74+
75+
let winAmount = calcWinAmount(this, betKeyPart + "_0_" + toString(winNum), 36) +
76+
calcWinAmount(this, betKeyPart + "_1_" + toString(winRedBlack), 2) +
77+
calcWinAmount(this, betKeyPart + "_2_" + toString(winEvenOdd), 2) +
78+
calcWinAmount(this, betKeyPart + "_3_" + toString(winDeskHalf), 2) +
79+
calcWinAmount(this, betKeyPart + "_4_" + toString(winDeskThird), 3) +
80+
calcWinAmount(this, betKeyPart + "_5_" + toString(winRow), 3)
81+
82+
if (winAmount == 0) then
83+
throw("You won nothing this round")
84+
else
85+
ScriptResult(
86+
WriteSet([DataEntry(withdrawKey, winAmount)]),
87+
TransferSet([ScriptTransfer(i.caller, winAmount, unit)])
88+
)
89+
}
90+
}
91+
92+
93+
@Verifier(tx)
94+
func verify() = {
95+
sigVerify(tx.bodyBytes, tx.proofs[0], base58'FYCT9GxhR2igEeyf9SWGi85bebBVaTAf9WUihEQnnBa9')
96+
#true
97+
}

casino/test/casino_test.js

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// ATTENTION: Tests can work incorrectly if you use nodes with cashed requests for balances
2+
// It could happen that 'testnodes.wavesnodes.com' has that kind of cache
3+
4+
const WAVES = 10 ** 8;
5+
6+
const SETSCRIPT_FEE = 0.01 * WAVES
7+
const ISSUE_FEE = 1 * WAVES
8+
const INV_FEE = 0.005 * WAVES
9+
const ADD_FEE = 0.004 * WAVES
10+
const MIN_FEE = 0.001 * WAVES
11+
12+
const BET_TYPE_NUMBER = 0
13+
const BET_TYPE_RED_BLACK = 1
14+
const BET_TYPE_EVEN_ODD = 2
15+
const BET_TYPE_DESK_HALF = 3
16+
const BET_TYPE_DESK_THIRD = 4
17+
const BET_TYPE_ROW = 5
18+
19+
const betAmount = 1.5 * WAVES
20+
21+
async function rememberBalances(text, forAddress) {
22+
const wavesBal = await balance(forAddress)
23+
24+
console.log (text + ": " + wavesBal + " WAVES")
25+
26+
return wavesBal
27+
}
28+
29+
function makeBet(casinoPubKey, roundId, betAmount, betType, guess, playerPubKey) {
30+
31+
const bet = invokeScript({fee:INV_FEE, dApp: address(casinoPubKey),
32+
call: {function:"bet", args:[{type:"string", value: roundId}, {type:"integer", value: betType}, {type:"integer", value: guess}]},
33+
payment: [{amount: betAmount, assetId:null }]},
34+
playerPubKey)
35+
36+
console.log("Bet. round: '" + roundId + "', amount: " + betAmount / 10 ** 8 + ", betType: " + betType + ", guess: '" + guess + "'")
37+
return bet
38+
}
39+
40+
function withdraw(casinoPubKey, roundId, playerPubKey) {
41+
42+
const withdrawTx = invokeScript({fee:INV_FEE, dApp: address(casinoPubKey),
43+
call: {function:"withdraw", args:[{type:"string", value: roundId}]},
44+
payment: []},
45+
playerPubKey)
46+
47+
return withdrawTx
48+
}
49+
50+
async function oracleStopRound(oraclePublicKey, roundId) {
51+
52+
const stopRound = data({data:[{key:roundId + '_stop', value:true, type: 'boolean'}]}, oraclePublicKey)
53+
54+
await broadcast(stopRound)
55+
await waitForTx(stopRound.id)
56+
console.log("Stop round: " + roundId)
57+
}
58+
59+
func makeOracleResultForNumber() {
60+
const s = "0" + theAnswer
61+
// TODO:
62+
// Currently emulating just bet on number. These "12345" digits must be calculated in other way
63+
// "1" must be changed to black/red indication
64+
// "2" must be changed to even/odd indication
65+
// etc.
66+
return s.substr(s.length - 2) + "12345"
67+
}
68+
69+
async function oraclePublishCorrectAnswer(oraclePublicKey, roundId, theAnswer) {
70+
71+
const answerStr = makeOracleResultForNumber(theAnswer)
72+
73+
const oraclePublishAnswer = data({data:[{key:roundId, value:answerStr, type: 'string'}]}, oraclePublicKey)
74+
75+
await broadcast(oraclePublishAnswer)
76+
await waitForTx(oraclePublishAnswer.id)
77+
console.log("Published result for round '" + roundId + "': " + answerStr)
78+
}
79+
80+
81+
describe('Casino script test suite', async function () {
82+
83+
this.timeout(100000);
84+
85+
before(async function () {
86+
87+
await setupAccounts({casino: SETSCRIPT_FEE + 35 * betAmount,
88+
player1: 2*betAmount + 2*INV_FEE,
89+
player2: betAmount + 2*INV_FEE,
90+
player3: betAmount + 2*INV_FEE,
91+
oracle: 2 * INV_FEE});
92+
93+
const scriptC = compile(file('casino.ride').replace('$ORACLE_ADDRESS', address(accounts.oracle)));
94+
const ssTx = setScript({script:scriptC}, accounts.casino);
95+
await broadcast(ssTx);
96+
await waitForTx(ssTx.id)
97+
console.log('Script has been set')
98+
});
99+
100+
const roundId = "Round 1"
101+
const correctAnswer = 12
102+
103+
it('Making bets', async function(){
104+
105+
const bet1 = makeBet(accounts.casino, roundId, betAmount, BET_TYPE_NUMBER, 11, accounts.player1)
106+
await broadcast(bet1)
107+
const bet2 = makeBet(accounts.casino, roundId, betAmount, BET_TYPE_NUMBER, 10, accounts.player2)
108+
await broadcast(bet2)
109+
const bet3 = makeBet(accounts.casino, roundId, betAmount, BET_TYPE_NUMBER, correctAnswer, accounts.player3)
110+
await broadcast(bet3)
111+
112+
await waitForTx(bet1.id)
113+
await waitForTx(bet2.id)
114+
await waitForTx(bet3.id)
115+
})
116+
117+
it('Inability to make bet after freeze round', async function(){
118+
119+
await oracleStopRound(accounts.oracle, roundId)
120+
121+
const bet1 = makeBet(accounts.casino, roundId, betAmount, BET_TYPE_NUMBER, 14, accounts.player1)
122+
await expect(broadcast(bet1)).rejectedWith("This round is already played")
123+
console.log("Bet must failed because round finished")
124+
})
125+
126+
it('Withdraw test', async function(){
127+
128+
await oraclePublishCorrectAnswer(accounts.oracle, roundId, correctAnswer)
129+
130+
const withdraw1 = withdraw(accounts.casino, roundId, accounts.player1)
131+
const withdraw2 = withdraw(accounts.casino, roundId, accounts.player2)
132+
const withdraw3 = withdraw(accounts.casino, roundId, accounts.player3)
133+
134+
const casinoBefore = await rememberBalances("casino before: ", address(accounts.casino))
135+
const winnerBefore = await rememberBalances("winner before: ", address(accounts.player3))
136+
137+
expect(broadcast(withdraw1)).rejectedWith("You won nothing this round")
138+
expect(broadcast(withdraw2)).rejectedWith("You won nothing this round")
139+
await broadcast(withdraw3)
140+
await waitForTx(withdraw3.id)
141+
142+
const casinoAfter = await rememberBalances("casino after: ", address(accounts.casino))
143+
const winnerAfter = await rememberBalances("winner after: ", address(accounts.player3))
144+
145+
expect(casinoAfter).to.equal(casinoBefore - 36 * betAmount, "Casino account reduced by win amount")
146+
expect(winnerAfter).to.equal(winnerBefore + 36 * betAmount - INV_FEE, "Winner account got win amount")
147+
})
148+
149+
150+
})

0 commit comments

Comments
 (0)