diff --git a/dapp/src/components/App.js b/dapp/src/components/App.js index 8315587..cad013d 100644 --- a/dapp/src/components/App.js +++ b/dapp/src/components/App.js @@ -26,6 +26,8 @@ import NiftyAlert from './NiftyAlert'; class App extends Component { state = { web3: null, + ebakus: null, + ebakusWallet: null, brand: [], brandItem: [], isLoading: true, @@ -42,6 +44,7 @@ class App extends Component { constructor(props) { super(props); this.setWeb3 = this.setWeb3.bind(this); + this.setEbakusWallet = this.setEbakusWallet.bind(this); } handleAlertOpen = errmag => { @@ -81,6 +84,9 @@ class App extends Component { setWeb3(web3) { this.setState({ web3 }); } + setEbakusWallet(ebakusWallet) { + this.setState({ ebakusWallet }); + } Elffn = e => { let wElf = document.querySelector('.Elf0').clientWidth; @@ -243,7 +249,12 @@ class App extends Component { - + {this.state.isErrorOpen && ( { - if(err) { - this.handleAlertOpen("Sorry, transaction failed"); + try { + const gas = await web3.eth.estimateGas(tx); + tx.gas = gas; + } catch (e) { + console.error(e); + this.handleAlertOpen('Sorry, transaction failed'); + this.setState({ + isLoading: false, + }); + return; + } + + this.props.ebakusWallet + .sendTransaction(tx) + .then(response => { + let t = setInterval(async () => { + const result = await axios.post(getRPCProvider(network), { + method: 'eth_getTransactionReceipt', + params: [response.transactionHash], + id: 1, + jsonrpc: '2.0', + }); + + if (result.data.result.status === '0x1') { + const gameChecker = window.setInterval(async () => { + const games = await doGetUserSingleGames(network, account); + //FIXME: 透過 user 戰鬥場數來判斷此役戰鬥在合約中是否已確實完成 + // 但礙於組建設計不良, 生命週期混亂, 只能透過在 App.js 取得的 historyGamesCount + // 以及本地取得的 historyGames 做雙重判斷, 之後需要更改 + if ( + games.length <= historyGames.length || + games.length <= historyGamesCount + ) { + return; + } + window.clearInterval(gameChecker); + const gamePromises = games.map(cur => + getSingleGame(network, cur, account) + ); + const gameDetails = await Promise.all(gamePromises); + const thisGame = gameDetails[gameDetails.length - 1]; + const userPointer = thisGame[1]; + const contractPointer = thisGame[2]; + const userBet = thisGame[3]; + const gameType = thisGame[4]; // 0 = small win 1 = big win + const isWin = thisGame[5]; // 0 win, 1 lost, 2 平手 + const isUserSmall = userPointer < contractPointer; + + const battleResult = { + userPointer, + contractPointer, + userBet, + isUserSmall, + gameType, + isWin, + }; + + window.setTimeout(() => { + this.setState({ + isLoading: false, + isShowResult: true, + isShowHistory: false, + hasBattleResult: true, + battleResult, + }); + }, 0); + }, 500); + window.clearInterval(t); + } + }, 500); + }) + .catch(err => { + let errmsg = 'Sorry, transaction failed' + if (err === 'no_funds') { + errmsg ='Not enough funds. Please add some funds and try again.' + } + + this.handleAlertOpen(errmsg); this.setState({ isLoading: false, }); return; - } - - let t = setInterval(async () => { - const result = await axios.get(`https://api-ropsten.etherscan.io/api?module=transaction&action=gettxreceiptstatus&txhash=${response}&apikey=RAADZVN65BQA7G839DFN3VHWCZBQMRBR11`) - if (result.data.status === "1") { - - const gameChecker = window.setInterval(async () => { - const games = await doGetUserSingleGames(network, account); - - //FIXME: 透過 user 戰鬥場數來判斷此役戰鬥在合約中是否已確實完成 - // 但礙於組建設計不良, 生命週期混亂, 只能透過在 App.js 取得的 historyGamesCount - // 以及本地取得的 historyGames 做雙重判斷, 之後需要更改 - if(games.length <= historyGames.length || games.length <= historyGamesCount) { - return; - } - window.clearInterval(gameChecker); - const gamePromises = games.map(cur => getSingleGame(network, cur, account)); - const gameDetails = await Promise.all(gamePromises); - const thisGame = gameDetails[gameDetails.length - 1]; - const userPointer = thisGame[1]; - const contractPointer = thisGame[2]; - const userBet = thisGame[3]; - const gameType = thisGame[4]; // 0 = small win 1 = big win - const isWin = thisGame[5]; // 0 win, 1 lost, 2 平手 - const isUserSmall = userPointer < contractPointer; - - const battleResult = { - userPointer, - contractPointer, - userBet, - isUserSmall, - gameType, - isWin, - }; - - window.setTimeout(() => { - this.setState({ - isLoading: false, - isShowResult: true, - isShowHistory: false, - hasBattleResult: true, - battleResult, - }); - }, 0); - }, 1234); - window.clearInterval(t); - } - }, 3000); - }); - } + }); + }; // 看歷史戰鬥 handleShowHistory = async e => { diff --git a/dapp/src/components/Card/index.js b/dapp/src/components/Card/index.js index 065721c..60a859f 100644 --- a/dapp/src/components/Card/index.js +++ b/dapp/src/components/Card/index.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import classnames from 'classnames/bind'; import cardtitle from '../../images/cardtitle.png'; import style from './Card.css'; -import { getCryptoHerosTokenAddress } from '../../lib/web3Service'; +import { getRPCProvider, getCryptoHerosTokenAddress } from '../../lib/web3Service'; import axios from 'axios'; import LoadingCoin from '../LoadingCoin'; import Button from 'material-ui/Button'; @@ -63,7 +63,7 @@ class Card extends Component { }); }; - handleSubmitEbakus = doMintTx => { + handleSubmitEbakus = async doMintTx => { const { account, network } = this.props.ebakus; const { web3 } = this.props; @@ -74,30 +74,49 @@ class Card extends Component { data: doMintTx, }; - web3.eth.sendTransaction(msk, this.handleEbakusCallBack); + const gas = await web3.eth.estimateGas(msk); + msk.gas = gas; + + this.props.ebakusWallet + .sendTransaction(msk) + .then(res => { + this.handleEbakusCallBack(null, res); + }) + .catch(err => { + this.handleEbakusCallBack(err, null); + }); }; handleEbakusCallBack = (err, result) => { + const { network } = this.props.ebakus; + if (err) { - this.setState({ errmsg: 'Sorry, transaction failed' }, () => + let errmsg = 'Sorry, transaction failed' + if (err === 'no_funds') { + errmsg ='Not enough funds. Please add some funds and try again.' + } + this.setState({ errmsg }, () => this.setState({ isOpenAlert: true }) ); - console.error('Ebakus Error:', err.message); + console.error('Ebakus Error:', err); this.setState({ isLoading: false }); return; } const tx = result; let t = setInterval(async () => { - const result = await axios.get( - `https://api-ropsten.etherscan.io/api?module=transaction&action=gettxreceiptstatus&txhash=${tx}&apikey=RAADZVN65BQA7G839DFN3VHWCZBQMRBR11` - ); - - if (result.data.result.status === '1') { + const result = await axios.post(getRPCProvider(network), { + method: 'eth_getTransactionReceipt', + params: [tx.transactionHash], + id: 1, + jsonrpc: '2.0', + }); + + if (result.data.result.status === '0x1') { this.ReloadDataFn(); window.clearInterval(t); } - }, 3000); + }, 500); }; ReloadDataFn = () => { diff --git a/dapp/src/components/Ebakus/Ebakus.js b/dapp/src/components/Ebakus/Ebakus.js index 1839af3..2b9a25b 100644 --- a/dapp/src/components/Ebakus/Ebakus.js +++ b/dapp/src/components/Ebakus/Ebakus.js @@ -8,52 +8,19 @@ import Dialog, { DialogTitle, } from 'material-ui/Dialog'; import Slide from 'material-ui/transitions/Slide'; +import { getProvider } from '../../lib/web3Service'; const messages = { LOAD_MATAMASK_WALLET_ERROR: 'Load ebakus wallet error, maybe try Ebakus later, or upload a wallet json file.', EMPTY_EBAKUS_ACCOUNT: 'You can choose one Ebakus wallet by unlocking it', - EBAKUS_ACCOUNT: 'You have choosen the MetamMask Wallet: ', + EBAKUS_ACCOUNT: 'You have choosen the Ebakus Wallet: ', NETWORK_ERROR: 'Network error, please check it.', EBAKUS_NOT_INSTALL: 'You must install Ebakus before start.', EBAKUS_TEST_NET: - 'Our game is available on Ropsten Test Network only. Please switch via Ebakus!', + 'Our game is available on Ebakus Test Network only. Please switch via Ebakus!', }; -const EbakusInstallDialog = props => ( - - {"Oops, you haven't installed Ebakus"} - - - {'What is Ebakus? '} - - Link - - - - - - - - -); - const EbakusLockDialog = props => ( { + // if account changed then change redux state + if (!this.props.ebakus || account !== this.props.ebakus.account) { + this.props.handleEbakusAccount(account); + } + }) + .catch(err => + this.setState({ message: messages.LOAD_METAMASK_WALLET_ERROR }) + ); } } - fetchAccounts() { - //const { web3 } = window; + fetchNetwork() { if (this.props.web3 !== null) { - this.props.web3.eth.getAccounts((err, accounts) => { - if (err) { - this.setState({ message: messages.LOAD_MATAMASK_WALLET_ERROR }); - } else { - if (accounts.length === 0) { - this.props.handleEbakusAccount(null); + this.props.web3.eth.net + .getId() + .then(netId => { + if (netId === '1') { + this.props.handleEbakusNetwork(null); this.setState({ ebakusLockDialogOpen: true, - message: messages.EMPTY_EBAKUS_ACCOUNT, + message: messages.EBAKUS_TEST_NET, }); - } else { - // if account changed then change redux state - if (accounts[0] !== this.props.ebakus.account) { - this.props.handleEbakusAccount(accounts[0]); - } } - } - }); - } - } - - fetchNetwork() { - //const { web3 } = window; - if (this.props.web3 !== null) { - this.props.web3.version.getNetwork((err, netId) => { - console.log('netId:', netId); - if (netId === '1') { - this.props.handleEbakusNetwork(null); - this.setState({ - ebakusLockDialogOpen: true, - message: messages.EBAKUS_TEST_NET, - }); - } - - if (err) { + // if network changed then change redux state + if (!this.props.ebakus || netId !== this.props.ebakus.network) { + this.props.handleEbakusNetwork(netId); + } + }) + .catch(err => { + console.log('TCL: Ebakus -> fetchNetwork -> err', err); this.props.handleEbakusNetwork(null); this.setState({ ebakusLockDialogOpen: true, message: messages.NETWORK_ERROR, }); - } else { - // if network changed then change redux state - if (netId !== this.props.ebakus.network) { - this.props.handleEbakusNetwork(netId); - } - } - }); + }); } } componentDidMount() { let self = this; - window.addEventListener('load', function() { - let web3 = window.web3; - if (typeof web3 !== 'undefined') { - window.web3 = new Web3(web3.currentProvider); - self.props.setWeb3(window.web3); - self.fetchAccounts(); - self.fetchNetwork(); - self.Web3Interval = setInterval(() => self.fetchWeb3(), 1000); - self.AccountInterval = setInterval(() => self.fetchAccounts(), 1000); - self.NetworkInterval = setInterval(() => self.fetchNetwork(), 1000); - } else { - self.setState({ - ebakusInstallDialogOpen: true, - message: messages.EBAKUS_NOT_INSTALL, - }); - } - }); - } - - componentWillUnmount() { - clearInterval(this.Web3Interval); - clearInterval(this.AccountInterval); - clearInterval(this.NetworkInterval); - } - handleEbakusInstallDialogClose() { - this.setState({ ebakusInstallDialogOpen: false, disableDialog: true }); + window.addEventListener('ebakusLoaded', function() { + window.web3 = window.Web3Ebakus(new Web3(getProvider(101))); + self.props.setWeb3(window.web3); + self.props.setEbakusWallet(window.ebakusWallet); + self.fetchAccounts(); + self.fetchNetwork(); + }); } handleEbakusLockDialogClose() { @@ -190,13 +117,6 @@ export class Ebakus extends Component { } render() { - const ebakusInstall = this.state.disableDialog === false && ( - - ); - const ebakusLock = this.state.disableDialog === false && ( ); - return ( -
- {ebakusInstall} - {ebakusLock} -
- ); + return
{ebakusLock}
; } } diff --git a/dapp/src/components/SendTransaction.js b/dapp/src/components/SendTransaction.js index 128f53b..911551a 100644 --- a/dapp/src/components/SendTransaction.js +++ b/dapp/src/components/SendTransaction.js @@ -9,21 +9,16 @@ class SendTransaction extends Component { handleSubmit() { let self = this; - this.props.web3.eth.sendTransaction( - { + + this.props.ebakusWallet + .sendTransaction({ from: this.props.ebakus.account, to: this.props.ebakus.account, value: this.props.web3.toWei(1, 'ether'), data: 'dead', - }, - function(err, result) { - if (err) { - self.props.handleWarningOpen(err.message); - } else { - self.props.handleWarningOpen(result); - } - } - ); + }) + .then(result => self.props.handleWarningOpen(result)) + .catch(err => self.props.handleWarningOpen(err.message)); } render() { diff --git a/dapp/src/lib/cryptoHerosGameService.js b/dapp/src/lib/cryptoHerosGameService.js index b110504..f274c0a 100644 --- a/dapp/src/lib/cryptoHerosGameService.js +++ b/dapp/src/lib/cryptoHerosGameService.js @@ -1,16 +1,12 @@ import { getProvider, getCryptoHerosGameAddress } from './web3Service'; import CryptoHerosGame from './cryptoHerosGame'; -const Web3 = require('web3'); - -let web3 = new Web3(); let cryptoHerosGameAddress = '0x0'; let cryptoHerosGame = null; const setWeb3Provider = (networkId) => { - web3.setProvider(new Web3(getProvider(networkId))); cryptoHerosGameAddress = getCryptoHerosGameAddress(networkId); - cryptoHerosGame = new CryptoHerosGame(web3, cryptoHerosGameAddress); + cryptoHerosGame = new CryptoHerosGame(window.web3, cryptoHerosGameAddress); } export const doCreateSingleGame = (networkId, tokenId) => { diff --git a/dapp/src/lib/cryptoHerosTokenService.js b/dapp/src/lib/cryptoHerosTokenService.js index 637e851..9559994 100644 --- a/dapp/src/lib/cryptoHerosTokenService.js +++ b/dapp/src/lib/cryptoHerosTokenService.js @@ -1,16 +1,12 @@ import { getProvider, getCryptoHerosTokenAddress } from './web3Service'; import CryptoHerosToken from './cryptoHerosToken'; -const Web3 = require('web3'); - -let web3 = new Web3(); let cryptoHerosTokenAddress = '0x0'; let cryptoHerosToken = null; const setWeb3Provider = networkId => { - web3.setProvider(new Web3(getProvider(networkId))); cryptoHerosTokenAddress = getCryptoHerosTokenAddress(networkId); - cryptoHerosToken = new CryptoHerosToken(web3, cryptoHerosTokenAddress); + cryptoHerosToken = new CryptoHerosToken(window.web3, cryptoHerosTokenAddress); } export const getName = (networkId) => { diff --git a/dapp/src/lib/web3Service.js b/dapp/src/lib/web3Service.js index 0ec410a..ca52b88 100644 --- a/dapp/src/lib/web3Service.js +++ b/dapp/src/lib/web3Service.js @@ -8,11 +8,22 @@ export const getProvider = networkId => { return 'https://rinkeby.infura.io/'; case '42': return 'https://kovan.infura.io/'; + case 101: + return 'wss://nodepow.ebakus.com/'; default: return 'http://localhost:8545/'; } }; +export const getRPCProvider = networkId => { + switch (networkId) { + case 101: + return 'https://nodepowrpc.ebakus.com'; + default: + return getProvider(networkId); + } +}; + export const getSimpleTokenAddress = networkId => { switch (networkId) { case '1': @@ -38,8 +49,8 @@ export const getCryptoHerosTokenAddress = networkId => { return '0x0'; case '42': return '0x0'; - case '101': - return '0x567853Ff4a22226959E155461024FC40EBB602C0'; + case 101: + return '0x04eAa3592a5C23b5415eb8Ab1aFabA8b30D867B6'; default: return '0x0'; } @@ -55,8 +66,8 @@ export const getCryptoHerosGameAddress = networkId => { return '0x0'; case '42': return '0x0'; - case '101': - return '0x2DfB48dF75632A41db8861295B7220a4721fB74D'; + case 101: + return '0xCE51DD657cD15AB88FCC0Fa637D3D4AD97D8F6a3'; default: return '0x0'; } diff --git a/truffle-config.js b/truffle-config.js index 261cd1b..f4e87f3 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -16,7 +16,7 @@ module.exports = { host: '127.0.0.1', port: 8545, network_id: '*', - gas: 4600000, + gas: 6000000, }, }, };