├── .soliumignore
├── app
├── robots.txt
├── favicon.ico
├── images
│ ├── imp.png
│ ├── mag.png
│ ├── bg-eth.jpg
│ ├── bg_1024.jpg
│ ├── bg_2048.jpg
│ ├── bg_2880.jpg
│ ├── favicon.ico
│ ├── identicon1.png
│ ├── identicon2.png
│ ├── identicon3.png
│ ├── bg-eth-1024.jpg
│ ├── icons-72x72.png
│ ├── doge-donations.png
│ ├── ether-donations.png
│ ├── icons-144x144.png
│ ├── icons-180x180.png
│ ├── icons-192x192.png
│ ├── bitcoin-donations.png
│ ├── money_with_wings.png
│ └── litecoin-donations.png
├── fonts
│ └── roboto
│ │ ├── Roboto-Bold.woff
│ │ ├── Roboto-Bold.woff2
│ │ ├── Roboto-Light.woff
│ │ ├── Roboto-Light.woff2
│ │ ├── Roboto-Medium.woff
│ │ ├── Roboto-Thin.woff
│ │ ├── Roboto-Thin.woff2
│ │ ├── Roboto-Medium.woff2
│ │ ├── Roboto-Regular.woff
│ │ └── Roboto-Regular.woff2
├── styles
│ ├── emoji.css
│ └── main.css
├── scripts
│ ├── blockies.js
│ └── lotthereum.js
├── grid.html
└── index.html
├── .gitignore
├── README.md
├── truffle
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── truffle.js
├── contracts
│ ├── Mortal.sol
│ ├── Migrations.sol
│ ├── SafeMath.sol
│ └── Lotthereum.sol
└── test
│ └── lotthereum.js
├── bower.json
├── .soliumrc.json
├── LICENSE
├── package.json
└── gulpfile.js
/.soliumignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org/
2 |
3 | User-agent: *
4 | Disallow:
5 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | truffle/build
3 | .tmp
4 | dist
5 | node_modules
6 | bower_components
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lotthereum
2 | Open source decentralized lottery based on Ethereum blockchain
3 |
--------------------------------------------------------------------------------
/app/images/imp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/imp.png
--------------------------------------------------------------------------------
/app/images/mag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/mag.png
--------------------------------------------------------------------------------
/app/images/bg-eth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/bg-eth.jpg
--------------------------------------------------------------------------------
/app/images/bg_1024.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/bg_1024.jpg
--------------------------------------------------------------------------------
/app/images/bg_2048.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/bg_2048.jpg
--------------------------------------------------------------------------------
/app/images/bg_2880.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/bg_2880.jpg
--------------------------------------------------------------------------------
/app/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/favicon.ico
--------------------------------------------------------------------------------
/app/images/identicon1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/identicon1.png
--------------------------------------------------------------------------------
/app/images/identicon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/identicon2.png
--------------------------------------------------------------------------------
/app/images/identicon3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/identicon3.png
--------------------------------------------------------------------------------
/app/images/bg-eth-1024.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/bg-eth-1024.jpg
--------------------------------------------------------------------------------
/app/images/icons-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/icons-72x72.png
--------------------------------------------------------------------------------
/app/images/doge-donations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/doge-donations.png
--------------------------------------------------------------------------------
/app/images/ether-donations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/ether-donations.png
--------------------------------------------------------------------------------
/app/images/icons-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/icons-144x144.png
--------------------------------------------------------------------------------
/app/images/icons-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/icons-180x180.png
--------------------------------------------------------------------------------
/app/images/icons-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/icons-192x192.png
--------------------------------------------------------------------------------
/app/images/bitcoin-donations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/bitcoin-donations.png
--------------------------------------------------------------------------------
/app/images/money_with_wings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/money_with_wings.png
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Bold.woff
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Bold.woff2
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Light.woff
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Light.woff2
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Medium.woff
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Thin.woff
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Thin.woff2
--------------------------------------------------------------------------------
/app/images/litecoin-donations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/images/litecoin-donations.png
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Medium.woff2
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Regular.woff
--------------------------------------------------------------------------------
/app/fonts/roboto/Roboto-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lotthereum/source/HEAD/app/fonts/roboto/Roboto-Regular.woff2
--------------------------------------------------------------------------------
/truffle/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | var Migrations = artifacts.require("./Migrations.sol");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/truffle/truffle.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | networks: {
3 | development: {
4 | host: "localhost",
5 | port: 8545,
6 | network_id: "*", // Match any network id
7 | // from: "0xf86c5833419fd517e38179519ccf102155d48952"
8 | }
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lotthereum",
3 | "private": true,
4 | "dependencies": {
5 | "jquery": "~2.1.1",
6 | "materialize": "~0.98.2",
7 | "async": "^2.5.0"
8 | },
9 | "devDependencies": {
10 | "chai": "~4.0.2",
11 | "mocha": "~3.4.2"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/styles/emoji.css:
--------------------------------------------------------------------------------
1 | .em{height:1.5em;width:1.5em;background-position:center;background-repeat:no-repeat;background-size:contain;display:inline-block;vertical-align:middle}.em-money_with_wings{background-image:url("/images/money_with_wings.png")}.em-imp{background-image:url("/images/imp.png")}.em-mag{background-image:url("/images/mag.png")}
2 |
--------------------------------------------------------------------------------
/truffle/contracts/Mortal.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.11;
2 |
3 |
4 | contract Owned {
5 | address owner;
6 |
7 | modifier onlyowner() {
8 | if (msg.sender == owner) {
9 | _;
10 | }
11 | }
12 |
13 | function Owned() {
14 | owner = msg.sender;
15 | }
16 | }
17 |
18 |
19 | contract Mortal is Owned {
20 |
21 | function kill() {
22 | if (msg.sender == owner)
23 | selfdestruct(owner);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/truffle/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | var Mortal = artifacts.require("./Mortal.sol");
2 | var Lotthereum = artifacts.require("./Lotthereum.sol");
3 |
4 | module.exports = function(deployer) {
5 | hash = '0x93036b147316017199338e191dbff124b5358520517f23a4b38db9769850f4ca';
6 | deployer.deploy(Mortal);
7 | deployer.link(Mortal, Lotthereum);
8 | // deployer.deploy(Lotthereum, 1, 20, 100000000000000000, 1000000000000000000, hash);
9 | deployer.deploy(Lotthereum);
10 | };
11 |
--------------------------------------------------------------------------------
/.soliumrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "custom-rules-filename": null,
3 | "rules": {
4 | "imports-on-top": true,
5 | "variable-declarations": true,
6 | "array-declarations": true,
7 | "operator-whitespace": true,
8 | "lbrace": true,
9 | "mixedcase": true,
10 | "camelcase": true,
11 | "uppercase": true,
12 | "no-with": true,
13 | "no-empty-blocks": true,
14 | "no-unused-vars": true,
15 | "double-quotes": true,
16 | "blank-lines": true,
17 | "indentation": true,
18 | "whitespace": true,
19 | "deprecated-suicide": true,
20 | "pragma-on-top": true
21 | }
22 | }
--------------------------------------------------------------------------------
/truffle/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.4;
2 |
3 | contract Migrations {
4 | address public owner;
5 | uint public last_completed_migration;
6 |
7 | modifier restricted() {
8 | if (msg.sender == owner) _;
9 | }
10 |
11 | function Migrations() {
12 | owner = msg.sender;
13 | }
14 |
15 | function setCompleted(uint completed) restricted {
16 | last_completed_migration = completed;
17 | }
18 |
19 | function upgrade(address new_address) restricted {
20 | Migrations upgraded = Migrations(new_address);
21 | upgraded.setCompleted(last_completed_migration);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Lotthereum
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "engines": {
4 | "node": ">=4"
5 | },
6 | "devDependencies": {
7 | "babel-core": "^6.4.0",
8 | "babel-preset-es2015": "^6.3.13",
9 | "babel-register": "^6.5.2",
10 | "browser-sync": "^2.2.1",
11 | "del": "^2.2.0",
12 | "gulp": "^3.9.0",
13 | "gulp-autoprefixer": "^3.0.1",
14 | "gulp-babel": "^6.1.1",
15 | "gulp-cache": "^0.4.2",
16 | "gulp-cssnano": "^2.0.0",
17 | "gulp-eslint": "^3.0.0",
18 | "gulp-htmlmin": "^3.0.0",
19 | "gulp-if": "^2.0.2",
20 | "gulp-imagemin": "^3.0.1",
21 | "gulp-load-plugins": "^1.2.4",
22 | "gulp-plumber": "^1.0.1",
23 | "gulp-size": "^2.1.0",
24 | "gulp-sourcemaps": "^2.2.0",
25 | "gulp-uglify": "^2.0.0",
26 | "gulp-useref": "^3.0.0",
27 | "main-bower-files": "^2.5.0",
28 | "run-sequence": "^1.2.2",
29 | "wiredep": "^4.0.0"
30 | },
31 | "eslintConfig": {
32 | "env": {
33 | "es6": true,
34 | "node": true,
35 | "browser": true,
36 | "jquery": true
37 | },
38 | "rules": {
39 | "quotes": [
40 | 2,
41 | "single"
42 | ]
43 | }
44 | },
45 | "dependencies": {}
46 | }
47 |
--------------------------------------------------------------------------------
/truffle/contracts/SafeMath.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.11;
2 |
3 |
4 | contract SafeMath {
5 |
6 | // ensure that the result of adding x and y is accurate
7 | function add(uint x, uint y) internal constant returns (uint z) {
8 | assert((z = x + y) >= x);
9 | }
10 |
11 | // ensure that the result of subtracting y from x is accurate
12 | function subtract(uint x, uint y) internal constant returns (uint z) {
13 | assert((z = x - y) <= x);
14 | }
15 |
16 | // ensure that the result of multiplying x and y is accurate
17 | function multiply(uint x, uint y) internal constant returns (uint z) {
18 | z = x * y;
19 | assert(x == 0 || z / x == y);
20 | return z;
21 | }
22 |
23 | // ensure that the result of dividing x and y is accurate
24 | // note: Solidity now throws on division by zero, so a check is not needed
25 | function divide(uint x, uint y) internal constant returns (uint z) {
26 | z = x / y;
27 | assert(x == ( (y * z) + (x % y) ));
28 | return z;
29 | }
30 |
31 | // return the lowest of two 64 bit integers
32 | function min64(uint64 x, uint64 y) internal constant returns (uint64) {
33 | return x < y ? x: y;
34 | }
35 |
36 | // return the largest of two 64 bit integers
37 | function max64(uint64 x, uint64 y) internal constant returns (uint64) {
38 | return x >= y ? x : y;
39 | }
40 |
41 | // return the lowest of two values
42 | function min(uint x, uint y) internal constant returns (uint) {
43 | return (x <= y) ? x : y;
44 | }
45 |
46 | // return the largest of two values
47 | function max(uint x, uint y) internal constant returns (uint) {
48 | return (x >= y) ? x : y;
49 | }
50 |
51 | function assert(bool assertion) internal {
52 | if (!assertion) {
53 | revert();
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/app/scripts/blockies.js:
--------------------------------------------------------------------------------
1 | // https://github.com/alexvandesande/blockies/blob/master/blockies.js
2 | // updated tayvano 3.9.16
3 | (function() {
4 | // The random number is a js implementation of the Xorshift PRNG
5 | var randseed = new Array(4); // Xorshift: [x, y, z, w] 32 bit values
6 |
7 | function seedrand(seed) {
8 | for (var i = 0; i < randseed.length; i++) {
9 | randseed[i] = 0;
10 | }
11 | for (var i = 0; i < seed.length; i++) {
12 | randseed[i%4] = ((randseed[i%4] << 5) - randseed[i%4]) + seed.charCodeAt(i);
13 | }
14 | }
15 |
16 | function rand() {
17 | // based on Java's String.hashCode(), expanded to 4 32bit values
18 | var t = randseed[0] ^ (randseed[0] << 11);
19 |
20 | randseed[0] = randseed[1];
21 | randseed[1] = randseed[2];
22 | randseed[2] = randseed[3];
23 | randseed[3] = (randseed[3] ^ (randseed[3] >> 19) ^ t ^ (t >> 8));
24 |
25 | return (randseed[3]>>>0) / ((1 << 31)>>>0);
26 | }
27 |
28 | function createColor() {
29 | //saturation is the whole color spectrum
30 | var h = Math.floor(rand() * 360);
31 | //saturation goes from 40 to 100, it avoids greyish colors
32 | var s = ((rand() * 60) + 40) + '%';
33 | //lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%
34 | var l = ((rand()+rand()+rand()+rand()) * 25) + '%';
35 |
36 | var color = 'hsl(' + h + ',' + s + ',' + l + ')';
37 | return color;
38 | }
39 |
40 | function createImageData(size) {
41 | var width = size; // Only support square icons for now
42 | var height = size;
43 |
44 | var dataWidth = Math.ceil(width / 2);
45 | var mirrorWidth = width - dataWidth;
46 |
47 | var data = [];
48 | for(var y = 0; y < height; y++) {
49 | var row = [];
50 | for(var x = 0; x < dataWidth; x++) {
51 | // this makes foreground and background color to have a 43% (1/2.3) probability
52 | // spot color has 13% chance
53 | row[x] = Math.floor(rand()*2.3);
54 | }
55 | var r = row.slice(0, mirrorWidth);
56 | r.reverse();
57 | row = row.concat(r);
58 |
59 | for(var i = 0; i < row.length; i++) {
60 | data.push(row[i]);
61 | }
62 | }
63 |
64 | return data;
65 | }
66 |
67 | function createCanvas(imageData, color, scale, bgcolor, spotcolor) {
68 | var c = document.createElement('canvas');
69 | var width = Math.sqrt(imageData.length);
70 | c.width = c.height = width * scale;
71 |
72 | var cc = c.getContext('2d');
73 | cc.fillStyle = bgcolor;
74 | cc.fillRect(0, 0, c.width, c.height);
75 | cc.fillStyle = color;
76 |
77 | for(var i = 0; i < imageData.length; i++) {
78 | var row = Math.floor(i / width);
79 | var col = i % width;
80 | // if data is 2, choose spot color, if 1 choose foreground
81 | cc.fillStyle = (imageData[i] == 1) ? color : spotcolor;
82 |
83 | // if data is 0, leave the background
84 | if(imageData[i]) {
85 | cc.fillRect(col * scale, row * scale, scale, scale);
86 | }
87 | }
88 |
89 | return c;
90 | }
91 |
92 | function createIcon(opts) {
93 | opts = opts || {};
94 | var size = opts.size || 8;
95 | var scale = opts.scale || 4;
96 | var seed = opts.seed || Math.floor((Math.random()*Math.pow(10,16))).toString(16);
97 |
98 | seedrand(seed);
99 |
100 | var color = opts.color || createColor();
101 | var bgcolor = opts.bgcolor || createColor();
102 | var spotcolor = opts.spotcolor || createColor();
103 | var imageData = createImageData(size);
104 | var canvas = createCanvas(imageData, color, scale, bgcolor, spotcolor);
105 |
106 | return canvas;
107 | }
108 |
109 | window.blockies = {create: createIcon};
110 | })();
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | // generated on 2017-06-14 using generator-webapp 3.0.1
2 | const gulp = require('gulp');
3 | const gulpLoadPlugins = require('gulp-load-plugins');
4 | const browserSync = require('browser-sync').create();
5 | const del = require('del');
6 | const wiredep = require('wiredep').stream;
7 | const runSequence = require('run-sequence');
8 |
9 | const $ = gulpLoadPlugins();
10 | const reload = browserSync.reload;
11 |
12 | let dev = true;
13 |
14 | gulp.task('styles', () => {
15 | return gulp.src('app/styles/*.css')
16 | .pipe($.if(dev, $.sourcemaps.init()))
17 | .pipe($.autoprefixer({browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']}))
18 | .pipe($.if(dev, $.sourcemaps.write()))
19 | .pipe(gulp.dest('.tmp/styles'))
20 | .pipe(reload({stream: true}));
21 | });
22 |
23 | gulp.task('scripts', () => {
24 | return gulp.src('app/scripts/**/*.js')
25 | .pipe($.plumber())
26 | .pipe($.if(dev, $.sourcemaps.init()))
27 | .pipe($.babel())
28 | .pipe($.if(dev, $.sourcemaps.write('.')))
29 | .pipe(gulp.dest('.tmp/scripts'))
30 | .pipe(reload({stream: true}));
31 | });
32 |
33 | function lint(files) {
34 | return gulp.src(files)
35 | .pipe($.eslint({ fix: true }))
36 | .pipe(reload({stream: true, once: true}))
37 | .pipe($.eslint.format())
38 | .pipe($.if(!browserSync.active, $.eslint.failAfterError()));
39 | }
40 |
41 | gulp.task('lint', () => {
42 | return lint('app/scripts/**/*.js')
43 | .pipe(gulp.dest('app/scripts'));
44 | });
45 | gulp.task('lint:test', () => {
46 | return lint('test/spec/**/*.js')
47 | .pipe(gulp.dest('test/spec'));
48 | });
49 |
50 | gulp.task('html', ['styles', 'scripts'], () => {
51 | return gulp.src('app/*.html')
52 | .pipe($.useref({searchPath: ['.tmp', 'app', '.']}))
53 | .pipe($.if(/\.js$/, $.uglify({compress: {drop_console: true}})))
54 | .pipe($.if(/\.css$/, $.cssnano({safe: true, autoprefixer: false})))
55 | .pipe($.if(/\.html$/, $.htmlmin({
56 | collapseWhitespace: true,
57 | minifyCSS: true,
58 | minifyJS: {compress: {drop_console: true}},
59 | processConditionalComments: true,
60 | removeComments: true,
61 | removeEmptyAttributes: true,
62 | removeScriptTypeAttributes: true,
63 | removeStyleLinkTypeAttributes: true
64 | })))
65 | .pipe(gulp.dest('dist'));
66 | });
67 |
68 | gulp.task('images', () => {
69 | return gulp.src('app/images/**/*')
70 | .pipe($.cache($.imagemin()))
71 | .pipe(gulp.dest('dist/images'));
72 | });
73 |
74 | gulp.task('fonts', () => {
75 | return gulp.src(require('main-bower-files')('**/*.{eot,svg,ttf,woff,woff2}', function (err) {})
76 | .concat('app/fonts/**/*'))
77 | .pipe($.if(dev, gulp.dest('.tmp/fonts'), gulp.dest('dist/fonts')));
78 | });
79 |
80 | gulp.task('extras', () => {
81 | return gulp.src([
82 | 'app/*',
83 | '!app/*.html'
84 | ], {
85 | dot: true
86 | }).pipe(gulp.dest('dist'));
87 | });
88 |
89 | gulp.task('clean', del.bind(null, ['.tmp', 'dist']));
90 |
91 | gulp.task('serve', () => {
92 | runSequence(['clean', 'wiredep'], ['styles', 'scripts', 'fonts'], () => {
93 | browserSync.init({
94 | notify: false,
95 | port: 9000,
96 | server: {
97 | baseDir: ['.tmp', 'app'],
98 | routes: {
99 | '/bower_components': 'bower_components'
100 | }
101 | }
102 | });
103 |
104 | gulp.watch([
105 | 'app/*.html',
106 | 'app/images/**/*',
107 | '.tmp/fonts/**/*'
108 | ]).on('change', reload);
109 |
110 | gulp.watch('app/styles/**/*.css', ['styles']);
111 | gulp.watch('app/scripts/**/*.js', ['scripts']);
112 | gulp.watch('app/fonts/**/*', ['fonts']);
113 | gulp.watch('bower.json', ['wiredep', 'fonts']);
114 | });
115 | });
116 |
117 | gulp.task('serve:dist', ['default'], () => {
118 | browserSync.init({
119 | notify: false,
120 | port: 9000,
121 | server: {
122 | baseDir: ['dist']
123 | }
124 | });
125 | });
126 |
127 | gulp.task('serve:test', ['scripts'], () => {
128 | browserSync.init({
129 | notify: false,
130 | port: 9000,
131 | ui: false,
132 | server: {
133 | baseDir: 'test',
134 | routes: {
135 | '/scripts': '.tmp/scripts',
136 | '/bower_components': 'bower_components'
137 | }
138 | }
139 | });
140 |
141 | gulp.watch('app/scripts/**/*.js', ['scripts']);
142 | gulp.watch(['test/spec/**/*.js', 'test/index.html']).on('change', reload);
143 | gulp.watch('test/spec/**/*.js', ['lint:test']);
144 | });
145 |
146 | // inject bower components
147 | gulp.task('wiredep', () => {
148 | gulp.src('app/*.html')
149 | .pipe(wiredep({
150 | ignorePath: /^(\.\.\/)*\.\./
151 | }))
152 | .pipe(gulp.dest('app'));
153 | });
154 |
155 | gulp.task('build', ['lint', 'html', 'images', 'fonts', 'extras'], () => {
156 | return gulp.src('dist/**/*').pipe($.size({title: 'build', gzip: true}));
157 | });
158 |
159 | gulp.task('default', () => {
160 | return new Promise(resolve => {
161 | dev = false;
162 | runSequence(['clean', 'wiredep'], 'build', resolve);
163 | });
164 | });
165 |
--------------------------------------------------------------------------------
/app/styles/main.css:
--------------------------------------------------------------------------------
1 | .browserupgrade {
2 | margin: 0.2em 0;
3 | background: #ccc;
4 | color: #000;
5 | padding: 0.2em 0;
6 | }
7 |
8 | body::before {
9 | will-change: transform;
10 | z-index: -1;
11 | left: 0;
12 | right: 0;
13 | bottom: 0;
14 | top: 0;
15 | position: fixed;
16 | background: #2196f3 !important;
17 | }
18 |
19 | body {
20 | background: #2196f3 !important;
21 | }
22 |
23 | .brand-logo {
24 | margin-left: 21px;
25 | margin-left: 0;
26 | color: red;
27 | }
28 |
29 | .logo {
30 | padding: 15px;
31 | width: 64px;
32 | }
33 |
34 | .btn-floating img {
35 | width: 100%;
36 | }
37 |
38 | .bet-btn {
39 | top: 15px !important;
40 | right: 15px !important;
41 | }
42 |
43 | .collection-item {
44 | background: rgba(255, 255, 255, 0) !important;
45 | }
46 |
47 | .collection {
48 | border: 0 !important;
49 | }
50 |
51 | .avatar {
52 | min-height: 65px !important;
53 | }
54 |
55 | .collection .collection-item.avatar .title {
56 | position: absolute;
57 | top: 21px;
58 | }
59 |
60 | .collection .collection-item.avatar .secondary-content {
61 | top: 21px !important;
62 | }
63 |
64 | .btn-bar {
65 | position: relative !important;
66 | }
67 |
68 | .nav-wrapper {
69 | background: #2196F3;
70 | /*background: white;*/
71 | }
72 |
73 | .progress {
74 | margin-top: 15px !important;
75 | margin-bottom: 40px !important;
76 | }
77 |
78 | .round-data td {
79 | font-weight: 100 !important;
80 | }
81 |
82 | .round-data {
83 | border-top: 1px solid #e0e0e0;
84 | }
85 |
86 | .page-footer {
87 | /*display: none;*/
88 | margin-top: 50px;
89 | background: #43c5ff !important;
90 | margin-top: 10px;
91 | }
92 |
93 | .footer-copyright {
94 | background: #2196f3 !important;
95 | }
96 |
97 | .eth-address, .eth-address2 {
98 | width: 42px;
99 | height: 42px;
100 | background-size: cover;
101 | background-repeat: no-repeat;
102 | border-radius: 50%;
103 | box-shadow: inset rgba(255, 255, 255, 0.6) 0 2px 2px, inset rgba(0, 0, 0, 0.3) 0 -2px 6px, 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);
104 | /*box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2);*/
105 | text-indent: -9999px;
106 | position: absolute;
107 | left: 15px;
108 | }
109 |
110 | .pick h3 {
111 | margin-top: 7px !important;
112 | }
113 |
114 | .pick {
115 | margin: 5px;
116 | }
117 |
118 | #accounts_dropdown {
119 | width: 90% !important;
120 | margin-top: -9px;
121 | }
122 |
123 | .wait_transaction {
124 | font-weight: 100;
125 | margin-top: -33px;
126 | }
127 |
128 | .dropdown-content li > span {
129 | color: #e29f3d !important;
130 | }
131 |
132 | #confirmations {
133 | opacity: 0;
134 | }
135 |
136 | .confirmation-badge {
137 | position: absolute;
138 | left: 435px;
139 | top: 20px;
140 | }
141 |
142 | .template {
143 | display: none;
144 | }
145 |
146 | #loading {
147 | display: none;
148 | text-align: center;
149 | padding: 5%;
150 | }
151 |
152 | .round {
153 | margin-top: 30px !important;
154 | }
155 |
156 | .main-container {
157 | margin: 0 !important;
158 | width: 100% !important;
159 | max-width: 100% !important;
160 | padding: 20px 5% 10px 5% !important;
161 | background-color: white;
162 | display: none;
163 | }
164 |
165 | .pagination {
166 | text-align: center;
167 | }
168 |
169 | .tabs {
170 | width: 80% !important;
171 | z-index: 101;
172 | height: 56px !important;
173 | }
174 |
175 | .tabs, .tab, .tab a {
176 | height: 64px !important;
177 | line-height: 64px !important;
178 | background-color: #2196f3 !important;
179 | color: white !important;
180 | }
181 |
182 | .indicator {
183 | background-color: #ffb74d !important;
184 | }
185 |
186 | .avatar2 {
187 | position: absolute !important;
188 | top: 11px;
189 | right: 15px;
190 | left: auto;
191 | min-height: auto !important;
192 | width: 42px;
193 | height: 42px;
194 | }
195 |
196 | #avatar {
197 | display: none;
198 | cursor: pointer;
199 | }
200 |
201 | .number {
202 | padding-top: 4px;
203 | }
204 |
205 | .dropdown-content {
206 | width: 350px !important;
207 | background-color: rgba(255, 255, 255, 1) !important;
208 | padding: 20px 70px !important;
209 | }
210 |
211 | .dropdown-content li a {
212 | padding-left: 70px !important;
213 | font-size: 11px !important;
214 | }
215 |
216 | #dropdown-nav {
217 | padding: 0px !important;
218 | z-index: 1000 !important;
219 | top: 64px !important;
220 | right: 0px !important;
221 | }
222 |
223 | .square {
224 | width: 24px !important;
225 | height: 24px !important;
226 | }
227 |
228 | .navbar-fixed {
229 | /*display: none;*/
230 | }
231 |
232 | .balance {
233 | display: none;
234 | top: 21px;
235 | position: relative;
236 | margin-left: 0px !important;
237 | }
238 |
239 | .modal-footer {
240 | padding: 0px 15px 15px !important;
241 | }
242 |
243 | .modal-content {
244 | padding-bottom: 0;
245 | }
246 |
247 | .card-content {
248 | min-height: 230px;
249 | }
250 |
251 | .tab.disabled a {
252 | color: #c1c1c1 !important;
253 | }
254 |
255 | #bet1 .modal-content {
256 | padding-bottom: 0px;
257 | margin-bottom: 0px;
258 | }
259 |
260 | #bet1 table {
261 | width: 95%;
262 | margin: auto;
263 | }
264 |
265 | .collection-header {
266 | background: none !important;
267 | background: transparent !important;
268 | }
269 |
270 | #intro .modal-content {
271 | padding: 0px;
272 | }
273 |
274 | #intro .modal-content .collection {
275 | margin-top: 0px;
276 | }
277 |
278 | .gitter-chat-embed {
279 | z-index: 1000;
280 | }
281 |
282 | .alert-note {
283 | display: none;
284 | }
285 |
286 | .navbar-fixed nav {
287 | z-index: 100;
288 | }
289 |
290 | .dropdown-content li > a, .dropdown-content li > span {
291 | width: 100% !important;
292 | }
293 |
294 | #bet-modal .modal-content {
295 | padding-top: 0 !important;
296 | }
--------------------------------------------------------------------------------
/app/grid.html:
--------------------------------------------------------------------------------
1 |
Lotthereumplay_arrow Round 3233 bets remaining
65bc6e9eab5c...3.70
ff1f52e03373...5.87
290f2e39ad4e...3.76
Round 324| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
Round 325Round winners
65bc6e9eab5c...3.70
ff1f52e03373...5.87
290f2e39ad4e...3.76
Round Closed| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
Round Closed| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
Round Closed| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
Round Closed| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
Round Closed| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
Round Closed| Account | Prize |
|---|
 65bc6e9eab5c... | 5.87 |
 290f2e39ad4e... | 3.76 |
 ff1f52e03373... | $3.76 |
