This commit is contained in:
elegant651
2020-03-24 14:39:38 +09:00
commit 6e0388b653
83 changed files with 92480 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 8

21
numbergame/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

29
numbergame/README.md Normal file
View File

@@ -0,0 +1,29 @@
# bet-dapp
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

View File

@@ -0,0 +1,61 @@
pragma solidity ^0.4.18;
contract Ownable {
address owner;
function Ownable() public {
owner = msg.sender;
}
modifier Owned {
require(msg.sender == owner);
_;
}
}
contract Mortal is Ownable {
function kill() public Owned {
selfdestruct(owner);
}
}
contract Betting is Mortal {
uint minBet; // 최소 베팅액
uint winRate; // 배당률 (%)
event Won(bool _result, uint _amount);
function Betting(uint _minBet, uint _winRate) payable public {
require(_minBet > 0);
require(_winRate <= 100);
minBet = _minBet;
winRate = _winRate;
}
function() public {
revert();
}
function bet(uint _num) payable public {
require(_num > 0 && _num <= 5);
require(msg.value >= minBet);
uint winNum = random();
if (_num == winNum) {
uint amtWon = msg.value * (100 - winRate)/10;
if(!msg.sender.send(amtWon)) revert();
Won(true, amtWon);
} else {
Won(false, 0);
}
}
function getBalance() Owned public view returns(uint) {
return address(this).balance;
}
function random() public view returns (uint) {
return uint(keccak256(block.difficulty, block.number, now)) % 5 + 1;
}
}

9943
numbergame/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
numbergame/package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "bet-dapp",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"vue": "^2.5.21",
"vue-router": "^3.0.2",
"vuex": "^3.0.1",
"web3": "^0.20.6"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.1.1",
"@vue/cli-service": "^3.1.1",
"vue-template-compiler": "^2.5.21"
}
}

View File

@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>bet-dapp</title>
</head>
<body>
<noscript>
<strong>We're sorry but bet-dapp doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

23
numbergame/src/App.vue Normal file
View File

@@ -0,0 +1,23 @@
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,57 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,101 @@
<template>
<div class="betting">
<h1>Bet Dapp Test</h1>
Amount: <input v-model="amount" placeholder="0 Ether">
<ul>
<li v-on:click="_clickNumber">1</li>
<li v-on:click="_clickNumber">2</li>
<li v-on:click="_clickNumber">3</li>
<li v-on:click="_clickNumber">4</li>
<li v-on:click="_clickNumber">5</li>
</ul>
<div v-if="pending" id="loader">Loading...</div>
<div class="event" v-if="winEvent">
Won: {{ winEvent._result }}
Amount: {{ winEvent._amount }} Wei
</div>
</div>
</template>
<script>
export default {
name: 'betting-component',
data() {
return {
amount: null,
pending: false,
winEvent: null
}
},
mounted () {
this.$store.dispatch('getContractInstance')
},
methods: {
_clickNumber (event) {
console.log('betting number', event.target.innerHTML, this.amount)
this.winEvent = null
this.pending = true
this.$store.state.contractInstance().bet(event.target.innerHTML, {
gas: 300000,
value: this.$store.state.web3.web3Instance().toWei(this.amount, 'ether'),
from: this.$store.state.web3.coinbase
}, (err, result) => {
if (err) {
console.error(err)
this.pending = false
} else {
const Won = this.$store.state.contractInstance().Won()
Won.watch((err, result) => {
if (err) {
console.error('won', error)
} else {
this.winEvent = result.args
this.winEvent._amount = parseInt(result.args._amount, 10)
this.pending = false
}
})
}
})
}
}
}
</script>
<style scoped>
.betting {
margin-top: 50px;
text-align:center;
}
ul {
margin: 25px;
list-style-type: none;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-column-gap:25px;
}
li{
padding: 20px;
margin-right: 3px;
border-radius: 30%;
cursor: pointer;
background-color:#fff;
color: #4b08e0;
box-shadow:3px 5px 1px #4b08e0;
}
li:hover{
background-color:#4b08e0;
color:white;
box-shadow:0px 0px 1px #4b08e0;
}
li:active{
opacity: 0.7;
}
*{
color: #444444;
}
</style>

View File

@@ -0,0 +1,21 @@
<template>
<div>
<DappMetamask />
<BettingComponent />
</div>
</template>
<script>
import DappMetamask from '@/components/dapp-metamask'
import BettingComponent from '@/components/betting-component'
export default {
name: 'betting-dapp',
beforeCreate () {
console.log('registerWeb3 Action dispatched')
this.$store.dispatch('registerWeb3')
},
components: {
DappMetamask,
BettingComponent
}
}
</script>

View File

@@ -0,0 +1,20 @@
<template>
<div class='metamask-info'>
<p>연결 상태 : {{ web3.isInjected }}</p>
<p>네트워크: {{ web3.networkId }}</p>
<p>코인베이스 주소: {{ web3.coinbase }}</p>
<p>잔액: {{ web3.balance }}</p>
</div>
</template>
<script>
export default {
name: 'dapp-metamask',
computed: {
web3 () {
return this.$store.state.web3
}
}
}
</script>
<style scoped>
</style>

12
numbergame/src/main.js Normal file
View File

@@ -0,0 +1,12 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import { store } from './store/'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')

View File

@@ -0,0 +1,92 @@
const address = '0x3bc507d62132520239a96a1c40b00086efbf3bf3'
const ABI = [
{
"constant": false,
"inputs": [
{
"name": "_num",
"type": "uint256"
}
],
"name": "bet",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "kill",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"name": "_minBet",
"type": "uint256"
},
{
"name": "_winRate",
"type": "uint256"
}
],
"payable": true,
"stateMutability": "payable",
"type": "constructor"
},
{
"payable": false,
"stateMutability": "nonpayable",
"type": "fallback"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "_result",
"type": "bool"
},
{
"indexed": false,
"name": "_amount",
"type": "uint256"
}
],
"name": "Won",
"type": "event"
},
{
"constant": true,
"inputs": [],
"name": "getBalance",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "random",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
export {address, ABI}

View File

@@ -0,0 +1,9 @@
import Web3 from 'web3'
import {address, ABI} from './betContract'
const getContract = new Promise(function (resolve, reject) {
const web3 = new Web3(window.web3.currentProvider)
const betContract = web3.eth.contract(ABI)
const betContractInstance = betContract.at(address)
resolve(betContractInstance)
})
export default getContract

View File

@@ -0,0 +1,54 @@
import Web3 from 'web3'
const getWeb3 = new Promise(function (resolve, reject) {
const web3js = window.web3
if (typeof web3js !== 'undefined') {
const web3 = new Web3(web3js.currentProvider)
resolve({
injectedWeb3: web3.isConnected(),
web3 () {
return web3
}
})
} else {
reject(new Error('Unable to connect to Metamask'))
}
})
.then(result => {
return new Promise(function (resolve, reject) {
result.web3().version.getNetwork((err, networkId) => {
if (err) {
reject(new Error('Unable to retrieve network ID'))
} else {
result = {...result, networkId}
resolve(result)
}
})
})
})
.then(result => {
return new Promise(function (resolve, reject) {
result.web3().eth.getCoinbase((err, coinbase) => {
if (err) {
reject(new Error('Unable to retrieve coinbase'))
} else {
result = {...result, coinbase}
resolve(result)
}
})
})
})
.then(result => {
return new Promise(function (resolve, reject) {
result.web3().eth.getBalance(result.coinbase, (err, balance) => {
if (err) {
reject(new Error('Unable to retrieve balance for address: ' + result.coinbase))
} else {
result = {...result, balance}
resolve(result)
}
})
})
})
export default getWeb3

View File

@@ -0,0 +1,9 @@
export const NETWORKS = {
'1': 'Main Net',
'2': 'Deprecated Morden test network',
'3': 'Ropsten test network',
'4': 'Rinkeby test network',
'42': 'Kovan test network',
'4447': 'Truffle Develop Network',
'5777': 'Ganache Blockchain'
}

View File

@@ -0,0 +1,42 @@
import Web3 from 'web3'
import {store} from '../store/'
const pollWeb3 = function (state) {
let web3 = window.web3
web3 = new Web3(web3.currentProvider)
setInterval(() => {
if (!web3 || !store.state.web3.web3Instance) {
return;
}
if (web3.eth.coinbase !== store.state.web3.coinbase) {
const newCoinbase = web3.eth.coinbase
web3.eth.getBalance(web3.eth.coinbase, function (err, newBalance) {
if (err) {
console.error(err)
} else {
store.dispatch('updateWeb3', {
coinbase: newCoinbase,
balance: parseInt(newBalance, 10)
})
}
})
} else {
web3.eth.getBalance(store.state.web3.coinbase, (err, newBalance) => {
if (err) {
console.log(err)
} else if (parseInt(newBalance, 10) !== store.state.web3.balance) {
store.dispatch('updateWeb3', {
coinbase: store.state.web3.coinbase,
balance: newBalance
})
}
})
}
}, 700)
}
export default pollWeb3

14
numbergame/src/router.js Normal file
View File

@@ -0,0 +1,14 @@
import Vue from 'vue'
import Router from 'vue-router'
import BettingDapp from '@/components/betting-dapp'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'betting-dapp',
component: BettingDapp
}
]
})

View File

@@ -0,0 +1,58 @@
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getWeb3 from '../network/getWeb3'
import pollWeb3 from '../network/pollWeb3'
import getContract from '../network/getContract'
Vue.use(Vuex)
export const store = new Vuex.Store({
strict: true,
state,
mutations: {
registerWeb3Instance (state, data) {
console.log('registerWeb3instance', data)
const result = data
let newWeb3 = state.web3
newWeb3.coinbase = result.coinbase
newWeb3.networkId = result.networkId
newWeb3.balance = parseInt(result.balance, 10)
newWeb3.isInjected = result.injectedWeb3
newWeb3.web3Instance = result.web3
state.web3 = newWeb3
pollWeb3()
},
updateWeb3Instance (state, data) {
console.log('updateWeb3Instance', data)
state.web3.coinbase = data.coinbase
state.web3.balance = parseInt(data.balance, 10)
},
registerContractInstance (state, data) {
console.log('contract instance: ', data)
state.contractInstance = () => data
}
},
actions: {
registerWeb3 ({commit}) {
getWeb3.then(result => {
console.log('commit result')
commit('registerWeb3Instance', result)
}).catch(e => {
console.error('error', e)
})
},
updateWeb3 ({commit}, data) {
commit('updateWeb3Instance', data)
},
getContractInstance ({commit}) {
getContract.then(result => {
commit('registerContractInstance', result)
}).catch(e => console.log(e))
}
}
})

View File

@@ -0,0 +1,12 @@
const state = {
web3: {
isInjected: false,
web3Instance: null,
networkId: null,
coinbase: null,
balance: null,
error: null
},
contractInstance: null
}
export default state