--------------------------------------------------------------------------------
/truffle/test/lotthereum.js:
--------------------------------------------------------------------------------
1 | var Lotthereum = artifacts.require("./Lotthereum.sol");
2 |
3 | contract('Lotthereum', function(accounts) {
4 | it("should allow owner to create multiple games", function(done) {
5 | var lotthereum;
6 | Lotthereum.deployed().then(function(instance) {
7 | watcher = instance.RoundOpen({origin: accounts[0]});
8 |
9 | instance.createGame(1, 20, 2, 20, {from: accounts[0]}).then(function() {
10 | return watcher.get();
11 | }).then(function(events) {
12 | assert.equal(events.length, 1);
13 | assert.equal(events[0].args.gameId.valueOf(), 0);
14 | assert.equal(events[0].args.roundId.valueOf(), 0);
15 |
16 | instance.createGame(2, 10, 5, 50, {from: accounts[0]}).then(function() {
17 | return watcher.get();
18 | }).then(function(events) {
19 | assert.equal(events.length, 1);
20 | assert.equal(events[0].args.gameId.valueOf(), 1);
21 | assert.equal(events[0].args.roundId.valueOf(), 0);
22 |
23 | instance.createGame(3, 10, 1, 10, {from: accounts[0]}).then(function() {
24 | return watcher.get();
25 | }).then(function(events) {
26 | assert.equal(events.length, 1);
27 | assert.equal(events[0].args.gameId.valueOf(), 2);
28 | assert.equal(events[0].args.roundId.valueOf(), 0);
29 | done();
30 | });
31 | });
32 | });
33 | });
34 | });
35 |
36 | it("should allow owner to close games", function(done) {
37 | var lotthereum;
38 | Lotthereum.deployed().then(function(instance) {
39 | var watcher = instance.GameClosed({gameId: 2});
40 | instance.closeGame(2, {from: accounts[0]}).then(function() {
41 | return watcher.get();
42 | }).then(function(events) {
43 | assert.equal(events.length, 1);
44 | assert.equal(events[0].args.gameId, 2);
45 |
46 | instance.getGames().then(function(games) {
47 | assert.equal(games.length, 2);
48 | assert.equal(games[0].valueOf(), 0);
49 | assert.equal(games[1].valueOf(), 1);
50 | }).then(done).catch(done);
51 | });
52 | });
53 | });
54 |
55 | it("should allow owner to open games", function(done) {
56 | var lotthereum;
57 | Lotthereum.deployed().then(function(instance) {
58 | var watcher = instance.GameOpened({gameId: 2});
59 | instance.openGame(2, {from: accounts[0]}).then(function() {
60 | return watcher.get();
61 | }).then(function(events) {
62 | assert.equal(events.length, 1);
63 | assert.equal(events[0].args.gameId, 2);
64 |
65 | instance.getGames().then(function(games) {
66 | assert.equal(games.length, 3);
67 | assert.equal(games[0].valueOf(), 0);
68 | assert.equal(games[1].valueOf(), 1);
69 | assert.equal(games[2].valueOf(), 2);
70 | }).then(done).catch(done);
71 | });
72 | });
73 | });
74 |
75 | it("should allow clients to get the current games", function(done) {
76 | var lotthereum;
77 | Lotthereum.deployed().then(function(instance) {
78 | return instance.getGames().then(function(games) {
79 | assert.equal(games.length, 3);
80 | assert.equal(games[0].valueOf(), 0);
81 | assert.equal(games[1].valueOf(), 1);
82 | assert.equal(games[2].valueOf(), 2);
83 | }).then(done).catch(done);
84 | });
85 | });
86 |
87 | it("should allow clients to get a game current round id", function(done) {
88 | var lotthereum;
89 | Lotthereum.deployed().then(function(instance) {
90 | return instance.getGameCurrentRoundId(0).then(function(roundId) {
91 | assert.equal(roundId.valueOf(), 0);
92 | }).then(done).catch(done);
93 | });
94 | });
95 |
96 | it("should allow clients to know if the round is open", function(done) {
97 | var lotthereum;
98 | var gameId = 0;
99 | var roundId = 0;
100 | Lotthereum.deployed().then(function(instance) {
101 | return instance.getGameRoundOpen(gameId, roundId).then(function(roundOpen) {
102 | assert.isTrue(roundOpen);
103 | }).then(done).catch(done);
104 | });
105 | });
106 |
107 | it("should allow clients to get the minimum bet amount of a game", function(done) {
108 | var lotthereum;
109 | var gameId = 0;
110 | Lotthereum.deployed().then(function(instance) {
111 | return instance.getGameMinAmountByBet(gameId).then(function(minBet) {
112 | assert.equal(minBet, 2);
113 | }).then(done).catch(done);
114 | });
115 | });
116 |
117 | it("should allow clients to get the maximum number of bets of a game", function(done) {
118 | var lotthereum;
119 | var gameId = 0;
120 | Lotthereum.deployed().then(function(instance) {
121 | return instance.getGameMaxNumberOfBets(gameId).then(function(maxNumberOfBets) {
122 | assert.equal(maxNumberOfBets, 20);
123 | }).then(done).catch(done);
124 | });
125 | });
126 |
127 | it("should allow clients to get the prize of a game", function(done) {
128 | var lotthereum;
129 | var gameId = 0;
130 | Lotthereum.deployed().then(function(instance) {
131 | return instance.getGamePrize(gameId).then(function(prize) {
132 | assert.equal(prize, 20);
133 | }).then(done).catch(done);
134 | });
135 | });
136 |
137 | it("should allow clients to get the number of bets already placed in a game round", function(done) {
138 | var lotthereum;
139 | var gameId = 0;
140 | var roundId = 0;
141 | Lotthereum.deployed().then(function(instance) {
142 | return instance.getRoundNumberOfBets(gameId, roundId).then(function(numberOfBets) {
143 | assert.equal(numberOfBets, 0);
144 | }).then(done).catch(done);
145 | });
146 | });
147 |
148 | it("should emit a BetPlaced event for every bet placed", function(done) {
149 | var lotthereum;
150 | var account = accounts[1];
151 | var gameId = 0;
152 | var roundId = 0;
153 | Lotthereum.deployed().then(function(instance) {
154 | var watcher = instance.BetPlaced({origin: account});
155 | instance.placeBet(gameId, 0, {from: account, value: 2}).then(function() {
156 | return watcher.get();
157 | }).then(function(events) {
158 | assert.equal(events.length, 1);
159 | assert.equal(events[0].args.origin, account);
160 | }).then(done).catch(done);
161 | });
162 | });
163 |
164 | it("should reject bets with value less than the game minAmountByBet", function(done) {
165 | var lotthereum;
166 | var account = accounts[1];
167 | var gameId = 0;
168 | Lotthereum.deployed().then(function(instance) {
169 | instance.placeBet(gameId, 0, {from: account, value: 1}).then(function(result) {
170 | assert.isNotNull(result.tx);
171 | assert.equal(result.receipt.logs.length, 0);
172 | }).then(done).catch(done);
173 | });
174 | });
175 |
176 | it("should allow 18 bets to be placed", function(done) {
177 | var lotthereum;
178 | var account = accounts[1];
179 | var gameId = 0;
180 | var roundId = 0;
181 | Lotthereum.deployed().then(function(instance) {
182 |
183 | watcher = instance.BetPlaced({origin: account});
184 | for (var i = 1; i <= 9; i++) {
185 | instance.placeBet(gameId, i, {from: account, value: 2}).then(function() {
186 | return watcher.get();
187 | }).then(function(events) {
188 | assert.equal(events.length, 1);
189 | assert.equal(events[0].args.origin, account);
190 | })
191 | }
192 |
193 | account = accounts[2];
194 | watcher = instance.BetPlaced({origin: account});
195 | for (var i = 0; i <= 8; i++) {
196 | instance.placeBet(gameId, i, {from: account, value: 2}).then(function() {
197 | return watcher.get();
198 | }).then(function(events) {
199 | assert.equal(events.length, 1);
200 | assert.equal(events[0].args.origin, account);
201 | })
202 | }
203 |
204 | done();
205 | });
206 | });
207 |
208 | it("should close the round when the last bet is placed", function(done) {
209 | var lotthereum;
210 | var account = accounts[2];
211 | var gameId = 0;
212 | var roundId = 0;
213 | var next_round = 1;
214 | var prize = 20;
215 | var maxNumberOfBets = 20;
216 | var minAmountByBet = 2;
217 |
218 | Lotthereum.deployed().then(function(instance) {
219 | var betPlacedWatcher = instance.BetPlaced({origin: account});
220 | var roundWinnerWatcher = instance.RoundWinner();
221 | var roundCloseWatcher = instance.RoundClose({gameId: gameId, roundId: roundId});
222 | var roundOpenWatcher = instance.RoundOpen({id: next_round});
223 | instance.placeBet(gameId, 9, {from: account, value: 2}).then(function() {
224 | return betPlacedWatcher.get();
225 | }).then(function(events) {
226 | assert.equal(events.length, 1);
227 | assert.equal(events[0].args.origin, account);
228 | return roundWinnerWatcher.get();
229 | }).then(function(events) {
230 | assert.equal(events.length, 2);
231 | assert.isTrue(events[0].args.winnerAddress == accounts[1] || events[0].args.winnerAddress == accounts[2])
232 | assert.equal(events[0].args.amount, prize / 2);
233 | return roundCloseWatcher.get();
234 | }).then(function(events) {
235 | assert.equal(events.length, 1);
236 | assert.equal(events[0].args.gameId, gameId);
237 | assert.equal(events[0].args.roundId, roundId);
238 | return roundOpenWatcher.get();
239 | }).then(function(events) {
240 | assert.equal(events.length, 1);
241 | assert.equal(events[0].args.gameId, gameId);
242 | assert.equal(events[0].args.roundId, next_round);
243 | }).then(done).catch(done);
244 | });
245 | });
246 |
247 | it("should allow clients to get the lucky number of a past round", function(done) {
248 | var lotthereum;
249 | var gameId = 0;
250 | var roundId = 0;
251 | Lotthereum.deployed().then(function(instance) {
252 | return instance.getRoundNumber(gameId, roundId).then(function(luckyNumber) {
253 | assert.isNotNull(luckyNumber);
254 | }).then(done).catch(done);
255 | });
256 | });
257 |
258 | it("should split the prize equaly by the round winners", function(done) {
259 | var lotthereum;
260 | var account = accounts[1];
261 | Lotthereum.deployed().then(function(instance) {
262 | instance.getBalance({from: account}).then(function(balance) {
263 | assert.equal(balance.valueOf(), 10);
264 | });
265 | account = accounts[2];
266 | instance.getBalance({from: account}).then(function(balance) {
267 | assert.equal(balance.valueOf(), 10);
268 | });
269 | });
270 | done();
271 | });
272 |
273 | it("should run automatically without mannual intervention... lets place 50 bets", function(done) {
274 | var lotthereum;
275 | var account = accounts[0];
276 | var gameId = 2;
277 | Lotthereum.deployed().then(function(instance) {
278 | var nextRoundId = 1;
279 | var numberOfRoundsOpenned = 0;
280 |
281 | var watcher = instance.BetPlaced({origin: account});
282 | var roundOpenWatcher = instance.RoundOpen({id: nextRoundId});
283 |
284 | var bet = -1;
285 | for (var i = 1; i <= 10; i++) {
286 |
287 | bet++;
288 | if (bet > 9) {
289 | bet = 0;
290 | }
291 | instance.placeBet(gameId, bet, {from: account, value: 5}).then(function() {
292 | return watcher.get();
293 | }).then(function(events) {
294 | if (typeof events != 'undefined') {
295 | if (events.length > 0) {
296 | assert.equal(events[0].args.origin, account);
297 | return roundOpenWatcher.get();
298 | }
299 | }
300 | }).then(function(events) {
301 | if (typeof events != 'undefined') {
302 | if (events.length > 0) {
303 | assert.equal(events.length, 1);
304 | assert.equal(events[0].args.gameId.valueOf(), gameId);
305 | assert.equal(events[0].args.roundId.valueOf(), 1);
306 | numberOfRoundsOpenned++;
307 | nextRoundId++;
308 | return numberOfRoundsOpenned;
309 | }
310 | }
311 | }).then(function(numberOfRoundsOpenned) {
312 | if (numberOfRoundsOpenned == 10) {
313 | done();
314 | }
315 | })
316 | }
317 | });
318 | });
319 |
320 | });
321 |
--------------------------------------------------------------------------------
/truffle/contracts/Lotthereum.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.16;
2 |
3 |
4 | contract SafeMath {
5 |
6 | // ensure that the result of adding x and y is accurate
7 | function add(uint x, uint y) internal constant returns (uint z) {
8 | assert((z = x + y) >= x);
9 | }
10 |
11 | // ensure that the result of subtracting y from x is accurate
12 | function subtract(uint x, uint y) internal constant returns (uint z) {
13 | assert((z = x - y) <= x);
14 | }
15 |
16 | // ensure that the result of multiplying x and y is accurate
17 | function multiply(uint x, uint y) internal constant returns (uint z) {
18 | z = x * y;
19 | assert(x == 0 || z / x == y);
20 | return z;
21 | }
22 |
23 | // ensure that the result of dividing x and y is accurate
24 | // note: Solidity now throws on division by zero, so a check is not needed
25 | function divide(uint x, uint y) internal constant returns (uint z) {
26 | z = x / y;
27 | assert(x == ( (y * z) + (x % y) ));
28 | return z;
29 | }
30 |
31 | // return the lowest of two 64 bit integers
32 | function min64(uint64 x, uint64 y) internal constant returns (uint64) {
33 | return x < y ? x: y;
34 | }
35 |
36 | // return the largest of two 64 bit integers
37 | function max64(uint64 x, uint64 y) internal constant returns (uint64) {
38 | return x >= y ? x : y;
39 | }
40 |
41 | // return the lowest of two values
42 | function min(uint x, uint y) internal constant returns (uint) {
43 | return (x <= y) ? x : y;
44 | }
45 |
46 | // return the largest of two values
47 | function max(uint x, uint y) internal constant returns (uint) {
48 | return (x >= y) ? x : y;
49 | }
50 |
51 | function assert(bool assertion) internal {
52 | if (!assertion) {
53 | revert();
54 | }
55 | }
56 | }
57 |
58 |
59 | contract Owned {
60 | address owner;
61 |
62 | modifier onlyowner() {
63 | if (msg.sender == owner) {
64 | _;
65 | }
66 | }
67 |
68 | function Owned() internal {
69 | owner = msg.sender;
70 | }
71 | }
72 |
73 |
74 | contract Mortal is Owned {
75 | function kill() public onlyowner {
76 | selfdestruct(owner);
77 | }
78 | }
79 |
80 |
81 | contract Lotthereum is Mortal, SafeMath {
82 |
83 | Game[] private games;
84 | mapping (address => uint) private balances; // balances per address
85 |
86 | struct Game {
87 | uint id;
88 | bool open;
89 | uint pointer;
90 | uint maxNumberOfBets;
91 | uint minAmountByBet;
92 | uint prize;
93 | uint currentRound;
94 | Round[] rounds;
95 | }
96 |
97 | struct Round {
98 | uint id;
99 | uint pointer;
100 | bytes32 hash;
101 | bool open;
102 | uint8 number;
103 | Bet[] bets;
104 | address[] winners;
105 | }
106 |
107 | struct Bet {
108 | uint id;
109 | address origin;
110 | uint amount;
111 | uint8 bet;
112 | uint round;
113 | }
114 |
115 | event RoundOpen(
116 | uint indexed gameId,
117 | uint indexed roundId
118 | );
119 | event RoundClose(
120 | uint indexed gameId,
121 | uint indexed roundId,
122 | uint8 number
123 | );
124 | event MaxNumberOfBetsChanged(
125 | uint maxNumberOfBets
126 | );
127 | event MinAmountByBetChanged(
128 | uint minAmountByBet
129 | );
130 | event BetPlaced(
131 | uint indexed gameId,
132 | uint indexed roundId,
133 | address indexed origin,
134 | uint betId
135 | );
136 | event RoundWinner(
137 | uint indexed gameId,
138 | uint indexed roundId,
139 | address indexed winnerAddress,
140 | uint amount
141 | );
142 | event GameOpened(
143 | uint indexed gameId
144 | );
145 | event GameClosed(
146 | uint indexed gameId
147 | );
148 |
149 | function createGame(
150 | uint pointer,
151 | uint maxNumberOfBets,
152 | uint minAmountByBet,
153 | uint prize
154 | ) onlyowner returns (uint id) {
155 | id = games.length;
156 | games.length += 1;
157 | games[id].id = id;
158 | games[id].pointer = pointer;
159 | games[id].maxNumberOfBets = maxNumberOfBets;
160 | games[id].minAmountByBet = minAmountByBet;
161 | games[id].prize = prize;
162 | games[id].open = true;
163 | games[id].currentRound = createGameRound(id);
164 | }
165 |
166 | function closeGame(uint gameId) onlyowner returns (bool) {
167 | games[gameId].open = false;
168 | GameClosed(gameId);
169 | return true;
170 | }
171 |
172 | function openGame(uint gameId) onlyowner returns (bool) {
173 | games[gameId].open = true;
174 | GameOpened(gameId);
175 | return true;
176 | }
177 |
178 | function createGameRound(uint gameId) internal returns (uint id) {
179 | id = games[gameId].rounds.length;
180 | games[gameId].rounds.length += 1;
181 | games[gameId].rounds[id].id = id;
182 | games[gameId].rounds[id].open = true;
183 | RoundOpen(gameId, id);
184 | }
185 |
186 | function payout(uint gameId) internal {
187 | address[] winners = games[gameId].rounds[games[gameId].currentRound].winners;
188 | for (uint i = 0; i < games[gameId].maxNumberOfBets -1; i++) {
189 | if (games[gameId].rounds[games[gameId].currentRound].bets[i].bet == games[gameId].rounds[games[gameId].currentRound].number) {
190 | uint id = winners.length;
191 | winners.length += 1;
192 | winners[id] = games[gameId].rounds[games[gameId].currentRound].bets[i].origin;
193 | }
194 | }
195 |
196 | if (winners.length > 0) {
197 | uint prize = divide(games[gameId].prize, winners.length);
198 | for (i = 0; i < winners.length; i++) {
199 | balances[winners[i]] = add(balances[winners[i]], prize);
200 | RoundWinner(gameId, games[gameId].currentRound, winners[i], prize);
201 | }
202 | }
203 | }
204 |
205 | function closeRound(uint gameId) internal {
206 | games[gameId].rounds[games[gameId].currentRound].open = false;
207 | games[gameId].rounds[games[gameId].currentRound].hash = getBlockHash(games[gameId].pointer);
208 | games[gameId].rounds[games[gameId].currentRound].number = getNumber(games[gameId].rounds[games[gameId].currentRound].hash);
209 | // games[gameId].pointer = games[gameId].rounds[games[gameId].currentRound].number;
210 | payout(gameId);
211 | RoundClose(
212 | gameId,
213 | games[gameId].rounds[games[gameId].currentRound].id,
214 | games[gameId].rounds[games[gameId].currentRound].number
215 | );
216 | games[gameId].currentRound = createGameRound(gameId);
217 | }
218 |
219 | function getBlockHash(uint i) constant returns (bytes32 blockHash) {
220 | if (i > 255) {
221 | i = 255;
222 | }
223 | blockHash = block.blockhash(block.number - i);
224 | }
225 |
226 | function getNumber(bytes32 _a) constant returns (uint8) {
227 | uint8 _b = 1;
228 | uint8 mint = 0;
229 | bool decimals = false;
230 | for (uint i = _a.length - 1; i >= 0; i--) {
231 | if ((_a[i] >= 48) && (_a[i] <= 57)) {
232 | if (decimals) {
233 | if (_b == 0) {
234 | break;
235 | } else {
236 | _b--;
237 | }
238 | }
239 | mint *= 10;
240 | mint += uint8(_a[i]) - 48;
241 | return mint;
242 | } else if (_a[i] == 46) {
243 | decimals = true;
244 | }
245 | }
246 | return mint;
247 | }
248 |
249 | function placeBet(uint gameId, uint8 bet) public payable returns (bool) {
250 | if (!games[gameId].rounds[games[gameId].currentRound].open) {
251 | return false;
252 | }
253 |
254 | if (msg.value < games[gameId].minAmountByBet) {
255 | return false;
256 | }
257 |
258 | if (games[gameId].rounds[games[gameId].currentRound].bets.length < games[gameId].maxNumberOfBets) {
259 | uint id = games[gameId].rounds[games[gameId].currentRound].bets.length;
260 | games[gameId].rounds[games[gameId].currentRound].bets.length += 1;
261 | games[gameId].rounds[games[gameId].currentRound].bets[id].id = id;
262 | games[gameId].rounds[games[gameId].currentRound].bets[id].round = games[gameId].rounds[games[gameId].currentRound].id;
263 | games[gameId].rounds[games[gameId].currentRound].bets[id].bet = bet;
264 | games[gameId].rounds[games[gameId].currentRound].bets[id].origin = msg.sender;
265 | games[gameId].rounds[games[gameId].currentRound].bets[id].amount = msg.value;
266 | BetPlaced(gameId, games[gameId].rounds[games[gameId].currentRound].id, msg.sender, id);
267 | }
268 |
269 | if (games[gameId].rounds[games[gameId].currentRound].bets.length >= games[gameId].maxNumberOfBets) {
270 | closeRound(gameId);
271 | }
272 |
273 | return true;
274 | }
275 |
276 | function withdraw() public returns (uint) {
277 | uint amount = getBalance();
278 | if (amount > 0) {
279 | balances[msg.sender] = 0;
280 | msg.sender.transfer(amount);
281 | return amount;
282 | }
283 | return 0;
284 | }
285 |
286 | function getBalance() constant returns (uint) {
287 | if ((balances[msg.sender] > 0) && (balances[msg.sender] < this.balance)) {
288 | return balances[msg.sender];
289 | }
290 | return 0;
291 | }
292 |
293 | function numberOfClosedGames() constant returns(uint numberOfClosedGames) {
294 | numberOfClosedGames = 0;
295 | for (uint i = 0; i < games.length; i++) {
296 | if (games[i].open != true) {
297 | numberOfClosedGames++;
298 | }
299 | }
300 | return numberOfClosedGames;
301 | }
302 |
303 | function getGames() constant returns(uint[] memory ids) {
304 | ids = new uint[](games.length - numberOfClosedGames());
305 | for (uint i = 0; i < games.length; i++) {
306 | if (games[i].open == true) {
307 | ids[i] = games[i].id;
308 | }
309 | }
310 | }
311 |
312 | function getGameCurrentRoundId(uint gameId) constant returns(uint) {
313 | return games[gameId].currentRound;
314 | }
315 |
316 | function getGameRoundOpen(uint gameId, uint roundId) constant returns(bool) {
317 | return games[gameId].rounds[roundId].open;
318 | }
319 |
320 | function getGameMaxNumberOfBets(uint gameId) constant returns(uint) {
321 | return games[gameId].maxNumberOfBets;
322 | }
323 |
324 | function getGameMinAmountByBet(uint gameId) constant returns(uint) {
325 | return games[gameId].minAmountByBet;
326 | }
327 |
328 | function getGamePrize(uint gameId) constant returns(uint) {
329 | return games[gameId].prize;
330 | }
331 |
332 | function getRoundNumberOfBets(uint gameId, uint roundId) constant returns(uint) {
333 | return games[gameId].rounds[roundId].bets.length;
334 | }
335 |
336 | function getRoundBetOrigin(uint gameId, uint roundId, uint betId) constant returns(address) {
337 | return games[gameId].rounds[roundId].bets[betId].origin;
338 | }
339 |
340 | function getRoundBetAmount(uint gameId, uint roundId, uint betId) constant returns(uint) {
341 | return games[gameId].rounds[roundId].bets[betId].amount;
342 | }
343 |
344 | function getRoundBetNumber(uint gameId, uint roundId, uint betId) constant returns(uint) {
345 | return games[gameId].rounds[roundId].bets[betId].bet;
346 | }
347 |
348 | function getRoundNumber(uint gameId, uint roundId) constant returns(uint8) {
349 | return games[gameId].rounds[roundId].number;
350 | }
351 |
352 | function getRoundPointer(uint gameId, uint roundId) constant returns(uint) {
353 | return games[gameId].rounds[roundId].pointer;
354 | }
355 |
356 | function getPointer(uint gameId) constant returns(uint) {
357 | return games[gameId].pointer;
358 | }
359 |
360 | function () payable {
361 | }
362 | }
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Lotthereum
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
38 |
39 |
40 |
48 |
49 |
53 |
54 |
55 |
60 |
61 |
62 |
65 |
66 |
67 |
72 |
73 |
74 |
75 |
76 | {betButton}
77 |
78 |
79 |
80 |
lock Round {round_id}
81 |
82 |
83 |
84 |
85 | | {prize} ether prize |
86 | {minAmount} ether per bet |
87 | {numberOfBets} bets placed |
88 | {remaining} bets remaining |
89 |
90 |
91 |
92 |
93 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
113 |
114 |
115 |
118 |
119 |
120 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
143 |
158 |
173 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
monetization_on Withdraw
186 |
187 |
188 |
189 | | Account: |
190 | |
191 |
192 |
193 | | Balance: |
194 | |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
attach_money WITHDRAW
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
Help Lotthereum Grow
215 |
216 |
217 |
218 | | Ethereum |
219 |  0xCa5BA0B004Fb198fbA60D52A060bc7fB4DAd34e5 |
220 |
221 |
222 | | Bitcoin |
223 |  1CDafgmGfDY7L6moSoHGo7fhyim8iBPvUx |
224 |
225 |
226 | | Litecoin |
227 | LaCM9YtCdXZZT3nLWpsxTzrvG5SVudMFo7 |
228 |
229 |
230 | | Dogecoin |
231 |  DFtRpv2a6FCBwsrjfwr2pEA4F8nDGQfibG |
232 |
233 |
234 |
235 |
236 |
239 |
240 |
241 |
242 |
243 |
244 |
Account not found
245 |
253 |
254 | You are connected to http://localhost:8545 but there are no accounts available.
255 |
256 |
257 |
260 |
261 |
262 |
263 |
342 |
343 |
366 |
367 |
368 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
449 |
450 |
451 |
--------------------------------------------------------------------------------
/app/scripts/lotthereum.js:
--------------------------------------------------------------------------------
1 | function Lotthereum() {
2 | this.contract = web3.eth.contract([{'constant':false,'inputs':[{'name':'gameId','type':'uint256'}],'name':'openGame','outputs':[{'name':'','type':'bool'}],'payable':false,'type':'function'},{'constant':false,'inputs':[{'name':'gameId','type':'uint256'},{'name':'bet','type':'uint8'}],'name':'placeBet','outputs':[{'name':'','type':'bool'}],'payable':true,'type':'function'},{'constant':true,'inputs':[],'name':'getBalance','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':false,'inputs':[{'name':'gameId','type':'uint256'}],'name':'closeGame','outputs':[{'name':'','type':'bool'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'_a','type':'bytes32'}],'name':'getNumber','outputs':[{'name':'','type':'uint8'}],'payable':false,'type':'function'},{'constant':false,'inputs':[],'name':'withdraw','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':false,'inputs':[],'name':'kill','outputs':[],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'}],'name':'getGameRoundOpen','outputs':[{'name':'','type':'bool'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'}],'name':'getRoundPointer','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[],'name':'numberOfClosedGames','outputs':[{'name':'numberOfClosedGames','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'}],'name':'getGameMinAmountByBet','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'}],'name':'getRoundNumberOfBets','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'},{'name':'betId','type':'uint256'}],'name':'getRoundBetOrigin','outputs':[{'name':'','type':'address'}],'payable':false,'type':'function'},{'constant':false,'inputs':[{'name':'pointer','type':'uint256'},{'name':'maxNumberOfBets','type':'uint256'},{'name':'minAmountByBet','type':'uint256'},{'name':'prize','type':'uint256'}],'name':'createGame','outputs':[{'name':'id','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[],'name':'getGames','outputs':[{'name':'ids','type':'uint256[]'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'}],'name':'getRoundNumber','outputs':[{'name':'','type':'uint8'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'}],'name':'getGameCurrentRoundId','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'},{'name':'betId','type':'uint256'}],'name':'getRoundBetNumber','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'}],'name':'getGameMaxNumberOfBets','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'}],'name':'getPointer','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'}],'name':'getGamePrize','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'i','type':'uint256'}],'name':'getBlockHash','outputs':[{'name':'blockHash','type':'bytes32'}],'payable':false,'type':'function'},{'constant':true,'inputs':[{'name':'gameId','type':'uint256'},{'name':'roundId','type':'uint256'},{'name':'betId','type':'uint256'}],'name':'getRoundBetAmount','outputs':[{'name':'','type':'uint256'}],'payable':false,'type':'function'},{'payable':true,'type':'fallback'},{'anonymous':false,'inputs':[{'indexed':true,'name':'gameId','type':'uint256'},{'indexed':true,'name':'roundId','type':'uint256'}],'name':'RoundOpen','type':'event'},{'anonymous':false,'inputs':[{'indexed':true,'name':'gameId','type':'uint256'},{'indexed':true,'name':'roundId','type':'uint256'},{'indexed':false,'name':'number','type':'uint8'}],'name':'RoundClose','type':'event'},{'anonymous':false,'inputs':[{'indexed':false,'name':'maxNumberOfBets','type':'uint256'}],'name':'MaxNumberOfBetsChanged','type':'event'},{'anonymous':false,'inputs':[{'indexed':false,'name':'minAmountByBet','type':'uint256'}],'name':'MinAmountByBetChanged','type':'event'},{'anonymous':false,'inputs':[{'indexed':true,'name':'gameId','type':'uint256'},{'indexed':true,'name':'roundId','type':'uint256'},{'indexed':true,'name':'origin','type':'address'},{'indexed':false,'name':'betId','type':'uint256'}],'name':'BetPlaced','type':'event'},{'anonymous':false,'inputs':[{'indexed':true,'name':'gameId','type':'uint256'},{'indexed':true,'name':'roundId','type':'uint256'},{'indexed':true,'name':'winnerAddress','type':'address'},{'indexed':false,'name':'amount','type':'uint256'}],'name':'RoundWinner','type':'event'},{'anonymous':false,'inputs':[{'indexed':true,'name':'gameId','type':'uint256'}],'name':'GameOpened','type':'event'},{'anonymous':false,'inputs':[{'indexed':true,'name':'gameId','type':'uint256'}],'name':'GameClosed','type':'event'}]);
3 |
4 | this.contractInstance = this.contract.at(window.contract_address);
5 | this.modalIntro = 0;
6 | this.tabbed = 0;
7 | this.selectedGame = 0;
8 | this.eventsInitialized = false;
9 | this.games = [];
10 | var self = this;
11 |
12 | ///////////////////////////////////////////////////////////////////////////
13 | //
14 | // INIT
15 | //
16 | this.init = function () {
17 | async.waterfall([
18 | function games(done) {
19 | self.contractInstance.getGames(function(error, result) {
20 | console.log('result.valueOf(): ' + result.valueOf())
21 | var games = result.valueOf();
22 | for (var i = 0; i < games.length; i++) {
23 | self.games.push(new Game(self, i));
24 | self.addGamePlaceHolder(i);
25 | }
26 | done(error, self.games);
27 | });
28 | },
29 | ],
30 | function (err) {
31 | if (err) {
32 | console.error(err);
33 | }
34 | });
35 | };
36 |
37 | this.isInitialized = function (gameId, roundId) {
38 | var initialized = false;
39 | if (self.games[gameId]){
40 | if (self.games[gameId].rounds[roundId]) {
41 | self.games[gameId].rounds[roundId].rendered++;
42 | console.log('rendered: ' + self.games[gameId].rounds[roundId].rendered)
43 | console.log('numberOfBets: ' + self.games[gameId].rounds[roundId].numberOfBets)
44 | if (self.games[gameId].rounds[roundId].rendered >= self.games[gameId].rounds[roundId].numberOfBets) {
45 | self.games[gameId].rounds[roundId].initialized = true;
46 | initialized = true;
47 | }
48 | }
49 | }
50 |
51 | if (initialized) {
52 | console.log('GAME ' + gameId + ' INITIALIZED!');
53 | self.renderRound(gameId, roundId);
54 |
55 | if (!self.eventsInitialized) {
56 | self.eventsInitialized = true;
57 | this.initEvents();
58 |
59 | if(self.games[gameId].rounds[roundId].payload) {
60 | if(self.games[gameId].rounds[roundId].payload.select) {
61 | console.log('>>>>>>>>>>>>>>>>>>>>>>>>', self.payload);
62 | $('ul.tabs').tabs('select_tab', '#game_' + self.gameId + '_holder');
63 | }
64 | }
65 | }
66 | }
67 | return initialized;
68 | }
69 |
70 | this.areAllRoundsInitialed = function () {
71 | var ret = true;
72 | for (var i = 0; i <= self.currentRoundId; i++) {
73 | if (self.rounds[i].initialized == false) {
74 | ret = false;
75 | }
76 | }
77 | return ret
78 | }
79 | //
80 | // INIT
81 | //
82 | ///////////////////////////////////////////////////////////////////////////
83 |
84 | ///////////////////////////////////////////////////////////////////////////
85 | //
86 | // PLACE BET
87 | //
88 | this.placeBet = function (gameId, bet) {
89 | var value = self.games[gameId].minAmount * 1000000000000000000
90 | var gas = 500000;
91 | console.log('sending from: ' + self.account)
92 |
93 | self.contractInstance.placeBet(gameId, bet, {from: self.account, value: value, gas: gas}, function(error, result) {
94 | if (error) {
95 | console.log('ERROR:');
96 | console.log(error);
97 | } else {
98 | $('#bet-btn').addClass('disabled');
99 | Materialize.fadeInImage('#confirmations');
100 | console.log(result);
101 | }
102 | });
103 | }
104 | //
105 | // PLACE BET
106 | //
107 | ///////////////////////////////////////////////////////////////////////////
108 |
109 | ///////////////////////////////////////////////////////////////////////////
110 | //
111 | // WITHDRAW
112 | //
113 | this.withdraw = function () {
114 | var gas = 500000;
115 | console.log('withdraw from: ' + self.account)
116 | self.contractInstance.withdraw({from: self.account, gas: gas}, function(error, result) {
117 | $('#withdraw-btn').addClass('disabled');
118 | $('#withdraw-transaction-id').html(result)
119 | Materialize.fadeInImage('#withdraw-confirmations');
120 | });
121 | }
122 | //
123 | // WITHDRAW
124 | //
125 | ///////////////////////////////////////////////////////////////////////////
126 |
127 | ///////////////////////////////////////////////////////////////////////////
128 | //
129 | // ACCOUNTS
130 | //
131 | this.initAccounts = function () {
132 | self.accounts = [];
133 | self.account = null;
134 |
135 | web3.eth.getAccounts(function(error, accounts) {
136 | var account = null;
137 | accounts.forEach(function(_account) {
138 | if (account == null) {
139 | account = _account;
140 | }
141 | $('#dropdown-nav').append('' + _account + '
' + _account + '');
142 | });
143 |
144 | if (account != null) {
145 | self.changeAccount(account)
146 | $('#avatar').css('display', 'block');
147 | } else {
148 | console.log('No accounts found!');
149 | $('#alert1').modal('open');
150 | }
151 |
152 | $('select').material_select();
153 |
154 | $('.accounts_dropdown_item').click(function() {
155 | var newValue = $(this).attr('href').replace('#!', '');
156 | console.log(newValue);
157 | self.changeAccount(newValue)
158 | });
159 | });
160 | }
161 |
162 | this.changeAccount = function (address) {
163 | self.account = address;
164 | self.renderAvatar(self.account);
165 | $('#current_account_number').html(self.account);
166 | self.getBalance();
167 | }
168 |
169 | this.getBalance = function () {
170 | self.contractInstance.getBalance({from: self.account}, function(error, result){
171 | var balance = web3.fromWei(result.valueOf(), 'ether');
172 | $('#current_account_balance').html(balance + ' ETH');
173 | if (balance <= 0) {
174 | $('#withdraw-btn').addClass('disabled');
175 | } else {
176 | $('#withdraw-btn').removeClass('disabled');
177 | }
178 | });
179 | }
180 | //
181 | // ACCOUNTS
182 | //
183 | ///////////////////////////////////////////////////////////////////////////
184 |
185 | ///////////////////////////////////////////////////////////////////////////
186 | //
187 | // EVENTS
188 | //
189 | this.initEvents = function () {
190 | var currentBlockNumber = 0;
191 | web3.eth.getBlockNumber(function(error, result){
192 | if(!error)
193 | currentBlockNumber = result;
194 | else
195 | console.error(error);
196 | })
197 |
198 | var range = {fromBlock: currentBlockNumber, toBlock: 'latest'};
199 | self.betPlacedEvent = self.contractInstance.BetPlaced(range);
200 | self.betPlacedEvent.watch(self.betPlaced);
201 |
202 | self.roundCloseEvent = self.contractInstance.RoundClose(range);
203 | self.roundCloseEvent.watch(self.roundClose);
204 |
205 | self.roundOpenEvent = self.contractInstance.RoundOpen(range);
206 | self.roundOpenEvent.watch(self.roundOpen);
207 |
208 | self.roundWinnerEvent = self.contractInstance.RoundWinner(range);
209 | self.roundWinnerEvent.watch(self.roundWinner);
210 | }
211 |
212 | this.roundClose = function (error, event) {
213 | console.log('round closed: ' + event.args.roundId + ' (game: ' + event.args.gameId);
214 | Materialize.toast('Game #' + event.args.gameId + ' Round #' + event.args.roundId + ' closed!', 5000, 'rounded');
215 | }
216 |
217 | this.roundOpen = function (error, event) {
218 | console.log('round opened: ' + event.args.roundId + ' (game: ' + event.args.gameId);
219 | Materialize.toast('Game #' + event.args.gameId + ' Round #' + event.args.roundId + ' opened!', 5000, 'rounded');
220 | self.games[event.args.gameId].init();
221 | }
222 |
223 | this.betPlaced = function (error, event) {
224 | if (error) {
225 | console.log('ERROR:');
226 | console.log(error);
227 | } else {
228 | console.log('betPlaced event origin: ' + event.args.origin);
229 | console.log('event.args.gameId: ' + event.args.gameId);
230 | console.log('event.args.roundId: ' + event.args.roundId);
231 | if (event.args.origin == self.account) {
232 | $('#bet-modal').modal('close');
233 | $('#bet-btn').removeClass('disabled');
234 | $('#confirmations').css('opacity', 0);
235 | }
236 | Materialize.toast('New bet placed', 5000, 'rounded');
237 | self.games[event.args.gameId].rounds[event.args.roundId].init({'select': true});
238 | }
239 | }
240 |
241 | this.roundWinner = function (error, event) {
242 | console.log('round winner: ' + event.args.winnerAddress + ' ' + web3.fromWei(event.args.amount, 'ether') + ' ETH');
243 | Materialize.toast('Round winner ' + event.args.winnerAddress, 5000, 'rounded');
244 | self.getBalance();
245 | }
246 | //
247 | // EVENTS
248 | //
249 | ///////////////////////////////////////////////////////////////////////////
250 |
251 | ///////////////////////////////////////////////////////////////////////////
252 | //
253 | // RENDER
254 | //
255 | this.renderRound = function (gameId, roundId) {
256 | var game = self.games[gameId];
257 | var round = self.games[gameId].rounds[roundId];
258 | console.log(round)
259 |
260 | console.log('redering game #' + gameId + ' round #' + roundId);
261 | $('#rounds_' + gameId + ' #round_' + roundId + '_holder').html('');
262 |
263 | var html = $('#round_template').html();
264 | html = html.replace(/{round_id}/g, roundId);
265 | if (round.open) {
266 | html = html.replace('>lock<', '>lock_open<');
267 | html = html.replace('{color}', 'blue');
268 | var bet_btn_html = $('#bet_button_template').html().replace('{game_id}', gameId);
269 | html = html.replace('{betButton}', bet_btn_html);
270 | } else {
271 | html = html.replace('{color}', 'grey lighten-1');
272 | }
273 | html = html.replace(/{prize}/g, game.prize);
274 | html = html.replace('{minAmount}', game.minAmount);
275 | html = html.replace('{minAmount}', game.minAmount);
276 | html = html.replace('{numberOfBets}', round.numberOfBets);
277 | html = html.replace('{remaining}', round.remaining);
278 | html = html.replace(/{progress}/g, round.progress);
279 |
280 | // render bets
281 | var bets_html = ''
282 | var has_winner = false;
283 | for (var j = round.bets.length-1; j >= 0; j--) {
284 | var bet_html = $('#bet_template').html();
285 |
286 | if ((!round.open) && (round.bets[j].bet == round.number)) {
287 | bet_html = bet_html.replace(' {win}', ' green lighten-3');
288 | has_winner = true;
289 | } else {
290 | bet_html = bet_html.replace(' {win}', '');
291 | }
292 |
293 | bet_html = bet_html.replace(/{bet_id}/g, round.bets[j].id);
294 | bet_html = bet_html.replace(/{origin}/g, round.bets[j].origin);
295 | bet_html = bet_html.replace(/{amount}/g, round.bets[j].amount);
296 | bets_html += bet_html;
297 | }
298 | html = html.replace('{bets}', bets_html);
299 |
300 | if (!round.open) {
301 | var btnHtml = $('#bet_number_template').html().replace(/{number}/g, round.number);
302 | if (has_winner) {
303 | btnHtml = btnHtml.replace('{color}', 'green');
304 | html = html.replace('{betButton}', btnHtml);
305 | } else {
306 | btnHtml = btnHtml.replace('{color}', 'red');
307 | html = html.replace('{betButton}', btnHtml);
308 | }
309 | }
310 |
311 | $('#rounds_' + gameId + ' #round_' + roundId + '_holder').html(html);
312 | $('#rounds_' + gameId + ' #round_' + roundId + '_progress').css('width', round.progress + '%');
313 |
314 | if (self.areAllRoundsInitialed()) {
315 | console.log('INITIALIZED!!!!');
316 | this.renderTabs();
317 | self.stopLoading();
318 | }
319 | }
320 |
321 | this.addGamePlaceHolder = function (gameId) {
322 | var html = $('#game_placeholder_template').html();
323 | html = html.replace(/{game_id}/g, gameId);
324 | $('#games-container').append(html);
325 | }
326 |
327 | this.addRoundPlaceHolder = function (gameId, roundId) {
328 | var html = $('#round_placeholder_template').html();
329 | html = html.replace(/{round_id}/g, roundId);
330 | $('#rounds_' + gameId).prepend(html);
331 | }
332 |
333 | this.stopLoading = function () {
334 | self.renderAllIdenticons();
335 | $('#loading').hide();
336 | $('.page-footer').css('display', 'block');
337 | $('.main-container').css('display', 'block');
338 | $('.navbar-fixed').css('display', 'block');
339 | $('.nav-wrapper').css('background', '#2196F3');
340 | }
341 |
342 | this.startLoading = function () {
343 | $('#loading').show();
344 | $('.page-footer').css('display', 'none');
345 | $('.main-container').css('display', 'none');
346 | $('.navbar-fixed').css('display', 'none');
347 | $('.nav-wrapper').css('background', 'white');
348 | }
349 |
350 | this.renderIdenticon = function (obj) {
351 | obj.style.backgroundImage = 'url(' + blockies.create({ seed:obj.innerHTML.toLowerCase(), size: 8, scale: 16}).toDataURL() + ')'
352 | }
353 |
354 | this.renderAllIdenticons = function () {
355 | $('.eth-address').each(function(i, obj) {
356 | self.renderIdenticon(obj)
357 | });
358 | }
359 |
360 | this.initUIElements = function () {
361 | $(document).ready(function() {
362 | $('.target').pushpin({top: 0, bottom: 1000, offset: 0});
363 |
364 | $('#bet-btn').click(function() {
365 | var bet = parseInt($('input[name=bet_pick]:checked').val());
366 | if (bet <= 9 && bet >= 0) {
367 | self.placeBet(self.selectedGame, $('input[name=bet_pick]:checked').val());
368 | }
369 | });
370 |
371 | $('#withdraw-btn').click(function() {
372 | self.withdraw();
373 | });
374 |
375 | $('#close-intro-btn').click(function() {
376 | $('#intro').css('display', 'none');
377 | });
378 |
379 | $('#close-intro-btn').click(function() {
380 | $('#intro').css('display', 'none');
381 | });
382 |
383 | $('#logo-btn').click(function() {
384 | $('ul.tabs').tabs('select_tab', 'intro1');
385 | });
386 |
387 | $('#place-your-bet').click(function() {
388 | window.scrollTo(0, 0);
389 | $('ul.tabs').tabs('select_tab', 'game_3_holder');
390 | });
391 |
392 | $(window).scroll(function() {
393 | var opacity = 1 - $(window).scrollTop() / 70;
394 | if (opacity < 0) {
395 | opacity = 0;
396 | }
397 | $('.tabs').css('opacity', opacity);
398 | });
399 |
400 | $('.modal').modal();
401 |
402 | $('.tooltipped').tooltip({delay: 60});
403 |
404 | $('.main-container').css('display', 'block');
405 | });
406 | }
407 |
408 | this.renderAvatar = function (address) {
409 | $('#avatar').html(address);
410 | $('#avatar').each(function(i, obj) {
411 | self.renderIdenticon(obj)
412 | });
413 | }
414 |
415 | this.renderTabs = function () {
416 | self.tabbed++;
417 | if (self.tabbed <= 1) {
418 | $('#tabs').html('Lotthereum');
419 | for (var i = this.games.length-1; i >= 0; i--) {
420 | $('#tabs').append('' + this.games[i].minAmount +' ETH');
421 | }
422 | $('#tabs').append('Withdraw');
423 | $('#tabs').tabs({
424 | 'swipeable': false,
425 | 'onShow': function (tab) {
426 | self.selectedGame = 0;
427 | var gameId = parseInt(
428 | tab.selector.replace('#game_', '').replace('_holder', '')
429 | );
430 | if (gameId >= 0) {
431 | self.selectedGame = gameId;
432 | }
433 | console.log('SELECTED GAME: ' + self.selectedGame);
434 | },
435 | });
436 | }
437 | }
438 | //
439 | // RENDER
440 | //
441 | ///////////////////////////////////////////////////////////////////////////
442 |
443 | this.init();
444 | this.initAccounts();
445 | this.initUIElements();
446 | }
447 |
448 | function Game(lotthereum, gameId) {
449 | this.id = gameId;
450 | this.lotthereum = lotthereum;
451 | this.contractInstance = lotthereum.contractInstance;
452 |
453 | this.rounds = [];
454 | this.currentRoundId = 0;
455 | this.minAmount = 0;
456 | this.maxNumber = 0;
457 | this.numberOfBets = 0;
458 | this.prize = 0;
459 | this.remaining = 0;
460 | this.progress = 0;
461 | this.number = -1;
462 | this.tabbed = 0;
463 | this.rounds = [];
464 | var self = this;
465 |
466 | this.init = function () {
467 | self.rounds = [];
468 | $('#rounds_' + self.id).html('');
469 |
470 | async.waterfall([
471 | function(done) {
472 | console.log(self.id)
473 | self.contractInstance.getGameCurrentRoundId(self.id, function(error, result) {
474 | self.currentRoundId = result.valueOf();
475 | done(error, result.valueOf());
476 | });
477 | },
478 | function(currentRoundId, done) {
479 | self.contractInstance.getGamePrize(self.id, function(error, result){
480 | self.prize = web3.fromWei(result.valueOf(), 'ether');
481 | done(error, result.valueOf());
482 | });
483 | },
484 | function(roundPrize, done) {
485 | self.contractInstance.getGameMinAmountByBet(self.id, function(error, result){
486 | self.minAmount = web3.fromWei(result.valueOf(), 'ether');
487 | done(error, self.minAmount);
488 | });
489 | },
490 | function(roundMinAmountByBet, done) {
491 | self.contractInstance.getGameMaxNumberOfBets(self.id, function(error, result){
492 | self.maxNumber = result.valueOf();
493 | done(error, result.valueOf());
494 | });
495 | },
496 | function rounds(maxNumberOfBets, done) {
497 | for (var j = 0; j <= self.currentRoundId; j++) {
498 | self.rounds.push(new Round(self, j));
499 | self.lotthereum.addRoundPlaceHolder(self.id, j);
500 | }
501 | done(null, self.currentRoundId);
502 | },
503 | ],
504 | function (err) {
505 | if (err) {
506 | console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!');
507 | console.error(err);
508 | }
509 | });
510 | }
511 |
512 | this.init();
513 | console.log('game.id >>>> ' + this.id);
514 | }
515 |
516 | function Round(game, roundId) {
517 | this.id = roundId;
518 | this.game = game;
519 | this.gameId = game.id;
520 | this.lotthereum = game.lotthereum;
521 | this.contractInstance = game.lotthereum.contractInstance;
522 | this.initialized = false;
523 | this.rendered = 0;
524 | this.tabbed = 0;
525 | var self = this;
526 |
527 | this.init = function (payload) {
528 | self.payload = payload;
529 | self.open = false;
530 | self.remaining = 0;
531 | self.progress = 0;
532 | self.number = -1;
533 | self.bets = [];
534 |
535 | async.waterfall([
536 | function(done) {
537 | self.contractInstance.getGameRoundOpen(self.game.id, self.id, function(error, result){
538 | self.open = result.valueOf();
539 | done(error, result.valueOf());
540 | });
541 | },
542 | function(isOpen, done) {
543 | self.contractInstance.getRoundNumber(self.game.id, self.id, function(error, result){
544 | self.number = result.valueOf();
545 | done(error, self.number);
546 | });
547 | },
548 | function(number, done) {
549 | self.contractInstance.getRoundNumberOfBets(self.game.id, self.id, function(error, result){
550 | self.numberOfBets = result.valueOf();
551 | console.log('self.game.maxNumber: '+ self.game.maxNumber)
552 | self.remaining = parseInt(self.game.maxNumber) - parseInt(self.numberOfBets);
553 | self.progress = parseFloat(100 * self.numberOfBets / self.game.maxNumber).toString();
554 | for (var i = self.numberOfBets - 1; i >= 0; i--) {
555 | self.bets.push(new Bet(self, i));
556 | }
557 | if (self.numberOfBets < 1) {
558 | self.initialized = true;
559 | self.lotthereum.isInitialized(self.gameId, self.id);
560 | }
561 | done(error, result.valueOf());
562 | });
563 | },
564 | ],
565 | function (err) {
566 | if (err) {
567 | console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!');
568 | console.error(err);
569 | }
570 | });
571 | }
572 | this.init();
573 | }
574 |
575 | function Bet(round, betId) {
576 | this.id = betId;
577 | this.roundId = round.id;
578 | this.round = round;
579 | this.contractInstance = round.contractInstance;
580 | this.lotthereum = round.lotthereum;
581 | this.origin = '';
582 | this.amount = 0;
583 | this.bet = '';
584 | this.transactionId = '';
585 | this.confirmations = 0;
586 | this.initialized = false;
587 | var self = this;
588 |
589 | this.init = function () {
590 | async.waterfall([
591 | function(done) {
592 | self.contractInstance.getRoundBetOrigin(self.round.game.id, self.round.id, self.id, function(error, result){
593 | self.origin = result.valueOf();
594 | done(error, self.origin);
595 | });
596 | },
597 | function(roundBetOrigin, done) {
598 | self.contractInstance.getRoundBetAmount(self.round.game.id, self.round.id, self.id, function(error, result){
599 | self.amount = web3.fromWei(result.valueOf(), 'ether');
600 | done(error, self.amount);
601 | });
602 | },
603 | function(amount, done) {
604 | self.contractInstance.getRoundBetNumber(self.round.game.id, self.round.id, self.id, function(error, result){
605 | self.bet = result.valueOf();
606 | done(error, self.bet);
607 | });
608 | },
609 | function(bet, done) {
610 | self.initialized = true;
611 | done(null, 'done!');
612 | }
613 | ],
614 | function (err) {
615 | if(err) {
616 | console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!');
617 | console.error(err);
618 | } else {
619 | self.round.game.lotthereum.isInitialized(self.round.game.id, self.round.id);
620 | }
621 | });
622 | }
623 | this.init();
624 | }
--------------------------------------------------------------------------------